x86汇编
x86汇编语言是用于x86架构处理器的低级编程语言。它提供了对处理器硬件的直接访问,因此通常用于编写操作系统、驱动程序和需要高效执行的程序。以下是一些基本概念和指令介绍:
基本概念
-
寄存器:CPU内的高速存储单元,用于暂时存储数据和指令。
- 通用寄存器:AX, BX, CX, DX, SI, DI, BP, SP
- 段寄存器:CS, DS, ES, FS, GS, SS
- 指令指针寄存器:IP
- 标志寄存器:FLAGS
-
内存寻址:包括立即数寻址、寄存器寻址、直接寻址、基址变址寻址等。
-
指令格式:通常包括操作码(opcode)和操作数(operand)。
实模式(Real Mode)
实模式是x86处理器的默认模式,也是最基本的模式。在这种模式下,处理器可以直接访问1MB的内存,并且没有内存保护机制。通常用于早期的操作系统和一些嵌入式系统。
特点:
- 20位地址总线,可以访问最多1MB的内存。
- 没有内存保护机制,所有程序都可以访问所有内存区域。
- 主要用于DOS和一些BIOS功能。
在x86汇编语言的实模式(Real Mode)中,处理器可以访问1MB的内存,寄存器的使用对于编程至关重要。以下是实模式下的寄存器分类和说明:
通用寄存器(General Purpose Registers)
- AX(累加器寄存器):
- 用于算术运算和数据传输。
- 可以分为AH(高8位)和AL(低8位)。
- BX(基址寄存器):
- 通常用于存储基址(内存地址)。
- 可以分为BH(高8位)和BL(低8位)。
- CX(计数器寄存器):
- 常用于循环和字符串操作中的计数。
- 可以分为CH(高8位)和CL(低8位)。
- DX(数据寄存器):
- 用于I/O操作和乘除法指令。
- 可以分为DH(高8位)和DL(低8位)。
指针和索引寄存器(Pointer and Index Registers)
- SP(堆栈指针寄存器):
- 用于堆栈操作,指向堆栈顶。
- BP(基址指针寄存器):
- 常用于基址寻址,特别是访问函数参数和局部变量。
- SI(源变址寄存器):
- 常用于字符串操作,指向源数据。
- DI(目的变址寄存器):
- 常用于字符串操作,指向目的数据。
段寄存器(Segment Registers)
- CS(代码段寄存器):
- 指向当前执行代码的段基址。
- DS(数据段寄存器):
- 指向当前数据段的基址。
- SS(堆栈段寄存器):
- 指向当前堆栈段的基址。
- ES(附加段寄存器):
- 用于额外的数据段,特别是字符串操作。
指令指针寄存器(Instruction Pointer Register)
- IP(指令指针寄存器):
- 指向下一条将要执行的指令地址。
标志寄存器(Flags Register)
- FLAGS(标志寄存器):
- 存储处理器状态的标志位,用于条件判断和控制程序流。
- 主要标志位包括:
- CF(进位标志):表示最高位的进位或借位。
- ZF(零标志):结果为零时置位。
- SF(符号标志):结果为负数时置位。
- OF(溢出标志):有符号运算溢出时置位。
- PF(奇偶标志):结果低8位有偶数个1时置位。
- AF(辅助进位标志):用于BCD运算。
- DF(方向标志):控制字符串操作的方向。
示例代码:
section .text org 0x7C00 ; 起始地址
start: mov ax, 0xB800 ; 设置视频内存段地址 mov es, ax mov byte [es:0], 'H' ; 在屏幕上显示字符'H'
hlt ; 停机
保护模式(Protected Mode)
保护模式是80286及以后处理器支持的一种模式,提供了内存保护、多任务处理和更大的内存寻址空间。现代操作系统如Windows和Linux都运行在保护模式下。
特点:
- 32位地址总线,可以访问4GB的内存。
- 提供内存保护机制,防止程序之间互相干扰。
- 支持分页机制,扩展内存管理能力。
通用寄存器(General Purpose Registers)
这些寄存器用于各种通用的算术运算、逻辑运算和数据传输。
-
EAX(累加器寄存器):
- 用于算术运算和数据传输。
- 可以分为AX(低16位),以及AH(高8位)和AL(低8位)。
-
EBX(基址寄存器):
- 用于基址寻址。
- 可以分为BX(低16位),以及BH(高8位)和BL(低8位)。
-
ECX(计数器寄存器):
- 用于循环计数和字符串操作。
- 可以分为CX(低16位),以及CH(高8位)和CL(低8位)。
-
EDX(数据寄存器):
- 用于I/O操作和乘除法指令。
- 可以分为DX(低16位),以及DH(高8位)和DL(低8位)。
指针和索引寄存器(Pointer and Index Registers)
这些寄存器主要用于内存寻址。
-
ESP(堆栈指针寄存器):
- 用于堆栈操作,指向堆栈顶。
- 可以分为SP(低16位)。
-
EBP(基址指针寄存器):
- 用于基址寻址,特别是访问函数参数和局部变量。
- 可以分为BP(低16位)。
-
ESI(源变址寄存器):
- 用于字符串操作,指向源数据。
- 可以分为SI(低16位)。
-
EDI(目的变址寄存器):
- 用于字符串操作,指向目的数据。
- 可以分为DI(低16位)。
段寄存器(Segment Registers)
这些寄存器用于分段内存管理,每个寄存器指向一个段描述符。
-
CS(代码段寄存器):
- 指向当前执行代码的段基址。
-
DS(数据段寄存器):
- 指向当前数据段的基址。
-
SS(堆栈段寄存器):
- 指向当前堆栈段的基址。
-
ES(附加段寄存器):
- 用于额外的数据段,特别是字符串操作。
-
FS 和 GS:
- 额外的段寄存器,用于指向更多的数据段,通常用于特定用途。
控制寄存器(Control Registers)
这些寄存器用于控制处理器的操作模式和状态。
-
CR0:
- 控制寄存器0,包含启用和配置保护模式、分页等功能的控制位。
-
CR2:
- 存储导致页面错误的线性地址。
-
CR3:
- 存储页目录基地址,分页机制启用时使用。
-
CR4:
- 包含更多控制位,用于扩展功能,如PAE(物理地址扩展)和SSE(流SIMD扩展)支持。
调试寄存器(Debug Registers)
这些寄存器用于硬件调试。
-
DR0-DR3:
- 存储硬件断点的线性地址。
-
DR6:
- 存储调试状态信息。
-
DR7:
- 控制硬件断点功能。
浮点寄存器(Floating Point Registers)
用于浮点运算,属于x87 FPU(浮点运算单元)。
- ST0-ST7:
- 8个80位的浮点寄存器,组成一个栈结构。
示例代码:
section .text global _start
_start: ; 设置段寄存器,切换到保护模式 mov eax, cr0 or eax, 1 mov cr0, eax
; 在保护模式下的操作 mov eax, [0x000B8000] mov [eax], 'H' ; 在屏幕上显示字符'H'
hlt ; 停机
长模式(Long Mode)
长模式是x86-64处理器的工作模式,支持64位地址空间和操作。长模式允许访问超过4GB的内存,是现代64位操作系统的基础。
特点:
- 64位地址总线,可以访问非常大的内存空间。
- 支持64位寄存器和操作,提高处理能力。
- 兼容32位保护模式的特性。
在x86-64架构的长模式(Long Mode)中,处理器支持64位操作,寄存器的数量和功能相较于32位保护模式有了显著扩展。以下是长模式下的主要寄存器分类及其功能:
扩展通用寄存器(Extended General Purpose Registers)
在64位模式下,原有的32位通用寄存器被扩展为64位,并增加了8个新的通用寄存器。
-
RAX(累加器寄存器):
- 用于算术运算和数据传输。
- 可以分为EAX(低32位),AX(低16位),以及AH(高8位)和AL(低8位)。
-
RBX(基址寄存器):
- 用于基址寻址。
- 可以分为EBX(低32位),BX(低16位),以及BH(高8位)和BL(低8位)。
-
RCX(计数器寄存器):
- 用于循环计数和字符串操作。
- 可以分为ECX(低32位),CX(低16位),以及CH(高8位)和CL(低8位)。
-
RDX(数据寄存器):
- 用于I/O操作和乘除法指令。
- 可以分为EDX(低32位),DX(低16位),以及DH(高8位)和DL(低8位)。
-
RSI(源索引寄存器):
- 用于字符串操作,指向源数据。
- 可以分为ESI(低32位)和SI(低16位)。
-
RDI(目标索引寄存器):
- 用于字符串操作,指向目标数据。
- 可以分为EDI(低32位)和DI(低16位)。
-
RBP(基址指针寄存器):
- 用于基址寻址,特别是访问函数参数和局部变量。
- 可以分为EBP(低32位)和BP(低16位)。
-
RSP(堆栈指针寄存器):
- 用于堆栈操作,指向堆栈顶。
- 可以分为ESP(低32位)和SP(低16位)。
-
R8-R15(新增加的通用寄存器):
- 这8个寄存器用于扩展操作。
- 每个寄存器也可以分为32位、16位和8位部分(例如,R8可以分为R8D(低32位),R8W(低16位),以及R8B(低8位))。
段寄存器(Segment Registers)
在长模式下,大多数段寄存器的功能有所简化,只有FS和GS段寄存器仍然可以用于基址访问。
-
CS(代码段寄存器):
- 指向当前执行代码的段基址。
-
DS(数据段寄存器):
- 通常为零,不用于段基址访问。
-
SS(堆栈段寄存器):
- 通常为零,不用于段基址访问。
-
ES、FS、GS:
- FS和GS段寄存器仍可以用于基址访问,特别是在访问线程本地存储(TLS)时。
控制寄存器(Control Registers)
控制寄存器用于控制处理器的操作模式和状态。
-
CR0:
- 包含启用和配置保护模式、分页等功能的控制位。
-
CR2:
- 存储导致页面错误的线性地址。
-
CR3:
- 存储页目录基地址,分页机制启用时使用。
-
CR4:
- 包含更多控制位,用于扩展功能,如PAE(物理地址扩展)和SSE(流SIMD扩展)支持。
-
CR8:
- 用于控制外部中断优先级。
调试寄存器(Debug Registers)
调试寄存器用于硬件调试。
-
DR0-DR3:
- 存储硬件断点的线性地址。
-
DR6:
- 存储调试状态信息。
-
DR7:
- 控制硬件断点功能。
浮点寄存器和SIMD寄存器
用于浮点运算和SIMD运算。
-
XMM0-XMM15:
- 128位的SIMD寄存器,支持SSE指令集。
-
YMM0-YMM15:
- 256位的SIMD寄存器,支持AVX指令集。
-
ZMM0-ZMM31:
- 512位的SIMD寄存器,支持AVX-512指令集。
示例代码:
section .text global _start
_start: ; 设置段寄存器,切换到长模式 mov eax, cr4 or eax, 0x20 mov cr4, eax
mov eax, cr0 or eax, 0x80000001 mov cr0, eax
; 在长模式下的操作 mov rax, [0x00000000B8000000] mov [rax], 'H' ; 在屏幕上显示字符'H'
hlt ; 停机
Intel
数据传输指令
-
MOV:将数据从源操作数传送到目的操作数。
mov eax, 1 ; 将立即数1加载到EAX寄存器mov ebx, eax ; 将EAX的值复制到EBX寄存器mov [var], eax ; 将EAX的值存储到内存变量var中 -
PUSH 和 POP:在堆栈中压入和弹出数据。
push eax ; 将EAX寄存器的值压入堆栈pop ebx ; 从堆栈中弹出数据到EBX寄存器 -
XCHG:交换两个操作数的值。
xchg eax, ebx ; 交换EAX和EBX的值 -
LEA:加载有效地址。
lea eax, [ebx+4] ; 将内存地址(EBX + 4)加载到EAX寄存器
算术指令
-
ADD 和 SUB:加法和减法。
add eax, 2 ; EAX = EAX + 2sub ebx, 1 ; EBX = EBX - 1 -
MUL 和 IMUL:无符号乘法和有符号乘法。
mul ecx ; EAX = EAX * ECX(无符号乘法)imul edx, 3 ; EDX = EDX * 3(有符号乘法) -
DIV 和 IDIV:无符号除法和有符号除法。
div ecx ; EAX = EAX / ECX(无符号除法)idiv ebx ; EAX = EAX / EBX(有符号除法) -
INC 和 DEC:自增和自减。
inc eax ; EAX = EAX + 1dec ebx ; EBX = EBX - 1
逻辑指令
-
AND、OR 和 XOR:按位与、或、异或。
and eax, 0x0F ; EAX = EAX & 0x0For eax, 0x01 ; EAX = EAX | 0x01xor eax, eax ; EAX = EAX ^ EAX(将EAX清零) -
NOT:按位取反。
not eax ; EAX = ~EAX -
SHL 和 SHR:逻辑左移和右移。
shl eax, 1 ; EAX 左移 1 位shr ebx, 2 ; EBX 右移 2 位 -
SAL 和 SAR:算术左移和右移。
sal eax, 1 ; EAX 左移 1 位(算术)sar ebx, 2 ; EBX 右移 2 位(算术)
控制流指令
-
JMP:无条件跳转。
jmp label ; 无条件跳转到label处执行 -
条件跳转指令:根据特定条件进行跳转。
- JE/JZ:如果相等/零标志置位,跳转。
je label
- JNE/JNZ:如果不相等/零标志未置位,跳转。
jne label
- JG/JNLE:如果大于/不小于等于,跳转。
jg label
- JL/JNGE:如果小于/不大于等于,跳转。
jl label
- JE/JZ:如果相等/零标志置位,跳转。
-
CALL 和 RET:调用和返回子程序。
call subroutine ; 调用子程序ret ; 从子程序返回
字符串操作指令
-
MOVS:移动字符串。
movsb ; 移动字节字符串movsw ; 移动字字符串movsd ; 移动双字字符串 -
CMPS:比较字符串。
cmpsb ; 比较字节字符串cmpsw ; 比较字字符串cmpsd ; 比较双字字符串 -
SCAS:扫描字符串。
scasb ; 扫描字节字符串scasw ; 扫描字字符串scasd ; 扫描双字字符串
系统指令
-
NOP:空操作,不执行任何操作。
nop -
HLT:停止处理器,直到下一个外部中断到达。
hlt -
INT:软件中断。
int 0x80 ; 调用系统中断 -
IRET:中断返回。
iret
示例代码
以下是一个简单的示例,演示了如何使用x86汇编指令进行基本的数据传输和算术运算:
section .data var1 dd 1234h ; 定义一个32位数据
section .text global _start
_start: mov eax, [var1] ; 将内存中的var1值加载到EAX寄存器 add eax, 1 ; EAX = EAX + 1 mov [var1], eax ; 将结果存回内存中的var1
; 设置视频模式并显示字符(假设视频内存基地址为0xB8000) mov ebx, 0xB8000 ; 设置视频内存段地址 mov byte [ebx], 'H' ; 在屏幕上显示字符'H'
; 停机,等待外部中断 hlt
AT&T
AT&T语法是GNU Assembler(GAS)使用的一种汇编语言语法,与Intel语法相比有一些显著的差异。以下是一些常见的x86汇编指令及其在AT&T语法中的表示方法:
基本规则
-
操作数顺序:
- AT&T语法中,源操作数在前,目的操作数在后。
- Intel语法中,目的操作数在前,源操作数在后。
-
寄存器命名:
- AT&T语法使用前缀
%
来标识寄存器。 - Intel语法不使用前缀。
- AT&T语法使用前缀
-
立即数:
- AT&T语法使用前缀
$
来标识立即数。 - Intel语法直接使用数值。
- AT&T语法使用前缀
-
内存操作数:
- AT&T语法使用
()
和,
来标识内存操作数的基址、变址和比例因子。 - Intel语法使用
[]
。
- AT&T语法使用
-
指令后缀:
- AT&T语法通常在指令后使用
b
,w
,l
,q
来表示操作数的大小(8位,16位,32位,64位)。 - Intel语法不需要这样的后缀。
- AT&T语法通常在指令后使用
常见指令示例
数据传输指令
-
MOV(传送数据)
movl $1, %eax ; 将立即数1加载到EAX寄存器movl %eax, %ebx ; 将EAX的值复制到EBX寄存器movl %eax, var ; 将EAX的值存储到内存变量var中 -
PUSH 和 POP(压栈和出栈)
pushl %eax ; 将EAX寄存器的值压入堆栈popl %ebx ; 从堆栈中弹出数据到EBX寄存器
算术指令
-
ADD 和 SUB(加法和减法)
addl $2, %eax ; EAX = EAX + 2subl $1, %ebx ; EBX = EBX - 1 -
MUL 和 IMUL(乘法)
imull $3, %edx ; EDX = EDX * 3 -
DIV 和 IDIV(除法)
divl %ecx ; EAX = EAX / ECXidivl %ebx ; EAX = EAX / EBX(有符号除法)
逻辑指令
-
AND、OR 和 XOR(按位与、或、异或)
andl $0x0F, %eax ; EAX = EAX & 0x0Forl $0x01, %eax ; EAX = EAX | 0x01xorl %eax, %eax ; EAX = EAX ^ EAX(将EAX清零) -
NOT(按位取反)
notl %eax ; EAX = ~EAX
控制流指令
-
JMP(无条件跳转)
jmp label ; 无条件跳转到label处执行 -
条件跳转指令
- JE/JZ(相等时跳转)
je label
- JNE/JNZ(不相等时跳转)
jne label
- JG/JNLE(大于时跳转)
jg label
- JL/JNGE(小于时跳转)
jl label
- JE/JZ(相等时跳转)
-
CALL 和 RET(调用和返回子程序)
call subroutine ; 调用子程序ret ; 从子程序返回
内存操作指令
- MOV(内存到寄存器,寄存器到内存)
movl 4(%eax), %ebx ; 将内存地址(EAX + 4)处的值加载到EBX寄存器movl %ebx, -8(%ebp) ; 将EBX的值存储到内存地址(EBP - 8)处movl array(,%ebx,4), %eax ; 将array基址 + EBX * 4处的值加载到EAX
字符串操作指令
-
MOVS(移动字符串)
movsb ; 移动字节字符串movsw ; 移动字字符串movsl ; 移动双字字符串 -
CMPS(比较字符串)
cmpsb ; 比较字节字符串cmpsw ; 比较字字符串cmpsl ; 比较双字字符串 -
SCAS(扫描字符串)
scasb ; 扫描字节字符串scasw ; 扫描字字符串scasl ; 扫描双字字符串
其他指令
-
NOP(空操作)
nop ; 空操作,不执行任何操作 -
HLT(停止处理器)
hlt ; 停止处理器,直到下一个外部中断到达
示例代码
以下是一个简单的示例,演示了如何使用AT&T语法进行基本的数据传输和算术运算:
.section .data var1: .long 1234 ; 定义一个32位数据
.section .text .globl _start
_start: movl var1, %eax ; 将内存中的var1值加载到EAX寄存器 addl $1, %eax ; EAX = EAX + 1 movl %eax, var1 ; 将结果存回内存中的var1
; 设置视频模式并显示字符(假设视频内存基地址为0xB8000) movl $0xB8000, %ebx ; 设置视频内存段地址 movb $'H', (%ebx) ; 在屏幕上显示字符'H'
; 停机,等待外部中断 hlt
NASM汇编伪指令
在NASM(Netwide Assembler)中,伪指令是汇编器提供的辅助指令,用于定义数据、控制段、控制汇编过程等。以下是一些常见的NASM汇编伪指令及其说明:
数据定义伪指令
这些伪指令用于定义各种类型的数据。
-
DB(Define Byte):定义一个或多个字节的数据。
msg db 'Hello, World!', 0 ; 定义一个字符串,以NULL结尾value db 0x12 ; 定义一个字节,值为0x12 -
DW(Define Word):定义一个或多个字的数据(2字节)。
word1 dw 0x1234 ; 定义一个字,值为0x1234words dw 0x5678, 0x9ABC ; 定义多个字 -
DD(Define Double Word):定义一个或多个双字的数据(4字节)。
dword1 dd 0x12345678 ; 定义一个双字,值为0x12345678dwords dd 0x9ABCDEF0, 0x12345678 ; 定义多个双字 -
DQ(Define Quad Word):定义一个或多个四字的数据(8字节)。
qword1 dq 0x123456789ABCDEF0 ; 定义一个四字,值为0x123456789ABCDEF0qwords dq 0x0FEDCBA987654321, 0x123456789ABCDEF0 ; 定义多个四字 -
DT(Define Ten Bytes):定义十个字节的数据(用于定义扩展精度的浮点数)。
tbytes dt 0x123456789ABCDEF01234 ; 定义十个字节的数据
段定义伪指令
这些伪指令用于定义代码段、数据段和堆栈段。
- SECTION 或 SEGMENT:定义一个段。
section .datamy_data db 'Hello, World!', 0 ; 数据段section .bssbuffer resb 64 ; 预留64字节的未初始化空间section .textglobal _start_start:; 代码段mov eax, 1 ; 系统调用号 (sys_exit)xor ebx, ebx ; 退出状态 0int 0x80 ; 调用内核
控制伪指令
这些伪指令用于控制汇编过程和设置程序入口点等。
-
ORG:设置起始地址。
org 0x7C00 ; 设置起始地址为0x7C00(通常用于引导扇区) -
EQU:定义常量。
MAX_LEN equ 100 ; 定义一个常量 MAX_LEN 值为100 -
ALIGN:对齐数据。
align 4 ; 将下一个数据对齐到4字节边界 -
TIMES:重复数据或指令。
times 512-($-$$) db 0 ; 填充至512字节 -
INCBIN:包含二进制文件的内容。
incbin "bootloader.bin" ; 包含bootloader.bin文件的内容
宏定义伪指令
这些伪指令用于定义和使用宏,以减少重复代码。
- %macro 和 %endmacro:定义一个宏。
%macro my_macro 2mov eax, %1add eax, %2%endmacro; 使用宏my_macro 5, 10
条件编译伪指令
这些伪指令用于条件编译代码块。
-
%if、%elif、%else、%endif:条件编译代码块。
%if 1mov eax, 1%elsemov eax, 0%endif -
%ifdef、%ifndef、%endif:条件编译代码块(基于符号是否定义)。
%define SYMBOL%ifdef SYMBOLmov eax, 1%elsemov eax, 0%endif
重复定义伪指令
- REPT 和 ENDR:重复定义一段代码。
%assign i 0%rep 5mov eax, iinc i%endrep
示例代码
以下是一个示例,演示了如何使用伪指令定义数据和段,并包含一个简单的程序入口点:
section .data msg db 'Hello, World!', 0 ; 定义一个字符串,结尾是NULL
section .bss buffer resb 64 ; 预留64字节的未初始化空间
section .text global _start ; 定义全局入口点
_start: mov edx, len ; 设置消息长度 mov ecx, msg ; 设置消息地址 mov ebx, 1 ; 设置文件描述符(stdout) mov eax, 4 ; 系统调用号(sys_write) int 0x80 ; 调用内核
mov eax, 1 ; 系统调用号(sys_exit) xor ebx, ebx ; 退出状态 0 int 0x80 ; 调用内核
len equ $ - msg ; 计算消息长度
gcc汇编伪指令
as
是GNU Assembler的缩写,是GNU Binutils的一部分,用于将汇编代码转换为目标代码。as
支持许多伪指令,这些指令用于数据定义、段定义、控制汇编过程和其他特殊用途。以下是一些常见的GNU汇编器(as
)伪指令及其说明:
数据定义伪指令
-
.byte:定义字节数据。
.byte 0x12 ; 定义一个字节,值为0x12 -
.word:定义字数据(2字节)。
.word 0x1234 ; 定义一个字,值为0x1234 -
.long:定义双字数据(4字节)。
.long 0x12345678 ; 定义一个双字,值为0x12345678 -
.quad:定义四字数据(8字节)。
.quad 0x123456789ABCDEF0 ; 定义一个四字,值为0x123456789ABCDEF0 -
.ascii:定义一个不带NULL结尾的字符串。
.ascii "Hello, World!" ; 定义一个字符串,不带NULL结尾 -
.asciz 或 .string:定义一个带NULL结尾的字符串。
.asciz "Hello, World!" ; 定义一个字符串,带NULL结尾 -
.zero:分配指定字节数的零初始化空间。
.zero 64 ; 分配64字节的零初始化空间
段定义伪指令
-
.section:定义一个段。
.section .datamy_data: .long 0x12345678 ; 数据段中的变量.section .bss.lcomm buffer, 64 ; 在BSS段中分配64字节的未初始化空间.section .text.globl _start_start:; 代码段中的指令movl $1, %eax ; 系统调用号 (sys_exit)xorl %ebx, %ebx ; 退出状态 0int $0x80 ; 调用内核 -
.text、.data、.bss:分别定义代码段、数据段和BSS段。
.text.data.bss
控制伪指令
-
.global 或 .globl:定义全局符号。
.global _start_start:; 代码段中的指令 -
.equ 或 .set:定义常量。
.equ MAX_LEN, 100 ; 定义一个常量 MAX_LEN 值为100.set BUFFER_SIZE, 256 -
.align:对齐数据。
.align 4 ; 将下一个数据对齐到4字节边界 -
.org:设置起始地址。
.org 0x7C00 ; 设置起始地址为0x7C00(通常用于引导扇区) -
.incbin:包含二进制文件的内容。
.incbin "bootloader.bin" ; 包含bootloader.bin文件的内容
宏定义伪指令
- .macro 和 .endm:定义一个宏。
.macro my_macro param1, param2movl \param1, %eaxaddl \param2, %eax.endm; 使用宏my_macro 5, 10
条件伪指令
-
.if、.else、.endif:条件汇编代码块。
.if 1movl $1, %eax.elsemovl $0, %eax.endif -
.ifdef、.ifndef、.endif:条件汇编代码块(基于符号是否定义)。
.ifdef SYMBOLmovl $1, %eax.elsemovl $0, %eax.endif
重复伪指令
- .rept 和 .endr:重复一段代码。
.rept 5nop.endr
示例代码
以下是一个示例,演示了如何使用GNU汇编器伪指令定义数据和段,并包含一个简单的程序入口点:
.section .data msg: .asciz "Hello, World!" ; 定义一个字符串,带NULL结尾
.section .bss .lcomm buffer, 64 ; 预留64字节的未初始化空间
.section .text .global _start ; 定义全局入口点
_start: movl $4, %eax ; 系统调用号 (sys_write) movl $1, %ebx ; 文件描述符 (stdout) movl $msg, %ecx ; 消息地址 movl $13, %edx ; 消息长度 int $0x80 ; 调用内核
movl $1, %eax ; 系统调用号 (sys_exit) xorl %ebx, %ebx ; 退出状态 0 int $0x80 ; 调用内核