musl libc 简介与其 porting(六)Busy Box Band

这篇我们将正式进入libc testing的部份,我们可以用以下方式来运行libc-test、并使用qemu linux-user来运行测项:
CROSS_COMPILE=riscv32-linux- CC=musl-gcc RUN_WRAP=$(which qemu-riscv32 make)

接下来,就会看到复数个syscall的fail,横跨数个测资,他们分别是:

statfs
fstatfs
lseek
utimensat

这几项的修复方式与上一篇相似,首先去寻找qemu当中的linux-user syscall 表: linux-user/riscv/syscall32_nr.h
我们可以发现目前与其他32平台一样,stat系的须全部搬迁至time64_t的版本,即:

statfs64
fstatfs64
llseek
utimensat_time64 

在消掉 arch/riscv32/bits/syscall.h.in对应的旧syscall、与增添新的syscall与对应的syscall no後。
我们再去巡视了一下相关的程序逻辑是否有对应的衔接,很幸运地是与上周的waitpid相反,这次的都在musl的REDIR64下有获得串接。

但是我们发现几乎所有shared测资都全挂,定睛一看各种 *.ld.err 的纪录档发现,会报怨vdso相关的linking会有undefined symbol。
这点是我们此篇的重点之一 — — 目前RISC-V平台上,仅有RV64有VDSO机制可以使用、RV32上游在日前被关掉了。
而riscv_icache_flush这个刷掉instruction cache的syscall,目前在musl的设计上,是直接去叫vdso起来用的。

为了方便,我们可以上一组简单暴力的patch来硬噜一次:

diff --git a/src/linux/cache.c b/src/linux/cache.c
index 0eb051c2..0cb65dee 100644
--- a/src/linux/cache.c
+++ b/src/linux/cache.c
@@ -20,6 +20,7 @@ weak_alias(__cachectl, cachectl);
 
 #ifdef SYS_riscv_flush_icache
 
+#if __riscv_xlen == 64
 #define VDSO_FLUSH_ICACHE_SYM "__vdso_flush_icache"
 #define VDSO_FLUSH_ICACHE_VER "LINUX_4.5"
 
@@ -47,4 +48,14 @@ int __riscv_flush_icache(void *start, void *end, unsigned long int flags)
        }
 }
 weak_alias(__riscv_flush_icache, riscv_flush_icache);
+#else // RV64 flush icache
+
+int __riscv_flush_icache(void *start, void *end, unsigned long int flags) 
+{
+               int r = __syscall(SYS_riscv_flush_icache, start, end, flags);
+               if (r != -ENOSYS) return __syscall_ret(r);
+}
+
+weak_alias(__riscv_flush_icache, riscv_flush_icache);
+#endif

在上完这组patch後,我们重跑一次test case,会发现仅剩以下错误:

FAIL src/functional/ipc_msg.exe [status 1]
FAIL src/functional/ipc_msg-static.exe [status 1]
FAIL src/functional/ipc_sem.exe [status 1]
FAIL src/functional/ipc_sem-static.exe [status 1]
FAIL src/functional/ipc_shm.exe [status 1]
FAIL src/functional/ipc_shm-static.exe [status 1]
FAIL src/functional/popen.exe [status 1]
FAIL src/functional/popen-static.exe [status 1]
FAIL src/functional/pthread_cancel.exe [timed out]
FAIL src/functional/pthread_cancel-points.exe [timed out]
FAIL src/functional/pthread_cancel-points-static.exe [timed out]
FAIL src/functional/pthread_cancel-static.exe [timed out]
FAIL src/functional/pthread_mutex.exe [signal Segmentation fault]
FAIL src/functional/pthread_mutex_pi.exe [signal Segmentation fault]
FAIL src/functional/pthread_mutex_pi-static.exe [signal Segmentation fault]
FAIL src/functional/pthread_mutex-static.exe [signal Segmentation fault]
FAIL src/functional/pthread_robust.exe [timed out]
FAIL src/functional/pthread_robust-static.exe [timed out]
FAIL src/functional/strptime.exe [status 1]
FAIL src/functional/strptime-static.exe [status 1]
FAIL src/functional/tls_init_dlopen.exe [status 1]
FAIL src/math/fma.exe [status 1]
FAIL src/math/fmaf.exe [status 1]
FAIL src/math/powf.exe [status 1]
FAIL src/regression/pthread_cancel-sem_wait.exe [timed out]
FAIL src/regression/pthread_cancel-sem_wait-static.exe [timed out]
FAIL src/regression/pthread_once-deadlock.exe [status 1]
FAIL src/regression/pthread_once-deadlock-static.exe [timed out]
FAIL src/regression/pthread-robust-detach.exe [status 1]
FAIL src/regression/pthread-robust-detach-static.exe [status 1]
FAIL src/regression/statvfs.exe [status 1]
FAIL src/regression/statvfs-static.exe [status 1]
FAIL src/regression/tls_get_new-dtv.exe [status 1]

