【问题标题】:Returning a value in x86 assembly language以 x86 汇编语言返回一个值
【发布时间】:2019-04-20 13:14:33
【问题描述】:

我目前正在学习 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 不是第二个参数的值吗? (在我的情况下没有)。 pop 用于移动esp 指针,但为什么我们需要 2 pop 和 2 return 在这种情况下?是不是只去掉函数声明时已经使用过的值?

【问题讨论】:

  • 什么是entier?这种情况下的调用约定是在eax 中返回一个返回值。你应该在谷歌上搜索为你运行的任何平台编写一个汇编程序,这样你就可以获得基础知识。而且您有两个 ret 语句按顺序仅由 pop ebp 分隔,我认为这是一个印刷错误?
  • 它是一个整数。 (抱歉忘记正确编辑)
  • 如果你在 C 中,那么它应该是int。你还没有说你正在使用什么高级语言或你在什么平台上。它可以有所作为。它看起来像 C,但我不想假设。特别是因为您使用 C 关键字 (double) 作为函数名,这通常不是一个好主意。
  • 返回值进入eax。函数返回时,eax 中的内容就是返回值。
  • @LoloGiordano 我无法告诉您您在互联网上找到的随机代码的作用。这似乎是荒谬和错误的。我建议您(至少在开始时)从教程或书籍中学习,而不是在互联网上找到随机的 sn-ps 代码。

标签: assembly x86


【解决方案1】:

既然你似乎完全被这个弄糊涂了,让我来告诉你怎么做吧:

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 是被调用者保存的寄存器(意味着被调用者,即double 必须保留其值)。如果我们想使用ebx,我们必须保存并恢复它的旧值。如果我们改用 eax 这样的调用者保存的寄存器,我们可以避免这种工作。
  • eax 是按照惯例找到返回值的寄存器。调用者会将eax的值作为返回值。

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


对于这些方面的未来问题,我建议您查阅启用了优化的 C 编译器的输出。 C 编译器非常擅长生成汇编,很少出错。在 Linux 等类 UNIX 系统上,您可以使用 -S 选项生成汇编代码。对于gcc,我建议您输入

gcc -m32 -O3 -masm=intel -fno-omit-frame-pointer -S -o- source.c

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

更多详情请参见How to remove "noise" from GCC/clang assembly output?

【讨论】:

  • 我喜欢这个术语:call-clobbered vs. call-preserved;更容易思考,您可以从调用者的角度(函数将对我的寄存器做什么)或从被调用者的角度(我如何使用寄存器)考虑这两个术语。也避免暗示任何人都需要保存任何东西。只是不要碰它们,或者让不再需要的值被破坏。
  • "如果我们想使用 ebx,我们必须保存并恢复它的旧值。如果我们使用被调用者保存的寄存器,我们可以避免这种工作。"我想你的意思是在这里说caller-saved register
猜你喜欢
  • 2023-03-25
  • 1970-01-01
  • 2015-04-05
  • 1970-01-01
  • 2017-01-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多