9. x86-64指令系统

9.1 x86-64指令系统概述

背景

  • Intel最早推出的64位架构是基于超长指令字VLIW技术的 IA-64体系结构,Intel 称其为
    显式并行指令计算机EPIC( Explicitly Parallel Instruction Computer)。
  • 安腾和安腾2分别在2000年和2002年问世,它们是IA-64体 系结构的最早的具体实现,
    因为是一种全新的、与IA-32不 兼容的架构,所以,没有获得预期的市场份额。
  • AMD公司利用Intel在IA-64架构上的失败,抢先在2003年 推出兼容IA-32的64位版本指
    令集x86-64,AMD获得了以 前属于Intel的一些高端市场。AMD后来将x86-64更名为
    AMD64。
  • Intel在2004年推出IA32-EM64T,它支持x86-64指令集。 Intel为了表示EM64T的64位
    模式特点,又使其与IA-64有 所区别,2006年开始把EM64T改名为Intel64。

1. IA-32 与 x86-64 寄存器比较

(1)IA-32支持的数据类型及格式

《9. x86-64指令系统》

(2)x86-64中各类数据的长度

《9. x86-64指令系统》

2. x86-64的寄存器

(1)x86-64的通用寄存器

  • 新增8个64位通用寄存器(整数寄存器)
    R8、R9、R10、R11、R12、R13、R14、R15
     可作为8位(R8B~ R15B)、16位(R8W~ R15W)或 32位寄存器(R8D~R15D)使用
  • 所有GPRs都从32位扩充到64位
     ①8个32位通用寄存器 EAX、EBX、ECX、EDX、EBP、 ESP、ESI、EDI
      对应扩展寄存器分别为 RAX、RBX、 RCX、RDX、RBP、RSP、RSI、RDI
     ② 新增EBP、ESP、ESI和EDI低8位寄存器:BPL、SPL 、SIL和DIL
     ③可兼容使用原AH、BH、CH和DH寄存器 (使原来IA-32中的每个通用寄存器都可以是8位、16位、 32位和64位,如:SIL、SI、ESI、RSI)

(2)x86-64中寄存器的使用

  • 指令可直接访问16个64位寄存器:RAX、RBX、RCX、RDX、 RBP、RSP、RSI、RDI,以及R8~ R15
  • 指令可直接访问16个32位寄存器:EAX、EBX、ECX、EDX、EBP 、ESP、ESI、EDI,以及R8D~ R15D
  • 指令可直接访问16个16位寄存器:AX、BX、CX、DX、BP、SP、 SI、DI,以及R8W~ R15W
  • 指令可直接访问16个8位寄存器:AL、BL、CL、DL、BPL、SPL 、SIL、DIL,以及R8B~R15B
64位RAXRBXRCXRDXRBPRSPRSIRDIR8~ R15
32位EAXEBXECXEDXEBPESPESIEDIR8D~ R15D
16位AXBXCXDXBPSPSIDIR8W~ R15W
8位ALBLCLDLBPLSPLSILDILR8B~R15B
  • 为向后兼容,指令也可直接访问AH、BH、CH、DH
  • 通过寄存器传送参数,因而很多过程不用访问栈,因此,与IA-32 不同,x86-64不需要帧指针寄存器,即RBP可用作普通寄存器使用
  • 程序计数器为64位寄存器 RIP

(3)寄存器组织

IA-32的寄存器组织

《9. x86-64指令系统》

x86-64的寄存器组织
63320
%rax%eax返回值
%rbx%ebx被调用保护者
%rcx%ecx第四个参数
%rdx%edx第三个参数
%rsi%esi第二个参数
%rdi%edi第一个参数
%rbp%ebp被调用保护者
%rsp%esp堆栈指针
%r8%r8d第五个参数
%r9%r9d第六个参数
%r10%r10d调用保护者
%r11%r11d调用保护者
%r12%r12d被调用保护者
%r13%r13d被调用保护者
%r14%r14d被调用保护者
%r15%r15d被调用保护者
  • 通用寄存器个数从8个增加到16个, 宽度从32位增加到64位
  • 增加了%sil、 %dil、%bpl、 %spl 四个8位寄存器
  • %riw为16位 %rib为8位 (i=8~15)

3. x86-64的地址和寻址空间

  • 字长从32位变为64位,64位(8B)数据被称为一个四字(qw: quadword)
  • 逻辑地址最长可达为64位,即理论上可访问的存储空 间达264字节或16EB(ExaByte)
  • 编译器为指针变量分配64位(8B)
  • 基址寄存器变址寄存器都应使用64位寄存器
  • 但实际上,AMD和Intel的x86-64仅支持48位虚拟地 址,因此,程序的虚拟地址空间大小为248=256TB
    《9. x86-64指令系统》

4. x86-64的浮点寄存器

  • long double型数据虽然还采用80位(10B)扩展精度格式,但所分配存储空间从12B扩展为16B,即改为 16B对齐方式,但不管是分配12B还是16B,都只用到低10B
  • 128位的XMM寄存器从原来的 8个增加到16个,XMM0~XMM15
  • 浮点操作指令集采用基于SSE的面向XMM寄存器的指令集,而不采用基于浮点寄存器栈的x87 FPU 指令集
  • 浮点操作数存放在XMM寄存器

x86-64继承了IA-32中的8、16、32位通用寄存器和128位XMM寄存器 而取消了IA-32中的80位浮点寄存器栈ST(0)-ST(7)
《9. x86-64指令系统》
16个128位寄 存器%xmmi (i=0~15)

5. x86-64中数据的对齐

  • 各类型数据遵循一定的对齐规则,而且更严格
  • 存储器访问接口被设计成按8字节或16字节为单位进行存取,其对齐规则是,任何K字节宽的基本数据类型和指针类型数据的起始地址一定是K的倍数
