Analysis: How printf Works on RP2040 at a Low Level#

In order to understand how printf works in the RP2040 UART bridge, let’s compile a simple program, build it with debug symbols, and then run GDB.

Consider this very simple Hello World example:

#include <pico/printf.h>
#include <pico/time.h>

int main() {
        int n = 0;
        stdio_init_all();
        while (true) {
                printf("Hello, world! %d\n", n);
                sleep_ms(1000);
                n = n + 1;
        }
}

Analysis#

../_images/c9a172f5c6ce190e648f88df4af894d4d115b23d2128cad758833395f62d3f5e5d2ef9cb3dc4d695806f538fa26510bf6bc0da2b995b7fceeaff4899.png

See?

=> 0x100002b4 <+12>:    bl      0x10003a64 <__wrap_printf>

It calls __wrap_printf, which means the backend uses -Wl,--wrap=printf internally. If we refer to /home/fadhil_riyanto/git_clone/pico-sdk/src/rp2_common/pico_stdio/stdio.c:347:

../_images/44f66f5c4e0c51a2c0b4bc3885079beb5de306725363b91ba2a38898bf59d0e76fe467ec3abf98eb4881aaf0ce408cf5ebd19b28747ef3720bb99107.png

The signature is different. This function has the signature:

int __printflike(1, 0) PRIMARY_STDIO_FUNC(printf)(const char* format, ...)

So, what exactly happens?

Result#

Let’s look at stdio.c line 289:

../_images/a00ffec057742b32cb76c8db0d3da75c299ceba49a218edf9d559fb31eb9ae029da9b798e2d255ca0af5754dcd7ac62929645ad3aa63155c62bf61a8.png

When the macro PICO_STDIO_SHORT_CIRCUIT_CLIB_FUNCS is active, we replace the function name by WRAPPER_FUNC(x), not stdio_printf.

After that, let’s jump into the pico-sdk compiler.h for a very clear definition.

So, basically, this function:

../_images/b0dbbe24380b5411221dbffcf2cb8409180135d704b9ede610a1e8f3e032a9ce6071551b9c2628a7d85fec6dcb88aa3f8b529d15999cdf75570fdab8.png

calls __wrap_printf.

Make sense?