【问题标题】:Accessing function parameters in C++ from assembly in IA-32从 IA-32 中的程序集访问 C++ 中的函数参数
【发布时间】:2013-01-07 11:46:35
【问题描述】:

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

【问题讨论】:

  • 我认为它通常类似于 esp+4,但我不是这方面的专家,尤其是涉及 VC++ 时。
  • 看起来你已经复制粘贴了程序集。这不是它的工作原理。
  • @Tinctorius:不是复制粘贴的。这是我经过大量谷歌搜索后达到的最终工作解决方案。但我自己觉得这是错误的方法(嗯,ebp-212 对我来说没有任何意义)。

标签: c++ function assembly x86 64-bit


【解决方案1】:

在 32 位架构上,它取决于调用约定,例如 Windows 有 __fastcall__thiscall 使用寄存器和堆栈参数,__cdecl__stdcall 使用堆栈参数但不同的人清理。 MSDN 有一个很好的列表 here(或更多 assembly orientated version)。请注意,FPU/SSE 操作也有其own conventions

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


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

【讨论】:

  • +1 - 嗨,第一种方法效果很好。为什么 [ESP+4] ?为什么不只是 [ESP]?
  • @AbidRahmanK:因为[ESP] 保存了由CALL 指令压入堆栈的返回地址。
  • +1 - 哦...我忘记了那部分。谢谢你。您有什么推荐的书籍或教程可以更好地理解这部分吗?
  • @AbidRahmanK:不幸的是,我的大部分学习都是在英特尔开发人员手册的帮助下通过调试调试器和汇编器完成的。
猜你喜欢
  • 2018-07-02
  • 1970-01-01
  • 1970-01-01
  • 2013-10-13
  • 1970-01-01
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多