2

I am pulling my hear out for quite a while. I am trying to achieve position independent code on a microcontroller. It's quite a rabbit hole. I've gotten so far that I can bring my app online, and responsive for mqtt commands.

The only thing I can't seem to do, for to me completely unknown reasons, is 1 specific stdlib call: vsnprintf or any of its siblings. I am using the arm-none-eabi toolchain version 10.2. I have the source code, also of this specific function, but it goes sooo deep into stuff I just don't understand enough about, so I am stuck on every attempt to get around this problem.

I am using lwip (light weight IP) which makes calls into snprintf. It works fine, until I relocate my app to a different location in flash. The .got section and sram memory is properly patched with the lma_offset I calculate in my custom bootloader. Again, everything works, except this horrible single freaking call into the std lib.

For debugging purposes, I made a wrapper for snprintf in the hope I could drill down just a bit more to understand what the problem is. I am not getting much further.

    int snprintf_override (
        char *__restrict buffer, 
        size_t size, 
        const char *__restrict format, 
        ...)
    {
        int result;
        va_list args;

        va_start(args, format);
        // The next line calls into std lib, and hard faults, I wish I could share anything else that made any sense...
        result = vsnprintf(buffer, sizeof(buffer), format, args);
        va_end(args);

        return result;
    }

Question 1:

Is there anybody who can suggest another way to achieve functionally the same as vsnprintf? Is it even doable to write something from scratch? I have the feeling there is quite some complexity in that function?

Question 2:

Or, is there anybody, who has ANY idea what can be the problem in relocating code which causes svnprintf to fail? Digging through the stdlib code I come across REENT / impurepointers (?), and the implementation relies on a FILE instance. Still "just data" I would say in my naivety, but maybe it's more than that? Is there some assembly hidden somewhere which is simply not relocatable (I can't believe it's not possible, linux OS does nothing else than compile with -fpic...)?

I am afraid this question gets little attention, but maybe, just maybe, somebody with some severe understanding of std lib and/or position independent code clicks on it, and is willing to help me out...

bas
  • 13,550
  • 20
  • 69
  • 146
  • For completeness of the question, if you are using the arm-none-eabi distribution from [arm](https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain), the included libc is usually newlib. – PhilMasteG Jun 10 '22 at 09:35
  • @Lundin, bad wording on my part. I meant the GNU GCC arm-none-eabi distribution that is provided by the arm developer page, that I have linked in my edit of the comment above. – PhilMasteG Jun 10 '22 at 09:39
  • @PhilMasteG Yes well I misunderstood you comment as well, nevermind :) – Lundin Jun 10 '22 at 09:40
  • I link against libc_nano.a, supplied by the arm toolchain if that's what you were asking. The version of the toolchain is 10.3-2021.10. – bas Jun 10 '22 at 10:01
  • For this and many other resons i do not use this implementation only have my own one. expect een more problems whe you use it in RTOS environment. – 0___________ Jun 10 '22 at 10:10
  • I am using freertos. Works fine as well. Only thing i had to do is save r9 on the stack when creating tasks. Hope sonebody can make a suggestion still :) – bas Jun 10 '22 at 10:15
  • *`"Only thing i had to do is save r9"`* did you try to see why? Investigate it :) This "workaround" only hides the problem – 0___________ Jun 10 '22 at 10:20
  • Yes, because r9 stores the address of the entry point of the global offset table. If it gets wiped after task creation, position independent code no longer works (in the task context). Did i earn enough credits now to get some time from you to help on this question? :) – bas Jun 10 '22 at 10:27

1 Answers1

2

I never found out exactly goes wrong in the libc-nano. But, I think I have proof enough that the problem I ran into has everything to do with libc-nano not being compiled with -fpic.

I reproduced the problem in a smaller test setup which consists of:

  • a bootloader (runs from 0x60000000) which copies vtor of app to sram,
  • an app (runs from 0x60020000 or 0x600a0000) which copies global offset table, data, and c++ constructors, to sram, during startup.
  • in the main of the app, I only make a call to stdlib's vsnprintf.
libc-nano picolibc picolibc recompiled with -fpic
app@0x60000000 works works works
app@0x600a0000 hard fault hard fault works

I integrated picolibc (recompiled with -fpic) into my original project. Now everything works perfectly.

bas
  • 13,550
  • 20
  • 69
  • 146