From f8a021a8c99d29914a59aec94d05c760b1e14f88 Mon Sep 17 00:00:00 2001 From: Keith Packard Date: Thu, 19 Sep 2019 19:10:04 -0700 Subject: [PATCH] Start building docs --- README.md | 224 +++---------------------------------------------- doc/build.md | 201 ++++++++++++++++++++++++++++++++++++++++++++ doc/init.md | 59 +++++++++++++ doc/testing.md | 37 ++++++++ doc/tls.md | 107 +++++++++++++++++++++++ doc/usage.md | 20 +++++ doc/using.md | 78 +++++++++++++++++ 7 files changed, 513 insertions(+), 213 deletions(-) create mode 100644 doc/build.md create mode 100644 doc/init.md create mode 100644 doc/testing.md create mode 100644 doc/tls.md create mode 100644 doc/usage.md create mode 100644 doc/using.md diff --git a/README.md b/README.md index ce4b136c65..a8bcab5759 100644 --- a/README.md +++ b/README.md @@ -6,219 +6,17 @@ small embedded systems with limited RAM. PicoLibc was formed by blending code from [Newlib](http://sourceware.org/newlib/) and [AVR Libc](https://www.nongnu.org/avr-libc/). -### Selecting build options +## License -Most of these options set configuration values for the newlib code -base and should match that configuration system. Use --D={true,false} to change from the default value. +Picolibc source comes from a variety of places and has a huge variety +of copyright holders and license texts. The bulk of the code is +covered by BSD-like licenses, either two or three clause, all of which +are GPLv2+ compatible. Some of the code is covered by a GPL varient, +but that code is only used on Linux platforms -| Option | Default | Description | -| ------ | ------- | ----------- | -| target-optspace | false | Compile with -Os | -| hw-fp | false | Turn on hardware floating point math | -| tests | false | Enable tests | -| newlib-tinystdio | false | Use tiny stdio from avr libc | -| newlib-io-pos-args | false | Enable printf-family positional arg support | -| newlib-io-c99-formats | false | Enable C99 support in IO functions like printf/scanf | -| newlib-register-fini | false | Enable finalization function registration using atexit | -| newlib-io-long-long | false | Enable long long type support in IO functions like printf/scanf | -| newlib-io-long-double | false | Enable long double type support in IO functions printf/scanf | -| newlib-mb | false | Enable multibyte support | -| newlib-iconv-encodings | false | Enable specific comma-separated list of bidirectional iconv encodings to be built-in | -| newlib-iconv-from-encodings | false | Enable specific comma-separated list of "from" iconv encodings to be built-in | -| newlib-iconv-to-encodings | false | Enable specific comma-separated list of "to" iconv encodings to be built-in | -| newlib-iconv-external-ccs | false | Enable capabilities to load external CCS files for iconv | -| newlib-atexit-dynamic-alloc | false | Enable dynamic allocation of atexit entries | -| newlib-global-atexit | false | Enable atexit data structure as global | -| newlib-reent-small | false | Enable small reentrant struct support | -| newlib-global-stdio-streams | false | Enable global stdio streams | -| newlib-fvwrite-in-streamio | false | Disable iov in streamio | -| newlib-fseek-optimization | false | Disable fseek optimization | -| newlib_wide_orient | false | Turn off wide orientation in streamio | -| newlib-nano-malloc | false | Use small-footprint nano-malloc implementation | -| newlib-unbuf-stream-opt | false | Enable unbuffered stream optimization in streamio | -| lite-exit | false | Enable light weight exit | -| newlib_nano_formatted_io | false | Use nano version formatted IO | -| newlib-retargetable-locking | false | Allow locking routines to be retargeted at link time | -| newlib-long-time_t | false | Define time_t to long | -| newlib-multithread | false | Enable support for multiple threads | -| newlib-iconv | false | Enable iconv library support | -| newlib-io-float | false | Enable printf/scanf family float support | -| newlib-supplied-syscalls | false | Enable newlib supplied syscalls | +## Documentation -## Building for embedded RISC-V and ARM systems - -Meson sticks all of the cross-compilation build configuration bits in -a separate configuration file. There are a bunch of things you need to -set, which the build system really shouldn't care about. Example -configuration settings for RISC-V processors are in -cross-riscv32-unknown-elf.txt: - - [binaries] - c = 'riscv32-unknown-elf-gcc' - ar = 'riscv32-unknown-elf-ar' - as = 'riscv32-unknown-elf-as' - - [host_machine] - system = '' - cpu_family = '' - cpu = '' - endian = '' - -Settings for ARM processors are in cross-arm-none-eabi.txt: - - [binaries] - c = 'arm-none-eabi-gcc' - ar = 'arm-none-eabi-ar' - as = 'arm-none-eabi-as' - - [host_machine] - system = '' - cpu_family = '' - cpu = '' - endian = '' - -If those programs aren't in your path, you can edit the file to point -wherever they may be. - -### Auto-detecting the compiler configurations - -The PicoLibc configuration detects the processor configurations -supported by the compiler using the `--print-multi-lib` command-line option: - - $ riscv32-unknown-elf-gcc --print-multi-lib - .; - rv32i/ilp32;@march=rv32i@mabi=ilp32 - rv32im/ilp32;@march=rv32im@mabi=ilp32 - rv32iac/ilp32;@march=rv32iac@mabi=ilp32 - rv32imac/ilp32;@march=rv32imac@mabi=ilp32 - rv32imafc/ilp32f;@march=rv32imafc@mabi=ilp32f - rv64imac/lp64;@march=rv64imac@mabi=lp64 - rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d - - $ arm-none-eabi-gcc --print-multi-lib - .; - thumb;@mthumb - hard;@mfloat-abi=hard - thumb/v6-m;@mthumb@march=armv6s-m - thumb/v7-m;@mthumb@march=armv7-m - thumb/v7e-m;@mthumb@march=armv7e-m - thumb/v7-ar;@mthumb@march=armv7 - thumb/v8-m.base;@mthumb@march=armv8-m.base - thumb/v8-m.main;@mthumb@march=armv8-m.main - thumb/v7e-m/fpv4-sp/softfp;@mthumb@march=armv7e-m@mfpu=fpv4-sp-d16@mfloat-abi=softfp - thumb/v7e-m/fpv4-sp/hard;@mthumb@march=armv7e-m@mfpu=fpv4-sp-d16@mfloat-abi=hard - thumb/v7e-m/fpv5/softfp;@mthumb@march=armv7e-m@mfpu=fpv5-d16@mfloat-abi=softfp - thumb/v7e-m/fpv5/hard;@mthumb@march=armv7e-m@mfpu=fpv5-d16@mfloat-abi=hard - thumb/v7-ar/fpv3/softfp;@mthumb@march=armv7@mfpu=vfpv3-d16@mfloat-abi=softfp - thumb/v7-ar/fpv3/hard;@mthumb@march=armv7@mfpu=vfpv3-d16@mfloat-abi=hard - thumb/v7-ar/fpv3/hard/be;@mthumb@march=armv7@mfpu=vfpv3-d16@mfloat-abi=hard@mbig-endian - thumb/v8-m.main/fpv5-sp/softfp;@mthumb@march=armv8-m.main@mfpu=fpv5-sp-d16@mfloat-abi=softfp - thumb/v8-m.main/fpv5-sp/hard;@mthumb@march=armv8-m.main@mfpu=fpv5-sp-d16@mfloat-abi=hard - thumb/v8-m.main/fpv5/softfp;@mthumb@march=armv8-m.main@mfpu=fpv5-d16@mfloat-abi=softfp - thumb/v8-m.main/fpv5/hard;@mthumb@march=armv8-m.main@mfpu=fpv5-d16@mfloat-abi=hard - -On RISC-V, PicoLibc is compiled 8 times, while on ARM, the library is -compiled 20 times with the specified compiler options (replace the -'@'s with '-' to see what they will be). - -### Running meson - -Because I'm targeting smaller systems like the STM32F042 Cortex-M0 -parts with 4kB of RAM and 32kB of flash, I enable all of the 'make it -smaller' options. This example is in the do-arm-configure file: - - #!/bin/sh - ARCH=arm-none-eabi - DIR=`dirname $0` - meson $DIR \ - -Dtarget-optspace=true \ - -Dnewlib-tinystdio=true \ - -Dnewlib-supplied-syscalls=false \ - -Dnewlib-reentrant-small=true\ - -Dnewlib-wide-orient=false\ - -Dnewlib-nano-malloc=true\ - -Dlite-exit=true\ - -Dnewlib-global-atexit=true\ - -Dincludedir=lib/newlib-nano/$ARCH/include \ - -Dlibdir=lib/newlib-nano/$ARCH/lib \ - --cross-file $DIR/cross-$ARCH.txt \ - --buildtype plain - -Note the use of '--buildtype plain'. This stops meson from adding -compilation options so that the '-Dtarget-optspace=true' option can -select '-Os'. - -This script is designed to be run from a build directory, so you'd do: - - $ mkdir build-arm-none-eabi - $ cd build-arm-none-eabi - $ ../do-arm-configure - -### Compiling - -Once configured, you can compile the libraries with - - $ ninja - ... - $ ninja install - ... - $ - -### Using the library - -We should configure the compiler so that selecting a suitable target -architecture combination would set up the library paths to match, but -at this point you'll have to figure out the right -L line by yourself -by matching the path name on the left side of the --print-multi-lib -output with the compiler options on the right side. For instance, my -STM32F042 cortex-M0 parts use - - $ arm-none-eabi-gcc -mlittle-endian -mcpu=cortex-m0 -mthumb - -To gcc, '-mcpu=cortex=m0' is the same as '-march=armv6s-m', so looking -at the output above, the libraries we want are in - - /usr/local/lib/newlib-nano/arm-none-eabi/lib/thumb/v6-m - -so, to link, we need to use: - - $ arm-none-eabi-gcc ... -L/usr/local/lib/newlib-nano/arm-none-eabi/lib/thumb/v6-m -lm -lc -lgcc - -## Building for the local processor - -If you want to compile the library for your local processor to test -changes in the library, the meson configuration is happy to do that -for you. You won't need a meson cross compilation configuration file, -so all you need is the right compile options. They're mostly the same -as the embedded version, but you don't want the multi-architecture -stuff and I prefer plain debug to an -Os, as that makes debugging the -library easier. - -The do-native-configure script has an example: - - #!/bin/sh - DIR=`dirname $0` - meson $DIR \ - -Dmultilib=false \ - -Dnewlib-tinystdio=true \ - -Dnewlib-supplied-syscalls=false \ - -Dnewlib-wide-orient=false\ - -Dnewlib-nano-malloc=true\ - -Dlite-exit=true\ - -Dnewlib-global-atexit=true\ - -Dincludedir=lib/newlib-nano/include \ - -Dlibdir=lib/newlib-nano/lib \ - -Dtests=true \ - --buildtype debug - -Again, create a directory and build there: - - $ mkdir build-native - $ cd build-native - $ ../do-native-configure - $ ninja - -This will also build a test case for printf and scanf in the -'test' directory, which I used to fix up the floating point input and -output code. + * [Building Picolibc](doc/build) + * [Using Picolibc](doc/using) + * [Picolibc initialization](doc/init) + * [Thread Local Storage](doc/tls) diff --git a/doc/build.md b/doc/build.md new file mode 100644 index 0000000000..00c7e9c095 --- /dev/null +++ b/doc/build.md @@ -0,0 +1,201 @@ +# Building Picolibc + +Picolibc is designed to be cross-compiled for embedded systems on a +Linux host using GCC. Picolibc uses the meson build system, which is a +slightly quirky build system designed to replace autotools with a +single language. + +## Selecting build options + +Use -D= on the meson command line to change from +the default value. Many of these options set configuration values for +the newlib code base and should match that configuration system. The +defaults should be reasonable for small embedded systems. + +| Option | Default | Description | +| ------ | ------- | ----------- | +| fast-strcmp | true | Always optimize strcmp for performance (to make Dhrystone happy) | +| multilib | true | Build every multilib configuration supported by the compiler | +| picolib | true | Include picolib bits for tls and sbrk support | +| thread-local-storage | true | Use TLS for global variables | +| tls-model | local-exec | Select TLS model (global-dynamic, local-dynamic, initial-exec or local-exec) | +| newlib-global-errno | false | Use single global errno even when thread-local-storage=true | +| newlib-initfini-array | true | Use .init_array and .fini_array sections in picocrt | +| newlib-initfini | true | Support _init() and _fini() functions in picocrt | +| hw-fp | false | Turn on hardware floating point math | +| have-alias-attribute | true | Compiler supports __alias__ attribute | +| tests | false | Enable tests | +| newlib-tinystdio | true | Use tiny stdio from avr libc | +| specsdir | auto | Where to install the .specs file (default is in the GCC directory) | +| newlib-mb | false | Enable multibyte support | +| newlib-iconv-encodings | false | Enable specific comma-separated list of bidirectional iconv encodings to be built-in | +| newlib-iconv-from-encodings | false | Enable specific comma-separated list of "from" iconv encodings to be built-in | +| newlib-iconv-to-encodings | false | Enable specific comma-separated list of "to" iconv encodings to be built-in | +| newlib-iconv-external-ccs | false | Enable capabilities to load external CCS files for iconv | +| newlib-register-fini | false | Enable finalization function registration using atexit | +| newlib-atexit-dynamic-alloc | false | Enable dynamic allocation of atexit entries | +| newlib-global-atexit | false | Enable atexit data structure as global | +| newlib_wide_orient | false | Turn off wide orientation in streamio | +| newlib-nano-malloc | true | Use small-footprint nano-malloc implementation | +| lite-exit | true | Enable light weight exit | +| newlib-retargetable-locking | false | Allow locking routines to be retargeted at link time | +| newlib-long-time_t | false | Define time_t to long | +| newlib-multithread | false | Enable support for multiple threads | +| newlib-iconv | false | Enable iconv library support | +| newlib-locale-info | false | Enable locale support | +| newlib-locale-info-extended | false | Enable even more locale support | + +### Options when using legacy stdio bits + +Normally, Picolibc is built with the small stdio library adapted from +avrlibc (newlib-tinystdio=true). It still has the original newlib +stdio bits and those might still work (newlib-tinystdio=false). These +options are relevant only in that configuration + +| Option | Default | Description | +| ------ | ------- | ----------- | +| newlib-reent-small | false | Enable small reentrant struct support | +| newlib-io-pos-args | false | Enable printf-family positional arg support | +| newlib-io-c99-formats | false | Enable C99 support in IO functions like printf/scanf | +| newlib-io-long-long | false | Enable long long type support in IO functions like printf/scanf | +| newlib-io-long-double | false | Enable long double type support in IO functions printf/scanf | +| newlib-global-stdio-streams | false | Enable global stdio streams | +| newlib-fvwrite-in-streamio | false | Disable iov in streamio | +| newlib-fseek-optimization | false | Disable fseek optimization | +| newlib-unbuf-stream-opt | false | Enable unbuffered stream optimization in streamio | +| newlib_nano_formatted_io | false | Use nano version formatted IO | +| newlib-io-float | false | Enable printf/scanf family float support | +| newlib-supplied-syscalls | false | Enable newlib supplied syscalls | + +## Building for embedded RISC-V and ARM systems + +Meson sticks all of the cross-compilation build configuration bits in +a separate configuration file. There are a bunch of things you need to +set, which the build system really shouldn't care about. Example +configuration settings for RISC-V processors are in +cross-riscv32-unknown-elf.txt: + + [binaries] + c = 'riscv32-unknown-elf-gcc' + ar = 'riscv32-unknown-elf-ar' + as = 'riscv32-unknown-elf-as' + + [host_machine] + system = '' + cpu_family = '' + cpu = '' + endian = '' + +Settings for ARM processors are in cross-arm-none-eabi.txt: + + [binaries] + c = 'arm-none-eabi-gcc' + ar = 'arm-none-eabi-ar' + as = 'arm-none-eabi-as' + + [host_machine] + system = '' + cpu_family = '' + cpu = '' + endian = '' + +If those programs aren't in your path, you can edit the file to point +wherever they may be. + +### Auto-detecting the compiler configurations + +The PicoLibc configuration detects the processor configurations +supported by the compiler using the `--print-multi-lib` command-line option: + + $ riscv64-unknown-elf-gcc --print-multi-lib + .; + rv32e/ilp32e;@march=rv32e@mabi=ilp32e + rv32em/ilp32e;@march=rv32em@mabi=ilp32e + rv32eac/ilp32e;@march=rv32eac@mabi=ilp32e + rv32emac/ilp32e;@march=rv32emac@mabi=ilp32e + rv32i/ilp32;@march=rv32i@mabi=ilp32 + rv32im/ilp32;@march=rv32im@mabi=ilp32 + rv32imf/ilp32f;@march=rv32imf@mabi=ilp32f + rv32iac/ilp32;@march=rv32iac@mabi=ilp32 + rv32imac/ilp32;@march=rv32imac@mabi=ilp32 + rv32imafc/ilp32f;@march=rv32imafc@mabi=ilp32f + rv32imafdc/ilp32d;@march=rv32imafdc@mabi=ilp32d + rv64i/lp64;@march=rv64i@mabi=lp64 + rv64im/lp64;@march=rv64im@mabi=lp64 + rv64imf/lp64f;@march=rv64imf@mabi=lp64f + rv64iac/lp64;@march=rv64iac@mabi=lp64 + rv64imac/lp64;@march=rv64imac@mabi=lp64 + rv64imafc/lp64f;@march=rv64imafc@mabi=lp64f + rv64imafdc/lp64d;@march=rv64imafdc@mabi=lp64d + + + $ arm-none-eabi-gcc --print-multi-lib + .; + thumb;@mthumb + hard;@mfloat-abi=hard + thumb/v6-m;@mthumb@march=armv6s-m + thumb/v7-m;@mthumb@march=armv7-m + thumb/v7e-m;@mthumb@march=armv7e-m + thumb/v7-ar;@mthumb@march=armv7 + thumb/v8-m.base;@mthumb@march=armv8-m.base + thumb/v8-m.main;@mthumb@march=armv8-m.main + thumb/v7e-m/fpv4-sp/softfp;@mthumb@march=armv7e-m@mfpu=fpv4-sp-d16@mfloat-abi=softfp + thumb/v7e-m/fpv4-sp/hard;@mthumb@march=armv7e-m@mfpu=fpv4-sp-d16@mfloat-abi=hard + thumb/v7e-m/fpv5/softfp;@mthumb@march=armv7e-m@mfpu=fpv5-d16@mfloat-abi=softfp + thumb/v7e-m/fpv5/hard;@mthumb@march=armv7e-m@mfpu=fpv5-d16@mfloat-abi=hard + thumb/v7-ar/fpv3/softfp;@mthumb@march=armv7@mfpu=vfpv3-d16@mfloat-abi=softfp + thumb/v7-ar/fpv3/hard;@mthumb@march=armv7@mfpu=vfpv3-d16@mfloat-abi=hard + thumb/v7-ar/fpv3/hard/be;@mthumb@march=armv7@mfpu=vfpv3-d16@mfloat-abi=hard@mbig-endian + thumb/v8-m.main/fpv5-sp/softfp;@mthumb@march=armv8-m.main@mfpu=fpv5-sp-d16@mfloat-abi=softfp + thumb/v8-m.main/fpv5-sp/hard;@mthumb@march=armv8-m.main@mfpu=fpv5-sp-d16@mfloat-abi=hard + thumb/v8-m.main/fpv5/softfp;@mthumb@march=armv8-m.main@mfpu=fpv5-d16@mfloat-abi=softfp + thumb/v8-m.main/fpv5/hard;@mthumb@march=armv8-m.main@mfpu=fpv5-d16@mfloat-abi=hard + +On RISC-V, PicoLibc is compiled 19 times, while on ARM, the library is +compiled 20 times with the specified compiler options (replace the +'@'s with '-' to see what they will be). + +### Running meson + +Because Picolibc targets smaller systems like the SiFive FE310 or ARM +Cortex-M0 parts with only a few kB of RAM and flash, the default +values for all of the configuration options are designed to minimize +the library code size. + + #!/bin/sh + ARCH=arm-none-eabi + DIR=`dirname $0` + meson $DIR \ + -Dtarget-optspace=true \ + -Dnewlib-tinystdio=true \ + -Dnewlib-supplied-syscalls=false \ + -Dnewlib-reentrant-small=true\ + -Dnewlib-wide-orient=false\ + -Dnewlib-nano-malloc=true\ + -Dlite-exit=true\ + -Dnewlib-global-atexit=true\ + -Dincludedir=lib/newlib-nano/$ARCH/include \ + -Dlibdir=lib/newlib-nano/$ARCH/lib \ + --cross-file $DIR/cross-$ARCH.txt \ + --buildtype plain + +Note the use of '--buildtype plain'. This stops meson from adding +compilation options so that the '-Dtarget-optspace=true' option can +select '-Os'. + +This script is designed to be run from a build directory, so you'd do: + + $ mkdir build-arm-none-eabi + $ cd build-arm-none-eabi + $ ../do-arm-configure + +### Compiling + +Once configured, you can compile the libraries with + + $ ninja + ... + $ ninja install + ... + $ + diff --git a/doc/init.md b/doc/init.md new file mode 100644 index 0000000000..5f017726ca --- /dev/null +++ b/doc/init.md @@ -0,0 +1,59 @@ +# Initializers/Constructors and Finalizers/Destructors in Picolibc + +The `__libc_init_array` and `__libc_fini_array` functions in Picolibc +are called by Picocrt during application startup and shutdown. If you +have custom start up code that replaces Picocrt, you probably want to +call these as well. + +## `__libc_init_array` + +This function performs application initialization by calling: + + 1) Functions listed in the preinit array + 2) _init function + 3) Functions listed in the init array + +### _init function + +Your program may declare an optional initialization function called +`_init`. If such a function is included in the binary it will be +called before the init array contents are evaluated. + +## `__libc_fini_array` + +Most embedded applications never return from main, but if yours does, +Picocrt will call: + + 1) Functions listed in the fini array + 2) _fini function + +### _fini function + +Your program may declare an optional finalization function called +`_fini`. If such a function is included in the binary it will be +called after the fini array contents are evaluated. + +## Automatically generated arrays of function pointers + +The `.preinit_array`, `.init_array` and `.fini_array` segments are +built by the linker using data generated by the compiler. When you +decorate a function with `__attribute__((constructor))`, the compiler +places the address of the function into the `.init_array` +segment. Decorate a function with `__attribute__((destructor))` and +the compiler will place its address in the `.fini_array` segment. The +`.preinit_array` segment is generated internally by the compiler for +things like C++ vtable validation + +The linker script should collect all of the data from these segments +into memory (probably flash as it doesn't change) so that the image +ends up with arrays of function pointers. For data from the +`.init_array` segment, the array should start with the symbol +`__init_array_start` and ending with `__init_array_end`. For data from +the `.fini_array` segment, the array should start with the symbol +`__fini_array_start` and end with `__fini_array_end`. Finally, for +data from the `.preinit_array` segment, the array starts with +`__preinit_array_start` and ends with `__preinit_array_end`. + +Each of these arrays are complicated by the optional priority assigned +to destructors and constructors, and the presense of the deprecated +`.ctors` and `.dtors` segments. diff --git a/doc/testing.md b/doc/testing.md new file mode 100644 index 0000000000..1e5e5af743 --- /dev/null +++ b/doc/testing.md @@ -0,0 +1,37 @@ +## Building for the local processor + +If you want to compile the library for your local processor to test +changes in the library, the meson configuration is happy to do that +for you. You won't need a meson cross compilation configuration file, +so all you need is the right compile options. They're mostly the same +as the embedded version, but you don't want the multi-architecture +stuff and I prefer plain debug to an -Os, as that makes debugging the +library easier. + +The do-native-configure script has an example: + + #!/bin/sh + DIR=`dirname $0` + meson $DIR \ + -Dmultilib=false \ + -Dnewlib-tinystdio=true \ + -Dnewlib-supplied-syscalls=false \ + -Dnewlib-wide-orient=false\ + -Dnewlib-nano-malloc=true\ + -Dlite-exit=true\ + -Dnewlib-global-atexit=true\ + -Dincludedir=lib/newlib-nano/include \ + -Dlibdir=lib/newlib-nano/lib \ + -Dtests=true \ + --buildtype debug + +Again, create a directory and build there: + + $ mkdir build-native + $ cd build-native + $ ../do-native-configure + $ ninja + +This will also build a test case for printf and scanf in the +'test' directory, which I used to fix up the floating point input and +output code. diff --git a/doc/tls.md b/doc/tls.md new file mode 100644 index 0000000000..4615c2335e --- /dev/null +++ b/doc/tls.md @@ -0,0 +1,107 @@ +# Thread Local Storage in Picolibc +Copyright © 2019 Keith Packard + +The standard C library API includes many functions that use persistent +state held by the library not the application. One obvious example is +'errno', a global variable originall designed to hold error status +from the Unix kernel, but which was co-opted by libc to provide +additional error status values from the library itself. There are +numerous other examples of this kind of state. + +To permit multiple threads sharing a single address space to use the +library without conflicting over this data, each thread needs a +private copy of data that it uses. Newlib did this by creating a +global structure holding all thread-specific values and then defining +an API for the library to get the global structure for the current +thread. + +Picolibc does this by using the built-in thread-local-storage +mechanisms in the toolchain. This has several benefits: + + 1) Source code is simpler as thread-local variables are + accessed directly by name. + + 2) Thread local storage contains only values used by the + application. + + 3) Generated code is smaller and faster. + +## TLS model used by Picolibc + +Picolibc is normally compiled with -ftls-model=local-exec. The selected +model is included in the .specs file so applications should not +include -ftls-model in their compile commands. + +Local-exec defines a single block of storage for all TLS +variables. Each TLS variable is assigned an offset within this block +and those values are placed in the binary during relocation. + +## Initial TLS block + +The sample Picolibc linker script (picolibc.ld) allocates RAM for an +initial TLS block and arranges for it to be initialized as a part of +the normal data/bss initialization process for the application. + +| flash | symbol | +| ----- | ------ | +| code | | +| rodata | | +| data initializers | __data_source | +| TLS data initializers | __tdata_source | + +| RAM | symbol | +| ---- | ------ | +| data | __data_start | +| TLS data | __tls_base | +| | __data_end | +| | __tdata_size = . - __tls_base | +| TLS bss | __bss_start | +| | __tbss_size = . - __bss_start | +| | __tls_size = . - __tls_base | +| bss | | +| | __bss_end + +The crt0 code copies __data_end - __data_start bytes from _data_source +to _data_start. This initializes the regular data segment *and* the +initial TLS data segment. Then, it clears memory from __bss_start to +__bss_end, initializing the TLS bss segment *and* the regular bss +segment. Finally, it sets the architecture-specific TLS data pointer +to __tls_base. Once that is set, access to TLS variables will +reference this initial TLS block. + +## Creating more TLS blocks + +If the application is multi-threaded and wants to allow these threads +to have separate TLS data, it may allocate memory for additional TLS +blocks: + + 1) Allocate a block of size __tls_size + 2) Copy __tdata_size bytes from __tdata_source to the new block to + set the initial TLS values. + 3) Clear __tbss_size bytes starting _tdata_size bytes into the new + block + 4) Set the TLS pointer as necessary + +## Picolibc APIs related to TLS + +Picolib provides a couple of helper APIs for TLS: + +* _set_tls +``` +void +_set_tls(void *tls); +``` +This is an architecture-specific function which sets the TLS +block pointer for the processor to `tls`. + +* _init_tls +``` +void +_init_tls(void *tls); +``` +This function initializes the specified TLS block, copying values +into the initialized data portion and clearing values in the +uninitialized data portion. + +Picolib also provides architecture-specific internal GCC APIs as +necessary, for example, __aeabi_read_tp for ARM processors. diff --git a/doc/usage.md b/doc/usage.md new file mode 100644 index 0000000000..dbe248bc3c --- /dev/null +++ b/doc/usage.md @@ -0,0 +1,20 @@ +### Using the library + +We should configure the compiler so that selecting a suitable target +architecture combination would set up the library paths to match, but +at this point you'll have to figure out the right -L line by yourself +by matching the path name on the left side of the --print-multi-lib +output with the compiler options on the right side. For instance, my +STM32F042 cortex-M0 parts use + + $ arm-none-eabi-gcc -mlittle-endian -mcpu=cortex-m0 -mthumb + +To gcc, '-mcpu=cortex=m0' is the same as '-march=armv6s-m', so looking +at the output above, the libraries we want are in + + /usr/local/lib/newlib-nano/arm-none-eabi/lib/thumb/v6-m + +so, to link, we need to use: + + $ arm-none-eabi-gcc ... -L/usr/local/lib/newlib-nano/arm-none-eabi/lib/thumb/v6-m -lm -lc -lgcc + diff --git a/doc/using.md b/doc/using.md new file mode 100644 index 0000000000..0911228533 --- /dev/null +++ b/doc/using.md @@ -0,0 +1,78 @@ +# Using Picolibc in Embedded Systems + +Picolibc is designed to be used in deeply embedded systems, those +running either no operating system or a small RTOS. It is designed to +be linked statically along with any operating system and application +code. + +## Compiling with Picolibc + +To compile source code to use Picolibc, you can use the GCC .specs +file delivered with Picolibc. This will set the system header file +path and the linker library path to point at Picolibc. For example, to +compile a single file and generate a .o file: + + $ gcc -specs=picolibc.specs -c foo.c + +When installed directly into the system, picolibc.specs is placed in +the gcc directory so that it will be found using just the base name of +the file. If installed in a separate location, you will need to provide an +absolute pathname to the picolibc.specs file. + +When building for an embedded system, you'll probably need to use a +longer name for the compiler, something like `riscv-unknown-elf-gcc` +or `arm-none-eabi-gcc`. + +## Picolibc startup + +Initializing an embedded system usually requires a combination of +hardware, run-time, library and application setup. You may want to +perform all of these in your own code, or you be able to use the +Picolibc startup code. Either works fine, using the Picolibc code +means writing less of your own. + +### Picocrt — Picolibc startup code + +Picocrt is the crt0.o code provided by Picolibc. This is enabled by +default when using -specs=picolibc.specs: + + $ gcc -specs=picolibc.specs -o foo.elf foo.c + +Picocrt goes through a sequence of initialization steps, many of which +you can plug your own code into: + + 1) Architecture-specific runtime initialization. For instance, RISC-V + needs the `gp` register initialized for quick access to global + variables while ARM with hardware floating point needs to have the + FPSCR register set up to match C semantics for rounding. + + 2) Data initialization. Here's the code inside Picocrt: +``` +memcpy(__data_start, __data_source, (uintptr_t) __data_size); +``` +For this to work, the linker script must assign correct values to +each of these symbols: + + * __data_start points to the RAM location where the .data segment + starts. + * __data_source points to the Flash location where the .data segment + initialization values are stored. + * __data_size is an absolute symbol noting the size of the + initialized data segment + + 3) BSS initialization. Here's the code inside Picocrt: +``` +memset(__bss_start, '\0', (uintptr_t) __bss_size); +``` +Assign these symbols in the linker script as follows: + + * __bss_start points to the RAM location where the .bss segment + starts. + * __bss_size is an absolute symbol noting the size of the cleared + data segment + + 4) Call [initializers/constructors](init) using `__libc_init_array()` + + 5) Call `main()` + + 6) Call [finalizers/destructors](fini) using `__libc_fini_array()`