我正在构建一个在裸机上具有可重定位代码的项目。它是Cortex M3嵌入式应用程序。我没有动态链接器,并已在我的启动代码中实现了所有重定位。
主要是它正常工作,但我的本地静态变量似乎位置不正确。它们的地址被我的可执行文件在内存中的偏移量所抵消 - 即我编译我的代码就好像它是在内存位置0加载但我实际上将它加载到位于0x8000的内存中。静态局部变量的内存地址偏移0x8000,这是不好的。
我的全局变量由GOT正确定位,但静态局部变量根本不在GOT中(至少它们在我运行readelf -r
时不会出现)。我正在使用-fpic
编译我的代码,并且链接器已指定-fpic
和-pie
。我想我必须缺少一个编译和/或链接选项来指示gcc
使用GOT作为静态局部变量或指示它使用绝对寻址。
目前似乎代码将PC添加到静态局部变量的位置。
答案 0 :(得分:3)
我想我已经重复了你所看到的:
statloc.c unsigned int glob; unsigned int fun ( unsigned int a ) { static unsigned int loc; if(a==0) loc=7; return(a+glob+loc); }
arm-none-linux-gnueabi-gcc -mcpu=cortex-m3 -Wall -Werror -O2 -nostdlib -nostartfiles -ffreestanding -mthumb -fpic -pie -S statloc.c
给出了:
.cpu cortex-m3
.fpu softvfp
.thumb
.text
.align 2
.global fun
.thumb
.thumb_func
fun:
ldr r3, .L6
.LPIC2:
add r3, pc
cbnz r0, .L5
ldr r1, .L6+4
movs r2, #7
.LPIC1:
add r1, pc
ldr ip, .L6+8
str r2, [r1, #0]
ldr r1, [r3, ip]
ldr r3, [r1, #0]
adds r0, r0, r3
adds r0, r0, r2
bx lr
.L5:
ldr ip, .L6+8
ldr r2, .L6+12
ldr r1, [r3, ip]
.LPIC0:
add r2, pc
ldr r2, [r2]
ldr r3, [r1, #0]
adds r0, r0, r3
adds r0, r0, r2
bx lr
.L7:
.align 2
.L6:
.word _GLOBAL_OFFSET_TABLE_-(.LPIC2+4)
.word .LANCHOR0-(.LPIC1+4)
.word glob(GOT)
.word .LANCHOR0-(.LPIC0+4)
.size fun, .-fun
.comm glob,4,4
.bss
.align 2
.LANCHOR0 = . + 0
.type loc.823, %object
.size loc.823, 4
loc.823:
.space 4
我还添加了启动代码并编译了二进制文件并进行了反汇编,以进一步了解/验证发生了什么。
this is the offset from the pc to the .got ldr r3, .L6 add pc so r3 holds a position independent offset to the .got add r3, pc offset in the got for the address of the glob ldr ip, .L6+8 read the absolute address for the global variable from the got ldr r1, [r3, ip] finally read the global variable into r3 ldr r3, [r1, #0] this is the offset from the pc to the static local in .bss ldr r2, .L6+12 add pc so that r2 holds a position independent offset to the static local in .bss add r2, pc read the static local in .bss ldr r2, [r2]
因此,如果您要更改.text被加载的位置并且要更改.got和.bss相对于.text加载的位置,那么.got的内容将是错误的并且全局变量将是从错误的地方装载。
如果您要更改.text的加载位置,请将.bss保留在链接器放置的位置,并将.got相对于.text移动。然后全球将从正确的地方拉出来,而本地将不会
如果要更改加载.text的位置,请更改相对于.text加载.got和.bss的位置,并修改.got内容以反映加载.text的位置,然后本地和全局变量都将是从正确的地方访问。
所以加载器和gcc / ld需要全部同步。我的直接建议是不使用静态本地,只使用全局。那个或者不担心位置无关的代码,毕竟它是一个cortex-m3并且资源有限,只需要预先定义内存映射。我假设问题是我如何让gcc使用.got作为本地全局,并且我不知道答案,但是采用上面的一个简单示例,你可以通过许多命令行选项,直到找到一个改变输出的。
答案 1 :(得分:3)
此处还讨论了此问题:https://answers.launchpad.net/gcc-arm-embedded/+question/236744
从gcc 4.8(ARM)开始,有一个名为-mpic-data-is-text-relative
的命令行开关,它也会导致通过GOT寻址静态变量。