這篇我們將正式進入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換成這一隻試試看。