Macos 这个汇编函数的序言/尾声代码对rbp/rsp/leave做了什么?

Macos 这个汇编函数的序言/尾声代码对rbp/rsp/leave做了什么?,macos,gcc,assembly,stack,x86-64,Macos,Gcc,Assembly,Stack,X86 64,我刚刚开始学习使用GCC编译器为mac汇编代码。不幸的是,如果你是一个初学者,学习如何做到这一点的资源非常有限。我终于找到了一些简单的示例代码,我可以开始认真思考,并使它正确地组装和运行。代码如下: .text # start of code indicator. .globl _main # make the main function vi

我刚刚开始学习使用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

注释解释了代码中的一些事情(我有点理解第2-5行的功能),但我不理解其中的大部分含义。我确实了解什么是寄存器以及每个寄存器的基本用途(
rbp
rsp
esp
eax
)以及它们的大小,我也了解(通常)堆栈是什么,但这仍然超出了我的理解范围。有人能告诉我这到底是怎么回事吗?另外,有谁能为我指出一个适合初学者的好教程的方向吗

堆栈是一种如下的数据结构。日常生活中的堆栈(我指的是计算机之外的堆栈)向上增长,而x86和x86-64处理器中的堆栈则向下增长。请参阅(但请考虑代码示例是英特尔语法中的32位x86代码,而您的代码是AT&T语法中的64位x86-64代码)

那么,您的代码是做什么的(我在这里用英特尔语法解释):

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

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

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

这是显而易见的。只需将
rsp
的值存储到
rbp
中即可

subl    $8, %esp
ret
esp
中减去8,并将其存储到
esp
中。实际上,这是代码中的一个bug,即使它在这里没有引起任何问题。x86-64中以32位寄存器(
eax
ebx
ecx
edx
ebp
esp
esi
edi
)作为目标的任何指令都会设置相应64位寄存器的最高位32位(
rax
rbx
rcx
rdx
rbp
rsp
rsi
rdi
)归零,导致堆栈指针指向低于4 GiB限制的某个位置,有效地做到了这一点(在英特尔语法中):

编辑:添加了
子esp的后果,8
如下

但是,在内存小于4 GiB的计算机上,这不会导致任何问题。在内存大于4 GiB的计算机上,这可能会导致分段错误。
在下面的代码中保留
会将一个正常值返回到
rsp
。通常在x86-64代码中,您不需要
esp
never(可能不包括一些优化或调整)。要修复此错误:

subq    $8, %rsp
到目前为止,这些指令是标准的输入序列(根据堆栈使用情况替换
$8
)(但请再次注意,它使用具有Intel语法的32位x86程序集,而不是具有AT&T语法的64位x86-64程序集)

然后:

这是显而易见的。将0存储到
eax
。这与堆栈无关

leave
这相当于
mov-rsp,rbp
,然后是
pop-rbp

subl    $8, %esp
ret

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

如果您不知道汇编,x86是最不适合首先学习的。拥有硬件是我听到的最不适合首先学习x86的借口(你不想第一次学习硬件)。如果您想通过做一些有用的事情来学习,并且您要在x86上评测某些东西,那么您最好先学习x86 asm。现代用户空间x86不错。是的,如果您试图编写操作系统,而不是查看普通程序的编译器输出,这会很复杂,但不要先这样做。有很多很好的链接可供参考和学习g中的手册。
leave
ret