再聊 CPUState、qemu 的 gdbserver

jollen 發表於 April 18, 2007 12:06 AM

前一則日記「小聊 qemu 的 CPUState」提到:

相關應用,例如,我們只要觀察 r15(pc)暫存器的值,就可以知道現在這台機器的程式執行位置。實際上的應用,像是 qemu 所實作的 gdbserver 即是透過此 object 的資訊,來回覆 gdb client 的命令。

以下展示實際的操作畫面。

下圖是利用 jk2410-emulator 模擬 Jollen-Kit! Pro. 的開機畫面,使用的 kernel 是 2.6.20.4:

在 kernel 檢查 root filesystem 是否為 initramfs 的時候,我們啟動了 gdb client,並透過 socket 連到 qemu 的 gdbserver;為了方便進行 source-level debug,我們使用 cgdb 來操作,下圖是 debug 畫面:

我們可以看到,此時 kernel 正執行到 lib/inflate.c 裡的第 59x 行程式,我們利用 next 做單步執行。這種「一邊跑 kernel,一邊看 kernel 跑到哪一個原始檔的哪一行」正是使用 qemu 做「整體系統」除錯的關鍵突破之一。

回到 qemu,找到 gdbserver 的實作 'gdbstub.c';當 host 的 gdb(gdb client)端對 target 端 gdb server 下命令時(透過 gdb remote protocol),qemu 實作的 gdb server 會先由 CPUState 讀取 register 的內容,再回應給 gdb client。gdb remote debug protocol 顯然不是我們目前的重點,所以先找到 'gdbstub.c' 關於「讀取 ARM 處理器 register」的主要程式片斷如下:

#elif defined (TARGET_ARM)
static int cpu_gdb_read_registers(CPUState *env, uint8_t *mem_buf)
{
    int i;
    uint8_t *ptr;

    ptr = mem_buf;
    /* 16 core integer registers (4 bytes each).  */
    for (i = 0; i < 16; i++)
      {
        *(uint32_t *)ptr = tswapl(env->regs[i]);
        ptr += 4;
      }
    /* 8 FPA registers (12 bytes each), FPS (4 bytes).
       Not yet implemented.  */
    memset (ptr, 0, 8 * 12 + 4);
    ptr += 8 * 12 + 4;
    /* CPSR (4 bytes).  */
    *(uint32_t *)ptr = tswapl (cpsr_read(env));
    ptr += 4;

    return ptr - mem_buf;
}

gdb server 回傳給 host 端的資訊,已經由 host 端的 gdb 為我們解析,並根據 debug sections 來找到 $pc 對應的原始程式位置,即 source-level debug。

由於這種除錯模式,是對「整粒 ARM 處理器」做除錯,概念上很像是 jtag debug,因此會比用 kgdb 或 kdb 來得實用。原理上來看,gdb 單純是對 qemu 做除錯,而 qemu 是一個可以讓「完整 Linux 系統」在上面執行的 'system emulator'(for various processors),所以我們可以對模擬器裡頭的程式概念上「直接」進行除錯,因為對 host 端的 gdb 來說,這是通透的(transparent)。

再者,對除錯的二大重要工作:source-level debug 或是「單步執行」來說,qemu 單純只回應 $pc 給 gdb client,qemu 並不會知道現在跑的程式是什麼,同時也不需再加入(或修改)gdb stub 至 kernel 或 application,這樣的通透性,讓此種除錯模式可說是非常「萬能」的。不過,在實務上仍有一些不足的地方,例如,我們需要在 host 端編譯出具備除錯資訊的程式,才能搭配 qemu 來除錯,並且,也要小心注意 host 端的 loaded ELF image 與 run-time 的程式是否相同。

高通透性並非全然都是好的,例如,即使 loaded kernel image(vmlinux)與 run-time kernel 不同,我們還是會看到「目前跑到 source code 什麼地方」,因此目前的 source code 位置$pc 的位址是對不起來的。實務上,host gdb 透過 qemu 來除錯 kernel 的高度通透性,也帶來了一些小問題,有機會再做整理分享,或許可在 [OrzLab] 的讀書會上提出討論。

不過,如果是因為忘了重新打包 kernel,就直接跑 emulator 進行除錯,而發生上述的錯誤,實在是非戰之罪。

來源: Jollen's Blog

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw