上一篇有提到,我們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有 zlib
、linux
(指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這邊被編譯器優化成了一個無窮迴圈:
這下子就奇怪了?合理懷疑是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品質。