汇编笔记
基本概念
机器语言(machinelanguage)是一种数字语言,专门设计成能被计算机处理器(CPU)理解。所有x86处理器都理解共同的机器语言。
汇编语言(assemblylanguage)包含用短助记符如ADD、MOV、SUB和CALL书写的语句。汇编语言与机器语言是一对一(one-to-one)的关系:每一条汇编语言指令对应一条机器语言指令。
汇编器(assembler)是一种工具程序,用于将汇编语言源程序转换为机器语言。
**常用的汇编编译器 **有MASM( Microsoft 宏汇编器)、TASM(Turbc汇编器),NASM(NetWide汇编器)和MASM32(MASM的一种变体)。GAS(GNU汇编器)和NASM是两种基于Linux的汇编器。在这些汇编器中,NASM的语法与MASM的最相似。
链接器(linker)是一种工具程序,它把汇编器生成的单个文件组合为一个可执行程序。
高级语言如Python、C++和Java与汇编语言和机器语言的关系是一对多(one-to-many)。比如,C++的一条语句就会扩展为多条汇编指
令或机器指令。
X86处理器
中央处理单元(CPU)处理算术和逻辑运算。它包含了有限数量的存储位置,即寄存器;一个高频时钟用于同步其操作;一个控制单元和一个算术逻辑单元。内存存储单元在计算机程序运行时,保存指令和数据。总线是一组并行线路,在计算机不同部件之间传输数据。

32位x86处理器
基本程序运行寄存器由4类寄存器组成。具体包括8个通用寄存器,6个段寄存器,1个EFLAGS寄存器(标志寄存器)以及1个指令指针寄存器。
- 通用寄存器主要用于算术运算、数据传输和逻辑操作。
- 段寄存器存放预先分配的内存区域的基址,这些内存区域就是段。
- 标志寄存器包含的独立二进制位用于控制CPU的操作,并反映ALU操作的结果。
- 指令指针寄存器存放的是下一条要执行指令的地址。
其他寄存器还包括控制寄存器(CR0、CR1、CR2、CR3)、调试寄存器(DR0-DR7)、系统地址寄存器、 浮点寄存器等。
通用寄存器是一种通用型的寄存器,用于传送和暂存数据,也可参与算数逻辑运算,并保存计算结果。
| 寄存器 | 名称 | 32位 | 16位 | 高8位 | 低8位 | 主要用途 |
|---|---|---|---|---|---|---|
| EAX | 累加器 | EAX | AX | AH | AL | 针对操作数和结果数据 |
| EBX | 基址寄存器 | EBX | BX | BH | BL | DS段中的数据指针 |
| ECX | 计数器 | ECX | CX | CH | CL | 字符串和循环操作 |
| EDX | 数据寄存器 | EDX | DX | DH | DL | I/O指针 |
| ESI | 源变址寄存器 | ESI | SI | - | - | 字符串操作源指针 |
| EDI | 字符串操作目标指针 | EDI | DI | - | - | 字符串操作目标寄存器 |
| EBP | 扩展基址指针寄存器 | EBP | BP | - | - | SS段中栈内数据指针 |
| ESP | 栈指针寄存器 | ESP | SP | - | - | 栈指针寄存器 |
段寄存器 IA-32 的保护模式中,段是一种内存保护技术,它把内存划分为多个区段,并为每个区段赋予起始地址、范围、访问权限等,以保护内存。此外同分页技术一起用于将虚拟内存变更为实际物理内存。段内存记录在 SDT(Segment Descriptor Table,段描述符表)中,而段寄存器就持有这些 SDT 的索引。
| 寄存器 | 名称 | 用途 |
|---|---|---|
| CS | Code Segment | 代码段寄存器 |
| SS | Stack Segment | 栈段寄存器 |
| DS | Data Segment | 数据段寄存器 |
| ES | Extra ( Data ) Segment | 附加(数据)段寄存器 |
| FS | Data Segment | 数据段寄存器 |
| GS | Data Segment | 数据段寄存器 |
EFLAGS寄存器 包含了独立的二进制位,主要用于反映处理器的状态和ALU运算结果的某些特征及控制指令的执行。
| 标志位(外语缩写) | 标志位名称 | 1 | 0 |
|---|---|---|---|
| CF | 进位标志 | 进位 | 无进位 |
| PF | 奇偶标志 | 偶 | 奇 |
| AF | 辅助进位标志 | 进位 | 无进位 |
| ZF | 零标志 | 等于零 | 不等于零 |
| SF | 符号标志 | 负 | 非负 |
| TF | 跟踪标志 | 单步工作方式 | 连续工作方式 |
| IF | 中断标志 | 允许 | 禁止 |
| DF | 方向标志 | 减少 | 增加 |
| OF | 溢出标志 | 溢出 | 未溢出 |
指令指针寄存器 EIP(Instruction Pointer),用于指向下一条要执行的指令。
64位x86-64处理器
x86-64处理器向下兼容x86指令集,且比x86处理器多了8个通用寄存器(R8到R15)。
x86-64通用寄存器RAX、RBX、RCX、RDX、RDI、RSI、RBP、RSP、R8、R9、R10、R11、R12、R13、R14、R15。
处理器架构
IA32和x86
IA-32为Intel Architecture 32bit简称,即英特尔32位体系架构,在英特尔公司1985年推出的80386微处理器中首先采用。通常也被称为i386、x86-32、x86等。
x86-64、x64、AMD64、Intel64
后来AMD推出了兼容32位的64位集关于IA-32的扩展,之后改名为AMD64,Intel后来也采用该架构,在其文档中称为Intel64。同时,该架构又被称为x86-64或者x64。
以上都属于x86架构,使用CISC(Complex Instruction Set Computer,复杂指令集计算机)。
其他主流cpu架构还包括ARM、RISC-V、MIPS。
IA64是后来intel和惠普联合推出的64位体系架构,但是不兼容原有的32位体系结构的应用程序,导致市场惨淡。不属于x86架构。
汇编语言基础
1 | |
用汇编语言编写的源程序不能直接在其目标计算机上执行,必须通过汇编将其转换为可执行代码。汇编器生成包含机器语言的文件,称为目标文件(object file)。这个文件还没有准备好执行,它还需传递给一个被称为链接器(linker)的程序,从而生成可执行文件(executable file)。编辑、汇编、链接和执行汇编语言程序的过程,如下:、
- 步骤1:编程者用编辑器创建一个ASCIl文本文件,称之为源文件。
- 步骤2:汇编器读取源文件,并生成目标文件,即对程序的机器语言翻译。或者,它也会生成列表文件。只要出现任何错误,编程者就必须返回步骤1,修改程序。
- 步骤3:链接器读取并检查目标文件,以便发现该程序是否包含了任何对链接库中过程的调用。链接器从链接库中复制任何被请求的过程,将它们与目标文件组合,以生成可执行文件。
- 步骤4:操作系统加载程序将可执行文件读入内存,并使CPU分支到该程序起始地址,然后程序开始执行。

