musl libc 简介与其 porting(一)

musl libc的起源为当时glibc於设计上有诸多不足之处,例如全静态连结因NSS机制而有状况、内部机制甚多而难以维护/移植、组语甚多、pthread实做有bug、子函式库......等等;Rich Felker在维运EWONTFIX此一网站记载甚多後,决定自己打造一套新的libc/runtime。

然而一个非常大的设计更动是,musl libc不支援lazy binding,也就是在动态连结时,符号解析仅在使用时才作连结。这点在musl的rtld (runtime dynamic loader上的设计非常特别,会在 __dls2 这个phase时,直接运行reloc_all()进行权符号解析[1],若有无法解析的symbol则会拒绝运行、形同於glibc下的LD_BIND_NOW;另一个特点是当时使用dlopen()时,仅有RTLD_NOW被接受。这件事情直到非常後期musl引入了自创的一套流程:deffered binding [2],在dlopen新的shared object时,会尽可能resolve多数的symbol。其中一个有趣的comment如下:〝call-time plt resolver is intentionally not implemented 〞、〝it is a huge bug surface and demands significant amounts of arch-specific code〞这也是musl libc设计的一个重点 — — 他要尽量减少architecture的porting困难。

好的,说到这边我们切入正题,到底musl libc porting与自身启动上有什麽部份。

首先最重要的是CRT,也就是所谓的C runtime,这边掌管了作业系统核心(i.e. Linux elf loader)跳转进user C program时,最初的初始化与最後的收屍(?)。
然而,出於musl对於RISC-V此类新平台不再支援传统的_init与_fini,故在 "crt" 此一资料夹下,没有平台相依的程序码,仅有stub用的空档案crtn.c。
平台相依的部份被封装在 arch/<platform>/crt_arch.h 中,而PIE时使用的Scrt1,与一般的程序的crt1.c一致。
对於RISC-V平台(或多数平台而言)来说,最原初所需要作的事情包含:
global pointer的初始化、传递dynamic linking时PT_DYNAMIC segment的资料位置、stack pointer的初始化。
其中gp的值与_DYNAMIC的值为link time才知道,故为weak symbol待linker填入。

接下来,便会跳转至crt1.c、做完argc与argv的shifting後,直接掉入 src/env/__libc_start_main.c ,开始作几个重要的处理:
(1) Auxiliary Vector 设定,这是一个许多平台优化程序会常常仰赖的阵列。内含作业系统核心提供的平台资讯,例如有哪些扩增指令集、cache大小、alignment资讯......etc,这点在user program可以使用getauxval()此一函数取得。
(2) Thread Local Storage初始化。在现行multi-threading的世界观下,thread与thread之间共用除了stack外多数的资源,若真的需要thread专属的空间存放资料,会使用TLS,而职掌TLS在哪、大小、初始值,就是CRT的工作。
(3) Stack Smash Protector的canary word初始化。在现代的compiler下,为了避免return address被攻击者覆写,一个简单的机制是透过设定canary word,compiler会自动在stack尾巴插入检查canary word有没有被破坏的code。不过如果编译musl libc时有设定no stack protector时,这点会被关闭。

最後,如果user program有好好使用GCC提供的 constructor 修饰字,musl的作者即便很不情愿(他多次在许多场合抨击相关设计),还是会去执行init_array中的function。

到这边,musl libc CRT的启动已经完全简介过一轮,在下一篇时,我们将会开始进行真正平台相依的程序码移植。

到时会使用的repo在:
https://github.com/Ruinland/musl-rv32port

[1] http://git.musl-libc.org/cgit/musl/tree/ldso/dynlink.c?id=b7a130e0b9195625ea96044ea9fbe99167b112bf#n1689
[2] http://git.musl-libc.org/cgit/musl/commit/?id=6476b8135760659b25c93ff9308425ca98a9e777


<<:  day11 : argo gitops服务以及ingress (上)

>>:  [Day11] CH08:积沙成塔——Array & ArrayList(上)

TailwindCSS 从零开始 - 伪类变体 Pseudo-Class Variants

什麽是伪类变体 又来一个专有名词,还没学就心慌慌... 但是发现有一个熟悉名词:伪类(看到这个我就...

Day 26 - 建立自己的K线资料库 (上)

本篇重点 本篇目标是要下载kbar资料及建立自已的K线资料库 抓取所有股票Contract 抓取所有...

[Day28] grid-row / grid-column + grid-area

如果你已经对上一篇 Day27 的属性熟悉,这篇很快就会懂,因为只是加以说明上一篇的缩写方式,一样都...

SQL Server 每日定期备份与定期删除旧有备份档

SQL Server 资料库备份是将存放在资料库里面的资料,转成单一档案保存,通常是副档名为 bak...

3.1 Design System - 管理平台

万事起头难 爬山一开始总会比较喘、比较累 但逐渐靠近山头後 身体就慢慢适应环境了 登山者也较能欣赏...