在 Linux/C 环境中横行几年后,越发体会到汇编对于 GDB 的重要。在开始前,先来看一段 sample:
#include<stdlib.h> #include<stdio.h> /*这个函数没有任何地方调用过 */ void why_here(void) { printf("why u here ?!\n"); exit(0); } int main(int argc,char * argv[]) { long long buff[1]; buff[3]=(long long)why_here; return 0; }
不带 -O 编译后发现,why_here 居然被调用了!
很神奇是吧?想要知道为什么,就得开始了解 x64 寄存器、汇编、Frame 和 stack 等,Let‘s start!
一. 通用寄存器
x86-64 与 x86 并不是同一个概念,且实际上变化挺大的,包括寄存器个数、传参方式等。鉴于 x86-64 已经是主流,最起码在阿狸的服务器上是主流(呃,alipay 不思进取,不包括在内),因此,x86 的相关实现就不列举了。毕竟,对过去了解得越多,除了证明你已经老了,并不能体现你现在有多牛B。
1. 寄存器数目
新增加寄存器 %r8 到 %r15,加上 x86 的原有 8 个,一共 16 个寄存器,分别是:%rax, %rbx, %rcx, %rdx, %rsi, %rdi, %rbp, %rsp, %r8, %r9, %r10, %r11, %r12, %r13, %r14, %r15。
2. 寄存器长度
x86-64 中的寄存器都是 64 位的,相对于 x86 来说,标识符发生了变化,比如:从原来的 %ebp 变成了 %rbp。为了向后兼容性,%ebp 依然可以使用,不过指向了 %rbp 的低 32 位。
3. 寄存器使用方法
所谓的通用,意味着在使用上没有限制,接下来提到的规则,仅仅是 GCC 遵循的规则。这些内容必须记住!
%rax 作为函数返回值使用
%rsp 栈指针寄存器,指向栈顶 (stack pointer)
%rbp 栈指针寄存器,指向栈底 (bottom pointer)
%rdi,%rsi,%rdx,%rcx,%r8,%r9 用作函数参数,依次对应第1参数,第2参数。。。
%rbx,%rbp,%r12,%r13,%14,%15 用作数据存储,遵循被调用者使用规则,简单说就是随便用,调用子函数之前要备份它,以防它被修改
%r10,%r11 用作数据存储,遵循调用者使用规则,简单说就是使用之前要先保存原值
%rip 下一条待执行的指令 (instruction pointer)
二. Frame
对应 GDB 中的 frame 指令,可以在调用栈中切换到不同的函数。因此,可以简单的将函数对应为 Frame。%rbp 和 %rsp 指向 Frame 的底和顶。
本文出自 “wilber” 博客,谢绝转载!
原文地址:http://wilber1202.blog.51cto.com/8738232/1626481