32位汇编
调用约定 cdecl, stdcall, fastcall
cdecl
、stdcall
和 fastcall
是三种常见的函数调用约定,主要在参数传递、栈清理和寄存器使用方面有所不同。以下是它们的详细区别:
1. cdecl (C Declaration)
- 参数传递:参数从右到左压栈。
- 栈清理:由调用者清理栈。
- 寄存器使用:通常不使用寄存器传递参数。
- 返回值:通过
eax
返回。 - 可变参数:支持可变参数函数(如
printf
)。
示例:
void __cdecl func(int a, int b);
调用时:
push b
push a
call func
add esp, 8 ; 调用者清理栈
2. stdcall (Standard Call)
- 参数传递:参数从右到左压栈。
- 栈清理:由被调用者清理栈。
- 寄存器使用:通常不使用寄存器传递参数。
- 返回值:通过
eax
返回。 - 可变参数:不支持可变参数函数。
示例:
void __stdcall func(int a, int b);
调用时:
push b
push a
call func
; 被调用者清理栈
3. fastcall
- 参数传递:前两个参数通过
ecx
和edx
传递,其余从右到左压栈。 - 栈清理:由被调用者清理栈。
- 寄存器使用:使用
ecx
和edx
传递前两个参数。 - 返回值:通过
eax
返回。 - 可变参数:不支持可变参数函数。
示例:
void __fastcall func(int a, int b, int c);
调用时:
mov ecx, a
mov edx, b
push c
call func
; 被调用者清理栈
总结对比
特性 | cdecl | stdcall | fastcall |
---|---|---|---|
参数传递 | 从右到左压栈 | 从右到左压栈 | 前两个通过 ecx 、edx ,其余压栈 |
栈清理 | 调用者清理 | 被调用者清理 | 被调用者清理 |
寄存器使用 | 不使用 | 不使用 | 使用 ecx 、edx |
返回值 | eax | eax | eax |
可变参数 | 支持 | 不支持 | 不支持 |
适用场景
- cdecl:适用于需要可变参数的函数,如
printf
。 - stdcall:常用于 Windows API,简化调用者代码。
- fastcall:适用于性能要求高的场景,减少栈操作。
EFLAGS 寄存器
EFLAGS
是 x86 架构中的一个 32 位寄存器,用于存储处理器的状态和控制信息。它包含多个标志位,每个标志位反映了 CPU 操作的特定状态或控制某些操作的行为。EFLAGS
寄存器在程序执行、调试和系统控制中起着关键作用。
EFLAGS
的主要标志位
EFLAGS
寄存器包含多个标志位,以下是一些重要的标志位及其作用:
1. 状态标志(Status Flags)
这些标志反映算术和逻辑操作的结果。
- CF (Carry Flag):进位标志。表示无符号数运算的进位或借位。
- PF (Parity Flag):奇偶标志。表示结果中 1 的个数是否为偶数。
- AF (Auxiliary Carry Flag):辅助进位标志。用于 BCD 运算,表示低 4 位的进位或借位。
- ZF (Zero Flag):零标志。表示操作结果是否为零。
- SF (Sign Flag):符号标志。表示操作结果的符号(最高位)。
- OF (Overflow Flag):溢出标志。表示有符号数运算是否溢出。
2. 控制标志(Control Flags)
这些标志控制处理器的行为。
- DF (Direction Flag):方向标志。控制字符串操作的方向(
std
设置 DF=1,从高到低;cld
清除 DF=0,从低到高)。 - IF (Interrupt Enable Flag):中断允许标志。控制是否响应可屏蔽硬件中断。
- TF (Trap Flag):陷阱标志。用于单步调试,设置后每条指令执行后触发调试异常。
3. 系统标志(System Flags)
这些标志用于操作系统和系统管理。
- IOPL (I/O Privilege Level):I/O 特权级别。决定当前程序执行 I/O 指令的权限。
- NT (Nested Task):嵌套任务标志。表示当前任务是否嵌套在另一个任务中。
- RF (Resume Flag):恢复标志。用于调试异常后恢复执行。
- VM (Virtual 8086 Mode):虚拟 8086 模式标志。表示处理器是否运行在虚拟 8086 模式。
- AC (Alignment Check):对齐检查标志。启用时检查内存访问是否对齐。
- VIF (Virtual Interrupt Flag):虚拟中断标志。虚拟模式下的中断允许标志。
- VIP (Virtual Interrupt Pending):虚拟中断挂起标志。虚拟模式下是否有挂起的中断。
- ID (Identification Flag):标识标志。表示 CPU 是否支持
CPUID
指令。
EFLAGS
的作用
- 状态反馈:通过状态标志,程序可以判断算术和逻辑操作的结果(如是否为零、是否有进位等)。
- 流程控制:通过条件跳转指令(如
jz
、jnz
、jc
等),程序可以根据标志位改变执行流程。 - 系统管理:通过控制标志和系统标志,操作系统可以管理中断、调试和任务切换等。
示例
以下是一个简单的汇编代码示例,展示如何通过 EFLAGS
判断操作结果:
mov eax, 10
sub eax, 20 ; eax = -10
; 此时 EFLAGS 的状态:
; CF = 1(借位)
; ZF = 0(结果不为零)
; SF = 1(结果为负)
; OF = 0(无溢出)
总结
EFLAGS
寄存器是 x86 架构中用于存储处理器状态和控制信息的关键组件。通过状态标志、控制标志和系统标志,EFLAGS
在程序执行、调试和系统管理中发挥着重要作用。理解 EFLAGS
的标志位及其作用,对于编写和调试汇编代码至关重要。
EAX 寄存器
eax 经常作为函数的返回值,如果遇到
mov eax, 0
或者
xor eax, eax
这种情况,通常是将 eax 寄存器清零,以便作为函数的返回值。
跳转指令
相关信息
跳转指令通常与比较指令一起使用,用于根据比较结果执行不同的操作。
jmp 指令
jmp
指令用于无条件跳转到指定地址,是汇编语言中最基本的跳转指令。jmp
指令的语法如下:
jmp <目标地址>
jne 指令
jne
是根据 Zero Flag (ZF) 的状态决定是否跳转。如果 ZF 为 0(即上一条指令的结果不为零或不相等),则跳转到指定地址;否则,继续执行下一条指令。
jne <目标地址>
je 指令
je
是根据 Zero Flag (ZF) 的状态决定是否跳转。如果 ZF 为 1(即上一条指令的结果为零或相等),则跳转到指定地址;否则,继续执行下一条指令。
je <目标地址>
ret 指令
ret
指令用于从函数或子程序返回到调用者,并可带有可选的字节数参数,形式为 ret n
,其中 n
是一个立即数。
ret <n>
作用
无参数
ret
:- 从栈顶弹出返回地址,并跳转到该地址继续执行。
- 通常用于无参数函数的返回。
带参数
ret n
:- 除了弹出返回地址,还会将栈指针
esp
增加n
字节,用于清理调用者压入栈的参数。 - 常用于调用约定要求调用者清理栈的情况(如
stdcall
)。
- 除了弹出返回地址,还会将栈指针
具体来说:
ret
:- 弹出返回地址到
eip
。 esp
增加 4 字节(32 位地址)。
- 弹出返回地址到
ret n
:- 弹出返回地址到
eip
。 esp
增加n + 4
字节(4 字节用于地址,n
字节用于参数)。
- 弹出返回地址到
ret
:适用于调用者不清理栈的情况(如cdecl
调用约定)。ret n
:适用于被调用者清理栈的情况(如stdcall
调用约定)。
比较指令
cmp 指令
cmp
指令用于比较两个操作数的大小,实际上是通过减法来实现的,但不保存结果,只更新标志寄存器。cmp
指令的语法如下:
cmp dest, src
cmp会将 dest - src 的结果保存在标志寄存器中,但不保存结果到目标操作数。通常,cmp
指令与条件跳转指令(如 je
、jne
)一起使用,用于比较两个值并根据结果执行不同的操作。
根据dest - src的结果,cmp
指令会更新以下标志位:
标志位 | 名称 | 说明 |
---|---|---|
ZF | 零标志 | 如果结果为 0,则 ZF = 1;否则 ZF = 0。 |
SF | 符号标志 | 如果结果为负数,则 SF = 1;否则 SF = 0。 |
CF | 进位标志 | 如果操作导致借位(无符号数下溢),则 CF = 1;否则 CF = 0。 |
OF | 溢出标志 | 如果结果溢出(有符号数溢出),则 OF = 1;否则 OF = 0。 |
PF | 奇偶标志 | 如果结果的最低字节中有偶数个 1,则 PF = 1;否则 PF = 0。 |
AF | 辅助进位标志 | 如果结果的低 4 位向高 4 位有进位或借位,则 AF = 1;否则 AF = 0。 |
CMP常见的配合跳转指令
指令 | 说明 | 跳转条件 |
---|---|---|
JE | 等于时跳转 | ZF = 1 |
JNE | 不等于时跳转 | ZF = 0 |
JG | 大于时跳转(有符号数) | ZF = 0 且 SF = OF |
JGE | 大于或等于时跳转(有符号数) | SF = OF |
JL | 小于时跳转(有符号数) | SF ≠ OF |
JLE | 小于或等于时跳转(有符号数) | ZF = 1 或 SF ≠ OF |
JA | 大于时跳转(无符号数) | CF = 0 且 ZF = 0 |
JAE | 大于或等于时跳转(无符号数) | CF = 0 |
JB | 小于时跳转(无符号数) | CF = 1 |
JBE | 小于或等于时跳转(无符号数) | CF = 1 或 ZF = 1 |
条件设置指令
setne 指令
setne
指令用于根据 ZF 标志位的状态设置一个字节寄存器的值。如果 ZF 为 0(即上一条指令的结果不为零或不相等),则将目标寄存器的值设置为 1;否则,设置为 0。
setne al
- AL: 是 8 位寄存器 AX 的低 8 位部分。
因此,SETNE AL 的意思是:
如果 零标志(ZF) 为 0(即前一条比较或算术指令的结果不相等),则将 AL 设置为 1。
如果 零标志(ZF) 为 1(即前一条比较或算术指令的结果相等),则将 AL 设置为 0。