DWARF debugging formatを利用する


はじめに

DWARFはデバッグ情報を格納するためのデータフォーマットです。
デバッグ情報を取り出して、利用する方法を紹介します。

コンパイル後のデバッグ情報を使って
網羅的にstruct / class の 情報を取り出せるため便利です。

sample

具体例を使って説明します。

test.cpp
class parent {
    char pvalue;
};

class child : public parent {
    short cvalue;
};

struct record {
    int rvalue;
};

int main()
{
    class parent p;
    class child c;
    struct record r;
    return 0;
}

-gオプションをつけてコンパイルすることでデバッグ情報をつけて実行ファイルを作成します

console
$ gcc -g -O0 test.cpp

objdumpを使ってDWARFの情報を出力します。

console
$ LANG=C objdump -W ./a.out  | grep "DWARF Version"
  DWARF Version:               2
$ LANG=C objdump -Wi ./a.out 

./a.out:     file format elf64-x86-64

Contents of the .debug_info section:

  Compilation Unit @ offset 0x0:
   Length:        0xe5 (32-bit)
   Version:       4
   Abbrev Offset: 0x0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
    <c>   DW_AT_producer    : (indirect string, offset: 0x46): GNU C++ 4.8.4 -mtune=generic -march=x86-64 -g -O0 -fstack-protector  
    <10>   DW_AT_language    : 4    (C++)
    <11>   DW_AT_name        : (indirect string, offset: 0x37): test.cpp    
    <15>   DW_AT_comp_dir    : (indirect string, offset: 0x8a): /home/user/tmp  
    <19>   DW_AT_low_pc      : 0x4004ed 
    <21>   DW_AT_high_pc     : 0xb  
    <29>   DW_AT_stmt_list   : 0x0  
 <1><2d>: Abbrev Number: 2 (DW_TAG_class_type)
    <2e>   DW_AT_name        : (indirect string, offset: 0x29): parent  
    <32>   DW_AT_byte_size   : 1    
    <33>   DW_AT_decl_file   : 1    
    <34>   DW_AT_decl_line   : 1    
    <35>   DW_AT_sibling     : <0x46>   
 <2><39>: Abbrev Number: 3 (DW_TAG_member)
    <3a>   DW_AT_name        : (indirect string, offset: 0x30): pvalue  
    <3e>   DW_AT_decl_file   : 1    
    <3f>   DW_AT_decl_line   : 2    
    <40>   DW_AT_type        : <0x46>   
    <44>   DW_AT_data_member_location: 0    
 <2><45>: Abbrev Number: 0
 <1><46>: Abbrev Number: 4 (DW_TAG_base_type)
    <47>   DW_AT_byte_size   : 1    
    <48>   DW_AT_encoding    : 6    (signed char)
    <49>   DW_AT_name        : (indirect string, offset: 0x1d): char    
 <1><4d>: Abbrev Number: 2 (DW_TAG_class_type)
    <4e>   DW_AT_name        : (indirect string, offset: 0x40): child   
    <52>   DW_AT_byte_size   : 4    
    <53>   DW_AT_decl_file   : 1    
    <54>   DW_AT_decl_line   : 5    
    <55>   DW_AT_sibling     : <0x6d>   
 <2><59>: Abbrev Number: 5 (DW_TAG_inheritance)
    <5a>   DW_AT_type        : <0x2d>   
    <5e>   DW_AT_data_member_location: 0    
    <5f>   DW_AT_accessibility: 1   (public)
 <2><60>: Abbrev Number: 3 (DW_TAG_member)
    <61>   DW_AT_name        : (indirect string, offset: 0x0): cvalue   
    <65>   DW_AT_decl_file   : 1    
    <66>   DW_AT_decl_line   : 6    
    <67>   DW_AT_type        : <0x6d>   
    <6b>   DW_AT_data_member_location: 2    
 <2><6c>: Abbrev Number: 0
 <1><6d>: Abbrev Number: 4 (DW_TAG_base_type)
    <6e>   DW_AT_byte_size   : 2    
    <6f>   DW_AT_encoding    : 5    (signed)
    <70>   DW_AT_name        : (indirect string, offset: 0x13): short int   
 <1><74>: Abbrev Number: 6 (DW_TAG_structure_type)
    <75>   DW_AT_name        : (indirect string, offset: 0x22): record  
    <79>   DW_AT_byte_size   : 4    
    <7a>   DW_AT_decl_file   : 1    
    <7b>   DW_AT_decl_line   : 9    
    <7c>   DW_AT_sibling     : <0x8d>   
 <2><80>: Abbrev Number: 3 (DW_TAG_member)
    <81>   DW_AT_name        : (indirect string, offset: 0xc): rvalue   
    <85>   DW_AT_decl_file   : 1    
    <86>   DW_AT_decl_line   : 10   
    <87>   DW_AT_type        : <0x8d>   
    <8b>   DW_AT_data_member_location: 0    
 <2><8c>: Abbrev Number: 0
 <1><8d>: Abbrev Number: 7 (DW_TAG_base_type)
    <8e>   DW_AT_byte_size   : 4    
    <8f>   DW_AT_encoding    : 5    (signed)
    <90>   DW_AT_name        : int  
 <1><94>: Abbrev Number: 8 (DW_TAG_subprogram)
    <95>   DW_AT_external    : 1    
    <95>   DW_AT_name        : (indirect string, offset: 0x7): main 
    <99>   DW_AT_decl_file   : 1    
    <9a>   DW_AT_decl_line   : 13   
    <9b>   DW_AT_type        : <0x8d>   
    <9f>   DW_AT_low_pc      : 0x4004ed 
    <a7>   DW_AT_high_pc     : 0xb  
    <af>   DW_AT_frame_base  : 1 byte block: 9c     (DW_OP_call_frame_cfa)
    <b1>   DW_AT_GNU_all_call_sites: 1  
 <2><b1>: Abbrev Number: 9 (DW_TAG_lexical_block)
    <b2>   DW_AT_low_pc      : 0x4004f1 
    <ba>   DW_AT_high_pc     : 0x5  
 <3><c2>: Abbrev Number: 10 (DW_TAG_variable)
    <c3>   DW_AT_name        : p    
    <c5>   DW_AT_decl_file   : 1    
    <c6>   DW_AT_decl_line   : 15   
    <c7>   DW_AT_type        : <0x2d>   
    <cb>   DW_AT_location    : 2 byte block: 91 60  (DW_OP_fbreg: -32)
 <3><ce>: Abbrev Number: 10 (DW_TAG_variable)
    <cf>   DW_AT_name        : c    
    <d1>   DW_AT_decl_file   : 1    
    <d2>   DW_AT_decl_line   : 16   
    <d3>   DW_AT_type        : <0x4d>   
    <d7>   DW_AT_location    : 2 byte block: 91 60  (DW_OP_fbreg: -32)
 <3><da>: Abbrev Number: 10 (DW_TAG_variable)
    <db>   DW_AT_name        : r    
    <dd>   DW_AT_decl_file   : 1    
    <de>   DW_AT_decl_line   : 17   
    <df>   DW_AT_type        : <0x74>   
    <e3>   DW_AT_location    : 2 byte block: 91 60  (DW_OP_fbreg: -32)
 <3><e6>: Abbrev Number: 0
 <2><e7>: Abbrev Number: 0
 <1><e8>: Abbrev Number: 0

