这种对应关系由汇编器(如 as)和链接器(如 ld) 决定,并最终体现在可执行文件(如 ELF)的布局中,操作系统据此把它们加载到具体的内存地址。
一、二者关系概览
汇编语法中的 .section | 可执行文件中的段(ELF) | 内存中的区域(虚拟地址空间) |
|---|---|---|
.text | .text 段 | 代码段(Code Segment) |
.data | .data 段 | 数据段(Data Segment) |
.rodata | .rodata 段 | 只读数据区(Read-Only Data) |
.bss | .bss 段 | BSS 段(零初始化数据) |
| — | — | 堆(Heap) |
| — | — | 栈(Stack) |
二、段如何变成内存映射
以 Linux 上典型 ELF 格式为例:
- 你写的
.section-> 被汇编器变成 节(section) - 链接器将节合并成段(segment),比如
.text节被放入 PT_LOAD 类型的 text 段 - 程序执行时,内核的 ELF 加载器按段加载到内存相应位置(通常通过
mmap)
三、程序运行时的内存段布局(虚拟地址空间)
一个 ELF 程序在 Linux 下的典型内存布局如下:
0x00400000 ────────────▶ 代码段 (.text, 只读可执行)
────────────▶ 只读数据段 (.rodata, 只读)
────────────▶ 数据段 (.data, 可读写)
────────────▶ BSS段 (.bss, 初始为0)
────────────▶ 堆区 heap(动态增长)
0x7fffc000 ────────────▶ 栈 stack(向下增长)
这些区域的属性由 .section + 链接脚本(如 linker.ld)共同决定。
四、例子对照:从汇编到内存
.section .text
_start:
mov $msg, %rdi
call print
.section .data
msg: .asciz "hello\n"
.section .bss
.lcomm buffer, 64
.text→ 映射到虚拟地址如0x400000,只读可执行.data→ 映射到如0x600000,读写可改.bss→ 不在 ELF 文件中占空间,但运行时分配(自动清零).rodata(若有) → 映射到如0x500000,只读不能写
五、如何验证这点?
你可以通过以下命令查看 ELF 段与内存布局的映射:
readelf -S your_program # 查看 .section
readelf -l your_program # 查看 segment 映射
objdump -h your_program # 查看段的地址、权限
或者运行后,在 proc 中看真实内存映射:
cat /proc/$(pidof your_program)/maps
六、总结
- 汇编的
.section指令决定了数据/代码的类别 - 链接器根据
.section生成 ELF 文件的段 - 操作系统将段映射到虚拟内存空间,形成代码段、数据段、BSS、堆和栈
- 所以:
.section与程序运行时内存布局是一一对应的,极为紧密