おしながき

ELFファイルフォーマット

  • .eh_frameセクションの構造と読み方

DWARFファイルフォーマット

NCURSESライブラリ

  • NCURSES Programing HOWTO ワタクシ的ほんやく
    1. Tools and Widget Libraries
    2. Just For Fun !!!
    3. References
  • その他、自分メモ
  • NCURSES雑多な自分メモ01


最近の更新 (Recent Changes)

2019-09-24
2013-10-10
2013-10-03
2013-10-01
2013-09-29
目次に戻る:DWARFファイルフォーマット

.debug_pubnames/.debug_pubtypesセクションの構造

.debug_pubnames/.debug_pubtypesって何?

".debug_pubnames"セクションは、「グローバル関数(staticな宣言がされてない奴)」「グローバル変数」の名前から、.debug_info内でのそれらの情報が書いてある場所の位置(オフセット)を即刻引くための早見表みたいです。
".debug_pubtypes"セクションは、同様?に「グローバルな型」の名前から、.debug_info内でのそれらの情報が書いてある場所の位置(オフセット)を即刻引くための早見表みたいです。
(2013/5/14 まだちゃんと調べてないけど、恐らく「グローバルな型」=関数外部で宣言した、typedef xxxx ですねきっと。(C言語の場合)

ということで、構造みていきまーす!


.debug_pubnames/.debug_pubtypesセクションの大まかな構造

えと、まず".debug_pubnamesセクション" と ".debug_pubtypesセクション" は、上記の通り、その目的は違うのですが、構造が「全く同じ」です。
よって、一緒に書いてますです。(これは、DWARF3な規格原文も一緒っす)

で、これらのセクションの中は、Compilation Unit、ようするに、.cソースの単位に、以下の構造でデータがつっこまれてます。

<ヘッダ> <オフセット1> <文字列1> <オフセット2> <文字列2> 。。。(グローバルxxxxがあるだけオフセットと文字列のコンビが続く)。。。 <0x00 のオフセット>

.cソースが複数あれば、この構造が、ソース数分連続する、って仕組みです。(あ、ただし、多分、.cソース内にグローバルxxxxがなければ、とーぜんないと思う)

構造ですが、これで終りらしいです。うむ、昨日の.debug_arangesと言い、このあたりは簡単ですね。(つか、frameとかlineが大変過ぎ。。。)


<ヘッダ>のデータ構造

<ヘッダ>ですが、以下表の通りみたいです。 .debug_arangesによく似てますな。

No. 項目名 Size 説明
1 unit_length 4Byte or 12byte
説明欄参照
いつもながらの、データ長ですね。このunit_lengthを除いた、このレコード(<ヘッダ>から<0x00のオフセットまで>)の長さです。
ところで、サイズ欄の「説明欄参照」ですが、おもろい記述がありました
この長さ、「unsigned扱いで、最初の4Byteをunsigned int扱いで読み出して、0xffffff00 以下ならそれがそのままサイズで、かつ32bit DWARFフォーマット。
逆に、4Byteの値が0xffffffff なら、 それは64Bit DWARFフォーマットで、続く8Byteにunsigned longで長さが格納される。」
これ、.debug_lineとか.debug_frameでヘッダのサイズが64bitコンパイルでなんで32bit って書いていましたが、どーやらこういう仕掛けで、64bitコンパイルでも32bit DWARFにしているみたいですね→gcc
2 version 2Byte
unsigned short
これもおなじみ君で、「このセクション」のバージョン番号です。
DWARD2とか3とかとは別のバージョンなんで、注意
3 debug_info_offset 4Byte(32bit DWARF)
8Byte(64bit DWARF))
どちらもunsigned整数
このレコードで表現するグローバルxxxが居座っているCompilation Unit、すなわち.cソースのCompilation Unit Header(CUHeader)の位置(オフセット)です。
このオフセットは、.debug_infoの頭からのオフセットですね。
4 debug_info_length 4Byte(32bit DWARF)
8Byte(64bit DWARF))
どちらもunsigned整数
.debug_infoセクション内での、No.3でリンクしているCU配下のデータの長さが入っている(よーです。ちょっと英訳自信なしこれ)


