具有可重定位代码的静态局部变量的问题

时间:2011-02-23 16:36:09

标签: arm static-variables relocation fpic

我正在构建一个在裸机上具有可重定位代码的项目。它是Cortex M3嵌入式应用程序。我没有动态链接器,并已在我的启动代码中实现了所有重定位。

主要是它正常工作,但我的本地静态变量似乎位置不正确。它们的地址被我的可执行文件在内存中的偏移量所抵消 - 即我编译我的代码就好像它是在内存位置0加载但我实际上将它加载到位于0x8000的内存中。静态局部变量的内存地址偏移0x8000,这是不好的。

我的全局变量由GOT正确定位,但静态局部变量根本不在GOT中(至少它们在我运行readelf -r时不会出现)。我正在使用-fpic编译我的代码,并且链接器已指定-fpic-pie。我想我必须缺少一个编译和/或链接选项来指示gcc使用GOT作为静态局部变量或指示它使用绝对寻址。

目前似乎代码将PC添加到静态局部变量的位置。

2 个答案:

答案 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寻址静态变量。