musl libc 简介与其 porting(四)Nobody speak.

上一篇有提到,我们porting时发生printf()在有format string印不出东西、segfault掉的状况,这下该怎麽办呢?
一样先来上首歌:《Nobody Speak》https://www.youtube.com/watch?v=NUC2EQvdzmY

好的,我们接下来正式踏入一步一脚印porting中较为麻烦的步骤了,万幸的是我们现在有qemu的linux-user mode可以用,配合着gdb stub,我们可以快速地进行除错。首先,我们将qemu linux-user运行起来:

qemu-riscv32 -g 1234 ./test_time

紧接着我们可以使用gdb连上它:

riscv32-linux-gdb -ex 'target remote localhost:1234'

然而眼尖的朋友可能会注意到我换toolchain了,不再是前几偏中使用riscv-gnu-toolchain那边release的prebuilt elf toolchain。其实这是一个想偷偷藉机推销一下buildroot埋下的伏笔,我们在Linux环境开发时,有时会遇到一种状况:BSP的工具链太旧、相依的函式库有状况,或选项开启不足......等等。小弟我遇到的情形是,因为我日常开发的环境是Arch Linux,因为采取rolling release的方式、常常会遇上外界编好的东西与系统上的函式库版本对不上的问题。另外就是小弟我非常仰赖gdb的TUI模式、可说是重度使用者,没有它我debug的能力会只剩下10%吧,而很多时候预编工具链的TUI mode常常是关闭的。此时,透过buildroot重新建置一套工具链便是必然的结果。

然而,手工调整buildroot的组态有点花时间,我们乾脆直接使用qemu virtual platform的组态来进行修改:

git clone https://github.com/buildroot/buildroot
cd buildroot
make qemu_riscv32_virt_defconfig

紧接着 make menuconfig 後,我们可以透过以下选单顺序开启gdb的建置 Toolchain => Build cross gdb for the host => TUI mode
关於buildroot的build target可以从其文件中得知,这边小弟只简单带过几个常用的与其规则:
在buildroot的世界观中,东西是以package的形式存在,就跟rpm/deb类似,常见的package有 zliblinux(指kernel) ......等等,也有类似meta-package的概念,如toolchain。 而这些package若是编译平台自身要用的,会加上前缀host-。如果要重新编译某个package,则是加上後缀-remake

所以这边我们要建置gdb,其实就是进行 make host-gdb。不过以我们的使用情境,我会建议直接make all,原因会在以後的篇章解释。
在漫长的下载与编译後,我们获得了打开TUI模式、target为riscv32的host-gdb可以使用,他会放在 output/host/bin 里面。

好的,回到TUI mode下,我们发现format string的处理函数fmt_u这边被编译器优化成了一个无穷回圈:

TUI_mode_infinite_loop

这下子就奇怪了?合理怀疑是ULONG_MAX的定义出现了错误。
我们干掉musl build资料夹下的object code重编 obj/src/stdio/vfprintf.lo 看看compiler有没有报什麽讯息好了:

src/stdio/vfprintf.c: In function ‘fmt_u’:
src/stdio/vfprintf.c:168:16: warning: integer overflow in expression of type ‘long long int’ results in ‘-2’ [-Woverflow]
  168 |  for (   ; x>ULONG_MAX; x/=10) *--s = '0' + x%10;
      |                ^

结果还真的有。
以compiler的理解上,因为ilp32下被错误定义的 LONG_MAX*2+1 远超过 unsigned long可容纳的大小,最後一连串type promote被算成了-1 ,而 x 是signed value,无论如何都大於 -1,结果被直接优化成一个无穷回圈。

回头去检查 arch/riscv32/alltypes.h.in ,发现我们需要重新定义data types。要简单的话可以直接搬arm32或mips32的来用。

+#define _Addr int
+#define _Int64 long long
+#define _Reg int

+#define __BYTE_ORDER 1234
+#define __LONG_MAX  0x7fffffffL
+//#define __LONG_MAX 0x7fffffffffffffffL

详情可见commit:
https://github.com/Ruinland/musl-rv32port/commit/fc3b36c2aa7d65a6de34ad727ce3e41c6af4aaf0
在搬迁完成後,我们就可以正常获得一个印时间的结果了!

$ qemu-riscv32 ./test_time
now: 2021-09-14 19:52:34

现在在修正了基本的data type後,我们明天将会来跑跑看libc-test来进行更深入的压力测试,来保障我们的porting品质。


<<:  案例:AWS MLOps Framework - AWS CloudFormation 模板,部署单帐号版本

>>:  予焦啦!支援 RISC-V 权限指令与暂存器

安装宝塔windows面板7.1.9免绑账号版本

宝塔windows版本自7.2开始需要绑定账户才能使用,删除“bind_path.pl”都没用,甚至...

ASP.NET Core MVC

什麽是 MVC 分别是 Model, View, Controller, 是一种软件架构, View...

Day19 CSS Transform

设定transform属性可以使文字或图像有旋转(rotate)、缩放(scale)、倾斜(ske...

Day 12 - Spring Boot & MyBatis

MyBatis 可以简单的使用注解或XML 的方式进行配置和对映,通过将引数对映到配置的SQL 形成...

[Day 03]取得Nonce与HashID以产出Sign - [C#]丰收款API必备前置作业(二)

首先来个永丰官方的文件图片作开场吧! (图一:由商户->永丰正向发动的所需参数) 而我们今天要...