今天在使用gdb
偵錯程式, 在函數A處打斷點, 在執行到斷點時, 單步偵錯進入這個函數, 但是與預期的結果不同, 進入函數B中了, 沒有進入到函數A中, 發截圖給組內的大佬, 讓仔細看 gdb 偵錯時輸出的資訊, 在查詢後發現 gdb 中有出現如圖中所出現的警告
新建test
目錄, 在test
目錄下新建三個檔案, main.c
, hello.c
, hello.h
內容如下
使用gcc hello.c main.c -o hello -g
編譯生成可執行程式 hello
接下來對hello.c
檔案內容做如下修改
1 #include "hello.h"
2 #include <stdio.h>
3
4 int Add(int x, int y)
5 {
6 return x + y;
7 }
8
9 void PrintHello()
10 {
11 printf("%s\n", "hello");
12 }
然後, 使用 gdb 偵錯 hello
在 main.c 第5行打斷點, b main.c:5
, r 啓動程式偵錯執行到該斷點, s
單步進入 PrintHello 函數, 這時我們會發現並沒有如我們預期的進入 PrintHello 函數內, 而是進入到 Add 函數內,
這是我們使用ll
檢視檔案的最後修改時間
由於對hello.c
檔案做了修改, 我們發現hello.c
的最後修改時間時是要比hello
的時間要新的。而我們沒有重新編譯生成程式, 導致 hello 可執行程式中的符號表儲存的資訊沒有更新, 從而出現問題。 對比可以發現, 原來hello.c
第6行對應的是 PrinHello 的定義, 而修改後hello.c
對應的 Add 的定義, 進而會導致 gdb 調試出現問題, 這模擬的比較簡單,只需要gcc
編譯, 在偵錯時就不會出現問題。
然乎經過一番谷歌翻譯, 意思是原始檔比可執行檔案要新, 那我們知道可執行程式中有要偵錯程式原始碼檔案的路徑, 如果我們修改了程式的原始檔, 沒有重新編譯程式, 直接偵錯就會導致程式符號表和原始檔的函數對應出現錯誤, 而 gdb 這時會有警告Source is more recent than executable
我當時根據工程專案結構猜測出, 猜測可能是我偵錯的函數所在的檔案和所在的目錄結構及目錄名字分析出, 程式依賴的某個動態庫有問題, 重新編譯該動態庫後,問題就解決了。
那這個問題已經解決了, 但這不是通過分析,是通過猜測和運氣解決的, 後面如果在遇到同樣的問題, 我們如何快速解決呢。
我當時想到的是, Linux中ldd
命令可以列舉出程式鏈接的所有動態庫, 但還是不知道出問題的函數在那個動態庫中, 那這時有人可能會說可以把程式依賴的所有動態庫更新, 這對於鏈接的動態庫比較少的程式還是可以接受的, 但是我\如果比較多的呢, 我偵錯的那個程式有鏈接10多個動態庫, 顯然這種方法對鏈接動態庫比較多的程式不可行的。
這時在組內大佬的提示下, 讓回想整個問題的解決過程, 提示 gdb 或許可以幫助快速解決這個問題。
問題: 快速定位 一個函數在偵錯程式鏈接的那個動態庫中
經過一番搜尋, 百度沒有我想要的答案, 這時我想或許看 gdb 的文件可以解決這個問題, 但是文件很長很多啊。在組內大佬的提示下, 用谷歌搜尋通過關鍵詞gdb function library
進行搜尋, 第一條搜尋結果就是想要的答案。這是 stackoverflow 上一個問題的回答
到這裏, 這個問題應該是有了一個比較好的解決方案
在gdb使用 info symbol
可以列印函數所在動態庫的名稱
使用方法:
使用gdb 啓動偵錯程式,在要查詢的函數處打斷點,然後執行到該斷點處,在單步偵錯進入該函數,在使用info symbol fun_name
就可以列印出函數的動態庫名稱
stackoverflow 作爲程式設計師專屬的問答網站, 很多問題都可以找到解決方案。 (英文還是要好好學呀)
gdb 中可以查到查到函數在偵錯程式依賴的那個動態庫及庫的路徑這個功能可以很多人都不知道, 一個經典強大的工具往往在很多方便都有完善的解決方案, 或許只是我們不知道。
gdb 作爲一款經典的BUG 偵錯工具, 自己對 gdb 的瞭解還是太少了, 還是要通過官方文件完整瞭解一下 gdb 的功能