我的处理器(Intel i7)支持“POPCNT指令”,我想从我的C#应用程序中调用它。这是可能的吗?
我相信我在某个地方读到过这不可能,但如果JIT发现它可用,它将调用它,但我需要调用哪个函数来替换此指令?
Popcount 在循环中被调用数百万次,因此如果可能的话,我希望能够进行CPU优化。
我相信我在某个地方读到过这不可能,但如果JIT发现它可用,它将调用它,但我需要调用哪个函数来替换此指令?
Popcount 在循环中被调用数百万次,因此如果可能的话,我希望能够进行CPU优化。
你想玩火,而我们喜欢玩火...
(这句话可能是引用或暗示某种技术或行为,需要更多上下文来确定确切的翻译。)class Program
{
const uint PAGE_EXECUTE_READWRITE = 0x40;
const uint MEM_COMMIT = 0x1000;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
private delegate int IntReturner();
static void Main(string[] args)
{
List<byte> bodyBuilder = new List<byte>();
bodyBuilder.Add(0xb8); // MOV EAX,
bodyBuilder.AddRange(BitConverter.GetBytes(42)); // 42
bodyBuilder.Add(0xc3); // RET
byte[] body = bodyBuilder.ToArray();
IntPtr buf = VirtualAlloc(IntPtr.Zero, (IntPtr)body.Length, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
Marshal.Copy(body, 0, buf, body.Length);
IntReturner ptr = (IntReturner)Marshal.GetDelegateForFunctionPointer(buf, typeof(IntReturner));
Console.WriteLine(ptr());
}
}
(这个汇编的小例子将简单地返回42……我认为这是这个答案的完美数字:-))
最终的诀窍在于:
A)您必须知道与您要编写的asm相对应的操作码
B)您使用VirtualAlloc使一个内存页面可执行
C)以某种方式将您的操作码复制到那里
(代码来自http://www.cnblogs.com/netact/archive/2013/01/10/2855448.html)
好的……另一个就像网站上写的一样(减去了uint
-> IntPtr dwSize
上的错误),这个就是应该写的方式(或者至少比原来多1个……我会将所有内容封装在一个IDisposable
类中,而不是使用try... finally
)
class Program
{
const uint PAGE_READWRITE = 0x04;
const uint PAGE_EXECUTE = 0x10;
const uint MEM_COMMIT = 0x1000;
const uint MEM_RELEASE = 0x8000;
[DllImport("kernel32.dll", SetLastError = true)]
static extern IntPtr VirtualAlloc(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, uint flProtect);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool VirtualProtect(IntPtr lpAddress, IntPtr dwSize, uint flAllocationType, out uint lpflOldProtect);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool VirtualFree(IntPtr lpAddress, IntPtr dwSize, uint dwFreeType);
private delegate int IntReturner();
static void Main(string[] args)
{
List<byte> bodyBuilder = new List<byte>();
bodyBuilder.Add(0xb8); // MOV EAX,
bodyBuilder.AddRange(BitConverter.GetBytes(42)); // 42
bodyBuilder.Add(0xc3); // RET
byte[] body = bodyBuilder.ToArray();
IntPtr buf = IntPtr.Zero;
try
{
// We VirtualAlloc body.Length bytes, with R/W access
// Note that from what I've read, MEM_RESERVE is useless
// if the first parameter is IntPtr.Zero
buf = VirtualAlloc(IntPtr.Zero, (IntPtr)body.Length, MEM_COMMIT, PAGE_READWRITE);
if (buf == IntPtr.Zero)
{
throw new Win32Exception();
}
// Copy our instructions in the buf
Marshal.Copy(body, 0, buf, body.Length);
// Change the access of the allocated memory from R/W to Execute
uint oldProtection;
bool result = VirtualProtect(buf, (IntPtr)body.Length, PAGE_EXECUTE, out oldProtection);
if (!result)
{
throw new Win32Exception();
}
// Create a delegate to the "function"
// Sadly we can't use Funct<int>
var fun = (IntReturner)Marshal.GetDelegateForFunctionPointer(buf, typeof(IntReturner));
Console.WriteLine(fun());
}
finally
{
if (buf != IntPtr.Zero)
{
// Free the allocated memory
bool result = VirtualFree(buf, IntPtr.Zero, MEM_RELEASE);
if (!result)
{
throw new Win32Exception();
}
}
}
}
}
VirtualProtect
,添加X位并移除W。因为实施W^X似乎有助于提高安全性。 - Ben Voigtpopcnt eax, [esp + 4]
对应的机器码是 F3 0F B8 44 24 04
。对于 win64 调用规约,可以使用 F3 0F B8 C1
作为 popcnt eax, ecx
的机器码。 - haroldtry... finally
和 VirtualProtect
,感觉更加...清爽 :) - xanatos