调用函数完成后,当前寄存器 %rbp 存储当前栈帧指针所在内存的地址; %rsp 存储当前整个堆栈的栈顶指针所在的地址。 当前栈帧指针所在内存的地址存储了 上一栈帧的栈帧指针所在内存的地址 当前栈帧指针所在内存的地址的上一个地址存储了 调用者的返回地址 我们使用一些工具对此进行细致观察,首先写一个 test.c: #inc...
如果是Caller Save寄存器,在进行子函数调用之前,需要由调用者提前保存寄存器中的值(入栈),然后在子函数中可以向这些寄存器中写入任何数据;在完成调用后,恢复寄存器原来的值(出栈)。如果是Callee Save寄存器,父函数在进行子函数调用前不会保存寄存器中的值,在调用子函数后,子函数会首先保存寄存器中的值(入栈);子函数...
栈是由高位向低位生长,所以此时是在调用函数的rsp基础上向低位移动,又因为x86-64机器上,地址占8字节,所以push %rbp的背后还有一个隐含的操作就是sub $0x8 %rsp(如果是32机器就是向下移动4字节)。 mov%rsp,%rbp 把caller 的栈顶的内存地址(存在 rsp 寄存器中)赋值到 rbp 寄存器。 sub$0x20,%rsp 把rsp再...
在x86_64中RIP的自增也即偏移一定字节。(可通过disassemble 查看下一个地址,字节大小不一定等长) 但是RIP并不总是自增,也有例外,例如call 指令和ret指令。call指令会将当前RIP的内容压入栈中,将程序的执行权交给目标函数;ret指令则执行出栈操作,将之前压入栈的8个字节的RIP地址弹出,重新放入RIP。 2 arm 寄存器...
在x86_64体系架构中,总共有16个64位通用寄存器,各寄存器及用途如下所示: 对上图中的寄存器做简单说明: %rax :通常存储函数调用的返回结果,也被用在idiv (除法)和imul(乘法)命令中。 %rsp :堆栈指针寄存器,指向栈顶位置。pop操作通过增大rsp的值实现出栈,push操作通过减小rsp的值实现入栈。
函数参数的传递用的是通用寄存器或堆栈,是可以由编译器来决定的,不过一般都会遵守特定规则,以GCC为例,看一下其在X86和X64上是什么情况 测试代码: inttest6(intp1,intp2,intp3,intp4,intp5,intp6) {returnp1+p2+p3+p4+p5+p6; }inttest7(intp1,intp2,intp3,intp4,intp5,intp6,intp7) ...
11 movl $3, %edx // 第3个参数,寄存器%rdx 12 movl $2, %esi // 第2个参数,寄存器%rsi 13 movl $1, %edi // 第1个参数,寄存器%rdi 14 call callee 15 leave 16 ret 看callee: 1 callee: 2 pushq %rbp 3 movq %rsp, %rbp 4 subq $72, %rsp -- 为所有参数准备本地空间 9 * 8 =...
函数运行阶段在调用栈上占用的这段空间就叫做栈帧,是编译原理运行时空间组织中活动记录(activation record)的一种实现 栈帧主要通过 ebp、esp 两个寄存器维护,ebp 始终指向栈底,esp 始终指向栈顶 每个函数被调用时执行下面两条命令 pushl %ebp ; ebp入栈,保存调用者的栈帧基址,以便返回 ...
除了扩展原来存在的通用寄存器,x64架构还引入了8个新的通用寄存器: r8-r15 在原来32位时代,函数调用时,那个时候通用寄存器少,参数绝大多数时候是通过线程的栈来进行传递(当然也有使用寄存器传递的,比如著名的C++this指针使用ecx寄存器传递,不过能用的寄存器毕竟不多)。