Remote GDB Debugging

binutils-aarch64-linux-gnu 套件缺乏 aarch64-linux-gnu-gdb,於是直接抓 binutils-gdb 下來編譯。

Build gdb

ubuntu 21.10

1
2
3
git clone git://sourceware.org/git/binutils-gdb.git --depth=1 --branch gdb-11.2-release
./configure --target=aarch64-linux-gnu
make -j8

ubuntu 24.04

1
2
3
git clone git://sourceware.org/git/binutils-gdb.git --depth=1 --branch gdb-13.1-release
./configure --target=aarch64-linux-gnu
make -j1

如果在 gdb 執行時看到這麼一段警告

1
warning: Can not parse XML target description; XML support was disabled at compile time

且無法看 aarch64 system registers

1
2
3
4
5
6
(gdb) info all-registers
...
b28 {u = 0x0, s = 0x0} {u = 0, s = 0}
b29 {u = 0x0, s = 0x0} {u = 0, s = 0}
b30 {u = 0x0, s = 0x0} {u = 0, s = 0}
b31 {u = 0x0, s = 0x0} {u = 0, s = 0}

那可先安裝 libexpat1-dev、在 configure 加入 --with-expat 來支援 Target Description

1
2
apt install libexpat1-dev
./configure --target=aarch64-linux-gnu --with-expat

gdb client 會收到一份 Target Description - XML,能顯示 register 當前內容。

1
2
3
4
5
6
7
8
9
10
11
(gdb) info registers
...
MVFR1_EL1 0x12111111 303108369
MDRAR_EL1 0x0 0
OSLSR_EL1 0xa 10
CTR_EL0 0x8444c004 2219098116
REVIDR_EL1 0x0 0
SCTLR 0xc50838 12912696
ACTLR_EL1 0x0 0
CPACR 0x0 0
...

Trouble shooting

編譯過程中可能發生錯誤

1
error: expat is missing or unusable
1
2
3
checking whether to use expat... yes
checking for libexpat... (cached) no
configure: error: expat is missing or unusable

大概看了一下腳本是透過創立 XML_Parser 來檢查 expat 的存在

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libexpat" >&5
$as_echo_n "checking for libexpat... " >&6; }
if ${ac_cv_libexpat+:} false; then :
$as_echo_n "(cached) " >&6
else

ac_save_LIBS="$LIBS"
LIBS="$LIBS $LIBEXPAT"
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#include "expat.h"
int
main ()
{
XML_Parser p = XML_ParserCreate (0);
;
return 0;
}

將這段程式碼拉出來單獨透過 aarch64-linux-gnu-gcc 編譯,看會遇到什麼問題

1
2
3
4
5
6
7
8
#include "expat.h"
int
main ()
{
XML_Parser p = XML_ParserCreate (0);
;
return 0;
}
1
2
3
aarch64-linux-gnu-gcc ./test.c $(pkg-config --libs expat) -o test
/usr/lib/gcc-cross/aarch64-linux-gnu/11/../../../../aarch64-linux-gnu/bin/ld: cannot find -lexpat: No such file or directory
collect2: error: ld returned 1 exit status

加上 verbose 進行除錯

1
2
3
4
5
6
7
/usr/lib/gcc-cross/aarch64-linux-gnu/11/../../../../aarch64-linux-gnu/bin/ld -lexpat --verbose

==================================================
attempt to open /lib/libexpat.so failed
attempt to open /lib/libexpat.a failed
attempt to open /lib/aarch64-linux-gnu/libexpat.so failed
attempt to open /lib/aarch64-linux-gnu/libexpat.a failed

Reference

透過 GDB 進行遠端除錯

為了方便將 gdb 設 alias

1
alias aarch64-linux-gnu-gdb='~/Desktop/OSDI/binutils-gdb/gdb/gdb'

運行 qemu 實體

  • -s: 在 port 1234 開啟 gdbserver
  • -S: 在 monitor 按下 c 才開始運行
1
qemu-system-aarch64 -M raspi3 -kernel kernel8.img -serial pty -monitor stdio -S -s

在另一個視窗開啟 gdb

1
2
3
4
5
6
7
~$ aarch64-linux-gnu-gdb

For help, type "help".
Type "apropos word" to search for commands related to "word"...
(gdb) file kernel8.elf
Reading symbols from kernel8.elf...
(gdb) target remote :1234

設定中斷點

1
2
3
4
5
6
7
(gdb) b _start
(gdb) continue
Continuing.

Thread 1 hit Breakpoint 1, _start () at a.S:3
3 wfe
(gdb) list

又或者在 qemu monitor 暫停模擬

1
(qemu) stop

同時 gdb 視窗可見以下輸出

1
2
3
4
Thread 1 received signal SIGINT, Interrupt.
err_hang () at src/exception_table.S:194
194 err_hang: b err_hang
(gdb)

Reference:

AArch64 calling convention

Ref: AArch64 calling convention

The 64-bit ARM (AArch64) calling convention allocates the 31 general-purpose registers as:[2]

x31 (SP): Stack pointer or a zero register, depending on context.
x30 (LR): Procedure link register, used to return from subroutines.
x29 (FP): Frame pointer.
x19 to x29: Callee-saved.
x18 (PR): Platform register. Used for some operating-system-specific special purpose, or an additional caller-saved register.
x16 (IP0) and x17 (IP1): Intra-Procedure-call scratch registers.
x9 to x15: Local variables, caller saved.
x8 (XR): Indirect return value address.
x0 to x7: Argument values passed to and results returned from a subroutine.

All registers starting with x have a corresponding 32-bit register prefixed with w. Thus, a 32-bit x0 is called w0.

Similarly, the 32 floating-point registers are allocated as:[3]

v0 to v7: Argument values passed to and results returned from a subroutine.
v8 to v15: callee-saved, but only the bottom 64 bits need to be preserved.
v16 to v31: Local variables, caller saved.