Skip to content

x86汇编

x86汇编语言是用于x86架构处理器的低级编程语言。它提供了对处理器硬件的直接访问,因此通常用于编写操作系统、驱动程序和需要高效执行的程序。以下是一些基本概念和指令介绍:

基本概念

  1. 寄存器:CPU内的高速存储单元,用于暂时存储数据和指令。

    • 通用寄存器:AX, BX, CX, DX, SI, DI, BP, SP
    • 段寄存器:CS, DS, ES, FS, GS, SS
    • 指令指针寄存器:IP
    • 标志寄存器:FLAGS
  2. 内存寻址:包括立即数寻址、寄存器寻址、直接寻址、基址变址寻址等。

  3. 指令格式:通常包括操作码(opcode)和操作数(operand)。

实模式(Real Mode)

实模式是x86处理器的默认模式,也是最基本的模式。在这种模式下,处理器可以直接访问1MB的内存,并且没有内存保护机制。通常用于早期的操作系统和一些嵌入式系统。

特点:

  • 20位地址总线,可以访问最多1MB的内存。
  • 没有内存保护机制,所有程序都可以访问所有内存区域。
  • 主要用于DOS和一些BIOS功能。

在x86汇编语言的实模式(Real Mode)中,处理器可以访问1MB的内存,寄存器的使用对于编程至关重要。以下是实模式下的寄存器分类和说明:

通用寄存器(General Purpose Registers)

  1. AX(累加器寄存器)
    • 用于算术运算和数据传输。
    • 可以分为AH(高8位)和AL(低8位)。
  2. BX(基址寄存器)
    • 通常用于存储基址(内存地址)。
    • 可以分为BH(高8位)和BL(低8位)。
  3. CX(计数器寄存器)
    • 常用于循环和字符串操作中的计数。
    • 可以分为CH(高8位)和CL(低8位)。
  4. DX(数据寄存器)
    • 用于I/O操作和乘除法指令。
    • 可以分为DH(高8位)和DL(低8位)。

指针和索引寄存器(Pointer and Index Registers)

  1. SP(堆栈指针寄存器)
    • 用于堆栈操作,指向堆栈顶。
  2. BP(基址指针寄存器)
    • 常用于基址寻址,特别是访问函数参数和局部变量。
  3. SI(源变址寄存器)
    • 常用于字符串操作,指向源数据。
  4. DI(目的变址寄存器)
    • 常用于字符串操作,指向目的数据。

段寄存器(Segment Registers)

  1. CS(代码段寄存器)
    • 指向当前执行代码的段基址。
  2. DS(数据段寄存器)
    • 指向当前数据段的基址。
  3. SS(堆栈段寄存器)
    • 指向当前堆栈段的基址。
  4. ES(附加段寄存器)
    • 用于额外的数据段,特别是字符串操作。

指令指针寄存器(Instruction Pointer Register)

  1. IP(指令指针寄存器)
    • 指向下一条将要执行的指令地址。

标志寄存器(Flags Register)

  1. 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(附加段寄存器):

    • 用于额外的数据段,特别是字符串操作。
  • FSGS

    • 额外的段寄存器,用于指向更多的数据段,通常用于特定用途。

控制寄存器(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(堆栈段寄存器):

    • 通常为零,不用于段基址访问。
  • ESFSGS

    • 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中
  • PUSHPOP:在堆栈中压入和弹出数据。

    push eax ; 将EAX寄存器的值压入堆栈
    pop ebx ; 从堆栈中弹出数据到EBX寄存器
  • XCHG:交换两个操作数的值。

    xchg eax, ebx ; 交换EAX和EBX的值
  • LEA:加载有效地址。

    lea eax, [ebx+4] ; 将内存地址(EBX + 4)加载到EAX寄存器

算术指令

  • ADDSUB:加法和减法。

    add eax, 2 ; EAX = EAX + 2
    sub ebx, 1 ; EBX = EBX - 1
  • MULIMUL:无符号乘法和有符号乘法。

    mul ecx ; EAX = EAX * ECX(无符号乘法)
    imul edx, 3 ; EDX = EDX * 3(有符号乘法)
  • DIVIDIV:无符号除法和有符号除法。

    div ecx ; EAX = EAX / ECX(无符号除法)
    idiv ebx ; EAX = EAX / EBX(有符号除法)
  • INCDEC:自增和自减。

    inc eax ; EAX = EAX + 1
    dec ebx ; EBX = EBX - 1

逻辑指令

  • ANDORXOR:按位与、或、异或。

    and eax, 0x0F ; EAX = EAX & 0x0F
    or eax, 0x01 ; EAX = EAX | 0x01
    xor eax, eax ; EAX = EAX ^ EAX(将EAX清零)
  • NOT:按位取反。

    not eax ; EAX = ~EAX
  • SHLSHR:逻辑左移和右移。

    shl eax, 1 ; EAX 左移 1 位
    shr ebx, 2 ; EBX 右移 2 位
  • SALSAR:算术左移和右移。

    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
  • CALLRET:调用和返回子程序。

    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语法中的表示方法:

基本规则

  1. 操作数顺序

    • AT&T语法中,源操作数在前,目的操作数在后。
    • Intel语法中,目的操作数在前,源操作数在后。
  2. 寄存器命名

    • AT&T语法使用前缀 % 来标识寄存器。
    • Intel语法不使用前缀。
  3. 立即数

    • AT&T语法使用前缀 $ 来标识立即数。
    • Intel语法直接使用数值。
  4. 内存操作数

    • AT&T语法使用 (), 来标识内存操作数的基址、变址和比例因子。
    • Intel语法使用 []
  5. 指令后缀

    • AT&T语法通常在指令后使用 b, w, l, q 来表示操作数的大小(8位,16位,32位,64位)。
    • Intel语法不需要这样的后缀。

常见指令示例

数据传输指令

  • MOV(传送数据)

    movl $1, %eax ; 将立即数1加载到EAX寄存器
    movl %eax, %ebx ; 将EAX的值复制到EBX寄存器
    movl %eax, var ; 将EAX的值存储到内存变量var中
  • PUSHPOP(压栈和出栈)

    pushl %eax ; 将EAX寄存器的值压入堆栈
    popl %ebx ; 从堆栈中弹出数据到EBX寄存器

算术指令

  • ADDSUB(加法和减法)

    addl $2, %eax ; EAX = EAX + 2
    subl $1, %ebx ; EBX = EBX - 1
  • MULIMUL(乘法)

    imull $3, %edx ; EDX = EDX * 3
  • DIVIDIV(除法)

    divl %ecx ; EAX = EAX / ECX
    idivl %ebx ; EAX = EAX / EBX(有符号除法)

逻辑指令

  • ANDORXOR(按位与、或、异或)

    andl $0x0F, %eax ; EAX = EAX & 0x0F
    orl $0x01, %eax ; EAX = EAX | 0x01
    xorl %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
  • CALLRET(调用和返回子程序)

    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 ; 定义一个字,值为0x1234
    words dw 0x5678, 0x9ABC ; 定义多个字
  • DD(Define Double Word):定义一个或多个双字的数据(4字节)。

    dword1 dd 0x12345678 ; 定义一个双字,值为0x12345678
    dwords dd 0x9ABCDEF0, 0x12345678 ; 定义多个双字
  • DQ(Define Quad Word):定义一个或多个四字的数据(8字节)。

    qword1 dq 0x123456789ABCDEF0 ; 定义一个四字,值为0x123456789ABCDEF0
    qwords dq 0x0FEDCBA987654321, 0x123456789ABCDEF0 ; 定义多个四字
  • DT(Define Ten Bytes):定义十个字节的数据(用于定义扩展精度的浮点数)。

    tbytes dt 0x123456789ABCDEF01234 ; 定义十个字节的数据

段定义伪指令

这些伪指令用于定义代码段、数据段和堆栈段。

  • SECTIONSEGMENT:定义一个段。
    section .data
    my_data db 'Hello, World!', 0 ; 数据段
    section .bss
    buffer resb 64 ; 预留64字节的未初始化空间
    section .text
    global _start
    _start:
    ; 代码段
    mov eax, 1 ; 系统调用号 (sys_exit)
    xor ebx, ebx ; 退出状态 0
    int 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 2
    mov eax, %1
    add eax, %2
    %endmacro
    ; 使用宏
    my_macro 5, 10

条件编译伪指令

这些伪指令用于条件编译代码块。

  • %if%elif%else%endif:条件编译代码块。

    %if 1
    mov eax, 1
    %else
    mov eax, 0
    %endif
  • %ifdef%ifndef%endif:条件编译代码块(基于符号是否定义)。

    %define SYMBOL
    %ifdef SYMBOL
    mov eax, 1
    %else
    mov eax, 0
    %endif

重复定义伪指令

  • REPTENDR:重复定义一段代码。
    %assign i 0
    %rep 5
    mov eax, i
    inc 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 .data
    my_data: .long 0x12345678 ; 数据段中的变量
    .section .bss
    .lcomm buffer, 64 ; 在BSS段中分配64字节的未初始化空间
    .section .text
    .globl _start
    _start:
    ; 代码段中的指令
    movl $1, %eax ; 系统调用号 (sys_exit)
    xorl %ebx, %ebx ; 退出状态 0
    int $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, param2
    movl \param1, %eax
    addl \param2, %eax
    .endm
    ; 使用宏
    my_macro 5, 10

条件伪指令

  • .if.else.endif:条件汇编代码块。

    .if 1
    movl $1, %eax
    .else
    movl $0, %eax
    .endif
  • .ifdef.ifndef.endif:条件汇编代码块(基于符号是否定义)。

    .ifdef SYMBOL
    movl $1, %eax
    .else
    movl $0, %eax
    .endif

重复伪指令

  • .rept.endr:重复一段代码。
    .rept 5
    nop
    .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 ; 调用内核