-
Notifications
You must be signed in to change notification settings - Fork 63
Use picolibc instead of newlib-nano #134
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
9cd8235 to
50a91d4
Compare
85833fd to
01be64f
Compare
|
These are (finally) passing Zephyr tests using both SDK 0.17 and SDK 0.18-alpha4 (although each requires some additional changes which are wending their way upstream). I think they're ready for review and help getting them upstream. |
| /* Reset current position for subsequent sections */ | ||
| . = LOADADDR(.ER_CODE_SRAM) + SIZEOF(.ER_CODE_SRAM); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did the linker get confused exactly? Is this really needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
. got set to LOADADDR instead of ADDR when building with binutils 2.43. I'm afraid that after spending several hours chasing this down, I didn't dig into the linker code to figure out precisely why that happened.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've re-added this patch as it is necessary even with SDK 0.17.4.
tomi-font
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Had a chat with upstream about these (picolibc) changes. Let's keep them only in this fork until they're not needed anymore (TF-M is moving away from C library usage), in the next release or the one following it.
But it would be good to have someone who understands this a bit to review the changes. Don't know who that could be. cc @frkv @d3zd3z @ceolin @dleach02 @Vge0rge
Sounds good. I'll keep running tests as this series moves forward. This is not buildable using SDK 0.17.2 because of a last-minute oops in that release, but it should be buildable with the upcoming SDK 0.17.3. |
|
I recommend @wearyzen and @adeaarm give it a quick look from TF-M's perspective. The Clang/LLVM toolchain for TF-M was added with no dependency on libc and/or picolib in the core TF-M code, but you still see references to nano.specs in the GNUARM toolchain Zephyr, making this choice for GNUARM toolchain (this change), will take any penalty related to support, any security advisories etc... But the choice to go to picolibc is an active choice in the Zephyr community, and in extension it will impact any vendor who makes use of standard c library function in PSA crypto and PSA crypto driver build as well as in bootloaders... With regards to these commits, I think we should consider them to be marked as commits emitted from zephyr integration, unless they are possible to add upstream in the TF-M project... If they are out-of-tree patches required for Zephyr integration, they would need to be maintained across updated versions of TF-M in our fork |
Yeah this is something we've agreed on and I think just needs to be implemented (documenting the practice basically). In the meantime I don't think we can require that of anyone yet. |
|
True, TF-M plans to remove Additionally, you might want to consider these recent fixes in the scripts: |
|
Sounds like any libc-specific changes will be transient then, which will be nice. |
This adds linker script bits and compiler options so that trusted-firmware-m will build with picolibc. This has not been merged to the Zephyr trusted-firmware-m repository yet, that PR is zephyrproject-rtos/trusted-firmware-m#134 Signed-off-by: Keith Packard <keithp@keithp.com>
This adds linker script bits and compiler options so that trusted-firmware-m will build with picolibc. This has not been merged to the Zephyr trusted-firmware-m repository yet: zephyrproject-rtos/trusted-firmware-m#134 Signed-off-by: Keith Packard <keithp@keithp.com>
This adds linker script bits and compiler options so that trusted-firmware-m will build with picolibc. This has not been merged to the Zephyr trusted-firmware-m repository yet: zephyrproject-rtos/trusted-firmware-m#134 Signed-off-by: Keith Packard <keithp@keithp.com>
|
While trying to build the non-secure MPS4 board with this PR and Zephyr SDK 0.18.0-alpha4 we see below error: Referring to the changes in this PR, I tried below patch and it seems to fix the issue: I am not a linker script expert but if above change looks good then please let me know if you would like to include it in this PR. |
|
@keith-packard I attached here the linker for when building the TF-M regression tests for nRF54L15. The symbols which I confirmed that they are not initialized correctly are:
There are probably more, I just found these until now. But these are normal global static variables which are placed in .data as expected. So maybe it is indeed crt related and not TLS after all? EDIT I did some more digging and it seems that the problem is not the linker script at all. The problem is simpler, the function picolibc_startup is never called for me. So the __copy_table_t which is initialized in this function and sets the values of the global initialized objects is never actually copied. I confirmed that by manually implementing the function inside the ResetHandler and then I could run and pass the TF-M regression tests. This was introduced with this PR because the cmsis header changes the __PROGRAM_START define based on the picolibc: So before this PR the define was pointing to the cmsis_start in the same file which copies the table but now that it is not used the global data are not initialized. EDIT2 I am wondering if this is because the TF-M main function belongs to a separate target (tfm_spm) compared to the picolib_startup source file. So since there is no main function in the target that the picolib_startup belongs to the constructor attribute does nothing? I did more checks with 2 Nordic devices, I run the TF-M tests and some TF-M applications and all work fine as long as I include the logic of the picolib_startup in the reset handler. So Keith please give a proposal on how to resolve the issue and we can move forward with this from Nordics side. |
e9b7412 to
51b9141
Compare
|
I've pushed an update which omits the linker script changes as those aren't necessary unless something needs thread local storage (which doesn't appear to be true in the current code). |
|
@keith-packard I cannot build with it anymore because of the same issues as here: But as I said before the problem at least for Nordic devices is not the linker script. The problem is that the "constructor" attribute doesn't seem to work as expected for me. Maybe @etienne-lms can also try in ST devices if the issue that I described exists there as well and maybe then we can try to overcome this issue here and keep your previous revision of the linker files which seems to work? |
51b9141 to
5a23cb8
Compare
As far as I can tell, If you can debug the initialization sequence of this firmware, you should be able to verify that picolibc's In any case, I've re-added the linker script changes, this time as a separate commit so that they can be tested in isolation. That patch now contains changes for the mps4 targets which should resolve the issue referenced above. There are a lot more linker scripts that could be modified; I'm unclear as to which of those should get included in this series as I can only test those used by Zephyr CI. |
5a23cb8 to
8c948b5
Compare
|
Ok, I did a bunch more digging today and discovered that the existing patch didn't track picolibc 1.8.10 changes in initialization code. That version changed the symbols used as the bounds for the constructor array; when undefined, the array is assumed to be empty (!) so no constructors would run. So sorry that I didn't dig into this in more detail earlier. In any case, I've radically restructured the patch so that the TLS linker script changes are not necessary as those are difficult to get correct without testing. The only linker script changes in the current series add aliases for existing symbols that match what picolibc expects for the constructor list and the heap bounds. picolibc.c now defines the picolibc-specific memory initialization symbols so that if it isn't included the link will fail, providing insurance that the initialization constructor will be included. That involved adding picolibc.c to a pile of target_sources calls, but each addition was a place which would have failed to work before. I've left the series as a sequence of changes on top of the old bits in case anyone wants to see how things have changed. Once we're happy, I'd propose restructuring the series to allow for bisecting; performing the link script and CMakeLists.txt changes first, then adding picolibc.c and finally updating the toolchain files to enable picolibc. |
|
Oh, as a bonus feature, I've also updated the llvm linker scripts -- those will also need the new constructor bounds symbols once ARM updates that toolchain to use picolibc 1.8.10. |
8c948b5 to
86e8958
Compare
This adds linker script bits and compiler options so that trusted-firmware-m will build with picolibc. This has not been merged to the Zephyr trusted-firmware-m repository yet: zephyrproject-rtos/trusted-firmware-m#134 Signed-off-by: Keith Packard <keithp@keithp.com>
|
and pushed another small set of changes that just stops using picolibc's crt0 entirely; we don't need any CPU initialization it might do, we just need memory set up and constructors run. |
|
Hey @keith-packard, thanks a lot for the detailed investigation! I tried the new version of this PR and I still could not boot TF-M in Nordic devices. I did some debugging and I can now confirm that the code to copy tables is now executed form picolib as you described. The issue that I found now is that the linker script does not include the TFM_BSS section in the zero table, so I added this patch: And this allowed me to boot and run the tests, do you think that you can include this patch in your commits? |
That's good to know; I had assumed that all of the initialization required was included in those tables. It would be easy enough to add an explicit initialization of the BSS segment separate from the table in case other linker scripts are also missing this element, but your change avoids the extra code that would involve.
I'll certainly fix it; I'll go investigate and see if it seems like it will be easier to just add explicit code so that we don't end up with picolibc-specific additions to the image (the other linker script changes only add symbols if needed and don't add anything to the image). |
I found that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks again for the fix @keith-packard . I can now confirm that this patch works for Nordic devices :)
Feel free to rework the history and let me know when you are done so that I can approve this.
Small suggestion, it would be nice in my opinion to include tags like the one used in mbedtls fork of Zephyr (https://github.com/zephyrproject-rtos/mbedtls/blob/zephyr/README.md) when you do the rewrite. I am not gonna block the PR for this since this is not an official rule in this repo yet but I am just suggesting it.
Edit: I ended up approving so that I don't NACK you anymore. If you rewrite it will anyway request my review again.
When processing the .ER_CODE_SRAM section, the location counter is set
to the VMA of that section, so after processing the next section will
use that VMA instead of the next available address in the FLASH
region.
Reset the location counter to the next available FLASH address after
processing this section will ensure that any subsequent FLASH data
is placed in the correct location.
This test fails without this patch:
$ west build -p -b lpcxpresso55s69/lpc55s69/cpu0/ns \
samples/synchronization -T sample.kernel.synchronization
.../ld: address 0x14003e84 of bin/tfm_s.axf section `.ER_TFM_CODE'
is not within region `FLASH'
.../ld: address 0x14005c20 of bin/tfm_s.axf section `.TFM_UNPRIV_CODE'
is not within region `FLASH'
.../ld: bin/tfm_s.axf section `.TFM_PSA_ROT_LINKER_DATA'
will not fit in region `FLASH'
.../ld: address 0x14003e84 of bin/tfm_s.axf section `.ER_TFM_CODE'
is not within region `FLASH'
.../ld: address 0x14005c20 of bin/tfm_s.axf section `.TFM_UNPRIV_CODE'
is not within region `FLASH'
.../ld: ERROR: CMSE stub (.gnu.sgstubs section) too far (0x10008540)
from destination (0x14003ee4)
Note that the linker is generating addresses within the CODE_RAM
memory region rather than the FLASH region.
Signed-off-by: Keith Packard <keithp@keithp.com>
…mplates While the copy table included the default data segment for these templates, the zero table didn't include the default bss segment. Make sure that area gets initialized to zero using the table-based mechanism instead of relying on the startup code to initialize it explicitly. Signed-off-by: Keith Packard <keithp@keithp.com>
…ipts Picolibc uses different names for the list of constructors and heap limits. Add values for __bothinit_array_start, __bothinit_array_end, __heap_start and __heap_end to every linker script. If not using picolibc, these will not have any effect. Signed-off-by: Keith Packard <keithp@keithp.com>
ad8bd92 to
6f5ffba
Compare
Just pushed a rebase -i that cleans up the sequence a bit and should work through bisect, but the result is exactly the same as what you've already tested (at least according to git diff?)
Sure, happy to tag these with [zep noup] as we're expecting upstream to drop libc entirely, making this whole sequence unnecessary.
Thanks. Really appreciate your help getting this over the line. I'm looking forward to having SDK 1.0 fully supported by Zephyr 4.3, even if it won't be the "official" toolchain. |
tomi-font
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you please add details to the commit message of at least the first commit to briefly explain why this is not submitted upstream and what is expected from upstream related to that? for tracking purposes
|
D
Diff looks good to me as well :) Thanks for adding the tags and for the support as well. Glad to see that moving forward! |
etienne-lms
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've successfully tested on the few supported STM32 platforms.
For info, while a couple of hundred bytes are saved in flash and RAM when using picolib, I also saw that BL2 footprint was increased by more than 2kByte of flash when console traces are enabled. Observed (from build tests) on various platforms, not only the STM32 ones.
platform/ext/common/picolibc.c
Outdated
| @@ -0,0 +1,48 @@ | |||
| /* | |||
| * Copyright © 2025, Keith Packard <keithp@keithp.com> | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Prefer ASCII char only? (replace © with (C))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(C) is meaningless. I'll just delete the ©
1. Support picolibc stdio. There was already picolibc support code
present in the library for the ARM LLVM toolchain. The
conditionals which selected it have been changed to use
__PICOLIBC__ instead of __clang_major__.
2. Add _exit stub. Code using assert or abort end up calling _exit
through the picolibc signal handling code.
3. Switch to picolibc.specs. This is needed for toolchains which
don't use picolibc by default, and can also be used without
trouble in toolchains where picolibc is the default.
4. Define CONFIG_PICOLIBC when using picolibc. This is enabled
by default, but can be disabled on the cmake command line.
5. Define picolibc_startup to initialize all .data and .bss
segments using the standard tf-m arrays.
6. Add picolibc.c to many source lists when CONFIG_PICOLIBC is
defined. If this file is missign, picolibc_startup will
not be defined which should cause a failure at link time.
Signed-off-by: Keith Packard <keithp@keithp.com>
6f5ffba to
7418104
Compare
What are console traces and how are they enabled? There's nothing obvious in the source tree that I could find. If this ends up enabling 'assert' macros, then that can cause a pretty significant jump in flash usage as the default picolibc assert behavior includes I just reviewed the picolibc code and there's no easy way to disable the verbose assert behavior once enabled. If you want to do this, you'd need to do: |
Uh oh!
There was an error while loading. Please reload this page.