<オフセット><文字列>の中身

<ヘッダ>に続いて、その<ヘッダ>が対象とするCU、.cソース内のグローバルxxxxの早見表になるデータが連続してつっこまれます。
早見表似奈留データですが、上の通り<オフセット>と<文字列>のセット、があるだけ連続するみたいです。で、それぞれの詳細は、以下ですね。

項目名 データの表記方法 説明
<オフセット> 4Byte (32bit DWARFの場合)
8byte (64bit DWARFの場合)
続く<文字列>で表現される、グローバルxxxxの情報が書かれた.debug_info内でのオフセットみたいです。
なお、このオフセットは、<ヘッダ>:debug_info_offsetからのオフセット。
なので、.debug_infoセクションの先頭からのオフセット、にする場合は「<ヘッダ>:debug_info_offset + <オフセット>」の値になるっぽいです。
<文字列> NULL文字を終端とする文字列 そのままです。グローバルxxxxのソース中での名前文字列っぽいです。(Cの場合、C++は以下参照)
原文には「.debug_info内での"DW_AT_name"属性で示す名称」とありますが、C言語の場合なんかいじってるのかなぁ。。。
(関数名がちょっと怪しい。("_ アンダ_スコア"を関数名の頭に付けたものかもですね。)

なお、CU内のグローバルxxxxがあるだけ、このセットを繰り返したあと、もうネタ切れー、となったら、<オフセット>=0 のデータ(DWARFのbit数次第で4Byteか8Byte表記)を付けて、それでそのCU分は終りです。
さらにcソースがある場合は、同じ構造を繰り返すっぽです。[[BR]


C++ソースでの注意

(わたくしC++はスーパーしろーとなんであんまり良く分からんですが。。。) 原文見る限り、注意数点あるっぽいです。

  • C++のクラス内のメンバ関数は、クラス宣言をやっているCU内に含まれるものとして扱われる。 「ただし、行外の具体的でないインスタンスの場合、.debug_pubnamesにそのメンバ関数のレコードを持つ必要はない?」
    • これは、分かりますね。さすがに、クラス使うCソース側で毎回全部含まれると、おんなじ物で溢れ買えるので。。。
    • スミマセンが、「」は、なんか英文の意味不明です。。。(多分、英語力+C++言語力が少なすぎて、分からんです。。。"concrete out-of-line instance"ってなんじゃ?)
  • C++の場合、<文字列>の値について、.debug_pubnames内でのクラスや共用体でのメンバ関数や静的なデータメンバの名前の放言は、(.debug_infoセクション内の)DW_AT_name属性で使われている単純な名前ではなく、そのデータや関数の"正式な名称"で表現される。
    • "正式な名称"が何かは良く分かっていませんが、恐らくC++のメンバ関数なんかは、どのクラスのかもはっきりさせる必要があるので、"クラス名::メンバ関数名"あたりの表記にしてまーす、ってことだと思ってます。
      (ただ、C++をアセンブラ procにアセンブルした際の名称の書き方って"::"挟みじゃなかった気もとちとしてますが、忘れた。)


実機でのデータの解析

ということで、またもや実機でデータ見てみました。
まず、使ったソースです。今回は、ヘッダファイル含め3つのソースを用いました。
まず、1つめ。

/* .debug_aranges sample source - test1.c */
#include"test.h"

int     g1;
extern  int     g2;             // by test2.c
extern  int     func1(int a, int b);    // by test2.c

int     func2(int d)    {
        return d + g2 + g1;
}

int main(int argc, char *argv[])        {
        int     a, b, r;
        DATA    d;              // by test.h
        
        g1      = 1;
        g2      = 2;
        a       = 3;
        b       = 4;

        r = func1(a,b);
        d.integer = func2(r);

        d.str[0] = 't';
        d.str[1] = 'e';
        d.str[2] = 's';
        d.str[3] = 't';

        return r;
}

んで、2発目

/* .debug_aranges sample source - test2.c */

int             g2;

int func1(int a, int b) {
        return a+b-g2;
}

この2つは、.debug_arangesの時とほとんど、おんなじです。ただ、今回はpubtypesのデータを出したくて、以下のヘッダファイルを追加しました。

/* .debug_pubnames/pubtypes sample source - test.h */

typedef struct  {
        int             integer;
        char    str[10];
} DATA;


ちなみに、使った環境は、以下です。

prompt # uname -a
FreeBSD xxxx.koinec.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #0:  amd64

prompt # gcc -v
Using built-in specs.
Target: amd64-undermydesk-freebsd
Configured with: FreeBSD/amd64 system compiler
Thread model: posix
gcc version 4.2.1 20070831 patched [FreeBSD]

で、こいつらを"-g3"付きでコンパイルしたんですがね。。。。

「なんと! .debug_pubtypesが出ない!」

ではありませんか!

→調べてみましたが。。。ちょっと現状.debug_pubtypesの出し方が分かりません。。。
まぁ、格納方法、内容は.debug_pubnamesとおんなじなんで、スミマセンが今回は.debug_pubnamesだけ見てみます。

ということで、.debug_pubnamesをいつも通り、セクション抜き出して、ダンプ→解析が以下の結果です。#ではじまる行はつっこんだコメントです。

 # Hex. Dump by .debug_pubnames Section
00000000  28 00 00 00       : unit_length = 0x00000028 = 40 Byte
 # なので、32bit Dwarfフォーマットで、以下の長さは40Byteってことですね。

00000004  02 00             : version = 0x0002 = 2
 # これは、そのままですね。ただ、毎度ですがこの2はDWARF2と言う意味ではないです。

00000006  00 00 00 00       : debug_info_offset = 0x00000000
 # このヘッダ以下にある情報は、.debug_infoセクションの頭から0Byte目からのデータってことです。

0000000a  9b 01 00 00       : debug_info_length = 0x0000019b = 411 Byte
 # debug_info_offsetの場所から411Byteの範囲内に、以下に列挙するグローバル関数/変数の
 # 情報があるよってことです。

 # それで、ヘッダが終り、ここから<オフセット><文字列>の繰り返しです。
0000000e  c4 00 00 00       : <オフセット> = 0x000000c4
 # なので、.debug_infoの先頭から、Header:debug_info_offset + <オフセット>の場所、
 # すなわち、0x00000000 + 0x000000c4 = 0x000000c4 にある情報、ってことを示しています。
 # では、その場所に何の情報があんの?が、次ですね。
00000012  66 75 6e 63 32 00 : <文字列>= func2
 # です。なので、文字の通り、func2関数の情報が0x000000c4にあるってことが分かりました。
 # これが正しいかは、最後に見てみますかね。

 # で、func2は終り、次のレコードです。
00000018  f8 00 00 00       : <オフセット> = 0x000000f8 = 248Byte目
 # .debug_infoの先頭から、0x00000000(debug_info_offset) + 0x000000f8 = 0x000000f8
 # にある情報ですな。
0000001c  6d 61 69 6e 00    : <文字列> = main
 # ので、main関数ですね。

00000021  79 01 00 00       : <オフセット> = 0x00000179 = 377 Byte目
 # まだ、0じゃないので、続きます。
00000025  67 31 00          : <文字列> = g1
 # グローバル変数 g1 のでした。

00000028  00 00 00 00       : <オフセット> = 0x00000000 = 0
 # というこで、オフセットが0になたので、これでこのCUは終りです。


 # はてさて、まだ続いてますねデータ。→なので、次のCUです。
0000002c  1f 00 00 00       : unit_length = 0x0000001f
00000030  02 00             : version = 0x0002
00000032  9b 01 00 00       : debug_info_offset = 0x0000019b
 # これから、次2目のCUの開始位置は、0x0000019bであることが分かります。
 # これ、下で本当か確かめます。
00000036  ca 00 00 00       : debug_info_length = 0x000000ca

0000003a  6d 00 00 00       : <offset> = 0x0000006d
 # 先頭からのオフセットは、0x00000208
0000003e  66 75 6e 63 31 00 : <文字列> = func1

00000044  b4 00 00 00       : <offset> = 0x000000b4
 # 先頭からのオフセットは、0x0000024f
00000048  67 32 00          : <文字列> = g2

0000004b  00 00 00 00       : <offset> = 0x00000000 (終り)

よって、今回もarangesと同じく、あっさり終りました。ただ、本当に.debug_info側と一致しているかだけは、気になったんで、以下readelfコマンドで吐かせた.debug_infoと見比べてみます。

The section .debug_info contains:

  Compilation Unit @ offset 0x0:
   Length:        407
   Version:       2
   Abbrev Offset: 0
   Pointer Size:  8
 <0><b>: Abbrev Number: 1 (DW_TAG_compile_unit)
 <1><6d>: Abbrev Number: 2 (DW_TAG_structure_type)
 <2><75>: Abbrev Number: 3 (DW_TAG_member)
 <2><87>: Abbrev Number: 3 (DW_TAG_member)
 <1><96>: Abbrev Number: 4 (DW_TAG_base_type)
 <1><9d>: Abbrev Number: 5 (DW_TAG_array_type)
 <2><a6>: Abbrev Number: 6 (DW_TAG_subrange_type)
 <1><ad>: Abbrev Number: 7 (DW_TAG_base_type)
 <1><b0>: Abbrev Number: 4 (DW_TAG_base_type)
 <1><b8>: Abbrev Number: 8 (DW_TAG_typedef)
 <1><c4>: Abbrev Number: 9 (DW_TAG_subprogram)  ←<>の2つ目が.debug_info内でのオフセットですが、ちゃんと0x000000c4になってますね。
  <c6>     DW_AT_name        : func2
 <2><eb>: Abbrev Number: 10 (DW_TAG_formal_parameter)
 <1><f8>: Abbrev Number: 9 (DW_TAG_subprogram)  ←main関数もちゃんと0x000000f8になってますよ。
  <fa>     DW_AT_name        : main
 <2><11e>: Abbrev Number: 10 (DW_TAG_formal_parameter)
 <2><12d>: Abbrev Number: 10 (DW_TAG_formal_parameter)
 <2><13c>: Abbrev Number: 11 (DW_TAG_variable)
 <2><148>: Abbrev Number: 11 (DW_TAG_variable)
 <2><154>: Abbrev Number: 11 (DW_TAG_variable)
 <2><160>: Abbrev Number: 11 (DW_TAG_variable)
 <1><16d>: Abbrev Number: 12 (DW_TAG_pointer_type)
 <1><173>: Abbrev Number: 12 (DW_TAG_pointer_type)
 <1><179>: Abbrev Number: 13 (DW_TAG_variable)    ← はい。グローバル変数g1は0x00000179になってます。これもOK。
  <17a>     DW_AT_name        : g1
 <1><18e>: Abbrev Number: 14 (DW_TAG_variable)  ← グローバル変数g2は、実体はtest2.cにあるので、pubnamesの指し先は、test2.c、すなわち下のCUです。
  <18f>     DW_AT_name        : g2
 # (参考) このグローバル変数g2がこっちにも出ているのは、恐らくexternして参照しているのを、出すようにしているからです。

  Compilation Unit @ offset 0x19b:
   Length:        198
   Version:       2
   Abbrev Offset: 199
   Pointer Size:  8
 <0><1a6>: Abbrev Number: 1 (DW_TAG_compile_unit)
 <1><208>: Abbrev Number: 2 (DW_TAG_subprogram)   ← これもOKですね。
  <20a>     DW_AT_name        : func1
 <2><22f>: Abbrev Number: 3 (DW_TAG_formal_parameter)
 <2><23b>: Abbrev Number: 3 (DW_TAG_formal_parameter)
 <1><248>: Abbrev Number: 4 (DW_TAG_base_type)
 <1><24f>: Abbrev Number: 5 (DW_TAG_variable)  ← これもOK!
  <250>     DW_AT_name        : g2  

ということで、.debug_pubnames/.debug_pubtypesもあっさり終了ですね。
.debug_pubtypesの現物を見れなかったのはちと悔しいのですが、まぁその内御目にかかるかと。


目次に戻る:DWARFファイルフォーマット