这个汇编函数prologue / epilogue代码用rbp / rsp / leave做什么?

时间:2013-01-12 17:46:59

标签: macos gcc assembly stack x86-64

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

.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

这些评论解释了代码中的一些内容(我有点理解第2 - 5行的内容),但我不明白大多数这意味着什么。我确实理解寄存器的基础知识以及每个寄存器的内容(rbprspespeax)用于它们有多大,我也是理解(通常)堆栈是什么,但这仍然是我的头脑。谁能告诉我到底这是做什么的?还有,有人能指出我为初学者提供一个好的教程吗?

1 个答案:

答案 0 :(得分:12)

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

那么,你的代码做了什么(我的解释是英特尔语法):

push %rbp

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

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

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

然后:

mov     %rsp, %rbp

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

subl    $8, %esp

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

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(但请再次注意,它使用32位x86汇编与英特尔语法,而不是64位x86-64汇编与AT&amp; T语法)。

然后:

movl    $0, %eax

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

leave

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

ret

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