ARM平台链接器无法正确解析外部指针json_start问题求助
问题:ARM musl-gcc编译时
json_start符号解析为偏移量而非PC相对地址 我使用arm-linux-musleabi-gcc (GCC) 9.4.0编译代码时,遇到json_start引用解析错误的问题——它被处理成了纯偏移量(比如0x4762),而非预期的PC相对访问地址(比如0x5114762)。
我的需求是将JSON内容嵌入二进制文件中。理想情况下会用#embed指令,但当前musl版本不支持;也考虑过.incbin,但不想为这一功能单独添加汇编文件,同时希望搞清楚问题的根源。
相关文件
file.h
#ifndef __FILE_H__ #define __FILE_H__ #include <stddef.h> typedef size_t (*strlen_fn)(const char *s); #define strlen ((strlen_fn)Strlen) extern const unsigned char * json_start; #endif
file.c
#include <file.h> void *entry(void) __attribute__((section("\".entry\""))); void do_stuff(unsigned char *, void *); void *entry(void) { do_stuff((unsigned char *)json_start, entry); } void do_stuff(unsigned char *json_start, void *entry) { json_start[0] = 0; return; }
file.json
{}
linker.ld
SECTIONS { .text : { *(.entry) *(.core) *(.text*) *(.rodata*) *(.data*) *(.bss*) *(.got*) *(COMMON) } /DISCARD/ : { *(*) } }
当前编译命令
rm -r build mkdir -p ./build arm-linux-musleabi-objcopy -I binary -B arm -O elf32-littlearm ./file.json ./build/json.o arm-linux-musleabi-objcopy --redefine-sym _binary___file_json_start=json_start ./build/json.o arm-linux-musleabi-gcc -o ./build/file.o -c -fPIE -fno-builtin -ffreestanding -march=armv5t -Os -I . file.c arm-linux-musleabi-gcc -o ./build/file -nostdlib -pie -T linker.ld ./build/file.o ./build/json.o -lgcc
尝试过的其他方法
arm-linux-musleabi-gcc -r -o ./build/json.o -Wl,-b,binary file.json arm-linux-musleabi-objcopy --redefine-sym _binary_file_json_start=json_start ./build/json.o
但效果不佳。
汇编代码对比
实际生成的汇编:
ldr r3,[r3,r2] ldr r0,[r3,#0x0] bl do_stuff
预期的汇编:
ldr r3,[r3,r2] ldr r0,[r3,#0x0] add r0, pc, r0 bl do_stuff
其中r0对应json_start。
更新发现
问题出在json_start被放入了GOT段,而我的链接脚本只是将GOT段打包进二进制,未对符号做地址修正。
解决方法
1. 调整链接脚本,单独处理二进制嵌入段
修改linker.ld,将二进制嵌入的JSON内容明确放到.rodata段,避免GOT介入:
SECTIONS { .text : { *(.entry) *(.core) *(.text*) } .rodata : { *(.rodata*) json_start = .; *(.data.bin) /* 对应objcopy生成的二进制内容段 */ json_end = .; } .data : { *(.data*) } .bss : { *(.bss*) *(COMMON) } /DISCARD/ : { *(.got*) *(*) } }
同时修改objcopy命令,指定段名:
arm-linux-musleabi-objcopy -I binary -B arm -O elf32-littlearm --rename-section .data=.rodata.bin,alloc,load,readonly,data,contents ./file.json ./build/json.o
这样JSON内容会直接被分配到只读数据段,json_start即为该段起始地址,无需通过GOT访问。
2. 关闭PIE模式,使用绝对/PC相对地址访问
如果不需要位置无关可执行文件,将编译和链接的PIE相关选项替换为非PIE:
arm-linux-musleabi-gcc -o ./build/file.o -c -fno-pie -fno-builtin -ffreestanding -march=armv5t -Os -I . file.c arm-linux-musleabi-gcc -o ./build/file -nostdlib -no-pie -T linker.ld ./build/file.o ./build/json.o -lgcc
编译器会直接生成PC相对的符号访问指令,而非通过GOT跳转。
3. 强制json_start为绝对符号
在链接脚本的.text段内添加绝对符号声明,避免GOT重定位:
.text : { *(.entry) *(.core) *(.text*) *(.rodata*) *(.data*) json_start = ABSOLUTE(json_start); *(.bss*) *(COMMON) }
链接器会将json_start解析为绝对地址,生成符合预期的PC相对访问指令。
内容的提问来源于stack exchange,提问作者Christopher Wrogg
相关产品推荐
相关产品推荐

