数据定义与伪指令(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,用 GDBx/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(软件断点)