X64处理器
X64处理器特点
X64处理器包括Intel 64和AMD64处理器家族。X64是X86指令集的64位扩展。1999年AMD率先推出AMD64位处理器,同年Intel退出IA64位处理器。2001年Intel推出第一个使用X64的英特尔处理器Xeon(至强),随后是一系列其他处理器,包括Core i5和Core i7处理器。AMD使用X64处理器如Opteron(皓龙)和Athlon(速龙)。
X64处理器有一些基本特点:
X64处理器与x86指令集向后兼容。
X64处理器地址是64位长,允许虚拟地址空间的大小为264个字节。在当前芯片中只使用低48位。地址范围从 0x000'00000000 至 0x7FF'FFFFFFFF 的8 TB 用于用户空间,范围从 0xFFFF0800'00000000 至 0xFFFFFFFF'FFFFFFFF 的 248 TB 的部分用于系统空间。
X64处理器可以使用64位通用寄存器,允许指令具有64位整数操作数。
X64处理器比x86多使用8个通用寄存器。r8、r9、r10、r11、r12、r13、r14、r15。
X64处理器使用一个48位的物理地址空间,支持最多256 TB的RAM寻址空间。
Intel的另一个64位体系结构IA-64(安腾Ita-nium)。IA-64指令集与X86和X64完全不同。Ita-nium 处理器通常用于高性能的数据库和网络服务器。
X86、X64、IA64处理器的区别
X64是X86的64位扩展,内存寻址空间由4GB扩展到48位(目前只用到48位)256TB寻址空间。
IA64的历史早于X64,最初由Intel和惠普于1990年联合推出。由于IA64不与32位兼容,所以没有受到重视。而后为了日益扩张的计算需求,Intel重新将IA64拿出来,发布了安腾系列服务器CPU。IA64是一种崭新的系统,和x86架构完全没有相似性,不应该把它与X86或X64混淆。基于IA64处理器架构的服务器具有64位运算能力、64位寻址空间和64位数据通路,突破了传统IA32架构的许多限制,在数据的处理能力,系统的稳定性、安全性、可用性、可管理性等方面获得了突破性的提高。它是Intel自推出32位微处理器以来,在高性能计算机领域的又一座里程碑。
X64和IA64处理器都能够运行64位操作系统和应用程序,但是区别在于:X64架构基于X86,是为了让X86架构CPU兼容64位计算而产生的技术。X64架构的设计是采用直接简单的方法将目前的X86指令集扩展。这个方法与当初的由16位扩展至32位的情形很相似。优点在于用户可以自行选择X86平台或X64平台,兼容性高。IA64则是原生的纯64位计算处理器,并且与X86指令不兼容。如果想要执行X86指令需要硬件虚拟化支持,而且效率不高。优点在于IA64架构体系将拥有64位内存寻址能力,能够支持更大的内存寻址空间。并且由于架构的改变,性能比起X64的64位兼容模式更高更强。所以,IA64操作系统也是比较少见的,由于只能在Intel安腾系列处理器及AMD部分服务器处理器运行,所以主流市场并不常见。而且,这些IA64架构处理器也不能够使用X64操作系统。而X64处理器则可以自由选择X86或是X64操作系统。
64位操作模式
不支持16位实模式
X64处理器当以本机64位模式运行时,不再支持16位实模式或虚拟8086模式。与早期版本的Windows不同,64位Windows没有利用处理器切换到DOS虚拟机虚拟8086模式的功能。Windows (Win16)和DOS系统下的16位应用程序不可以运行在64位Windows上。
兼容模式
Intel 64架构引入了一种名为IA-32e的新模式,从技术上讲,它包含两个子模式,即兼容性模式和64位模式。
在兼容模式下运行时,现有的32位应用程序可以正常运行,不需要重新编译。
64位模式
在64位模式下,处理器运行使用64位线性地址空间的应用程序。这是64位Microsoft Windows的本机模式。此模式启用64位指令操作数。
64位寄存器
X64处理器寄存器概述
X64通用寄存器
指令指针寄存器和段寄存器
X64处理器寄存器概述
摘抄自:AMD技术开发手册,AMD寄存器
在寄存器方面,以下是64位处理器与32位处理器最重要的的区别:
X64处理器拥有16个64位通用寄存器, 32位处理器只有8个通用寄存器。X64处理器增加了R8、R9、R10、R11、R12、R13、R14、R15 8个通用寄存器,并且新寄存器从r8到r15,可以在各种位范围内访问,如:r8(qword),r8d(dword),r8w(字),r8b(低字节)。
X64处理器拥有8个80位浮点寄存器,与32位X86处理器相同。
X64处理器拥有64位标志寄存器RFLAGS(实际只使用了低32位),功用与32位处理器相同。
X64处理器拥有一个名为RIP的64位指令指针寄存器。
X64处理器拥有8个64位MMX寄存器。
X64处理器拥有16个128位XMM寄存器,在32位处理器中只有8个XMM寄存器。
X64通用寄存器
通用寄存器通常用于执行逻辑算术运算、数据移动。X64通用寄存器可以访问8-bit、16-bit、32-bit或 64位操作数(用于64位操作数时带有特殊 prefix指令前缀)。
64位模式下,默认操作数的大小是32位,有八个(EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP)通用寄存器。但是,通过在每个指令中添加REX(寄存器扩展)前缀,操作数可以是64位,总共有16个通用寄存器可用,增加了从R8到R15 8个编号寄存器。
X64处理器可以使用的8位、16位、32位和64位通用寄存器
X64寄存器
在64位模式下,一条指令不能同时访问两个高字节寄存器,比如AH、BH、和DH。
同时,SI、DI、BP、SP新增了字节寄存器:SIL、DIL、BPL、SPL。
在64位模式下,32位EFLAGS寄存器被64位RFLAGS寄存器替换。两个寄存器共享相同的低32位,不使用RFLAGS的高32位。
32位模式和64位模式下的状态标志是相同的。
指令指针寄存器和段寄存器
RIP指令指针寄存器
X64处理器的指令指针寄存器RIP为64位寄存器,用于指令寻址,相当于X86-32的EIP。
但与X86-32的EIP又有所不同。X86-32 EIP是不可直接访问的,如mov eax,eip是错的,但在x86-64位下却可以,如 mov rax,qword ptr [rip+100]是对的。而且,它除了是个指令指针寄存器外,也是个“数据基地址”寄存器,有此可见,它现在是身兼两职!
为什么在X86-64位下可以用rip做访问数据的基地址呢?这是因为在X86-64下,段寄存器已经不再用于寻址了。
X64段寄存器
X86-64下的DS、ES、CS、SS都没有实际意义了,也就是说,它们不再参与地址计算,只是为了兼容X86-32。FS、GS还是参与地址计算,它们两个和X86-32的意义相同,用于访问特定的内存。
X86-64位下CS记录属性,FS、GS记录基址。其他段寄存器会被忽略。它们的存在只是为了向后兼容
X64段寄存器
此外,需要特别提醒。64位程序不再支持内联汇编,可以将内联汇编改成内联函数的形式实现。
这边使用X64dbg调试函数:MessageBox
64位程序采用寄存器传参。MessageBox函数调用分别使用rcx、rdx、r8、r9传递参数。接着调用ExitProcess函数时,使用rcx寄存器传递参数。
1.64位程序不再需要说明支持指令集和内存模型以及调用约定。
2.64位程序的函数声明不再需要说明参数。
3.64位程序不再需要指定程序入口点。
64位调用约定和堆栈
调用约定
在32位程序中,每种编程语言和操作系统都有自己的调用约定。在64位程序中统一使用FASTCALL作为调用约定。
64位堆栈空间
FASTCALL调用约定意为快速调用约定,优先使用RCX、RDX、R8、R9 4个寄存器传递参数,如果不够,其余参数仍然使用堆栈传参。这意味着它使用寄存器传递前4个参数(然后是堆栈)。因此,堆栈帧由以下组成:堆栈参数,寄存器参数,返回地址(是qword)和局部变量,假设参数寄存器是堆栈帧的一部分,也清楚的表明,任何调用另一个子程序的函数都必须初始化堆栈,为这四个寄存器提供空间,即使传递给子程序的参数小于4。堆栈指针的初始化仅在函数的序言中完成,它必须足够大,以容纳传递给子程序的所有参数,并且被调用者始终有责任清理堆栈。
现在,了解堆栈帧中如何提供空间的最重要的事情是堆栈必须是16字节对齐的。实际上,返回地址必须与16个字节128位对齐。因此,堆栈空间总是类似于16n + 8,其中n取决于参数的数量,8为补齐8个字节后形成16个字节对齐。
FASTCALL调用约定基本特征:
1.调用指令从RSP(堆栈指针)寄存器中减去8,因为地址是64位长。
2.传递给过程的前四个参数放在RCX、RDX、R8和R9寄存器中。按这个顺序,如果只传递一个参数,它将被放置在RCX中。如果有第二个参数,它会放在RDX中,等等。按RCX、RDX、R8、R9的排列顺序压入参数堆栈。
3.调用者的职责是在运行时分配至少32字节的影子空间,以便调用的过程可以选择在此区域保存寄存器参数。
4.当调用子程序时,堆栈指针(RSP)必须在16字节边界上对齐(16的倍数)。调用指令在堆栈上压入一个8字节的返回地址,因此调用程序必须从堆栈指针中减去8,加上它已经为隐藏参数保留32个字节(4个8字节的64位寄存器),一共40个字节。我们将很快在示例程序中展示如何实现这一点。
堆栈指针
在 64 位模式,堆栈指针为 64 位。堆栈大小不是像兼容模式或传统模式中那样靠 SS 段描述符中的某位来控制,也不通过指令前缀来指示。
对隐式堆栈引用将忽略地址大小的指示。除远分支以外,所有隐式引用 RSP 的指令在64 位模式下缺省为 64 位操作数。
影响到的指令包括:PUSH、POP、PUSHF、POPF、ENTER和LEAVE。使用这些指令在 64 位模式下将不可能产生 32 位堆栈值的压栈和出栈。如果使用 66H操作数前缀,将支持 16 位的压栈和出栈。
当寄存器 RAX-RSP 被用作操作数的时候,64 位模式缺省的操作尺寸无需 REX 前缀作为这些指令的先导。如果是 R8‐R15 作为操作数,则 REX 依然是需要的。这是因为前缀在访问新扩展寄存器中是需要的。
X64寻址
64位模式下的地址计算
在 64 位模式(如果没有地址大小的转变),有效地址计算的大小是 64 位。一个有效地址计算使用一个 64 位的基地址和索引寄存器以及符号扩展变换成 64 位。例如[rax+rsi*TYPE table+1]。
对于 64 位模式下平面地址空间,线性地址等同于有效地址。在使用 FS 和 GS 段的非 0为基地址的事务中,这个规则不被使用。在 64 位模式下,有效地址成分被加进来,并且有效地址在加 64 位基地址之前被缩短。地址映射模式在 64 位模式时,基地址从不会被缩短。
在 IA‐32e 模式下,指令指针被扩展到 64 位来支持 64 位代码偏移。64 位指令指针在调用中将值赋给 RIP。
通常,替换和直接在 64 位模式下不被扩展到 64 位。它们在有效地址计算中依然被限制在 32 位和符号扩展。然而,在 64 位模式提供了 MOV 指令的 64 位替换和直接形式的支持。
所有的在 IA‐32e 模式下的 16 位和 32 位地址计算用 0 扩展来形成 64 位地址。地址计算的是缩短到当前模式的有效地址宽度,就像地址宽度前缀的指定那样。其结果是用 0 扩展得到完全的 64 位地址宽度。因为这个,16 位和 32 位应用程序运行在兼容模式只能存取 64位模式有效地址的低 4GB。同样,在 64 位模式产生一个 32 位地址只能访问 64 位模式有效地址的低 4GB。
IA-32e模式
IA-32e是IA-32模式的扩展,它是一种状态,其内核为64位,用户可以是32位,也可以是64位。
当在64位CPU中安装32位操作系统时,内核和用户都是32位的,这种状态叫做Legacy模式。
IA-32e模式的几个特性:
强制平坦段:段基址不可随意设置,不再兼容16位模式。
不支持任务切换:取消了TSS任务切换。
取消了虚拟8086模式和实模式切换。
强制平坦段
在x64模式下,段描述符已经不再描述段的基址和界限(除了FS和GS),因此把这种机制叫做强制平坦段。
对于x64模式的gdt表来说,段描述符比x86系统少了很多,这是因为x64将侧重点放在了页保护而不是段保护。
关于64位段描述符的具体结构可以参考Intel开发手册卷3。下图为X64段描述符。其中,L位为0表示兼容模式(x86模式),为1则表示x64模式。
X64段描述符
X64段描述符
任务切换
在IA-32e模式下,TSS段描述符扩展到128位,用于满足寻址要求(普通段寄存器不再需要基址和界限)。
TSS段描述符不用来进行任务切换,而是主要保存一堆RSP的备用指针(当3环和0环发生任务切换时)。
FS / GS
在x64系统中,当处于0环时,FS不再指向KPCR,而是由GS指向KPCR,在3环时GS指向TEB。
中断门
x64不再支持调用门、陷阱门、任务门,一律只支持中断门。中断门描述符。
模式切换
中断:只使用一张IDT表,内核可以根据栈上的CS判断先前模式。
系统调用
只使用一张SSDT表。64位程序通过syscall进入内核。32位程序在ring3转入x64模式再进入内核。
32位程序进内核:32位程序在进入内核前,会先将环境转换成x64模式。
64位程序进内核:64位程序进内核的方式则要简单得多,直接调用syscall指令。
任务段:
中断门描述符:
规范寻址
一个规范形式的地址有64位, 0~63 位,IA‐32e 模式定义一个 64 位的线性地址,但实现的时候支持的位数要少些。第一个具有64 位扩展技术的 IA‐32e 结构的处理器将支持 48 位线性地址。这意味着规范的地址必须将位63 到位 48 全填 0 或全填 1,填 0 还是填 1 要看位 47 是 0 还是 1。
X64完结