数据类型对齐边界
short2字节
int、float4字节
long、double、指针8字节
long double16字节

9.2 x86-64的基本指令

传送指令
movabsq I, R:将64位立即数送64位通用寄存器
movq:传送一个64位的四字
movsbq、movswq、movslq:将源操作数进行符号扩展并传送到一个64位寄存器或存储单元中
movzbq、movzwq:将源操作数进行零扩展后传送到一个64位寄存器或存储单元中
movl:的功能相当于movzlq指令
pushq S:R[rsp]←R[rsp]-8; M[R[rsp]] ←S
popq D:D← M[R[rsp]]; R[rsp]←R[rsp]-8
算术逻辑指令
常规的算术逻辑运算指令只要将原来IA-32中的指令扩展到64位即可
addq / subq四字相加 / 四字相减
incq / decq四字加1 / 四字减1
imulq带符号整数四字相乘
orq64位相
salq64位算术左移
leaq有效地址 加载到64位寄存器
对于x86-64,还有一些特殊的算术逻辑运算指令
imulq SR[rdx]:R[rax]← S * R[rax] (64位*64位带符号整数)
mulq SR[rdx]:R[rax]← S * R[rax] (64位*64位无符号整数)
cltqR[rax] ← SignExtend(R[eax]) (将EAX内容符号扩展为四字
cltoR[rdx]:R[rax]← SignExtend(R[rax]) (符号扩展为八字
idivq SR[rdx] ← R[rdx]:R[rax]mod S (带符号整数相除、余数
R[rax] ← R[rdx]:R[rax]÷S (带符号整数相除、商
divq SR[rdx] ← R[rdx]:R[rax]mod S (无符号整数相除、余数
R[rax] ← R[rdx]:R[rax]÷S (无符号整数相除、商
上述功能描述中,R[rdx]:R[rax]是一个128位的八字(oct word)
比较和测试指令
与IA-32中比较和测试指令类似
cmpq S2, S1S1-S2 (64位数相减进行比较)
testq S2, S1S1∧S2 (64位数相与进行比较)
条件转移指令、条件传送指令、条件设置指令
都根据上述比较指令 和测试指令生成的标志进行处理

1. 数据传送指令

《9. x86-64指令系统》

2. 算术逻辑指令

例一

《9. x86-64指令系统》

例二 :不同长度操作数混合运算时,编译器必须选择正确的指令的组合。

《9. x86-64指令系统》

3. x86-64逆向工程举例

《9. x86-64指令系统》

9.3 x86-64的过程调用

IA-32与x86-64过程调用例子

程序代码:

long int sample(long int *xp, long int y)
{  
	long int t=*xp+y; 
	*xp=t; 
	reutrn t;
}

在x86-64/Linux平台上用以 下命令执行汇编操作,得到与 IA-32兼容的汇编指令代码
$ gcc –O1 –S –m32sample.c

sample:
pushl	%ebp 
movl	%esp,  %ebp 
movl	8(%ebp),  %edx 
movl	12(%ebp), %eax 
addl	(%edx), %eax 
movl	%eax, (%edx) 
popl	%ebp 
ret

在x86-64/Linux平台上用以 下命令执行汇编操作,得到 x86-64汇编指令代码
$ gcc –O1 –S –m64sample.c

sample: 
movq	%rsi, %rax 
addq	(%rdi), %rax 
movq	%rax, (%rdi) 
ret
  • 在x86-64/Linux 平台上默认生成 x86-64格式代码 ,故可省略-m64
  • Long型数据长度不同 参数传递方式不同

1. x86-64过程调用的参数传递

 ①通过通用寄存器传送参数,很多过程不用访问栈,故执行时间比IA-32代码更短
 ②最多可有6个整型或指针型参数通过寄存器传递
 ③超过6个入口参数时,后面的通过来传递
 ④在栈中传递的参数若是基本类型,则都被分配8个字节
 ⑤call(或callq)将64位返址保存在栈中之前,执行R[rsp]←R[rsp]-8
 ⑥ret从栈中取出64位返回地址后,执行R[rsp]←R[rsp]+8

操作数宽度
(字节)
123456返回参数
8RDIRSIRDXRCXR8R9RAX
4EDIESIEDXECXR8DR9DEAX
2DISIDXCXR8WR9WAX
1DILSILDLCLR8BR9BAL

2. x86-64过程调用的寄存器使用约定

 在过程(函数) 中尽量使用寄存器**RAX、 R10** 和 R11。 若使用 RBX、 RBP、R12、 R13、R14R15,则需要 将它们先保存在栈中再使用 ,最后返回前 再恢复其值.

3. x86-64过程调用举例

参数存放

《9. x86-64指令系统》

caller函数中部分指令(return语句前)

《9. x86-64指令系统》

test函数中部分指令

《9. x86-64指令系统》

caller函数中部分指令(return语句)

《9. x86-64指令系统》

4. IA-32和x86-64的比较

例子

《9. x86-64指令系统》

IA-32过程调用参数传递

《9. x86-64指令系统》

  • long double型数据虽然还采用80位(10B)扩展精度 格式,但所分配存储空间从12B扩展为16B,即改为16B对齐方式,但不管是分配12B还是16B,都只用到 低10B
  • 128位的XMM寄存器从原来的8个增加到16个
  • 浮点操作指令集采用基于SSE的面向XMM寄存器的指令集,而不采用基于浮点寄存器栈的x87 FPU 指令集
  • 浮点操作数存放在XMM寄存器中
x86-64过程调用参数传递

《9. x86-64指令系统》

    原文作者:WuDL.
    原文地址: https://blog.csdn.net/baidu_41667019/article/details/84678407
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