おしながき

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_arangesセクションの構造

(2013/5/19 不明点を訂正のうえ、作成終り。一応これで大丈夫かなarangesは)

これ、何に使うの?

このセクションには、プログラムコードのどの範囲が、.debug_info内のcompilation unit、すなわち「(C言語の).cファイル」のどれに対応するか、の「早見表」が格納されています。
なので、デバッガちゃんはこいつを読んでメモリ展開なんかしておいて、その状態でプログラムコードアドレスを与えられたら、どのソースファイルの情報を調べればいいか、がすぐに分かるのですね。
ということで、早速構造見て行きます。っていっても、こいつは簡単そーです。(原文がA4 1ページしかない。lineやframeに比べて大違い)


まずは、全体の構造

.debug_arangesセクションでは、compilation unit、つまり.cソースの単位に、以下のレコード構造を持っているみたいです。

<arangesヘッダ> <開始アドレス1> <長さ1> <開始アドレス2> <長さ2> 。。。(以後、開始アドレスと長さのセットが必要数分繰り返し>。。。 <アドレス 0番地> <長さ 0>

この構成が.cソース数分、繰り返しています。また、最後は0番地のアドレスと、長さ0のレコードで終ります。
それでは、以下で詳細に見て行きますかね。


arangesヘッダ

arangesヘッダは、以下の構造みたいです。

No. 項目名 サイズ 説明
1 unit_length 4byteor 12Byte
unsigned 整数
このcompilation unitのarangesレコードの長さです。
なお、このunit_lengthの長さは含みません
なお、他と同様、0xffffffffじゃなかったら、この値は4Byteで以下のNo.3も4Byte
逆に0xffffffffなら続く8Byteが長さで、No.3も8Byte
2 version 2Byte
unsigned short
.debug_arangesセクションのバージョン
これ、DWARF自体のバージョンとは違うので、注意
3 debug_info_offset 4Byte or 8Byte
unit_lengthの長さ次第でいずれか
このarangesレコードが対象とするcompilation unitの情報が書かれている、.debug_infoセクション内でのオフセット、つまりセクションの頭からの位置です。
4 address_size 1Byte
unsigned char
これは、<開始アドレス>のサイズをByte数で表現したものです。64bitなら、8ですね。
なお、セグメント方式のアドレス表現をするアーキでは、この値はセグメント内での開始アドレスまでのオフセットを表現するために必要なサイズとなります。
32bit(x86)は影響ありそうですね。(つか、Dr.deamon64はこれが面倒+もう32bitは本人使ってないんで、割り切って64bitだからとりあえず無視でOK?)
5 segment_size 1Byte
unsigned char
セグメント方式アドレッシングのアーキの場合、セグメントサイズのByte表記が入ります。リニア(フラット)アドレッシングの場合は、0です。
6 Boundary Padding (参照→) 次に続く、<開始アドレス>は、32bitアーキなら4Byte、64bitアーキなら8Byte単位のセクション内のオフセットからはじまる必要があるっぽいです。なので、ヘッダのNo.5終了後のセクション内のオフセット値(アドレス)が、4ないし8で割り切れない時は、割り切れるオフセットになるまで分のサイズを0x00でパディングする必要があります。これ「要注意」
(ちなみに、DWARF3の原文には、このパディングの記述がありません。。。)


<開始アドレス> <長さ>

arangesヘッダの後には、<開始アドレス><長さ>の組が、連続します。
連続する、と書いたのは、例えば1つのCソースをコンパイルした結果、 0x004005b0 - 0x004005c4 と 0x004006b0 - 0x004006c8 のの2つの範囲に分断されてコードが配置された、というケースが考えられます。
この場合、 0x004005b0 0x14 0x004006b0 0x18 と連続させて表現するためです。

さて、表現形式です。これは、上の例の様に、バイナリ列で、<開始アドレス><長さ>が連続するだけです。。。
よって、上の例が説明になっちゃってます。 ただし、以下に注意するみたいです。

  • <開始アドレス>
    • バイナリ列の長さ: arangesヘッダ:address_size の値。
  • <長さ>
    • バイナリ列の長さ: arangesヘッダ:address_size の値。

なお、開始アドレス=0番地、長さ=0 の<開始アドレス><長さ>の組が登場したら、1つのarangesヘッダが終了です。
.debug_arangesセクションの長さがまだある場合は、もう1つ以上arangesレコードがありますきっと。逆に、その部分で.debug_arangesセクションが終ったら、それでサイナラです。


実機の例

ということで、実機でやってみました。
まず、イケニエにしたソースです。ちょー適当な例ですが、.cソースが2つあればそれで十分、なので、これで勘弁してくださいです。

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

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;
    
    g1  = 1;
    g2  = 2;
    a   = 3;
    b   = 4;

    r = func1(a,b);
    r = func2(r);

    return r;
}

