Problems with static local variables with relocatable code
Asked Answered
H

2

3

I am building a project which has relocatable code on bare metal. It is a Cortex M3 embedded application. I do not have a dynamic linker and have implemented all the relocations in my startup code.

Mostly it is working but my local static variables appear to be incorrectly located. Their address is offset by the amount that my executable is offset in memory - ie I compile my code as if it is loaded at memory location 0 but I actually load it in memory located at 0x8000. The static local variable have their memory address offset by 0x8000 which is not good.

My global variables are located properly by the GOT but the static local variables are not in the GOT at all (at least they don't appear when I run readelf -r). I am compiling my code with -fpic and the linker has -fpic and -pie specified. I think I must be missing a compile and/or link option to either instruct gcc to use the GOT for the static local variables or to instruct it to use absolute addressing for them.

It seems that currently the code adds the PC to the location of the static local variables.

Hypoblast answered 23/2, 2011 at 16:36 Comment(0)
D
3

I think I have repeated what you are seeing:

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

Which gives:

    .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

I also added startup code and compiled a binary and disassembled to further understand/verify what is going on.

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]

So if you were to change where .text is loaded and were to change where both .got and .bss are loaded relative to .text and that is it then the contents of .got would be wrong and the global variable would be loaded from the wrong place.

if you were to change where .text is loaded, leave .bss where the linker put it and move .got relative to .text. then the global would be pulled from the right place and the local would not

if you were to change where .text is loaded, change where both .got and .bss are loaded relative to .text and modify .got contents to reflect where .text is loaded, then both the local and global variable would be accessed from the right place.

So the loader and gcc/ld need to all be in sync. My immediate recommendation is to not use a static local and just use a global. That or dont worry about position independent code, it is a cortex-m3 after all and somewhat resource limited, just define the memory map up front. I assume the question is how do I make gcc use the .got for the local global, and that one I dont know the answer to, but taking a simple example like the one above you can work through the many command line options until you find one that changes the output.

Dallas answered 23/2, 2011 at 23:57 Comment(7)
Unfortunately for this application I need to get the position independence working. It is good to see that you were able to reproduce what I found so far. Would the following work:Hypoblast
In the linker script set the GOT to be in SRAM. Leave .dynamic in flash so that symbol table can be located. Using the symbol table locate text, data, edata ect. Copy over the GOT from flash and modify the contents?Hypoblast
As I did here I would take a simple example like above make a super simple startup file with not much more than a _Start: and bl fun and build with various linker scripts to see exactly what the toolchain does. Personally I take the KISS approach and everything is in a non-relocatable .text segment, and .bss init doesnt matter because I never read before I write the first time. Basically I am not a gnu linker pro, maybe someone else listening who is may know how to manipulate these segments without having to solve it in the loader.Dallas
How does the code locate the GOT? Does it expect to find it an absolute address in memory or is it also a relative location to the PC? From the first two lines of your example it looks like it is based on the PC - this could be a problem.Hypoblast
It appears that gcc has made it relative to the PC. So if you move the .text then you need to move the .got with it. How far away has to do with the linker script, so if you move .text by x bytes then move .got by x bytes. At least the way gcc has compiled it with the command line options.Dallas
Any idea what compiler options -mpic-register and -msingle-pic-base do?Hypoblast
I think -msingle-pic-base might be important but I don't know how to use it. The assembly code seems to remove all references to the PC when it is used.Hypoblast
S
3

This problem is also discussed here: https://answers.launchpad.net/gcc-arm-embedded/+question/236744

Starting with gcc 4.8 (ARM), there's a command line switch called -mpic-data-is-text-relative which causes static variables being addressed through the GOT as well.

Shirker answered 25/6, 2014 at 12:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.