移植OpenHarmony轻量系统【5】newlibc库移植 原创 精华
摘要:本文简单介绍OpenHarmony轻量系统移植,libc库移植
适合群体:想自己动手移植OpenHarmony轻量系统的朋友
当前M核编译时上层组件与内核统一使用musl-C,位于
//third_party/musl/porting/liteos_m/kernel/,
内核当前的内部实现也是主要适配musl的结构体定义,但社区及三方厂商开发多使用公版工具链arm-none-eabi-gcc加上私有定制优化进行编译,考虑我们内核的易用性提升,故支持公版arm-none-eabi-gccC库编译内核运行。
5.1 newlib库介绍
newlib是小型C库,针对posix接口涉及系统调用的部分,newlib提供一些需要系统适配的钩子函数,例如_exit(),_open(),_close(),_gettimeofday()等,操作系统适配这些钩子,就可以使用公版newlib工具链编译运行程序。
公版arm-none-eabi-工具链中带有通用newlib的C库头文件,在一份通用的C库头文件中,使用不同的宏开关来控制一些 宏、类型、结构体、函数的声明或者定义。这些宏有以下几种:
1> 根据posix标准划分,例如:_POSIX_TIMERS,_POSIX_CPUTIME,_POSIX_THREADS等。
2> 根据已支持的系统,例如:CYGWIN,rtems,__unix__等。
3> 根据C库不同版本,例如:_GNU_SOURCE,_BSD_SOURCE 等。
5.2 liteos-m的libc组成
LiteOS-M的LibC组成结构:
newlib的适配实现:
头文件的适配:
5.3 newlibc库移植
在//kernel/liteos_m目录下使用make menuconfig–>Compat–>Choose libc implementation选择newlibc,如图:
除此之外我们还得做如下适配:
1 malloc适配
malloc适配参考The Red Hat newlib C Library-malloc。实现malloc适配有以下两种方法:
(1)实现 _sbrk_r 函数。这种方法中,内存分配函数使用newlib中的。
(2)实现 _malloc_r, _realloc_r, _reallocf_r, _free_r, _memalign_r, 和 _malloc_usable_size_r。这种方法中,内存分配函数可以使用内核的。
为了方便地根据业务进行内存分配算法调优和问题定位,在这两种方法中,本案例选择后者。
首先,由于newlib中已经存在这些函数的符号,因此需要用到gcc的wrap的链接选项替换这些函数符号为内核的实现,内核的实现为 //kernel/liteos_m/kal/libc/newlib/porting/src/malloc.c。
然后,在device/board/lianzhian/gd32f303_lianzhian/liteos_m/config.gni的新增这些函数的wrap链接选项。
"-Wl,--wrap=_malloc_r",
"-Wl,--wrap=_realloc_r",
"-Wl,--wrap=_reallocf_r",
"-Wl,--wrap=_free_r",
"-Wl,--wrap=_memalign_r",
"-Wl,--wrap=_malloc_usable_size_r",
]
2 vsprintf等适配
参考 https://sourceware.org/newlib/libc.html#vfprintf ,实现 vprintf, vfprintf, printf, snprintf 和sprintf。
类似malloc适配,首先要提供这些函数的实现,device/soc/gd32/gd32f303/liteos_m/components/printf.c,本案例直接采用开源协议友好的实现。与malloc适配不同的是,这个函数由芯片原厂提供。因为就打印来说,根据项目的需要,实现可大可小,内核不方便提供统一的实现。
然后,在device/board/lianzhian/gd32f303_lianzhian/liteos_m/config.gni的新增这些函数的wrap链接选项。
board_ld_flags += [
"-Wl,--wrap=printf",
"-Wl,--wrap=sprintf",
"-Wl,--wrap=snprintf",
"-Wl,--wrap=vsnprintf",
"-Wl,--wrap=vprintf",
]
最终printf函数会调用串口输出内容,这里我们的串口打印接口函数是
_out_char,其实现在printf文件中,如下:
可以看到最终是调用 __io_putchar 函数,所以我们最后需要实现 __io_putchar 函数。该函数目前我们放在uart.c中实现。函数原型如下: