C runtime/lib 通常都会有一个测试自己实做正确性的testsuite,像在glibc内部、就有两套测试,一套是检验正确性、回归测试用的testsuite
,另外一套是效能的benchtest
;而musl-libc的同等物叫作libc-test
的testsuite,目前由libm的作者 Nagy 在进行维运。
然而,在测试libc-test之前,我们先来处理一件事情会比较好,那就是 dynamic linking/loading 的问题。因为musl-libc的检验方式,会测试 statically-linked build, statically-linked run, dynamically-linked build, dynamically-linked run 这四个排列组合。而且因为他的编译方式,我们先初步bringup dynamica,会有意外的好处。
为了打通dynamic linking/loading,我们必须修正原本来自RV64平台 arch/riscv32/reloc.h
中的relocation type定义:
diff ./arch/riscv32/reloc.h ./arch/riscv64/reloc.h
9c9
< #define LDSO_ARCH "riscv32" RISCV_FP_SUFFIX
---
> #define LDSO_ARCH "riscv64" RISCV_FP_SUFFIX
13c13
< #define REL_SYMBOLIC R_RISCV_32
---
> #define REL_SYMBOLIC R_RISCV_64
17,19c17,19
< #define REL_DTPMOD R_RISCV_TLS_DTPMOD32
< #define REL_DTPOFF R_RISCV_TLS_DTPREL32
< #define REL_TPOFF R_RISCV_TLS_TPREL32
---
> #define REL_DTPMOD R_RISCV_TLS_DTPMOD64
> #define REL_DTPOFF R_RISCV_TLS_DTPREL64
> #define REL_TPOFF R_RISCV_TLS_TPREL64
紧接着,我们在编译的时候需要加上对应的参数:
CROSS_COMPILE=riscv32-linux- configure --target=riscv32 --enable-shared --enable-debug --prefix=/path/to/install
DESTDIR=/path/to/install make install
在编译时,可以观察到除了有link出shared library libc.so
外,还有一份 musl-gcc 的档案被写了出去。
此时我们把他打开来一探究境:
#!/bin/sh
exec "${REALGCC:-riscv32-linux-gcc}" "$@" -specs "/path/to/install/musl-gcc.specs"
简单说,他是会去呼叫configure时,吃进去的 compiler name,并且使用特殊的spec file:
%rename cpp_options old_cpp_options
*cpp_options:
-nostdinc -isystem /path/to/install/include -isystem include%s %(old_cpp_options)
*cc1:
%(cc1_cpu) -nostdinc -isystem /path/to/install/include -isystem include%s
*link_libgcc:
-L/path/to/install/lib -L .%s
*libgcc:
libgcc.a%s %:if-exists(libgcc_eh.a%s)
*startfile:
%{!shared: /path/to/install/lib/Scrt1.o} /path/to/install/lib/crti.o crtbeginS.o%s
*endfile:
crtendS.o%s /path/to/lib/crtn.o
*link:
-dynamic-linker /lib/ld-musl-riscv32.so.1 -nostdlib %{shared:-shared} %{static:-static} %{rdynamic:-export-dynamic}
*esp_link:
*esp_options:
*esp_cpp_options:
简单来说,这份档案是会让gcc执行时,会在对应的使用情境下,开启不同的编译选项,例如nostdinc
,所以不会去寻找toolchain stage2时设定的C runtime/library,CRT档案在何方、以及最重要的 — — 在load time时会把so放好、做好symbol resolve的dynamic-linker 。
这时,我们可以非常快速的补正一下上面对应的路径,到你安装的位置上,从此之後就能非常轻松地使用 musl-gcc这个wrapper来编译程序。
於是,头号拿来编的对象就是,libc-test ~~~
按照readme复制一份config.mak出来,
cp config.mak.def config.mak
接着修改Makefile中的RUN_TEST
为qemu-riscv32
,并且开始编译/执行:
CC=musl-gcc make all run
编译、运行完libc-test号,我们发现有两个syscall目前有状况,直接坏在testsuite的大门上,他们便是:
sigtimedwait
以及waitpid
。
第一件事情比较简单,我们快速翻阅qemu的./linux-user/riscv/syscall32_nr.h
发现,在riscv32跟其他32平台上,该syscall已经被重新定义成sigtimedwait_time64
,快速修正一下:
diff --git a/arch/riscv32/bits/syscall.h.in b/arch/riscv32/bits/syscall.h.in
index 480adc58..67070141 100644
--- a/arch/riscv32/bits/syscall.h.in
+++ b/arch/riscv32/bits/syscall.h.in
@@ -133,7 +134,8 @@
#define __NR_rt_sigaction 134
#define __NR_rt_sigprocmask 135
#define __NR_rt_sigpending 136
-#define __NR_rt_sigtimedwait 137
+//#define __NR_rt_sigtimedwait 137
+#define __NR_rt_sigtimedwait_time64 421
#define __NR_rt_sigqueueinfo 138
#define __NR_rt_sigreturn 139
#define __NR_setpriority 140
diff --git a/arch/riscv32/syscall_arch.h b/arch/riscv32/syscall_arch.h
index ecf2b8a5..5f8760f7 100644
--- a/arch/riscv32/syscall_arch.h
+++ b/arch/riscv32/syscall_arch.h
@@ -2,6 +2,7 @@
#define SYSCALL_FADVISE_6_ARG
#define SYSCALL_IPC_BROKEN_MODE
#define SYS_clock_gettime SYS_clock_gettime64
+#define SYS_rt_sigtimedwait SYS_rt_sigtimedwait_time64
第二件事情就复杂了,简单说Linux kernel 32bit平台在搬到64bit time_t後,wait4这个syscall直接被干掉了。而新的waitid其实跟waitpid、wait4都差很远。
万幸的是我们可以找到glibc那边有一组可以用、但是没有被收进去的patch: https://sourceware.org/legacy-ml/libc-alpha/2019-09/msg00443.html
具体修正长这样:
#include <sys/wait.h>
+#include <signal.h>
#include "syscall.h"
pid_t waitpid(pid_t pid, int *status, int options)
{
+ #if __riscv_xlen != 32
return syscall_cp(SYS_wait4, pid, status, options, 0);
+ #else // generic wait4
+ pid_t ret;
+ idtype_t idtype = P_PID;
+ siginfo_t infop;
+
+ if (pid < -1)
+ {
+ idtype = P_PGID;
+ pid *= -1;
+ }
+ else if (pid == -1)
+ {
+ idtype = P_ALL;
+ }
+ else if (pid == 0)
+ {
+ /* Linux Kernels 5.4+ support pid 0 with P_PGID to specify wait on
+ * the current PID's group. Earlier kernels will return -EINVAL.
+ */
+ idtype = P_PGID;
+ }
+
+ options |= WEXITED;
+
+ ret = syscall_cp (SYS_waitid, idtype, pid, &infop, options, 0);
+ //ret = SYSCALL_CANCEL (waitid, idtype, pid, &infop, options, NULL);
+
+ if (ret < 0)
+ {
+ return ret;
+ }
+
+ if (status)
+ {
+ *status = 0;
+ switch (infop.si_code)
+ {
+ case CLD_EXITED:
+ *status = infop.si_status << 8;
+ break;
+ case CLD_DUMPED:
+ *status = 0x80;
+ /* Fallthrough */
+ case CLD_KILLED:
+ *status |= infop.si_status;
+ break;
+ case CLD_TRAPPED:
+ case CLD_STOPPED:
+ *status = infop.si_status << 8 | 0x7f;
+ break;
+ case CLD_CONTINUED:
+ *status = 0xffff;
+ break;
+ }
+ }
+
+ return infop.si_pid;
+ #endif // rv32 waitid hack
}
在上了这两组patch後,我们至少敲得进大门了QQ
$ qemu-riscv32 ./src/common/runtest.exe -w /usr/bin/qemu-riscv32 -t 1 ./src/functional/argv.exe
$ echo $?
0
然而,一放下去给他全跑:
RUN_WRAP=/usr/bin/qemu-riscv32 make run
马上又是debug地狱了XD
先让我富坚一下XDDDDD
<<: [Day12] Android - Kotlin笔记:JetPack - Fragments在Navigation中的参数传递(Safe Args)
介绍完前端就是要接着後端介绍阿! 今天挑Node.js Node.js入门 首先先进入到点选左边的长...
docker的使用也是一直都想学了 我们是开始满久才开始套用进来的 因为刚开始都觉得这是一个很难的东...
或许有人会问「那我要开发 SPA 网站时,要如何跟 Grails 搭配呢?」。这个问题的答案很简单,...
很快的地狱般的铁人赛终於要结束了,今天就来聊聊这30天的学习心得。 其实一开始挑选这个题目时,也是无...
前情提要 最後一篇正篇,稍稍回顾了一下之前的每一篇 发现对於现实上的使用案例,好像没有太多的描述 所...