Position Independent Code, GCC versus ARMCC

354 Views Asked by At

We have a bunch of C code that needs to be compiled as position independent code on an embedded system (Cortex-M7). This all works fine with ARMCC (--ropi --rwpi).

Now we need to do the same under GCC (12.2 release 1).

The system we are loading the code with (ThreadX Modules) handles using r9 as an offset register for the Global Offset Table and offers an example which employs the following compiler options:

-fpie -fno-plt -mno-pic-data-is-text-relative -msingle-pic-base

...and no particular linker otions.

The problem we are having is that any attempt to take the address of a global variable does not work under GCC (and does work under ARMCC). For instance:

char buffer[100];
char* pBuffer = buffer;

int appModMain()
{
    printf("buffer address: %p, buffer address via pBuffer: %p \n", buffer, pBuffer);
}

...results in a printed output of the form:

buffer address:  60C8C25C, buffer address via pBuffer: 1000025C

In other words pBuffer is not being offset correctly. This is clear when you look at the disassembled code:

00000188 <appModMain>:
     188:   b580        push    {r7, lr}
     18a:   af00        add r7, sp, #0
     18c:   4b1a        ldr r3, [pc, #104]  ; (1f8 <appModMain+0x70>)
     18e:   f859 3003   ldr.w   r3, [r9, r3]
     192:   681b        ldr r3, [r3, #0]
     194:   461a        mov r2, r3
     196:   4b19        ldr r3, [pc, #100]  ; (1fc <appModMain+0x74>)
     198:   f859 3003   ldr.w   r3, [r9, r3]
     19c:   4619        mov r1, r3
     19e:   4b18        ldr r3, [pc, #96]   ; (200 <appModMain+0x78>)
     1a0:   f859 3003   ldr.w   r3, [r9, r3]
     1a4:   4618        mov r0, r3
     1a6:   f000 f8bb   bl  320 <printf>
     1aa:   f640 30b8   movw    r0, #3000   ; 0xbb8

When loading-up the variable to put into r2, which is going to be passed to printf() as pBuffer, r3 needs to be adjusted, yet it is not.

We've tried various permutations of -mpic-data-is-text-relative/-mnopic-data-is-text-relative and -msingle-pic-base/-mnosingle-pic-base but none of them make a difference: we just cannot make a pointer reference to a global variable work. Unfortunately we can't change the source code, there's quite a large amount of it and some if it is not ours.

Does anyone have any advice as to how we might make GCC comply?

EDIT:

In the disassembled output, the contents of 1f8 and 1fc for the example above are:

     1f8:   000001bc
     1fc:   000001b0

...in the .got section of the disassembled output, these offset are towards the end, here:

Disassembly of section .got:

00006b88 <__ctors_end__>:
...
    6d34:   1002c5a8
    6d38:   1000025c    ; 6b88 + 1b0
    6d3c:   00001e15
    6d40:   1002c5ac
    6d44:   100001d0    ; 6b88 + 1bc

...and 100001d0 in the disassembled output turns out to be:

Disassembly of section .data:

...

100001d0 <pBuffer>:
100001d0:   1000025c

...while 1000025c in the disassembled output turns out to be:

Disassembly of section .bss:

...

1000025c <buffer>:
    ...

So pBuffer is initialised by the compiler to the un-offsetted address of buffer, and the compiler knows that pBuffer it is a pointer to something in the GOT, yet it did not do anything to add the offset when it generated the code.

0

There are 0 best solutions below