今天到了 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_GLOBS
跟ARCH_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.S
跟longjmp.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
前言 虽说ES6推出了promise解决了callback hell的问题,但人总是不容易满足。 於...
最後,不免俗的还是要说一下完赛感言: 今年是第一次参赛,参赛完的感言是囤文章很重要!! 其实我对後面...
OpenCV 广泛被应用在对目标进行辨识、测量、纪录等,并更进一步的进行影像处理! 读取图片 由於 ...
昨晚睡一睡突然一个念头闪过, 为什麽用那麽多try catch 昨天写的程序中,我用了大量的try ...
目前Kubernetes的安装方式有很多种可以参考CNCF Landscape中的certified...