2015年2月6日 星期五

[Embedded] GDB script -- check stack when stack corrupt

若使用 gdb 檢查 core dump 時,發現 stack corrupt,此時該如何看到正確的 stack trace 呢?

問題情況舉例如下:(使用 stackoverflow 的例子做說明)

Program received signal SIGSEGV, Segmentation fault.
0x00000002 in ?? ()
(gdb) bt
#0  0x00000002 in ?? ()
#1  0x00000001 in ?? ()
#2  0xbffff284 in ?? ()
Backtrace stopped: previous frame inner to this frame (corrupt stack?)

問題解決方式:(假設系統架構為 Linux 32-bit x86)

1. 首先必須先大概了解 Linux 作業系統內的虛擬記憶體配置
例如:
stack 會位於 0xBFxxxxxx 附近
text (程式碼) 會從 0x08048000 開始增加
因此 0xbffff284可能是正確的 stack位址,而 0x00000001 與 0x00000002 並非正確的 stack 位址,此時 stack 已經亂掉。
若對於 stack 使用有疑問,可以直接參考此範例。 

2. 以上述的錯誤位址為例,這個問題有可能因為呼叫function 時,指向了錯誤的 function pointer。
An indirect call instruction just pushes the PC after the call onto the stack and then sets the PC to the target value (bogus in this case), so if this is what happened, you can easily undo it by manually popping the PC off the stack. In 32-bit x86 code you just do:

(gdb) set $pc = *(void **)$sp
(gdb) set $sp = $sp + 4

3. 另外一個可能是正確的 stack 被覆蓋了。此時基本處理原則是將整個 stack 都尋找一遍,直到找到對應的程式進入點為止 (例如:main())。
例如: (gdb) x/256wa $sp 
其基本原理為從目前的 sp register 開始,每隔一個 word(4bytes),印出對應的位址,若有對應的 symbol 會一併列印,持續尋找 256 words。

4. 若想要將 stack 狀態寫入檔案,可以參考 Loda's blog 的 GDB 筆記,其基本原理與 x/256wa $sp 相同,不過產生出的 log 較方便閱讀。摘錄如下:
file executable_file_build_with_debug_flag
set solib-search-path builtin-symbol-library-file-path
set solib-absolute-prefix builtin-symbol-library-filepath-prefix
core core_dump_file
set print symbol-filename on
set logging file stack-out.txt
set logging on
set $stack_addr=$sp
printf "Start to decode tack\n"
set $i=0
while($i<256 br=""> printf "i:%ld stack_addr:%xh\n",$i,$stack_addr
print /a *(int *) $stack_addr
set $stack_addr = $stack_addr +4
set $i = $i + 1
end
printf "End\n"
set logging off

Reference: