以 x86 汇编语言返回值

Lol*_*ano 1 x86 assembly

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

假设我有这个代码:

double(entier n) { return n + n; }
Run Code Online (Sandbox Code Playgroud)

我试图将其转换为 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 
Run Code Online (Sandbox Code Playgroud)

但是后来我完全被阻止了,无法找到如何返回ebx. 我在互联网上找到了一个解决方案,如下所示:

mov [ebp + 12], ebx
pop ebp
ret
pop ebp
ret
Run Code Online (Sandbox Code Playgroud)

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

fuz*_*fuz 5

既然您似乎对此感到非常困惑,那么让我向您展示如何做到这一点:

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
Run Code Online (Sandbox Code Playgroud)

请注意,我选择了eax代替ebx寄存器。这是出于两个原因:

  • eax是调用者保存的寄存器(意味着如果需要调用者必须注意保存其值),而ebx是被调用者保存的寄存器(意味着被调用者,即double必须保存其值)。如果我们想使用ebx,我们必须保存并恢复它的旧值。如果我们改用调用者保存的寄存器eax,我们可以避免这种努力。
  • eax是按照约定在其中找到返回值的寄存器。调用者会将 的值eax作为返回值。

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


对于这些方面的未来问题,我建议您参考打开优化的 C 编译器的输出。C 编译器非常擅长生成汇编,很少出错。在类 UNIX 系统(如 Linux)上,您可以使用该-S选项生成汇编代码。因为gcc我建议你打字

gcc -m32 -O3 -masm=intel -fno-omit-frame-pointer -S -o- source.c
Run Code Online (Sandbox Code Playgroud)

将汇编代码以source.c英特尔语法打印到终端,这是您似乎正在使用的汇编风格。

另请参阅如何从 GCC/clang 程序集输出中去除“噪音”?更多细节。

  • 我喜欢这个术语:呼叫破坏与呼叫保留;更容易思考,您可以从调用者的角度(函数对我的寄存器做什么)或从被调用者的角度(我如何使用寄存器)来考虑这两个术语。也避免暗示任何人都需要做任何保存。只是不要碰它们,或者让不再需要的值被破坏。 (5认同)