ARM裸机编程疑问:裸二进制加载时链接脚本地址如何生效?
ARM裸机链接脚本与Raw Binary加载问题
我的链接脚本
ENTRY(ResetHandler) MEMORY { ROM (rx) : ORIGIN = 0x08000000, LENGTH = 512K RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K } SECTIONS { .text : { KEEP(*(.isr_vector)) *(.text) *(.text.*) *(.rodata) *(.rodata.*) . = ALIGN(4); _etext = .; }>ROM AT>ROM .data : { _sdata = .; *(.data) *(.data.*) . = ALIGN(4); _edata = .; }>RAM AT>ROM .bss (NOLOAD) : { _sbss = .; *(.bss) *(.bss.*) *(COMMON) . = ALIGN(4); _ebss = .; }>RAM AT>ROM }
核心疑问
- 编译得到无地址信息的raw binary文件,加载时未额外指定信息,各段如何定位到链接脚本定义的正确地址?
- 链接脚本中的LMA和VMA信息是否丢失?
- 该定位过程是否全部由bootloader完成?
解答
1. 各段的定位逻辑
Raw binary确实没有地址元数据,但链接脚本的地址信息已经通过编译链接阶段的重定位固化到程序中,同时需要你在ResetHandler里手动处理.data和.bss段:
.text和.rodata的VMA(运行地址)与LMA(加载地址)都是ROM起始地址0x08000000,只要把raw binary烧写到ROM的这个起始地址,CPU复位后会直接从该地址取指令执行,自然能正确运行代码。.data的VMA是RAM地址0x20000000,但LMA是ROM(在raw binary里紧跟.text结尾),所以必须在ResetHandler里手动把ROM中_etext到_edata区间的数据拷贝到RAM的_sdata到_edata地址。.bss是NOLOAD段,本身不在raw binary里,需要在ResetHandler里把RAM中_sbss到_ebss的区间清零。
这些拷贝、清零逻辑是你必须写在启动代码里的,不是自动完成的。
2. LMA和VMA信息的去向
LMA和VMA信息没有丢失:
- 链接器生成ELF文件时会保存这些元数据,但转换成raw binary时,ELF的元数据会被丢弃,但链接器已经根据LMA和VMA计算出
_etext、_sdata等符号的地址,这些地址会以常量形式固化到你的代码中(比如拷贝.data的代码会直接使用这些地址)。 - Raw binary的内容是按LMA顺序拼接的:
.text在前,.data紧跟其后,只要把整个文件烧写到ROM的0x08000000起始地址,就能保证.data的LMA位置正确。
3. 定位过程的执行者
不全由bootloader完成:
- 对于STM32这类无独立bootloader的单片机,CPU复位后直接从ROM起始地址执行
ResetHandler,.text的定位由硬件复位逻辑决定,.data和.bss的初始化则由你写的ResetHandler完成。 - 如果有bootloader,它的工作只是把raw binary加载到指定的ROM地址,然后跳转到
ResetHandler;.data和.bss的初始化依然是你的程序自己完成的,bootloader不会主动处理这部分——除非你在bootloader里特意编写了相关逻辑,但这不是常规做法。
内容的提问来源于stack exchange,提问作者Korsarq
相关产品推荐
相关产品推荐

