1# Picolibc and Operating Systems 2 3Picolibc is designed to be operating-system independent, so it doesn't 4embed any operating system support into libc. Some portions of 5Picolibc need system support, like I/O and task termination. 6 7## System Interfaces used by Picolibc 8 9Here's the full list of system functions used by Picolibc, split into 10sections based on what libc support they enable 11 12### stdin/stdout/stderr 13 14Picolibc stdio splits support for simple console input/output and more 15complex file operations so that a minimal system can easily support 16the former with only a few functions. 17 18To get stdin/stdout/stderr working, the application needs to define 19the `stdin`, `stdout` and `stderr` globals, which contain pointers to 20FILE objects. The pointers may reside in read-only memory, but the 21FILE objects may not. A single FILE object may be used for all three 22pointers, and linker aliases may be used to make all three pointers be 23stored in the same location. The FILE object contains function 24pointers for putc, getc, which might be defined as follows: 25 26 static int 27 sample_putc(char c, FILE *file) 28 { 29 (void) file; /* Not used in this function 30 __uart_putc(c); /* Defined by underlying system */ 31 return c; 32 } 33 34 static int 35 sample_getc(FILE *file) 36 { 37 unsigned char c; 38 (void) file; /* Not used in this function */ 39 c = __uart_getc(); /* Defined by underlying system */ 40 return c; 41 } 42 43It also contains a pointer to an optional `flush` function, which, if 44provide, will be called to flush pending output to the file, e.g., 45when the `fflush()` function is called: 46 47 static int 48 sample_flush(FILE *file) 49 { 50 /* This function doesn't need to do anything */ 51 (void) file; /* Not used in this function */ 52 return 0; 53 } 54 55These functions are used to initialize a FILE structure: 56 57 static FILE __stdio = FDEV_SETUP_STREAM(sample_putc, 58 sample_getc, 59 NULL, 60 _FDEV_SETUP_RW); 61 62This defines a FILE which can read and write characters using the putc 63and getc functions described above, but not using any flush 64function. The final paramter, which specifies the operations 65supported, can be one of the following: 66 67| Mode | Operations | Required Functions | 68|-------------------|------------|--------------------| 69| _FDEV_SETUP_READ | Read | getc | 70| _FDEV_SETUP_WRITE | Write | putc | 71| _FDEV_SETUP_RW | Read/Write | putc, getc | 72 73Finally, the FILE is used to initialize the `stdin`, `stdout` and 74`stderr` values, the latter two of which are simply aliases to `stdin`: 75 76 FILE *const stdin = &__stdio; 77 __strong_reference(stdin, stdout); 78 __strong_reference(stdin, stderr); 79 80### fopen, fdopen 81 82Support for these requires malloc/free along with a handful of 83POSIX-compatible functions: 84 85 int open (const char *, int, ...); 86 int close (int fd); 87 ssize_t read (int fd, void *buf, size_t nbyte); 88 ssize_t write (int fd, const void *buf, size_t nbyte); 89 off_t lseek (int fd, off_t offset, int whence); 90 91The code needed for this is built into Picolibc by default, but can be 92disabled by specifying `-Dposix-io=false` in the meson command line. 93 94### exit 95 96Exit is just a wrapper around _exit that also calls destructors and 97callbacks registered with atexit. To make it work, you'll need to 98implement the `_exit` function: 99 100 void _exit (int status) _ATTRIBUTE ((__noreturn__)); 101 102### malloc and free 103 104Both versions of malloc in picolibc require sbrk to be supported. The 105smaller version, enabled (by default) with -Dnewlib-nano-malloc=true, 106can handle sbrk returning dis-continuous memory while the larger 107version (enabled with -Dnewlib-nano-malloc=false) requires sbrk return 108contiguous chunks of memory. 109 110### sbrk 111 112Picolibc includes a simple version of sbrk that can return chunks of 113memory from a pre-defined contiguous heap. To use this function, your 114application linking process needs to define two symbols: 115 116 * __heap_start — points at the start of the heap available for sbrk. 117 * __heap_end — points at the end of the heap available for sbrk. 118 119The sample linker script provided with picolibc defines these two 120symbols to enclose all RAM which is not otherwise used by the 121application. 122 123## Linking with System Library 124 125To get Picolibc to use a system library, that library needs to be 126specified *after* libc on the linker command line. The picolibc.specs 127file provides a way to specify a library after libc using the 128`--oslib=` parameter: 129 130 $ gcc -o program.elf program.o --oslib=myos 131 132This will include -lmyos after -lc so that the linker can resolve 133functions used by picolibc from libmyos.a. You can, alternatively, 134include the functions in object files with the rest of your 135application, which avoids the problem with libraries. Note that this 136mechanism requires the definition of _exit in the myos library. 137 138## Semihosting support 139 140For RISC-V and ARM processors, Picolibc provides an implementation of 141all of the above functions along with a couple more POSIX APIs used by 142the Picolibc test suite. This implementation relies on semihosting 143support from the execution environment, which is available when 144running under qemu or when using openocd and gdb. Link this into your 145application using `--oslib=semihost` in your link command line. 146Please note that this will replace the default `crt0` with a variant 147calling `exit` upon return from `main`. The default is to enter an 148infinite loop, and the change ensure a clean return to the execution 149environment. 150 151## POSIX console support 152 153As a build-time option, Picolibc can be configured to use POSIX read 154and write APIs to support stdin, stdout and stderr. Add 155`-Dposix-console=true` to enable this. This is incompatible with 156semihosting support above. 157 158## Building picolibc on native POSIX systems 159 160To allow for testing of picolibc and applications using picolibc, you 161can actually build picolibc on a full POSIX system. In this 162configuration, picolibc provides the non-POSIX libc APIs while the 163underlying system C library is used for the POSIX functions described 164above. To build in this mode, you'll need to override a few default 165picolibc configuration parameters: 166 167 $ meson \ 168 -Dtls-model=global-dynamic \ 169 -Dmultilib=false \ 170 -Dpicolib=false \ 171 -Dpicocrt=false \ 172 -Dposix-console=true \ 173 -Dnewlib-global-atexit=true \ 174 -Dincludedir=lib/picolibc/include \ 175 -Dlibdir=lib/picolibc/lib \ 176 -Dspecsdir=none 177 178 * -Dtls-model=global-dynamic makes picolibc use the default TLS model 179 for GCC. 180 181 * -Dmultilib-false makes picolibc build only a single library for the 182 default GCC configuration. 183 184 * -Dpicolib=false disables building the TLS and sbrk support built-in 185 to picolibc so that the underlying system support is used instead. 186 187 * -Dpicocrt=false disables building the C startup code as that is 188 provided by the underlying system. 189 190 * -Dposix-console=true uses POSIX I/O read/write APIs for stdin, 191 stdout and stderr. 192 193 * -Dnewlib-global-atexit=true disables the per-thread atexit behavior 194 so that picolibc acts like a regular C library. 195 196 * -Dincludedir and -Dlibdir specify install locations for the headers 197 and library 198 199 * -Dspecsdir=none disables installing picolibc.specs as that file 200 is not useful in this environment 201 202Once built, you can install and use picolibc on the host: 203 204 $ cc -I/usr/local/lib/picolibc/include hello-world.c \ 205 /usr/local/lib/picolibc/lib/libc.a 206