2009年09月16日
DWARF と有限状態機械 (2)
これまでの流れ
- DWARF と有限状態機械
前回は、どのようにして行番号情報が保持されているのかという概要、そして有限状態機械の仕様について説明しました。
今回は、具体的なヘッダのフォーマットを見ていきます。
デバッグ情報の自由なベンダー拡張を許しつつ、それをサポートしないデバッガでも独自拡張部分以外のデバッグ情報は正しく読めるような工夫がしてある点が面白いところだと思います。このしくみは実際に多用されていて、コンパイラベンダーごとに、様々な独自拡張が入ったDWARFが生成されるようです。
行番号プログラムヘッダ(The Line Number Program Header)は、以下のようなフォーマットになっています。
- DWARF と有限状態機械
前回は、どのようにして行番号情報が保持されているのかという概要、そして有限状態機械の仕様について説明しました。
今回は、具体的なヘッダのフォーマットを見ていきます。
デバッグ情報の自由なベンダー拡張を許しつつ、それをサポートしないデバッガでも独自拡張部分以外のデバッグ情報は正しく読めるような工夫がしてある点が面白いところだと思います。このしくみは実際に多用されていて、コンパイラベンダーごとに、様々な独自拡張が入ったDWARFが生成されるようです。
行番号プログラムヘッダ(The Line Number Program Header)は、以下のようなフォーマットになっています。
型名 | 型 |
sbyte | 符号付き1byte整数 |
ubyte | 符号無し1byte整数 |
uhalf | 符号無し2byte整数 |
uword | 符号無し4byte整数 |
・行番号プログラムヘッダ
- unit_length (initial length という特別な扱いとなります。)
- コンパイル単位中の行番号情報のサイズ(バイト数)です。
このフィールド自身は、行番号プログラムヘッダの内部ではなく、DWARFセクション内のinitial lengthフィールド(.debug_line)に格納されます。
そのためこのフィールド自身のサイズは除いたサイズとなっています。
32bit DWARF3 formatにおける1つのセクションの最大サイズは4Gですが、0xfffffff0から0xffffffffまでは予約されていて特別な意味を持ちます。また、64bit DWARF3 formatでは、4G以上のサイズの場合は96 bitで表現されます。最初の32bitが0xffffffffの場合、残りの64bitが符号無し整数として扱われます。 - version (uhalf)
- これは本来、行番号情報(.debug_line)のバージョンであり、DWARF formatのバージョンとは独立しているのですが、現在はDWARF ver.2の場合は2、ver.3の場合は3と同じになっているようです。
- header_length
- このフィールドから行番号プログラムの開始位置までのバイト数です。
32bit DWARF formatの場合は符号無し4byte整数、64bitの場合は8byte整数となります。 - minimum_instruction_length (ubyte)
- 最小のターゲットマシン命令のバイト数です。
- default_is_stmt (ubyte)
- is_stmtレジスタの初期値です。
- line_base (sbyte)
- line_range (ubyte)
- special opcodesの動作を規定します。詳細は次回のバイトコードの説明時に説明します。
- opcode_base (ubyte)
- 最初のspecial opcodeの値です。典型的には、最大のstandard opcodeの値+1(DWARF2では10、3では13)となります。
もしopcode_baseが典型的な値よりも小さい場合は、このコンパイル単位中ではopcode_base以上の値のstandard opcodeは使用されていないということを意味します。もしopcode_baseが典型的な値よりも大きかった場合は、その間の数はベンダー固有の拡張のために使用されているということを意味します。 - standard_opcode_lengths (ubyte配列)
- 値1からopcode_base-1までの各standard opcodeのオペランド数が、LEB128でエンコードされて格納されています。
opcode_baseを増やし、対応するオペランド数をこのエントリに追加することにより、ベンダーは独自のstandard opcodeを追加することができ、なおかつその独自拡張に未対応のデバッガでもこのエントリを見ることにより、読めないオペコードをスキップすることが可能となります。 - include_directories (パス名(= null終端文字列)の列)
- コンパイル単位中でincludeされる全てのファイルのパス(絶対パスか、カレントディレクトリからの相対パス)が格納されます。
最初のエントリがカレントディレクトリとして扱われます。最後のエントリは単一のヌルバイトとなります。
- file_names (ファイルエントリ配列)
- コンパイル単位中の、行番号情報の生成あるいは他の目的※に使用されるファイルエントリ列が格納されます。
※ どのファイルで行われた宣言かという属性が格納されるDW_AT_decl_fileなどのセクション(Declaration Coordinates)や、.debug_macinfoセクション内のマクロデバッグ情報などから参照される場合などがあります。 このセクションの各エントリの添字が有限状態機械のfileレジスタの値と成り得ます。添字は1からカウントされます。
ファイルエントリは以下のような構成です。- ファイル名(null終端文字列)
- ファイルパス(include_directoriesセクション内の添字をLEB128エンコードしたもの)
- ファイルが最後に修正された時刻(実装定義の値をLEB128エンコードしたもの)
- ファイルサイズ(バイト数をLEB128エンコードしたもの)
最後のエントリは単一のヌルバイトとなります。
ファイルパスのLEB(0)はカレントディレクトリ、LEB(1)はinclude_directorieesセクション内の最初のディレクトリを意味します。
ファイル名が絶対パスの場合、ファイルパスは無視されます。
コンパイラは単一のヌルバイトをファイル名に生成しておき、後からDW_LNE_define_file(ファイルエントリそのものをオペランドに取るextended opcode)を使用して定義する場合もあります。