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.