ARM裸机PIC二进制全局变量地址异常问题求助
ARM裸机PIC/PIE全局变量地址问题解决方案
你的问题核心在于:裸机环境没有动态链接器,直接用-fpic编译的代码无法自动处理全局变量的绝对地址重定位,导致返回的是变量相对于二进制的偏移而非实际加载后的绝对地址。以下是具体解决思路和步骤:
1. 改用PIE而非PIC
-fpic是为共享库设计的,依赖动态链接器处理GOT(全局偏移表);而-fpie是为位置独立可执行文件设计的,更适合裸机场景,配合链接时的-pie选项,能生成无需动态链接器的位置独立代码。
修改编译链接命令
把原来的命令调整为:
# 用-fpie编译,生成PIE目标文件 $(ARMCC)-gcc -fpie -mcpu=cortex-a7 main.c -o main.o # 链接时加-pie选项,生成PIE可执行文件 $(ARMCC)-ld -pie main.o -T linker.ld -o main.elf # 转成二进制 $(ARMCC)-objcopy -O binary main.elf main.bin
(注:你原来的链接命令里重复了main.o,这是笔误,需要去掉一个)
2. 更新链接脚本,添加GOT段
PIE代码需要GOT段存储全局变量的偏移信息,所以要在linker.ld中添加.got段:
OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") OUTPUT_ARCH(arm) ENTRY(_start) SECTIONS { . = ALIGN(4); .text : { _text_start = .; *(.text) _text_end = .; } /* Read only data */ . = ALIGN(4); .rodata : { _rodata_start = .; *(.rodata) _rodata_end = .; } /* 新增GOT段,用于PIE的全局变量重定位 */ . = ALIGN(4); .got : { _got_start = .; *(.got) _got_end = .; } /* BSS, Block starting symbol */ . = ALIGN(4); .bss : { _bss_start = .; *(.bss) _bss_end = .; } /* Regular data */ . = ALIGN(4); .data : { _data_start = .; *(.data) _data_end = .; } }
3. 在启动代码中初始化GOT基地址
ARM架构的PIE代码约定用r9寄存器存储GOT基地址,需要在启动汇编代码中计算并设置这个基地址(因为裸机没有动态链接器帮你做这件事):
.global _start _start: ; 计算GOT的实际运行时基地址 adr r0, .Lgot_ref ; 获取当前PC相对的.Lgot_ref地址 ldr r1, =_got_start ; 加载链接时GOT的起始地址 sub r0, r0, r1 ; 计算加载偏移:当前位置 - 链接时GOT地址 add r9, r1, r0 ; 实际GOT基地址 = 链接时地址 + 加载偏移,存入r9 ; 调用main函数 bl main ; 程序结束后死循环 loop: b loop .Lgot_ref: .word _got_start
这段代码的作用是:根据程序实际加载的地址,动态计算GOT的基地址,确保C代码中访问全局变量时,能通过GOT正确获取到绝对地址。
原理说明
当你用-fpie/-pie编译链接后,全局变量的访问会通过GOT间接获取:代码中会先从GOT中读取变量的偏移,再加上GOT基地址得到变量的绝对地址。启动代码中初始化GOT基地址后,main()返回的&foo就会是实际加载后的绝对地址,而非偏移量。
内容的提问来源于stack exchange,提问作者user16506491
相关产品推荐
相关产品推荐

