我使用以下类型在运行时创建一个新函数:
typedef int (*pfunc)(int);
union funcptr {
pfunc x;
byte* y;
};
这使我能够在y
中编写指令,然后调用这样的函数:
byte* p = (byte*)VirtualAllocEx(GetCurrentProcess(), 0, 1<<16, MEM_COMMIT, PAGE_EXECUTE_READWRITE );
// Write some instructions to p
funcptr func;
func.y = p;
int ret = func.x(arg1); // Call the generated function
了解C ++如何准备参数(调用约定)至关重要,因此我查找了项目属性(Visual C ++),我可以看到它使用__cdecl
。它应该根据:http://msdn.microsoft.com/en-us/library/aa271989(v=vs.60).aspx和http://en.wikipedia.org/wiki/X86_calling_conventions#cdecl将参数放在堆栈上,但是当我查看生成的汇编时,参数将被移动到EAX寄存器。
我想绝对肯定这些论据是如何准备的。那么我是否忽略了cdecl
或Visual C ++优化调用的内容,如果是这样,我该如何确保它不会发生呢?
致以最诚挚的问候,Lasse Espeholt
答案 0 :(得分:5)
EAX
注册为used for the return value of the function。您在评论中说明了您正在使用/Gd
进行编译,因此该函数将使用__cdecl
。在我看来,使用明确的pfunc
标记函数类型__cdecl
的声明是有意义的,这样就不会有混淆和不匹配的空间。
当然,没有什么能阻止你使用编译器支持的其他调用约定之一。最重要的一点是,无论您采用何种调用约定,都应该明确指定函数指针的调用约定,因为编译器只负责接口的一半。
答案 1 :(得分:2)
至少在Linux上,你可能想要使用libffi(外部函数接口),它甚至已被移植到其他系统(包括Windows)。
如果您想在运行时生成机器代码,请考虑使用GNU lightning,DotGnu的libjit,LLVM。 LuaJit的dynasm等你也可以在foo.c
中生成C代码,通过分配gcc -fPIC -shared foo.c -o foo.so
命令来编译它,dlopen("./foo.so", RTLD_GLOBAL)
(和Windows具有相同的能力)