gdb調試單獨的debug文件
2023.11.26
背景
Debug和Release區別
實際上,Debug 和 Release 並沒有本質的界限,人為的區別,沒有特殊的規定。他們只是一組編譯選項的集合,編譯器只是按照預定的選項行動。事實上,我們甚至可以修改這些選項,從而得到優化過的調試版本或是帶跟蹤語句的發佈版本。約定俗成的區別是:
Debug通常稱為調試版本,它包含調試信息,並且不作任何優化,便於程序員調試程序。
Release 稱為發佈版本,它往往是進行了各種優化,使得程序在代碼大小和運行速度上都是最優的,以便用户很好地使用。約定俗成的是release版本不帶調試信息。
不同的軟件有自己不同的選擇。有些軟件會在release版本和debug版本之外,根據自己的需要,出別的版本,如QT分為debug、release、profile版本:
Debug通常稱為調試版本,它包含調試信息,並且不作任何優化,便於程序員調試程序。
Release 稱為發佈版本,它往往是進行了各種優化,使得程序在代碼大小和運行速度上都是最優的,以便用户很好地使用。
profile則是在這兩種之中取一個平衡,兼顧性能和調試, 可以類似的看做是性能更優但是又方便調試的版本。
沒有對錯,也沒有是否最優,看自己的目的,根據自己的目的進行取捨,平衡利弊後,使用一個最優的做法。比如有些軟件可能為了在發佈後調試方便,在添加調試信息的同時,也會添加優化選項進行優化,從而求得一個平衡。有些軟件考慮發佈時攜帶調試信息會讓發佈包變得非常大,不利於網絡分發,就會在發佈時去掉調試信息。
發佈了Release版本後,出問題時定位問題的常用做法
Release版本確實比debug版本在運行速度及體積大小上有一定的優勢,不過如果發佈後的程序出現問題,對於定位問題的人員來説,就比較痛苦了,畢竟沒有調試信息很不方便。為了解決這個問題,linux下的程序一般會有獨立的debuginfo包,這個debuginfo包是和release包一一對應的,當release版本的程序出現問題時,將debuginfo包安裝後,即可像debug版本一樣進行調試。如ls命令,在gdb的時候,如果沒有調試信息,會提示進行安裝,我的操作系統是fedora,有如下提示:
[zy@fedora debuginfo]$ gdb /usr/bin/ls
GNU gdb (Fedora Linux) 13.2-10.fc39
Reading symbols from /usr/bin/ls...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Reading symbols from .gnu_debugdata for /usr/bin/ls...
(No debugging symbols found in .gnu_debugdata for /usr/bin/ls)
Missing separate debuginfos, use: dnf debuginfo-install coreutils-9.3-4.fc39.x86_64
(gdb) quit
可以看到,gdb提示使用dnf debuginfo-install coreutils-9.3-4.fc39.x86_64命令安裝debuginfo包。安裝完後再gdb,就可以看到已經讀取到符號信息了,如下所示:
[root@fedora debug]# dnf debuginfo-install coreutils
[zy@fedora debuginfo]$ gdb --args /usr/bin/ls .
GNU gdb (Fedora Linux) 13.2-10.fc39
Reading symbols from /usr/bin/ls...
Reading symbols from /usr/lib/debug/usr/bin/ls-9.3-4.fc39.x86_64.debug...[zy@fedora debuginfo]$ gdb --args /usr/bin/ls .
GNU gdb (Fedora Linux) 13.2-10.fc39
如何製作單獨的debuginfo文件
linux下可以通過objcopy命令分離出單獨的debuginfo包,比如一個a.out程序,可以通過如下命令得到deuginfo包
objcopy --only-keep-debug ./a.out a.out.debug
objcopy --strip-debug ./a.out
objcopy --add-gnu-debuglink=a.out.debug ./a.out
objcopy的--only-keep-debug選項,將debug信息分離到一個單獨的文件中。--strip-debug選項將debug信息從a.out中刪除。--add-gnu-debuglink選項將a.out和前面生成的a.out.debug文件關聯起來,這一步很重要,如果沒有這一步,則在gdb調試的時候會找不到調試信息。
這只是其中一種方法,還有一種方法是在編譯時生成build-id,將build-id信息放到編譯後的文件中。本文不介紹此種方式。
gdb調試單獨debuginfo文件的原理介紹
先看一個小例子,使用的gcc版本及gdb版本如下:
[zy@fedora debuginfo]$ gcc --version
gcc (GCC) 13.2.1 20231011 (Red Hat 13.2.1-4)
Copyright © 2023 Free Software Foundation, Inc.
[zy@fedora debuginfo]$ gdb --version
GNU gdb (Fedora Linux) 13.2-10.fc39
Copyright (C) 2023 Free Software Foundation, Inc.
以下是代碼結構及具體的代碼內容:
[zy@fedora gdb_test]$ ls
liba.c liba.h main.c
[zy@fedora gdb_test]$ cat liba.c
#include <stdio.h>
#include <stdlib.h>
int liba_func(int a, int b)
{
int sum = 0;
sum = a + b;
printf("%d + %d = %d.\n", a, b, sum);
return 0;
}
[zy@fedora gdb_test]$ cat liba.h
#ifndef __LIBA_FUNC__
#define __LIBA_FUNC__
int liba_func(int a, int b);
#endif
[zy@fedora gdb_test]$ cat main.c
#include <stdio.h>
#include <stdlib.h>
#include "liba.h"
int main()
{
printf("hello, this is main.\n");
liba_func(1, 2);
return 0;
}
[zy@fedora gdb_test]$export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:.
如何添加調試信息到目標文件
當不加任何編譯參數,直接gdb運行的時候,發現設置斷點後,看不到調試信息,如下所示:
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -o liba.so
[zy@fedora gdb_test]$ gcc main.c -L. -I. -la -o test
[zy@fedora gdb_test]$ gdb ./test
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./test)
(gdb) b main
Breakpoint 1 at 0x40113a
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Breakpoint 1, 0x000000000040113a in main ()
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function main,
which has no line number information.
main_sum = 90.
Breakpoint 2, 0x00007ffff7fbd10d in liba_func () from liba.so
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in main ()
(gdb) n
Single stepping until exit from function main,
which has no line number information.
而加了-g選項後,再用gdb調試的時候,可以看到具體的調試信息,如下所示:
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ gcc main.c -L. -I. -la -g -o test
[zy@fedora gdb_test]$ gdb ./test
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
(gdb) b main
Breakpoint 1 at 0x40113e: file main.c, line 7.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Breakpoint 1, main () at main.c:7
7 int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 int main_b = 5;
(gdb) c
Continuing.
main_sum = 90.
Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11 a = a * a;
和看不到調試信息的編譯選項相比,多加了-g編譯選項,查看編譯後的文件,發現多了很多debug相關的segment,如下所示:
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -o liba.so
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
[26] .debug_aranges PROGBITS 0000000000000000 00003160
[27] .debug_info PROGBITS 0000000000000000 00003190
[28] .debug_abbrev PROGBITS 0000000000000000 00003273
[29] .debug_line PROGBITS 0000000000000000 0000330f
[30] .debug_str PROGBITS 0000000000000000 0000337a
[31] .debug_line_str PROGBITS 0000000000000000 0000342c
是否包含調試信息,關鍵在於編譯時是否添加-g選項。添加了-g選項後,編譯出來的文件中帶有很多debug相關的段。可以將這些debug信息進行清除,如下所示:
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ file liba.so
liba.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=cc2b549427fb52d590a07ad72efec99085b9a877, with debug_info, not stripped
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
[26] .debug_aranges PROGBITS 0000000000000000 00003160
[27] .debug_info PROGBITS 0000000000000000 00003190
[28] .debug_abbrev PROGBITS 0000000000000000 00003273
[29] .debug_line PROGBITS 0000000000000000 0000330f
[30] .debug_str PROGBITS 0000000000000000 0000337a
[31] .debug_line_str PROGBITS 0000000000000000 0000342c
[zy@fedora gdb_test]$ readelf -S liba.so |grep sym
[ 4] .dynsym DYNSYM 0000000000000328 00000328
[32] .symtab SYMTAB 0000000000000000 00003490
[zy@fedora gdb_test]$ strip -s liba.so
[zy@fedora gdb_test]$ file liba.so
liba.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=cc2b549427fb52d590a07ad72efec99085b9a877, stripped
[zy@fedora gdb_test]$ readelf -S liba.so |grep debug
[zy@fedora gdb_test]$ readelf -S liba.so |grep sym
[ 4] .dynsym DYNSYM 0000000000000328 00000328
清除後,在編譯後的文件中就看不到debug相關的段了。文件大小也比之前有所減少,如下所示:
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -o liba.so
[zy@fedora gdb_test]$ ll liba.so
-rwxr-xr-x. 1 zy zy 15800 11月15日 22:35 liba.so
[zy@fedora gdb_test]$ gcc liba.c -fpic -shared -g -o liba.so
[zy@fedora gdb_test]$ ll liba.so
-rwxr-xr-x. 1 zy zy 17080 11月15日 22:35 liba.so
[zy@fedora gdb_test]$ strip -s liba.so
[zy@fedora gdb_test]$ ll liba.so
-rwxr-xr-x. 1 zy zy 14392 11月15日 22:35 liba.so
使用objcopy命令分離出單獨的debuginfo文件
前面已經介紹過,使用objcopy命令可以將debug信息分離到單獨的文件中,如下所示:
[zy@fedora gdb_test]$ gcc main.c -L. -I. -la -g -o test
[zy@fedora gdb_test]$ ls
liba.c liba.h liba.so main.c test
[zy@fedora gdb_test]$ objcopy --only-keep-debug ./liba.so liba.so.debug
[zy@fedora gdb_test]$ objcopy --strip-all liba.so
liba.so liba.so.debug
[zy@fedora gdb_test]$ objcopy --strip-all liba.so
[zy@fedora gdb_test]$ ls
liba.c liba.h liba.so liba.so.debug main.c test
如果不使用objcopy --add-gnu-debuglink=liba.so.debug liba.so命令將分離出的debuginfo文件和原始程序關聯起來,在使用gdb調試的時候,發現無法看到調試信息,如下所示:
[zy@fedora gdb_test]$ gdb ./test
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
(No debugging symbols found in ./test)
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 2, 0x00007ffff7fbd10d in liba_func () from liba.so
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in ?? ()
(gdb) n
Cannot find bounds of current function
使用了objcopy --add-gnu-debuglink命令之後,可以看到具體的調試信息了,如下所示:
[zy@fedora gdb_test]$ objcopy --add-gnu-debuglink=liba.so.debug liba.so
[zy@fedora gdb_test]$ objcopy --add-gnu-debuglink=test.debug test
[zy@fedora gdb_test]$ gdb ./test
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/test.debug...
(gdb) b main
Breakpoint 1 at 0x40113e: file main.c, line 7.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Using host libthread_db library "/lib64/libthread_db.so.1".
Breakpoint 1, main () at main.c:7
7 int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 int main_b = 5;
(gdb) n
9 int main_sum = 0;
(gdb) n
11 main_a = main_a * main_a;
(gdb) c
Continuing.
main_sum = 90.
Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11 a = a * a;
(gdb) n
12 b = b * 2;
(gdb)
為什麼一定要使用--add-gnu-debuglink選項後才能看到調試信息呢?因為分離出單獨的debuginfo文件後,文件名稱是可以隨意起的,gdb在調試程序的時候,怎麼知道去哪個文件中找調試信息呢?這就需要在原來的程序中添加一些線索才行,這個線索就是調試文件的名稱。其實現原理是在程序中有一個專門的段,叫做.gnu_debuglink段,用以記錄debuginfo文件相關的信息,主要是文件名稱和該文件的CRC校驗碼。如下所示:
[zy@fedora gdb_test]$ readelf -S liba.so|grep debug
[26] .gnu_debuglink PROGBITS 0000000000000000 00003160
[zy@fedora gdb_test]$ objdump -s -j .gnu_debuglink liba.so
liba.so: 文件格式 elf64-x86-64
Contents of section .gnu_debuglink:
0000 6c696261 2e736f2e 64656275 67000000 liba.so.debug...
0010 a1091f2f .../
[zy@fedora gdb_test]$ readelf -S test|grep debug
[28] .gnu_debuglink PROGBITS 0000000000000000 00004768
[zy@fedora gdb_test]$ objdump -s -j .gnu_debuglink test
test: 文件格式 elf64-x86-64
Contents of section .gnu_debuglink:
0000 74657374 2e646562 75670000 f2fa32cc test.debug....2.
上面的objcopy --add-gnu-debuglink=liba.so.debug liba.so命令就是將指定的調試文件的名稱(liba.so.debug)及其CRC校驗值寫入到liba.so文件中。但並未告訴gdb去哪個目錄下找這個文件,gdb會去哪些路徑下找這個文件呢?在網上找到如下一段話:
So, for example, suppose you ask GDB to debug /usr/bin/ls, which has a debug link that specifies the file ls.debug, and a build ID whose value in hex is abcdef1234. If the list of the global debug directories includes /usr/lib/debug, then GDB will look for the following debug information files, in the indicated order:
- /usr/lib/debug/.build-id/ab/cdef1234.debug
- /usr/bin/ls.debug
- /usr/bin/.debug/ls.debug
- /usr/lib/debug/usr/bin/ls.debug.
Global debugging info directories default to what is set by GDB configure option --with-separate-debug-dir and augmented by the colon-separated list of directories provided via GDB configure option --additional-debug-dirs. During GDB run you can also set the global debugging info directories, and view the list GDB is currently using.
set debug-file-directory directories
Set the directories which GDB searches for separate debugging information files to directory. Multiple path components can be set concatenating them by a path separator.
show debug-file-directory
Show the directories GDB searches for separate debugging information files.
這段話以ls命令做了一個舉例。第一種build-id的方式不在本文闡述範圍之內,是另一種尋找debuginfo文件的方法,本文不再描述。對於剩餘的三個目錄,有兩個比較好理解,就是ls命令所在的目錄、ls所在目錄下的.debug目錄去尋找對應的文件。第三個比較難理解,第三個是到/usr/lib/debug/目錄下,然後根據ls目錄對應的完整路徑,到這個目錄下去尋找。就是用/usr/lib/debug目錄加上ls文件的實際路徑進行拼接後的目錄去尋找。比如一個可執行文件所在的目錄是/home/test/example,那麼gdb就回去/usr/lib/debug/home/test/下去尋找example.debug文件。再比如一個so庫所在的目錄是/home/libs/libxxx.so,那麼gdb會去/usr/lib/debug/home/libs/目錄下去尋找libxxx.so.debug文件。其它的也類似。就是將/usr/lib/debug目錄當作根目錄,然後在這個根目錄下,去可執行文件所在的目錄下去尋找對應的調試文件。因為ls命令在/usr/bin目錄下,所以gdb才會去/usr/lib/debug/usr/bin目錄下去尋找ls.debug文件。先做一個簡單的實驗,如下所示:
[zy@fedora debuginfo]$ ls
fff test test.debug
[zy@fedora debuginfo]$ gdb ./test
GNU gdb (Fedora Linux) 13.2-10.fc39
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/test.debug...
[zy@fedora debuginfo]$ ls -a
. .. .debug fff test
[zy@fedora debuginfo]$ ls .debug/
test.debug
[zy@fedora debuginfo]$ gdb ./test
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/.debug/test.debug...
[zy@fedora debuginfo]$ pwd
/home/zy/debuginfo
[zy@fedora debuginfo]$ ls /usr/lib/debug/home/zy/debuginfo/test.debug
/usr/lib/debug/home/zy/debuginfo/test.debug
[zy@fedora debuginfo]$ gdb ./test
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /usr/lib/debug//home/zy/debuginfo/test.debug...
通過上面的例子中的Reading sysbols from ......可以看到,能在對應的目錄下讀取到調試符號的信息。
對於依賴的so庫,也是一樣的道理,也會按照以上的路徑去尋找so庫所在的符號文件。如下所示:
[zy@fedora debuginfo]$ tree .
.
├── liba.so.debug
├── libs
│ ├── liba.so
│ └── liba.so.debug
├── test
└── test.debug
[zy@fedora debuginfo]$ gdb ./test
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
Reading symbols from /home/zy/debuginfo/test.debug...
(gdb) b main
Breakpoint 1 at 0x40113e: file main.c, line 7.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Breakpoint 1, main () at main.c:7
7 int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 int main_b = 5;
(gdb) n
9 int main_sum = 0;
(gdb) c
Continuing.
main_sum = 90.
Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb)
gdb也可以支持不從默認的/usr/lib/debug目錄中尋找,可以單獨指定存放debuginfo文件的根目錄。通過在gdb中的show debug-file-directory命令查看當前搜索的根目錄。通過set debug-file-directory命令可以修改搜索的根目錄。也支持設置多個根目錄,如果有多個根目錄,每個目錄之間用路徑分隔符“:“分開。如set debug-file-directory /home/test,將根目錄設置為/home/test。set debug-file-directory /home/test:/root/test,將根目錄設置為/home/test和/root/test。如下所示:
[zy@fedora debuginfo]$ gdb ./test
GNU gdb (Fedora Linux) 13.2-10.fc39
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) show debug-file-directory
The directory where separate debug symbols are searched for is "/usr/lib/debug".
(gdb) set debug-file-directory /usr/lib/debug/:/home/zy/debuginfo/debug_infos/
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11 a = a * a;
(gdb) n
12 b = b * 2;
如上所示,liba_func可以正常斷住,可以看到調試信息。但test文件中的main函數,依然無法讀取到調試信息,斷點也並沒有停住。這是什麼原因呢?原來gdb程序在使用的時候,會主動從那幾個位置讀取被gdb的程序的調試信息,如果能讀取到,就可以看到,如果讀取不到,就認為讀取失敗。而執行set-debug-file-directory命令後,並不會再次去讀取已經認為失敗的可執行文件的調試信息,所以test的調試信息依然讀取不到。而在執行set-debug-file-directory命令後才去加載的可執行文件,就能正確讀取到調試信息。此種情況下,需要手動使用addd-symbol-file讀取那些讀取不到的可執行文件的調試信息。如下所示:
[zy@fedora debuginfo]$ gdb ./test
For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from ./test...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) set debug-file-directory /home/zy/debuginfo/debug_infos/
(gdb) add-symbol-file /home/zy/debuginfo/debug_infos/test.debug
add symbol table from file "/home/zy/debuginfo/debug_infos/test.debug"
(y or n) y
Reading symbols from /home/zy/debuginfo/debug_infos/test.debug...
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) b main
Breakpoint 2 at 0x40113e: file main.c, line 7.
(gdb) run
Starting program: /home/zy/debuginfo/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Breakpoint 2, main () at main.c:7
7 int main_a = 3;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 int main_b = 5;
(gdb) n
9 int main_sum = 0;
(gdb) c
Continuing.
main_sum = 90.
Breakpoint 1, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb)
GDB讀取動態庫的debuginfo文件時需要注意的地方
如前所述,如果將debuginfo文件直接放到/usr/lib/debug目錄(或者通過set-debug-file-directory命令設置的根目錄)下,也是可以的,但文件目錄的組織結構必須和運行時的目錄組織結構一樣,這個地方有一個需要注意的地方,特別是對於so文件,在debug-root-directory目錄下尋找對應的debuginfo文件時,和文件實際所在的位置不一定完全一致,而要看連接器能夠找到的路徑,即ldd所顯示的路徑。gdb是會到debug-root-directory/$LDD顯示的目錄下去尋找對應的debuginfo文件。而ldd顯示出來的路徑,可能是一個絕對路徑,也可能是一個相對路徑,者取決於你的操作系統中所設置的一些so查找路徑,比如LD_LIBRARY_PATH設置為一個相對路徑還是一個絕對路徑,對於gdb來説,去debug-root-directory目錄下尋找的路徑也不一樣。gdb會拿ldd顯示出來的路徑直接和debug-root-directory拼接一下。比如我當前的LD_LIBRARY_PATH設置為.fff,那麼gdb就會去/usr/lib/debug/./fff目錄下去讀取liba.so所對應的debuginfo文件,因為ldd現實出來的liba.so的路徑是./fff/liba.so。如下所示:
[zy@fedora debuginfo]$ echo $LD_LIBRARY_PATH
./fff
[zy@fedora debuginfo]$ tree
.
├── fff
│ └── liba.so
└── test
2 directories, 2 files
[zy@fedora debuginfo]$ ldd test
linux-vdso.so.1 (0x00007ffda31b6000)
liba.so => ./fff/liba.so (0x00007f0c0a237000)
libc.so.6 => /lib64/libc.so.6 (0x00007f0c0a03e000)
/lib64/ld-linux-x86-64.so.2 (0x00007f0c0a23e000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/fff/liba.so.debug
/usr/lib/debug/fff/liba.so.debug
[zy@fedora debuginfo]$ gdb ./test
Reading symbols from ./test...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b main
Function "main" not defined.
Make breakpoint pending on future shared library load? (y or [n]) y
Breakpoint 1 (main) pending.
(gdb) b liba_func
Breakpoint 2 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 2, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb) n
1 + 2 = 3.
11 a = a * a;
(gdb) n
12 b = b * 2;
(gdb)
如果我將LD_LIBRARY_PATH設置為該so所在的實際絕對路徑/home/zy/debuginfo/fff/,那麼gdb就會去/usr/lib/debug/home/zy/debuginfo/fff目錄下去讀取liba.so所對應的debuginfo文件,因為ldd顯示出來的liba.so的路徑是/home/zy/debuginfo/fff/liba.so。如下所示,debuginfo文件在/usr/lib/debug/fff/liba.so.debug時,無法讀取到調試信息:
[zy@fedora debuginfo]$ export LD_LIBRARY_PATH=/home/zy/debuginfo/fff/
[zy@fedora debuginfo]$ echo $LD_LIBRARY_PATH
/home/zy/debuginfo/fff/
[zy@fedora debuginfo]$ tree
.
├── fff
│ └── liba.so
└── test
2 directories, 2 files
[zy@fedora debuginfo]$ ldd test
linux-vdso.so.1 (0x00007ffc263bf000)
liba.so => /home/zy/debuginfo/fff/liba.so (0x00007f98d6ead000)
libc.so.6 => /lib64/libc.so.6 (0x00007f98d6cb4000)
/lib64/ld-linux-x86-64.so.2 (0x00007f98d6eb4000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/fff/liba.so.debug
/usr/lib/debug/fff/liba.so.debug
[zy@fedora debuginfo]$ gdb ./test
Reading symbols from ./test...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
Missing separate debuginfo for /home/zy/debuginfo/fff/liba.so.
The debuginfo package for this file is probably broken.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 1, 0x00007ffff7fbd10d in liba_func () from /home/zy/debuginfo/fff/liba.so
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in ?? ()
(gdb) n
Cannot find bounds of current function
而將調試文件移動到/usr/lib/debug/home/zy/debuginfo/fff/目錄後,即可讀取到正確的調試信息,如下所示:
[zy@fedora debuginfo]$ echo $LD_LIBRARY_PATH
/home/zy/debuginfo/fff/
[zy@fedora debuginfo]$ ldd test
linux-vdso.so.1 (0x00007fff84fdf000)
liba.so => /home/zy/debuginfo/fff/liba.so (0x00007f4e94e1d000)
libc.so.6 => /lib64/libc.so.6 (0x00007f4e94c24000)
/lib64/ld-linux-x86-64.so.2 (0x00007f4e94e24000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/home/zy/debuginfo/fff/
liba.so.debug
[zy@fedora debuginfo]$ gdb ./test
Reading symbols from ./test...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 1, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
(gdb)
如果沒有使用環境變量,而是程序中設置了rpath目錄去讀取動態庫呢?其實是和環境變量時一樣,都是根據ldd顯示的路徑去確定的。將liba.so.debug文件放到/usr/lib/debug/fff/目錄下,通過設置rpath為$ORIGIN/fff,發現找不到調試信息,如下所示:
[zy@fedora debuginfo]$ patchelf --print-rpath test
$ORIGIN/fff
[zy@fedora debuginfo]$ ldd test
linux-vdso.so.1 (0x00007ffef6ffc000)
liba.so => /home/zy/debuginfo/./fff/liba.so (0x00007f1ea3ac9000)
libc.so.6 => /lib64/libc.so.6 (0x00007f1ea38d0000)
/lib64/ld-linux-x86-64.so.2 (0x00007f1ea3ad0000)
[zy@fedora debuginfo]$ ls -l /usr/lib/debug/fff/liba.so.debug
-rwxr-xr-x. 1 root root 5544 11月21日 22:19 /usr/lib/debug/fff/liba.so.debug
[zy@fedora debuginfo]$ gdb ./test
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
Missing separate debuginfo for /home/zy/debuginfo/fff/liba.so.
The debuginfo package for this file is probably broken.
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 1, 0x00007ffff7fbd10d in liba_func () from /home/zy/debuginfo/fff/liba.so
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
Single stepping until exit from function liba_func,
which has no line number information.
1 + 2 = 3.
sum = 4.
0x000000000040118c in ?? ()
(gdb) n
Cannot find bounds of current function
因為ldd顯示的liba.so的目錄是/home/zy/debuginfo/./fff/liba.so,gdb在和debug-file-directory目錄拼接後的路徑是/usr/lib/debug/home/zy/debuginfo/./fff/,所以會去/usr/lib/debug/home/zy/debuginfo/./fff目錄下尋找對應的debuginfo文件,如下所示:
zy@fedora debuginfo]$ patchelf --print-rpath test
$ORIGIN/fff
[zy@fedora debuginfo]$ ldd test
linux-vdso.so.1 (0x00007fff495d5000)
liba.so => /home/zy/debuginfo/./fff/liba.so (0x00007fbf93e95000)
libc.so.6 => /lib64/libc.so.6 (0x00007fbf93c9c000)
/lib64/ld-linux-x86-64.so.2 (0x00007fbf93e9c000)
[zy@fedora debuginfo]$ ls /usr/lib/debug/home/zy/debuginfo/fff/
liba.so.debug
[zy@fedora debuginfo]$ gdb ./test
Reading symbols from ./test...
This GDB supports auto-downloading debuginfo from the following URLs:
<https://debuginfod.fedoraproject.org/>
Enable debuginfod for this session? (y or [n]) n
Debuginfod has been disabled.
To make this setting permanent, add 'set debuginfod enabled off' to .gdbinit.
Missing separate debuginfo for /home/zy/debuginfo/test.
The debuginfo package for this file is probably broken.
(No debugging symbols found in ./test)
(gdb) b liba_func
Breakpoint 1 at 0x401030
(gdb) run
Starting program: /home/zy/debuginfo/test
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
main_sum = 90.
Breakpoint 1, liba_func (a=1, b=2) at liba.c:6
6 int sum = 0;
Missing separate debuginfos, use: dnf debuginfo-install glibc-2.38-10.fc39.x86_64
(gdb) n
8 sum = a + b;
(gdb) n
9 printf("%d + %d = %d.\n", a, b, sum);
可以看到能正確的找到調試信息了。
綜上總結,gdb尋找單獨的debug文件的路徑是:
- 當前文件所在的目錄。
- 當前文件所在目錄下的.debug目錄。
- debug-file-directory設置的目錄下,根據文件所在的路徑去尋找。如果是動態庫文件,則會使用ldd所顯示的目錄。可以直接使用debug-file-directory目錄+ldd顯示目錄進行拼接即可。
參考文檔
https://stackoverflow.com/questions/36070648/search-symbols-again-after-setting-debug-file-directory
https://sourceware.org/gdb/current/onlinedocs/gdb.html/Separa...
深入理解debuginfo(轉載) - 簡書