#1 問題: gdb 斷點異常
這幾天更新了 qemu, 然後在進行 gdb 調試的時候, 發現斷點斷不住了.
之前都是正常的, 從來沒有出現過這種情況啊. 繼續分析下看看是哪裏出現的異常.
#2 原因分析
難道是 gdb 或者 QEMU 出現 BUG 了, 我們先看下斷點的位置是否正確.
- vmlinux 中符號的地址(gdb插入斷點的位置)
gdb 是直接讀取 vmlinux 中的符號的加載地址去添加斷點的, 那麼 0xffffffff81aa1800
應該就是 vmlinux
中 schedule 的函數地址
#objdump -d ./vmlinux| grep "<schedule>:"
ffffffff81aa1800 <schedule>:
可以看到沒有問題, 但是爲什麼沒有斷到呢, 難道當前內核鏡像中的地址不是這個麼?
- 當前內核鏡像的符號地址
# cat /proc/kallsyms | grep -E " schedule$"
ffffffffb0ca1800 T schedule
- 原因分析
schedule 在 vmlinux 鏡像中的符號地址(ffffffff81aa1800)與 qemu 啓動的內核中虛擬地址(ffffffffb0ca1800)不一樣, 貌似發現問題所在了. 所以 gdb 根據 vmlinux 中的地址插入斷點, 其實插入的位置並不是我們想要的, 這也就解釋了爲什麼斷點斷不到.
可以看到兩個地址剛好差了一個偏移 0x2f200000L.
這讓我們想到了什麼? 地址隨機化?
內核啓用 kaslr 這項特性之後, 內核啓動時會隨機化內核的各個 section 的虛擬地址(VA), 導致 gdb 斷點設置在錯誤的虛擬地址上, 內核執行時就不會觸發這些斷點。
新版本的 qemu 竟然已經支持了地址隨機化, 好事情.
#3 問題解決
##3.1 方法1 disable KASLR
最直接了當的方法(也是官方提供的方法) 是關閉地址隨機化,
- 重新編譯內核關閉地址隨機化
- 或者在cmdline 裏面直接添加
nokaslr
現在可以看到我們斷點成功了, 內核的符號也沒有偏移.
##3.2 方法2 重新加載內核鏡像
###3.2.1 重新加載內核 text 段
我們找到內核加載的起始地址, 這個一般是 _text
符號的地址.
# echo 0x$(cat /proc/kallsyms | egrep -e "T _text$" | awk '{print $1}')
# cat /proc/kallsyms | grep -E " _text$"
ffffffff9ee00000 T _text
# cat /proc/kallsyms | grep -E " schedule$"
ffffffff9f8a1800 T schedule
此時 KASLR 的偏移量爲 0xffffffff9ee00000 - 0xffffffff81000000 = 0x1de00000L
然後通過 add-symbol-file 將 vmlinux 的起始地址指定到 0xffffffff9ee00000 位置處.
add-symbol-file ./vmlinux 0xffffffff9ee00000
可以看到斷點斷到了實際位置.
但是由於開啓 KASLR 之後內核各個段的地址都是分別映射的, 這段我們只重新指定了內核代碼段的位置.
###3.2.2 進階用法
objdump -h ./vmlinux | grep -E " .text| .data "
0 .text 00e010f1 ffffffff81000000 0000000001000000 00200000 2**12
11 .data 0014c540 ffffffff82400000 0000000002400000 01600000 2**13
之前算出來 KASLR 的偏移是 0x1de00000L, .data 在 vmlinux 中的地址爲 0xffffffff82400000, 加上偏移後實際的加載地址就是 0xffffffffa0200000. 這個地址就是 init_stack 的地址
使用 add-symbol-file 指定其他段(比如 data) 的地址.
# add-symbol-file ./vmlinux 0xffffffff9ee00000 -s .data 0xffffffffa0200000
add symbol table from file "./vmlinux" at
.text_addr = 0xffffffff9ee00000
.data_addr = 0xffffffffa0200000
(y or n) y
Reading symbols from ./vmlinux...done.
但是如此讀取後, 再去讀取數據段的值, 依舊失敗, gdb 還是尋找錯誤的地址.
我們在 StackOverFlow 中找到了類似的例子, GDB can’t load kernel symbol correctly with KASLR enabled
這個感覺應該是 gdb 的問題, 記錄在此, 等待問題解決後, 更新
#4 參考資料
gdb-qemu-cant-put-break-point-on-kernel-function-kernel