数据定义与伪指令(Data & Directives)

伪指令(assembler directives)由汇编器处理,不构成 CPU 指令,用于定义数据、分配空间、设置节区等。

节区(Sections)

伪指令 含义
.text 代码段(可执行)
.data 已初始化数据段
.bss 未初始化数据段(只预留空间,不占文件体积)
.rodata 只读数据

完整形式 .section name[, "flags"[, @type]] 可自定义节区属性,例如关闭可执行栈标记(避免链接器警告):

.section .note.GNU-stack,"",@progbits

数据定义

格式:.type value[, value, ...]

伪指令 大小 说明
.byte 1 字节
.word / .short 2
.long / .int 4 长字
.quad 8 四字(64 位)
.float 4 单精度浮点
.double 8 双精度浮点
.ascii "str" 不含结尾 0 的字符串
.asciz "str" / .string "str" 以 0 结尾的字符串(C 风格)
.incbin "file" 把二进制文件内容原样嵌入

字符串支持 C 风格转义:\n \t \r \\ \" \0 \xNN(如 "\x0a" 是换行)。

.data
    msg:    .asciz "Hello, World!\n"
    val:    .long  42
    arr:    .byte  1, 2, 3, 4
    buf:    .zero  64        # 64 字节清零
    .fill   10, 4, 0        # 10 个元素,每个 4 字节,填充 0

x86 是小端序:.long 0x11223344 在内存中按字节存为 44 33 22 11,用 GDB x/4xb 查看即可验证。

符号与标号属性

.globl _start        # 声明为全局符号(入口)
.set PI, 3           # 常量定义 PI = 3
.equ  MAX, 100       # 同 .set
N = 42               # 也可直接赋值
.hidden local_fn     # 符号仅本模块可见(不导出)
.weak  sym           # 弱符号(可被同名强符号覆盖)

.type func, @function
func:
    nop
.size func, . - func # 符号大小 = 当前位置 - func(供链接器/调试器)

对齐与位置

伪指令 说明
.align n x86 ELF 上按 2^n 字节对齐(行为随平台变化,慎用)
.p2align n 明确按 2^n 字节对齐(跨平台一致)
.balign n 按 n 字节对齐(更直观)
.org off 设置当前位置计数器
.space n[, fill] 预留 n 字节(可指定填充值)
.skip n .space

重复与宏式数据

.rept 4             # 重复 4 次
    .long 0
.endr

节区切换辅助

伪指令 作用
.pushsection name[, "flags"] 记住当前节区并切换到 name
.popsection 恢复到 .pushsection 之前的节区
.previous 切回上一次使用的节区(等价简写)
.text
    # ... 代码 ...
.pushsection .rodata
errmsg: .asciz "error\n"
.popsection            # 回到 .text

在宏内切换节区(如内嵌只读数据)后记得 .popsection/.previous 切回,否则后续代码会进错段。

位宽模式与指令集选择

.code16                 # 16 位实模式(引导扇区等)
.code32                 # 32 位模式
.code64                 # 64 位模式(默认)

若汇编器报 no such instruction(用了较新扩展如 AVX-512 而发行版 GAS 默认未开启),用 gcc 的 -m 选项传递正确的指令集级别,比裸 as 省心:

gcc -mavx2   -c foo.s -o foo.o    # 需要 AVX2 时
gcc -mavx512f -c foo.s -o foo.o   # 需要 AVX-512 时

.arch 伪指令在 x86 的 64 位模式下支持有限,不推荐作为开启扩展的手段。)

完整数据段示例

.section .data
    greeting: .asciz "hello\n"
.section .bss
    .lcomm buffer, 128     # 本地公共变量,128 字节(等价 .bss 内标号 + .zero)

.comm.lcomm 的区别:.comm sym, size 定义的是全局公共符号(多个目标文件可引用同一个),.lcomm 只在本文件内可见。

嵌入原始机器码

.byte 也可直接写机器码字节,常用于插入特殊指令(如调试断点 int3 = 0xcc):

.byte 0xcc               # 相当于 int3(软件断点)