struct / class の 情報

DWARFにはstruct / class の 情報があります。
struct / classの名前やデータサイズを知りたいというケースで役立ちます。

次のタグでstruct / classの情報であるかどうかを判定できます。
DW_TAG_base_typeは組み込み型を表します。

DW_TAG_base_type
DW_TAG_structure_type
DW_TAG_class_type

具体例は以下の通りです。class name = parent, size = 1 であることが分かります。

<1><2d>: Abbrev Number: 2 (DW_TAG_class_type)
<2e> DW_AT_name : (indirect string, offset: 0x29): parent
<32> DW_AT_byte_size : 1

rubyを使ってtype / name / sizeを取り出します。

extract.rb
#!/usr/bin/ruby

while str = STDIN.gets
    if str.match(/DW_TAG_(.*)_type/) then
        if $1 == "class" or $1 == "structure" then
            # type
            type = $1
            # name
            str = STDIN.gets
            str.match(/.*: (.*)/)
            name = $1.strip
            # byte size
            str = STDIN.gets
            str.match(/.*: (.*)/)
            byte_size = $1.strip
            # print
            print type + "\t" + name + "\t" + byte_size + "\n"
        end
    end
end
console
$ ruby --version
ruby 1.9.3p484 (2013-11-22 revision 43786) [x86_64-linux]
$ LANG=C objdump -Wi ./a.out | ./extract.rb 
class   parent  1
class   child   4
structure   record  4