(Visual) C++ 中动态创建函数的调用约定
Posted
技术标签:
【中文标题】(Visual) C++ 中动态创建函数的调用约定【英文标题】:Calling convention for dynamically created function in (Visual) C++ 【发布时间】:2011-11-26 16:09:44 【问题描述】:我使用以下类型在运行时创建新函数:
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
【问题讨论】:
我很好奇你用这种技术解决了什么问题。 我不是MSVC,但你不能在函数指针类型中嵌入调用约定吗? @KerrekSB:比起联合技巧,我更担心使用一些动态填充的二进制代码运行函数。前者更加“未定义”。 ;) @JohnZwinck 和其他人。我正在开发一个迷你 JIT 编译器,有没有更好的方法呢? @lasseespeholt:是的,当然:声明函数指针:pfunc f;
,然后获取指向其内存的字符指针:char * pf = reinterpret_cast<char*>(&f);
,然后用 that 填充正确的值。 (将指针转换为 char 指针显然是 not 类型的双关语。)这并不意味着函数调用本身就有意义;只是你的建设是宽恕的。
【参考方案1】:
EAX
寄存器是used for the return value of the function。您在 cmets 中声明您正在使用 /Gd
进行编译,因此该函数将使用 __cdecl
。尽管如此,在我看来,用明确的__cdecl
标记函数类型pfunc
的声明是有意义的,这样就不会有混淆和不匹配的余地。
当然,没有什么可以阻止您使用编译器支持的其他调用约定之一。最重要的一点是,无论你选择哪种调用约定,都应该明确指定函数指针的调用约定,因为编译器只负责接口的一半。
【讨论】:
根据我正在使用的项目属性__cdecl (/Gd)
?
@lasseespeholt,无论如何都试试__cdecl
(例如typedef int (__cdecl *pfunc)(int);
),让我们知道发生了什么。
对不起,我搞混了。 EAX
用于函数的返回值。答案已更新,现在是正确的(我相信!!)
@avakar 成功了。但同样,项目属性说它是cdecl
。我会尝试创建一个新项目。谢谢大家:)
@lasseespeholt 请查看我的更新答案。我的原始版本让你感到困惑。对不起。所有调用约定都使用 EAX 寄存器作为返回值。【参考方案2】:
至少在Linux上,你可能想使用libffi(外部函数接口),甚至已经移植到其他系统(包括Windows)。
如果你想在运行时生成机器码,可以考虑使用GNU lightning、DotGnu 的libjit、LLVM。 LuaJit 的dynasm 等你也可以生成C 代码到foo.c
,通过fork 一个gcc -fPIC -shared foo.c -o foo.so
命令和dlopen("./foo.so", RTLD_GLOBAL)
来编译它(和Windows 有同等的能力)
【讨论】:
GNU 闪电看起来很有希望。但是对于这个宠物项目,我想自己做这一切——包括 x86 汇编。但我想我可以看看源代码,看看它们是如何生成和调用函数的。 libjit 目前由Aleksey Demakov 维护;您可以从git.savannah.gnu.org 获取最新版本; libffi 的替代方案是dyncall,它支持 MSVC,而 Windows 版本的 libffi 需要使用 MinGW 或 Cygwin 构建以上是关于(Visual) C++ 中动态创建函数的调用约定的主要内容,如果未能解决你的问题,请参考以下文章