rp2: Build with nano.specs, add linker cref table#19299
Conversation
|
Code size report: |
|
Thanks for doing this. I like the idea, and on RPI_PICO_W it saves 1624 bytes of flash and 960 bytes of RAM. But... the
I guess that's the cause of the crashes? I tried adding I'd be interested to know exactly what mbedTLS is pulling in from libc that doesn't work with newlib-nano... it's a bit scary that we don't fully understand the dependencies here, especially since ports like stm32 work just fine with @Gadgetoid I'm surprised you didn't see failures with your firmware when using |
Oh wow, sorry! I didn't realise those tests weren't part of the default run-tests.py coverage! I wonder if it's to do with linker placement instead of the actual features, there might be something that's now reading from cached flash. I'll do a bit of spelunking and try to figure this out. |
You mentioned you ran the tests on RPI_PICO2, which won't be able to run the network tests anyway. Eventually octoprobe will help here, by just running all tests regardless. But also probably we should think about merging #16112; I use it all the time now (and is how I tested this PR). |
Useful to see why particular symbols have been pulled in. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
|
Ah, OK... Here's a stack trace from a hanging test (which has actually panicked): I believe what happens is that in the "nano" configuration Because MicroPython on rp2 has no C heap by default, this malloc fails and the pico-sdk malloc implementation panics.
|
It looks like the way around this is to do what stm32 does for mbedtls_port.c and provide our own gmtime implementation layered on top of timeutils. Will take a crack at this, it should drop out a bunch of the other libc code which is being pulled in. |
Interesting. Maybe that's why @Gadgetoid did not see any failures, if the C heap was enabled. |
b353960 to
52ee6ff
Compare
|
This was easier than I thought, just need to patch mbedTLS to call gmtime_r instead of gmtime(). Before marking this as ready I'm going to audit the remaining code paths that lead to (There's also possibly some follow-up work we could do to replace all of libc time functions with timeutils on rp2, would possibly save some code size.) |
52ee6ff to
54511f1
Compare
Octoprobe PR report
FailuresGroup: run-tests.py
Group: run-tests.py --via-mpy --emit native
|
|
The above octoprobe failures of |
As far as I can tell, the other cross-references shown here aren't actually being linked into the final binary. At least, it doesn't look like it - and if I try to stub out calls like In any case, with the green Octoprobe run I think this is ready. |
54511f1 to
df352bd
Compare
|
Updated the PR to also add --specs=nano.specs on the compiler command line, as this adds the newlib-nano standard directory to the search path. This doesn't seem to actually change anything in the build that I can see, but it's good practice. Ideally we'd also pass it when building pico-sdk files, but I can't find a clean way to do this (probably should talk to the pico-sdk maintainers about adding support properly). |
dpgeorge
left a comment
There was a problem hiding this comment.
This looks good now. I tested on RPI_PICO2_W in RISCV mode, running the network tests, and it passes.
358a27b to
de7adc0
Compare
Reduces binary, static RAM, and (probably) runtime memory usage. Necessary mbedTLS fix to prevent it calling the non-reentrant gmtime(), which allocates the time buffer on demand with newlib-nano. This work was funded through GitHub Sponsors. Signed-off-by: Angus Gratton <angus@redyak.com.au>
de7adc0 to
c65b256
Compare
Plausible. I had the C heap enabled long before experimenting with this. Edit: Note with respect to catching panics on Pico/Pico 2, I proposed this: https://github.com/orgs/micropython/discussions/18736 - I believe this might make a test fail with a useful message (depending on the verbosity of the panic) and avoid needing to sleuth quite so much in cases like this. |
Summary
Passes
--specs=nano.specson the gcc compiler & linker command line for rp2, meaning newlib-nano is used instead of regular newlib. As mentioned by @Gadgetoid in #11143. This reduces both the binary size and (significantly) the static RAM usage. Should also save some RAM at runtime, particularly the stack footprint of libc printf.Includes a commit to add the
--creflinker argument, which adds a cross-reference table in the linker map file. This is useful to figure out how/why certain symbols are linked.The specific libc pieces which are linked in the default rp2 build seem to be malloc/free/realloc and some printf support.
pico_utilqueue.c and time.c, and operator new & delete (unclear why these are pulled in at all!) Can find via__wrap_freein the cross-reference table. This feels like with a small amount of work it could be removed, maybe...? Particularly as the default rp2 build has no C heap available.shared/readline, andshared/netutils. Can find via__wrap_snprintfin the cross-reference table. This feels like it'd be harder to remove the dependency for.I don't think any of these use cases require "full" newlib support.
The best description I know for gcc specs and nano vs normal newlib is https://metebalci.com/blog/demystifying-arm-gnu-toolchain-specs-nano-and-nosys/
Testing
Trade-offs and Alternatives
Generative AI
I did not use generative AI tools when creating this PR.