栈帧的概念可以从下图 当前帧 具象理解 该架构下,堆栈由高地址往低地址生长 (栈顶在低地址) 调用函数完成后,当前寄存器 %rbp 存储当前栈帧指针所在内存的地址; %rsp 存储当前整个堆栈的栈顶指针所在的地址。 当前栈帧指针所在内存的地址存储了 上一栈帧的栈帧指针所在内存的地址 当前栈帧指针所在内存的地址的上一个地址存储了 调用者的返回
X86-64的出现,给GCC提供了一个绝好的机会,在新的x86-64机器上,放弃保守的假设,进而充分利用x86-64的各种特性,比如:在过程调用中,通过寄存器来传递参数,而不是传统的堆栈。又如:尽量使用条件传送指令,而不是控制跳转指令。 寄存器简介 先明确一点,本文关注的是通用寄存器(后简称寄存器)。既然是通用的,使用并没有...
movq%rbp,%rsp ; 使%rsp 和%rbp 指向同一位置,即子栈帧的起始处, 收回子栈帧空间 popq%rbp ; 将栈中保存的父栈帧的%rbp 的值赋值给%rbp,并且%rsp 上移一个位置指向父栈帧的结尾处 为了便于栈帧恢复,x86_64 架构中提供了 leave 指令来实现上述两条命令的功能。执行 leave 后,前面图中函数调用的栈帧...
在这段空间上保存调用者栈帧的基址(ebp)、本函数的局部变量、调用其他函数时的返回地址, 并在需要时保存调用者使用的寄存器值, 被调函数结束后esp上移表示释放这段空间,然后回到调用者的占用的空间与代码位置继续执行, 函数运行阶段在调用栈上占用的这段空间就叫做栈帧,是编译原理运行时空间组织中活动记录(activatio...
堆栈的主要用途在于过程调用,一个堆栈将由一个或多个堆栈帧组成,每个堆栈帧(也称作活动记录)对应于对尚未以返回终止的函数或过程的调用,堆栈帧本质就是函数或者方法。我们知道对于函数或者方法有参数、局部变量、返回值。所以对于堆栈帧由函数参数、指向前一个堆栈帧的反向指针、局部变量组成。有了上述基础知识铺垫,接...
X86-64寄存器的变化,不仅体现在位数上,更加体现在寄存器数量上。新增加寄存器%r8到%r15。加上x86的原有8个,一共16个寄存器。 刚刚说到,寄存器集成在CPU上,存取速度比存储器快好几个数量级,寄存器多了,GCC就可以更多的使用寄存器,替换之前的存储器堆栈使用,从而大大提升性能。
栈指针与程序计数器在汇编代码都不能随意改,使用gcc内联汇编改改rip有时还没事(但x86指令不是等长的,很容易出问题)。如果像我一样在对齐堆栈时直接把rsp的值,末尾抹成0然后什么也不管了,必出问题。segmentation fault已经算好的了,因为整个栈帧都歪了。不过也有可能直接被检查出来报错了。
X86-64寄存器的变化,不仅体现在位数上,更加体现在寄存器数量上。新增加寄存器%r8到%r15。加上x86的原有8个,一共16个寄存器。 刚刚说到,寄存器集成在CPU上,存取速度比存储器快好几个数量级,寄存器多了,GCC就可以更多的使用寄存器,替换之前的存储器堆栈使用,从而大大提升性能。
在x86_64汇编中,rbp和返回地址扮演着不同的角色,理解它们之间的区别对理解程序流程至关重要。rbp,即栈帧基址指针,是栈底指针。它是用来定位函数执行栈帧的开始位置,每个函数都有自己独立的栈帧,用于存储局部变量和调用堆栈。在执行函数时,rbp被用来保存当前栈帧的底部位置,以便在函数执行结束后...
x86_64函数调用惯例及其栈帧 为了向栈中注入我们自己的代码,首先需要了解的是函数调用时的栈的变化。需要提前说明的是,x86是小端结构,栈的起点位于高地址。写一段简单的代码: gcc call.c -o callobjdump -d call 编译之后,让我们看看可执行文件的情况。