rpmパッケージとかは通常のパッケージとデバッグ用のパッケージを分けて、debugしたいときはデバッグ情報付きのパッケージをインストールしますよね。あれがどんな感じで動いているのか確認したのでメモです。
使っている機能としては、gdbのデバッグ情報の分割機能とdebuglink機能だと思います。debuglinkはデバッグ情報のないバイナリファイルにデバッグ情報のあるバイナリファイルの情報を書き込むことでgdbがそのファイルを参照してくれるようになります。
やることとしては、以下の2点というところです。
では、実際に試してみます。まずは適当なc言語で書いたコードを-g付きでビルドします。そして、デバッグ情報付きのバイナリを以下の2ファイルに分けます。
ここでは、以下のようにtestという名称でバイナリを作ります。
masami@saga:~/tmp$ gcc -g test.c -o test
そして、このtestバイナリをobjcopyを使ってデバッグ情報を抜き出したファイルを作ります。
masami@saga:~/tmp$ objcopy --only-keep-debug test test.debug
これで、testに含まれるデバッグ情報をtest.debugファイルに書き出しました。この時点ではtestのほうは変更はありません。 次にtestからデバッグ情報を削除します。
masami@saga:~/tmp$ strip -g test
これでtestからデバッグ情報が消えたのでgdbでちょっと確認してみると、当然デバッグシンボルは無いと言われます。
masami@saga:~/tmp$ gdb ./test Reading symbols from ./test...(no debugging symbols found)...done. (gdb) quit
この時点でのtestバイナリには31個のセクションヘッダーがあります。
masami@saga:~/tmp$ readelf -S test There are 31 section headers, starting at offset 0x1290: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400200 00000200 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 000000000040021c 0000021c 0000000000000020 0000000000000000 A 0 0 4 [ 3] .note.gnu.build-i NOTE 000000000040023c 0000023c 0000000000000024 0000000000000000 A 0 0 4 [ 4] .gnu.hash GNU_HASH 0000000000400260 00000260 000000000000001c 0000000000000000 A 5 0 8 [ 5] .dynsym DYNSYM 0000000000400280 00000280 0000000000000060 0000000000000018 A 6 1 8 [ 6] .dynstr STRTAB 00000000004002e0 000002e0 000000000000003d 0000000000000000 A 0 0 1 [ 7] .gnu.version VERSYM 000000000040031e 0000031e 0000000000000008 0000000000000002 A 5 0 2 [ 8] .gnu.version_r VERNEED 0000000000400328 00000328 0000000000000020 0000000000000000 A 6 1 8 [ 9] .rela.dyn RELA 0000000000400348 00000348 0000000000000018 0000000000000018 A 5 0 8 [10] .rela.plt RELA 0000000000400360 00000360 0000000000000030 0000000000000018 AI 5 24 8 [11] .init PROGBITS 0000000000400390 00000390 000000000000001a 0000000000000000 AX 0 0 4 [12] .plt PROGBITS 00000000004003b0 000003b0 0000000000000030 0000000000000010 AX 0 0 16 [13] .plt.got PROGBITS 00000000004003e0 000003e0 0000000000000008 0000000000000000 AX 0 0 8 [14] .text PROGBITS 00000000004003f0 000003f0 00000000000001c2 0000000000000000 AX 0 0 16 [15] .fini PROGBITS 00000000004005b4 000005b4 0000000000000009 0000000000000000 AX 0 0 4 [16] .rodata PROGBITS 00000000004005c0 000005c0 0000000000000004 0000000000000004 AM 0 0 4 [17] .eh_frame_hdr PROGBITS 00000000004005c4 000005c4 000000000000003c 0000000000000000 A 0 0 4 [18] .eh_frame PROGBITS 0000000000400600 00000600 0000000000000114 0000000000000000 A 0 0 8 [19] .init_array INIT_ARRAY 0000000000600718 00000718 0000000000000008 0000000000000000 WA 0 0 8 [20] .fini_array FINI_ARRAY 0000000000600720 00000720 0000000000000008 0000000000000000 WA 0 0 8 [21] .jcr PROGBITS 0000000000600728 00000728 0000000000000008 0000000000000000 WA 0 0 8 [22] .dynamic DYNAMIC 0000000000600730 00000730 00000000000001d0 0000000000000010 WA 6 0 8 [23] .got PROGBITS 0000000000600900 00000900 0000000000000008 0000000000000008 WA 0 0 8 [24] .got.plt PROGBITS 0000000000600908 00000908 0000000000000028 0000000000000008 WA 0 0 8 [25] .data PROGBITS 0000000000600930 00000930 0000000000000010 0000000000000000 WA 0 0 8 [26] .bss NOBITS 0000000000600940 00000940 0000000000000008 0000000000000000 WA 0 0 1 [27] .comment PROGBITS 0000000000000000 00000940 0000000000000034 0000000000000001 MS 0 0 1 [28] .shstrtab STRTAB 0000000000000000 00001184 000000000000010c 0000000000000000 0 0 1 [29] .symtab SYMTAB 0000000000000000 00000978 0000000000000600 0000000000000018 30 44 8 [30] .strtab STRTAB 0000000000000000 00000f78 000000000000020c 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
次に、objcopyを使って、debuglinkを埋め込みます。
masami@saga:~/tmp$ objcopy --add-gnu-debuglink=test.debug test
この結果、index 28のセクションヘッダに.gnu_debuglinkというのが追加されていて、合計32個のセクションヘッダになります。
asami@saga:~/tmp$ readelf -S test There are 32 section headers, starting at offset 0x12c8: Section Headers: [Nr] Name Type Address Offset Size EntSize Flags Link Info Align [ 0] NULL 0000000000000000 00000000 0000000000000000 0000000000000000 0 0 0 [ 1] .interp PROGBITS 0000000000400200 00000200 000000000000001c 0000000000000000 A 0 0 1 [ 2] .note.ABI-tag NOTE 000000000040021c 0000021c 0000000000000020 0000000000000000 A 0 0 4 [ 3] .note.gnu.build-i NOTE 000000000040023c 0000023c 0000000000000024 0000000000000000 A 0 0 4 [ 4] .gnu.hash GNU_HASH 0000000000400260 00000260 000000000000001c 0000000000000000 A 5 0 8 [ 5] .dynsym DYNSYM 0000000000400280 00000280 0000000000000060 0000000000000018 A 6 1 8 [ 6] .dynstr STRTAB 00000000004002e0 000002e0 000000000000003d 0000000000000000 A 0 0 1 [ 7] .gnu.version VERSYM 000000000040031e 0000031e 0000000000000008 0000000000000002 A 5 0 2 [ 8] .gnu.version_r VERNEED 0000000000400328 00000328 0000000000000020 0000000000000000 A 6 1 8 [ 9] .rela.dyn RELA 0000000000400348 00000348 0000000000000018 0000000000000018 A 5 0 8 [10] .rela.plt RELA 0000000000400360 00000360 0000000000000030 0000000000000018 AI 5 24 8 [11] .init PROGBITS 0000000000400390 00000390 000000000000001a 0000000000000000 AX 0 0 4 [12] .plt PROGBITS 00000000004003b0 000003b0 0000000000000030 0000000000000010 AX 0 0 16 [13] .plt.got PROGBITS 00000000004003e0 000003e0 0000000000000008 0000000000000000 AX 0 0 8 [14] .text PROGBITS 00000000004003f0 000003f0 00000000000001c2 0000000000000000 AX 0 0 16 [15] .fini PROGBITS 00000000004005b4 000005b4 0000000000000009 0000000000000000 AX 0 0 4 [16] .rodata PROGBITS 00000000004005c0 000005c0 0000000000000004 0000000000000004 AM 0 0 4 [17] .eh_frame_hdr PROGBITS 00000000004005c4 000005c4 000000000000003c 0000000000000000 A 0 0 4 [18] .eh_frame PROGBITS 0000000000400600 00000600 0000000000000114 0000000000000000 A 0 0 8 [19] .init_array INIT_ARRAY 0000000000600718 00000718 0000000000000008 0000000000000000 WA 0 0 8 [20] .fini_array FINI_ARRAY 0000000000600720 00000720 0000000000000008 0000000000000000 WA 0 0 8 [21] .jcr PROGBITS 0000000000600728 00000728 0000000000000008 0000000000000000 WA 0 0 8 [22] .dynamic DYNAMIC 0000000000600730 00000730 00000000000001d0 0000000000000010 WA 6 0 8 [23] .got PROGBITS 0000000000600900 00000900 0000000000000008 0000000000000008 WA 0 0 8 [24] .got.plt PROGBITS 0000000000600908 00000908 0000000000000028 0000000000000008 WA 0 0 8 [25] .data PROGBITS 0000000000600930 00000930 0000000000000010 0000000000000000 WA 0 0 8 [26] .bss NOBITS 0000000000600940 00000940 0000000000000008 0000000000000000 WA 0 0 1 [27] .comment PROGBITS 0000000000000000 00000940 0000000000000034 0000000000000001 MS 0 0 1 [28] .gnu_debuglink PROGBITS 0000000000000000 00000974 0000000000000010 0000000000000000 0 0 1 [29] .shstrtab STRTAB 0000000000000000 000011ac 000000000000011b 0000000000000000 0 0 1 [30] .symtab SYMTAB 0000000000000000 00000988 0000000000000618 0000000000000018 31 45 8 [31] .strtab STRTAB 0000000000000000 00000fa0 000000000000020c 0000000000000000 0 0 1 Key to Flags: W (write), A (alloc), X (execute), M (merge), S (strings), l (large) I (info), L (link order), G (group), T (TLS), E (exclude), x (unknown) O (extra OS processing required) o (OS specific), p (processor specific)
readelfコマンドでどんな内容か確認すると、test.debugという文字列が入っています。
masami@saga:~/tmp$ readelf -p .gnu_debuglink test String dump of section '.gnu_debuglink': [ 0] test.debug [ c] c
で、ここでgdbを再度実行すると、自動的にtest.debugを読み込んで、デバッグ情報が使えるようになります。
masami@saga:~/tmp$ gdb ./test Reading symbols from ./test...Reading symbols from /home/masami/tmp/test.debug...done. done. (gdb) list 1 #include <stdio.h> 2 3 static void print_args(char **argv) 4 { 5 while (*argv) { 6 printf("%s\n", *argv++); 7 } 8 } 9 10 int main(int argc, char **argv) (gdb)
今はtestとtest.debugが同じ場所にあるので問題なかったですが、例えば、test.debugをdebug/においた場合、gdbはデバッグシンボルを見つけられません。このような場合は、set debug-file-directoryでデバッグ情報のあるディレクトリを指定します。setでディレクトリを指定して、fileコマンドで読み込んだりすれば大丈夫です。
masami@saga:~/tmp$ gdb ./test Reading symbols from ./test...(no debugging symbols found)...done. (gdb) set debug-file-directory ./debug (gdb) list No symbol table is loaded. Use the "file" command. (gdb) show debug-file-directory The directory where separate debug symbols are searched for is "./debug". (gdb) file ./debug/test.debug Reading symbols from ./debug/test.debug...done. (gdb) list 1 #include <stdio.h> 2 3 static void print_args(char **argv) 4 { 5 while (*argv) { 6 printf("%s\n", *argv++); 7 } 8 } 9 10 int main(int argc, char **argv) (gdb)
あと、Build IDも重要なはずですね。これもreadelfコマンドで確認できます。どちらのファイルもBuild IDは同じなことを確認できます。
masami@saga:~/tmp$ readelf -n test Displaying notes found at file offset 0x0000021c with length 0x00000020: Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 2.6.32 Displaying notes found at file offset 0x0000023c with length 0x00000024: Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: d97bf9a494dbde8a03785c102de536b2d8f82ac7 masami@saga:~/tmp$ readelf -n test.debug Displaying notes found at file offset 0x0000021c with length 0x00000020: Owner Data size Description GNU 0x00000010 NT_GNU_ABI_TAG (ABI version tag) OS: Linux, ABI: 2.6.32 Displaying notes found at file offset 0x0000023c with length 0x00000024: Owner Data size Description GNU 0x00000014 NT_GNU_BUILD_ID (unique build ID bitstring) Build ID: d97bf9a494dbde8a03785c102de536b2d8f82ac7
( ´ー`)フゥー...
参考
Separate Debug Files - Debugging with GDB
Learning Linux Binary Analysis
- 作者: Ryan "elfmaster" O'Neill
- 出版社/メーカー: Packt Publishing
- 発売日: 2016/02/29
- メディア: Kindle版
- この商品を含むブログを見る