Skip to content

Fixes #2839 Picolibc compatibility issues#2840

Open
keith-packard wants to merge 6 commits intoraspberrypi:developfrom
keith-packard:picolibc-fixes
Open

Fixes #2839 Picolibc compatibility issues#2840
keith-packard wants to merge 6 commits intoraspberrypi:developfrom
keith-packard:picolibc-fixes

Conversation

@keith-packard
Copy link
Copy Markdown

@keith-packard keith-packard commented Feb 24, 2026

As a part of the Debian migration from newlib to picolibc for the arm-none-eabi GCC toolchain, a number of picolibc portability issues were discovered in pico-sdk. Here's a set of fixes that allows use of pico-sdk with picolibc 1.8.11.

  • Only use --specs=nosys.specs with newlib.
  • Provide a definition of __printflike in terms of __picolibc_format
  • Ignore -Wredundant-decls messages when building picolibc_interface.c. This makes sure the 'kitchen-sink' example builds with its long list of warnings.
  • Switch from -Wl,--script= to -T when building a linker command line; this avoids referencing picolibc's default linker script. The alternative here would be to add -T/dev/null when using picolibc; other projects have done that to allow continued use of LINKER:--script for compatibility with toolchains which don't support -T (I'm not sure which ones those might be).
  • Add definitions for __bothinit_array_start and __bothinit_array_end to all of the linker scripts to allow picolibc's __libc_init_array function to correctly call constructors.
  • Add picolibc-specific printf format selection arguments to the linker command line when using pico_printf_compiler. This allows applications to use pico-sdk style options instead of needing to use target_linker_options to add the picolibc-specific versions.

A separate PR which stacks an implementation of per-core thread local variables is available as #2846

@lurch lurch linked an issue Feb 24, 2026 that may be closed by this pull request
__libc_init_array();
}

#if !PICO_RUNTIME_NO_INIT_PER_CORE_TLS_SETUP
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The complete removal of these #if directives seems a bit odd?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wasn't sure how these symbols could ever be defined so that I could test to make sure the code operated when set.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dunno if it helps answer your question, but see sections 5.5.19. and 6.1. of https://pip.raspberrypi.com/documents/RP-009085-KB
If that doesn't help (I'm afraid the code here is beyond my level of knowledge), then you'll have to wait until somebody else has time to look at this.

