2

我一直在学习 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。

4

1 回答 1

8

在 32 位体系结构上,它取决于调用约定,例如 Windows 有两者__fastcall__thiscall并且使用寄存器和堆栈参数,并且__cdecl使用__stdcall堆栈参数,但在谁进行清理方面有所不同。MSDN在这里有一个很好的列表(或者更面向汇编的版本)。请注意,FPU/SSE 操作也有自己的约定

为了简单起见,请尝试使用__stdcall所有内容,这允许您使用堆栈帧通过 来访问 args MOV r32,[EBP+4+(arg_index * 4)],或者如果您不使用堆栈帧,则可以使用MOV r32,[ESP+local_stack_offset+(arg_index * 4)]. 此处带注释的 C++ -> x86 Assembly 示例应该会有所帮助。


举个简单的例子,假设我们MulAdd在汇编中有函数,使用 C++ 原型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使用堆栈帧避免需要通过 args、溢出或寄存器或为局部变量进行的堆栈分配来调整对堆栈所做的更改。它的缺点是它减少了您必须使用的寄存器数量。

于 2013-01-07T12:13:54.273 回答