iT邦幫忙

2021 iThome 鐵人賽

DAY 5
0

上一篇有提到,我們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品質。


上一篇
musl libc 簡介與其 porting(三)No time to die.
下一篇
musl libc 簡介與其 porting(五) Knocking on Heaven's Door
系列文
Port Alpine Linux to open source RISC-V platform30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言