咱們從一個最簡單的C語言程序開始。源文件main.c在 用戶目錄gdb文件夾下。ui
florian@florian-pc:~/gdb$ cat main.cspa
int main().net
{debug
return 0;調試
};orm
而後將源文件編譯爲main(須要調試選項-g),並將main.c移動到src/main.c下,而後對main進行調試。blog
florian@florian-pc:~/gdb$ gcc main.c -o main -g字符串
florian@florian-pc:~/gdb$ mv main.c src/main.cget
florian@florian-pc:~/gdb$ gdb main源碼
(gdb) b main
Breakpoint 1 at 0x8048397: file main.c, line 3.
(gdb) list
1 main.c: 沒有那個文件或目錄.
in main.c
在gdb中,使用list命令查看源代碼時,沒法找到源文件main.c。
探究
因爲對DWARF調試格式並不清晰,我本覺得使用調試選項編譯的可執行程序內部包含了源文件的內容,這樣不管源碼是否存在,可執行程序均可以被正常調試。可是,從上邊的例子中能夠看出,事實並不是如此。
咱們能夠做一個簡單的推測:因爲移動源文件的位置後,gdb沒法找到源文件的位置,估計可執行文件的調試段內保存的不是源文件的內容,而是路徑信息。
將main.c移動回來,從新編譯,生成目標文件main.o,並查看其段信息。
florian@florian-pc:~/gdb$ mv src/main.c main.c
florian@florian-pc:~/gdb$ gcc -c main.c -o main.o -g
florian@florian-pc:~/gdb$ objdump -s main.o
main.o: file format elf32-i386
Contents of section .text:
0000 5589e5b8 00000000 5dc3 U.......].
……
Contents of section .debug_str:
0000 6d61696e 2e63002f 686f6d65 2f666c6f main.c./home/flo
0010 7269616e 2f676462 00474e55 20432034 rian/gdb.GNU C 4
0020 2e342e35 006d6169 6e00 .4.5.main.
……
咱們發如今.debug_str段內,有兩個很明顯的字符串信息。
(1)/home/florian/gdb:看起來很像源文件所在的絕對路徑。
(2)main.c:顯而易見,是源文件的名稱。
驗證
咱們再把main.c移動到src目錄下,再次編譯,看看字符串的信息有何變化。
florian@florian-pc:~/gdb$ mv main.c src/main.c
florian@florian-pc:~/gdb$ gcc -c src/main.c -o main.o -g
florian@florian-pc:~/gdb$ objdump -s main.o
main.o: file format elf32-i386
Contents of section .text:
0000 5589e5b8 00000000 5dc3 U.......].
……
Contents of section .debug_str:
0000 2f686f6d 652f666c 6f726961 6e2f6764 /home/florian/gd
0010 6200474e 55204320 342e342e 35007372 b.GNU C 4.4.5.sr
0020 632f6d61 696e2e63 006d6169 6e00 c/main.c.main.
在.debug_str段內的兩個字符串信息發生了變化。
(1)/home/florian/gdb:可見該字符串爲執行gcc命令時的當前目錄的絕對路徑。
(2)src/main.c:該字符串爲源文件相對於當前目錄的相對路徑。
將兩個目錄合併,即可以獲得源文件的絕對路徑:
/home/florian/gdb /src/main.c
結論
因而可知,使用調試選項編譯生成的可執行文件內並不是保存了源文件的內容,而是源文件的絕對路徑信息。DWARF調試格式(詳見這裏)定義的其餘debug段內保存了二進制代碼與源文件行號的對應關係,這樣gdb的list命令工做時,實際是讀取可執行文件內的行號信息,並將源文件的代碼內容顯示出來。這也是爲何將源文件移動後,list命令信息沒法找到源文件的緣由。
從這裏,咱們也能夠清楚一個事實:當源碼目錄的源文件路徑發生變化後,若是須要對可執行文件進行調試,則必須從新編譯。
相似的問題不單單在gdb的list命令中存在,objdump –S命令用於交叉顯示反彙編代碼與源代碼,一樣會受移動源文件的影響。