musl libc 简介与其 porting(二)Say hello to my little friend!

今天到了 day3,上次有预告了这次会开始进行RISC-V 32平台的musl libc porting。
好的,那麽先来概算一下我们若要以最小限度进行porting,有哪些方法论?

在已经有RISC-V 64的前提下,个人觉得最快的方法是:研究一下musl的build system如何选取要编译的source set,继而增加新的平台参数、尝试duplicate riscv64作基底,先处理静态连解版本(musl最一开始主打的特技也就是极方便的static link),然後qemu-linux-user进行libc-test的测试修bug。尔後再对动态连结时的版本进行微调。

於是事不宜迟,我们来看一下musl如何得到平台资料以及选取对应的source set:
(1) 我们可以发现手工撰写的configure script中,会透过case switch将build tuple中的target换成他自己定义的ARCH,并且在最後写出去到config.mak当中,而musl纯手工的Makefile会 include 两份档案,一份是生出来的config.mak、另一份是基於config.mak 当中的 ARCH arch.mak

(2) 在Makefile中,我们看到了以下的叙述:

MALLOC_DIR = mallocng
SRC_DIRS = $(addprefix $(srcdir)/,src/* src/malloc/$(MALLOC_DIR) crt ldso $(COMPAT_SRC_DIRS))
BASE_GLOBS = $(addsuffix /*.c,$(SRC_DIRS))
ARCH_GLOBS = $(addsuffix /$(ARCH)/*.[csS],$(SRC_DIRS))
BASE_SRCS = $(sort $(wildcard $(BASE_GLOBS)))
ARCH_SRCS = $(sort $(wildcard $(ARCH_GLOBS)))
BASE_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(BASE_SRCS)))
ARCH_OBJS = $(patsubst $(srcdir)/%,%.o,$(basename $(ARCH_SRCS)))
REPLACED_OBJS = $(sort $(subst /$(ARCH)/,/,$(ARCH_OBJS)))
ALL_OBJS = $(addprefix obj/, $(filter-out $(REPLACED_OBJS), $(sort $(BASE_OBJS) $(ARCH_OBJS))))

首先先定义出有哪些top-level资料夹要编,分别是src底下所有的第一层、以及musl目前有两套的malloc实做、crt、以及runtime dynamic loader,还有出於abi相容需要的程序码(spoiler warning,here's a catch)。

macro的部份简单翻译、翻译,这是想要拼出一组给musl编译时候的两组参数:
${CROSS_COMPILE}gcc -c ${1}.c -o ${2}.o 代表着要将某个C/组语档案,要编成什麽objec档。而我们最在意的是ARCH_GLOBSARCH_SRC这部份,意思就是先拼出所有architecure C/asm档案与对应的object档名关系。

而先前在configure中看见的SUBARCH几乎在Makefile中没有什麽被使用到的场所。

看到这里我们可以先有个初步的概念,虽然初步略看可以使用SUBARCH的机制来进行porting,但是我们会需要很大量的精力深入去看详细的编译流程。
作为30天要跑完整个前中後端的工作,我们最简便、合理的作法是:复制多数 riscv64 一份给 riscv32,并且修改configure

事不宜迟,我们对configure做出以下修改:

diff --git a/configure b/configure
index a5231a0e..b7d06689 100755
--- a/configure
+++ b/configure
@@ -336,6 +336,7 @@ or1k*) ARCH=or1k ;;
 powerpc64*|ppc64*) ARCH=powerpc64 ;;
 powerpc*|ppc*) ARCH=powerpc ;;
 riscv64*) ARCH=riscv64 ;;
+riscv32*) ARCH=riscv32 ;;
 sh[1-9bel-]*|sh|superh*) ARCH=sh ;;
 s390x*) ARCH=s390x ;;
 unknown) fail "$0: unable to detect target arch; try $0 --target=..." ;;
@@ -699,6 +700,11 @@ trycppif __riscv_float_abi_soft "$t" && SUBARCH=${SUBARCH}-sf
 trycppif __riscv_float_abi_single "$t" && SUBARCH=${SUBARCH}-sp
 fi

+if test "$ARCH" = "riscv32" ; then
+trycppif __riscv_float_abi_soft "$t" && SUBARCH=${SUBARCH}-sf
+trycppif __riscv_float_abi_single "$t" && SUBARCH=${SUBARCH}-sp
+fi
+
 if test "$ARCH" = "sh" ; then
 tryflag CFLAGS_AUTO -Wa,--isa=any
 trycppif __BIG_ENDIAN__ "$t" && SUBARCH=${SUBARCH}eb

duplicate出以下的档案/资料夹:

./arch/riscv32
./src/fenv/riscv32
./src/ldso/riscv32
./src/math/riscv32
./src/setjmp/riscv32
./src/signal/riscv32
./src/thread/riscv32

接下来,我们可以使用riscv-gnu-toolchain中的riscv32 toolchain先行编译看看:

mkdir install_dir; CROSS_COMPILE=riscv32-linux- ./configure --target=riscv32 --disable-shared --prefix=$PWD/install_dir
make

这时我们马上就遇到了在setjmp/riscv32/setjmp中的setjmp.Slongjmp.S,於备份及还原register值时,因为riscv32的暂存器宽度仅为32bit,所以riscv64的ld/sd指令并不支援。将其逻辑更改成lw/sw便可以正常执行。相似的还有对於浮点数暂存器作存储的fld/fsd,修改成flw/fsw即可。

类似的问题,我们可以反覆透过make找出,数次迭代後我们就有了非常初期能编译成功的版本,与其相应的patchset:

diff -dr ./arch/riscv64/atomic_arch.h ./arch/riscv32/atomic_arch.h
29c29
< 		"\n1:	lr.d.aqrl %0, (%2)\n"
---
> 		"\n1:	lr.w.aqrl %0, (%2)\n"
31c31
< 		"	sc.d.aqrl %1, %4, (%2)\n"
---
> 		"	sc.w.aqrl %1, %4, (%2)\n"
diff -dr ./src/setjmp/riscv64/longjmp.S ./src/setjmp/riscv32/longjmp.S
10,23c10,23
< 	ld s0,    0(a0)
< 	ld s1,    8(a0)
< 	ld s2,    16(a0)
< 	ld s3,    24(a0)
< 	ld s4,    32(a0)
< 	ld s5,    40(a0)
< 	ld s6,    48(a0)
< 	ld s7,    56(a0)
< 	ld s8,    64(a0)
< 	ld s9,    72(a0)
< 	ld s10,   80(a0)
< 	ld s11,   88(a0)
< 	ld sp,    96(a0)
< 	ld ra,    104(a0)
---
> 	lw s0,    0(a0)
> 	lw s1,    8(a0)
> 	lw s2,    16(a0)
> 	lw s3,    24(a0)
> 	lw s4,    32(a0)
> 	lw s5,    40(a0)
> 	lw s6,    48(a0)
> 	lw s7,    56(a0)
> 	lw s8,    64(a0)
> 	lw s9,    72(a0)
> 	lw s10,   80(a0)
> 	lw s11,   88(a0)
> 	lw sp,    96(a0)
> 	lw ra,    104(a0)
26,37c26,37
< 	fld fs0,  112(a0)
< 	fld fs1,  120(a0)
< 	fld fs2,  128(a0)
< 	fld fs3,  136(a0)
< 	fld fs4,  144(a0)
< 	fld fs5,  152(a0)
< 	fld fs6,  160(a0)
< 	fld fs7,  168(a0)
< 	fld fs8,  176(a0)
< 	fld fs9,  184(a0)
< 	fld fs10, 192(a0)
< 	fld fs11, 200(a0)
---
> 	flw fs0,  112(a0)
> 	flw fs1,  120(a0)
> 	flw fs2,  128(a0)
> 	flw fs3,  136(a0)
> 	flw fs4,  144(a0)
> 	flw fs5,  152(a0)
> 	flw fs6,  160(a0)
> 	flw fs7,  168(a0)
> 	flw fs8,  176(a0)
> 	flw fs9,  184(a0)
> 	flw fs10, 192(a0)
> 	flw fs11, 200(a0)
diff -dr ./src/setjmp/riscv64/setjmp.S ./src/setjmp/riscv32/setjmp.S
10,23c10,23
< 	sd s0,    0(a0)
< 	sd s1,    8(a0)
< 	sd s2,    16(a0)
< 	sd s3,    24(a0)
< 	sd s4,    32(a0)
< 	sd s5,    40(a0)
< 	sd s6,    48(a0)
< 	sd s7,    56(a0)
< 	sd s8,    64(a0)
< 	sd s9,    72(a0)
< 	sd s10,   80(a0)
< 	sd s11,   88(a0)
< 	sd sp,    96(a0)
< 	sd ra,    104(a0)
---
> 	sw s0,    0(a0)
> 	sw s1,    8(a0)
> 	sw s2,    16(a0)
> 	sw s3,    24(a0)
> 	sw s4,    32(a0)
> 	sw s5,    40(a0)
> 	sw s6,    48(a0)
> 	sw s7,    56(a0)
> 	sw s8,    64(a0)
> 	sw s9,    72(a0)
> 	sw s10,   80(a0)
> 	sw s11,   88(a0)
> 	sw sp,    96(a0)
> 	sw ra,    104(a0)
26,37c26,37
< 	fsd fs0,  112(a0)
< 	fsd fs1,  120(a0)
< 	fsd fs2,  128(a0)
< 	fsd fs3,  136(a0)
< 	fsd fs4,  144(a0)
< 	fsd fs5,  152(a0)
< 	fsd fs6,  160(a0)
< 	fsd fs7,  168(a0)
< 	fsd fs8,  176(a0)
< 	fsd fs9,  184(a0)
< 	fsd fs10, 192(a0)
< 	fsd fs11, 200(a0)
---
> 	fsw fs0,  112(a0)
> 	fsw fs1,  120(a0)
> 	fsw fs2,  128(a0)
> 	fsw fs3,  136(a0)
> 	fsw fs4,  144(a0)
> 	fsw fs5,  152(a0)
> 	fsw fs6,  160(a0)
> 	fsw fs7,  168(a0)
> 	fsw fs8,  176(a0)
> 	fsw fs9,  184(a0)
> 	fsw fs10, 192(a0)
> 	fsw fs11, 200(a0)
diff -dr ./src/signal/riscv64/sigsetjmp.s ./src/signal/riscv32/sigsetjmp.s
11,12c11,12
< 	sd ra, 208(a0)
< 	sd s0, 224(a0)
---
> 	sw ra, 208(a0)
> 	sw s0, 224(a0)
19,20c19,20
< 	ld s0, 224(a0)
< 	ld ra, 208(a0)
---
> 	lw s0, 224(a0)
> 	lw ra, 208(a0)
diff -dr ./src/thread/riscv64/clone.s ./src/thread/riscv32/clone.s
12,13c12,13
< 	sd a0, 0(a1)
< 	sd a3, 8(a1)
---
> 	sw a0, 0(a1)
> 	sw a3, 8(a1)
28,29c28,29
< 1:      ld a1, 0(sp)
< 	ld a0, 8(sp)
---
> 1:      lw a1, 0(sp)
> 	lw a0, 8(sp)
diff -dr ./src/thread/riscv64/syscall_cp.s ./src/thread/riscv32/syscall_cp.s
23c23
< 	ld a6, 0(sp)
---
> 	lw a6, 0(sp)

到这里,我们可以运行make install将获得的libc.a,安装出去,然後火速写个hello world、配qemu-riscv32来验验看:

$ cat ./hello.c
#include <stdio.h>

int main(int ac, char* av) {
    printf("Say hello to my little friend.\n");
    return 0;
    }
$ riscv32-unknown-elf-gcc -nostdlib -nostdinc -L./install_dir/lib -I./install_dir/include ./test.c ./lib/Scrt1.o -lc -o ./test
$ qemu-riscv32 ./test
Say hello to my little friend.

Easy peasy, huh ?

但是接下来问题就来了:

$ cat ./test_time.c 
#include <stdio.h>
#include <time.h>

int main()
{
  time_t t = time(NULL);
  struct tm tm = *localtime(&t);
  printf("now: %d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
}
$ riscv32-unknown-elf-gcc -nostdlib -nostdinc -static -L./lib -I./include ./test_time.c ./lib/Scrt1.o -static-libgcc -lc -lgcc -o ./test_time
$ qemu-riscv32 ./test_time
Segmentation Fault.

嘿嘿,这是为什麽咧?其实就跟上面的ARCH COMPAT有关。且待下回分解~


<<:  Day 12-假物件 (Fake) - 模拟物件 (Mock)-1 (核心技术-4)

>>:  讯息是怎麽进到网际网路的(二)?区网内的装置:AP, Switch, Router

Day13-Async && Await

前言 虽说ES6推出了promise解决了callback hell的问题,但人总是不容易满足。 於...

Day 30: DevOps完赛心得

最後,不免俗的还是要说一下完赛感言: 今年是第一次参赛,参赛完的感言是囤文章很重要!! 其实我对後面...

[D06] OpenCV 介绍与用法

OpenCV 广泛被应用在对目标进行辨识、测量、纪录等,并更进一步的进行影像处理! 读取图片 由於 ...

[Day 20] - 初探永丰银行线上收款API - 订单查询及其他(2)

昨晚睡一睡突然一个念头闪过, 为什麽用那麽多try catch 昨天写的程序中,我用了大量的try ...

Day11,看法讲完後该安装了

目前Kubernetes的安装方式有很多种可以参考CNCF Landscape中的certified...