STM32, Position independent code - function pointers not in GOT?

Asked by Robert Sedláček on 2017-10-17

I need a position independent code (PIC) working on STM32F401. But i have problem with pointers to functions used e.g. in struct.

Short example:

    struct process {
      struct process *next;
      const char *name;
      PT_THREAD((* thread)(struct pt *, process_event_t, process_data_t));
      struct pt pt;
      unsigned char state, needspoll;
    };

    process etimer_process...

    static void call_process(struct process *p, process_event_t ev, process_data_t data) {
      int ret;
      ret = p->thread(&p->pt, ev, data);
    }

After diassembly:

    Disassembly of section .data:
       ...
     20000768 <etimer_process>:
     20000768: 00000000 andeq r0, r0, r0
     2000076c: 0803b134 stmdaeq r3, {r2, r4, r5, r8, ip, sp, pc}
     20000770: 08027435 stmdaeq r2, {r0, r2, r4, r5, sl, ip, sp, lr}
     20000774: 00000000 andeq r0, r0, r0
       ...

    Disassembly of section .text:
       ...
     8027da4: 68fb ldr r3, [r7, #12] //R3 = 0x20000768
     8027da6: 689b ldr r3, [r3, #8] //R3 = 0x8027435
       ...
     8027db2: 4798 blx r3 //Branch to R3
       ...

Branch address in R3 is wrong for code at offset different than 0 I don't see use of GOT here. It is a bug or missing compiler/linker option?

----

Things which i have probably successfully solved:

 - GOT fixup
 - Interrupt table fixup
 - Newlib compiled with appropriate cflags

Used CFLAGS:

> -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m4 -fsingle-precision-constant -Wdouble-promotion -msoft-float -fpic -msingle-pic-base -mpic-data-is-text-relative -mpic-register=r10 -Wno-strict-aliasing -lc

and linking with

> -fpic

Question information

Language:
English Edit question
Status:
Answered
For:
GNU ARM Embedded Toolchain Edit question
Assignee:
No assignee Edit question
Last query:
2017-10-23
Last reply:
2017-10-31
Tejas Belagod (belagod-tejas) said : #1

Hi Robert,

Can you please provide a test case that I can compile to reproduce the issue?

Thanks,
Tejas.

Robert Sedláček (sedlons) said : #2

Hi Tejas,
I just created example project. Download here: http://www.vranet.cz/sedlons/stm32f4-example.tar.gz

In test.objdump is problem visible e.g. at 0x800c56a.

(At the begin, in the register R10 is offset. This value is used for computing GOT and VTOR. After then R10 point to GOT. )

Robert Sedláček (sedlons) said : #3

I prepared simpler example.

Simple example code in test.c:
    void foo(void) {}

    typedef struct {
        int intValue;
        void (*ptrFoo) (void);
    } funcPtr_t;

    funcPtr_t funcPtr = {0, &foo};

    int main() {
        funcPtr.intValue = 12345;
        funcPtr.ptrFoo();
    }

Compile with:
    arm-none-eabi-gcc -mlittle-endian -mthumb -mthumb-interwork -mcpu=cortex-m4 -fpic -msingle-pic-base -mpic-data-is-text-relative -mpic-register=r10 test.c -S

This generates code, where the problem is at line 58:
      ...
    ldr r3, .L4
    ldr r3, [r10, r3]
    ldr r3, [r3, #4]
    blx r3
     ...

Of course, one solution is use of function referencing only at runtime. But this can lead to some unexpected error in future (after forgotten this rule).

Tejas Belagod (belagod-tejas) said : #4

Hi Robert,

Using GNU Tools for ARM Embedded Processors 6-2016-q4-major, I used your command-line and compiled your test case to generate an executable. This is my observation:

Using 'arm-none-eabi-objdump -Dr a.out', I get:

.....
main:
    8210: b580 push {r7, lr}
    8212: af00 add r7, sp, #0
    8214: 4b07 ldr r3, [pc, #28] ; (8234 <main+0x24>) // <---- load the GOT entry value (0xc)
    8216: f85a 3003 ldr.w r3, [sl, r3] // <----- load the GOT entry (0x00019c8c) which is ptr to funcPtr
    821a: 461a mov r2, r3
    821c: f243 0339 movw r3, #12345 ; 0x3039
    8220: 6013 str r3, [r2, #0] // <------ store 12345 at *(0x00019c8c) = 12345
    8222: 4b04 ldr r3, [pc, #16] ; (8234 <main+0x24>) // <------ load the GOT entry value (0xc)
    8224: f85a 3003 ldr.w r3, [sl, r3] // <------ load the GOT entry (0x00019c8c) which is ptr to funcPtr
    8228: 685b ldr r3, [r3, #4] // <------ load the value at offset 4 from start of funcPtr (foo)
    822a: 4798 blx r3 // <------- branch to foo.
    822c: 2300 movs r3, #0
    822e: 4618 mov r0, r3
    8230: bd80 pop {r7, pc}
    8232: bf00 nop
    8234: 0000000c andeq r0, r0, ip

...

Disassembly of section .got:

00019b64 <_GLOBAL_OFFSET_TABLE_>:
        ...
   19b70: 00019c8c andeq r9, r1, ip, lsl #25

Disassembly of section .data:

00019b78 <__data_start>:
   19b78: 00000000 andeq r0, r0, r0

00019b7c <HeapBase>:
   19b7c: 00000000 andeq r0, r0, r0

00019b80 <HeapLimit>:
   19b80: 00000000 andeq r0, r0, r0

00019b84 <__stack_base__>:
   19b84: 00000000 andeq r0, r0, r0

00019b88 <StackLimit>:
   19b88: 00000000 andeq r0, r0, r0

00019b8c <CommandLine>:
        ...

00019c8c <funcPtr>:
   19c8c: 00000000 andeq r0, r0, r0
   19c90: 00008205 andeq r8, r0, r5, lsl #4

I can't see what's wrong here. What am I missing?

Thanks,
Tejas.

Robert Sedláček (sedlons) said : #5

Hi Tejas,
i suppose two things: section .text is in flash memory and section .data is in ram (but initialized from flash at program startup). So the funcPtr is copied from flash to ram with address of foo function.

The foo function address at 0x19c90 is correctly used only in case you load code at address for which the code was linked.

In case the code is loaded with offset 0x1000, the foo function will be at 0x9204. But because section .data is initialized from flash, in funcPtr is still value 0x8205. The resulting code is not position independent without any warning or error.

(Using GNU ARM 6_2017_q2_update binary version)

Thanks,
Robert

Tejas Belagod (belagod-tejas) said : #6

Hi Robert,

Ok, I get your point. Few things I'd like to understand with your setup:

1. Are you running 'bare-metal' (no OS) or under some RTOS?
2. If you're running baremetal, how are you planning to achieve position independence without a loader?
3. If running under an RTOS that supports a loader and dynamic linking, then your loader is probably not resolving the dynamic relocation correctly at runtime.

Can you help me understand what exactly you're trying to achieve with position independence?

Thanks,
Tejas.

Robert Sedláček (sedlons) said : #7

Hi Tejas,

1. Im using Contiki OS.
2. I have simple bootloader, which determine, which firmware will run.

Iam developing IoT device and i need OTA firmware update. In the flash of STM32 will be two or maybe three firmware at different locations. In case of any error, the bootloader run another firmware.

Thanks,
Tejas.

Tejas Belagod (belagod-tejas) said : #8

OK thanks for the info. I understand a bit better now. How have you compiled all your firmware binaries? Are they shared objects? If so, does your loader support dynamic linking?

Tejas Belagod (belagod-tejas) said : #9

>In the flash of STM32 will be two or maybe three firmware at different locations. In case of any error, the bootloader run another firmware.

So, I'm guessing you're not looking to statically link them all into the same binary?

Robert Sedláček (sedlons) said : #10

No, my bootloader do not support dynamic linking. I plan to change another firmware over the air from currently running firmware.

Firmware binary must be small as posible. I plan to have 10s of mesh networks, each with over 100s of devices. Because all routes to each devices are held in ram, i can be limited. Each received part of firmware will be immediately written to flash.

Tejas Belagod (belagod-tejas) said : #11

Hi Robert,

Sorry for the late reply. OK, so you want to keep part of your application constant while changing firmware with OTA updates?

Can you help with this problem?

Provide an answer of your own, or ask Robert Sedláček for more information if necessary.

To post a message you must log in.