(The reason I found the removal of these directives surprising, is because I'm sure they must have been added for a good reason 😉 )

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty sure they were added because there wasn't any per-core TLS support in pico-sdk before I copied bits over from picolibc's implementation (which is only available when you build picolibc from source; I haven't figured out a good way to expose it without that).

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ahh, a git grep reveals that these are indeed only used by the picolibc's bindings:

$ git grep PER_CORE_TLS_SETUP
src/rp2_common/pico_clib_interface/picolibc_interface.c:#if !PICO_RUNTIME_NO_INIT_PER_CORE_TLS_SETUP
src/rp2_common/pico_clib_interface/picolibc_interface.c:#if !PICO_RUNTIME_SKIP_INIT_PER_CORE_TLS_SETUP
src/rp2_common/pico_clib_interface/picolibc_interface.c:PICO_RUNTIME_INIT_FUNC_PER_CORE(runtime_init_pre_core_tls_setup, PICO_RUNTIME_INIT_PER_CORE_TLS_SETUP);
src/rp2_common/pico_runtime_init/include/pico/runtime_init.h:// PICO_RUNTIME_SKIP_INIT_PER_CORE_TLS_SETUP defaults to 0
src/rp2_common/pico_runtime_init/include/pico/runtime_init.h:#ifndef PICO_RUNTIME_INIT_PER_CORE_TLS_SETUP
src/rp2_common/pico_runtime_init/include/pico/runtime_init.h:#define PICO_RUNTIME_INIT_PER_CORE_TLS_SETUP                 "10060"

I was mistakenly assuming that this was some kind of "standard feature" that all C-lib bindings were expected to support 🤦

Comment on lines +136 to +148
/* Assign TLS offsets and load TLS initialization data */
.tdata :
{
*(.tdata .tdata.* .gnu.linkonce.td.*)
PROVIDE( __tdata_end = . );
} >FLASH

.tbss (NOLOAD) : {
*(.tbss .tbss.* .gnu.linkonce.tb.*)
*(.tcommon)
PROVIDE( __tls_end = . );
PROVIDE( __tbss_end = . );
} >FLASH
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the reason behind moving these sections to FLASH rather than keeping them in RAM?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.tdata needs to be in FLASH so that it's initial contents are available to use in initializing TLS areas, .tbss needs to be sequential in addressing with .tdata so that the thread local offsets computed by the linker are correct. .tbss should not actually allocate any space in FLASH; the addresses generated are used only to compute TLS offsets during relocation. RAM for each of the per-core TLS areas is allocated below.

The previous kludge avoided the need to even initialize the TLS area by wedging it between .data and .bss and using the existing initializers of those to also initialize the TLS area. That relied upon careful layout of the TLS area to account for padding and alignment constraints, which are not entirely correct in the existing scripts.

The new way introduced here is far more traditional, with the exception of the static allocation of two TLS blocks in RAM.

@kilograham kilograham added this to the 2.2.1 milestone Mar 2, 2026
@kilograham kilograham self-assigned this Mar 2, 2026
@kilograham
Copy link
Copy Markdown
Contributor

where can we get/build a GCC compiler that uses picolibc to test with?

@kilograham
Copy link
Copy Markdown
Contributor

@keith-packard can we split the tls stuff into a separate PR - it is going to clash with some other PRs.

@keith-packard
Copy link
Copy Markdown
Author

keith-packard commented Mar 2, 2026

where can we get/build a GCC compiler that uses picolibc to test with?

You can either use the current debian unstable toolchain for arm-none-eabi for which picolibc is the only supported C library, or you can use the debian stable toolchain and add the picolibc package. For the latter, you must build and link with --specs=picolibc.specs. In either case, sudo apt install picolibc-arm-none-eabi should do the trick on any debian or ubuntu system.

For other systems, you can use the Zephyr SDK toolchain which is available for Linux, Mac OS X and Windows.

You can also use the ARM GNU Toolchain version 15.2.rel1 and add the pre-built picolibc add-ons available here: https://github.com/picolibc/picolibc/releases/download/1.8.11/picolibc-arm-none-eabi-1.8.11-15.2.rel1.zip However, this toolchain is flawed in disabling TLS usage entirely, so I recommend against it. The current debian toolchain uses the same sources with slightly different configuration to enable TLS and use picolibc by default.

nosys.specs controls how newlib performs linking, so it's not relevant
to other C libraries.

Signed-off-by: Keith Packard <keithp@keithp.com>
Picolibc doesn't provide __printflike, instead it provides a more
general __picolibc_format for either scanf or printf. Use that to
define our own __printflike version.

Signed-off-by: Keith Packard <keithp@keithp.com>

Gbp-Pq: Name 0005-pico_platform_compiler-Picolibc-compatibility-fix.patch
The kitchen_sink test enables -Wredundant-decls which generates
a warning about the stdout and stderr alias definitions. Disable the
warning as it is incorrect in this case.

Signed-off-by: Keith Packard <keithp@keithp.com>
Using -Wl,--script= hides this option from the compiler driver causing
it to insert it's own linker script when it uses picolibc. Using
the -T option asks it to skip adding its own.

Signed-off-by: Keith Packard <keithp@keithp.com>
Picolibc may combine the preinit and init arrays into a single list,
in which case it will use __bothinit_array_start and
__bothinit_array_end symbols instead of the separate
__preinit_array_start, __preinit_array_end and __init_array_start,
__init_array_end pairs.

Define both so that either method works.

Signed-off-by: Keith Packard <keithp@keithp.com>
When using pico_printf_compiler with picolibc, map the
various RUNTIME_INCLUDE_PRINTF_* options into the matching
picolibc link flags.

Signed-off-by: Keith Packard <keithp@keithp.com>
@keith-packard
Copy link
Copy Markdown
Author

keith-packard commented Mar 2, 2026

@keith-packard can we split the tls stuff into a separate PR - it is going to clash with some other PRs.

Can do. I've posted a separate PR which stacks the TLS changes atop the rest of these so they don't get lost. #2846

@edo9300
Copy link
Copy Markdown

edo9300 commented Mar 3, 2026

for what it's worth, i could successfully use this branch to compile using the picolibc based toolchain https://wonderful.asie.pl/, (only things i had to do were explicitly set the libc to use in the cmake and toolchain path)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

pico-sdk doesn't work when compiling with picolibc

5 participants