【问题标题】:What is this assembly function prologue / epilogue code doing with rbp / rsp / leave?rbp / rsp / leave 这个汇编函数序言/尾声代码是做什么的?
【发布时间】:2012-12-27 02:14:17
【问题描述】:

我刚刚开始学习使用 GCC 编译器来组装我的代码的 mac 程序集。不幸的是,如果您是初学者,学习如何做到这一点的资源非常有限。我终于设法找到了一些简单的示例代码,我可以开始琢磨它,并且我得到了它的组装和正确运行。这是代码:

.text                                           # start of code indicator.
.globl _main                                    # make the main function visible to the outside.
_main:                                          # actually label this spot as the start of our main function.
    push    %rbp                            # save the base pointer to the stack.
    mov     %rsp, %rbp                      # put the previous stack pointer into the base pointer.
    subl    $8, %esp                        # Balance the stack onto a 16-byte boundary.
    movl    $0, %eax                        # Stuff 0 into EAX, which is where result values go.
    leave                                   # leave cleans up base and stack pointers again.
    ret

cmets 在代码中解释了一些事情(我有点理解第 2 到 5 行的作用),但我不明白其中大部分是什么意思。我确实了解寄存器是什么以及这里的每个寄存器(rbprspespeax)的基本用途以及它们有多大,我也(通常)了解堆栈是什么,但这仍然超出了我的想象。谁能告诉我这是在做什么?另外,任何人都可以为我指出一个适合初学者的好教程的方向吗?

【问题讨论】:

  • 如果你不懂汇编,x86 是最差的先学。拥有硬件是我听过的最糟糕的第一次学习 x86 的借口(你不想第一次在硬件上学习)。
  • 如果你想通过做一些有用的事情来学习,并且你在 x86 上分析一些东西,你不妨先学习 x86 asm。现代用户空间 x86 还不错。是的,如果您尝试编写操作系统,而不是查看普通程序的编译器输出,这很复杂,但不要先这样做。 the x86 tag wiki 中有很多很好的参考和学习手册链接。

标签: macos gcc assembly stack x86-64


【解决方案1】:

堆栈是遵循LIFO principle 的数据结构。日常生活中的堆栈(我的意思是在计算机之外)向上增长,而 x86 和 x86-64 处理器中的堆栈向下增长。请参阅Wikibooks article on x86 stack(但请注意代码示例是 Intel 语法中的 32 位 x86 代码,而您的代码是 AT&T 语法中的 64 位 x86-64 代码)。

那么,你的代码做了什么(我在这里的解释是使用 Intel 语法):

push %rbp

rbp 推入堆栈,实际上是从rsp 中减去8(因为rbp 的大小是8 个字节),然后将rbp 存储到[ss:rsp]

因此,在 Intel 语法中,push rbp 实际上是这样做的:

sub rsp, 8
mov [ss:rsp], rbp

然后:

mov     %rsp, %rbp

这很明显。只需将rsp 的值存储到rbp 中即可。

subl    $8, %esp

esp 中减去 8 并将其存储到esp。实际上这是您的代码中的一个错误,即使它在这里没有引起任何问题。在 x86-64 中,任何以 32 位寄存器(eaxebxecxedxebpespesiedi)作为目标的指令都设置了最顶层对应的 64 位寄存器(raxrbxrcxrdxrbprsprsirdi)的 32 位为零,导致堆栈指针指向指向低于 4 GiB 限制的某处,有效地执行此操作(在 Intel 语法中):

sub rsp,8
and rsp,0x00000000ffffffff

编辑:在下面添加了sub esp,8 的结果。

但是,这在内存小于 4 GiB 的计算机上不会引起任何问题。在内存超过 4 GiB 的计算机上,可能会导致分段错误。 leave 在您的代码下方进一步向rsp 返回一个合理的值。通常在 x86-64 代码中,您永远不需要 esp(可能不包括一些优化或调整)。要修复此错误:

subq    $8, %rsp

到目前为止的指令是标准的进入顺序(根据堆栈使用情况替换$8)。 Wikibooks has a useful article on x86 functions and stack frames(但请再次注意,它使用 Intel 语法的 32 位 x86 程序集,而不是 AT&T 语法的 64 位 x86-64 程序集)。

然后:

movl    $0, %eax

这很明显。将 0 存储到 eax。这与堆栈无关。

leave

这相当于mov rsp, rbp 后跟pop rbp

ret

最后,这会将rip 设置为存储在[ss:rsp] 的值,有效地将代码指针返回到调用此过程的位置,并将8 加到rsp

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2021-07-30
    • 2015-01-14
    • 1970-01-01
    • 2019-02-27
    • 1970-01-01
    • 1970-01-01
    • 2016-04-02
    • 2018-02-02
    相关资源
    最近更新 更多