列表文件(listingfile)包括了程序源文件的副本,再加上行号、每条指令的数字地址、每条指令的机器代码字节(十六进制)以及符号表。符号表中包含了程序中所有标识符的名称、段和相关信息。
传递参数
x86-32时,使用栈传递参数。程序中的参数会以从右往左的方向依次压入栈中。这也是方便出栈时,左边第一个参数最先出来。
x86-64时,前6个基础类型参数通过寄存器传递,第7个参数开始用栈传递。通过寄存器传递可减少压栈和弹栈开销,提升性能。具体来说,前6个参数分别通过寄存器rdi,rsi,rdx,rcx,r8和r9寄存器传递,剩下的参数,依次通过调用者的rsp+0,rsp+8,rsp+16,……,rsp+8^N 这些地址空间传递。
| rdi | rsi | rdx | rcx | r8 | r9 | 栈传递 |
|---|
在Windows 64位系统中,函数参数传递的顺序通常是按照以下规则:
- 整数和指针参数: 从左到右依次压入64位寄存器
RCX,RDX,R8,R9。如果参数超过4个或者是浮点数,则额外的参数通过栈传递。 - 浮点数参数: 浮点数参数通过
XMM0,XMM1,XMM2,XMM3寄存器传递,超过4个或者是整数参数则通过栈传递。
这种参数传递顺序适用于大多数的函数调用约定(calling convention)在Windows x64系统上。
https://learn.microsoft.com/zh-cn/cpp/build/x64-calling-convention?view=msvc-170#parameter-passing
栈和栈桢
汇编中一般堆栈(stack)指的就是栈(stack),**区分堆(heap)、栈(stack)、堆栈(stack)**。
堆栈帧(stack frame)(或活动记录(activation record))是一块堆栈保留区域,用于保存被传递的实际参数、子程序的返回值、局部变量以及被保存的寄存器。
寄存器EBP指向当前的栈帧的底部,寄存器ESP指向当前的栈帧的顶部。
注意:EBP指向当前位于系统栈最上边一个栈帧的底部,而不是系统栈的底部。严格说来,“栈帧底部”和“栈底”是不同的概念;ESP所指的栈帧顶部和系统栈的顶部是同一个位置。