STM32中GNU linker生成段(program header)地址与执行代码地址不一致问题
STM32 程序地址偏移与烧录镜像问题
问题背景
为实现Bootloader功能,尝试将STM32可执行代码生成在默认地址0x8000000以外的位置,使用STM32CubeIDE 1.11.2版本,工具链为GCC 10.3.1(GNU Tools for STM32 10.3-2021.10.20211105-1100)。
修改链接脚本中的FLASH起始地址为0x8003800:
/* Memories definition */ MEMORY { RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 144K FLASH (rx) : ORIGIN = 0x8003800, LENGTH = 512K }
编译正常,但通过arm-none-eabi-objdump查看发现:所有符号和执行代码地址正确,但ELF文件的program header地址仍为0x8000000,而非0x8003800。该program header的P_OFFSET=0、P_ADDR=0x8000000、P_FILESZ=0x000039c4,会导致烧录工具将ELF文件偏移0处的内容(含ELF头)烧录到STM32的0x8000000地址,覆盖原有Bootloader,同时0x8003800地址前会被写入无效内容。
问题
- 上述操作是否存在错误?
- 如何生成可烧录到指定地址且不会覆盖已有镜像(如Bootloader)的纯二进制镜像?
解答
1. 操作中的错误点
- 链接脚本修改不完整:仅修改MEMORY区块的FLASH起始地址是不够的,还需要检查链接脚本中
SECTIONS部分的配置,确保*.text、.rodata等代码/只读数据段的起始地址*与MEMORY中定义的0x8003800对齐。部分默认链接脚本会在SECTIONS里硬编码FLASH起始地址,而非引用MEMORY的定义,这会导致program header仍使用旧地址。 - 未明确指定程序段加载地址:GCC链接时,若未强制指定程序段的加载基地址,可能会沿用默认的
0x8000000。需要确保链接脚本中SECTIONS里的.text等段的AT属性(加载地址)与运行地址一致,或者通过链接参数-Ttext=0x8003800强制指定。
2. 生成指定地址的纯二进制镜像方法
方法一:使用arm-none-eabi-objcopy手动生成
编译完成后,通过以下命令从ELF文件提取有效代码段,生成纯二进制镜像:
arm-none-eabi-objcopy -O binary -j .text -j .rodata -j .data your_project.elf your_project.bin
-O binary:指定输出格式为纯二进制-j:仅保留指定的有效段(可根据项目需求添加/删除段,比如.bss无需包含,会在启动时初始化)- 生成的
.bin文件可直接通过烧录工具指定目标地址0x8003800烧录,不会覆盖0x8000000处的Bootloader。
方法二:在STM32CubeIDE中配置自动生成
- 打开项目属性,进入
C/C++ Build > Settings > Tool Settings > MCU Post build outputs - 勾选
Convert to binary file using objcopy - 在
objcopy flags中添加参数:-O binary -j .text -j .rodata -j .data - 重新编译后,IDE会自动在输出目录生成对应的
.bin文件,烧录时指定目标地址为0x8003800即可。
方法三:修正链接脚本确保ELF程序段地址正确
修改链接脚本的SECTIONS部分,确保所有FLASH相关段的起始地址指向0x8003800,示例:
SECTIONS { .isr_vector : { . = ALIGN(4); KEEP(*(.isr_vector)) /* 中断向量表 */ . = ALIGN(4); } >FLASH .text : { . = ALIGN(4); *(.text) /* 代码段 */ *(.text*) /* 扩展代码段 */ *(.glue_7) /* ARM与Thumb代码衔接段 */ *(.glue_7t) /* Thumb与ARM代码衔接段 */ *(.eh_frame) KEEP (*(.init)) KEEP (*(.fini)) . = ALIGN(4); _etext = .; /* 代码段结束标记 */ } >FLASH /* .rodata、.data等其他FLASH相关段同理,确保都关联到FLASH区块 */ }
修改后重新编译,ELF的program header地址会与MEMORY定义一致,但仍建议使用纯二进制镜像烧录,避免ELF头带来的潜在干扰。
内容的提问来源于stack exchange,提问作者yo3hcv
相关产品推荐
相关产品推荐