其中popen()的测项是因为他试图呼叫host的echo,但是host的echo是x86-64的档案,当然没办法被qemu-riscv32 (RUN_WRAP)所包围的环境去运行。
为了先在这个环境底下跑,我们不如就来喂它一个cross-build的busybox吧,这时就是我们之前建置 buildroot 全家桶的原因了:

(1) 首先,请将 buildroot/output/build/busybox-xxx 复制一份出来
(2) 修改 .config 将CC 与 LD 修改为 musl-gcc
(3) 修改CLFAGS,添加 /path/to/buildroot/output/build/linux-headers-5.4.139/usr/include 以便让 busybox 可以吃Linux kernel header
(4) 建议 make menuconfig 开启 debug build以及static link以便抓虫

接下来: CROSS_COMPILE=riscv32-linux- make 便可以获得一个 staticall linked的busybox
我们快速地把它 cp 一份成echo看看。

然後,回到我们的 libc-test,修改 popen.c 测项的程序码,将echo指向刚刚的busybox重编重跑。
结果发现他死在 ppclose上,此时发现,又是wait4遗毒。

但是这时,我决定采取截然不同的道路来处理这件事了,wait4的manual page直接明讲:

the following wait4() call:

           wait4(pid, wstatus, options, rusage);

       is equivalent to:

           waitpid(pid, wstatus, options);

在上一篇,我们已经将waitpid打好patch了,直接让wait4串上来吧:

--- a/src/linux/wait4.c
+++ b/src/linux/wait4.c
@@ -8,32 +8,33 @@
 pid_t wait4(pid_t pid, int *status, int options, struct rusage *ru)
 {
        int r;
-#ifdef SYS_wait4_time64
-       if (ru) {
-               long long kru64[18];
-               r = __syscall(SYS_wait4_time64, pid, status, options, kru64);
-               if (!r) {
-                       ru->ru_utime = (struct timeval)
-                               { .tv_sec = kru64[0], .tv_usec = kru64[1] };
-                       ru->ru_stime = (struct timeval)
-                               { .tv_sec = kru64[2], .tv_usec = kru64[3] };
-                       char *slots = (char *)&ru->ru_maxrss;
-                       for (int i=0; i<14; i++)
-                               *(long *)(slots + i*sizeof(long)) = kru64[4+i];
-               }
-               if (SYS_wait4_time64 == SYS_wait4 || r != -ENOSYS)
-                       return __syscall_ret(r);
-       }
-#endif
-       char *dest = ru ? (char *)&ru->ru_maxrss - 4*sizeof(long) : 0;
-       r = __syscall(SYS_wait4, pid, status, options, dest);
-       if (r>0 && ru && sizeof(time_t) > sizeof(long)) {
-               long kru[4];
-               memcpy(kru, dest, 4*sizeof(long));
-               ru->ru_utime = (struct timeval)
-                       { .tv_sec = kru[0], .tv_usec = kru[1] };
-               ru->ru_stime = (struct timeval)
-                       { .tv_sec = kru[2], .tv_usec = kru[3] };
-       }
+    r = waitpid(pid, status, options);

打完这组patch,恭喜~除了解调一个popen的测项,我们那只busybox shell也可以正常运作了。

事到如今,其实我们的musl libc porting已经有个60分左右,SYSV IPC实际使用的比例不大。
下期预告,我们将会先尝试看看把 Litex-on-Vexriscv 的 buildroot based filessystem换成这一只试试看。


<<:  人脸辨识的简介

>>:  Day1 补贴目录与相关概念

Day-02 机器学习的介绍

昨天我们已经聊过了整个机器学习的大概念了,也知道深度学习和机器学习之间的关系,因此我们在这里先好好...

Day8 初探CFS 中

前言 昨天要处里要返回台北的家当,所以只有简单的介绍CFS 是什麽,以及CFS使用了什麽样的资料结构...

[Day11]日期函数

这篇文章要介绍的是单列函数中的日期函数。 前面介绍的是单纯的日期单列函数,接下来列出一些简单的日期运...

[DAY14]跟 Vue.js 认识的30天 - Vue 模组插槽(`slot`)

最近在 youtube 找到一个学习 webpack 很好用的教学影片,所以正努力的学习 webpa...

卡夫卡的藏书阁【Book20】- Kafka - KafkaJS 消费者 2

“I am in chains. Don't touch my chains.” ― Kafka,...