我一直在学习 IA-32 汇编编程。所以我想在汇编中编写一个函数并从 C++ 中调用它。
我正在学习的教程实际上是针对 x64 汇编的。但我正在研究 IA-32。 在 x64 中,函数参数存储在 RCX、RDX、R8、R9 等寄存器中。
但稍微搜索一下,我可以理解在 IA-32 中,参数存储在堆栈中,而不是寄存器中。
下面是我的 C++ 代码:
#include <iostream>
#include <conio.h>
using namespace std;
extern "C" int PassParam(int a,int b);
int main()
{
cout << "z is " << PassParam(15,13) << endl;
_getch();
return 0;
}
下面是PassParam()函数的汇编代码(只是加了两个参数而已,仅供学习):
汇编中的 PassParam() :
.model C,flat
.code
PassParam proc
mov eax,[ebp-212]
add eax,[ebp-216]
ret
PassParam endp
end
在我的汇编代码中,您可以看到我将第一个参数从 [ebp-212] 移到了 eax。该值的获取方式如下:
我用 C++ 本身编写了 PassParam() 函数并将其反汇编。然后检查 ebp 在哪里以及第二个参数存储在哪里(参数从右到左存储)。我可以看到有 212 的差异,所以这就是我获得该值的方式。然后像往常一样,第一个参数在 4 个字节后存储。而且效果很好。
问题:
这是从程序集访问参数的正确方法吗?我的意思是,它总是 [ebp-212] 存储参数的地方吗?
如果不是,谁能解释将参数从 C++ 传递到程序集的正确方法?
注意:
我在 Windows 7 机器上使用 Visual C++ 2010。
最佳答案
在 32 位架构上,它取决于调用约定,例如 Windows 同时具有使用寄存器和堆栈参数的 __fastcall
和 __thiscall
,以及 __cdecl
和 __stdcall
使用堆栈参数,但在谁进行清理方面有所不同。 MSDN 有一个很好的 list here (或更多 assembly orientated version )。请注意,FPU/SSE 操作也有它们的 own conventions .
为了方便和简单,尝试对所有内容使用 __stdcall
,这允许您使用堆栈帧通过 MOV r32,[EBP+4+(arg_index * 4)]
,或者如果您不使用堆栈帧,则可以使用 MOV r32,[ESP+local_stack_offset+(arg_index * 4)]
。带注释的 C++ -> x86 程序集示例 here应该有帮助。
作为一个简单的例子,假设我们在汇编中有函数 MulAdd
,其 C++ 原型(prototype)为 int __stdcall MulAdd(int base, int mul, int add)
,它看起来像:
MOV EAX,[ESP+4] //get the first arg('base') off the stack
MOV ECX,[ESP+8] //get the second arg('mul') off the stack
IMUL EAX,ECX //base * mul
MOV ECX,[ESP+12] //get arg 3 off the stack
ADD EAX,ECX
RETN 12 //cleanup the 3 args and return
或者如果你使用栈帧:
PUSH EBP
MOV EBP,ESP //save the stack
MOV EAX,[EBP+8] //get the first arg('base') off the stack
MOV ECX,[EBP+12] //get the second arg('mul') off the stack
IMUL EAX,ECX //base * mul
MOV ECX,[EBP+16] //get arg 3 off the stack
ADD EAX,ECX
MOV ESP,EBP //restore the stack
POP EBP
RETN //return to caller
使用堆栈框架可以避免因 PUSH
参数、溢出或寄存器或为局部变量进行的堆栈分配而对堆栈所做的更改进行调整。它的缺点是减少了您必须使用的寄存器数量。
关于c++ - 从 IA-32 中的程序集访问 C++ 中的函数参数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14195283/