0
votes

I'm learning to write bare-metal ARM Cortex-A7 firmware to run on QEMU with semihosting. I know that ARM GCC has a libc implementation called newlib, which supports semihosting for common libc functions. So I'm trying to get newlib to work as well.

After addressing a lot of issues, the code is finally running correctly on QEMU: https://github.com/iNvEr7/qemu-learn/tree/master/semihosting-newlib

(Note: QEMU 5.2.0 seems to have a bug that would crash newlib's semihosting call to HEAPINFO, so to run my code on QEMU, you have to compile QEMU master, and use make run target to run the code with QEMU in a tmux session)

However I'd like to find some answers to some of the problems I encountered when integrating with newlib.

To my understanding, newlib, as a libc implementation, provides a crt0 routine that initialize the application's memory region, including .bss, .data, heap and stack.

However, from my tests, the crt0 that GCC linked with doesn't initialize the .bss and .data region, and would crash the later crt0 routine because of that.

So I had to write my own initialization code for .bss and .data in order for it to run correctly.

So I want to understand if I'm doing it the right way? Did I missing something that would instead enable newlib to initialize these regions for me? Or is it conventional to do the initialization myself?

Note: I'm using arm-none-eabi-gcc stable 9-2019-q4-major

1
you confirmed that you linked the newlib start file and examined the newlib crt sources but despite that it didnt link that file? Or when you examined newlib it's doesnt have a bootstrap file that initializes .data and .bss? (is this a linker problem or a code problem)?old_timer
gcc doesnt link the linker does, gcc launches the linker if you let it but in this case you might want to control the linker yourself. YMMV If you let gcc call the linker then you need to make sure it is using the file you want and is putting it in the right placeold_timer
the bootstrap code and the linker script have an intimate relationship, they are a pair/set (for the folks that choose to overcomplicate the linker script (most everyone))old_timer
@old_timer I confirmed that the linker linked a crt0 and through dissaembly it seems to be the newlib crt0 except it doesn't have the .bss and .data init code. Although newlib source seems to have init .bss code: github.com/bminor/newlib/blob/… but I didn't find .data init code in newlib source.iNvEr7
One remark: you should probably not use the toolchain for Cortex-M , and use the toolchain for Cortex-A instead.Frant

1 Answers

0
votes

It seems like I'm hitting a bug in newlib itself, and my current code is running fine because of some random luck.

So I updated my toolchain to gcc-arm-none-eabi-10-2020-q4-major and tried to compile the same code. This time it crashes again.

So I attached GDB and stepped through the ctr0 assembly code trying to figure out why.

It turns out that this line of code is loading the label's address to r1, but it should be loading the content in that label's address, i.e. ldr r1, .LC0 instead of adr r1, .LC0 .

The consequence of this typo is that the returned data from the heapinfo semihosting call is overwriting other data after that label, which contains information about the memory regions. It in turns affected the .bss initialization code later in the crt0 routine. With my previous test using an older toolchain it luckily runs without crashes, but with latest toolchain such error is causing fatal crashes.

I also realized that the 5.2.0 QEMU crash may also be caused by this newlib bug, instead of a QEMU problem. Somehow the master QEMU version behaved differently making the crash to dissapear.

I have submitted a patch to newlib. It surprised me that such a fatal mistake can slip through so many years without notice while it can be revealed by a simple hello world program.

Anyway, it seems my question is also answered by my digging. If newlib was working correctly, it should have initialized .bss section. But there's no code in newlib to initialize .data section, and we have to do that manually for bare-metal.


Plot twist: got back from newlib mailing list. It turns out the newlib's implementation is indeed correctly conforming to the ARM spec:

https://developer.arm.com/documentation/100863/0300/Semihosting-operations/SYS-HEAPINFO--0x16-?lang=en

Where "the PARAMETER REGISTER contains the address of a pointer to a four-field data block."

It's instead QEMU made an misinterpretation and wrote to the wrong address. Will file an issue with QEMU instead.