我正在研究一个cpp项目。该项目需要迁移到64位。它包含一些无法在x64上编译的内联汇编代码。 这是包含汇编代码的函数:
void ExternalFunctionCall::callFunction(ArgType resultType, void* resultBuffer)
{
#if defined(_NT_) || defined(__OS2__)
// I386
// just copy the args buffer to the stack (it's already layed out correctly)
int* begin = m_argsBegin;
int* ptr = m_argsEnd;
int arr[1000], i=0;
while (ptr > begin) {
int val = *(--ptr);
__asm push val
}
void* functionAddress = m_functionAddress;
// call the function & handle the return value. use __stdcall calling convention
switch (resultType) {
case voidType:
__asm {
call functionAddress
}
break;
case pointerType:
case int32Type:
__asm {
call functionAddress
mov ebx, resultBuffer
mov dword ptr [ebx],eax
}
break;
case floatType:
__asm {
call functionAddress
mov ebx, resultBuffer
fstp dword ptr [ebx]
}
break;
case doubleType:
__asm {
call functionAddress
mov ebx, resultBuffer
fstp qword ptr [ebx]
}
break;
}
我使用stack,array来迁移这个“asm push val”但是没有用。虽然,它不会抛出任何编译错误,但逻辑无效。
所以,我想问一下,我可以在C ++中使用什么而不是“__asm push val”。任何帮助将不胜感激。
答案 0 :(得分:13)
这个问题一般无法解决;这是因为评论,
// call the function & handle the return value. use __stdcall calling convention
表示依赖 32位呼叫约定。
在32位x86中,stdcall
表示所有参数以相反的顺序在堆栈上传递(即最后一个arg被推送。即,如果arg[0]
在addr
然后arg[1]
,无论它的类型是addr + sizeof(arg[0])
)。这就是您的样本中以下代码的原因:
// just copy the args buffer to the stack (it's already layed out correctly)
int* begin = m_argsBegin;
int* ptr = m_argsEnd;
int arr[1000], i=0;
while (ptr > begin) {
int val = *(--ptr);
__asm push val
}
实际上可以工作 - 因为它究竟是什么并不重要,参数是什么类型的;所有相关的是它们中的每一个都在一个已知的存储位置,并且已知在内存中是连续的。如果您知道参数N
位于addr
,那么您可以知道参数N+1
位于addr + sizeof(arg[N])
。
这就是评论所说的“它已经正确布局” - 不幸的是,这在64位模式下不为真。因此代码不能“移植”;没有相当于港口的。
调用至少部分基于寄存器的约定 - 就像x64(64位x86)上的Win64一样,行为也不同。对于那些,它取决于被调用函数采用何种类型的参数(在Windows中,您可以在通用寄存器中传递四个整数类型的args,在XMM
寄存器中传递一些float类型的args)。因此,您需要知道 more 关于您调用的函数的签名(原型),而不仅仅是“它需要N
个参数”,以便能够正确地编组“anycall”中的args像上面这样的类型包装器。在64位模式下,对于您希望通过包装器调用的每个函数,您不仅要知道总共有多少个args,还要知道通用regs中有多少个,XMM
中有多少个regs,堆栈中有多少。
“通过指针调用func并将返回值复制到已知位置”部分是可移植的,可以用普通的C / C ++表示。但是,如上所述,为此获取参数的部分在32位stdcall
和任何64位x86调用约定之间的任何直接方式 not port 我知道(Win64 / x64和UN * X x86_64
约定都不允许预测函数的所有参数的位置和总堆栈内存使用量,只给出args的数量和类型,而不是它们的排序。 / p>
您需要做什么,取决于上面class ExternalFunctionCall
的调用者/用户更多,而不是您所显示的内联汇编的小样本。特别需要了解成员m_argsBegin
和m_argsEnd
的初始化方式以及位置。您能否提供一些关于类的外观(所有成员变量/函数)的详细信息,以及实际使用的示例?
答案 1 :(得分:0)
您需要解决几件事。 (我在另一个问题上查找了你的代码)。据我所知,这段代码是一个包装器,用于调用位于指定地址的相当抽象的函数,该函数需要堆栈中的一定数量的数据,并且可以返回基于ArgType的不同内容。
如果你想通过普通的C包装它,你必须定义几个函数原型(基于返回值)然后在你的开关上使用,但是你必须解决另一个问题,这更棘手。
在C语言中移植此类内容的问题在于,您不必预先知道必须推入堆栈的参数(数据大小),因此您将无法定义原型。
假设func(char c)肯定会推入堆栈1字节(但由于数据对齐也不一定正确)..在你的情况下你必须考虑解决方案,它将具有相同大小的参数集您需要在堆栈上的数据大小。乍一看,你不能马上做什么。
UPD。你可以用func(char [] param)来做;但它也有上面回答中解释的问题。
答案 2 :(得分:0)
我意识到这是一个有点老问题,但我偶然发现:xbyak。
也许你在寻找什么?