pwn学习小记(1)
PWN学习小记(1)
汇编基础
CPU结构和指令集
CPU是名称为中央处理单元,简称处理器,主要的作用是从内存中读取指令,然后解码和执行。
CPU架构就是CPU内部设计的结构,是一堆硬件组成,用于实现指令集所规定的操作,指令集包含了一系列的操作码以及特定的CPU执行的基本命令。根据现在指令集的特征,可以分为两类:CSSC(复杂指令集计算机,代表的是x86处理器)和RISC(精简指令集计算机,典型代表是ARM处理器)。RISC可以完全使用寄存器来传递参数,而CISC只能使用栈,或者是栈和寄存器结合起来使用
语法风格
x86汇编主要的语法有两种风格: AT&T和Intel风格
通常在Linux中见到的AT&T的风格比较多,常见的GCC、GDB、objdump都是使用的AT&T风格,他们两个之间语法有一些简单的区别,我们主要了解Intel风格
有很多细节的不同,AT&T的特征是寄存器和数字的前面通常会加入% $的符号,Intel没有,十六进制使用的是0x而Intel使用的是后方计入h
寄存器
寄存器是 CPU 内部用于存放数据、地址和状态信息的高速存储单元,是 CPU 进行计算和控制的核心组成部分。
下面是8到64位寄存器的实例和说明
| 位数 | 通用寄存器示例 | 说明 |
|---|---|---|
| 8 位 | AL, AH, BL, BH, CL, CH, DL, DH | 8086 中 AX、BX、CX、DX 的高 8 位/低 8 位拆分 |
| 16 位 | AX, BX, CX, DX, SP, BP, SI, DI | 8086 基本寄存器:累加、基址、计数、数据、堆栈指针、基址指针、变址寄存器 |
| 32 位 | EAX, EBX, ECX, EDX, ESP, EBP, ESI, EDI | IA-32 架构:E 前缀表示扩展为 32 位 |
| 64 位 | RAX, RBX, RCX, RDX, RSP, RBP, RSI, RDI,及 R8–R15 | x86-64 架构:R 前缀表示 64 位,新增 R8–R15 共 16 个通用寄存器 |
补充:
- 在 64 位模式下,默认操作数大小是 32 位,如果要使用 64 位操作数,需要在指令中加上 REX 前缀(由汇编器自动生成)。
- R8–R15 的低 32 位、16 位和 8 位也能单独访问(如 R8D, R8W, R8B)。
常量表示方法
(1)整数常量
- 十进制(默认):直接写,例如
1234。 - 十六进制:x86 汇编常用写法是 后缀 h,如
1Ah,如果首位是字母要在前加0,如0ABCh。- C 语言风格:
0x1A。
- C 语言风格:
- 八进制:前缀
0,如0123(等于十进制 83)。 - 二进制:某些汇编器支持后缀
b,如1010b。
(2)浮点数常量(实数常量)
- 必须有整数部分和小数部分至少一部分,比如:
1.00.53.(整数部分+小数点).25(小数点+小数部分)
- 可以带正负号:
+2.3,-3.14159。 - 科学计数法:
26.E3(即 26 × 10³)。
(3)字符串常量
用 单引号
'或 双引号"包裹:'abc'或"abc"
在汇编中字符串常量通常是 一串 ASCII 字符,比如:
1
msg db 'Hello, world!', 0这里
db定义字节数组,最后的0代表字符串结束符(C 风格)。
常见寄存器
32位CPU
1. 通用寄存器
4 个最基本的通用寄存器,每个 32 位,可以拆分访问:
- EAX:累加器(Accumulator),常用于算术运算、函数返回值。
- 高 16 位:AX → AH(高 8 位)、AL(低 8 位)
- EBX:基址寄存器(Base Register),常用作内存访问基址。
- ECX:计数寄存器(Counter Register),常用于循环、移位/旋转操作。
- EDX:数据寄存器(Data Register),常配合 EAX 参与乘除法运算,高位存放结果。
特点:虽然称为“通用”,但在指令集里常有特定用途。
2. 段寄存器
8086 的分段机制在 32 位中仍保留,但在保护模式下意义有所变化。共有 6 个段寄存器,每个 16 位:
- CS:代码段寄存器(Code Segment)
- DS:数据段寄存器(Data Segment)
- SS:堆栈段寄存器(Stack Segment)
- ES:附加数据段寄存器(Extra Segment)
- FS:附加段寄存器(常用于线程本地存储 TLS 等)
- GS:附加段寄存器(在操作系统和多任务环境中常有特殊用途)
在 32 位保护模式中,这些寄存器通常保存的是“段选择子”,对应 GDT/LDT 表项。
3. 变址寄存器
- ESI(Extended Source Index):扩展源变址寄存器
- EDI(Extended Destination Index):扩展目的变址寄存器
主要用于数组、字符串、内存块操作,常与 MOVSx、CMPSx、SCASx 等字符串指令配合。
其低 16 位与 8086 的 SI、DI 兼容。
4. 指针寄存器
- ESP(Extended Stack Pointer):堆栈指针寄存器,指向当前栈顶。
- EBP(Extended Base Pointer):基址指针寄存器,常用于访问栈帧中的参数和局部变量。
5. 指令指针寄存器
EIP(Extended Instruction Pointer):保存下一条即将执行指令的偏移地址。
CPU 自动更新 EIP,程序员一般不能直接修改(必须用跳转、调用、返回等指令间接修改)。
6. 标志寄存器
32 位的标志寄存器,存放算术/逻辑运算的状态信息和控制标志。
常见标志位:
- ZF:零标志(Zero Flag)
- CF:进位标志(Carry Flag)
- SF:符号标志(Sign Flag)
- OF:溢出标志(Overflow Flag)
- IF:中断允许标志(Interrupt Flag)
- DF:方向标志(Direction Flag)
| 类别 | 寄存器 | 说明 |
|---|---|---|
| 通用寄存器 | EAX, EBX, ECX, EDX | 算术、逻辑、计数、基址等 |
| 段寄存器 | CS, DS, SS, ES, FS, GS | 分段寻址(保护模式下为选择子) |
| 变址寄存器 | ESI, EDI | 数组、字符串、块操作 |
| 指针寄存器 | ESP, EBP | 栈顶指针、栈帧基址 |
| 指令指针寄存器 | EIP | 指向下一条指令 |
| 标志寄存器 | EFLAGS | 记录结果状态、控制流程 |
16位CPU
8086 是经典的 16 位 CPU,内部所有寄存器宽度为 16 位。它的寄存器主要分为以下几类:
(1)通用寄存器
共有 4 个,每个 16 位,可以再分为高 8 位和低 8 位:
- AX(累加器,Accumulator) → AH(高 8 位)、AL(低 8 位)
- BX(基址寄存器,Base) → BH、BL
- CX(计数寄存器,Count) → CH、CL
- DX(数据寄存器,Data) → DH、DL
作用:
- AX 常用于算术逻辑运算,很多指令默认使用它。
- BX 常用作内存寻址的基址。
- CX 常用作循环/移位的计数器(比如
LOOP指令)。 - DX 常用于乘法、除法的扩展寄存器。
(2)段寄存器
8086 采用 分段存储管理,物理地址 = 段基址(段寄存器 ×16) + 偏移量。
- CS(代码段寄存器)
- DS(数据段寄存器)
- SS(堆栈段寄存器)
- ES(附加段寄存器)
用途:
- CS:IP 指定当前正在执行的指令地址。
- SS:SP 指定当前栈顶位置。
- DS、ES 常用于数据访问,ES 常配合字符串操作。
(3)指针与变址寄存器
- SP(栈指针,Stack Pointer):指向栈顶,配合 SS。
- BP(基址指针,Base Pointer):主要用于访问栈中的参数和局部变量。
- SI(源变址寄存器,Source Index):常用于数组、字符串操作(数据源)。
- DI(目的变址寄存器,Destination Index):常用于数组、字符串操作(数据目的地)。
(4)指令指针寄存器
- IP(Instruction Pointer):保存下一条要执行指令在代码段内的偏移量。
和 CS 配合:CS:IP 就是当前指令的物理地址。
(5)标志寄存器
8086 的 FLAGS 寄存器(16 位) 保存运算状态和控制信息。
常见标志位:
- ZF(Zero Flag,零标志)
- CF(Carry Flag,进位标志)
- SF(Sign Flag,符号标志)
- OF(Overflow Flag,溢出标志)
- IF(Interrupt Enable,中断允许)
- DF(Direction Flag,字符串操作方向)
| 类别 | 寄存器 | 说明 |
|---|---|---|
| 通用寄存器 | AX, BX, CX, DX(可拆 AH/AL 等) | 存放操作数、结果,部分有专用用途 |
| 段寄存器 | CS, DS, SS, ES | 用于分段寻址,提供段基址 |
| 指针寄存器 | SP, BP | 指向栈顶、基址(栈帧) |
| 变址寄存器 | SI, DI | 用于字符串、数组操作 |
| 指令指针寄存器 | IP | 存放下一条指令偏移地址 |
| 标志寄存器 | FLAGS | 存放状态和控制标志 |
AX、BX、CX、DX → CPU 的“算术草稿本”。
CS、DS、SS、ES → 指定不同“书本章节”(代码段、数据段、栈段)。
SP、BP、SI、DI → 用来在“书中”翻页找具体数据。
IP → 当前读到的“下一句”。
FLAGS → CPU 计算结果的“批注标记”。
64位CPU
64 位寄存器架构在 32 位的基础上扩展了通用寄存器数量与宽度,弱化了段寄存器功能,同时引入了更强大的 SIMD 寄存器体系。
1. 通用寄存器
在 x86-64 架构中,共有 16 个 64 位通用寄存器:
- 原有的 8 个寄存器(由 32 位扩展而来):
- RAX, RBX, RCX, RDX, RSP, RBP, RSI, RDI
- 新增的 8 个寄存器:
- R8, R9, R10, R11, R12, R13, R14, R15
特点:
- 每个寄存器都是 64 位,并且可以分段访问:
- RAX → EAX (32位) → AX (16位) → AH/AL (8位)
- R8 → R8D (32位) → R8W (16位) → R8B (8位)
- 使用方式完全兼容旧的 32 位和 16 位寄存器。
2. 指针与栈相关寄存器
- RSP(Stack Pointer):栈顶指针
- RBP(Base Pointer):栈基址指针,常用于函数调用栈帧
- RIP(Instruction Pointer):指向下一条即将执行的指令地址(在 64 位模式下是 64 位宽度)。
3. 段寄存器
虽然 x86-64 仍然保留了段寄存器(CS、DS、SS、ES、FS、GS),
但在 64 位长模式下,大部分段机制被弱化:
- CS、DS、SS、ES 基本被固定使用,几乎不起作用。
- FS、GS 仍然有用,常用于存放 线程本地存储(TLS) 或操作系统内核结构的基地址。
4. 标志寄存器
- 64 位扩展的 RFLAGS(以前是 EFLAGS/FLAGS),保存运算状态与控制标志。
- 常见标志位:
- CF(进位标志)、ZF(零标志)、SF(符号标志)、OF(溢出标志)、IF(中断允许)、DF(方向标志)等。
5. 控制寄存器
- CR0, CR2, CR3, CR4, CR8 等,用于存储分页机制、虚拟内存、保护模式和特权级信息。
- CR3:页目录基地址寄存器(内存分页关键)。
- CR8:任务优先级控制(TLP)。
6. SIMD/浮点寄存器
除了通用寄存器外,64 位 CPU 还包含丰富的 SIMD 和浮点扩展寄存器:
- XMM0–XMM15(128 位,SSE 指令集)
- YMM0–YMM15(256 位,AVX 指令集)
- ZMM0–ZMM31(512 位,AVX-512 指令集,部分 CPU 支持)
- x87 FPU 寄存器栈(ST0–ST7,80 位浮点寄存器,兼容旧代码)
| 类别 | 64 位寄存器 | 说明 |
|---|---|---|
| 通用寄存器 | RAX, RBX, RCX, RDX, RSI, RDI, RBP, RSP, R8–R15 | 16 个通用寄存器,64 位宽 |
| 指令指针 | RIP | 下一条执行指令地址 |
| 段寄存器 | CS, DS, SS, ES, FS, GS | 长模式下大多弱化,FS/GS 仍常用 |
| 标志寄存器 | RFLAGS | 状态/控制标志 |
| 控制寄存器 | CR0, CR2, CR3, CR4, CR8… | 内存管理、分页、特权控制 |
| SIMD/浮点寄存器 | XMM0–XMM15, YMM0–YMM15, ZMM0–ZMM31, ST0–ST7 | 向量计算、浮点运算 |
- 通用寄存器:CPU 的“草稿纸”。
- RIP:指令“书签”,告诉 CPU 读下一条指令。
- RSP/RBP:函数调用的“栈工具”。
- FS/GS:线程局部数据“起点”。
- SIMD 寄存器:并行计算的“高速计算器”。
基本指令
不同 CPU 有不同的指令集架构(Instruction Set Architecture, ISA)。汇编语言就是 ISA 的最底层表现形式,因此 CPU 架构不同,汇编指令也不同。所以汇编语言不是通用的,它紧密依赖 CPU 的指令集架构。换 CPU,汇编指令就会变化。
16位CPU的基本命令
16 位汇编指令(8086 指令集)是 x86 架构的最早版本,主要包括数据传送、算术逻辑、控制转移、字符串处理和处理机控制五大类。
1. 数据传送指令
| 指令 | 解释 |
|---|---|
| MOV dst, src | 数据传送,把 src 的值送到 dst |
| XCHG a, b | 交换两个寄存器/内存操作数的值 |
| PUSH reg/mem/imm | 将操作数压入栈,SP 自动减 2 |
| POP reg/mem | 将栈顶数据弹出到目的操作数,SP 自动加 2 |
| IN AL/AX, port | 从端口读入数据到 AL/AX |
| OUT port, AL/AX | 将 AL/AX 的内容输出到端口 |
| LEA reg, mem | 装入有效地址(即偏移量),常用于指针运算 |
| LDS reg, mem | 从内存中取一个 32 位的指针(段:偏移)到寄存器和 DS |
| LES reg, mem | 同上,但段寄存器为 ES |
2. 算术运算指令
| 指令 | 解释 |
|---|---|
| ADD dst, src | 加法,dst = dst + src |
| ADC dst, src | 带进位加法,dst = dst + src + CF |
| SUB dst, src | 减法,dst = dst - src |
| SBB dst, src | 带借位减法,dst = dst - src - CF |
| INC reg/mem | 自增 1 |
| DEC reg/mem | 自减 1 |
| MUL reg/mem | 无符号乘法,隐含操作数 AX;结果放在 AX 或 DX:AX |
| IMUL reg/mem | 有符号乘法 |
| DIV reg/mem | 无符号除法,结果:商在 AL/AX,余数在 AH/DX |
| IDIV reg/mem | 有符号除法 |
| NEG reg/mem | 取负,等于 0 - 操作数 |
| CMP a, b | 比较(执行 a - b,结果不保存,只修改标志位) |
3. 逻辑与位操作指令
| 指令 | 解释 |
|---|---|
| AND a, b | 按位与 |
| OR a, b | 按位或 |
| XOR a, b | 按位异或 |
| NOT a | 按位取反 |
| TEST a, b | 按位与,但结果不保存,仅修改标志位 |
| SHL/SAL a, n | 左移(逻辑/算术左移相同),低位补 0 |
| SHR a, n | 逻辑右移,高位补 0 |
| SAR a, n | 算术右移,高位补符号位 |
| ROL a, n | 循环左移 |
| ROR a, n | 循环右移 |
| RCL a, n | 带进位循环左移 |
| RCR a, n | 带进位循环右移 |
4. 控制转移指令
| 指令 | 解释 |
|---|---|
| JMP label | 无条件跳转到指定地址 |
| CALL label | 调用子程序(压栈返回地址,然后跳转) |
| RET | 从子程序返回(弹出返回地址并跳转) |
| INT n | 触发软件中断 n(调用中断向量表中的服务程序) |
| IRET | 从中断返回(恢复标志、CS、IP) |
条件跳转(根据标志位决定是否跳转):
| 指令 | 条件(FLAGS) |
|---|---|
| JE/JZ | 等于 / 零标志 ZF=1 |
| JNE/JNZ | 不等 / 非零 ZF=0 |
| JC | 有进位 CF=1 |
| JNC | 无进位 CF=0 |
| JS | 负数 SF=1 |
| JNS | 非负数 SF=0 |
| JO | 溢出 OF=1 |
| JNO | 无溢出 OF=0 |
| JG/JNLE | 大于(有符号)ZF=0 且 SF=OF |
| JL/JNGE | 小于(有符号)SF≠OF |
| JGE/JNL | 大于等于(有符号)SF=OF |
| JLE/JNG | 小于等于(有符号)ZF=1 或 SF≠OF |
| JA | 大于(无符号)CF=0 且 ZF=0 |
| JB | 小于(无符号)CF=1 |
| JAE | 大于等于(无符号)CF=0 |
| JBE | 小于等于(无符号)CF=1 或 ZF=1 |
5. 字符串操作指令
和 SI(源)、DI(目的)、CX(计数器)、DF(方向标志) 联用,常配合 REP/REPE/REPNE。
| 指令 | 解释 |
|---|---|
| MOVS/MOVSB/MOVSW | 从 DS:SI 指向的内存传送到 ES:DI,自动更新 SI/DI |
| CMPS/CMPSB/CMPSW | 比较 DS:SI 和 ES:DI 的数据 |
| SCAS/SCASB/SCASW | 用 AL/AX 与 ES:DI 的数据比较 |
| LODS/LODSB/LODSW | 取 DS:SI 的数据放到 AL/AX |
| STOS/STOSB/STOSW | 把 AL/AX 的数据存到 ES:DI |
前缀:
- REP:重复执行,直到 CX=0
- REPE/REPZ:当相等且 CX≠0 时继续
- REPNE/REPNZ:当不相等且 CX≠0 时继续
6. 处理机控制指令
| 指令 | 解释 |
|---|---|
| CLC | 清除进位标志 CF=0 |
| STC | 置位进位标志 CF=1 |
| CMC | 进位标志取反 CF=¬CF |
| CLD | 清除方向标志 DF=0(字符串操作递增) |
| STD | 设置方向标志 DF=1(字符串操作递减) |
| CLI | 禁止中断 IF=0 |
| STI | 允许中断 IF=1 |
| HLT | 处理器停止,等待中断/复位唤醒 |
| NOP | 空操作(占位用,不做任何事) |
32位CPU的基本命令
1. 数据传送指令
和 16 位类似,只是寄存器换成 32 位(EAX、EBX、ECX、EDX、ESI、EDI、ESP、EBP):
| 指令 | 解释 |
|---|---|
| MOV dst, src | 传送数据 |
| XCHG a, b | 交换两个操作数 |
| PUSH/POP reg/mem/imm32 | 压栈/出栈,ESP 自动 ±4 |
| PUSHAD / POPAD | 压入/弹出所有 32 位通用寄存器 |
| IN/OUT | I/O 端口读写 |
| LEA reg, [mem] | 装入有效地址(偏移量) |
| MOVZX/MOVSX | 零扩展/符号扩展传送(从小操作数 → 大操作数) |
2. 算术运算指令
基本和 16 位一致,只是操作数可以是 32 位。
| 指令 | 解释 |
|---|---|
| ADD / ADC | 加 / 带进位加 |
| SUB / SBB | 减 / 带借位减 |
| INC / DEC | 自增 / 自减 |
| MUL / IMUL | 乘法(无符号 / 有符号) |
| DIV / IDIV | 除法(无符号 / 有符号) |
| NEG | 取负 |
| CMP | 比较(结果不保存,仅改标志位) |
新增:
- BSWAP reg32:字节反转(如
12345678h → 78563412h) - IMUL reg, imm32:支持立即数乘法,简化了算式编译
3. 逻辑与位操作指令
和 16 位相同,但支持 32 位操作数,另外增加了一些 位扫描/测试类:
| 指令 | 解释 |
|---|---|
| AND / OR / XOR / NOT | 与 / 或 / 异或 / 取反 |
| TEST | 按位与,只改标志 |
| SHL/SAL / SHR / SAR | 移位 |
| ROL / ROR / RCL / RCR | 循环移位(可带进位) |
| BT / BTS / BTR / BTC | 位测试、置位、复位、取反(Bit Test & Set/Reset/Complement) |
| BSF / BSR | 找到第一个/最后一个置位的比特位 |
4. 控制转移指令
| 指令 | 解释 |
|---|---|
| JMP label | 无条件跳转 |
| CALL / RET | 子程序调用与返回 |
| INT n / IRET | 中断调用与返回 |
| LOOP/LOOPZ/LOOPNZ | 用 ECX 循环计数器控制的循环跳转 |
| 条件跳转 Jcc | 和 16 位一样,支持 JE/JNE/JG/JL/JA/JB 等 |
特点:
- 偏移量可以是 8 位或 32 位(16 位时代是 8 位或 16 位)。
- CALL / JMP 支持远指针(段:偏移),但保护模式下主要用选择子(Selector)。
5. 字符串操作指令(增强)
继承 16 位的串操作,但寄存器扩展为 32 位:
| 指令 | 解释 |
|---|---|
| MOVSD | 传送双字(4 字节)串 |
| STOSD | 存储双字 |
| LODSD | 装入双字 |
| SCASD | 扫描双字 |
| CMPSD | 比较双字 |
可配合 REP/REPE/REPNE,和 16 位类似。
6. 处理机控制指令
在 80386 引入了保护模式,因此多了一些控制类指令:
| 指令 | 解释 |
|---|---|
| CLC/STC/CMC | 清除/设置/取反进位标志 |
| CLD/STD | 清除/设置方向标志 |
| CLI/STI | 禁止/允许中断 |
| HLT | 停机,等待中断 |
| NOP | 空操作 |
| LGDT / SGDT | 加载/保存全局描述符表寄存器 GDTR |
| LIDT / SIDT | 加载/保存中断描述符表寄存器 IDTR |
| LLDT / SLDT | 加载/保存局部描述符表寄存器 LDTR |
| LTR / STR | 加载/保存任务寄存器 TR |
| MOV CRx, reg / MOV reg, CRx | 控制寄存器 CR0–CR4 的访问(分页、保护模式相关) |
64位CPU的基本指令
1. 数据传送指令
| 指令 | 说明 |
|---|---|
| MOV r64, r/m64 / imm64 | 数据传送,支持 64 位立即数 |
| XCHG r64, r/m64 | 交换两个操作数 |
| PUSH r64 / POP r64 | 压栈/出栈(栈指针 RSP ± 8) |
| PUSHFQ / POPFQ | 压栈/出栈 RFLAGS(64 位) |
| LEA r64, [mem] | 取内存有效地址(常用于指针/数组计算) |
| MOVZX / MOVSX / MOVSXD | 零扩展/符号扩展(特别是 MOVSXD 从 32 位 → 64 位) |
2. 算术运算指令
| 指令 | 说明 |
|---|---|
| ADD / ADC | 加 / 带进位加 |
| SUB / SBB | 减 / 带借位减 |
| INC / DEC | 自增 / 自减 |
| NEG | 取负 |
| CMP | 比较(只修改标志位) |
| MUL / IMUL | 乘法(无符号/有符号) |
| DIV / IDIV | 除法(无符号/有符号) |
| BSWAP r64 | 字节反转(大端/小端转换) |
| POPCNT | 统计 1 的个数(SSE4.2+) |
| LZCNT / TZCNT | 前导零/尾随零计数(BMI 指令集) |
3. 逻辑与位操作指令
| 指令 | 说明 |
|---|---|
| AND / OR / XOR / NOT | 按位逻辑运算 |
| TEST | 按位与,不保存结果,仅改标志 |
| SHL / SHR / SAR | 移位(逻辑/算术) |
| ROL / ROR / RCL / RCR | 循环移位(可带进位) |
| BT / BTS / BTR / BTC | 位测试/置位/复位/取反 |
| BSF / BSR | 位扫描(找第一个/最后一个 1 位) |
4. 控制转移指令
| 指令 | 说明 |
|---|---|
| JMP label | 无条件跳转(支持 64 位地址) |
| CALL / RET | 调用子程序 / 返回 |
| INT n / IRETQ | 软件中断 / 中断返回 |
| Jcc | 条件跳转(JE/JNE/JG/JL/JA/JB 等) |
| LOOP / LOOPE / LOOPNE | 用 RCX 控制循环 |
| RIP 相对寻址 | 64 位特有,用于位置无关代码(PIC) |
5. 字符串操作指令(64 位版本)
| 指令 | 说明 |
|---|---|
| MOVSQ | 传送 8 字节(Quadword) |
| STOSQ | 存储 8 字节 |
| LODSQ | 装入 8 字节 |
| SCASQ | 扫描 8 字节 |
| CMPSQ | 比较 8 字节 |
| REP/REPE/REPNE 前缀 | 串操作重复执行(由 RCX 控制次数) |
6. 处理机控制指令
| 指令 | 说明 |
|---|---|
| CLC / STC / CMC | 清除/设置/取反进位标志 |
| CLD / STD | 清除/设置方向标志 |
| CLI / STI | 禁止/允许中断 |
| HLT | 停机,等待中断 |
| NOP | 空操作 |
| SYSCALL / SYSRET | 系统调用/返回(64 位新增) |
| SYSENTER / SYSEXIT | 快速系统调用 |
| SWAPGS | 用户态/内核态切换时交换 GS 基址 |
| CPUID | 查询 CPU 信息 |
| RDMSR / WRMSR | 读/写 MSR(模型特定寄存器) |
| RDTSC / RDTSCP | 读取时间戳计数器 |
C与汇编
下面是一个很简单的一个加法功能实现
1 | |
这是所对应的汇编命令:
1 | |
C 代码:
1 | |
汇编(反汇编片段):
1 | |
解释:
rdi和rsi在 x86-64 System V ABI 中是 前两个整数参数(a和b)。lea (%rdi,%rsi,1),%eax计算rdi + rsi,结果放进eax(函数返回值寄存器)。retq返回。
C 代码:
1 | |
汇编关键部分:
1 | |
结合 C 和汇编的理解
- C 里的局部变量
a, b→ 栈上分配sub $0x10,%rsp给a和b留了空间&a→rsp&b→rsp+4
- scanf(“%d”, &a) → 调用外部函数
%d的格式字符串地址放到rdi(第1参数)&a放到rsi(第2参数)- 然后
call __isoc99_scanf@plt
- 函数调用规范(System V AMD64 ABI)
- 前 6 个整数参数:
rdi, rsi, rdx, rcx, r8, r9 - 返回值:放在
rax
- 前 6 个整数参数:
- sum(a, b) → 内联优化
sum的实现就是a+b- 编译器可能直接用
lea计算了,不会真的存储调用结果(因为没用它)。