2019年9月底,知名开发者 Keith Packard通过他的博客宣布推出了“picolibc”。Picolibc 是一种适用于小型微控制器嵌入式系统的C标准库,该库的API甚至允许在低内存 (RAM) 设备中运行。picolibc其实是“newlib-nano”的升级版本,这个升级版本有一些有趣的变化,比如:用从avrlibc采用 ATMEL 特定的 printf 代码替换“stdio”库。作为这个库的一部分,Keith 还推出了 picocrt,它负责在调用自己的 C 程序主函数之前初始化内存和调用各种构造函数。
特征
- picolibc 是 newlibc 的修订版,它没有成熟的 stdio lib,使用的是avrlibc 的轻量级 stdio lib,更适合低内存嵌入式设备。
- Meson 构建系统简化了针对各种目标平台和硬件 picolibc 源代码树的构建过程。
- 使用 Glibc 作为参考更新了数学测试套件
- 该库是 BSD 许可的,非 BSD 组件已从源代码树中删除
- picocrt-picolibc 启动代码,无需编写自己的代码即可初始化嵌入式系统,这可以通过在编译时使用-specs= picolibc.specs来启用
- 使用内置的线程本地存储 (TLS),它有几个优点:
- 源代码更简单,因为线程局部变量可以通过名称直接访问
- 线程本地存储仅包含应用程序使用的值
- 生成的代码更小更快
开始使用 picolibc
Picolibc 使用meson构建系统在 GCC 的 Linux 主机上为各种硬件平台编译源代码树。除了默认参数外,该库还允许构建多个属性,通过使用属性行“-D=”来启用和禁用各种功能。
即使 Picolibc 在来自 avrlibc 的小型 stdio 库上运行,用户也可以选择使用属性 (newlib-tinystdio=false) 来启用旧的 newlibc stdio 库。默认情况下,它会是 newlib-tinystdio=true,这意味着它将使用基于 avrlibc 的小型 stdio 进行编译。
在开始构建 picolibc 之前,你们记得要确保meson构建系统是构建环境的一部分,具体请参阅meson的设置说明。
作为meson标准,交叉编译构建的所有构建配置都是单独配置文件的一部分,也是源代码树的一部分。下面是各种构建环境的示例配置文件。
RISC-V 的配置文件(cross-riscv32 -unknown -elf.txt)
1 2 3 4 5 6 7 8 9 10 11 12 |
[binaries] c = 'riscv64-unknown-elf-gcc' ar = 'riscv64-unknown-elf-ar' as = 'riscv64-unknown-elf-as' ld = 'riscv64-unknown-elf-ld' strip = 'riscv64-unknown-elf-strip' [host_machine] system = 'unknown' cpu_family = 'riscv' cpu = 'riscv' endian = 'little' |
使用meson编译 picolibc 源代码树
源码树本身提供了用于各种架构的配置脚本,包括riscv、arm、x86等。下面是我列出的针对 arm的示例编译脚本。
1 2 3 4 5 6 7 8 |
#!/bin/sh ARCH=arm-none-eabi DIR=`dirname $0` meson "$DIR" \ -Dincludedir=picolibc/$ARCH/include \ -Dlibdir=picolibc/$ARCH/lib \ --cross-file "$DIR"/cross-$ARCH.txt \ "$@" |
一旦你们准备好meson和所需的交叉编译器 gcc lib 环境,请按照以下步骤构建具有特定目标(用于 arm)的 picolibc。我的测试构建环境是 ubuntu 18.04。
安装meson
1 2 |
$ sudo apt-get install python3 python3-pip python3-setuptools python3-wheels ninja-build $ pip install --user meson |
安装交叉编译器 GCC(用于arm)
1 |
$ sudo apt-get install gcc-arm-none-eabi |
编译和构建 picolibc
1 2 3 4 5 |
$ mkdir build_picolibc_arm $ cd build_picolibc_arm $ ~/picolibc/do-arm-configure (define your binary file path, which will be part of the picolibc source tree) $ ninja $ sudo ninja install |
成功构建后,你们可以通过在构建文件夹的“test”目录中运行 printf 和 scanf 的示例测试用例应用程序进行验证。
Picolibc 代码编译过程
Picolibc 使用GCC.spec文件来编译源代码,这会使得设置系统头文件的路径和链接器指向 Picolibc。下面是编译单个文件并生成 a.o 文件的示例:
1 |
$ gcc -specs=picolibc.specs -c test.c |
Picocrt (picolibc.specs)—启动代码
Picocrt 是 picolib 的一部分,名为 crt0.o,在使用 -specs= picolibc.specs时默认是启用的。Picocrt 启用初始化步骤,包括如下
- 特定于架构的运行时初始化
- 数据初始化,参考负责处理数据初始化picocrt 的代码片段
1 |
memcpy(__data_start, __data_source, (uintptr_t) __data_size); |
- __data_start 指向 .data 段开始的 RAM 位置
- __data_source 指向存储.data 段初始化值的flash位置
- __data_size 是一个绝对符号,表示初始化数据段的大小
- BSS 初始化,参考 picocrt 的代码片段,它处理数据初始化
1 |
memset(__bss_start, '\0', (uintptr_t) __bss_size); |
- __bss_start 指向 .bss 段开始的 RAM 位置
- __bss_size 是一个绝对符号,表示已清除数据段的大小
- Picolibc 中的 __libc_init_array 和 __libc_fini_array 函数在应用程序启动和关闭期间由 Picocrt 调用,具体如下所示(__libc_init_array() (AppStart) —> main() —> –libc_fini_array() (App Stop))
小标题:参考
文章翻译者:Jacob,嵌入式系统测试工程师、RAK高级工程师,物联网行业多年工作经验,熟悉嵌入式开发、测试各个环节,对不同产品有自己专业的分析与评估。