以x86汇编语言返回值

时间:2019-04-20 13:14:34

标签: assembly x86

我目前正在学习x86汇编语言(我正在学习本课程的开始),并且在理解特定情况下堆栈的工作方式时遇到了一些问题。

假设我有以下代码:

double(entier n) { return n + n; }

我试图将其转换为x86代码,但最终得到了这个结果:

push ebp #save old pointer stack
mov ebp, esp #put new pointer stack
mov ebx, dword[ebp + 8] #get argument n and put it in ebx
add ebx, dword[ebp + 8] #add n to ebx 

但是后来我完全被封锁了,无法找到如何返回ebx的值。我在互联网上找到了以下解决方案:

mov [ebp + 12], ebx
pop ebp
ret
pop ebp
ret

我不知道它是如何工作的。 ebp+12是否不是第二个参数的值? (就我而言,没有)。弹出键用于移动esp指针,但是为什么在这种情况下我们需要2个弹出键和2个返回键?仅仅是删除函数声明期间使用的值吗?

1 个答案:

答案 0 :(得分:4)

由于您似乎对此完全感到困惑,所以让我向您展示如何做:

double: push ebp           ; establish...
        mov ebp, esp       ; ...stack frame

        mov eax, [ebp + 8] ; load argument from stack into eax
        add eax, eax       ; add it to itself

        leave              ; tear down the stack frame
        ret                ; return to the caller

请注意,我选择eax代替ebx作为寄存器。这有两个原因:

  • eax是保存呼叫者的寄存器(意味着,如果需要,调用者必须注意保留其值),而ebx是保存呼叫者的寄存器(意味着被呼叫者,即{{ 1}}必须保留其值)。如果要使用double,则必须保存并恢复其旧值。如果我们改用ebx之类的保存呼叫者的寄存器,则可以避免这种麻烦。
  • eax是根据约定在其中找到返回值的寄存器。调用方将把eax的值作为返回值。

    在几乎所有x86调用约定中,返回值都是返回时在eax中找到的值。因此,通过将加法的结果放在eax中,我们不必做任何额外的工作来设置返回值。


对于这些方面的未来问题,我建议您参考启用了优化的C编译器的输出。 C编译器非常擅长生成程序集,很少犯任何错误。在类似UNIX的系统(例如Linux)上,可以使用eax选项生成汇编代码。对于-S,我建议您键入

gcc

以Intel语法将gcc -m32 -O3 -masm=intel -fno-omit-frame-pointer -S -o- source.c 的汇编代码打印到终端,这是您似乎正在使用的汇编样式。

另请参阅How to remove "noise" from GCC/clang assembly output?