x86-64

X86-64架构寄存器,64位宽,同时兼容IA-32寄存器。

寄存器名 寄存器简介 主要功能 63-0 31-0 15-0 8-0
rax 累加器,是算术运算的主要寄存器 存储返回值 rax eax ax al
rbx 基址寄存器,被调用者保存 存放存储区的起始地址 rbx ebx bx bl
rcx 计数寄存器 循环操作和字串处理的计数控制;函数调用时的第4个参数 rcx ecx cx cl
rdx I/O指针 I/O操作时提供外部设备接口的端口地址;函数调用时的第3个参数 rdx edx dx dl
rsi (source index)源变址寄存器,与rds段寄存器联用,可以访问数据段中的任一个存储单元 函数调用时的第2个参数 rsi esi si sil
rdi (destination index)目的变址寄存器,与res段寄存器联用,可以访问附加段中的任一个存储单元 函数调用时的第1个参数 rdi edi di dil
rbp (base pointer)基址指针寄存器,用于提供堆栈内某个单元的偏移地址,与rss段寄存器联用,可以访问堆栈中的任一个存储单元,被调用者保存 rbp ebp bp bpl
rsp (stack pointer)栈顶指针寄存器,提供堆栈栈顶单元的偏移地址,与rss段寄存器联用,以控制数据进栈和出栈 rsp esp sp spl
r8 函数调用时的第5个参数 r8 r8d r8w r8b
r9 函数调用时的第6个参数 r9 r9d r9w r9b
r10 调用者保存 r10 r10d r10w r10b
r11 调用者保存 r11 r11d r11w r11b
r12 被调用者保存 r12 r12d r12w r12b
r13 被调用者保存 r13 r13d r13w r13b
r14 被调用者保存 r14 r14d r14w r14b
r15 被调用者保存 r15 r15d r15w r15b

CALL和RET

指令CALL和RET用于处理函数调用和返回操作。调用指令CALL的作用是把返回地址压入栈中并且跳转到被调用函数开始处执行。返回地址是程序中紧随调用指令CALL后面一条指令的地址。因此当被调函数返回时就会从该位置继续执行。返回指令RET用于弹出栈顶处的地址并跳转到该地址处。在使用该指令之前,应该先正确处理栈中内容,使得当前栈指针所指位置内容正是先前CALL指令保存的返回地址。另外,若返回值是一个整数或一个指针,那么寄存器eax将被默认用来传递返回值。

call指令执行函数调用。CPU执行call指令时首先会把rip寄存器中的值入栈,然后设置rip值为目标地址,又因为rip寄存器决定了下一条需要执行的指令,所以当CPU执行完当前call指令后就会跳转到目标地址去执行。

ret指令从被调用函数返回调用函数,它的实现原理是把call指令入栈的返回地址弹出给rip寄存器。

ebp

ebp,esp,eip均为32位寄存器,在64位系统里面对应的是64位寄存器rbp,rsp,rip 栈是往低(小)地址方向扩展的,而esp指向当前栈顶处的元素。通过使用push和pop指令我们可以把数据压入栈中或从栈中弹出。对于没有指定初始值的数据所需要的存储空间,我们可以通过把栈指针递减适当的值来做到。类似地,通过增加栈指针值我们可以回收栈中已分配的空间。 大多数CPU上的程序实现使用栈来支持函数调用操作。栈被用来传递函数参数、存储返回信息、临时保存寄存器原有值以备恢复以及用来存储局部数据。单个函数调用操作所使用的栈部分被称为栈帧(stack frame)结构,其一般结构如下图所示。栈帧结构的两端由两个指针来指定。寄存器%ebp通常用做帧指针(frame pointer),而esp则用作栈指针(stack pointer)。在函数执行过程中,栈指针esp会随着数据的入栈和出栈而移动,因此函数中对大部分数据的访问都基于帧指针%ebp进行。(通过对ebp的加减运算来指定在某个数据的地址) movl 12(%ebp), %eax,等同于Intel格式中的mov EAX, [EBP + 12],AT&T中,源操作数在左,目的操作数在右。“l”是Longword,相当于Intel格式中的dword ptr操作限定符; 表示将地址SS:[EBP +12]指向的双字数据传送至EAX寄存器。 addl 8(%ebp), %eax,等同于Intel格式中的add EAX, [EBP + 8],表示将SS:[EBP + 8]指向的一个双字数据同寄存器EAX中的原值相加,所得的结果保存在EAX寄存器。 Intel格式和AT&T格式的区别,这里有个文档可以作参考:http://blog.sina.com.cn/s/blog_51e9c0ab010099ow.html

比如: movl $0x1,0xfffffffc(%ebp)

就是把1赋值给ebp+0xfffffffc的地址 而ebp+0xfffffffc由于ebp是32位寄存器所以也就是ebp-4的地址

eip

EIP寄存器里存储的是CPU下次要执行的指令的地址。

用例:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#include <stdio.h>

int Add(int a, int b)
{
    int c;
    c=a+b;
    return c;
}
void main()
{
    Add(10, 5);
    printf("hello world!");
}

程序从main()函数开始执行,首先将main()函数压入系统调用栈(call stack)(下面如无特殊说明,用栈代指系统调用栈(call stack)),并给它分配一个栈帧用以保存所需信息。然后执行main函数中第一条语句,这是一条函数调用语句,在实际调用执行Add函数前需要做一些准备工作。

  首先将5和10两个参数压入栈,同时更新ESP指针的值,如下图所示: 当Add函数调用执行完毕之后,我们需通过某种方式返回到main函数中继续执行下面的指令,在本例中也就是执行print函数。解决这个问题的方式就是将下一条指令的地址压入栈中: 以上准备工作就绪,下面开始调用执行Add函数。   首先,我们需要将main函数用来寻址参数或变量信息的EBP寄存器值压入栈中保存,以便于从Add函数返回之后,从栈中取出EBP的值赋给EBP寄存器让main函数用来寻址参数或变量信息,同时更新ESP的值。为了Add函数能够寻址到所需信息,将此时的ESP寄存器的值赋值给EBP寄存器(图中的EBP-new)。此时将接着执行int c语句,为变量c开辟一段内存空间压入栈中,同时更新ESP的值。接下来执行c=a+b,然后返回c,但main函数中并没有声明变量来存储该返回值,故该返回值丢失。函数返回时将ESP更新为EBP-new,接着将EBP-old弹出赋值给EBP寄存器,让main函数拿来寻址所需信息,此时就从Add函数的栈帧恢复到了main函数的栈帧。接着弹出RET(Add函数的返回地址),对应的汇编代码中会有一条ret指令,该指令会将RET返回地址保存到EIP寄存器中,然后处理器根据这个地址无条件跳转到main函数的相应位置去取下一条指令即print函数继续执行。print函数调用执行过程中压栈出栈过程与Add函数类似,不再细说。

cr2,cr3

CR2寄存器由CPU在页面故障(page fault)时自动设置,并包含导致页面故障的已访问虚拟地址。 存储在CR3寄存器中的一般是4级页表的物理地址(4级页表的根)

控制寄存器(CR0~CR3)用于控制和确定处理器的操作模式以及当前执行任务的特性。CR0中含有控制处理器操作模式和状态的系统控制标志;CR1保留不用;CR2含有导致页错误的线性地址;CR3中含有页目录表物理内存基地址,因此该寄存器也被称为页目录基地址寄存器PDBR(Page-Directory Base address Register)。