んで、次に2つめのソースです。

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

int     g2;

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

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

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]

さて、この環境で、上記ソースをビルドして、そこから.debug_arangesセクションをバイナリで抜きだして解析してみました。その結果が以下です。
なお、#の行はつっこんだコメントですので、よろしく。

# Hex.Dump of .debug_aranges Section ----

00000000  2c 00 00 00             : unit_length = 0x0000002c = 44 Byte ですね。
00000004  02 00                   : version = 0x0002 = 2
 # これはDWARFのversionは異なるので注意。

00000006  00 00 00 00             : debug_info_offset = 0x00000000
 # .debug_infoセクションの頭0x00000000バイト目から始まるCUの範囲ですよってことです。

0000000a  08                      : address_size = 0x08
 # 64bitコンパイルですから、0x08になっていて、これもOKです。

0000000b  00                      : segment_size = 0x00
 # FreeBSDのINTEL64のアドレス空間はリニア(フラット)ですから、0x00でOKです。
 # (やってないけど、これdosなら値もつかな?)

0000000c  00 00 00 00             : Boundary Padding(4Byte)
 # 64bit(8Byte)アーキなので、アドレス表現はセクション内の8Byte単位のオフセットから
 # 始める必要があるみたいですが、0x0000000cなので、4Byte足りません。
 #  → よって、4Byteを0x00でパディング!! (原文の漏れじゃ。。。)

00000010  60 05 40 00 00 00 00 00 : <開始アドレス>=0x0000000000400560
 # .symtabセクションを見ると、func2関数のアドレスと一致しています。
 # よって、test2.cのアドレスの開始範囲と見れますので、大丈夫そーですね。

00000018  6f 00 00 00 00 00 00 00 : <長さ>=0x000000000000006f = 111 Byte
 # 開始アドレス 0x0000000000400560 - 終了アドレス0x00000000004005cf です。
 # .symtabセクションでは、test1.c内のfunc1関数の開始が0x004005d0になってますから
 # これもあってそうですね。

00000020  00 00 00 00 00 00 00 00 : <開始アドレス>= 0x0000000000000000
00000028  00 00 00 00 00 00 00 00 : <長さ> = 0x0000000000000000
 # ということで、<開始アドレス><長さ>ともに、0のレコードが登場しました。
 # ので、このCソース分は、終りましたです。

 # で、次のCソース分(test1.c)です。
 # が、もう以下ざっと見た瞬間に、上のtest2とおんなじ構造であることが
 # 明らかなので、もういいですね。。。ということで、割愛。
00000030  2c 00 00 00
00000034  02 00
00000036  47 01 00 00
0000003a  08
0000003b  00
0000003c  00 00 00 00
00000040  d0 05 40 00 00 00 00 00
00000048  20 00 00 00 00 00 00 00
00000050  00 00 00 00 00 00 00 00
00000058  00 00 00 00 00 00 00 00

最後に、念のため上記のわたくしの解析が正しいかどうか、あやしかったりするんで、答え、としてreadelf君の解析結果を載せます。
これ見る限り、上記あってるっぽいですね。

The section .debug_aranges contains:

  Length:                   44
  Version:                  2
  Offset into .debug_info:  0
  Pointer Size:             8
  Segment Size:             0

    Address            Length
    0x0000000000400560 0x6f
    0x0000000000000000 0x0

  Length:                   44
  Version:                  2
  Offset into .debug_info:  147   ←ちなみに、これ10進へ変換忘れ or 0xの付け忘れっぽいですね。。。
  Pointer Size:             8
  Segment Size:             0

    Address            Length
    0x00000000004005d0 0x20
    0x0000000000000000 0x0

というこで、.debug_arangesはこれでおわりです。これは、簡単でした。
が、ヘッダのケツにパディングが必要だったのは、「んなもん規約に書いてなきゃ分かるかい!!」というのが感想ですな。


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