AT&T语法,x86-64:
.text
.globl _start
_start:
push %rbp /* push base pointer on stack */
mov %rsp, %rbp /* base pointer points to top of the stack */
sub $12, %rsp /* allocate space for variable */
push $3
push $2
push $1
mov -4(%rbp), %rax
mov %rbp, %rsp
pop %rbp
我正在尝试了解有关堆栈的更多信息。我想使用1, 2, 3
的偏移量从堆栈(rbp
)访问一些变量。我的目标是将值压入rax
,然后在调试器中检查寄存器。但是,当我这样做时:
(gdb) x/d $rax
0x0: Cannot access memory at address 0x0
push $3
是64位推送。 How many bytes does the push instruction push onto the stack when I don't specify the operand size?
但是这甚至没有关系,因为您在第一次推送之前将RSP移到了保存的RBP值(RBP指向的位置)以下12个字节的位置。
[在Linux运行的静态可执行文件中的进程启动时,即在您显然地构建程序的方式为_start
时,所有寄存器(RSP除外)均被清零,并且低于初始RSP的堆栈存储器开始为零。 ABI并没有官方保证,但是实际上Linux是如何工作的。这就是为什么您加载零的原因。
mov -4(%rbp), %rax
加载8个字节。该负载的低4个字节来自您用sub $12, %rsp
跳过的空间。高4字节从保存的RBP值的底部开始。这两件事都是0
,因为Linux在初始化新进程时将它们清零了。
您从内存加载到RAX的值永远不会成为指针,因此,对于GDB的x
命令来说,它作为arg没有意义。 x
检查给定地址的内存。 would的意义是x /16gx $rsp
在RSP之上转储16个qword。
还请注意,sub $12, %rsp
看起来像是从32位代码中天真的移植过来的。这会使堆栈未对齐。在_start
,它已经对齐了16。_start
是not一个函数;没有什么叫它,你不能从它那里ret
。您无需保存旧的RBP,甚至根本不需要使用RBP进行任何操作。通常,一个指向堆栈(RSP)的指针就足够了。
在确实被调用的函数的顶部,RSP-8将按16字节对齐,因此一按即可重新对齐堆栈。