おしながき

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ファイルフォーマット

DWARF_expressionな表現:その2(LocationDescription)

(2013/05/28 分かりやすさ向上。。。のため、このページ内でのメモ順番、大幅入れ換え)

(まず、前提知識) レジスタ名命令

とりあえず、前提知識として、DWARF expressionsの続きです。
以下、いずれも「レジスタ名を指定する命令」で、「Location expressions」内でのDWARF expressions表現に使います(逆に、「Location expressions」以外のDWARF expressionを使うところでこの命令は使いません)
また、この命令群の命令は、2つ連続でつかったりすることはなく、命令自体は、簡単です。

No 命令 コード 引数1 引数2 ごせつめい
1 DW_OP_reg0 0x50 - - 探し物の格納場所は、レジスタ0 にあるよ、って表明します。それだけ。。。(DWARFスタックをいじったり、しません)
2 DW_OP_reg1 0x51 - - 探し物の格納場所は、レジスタ1 にあるよ、って表明します。それだけ。。。(DWARFスタックをいじったり、しません)
: - - - - (このまま、reg2,reg3と続く。。。)
32 DW_OP_reg31 0x8f - - 探し物の格納場所は、レジスタ31 にあるよ、って表明します。それだけ。。。(DWARFスタックをいじったり、しません)
33 DW_OP_regx 0x90 uLEB128
Register
- 探し物の格納場所は、引数1の番号のレジスタ にあるよって宣言です。これもDWARFスタックはいじりません

なお、これ注意があるです。
ここでの「レジスタ番号」は、実際のマシンでの「レジスタ番号」ではなく、DWARF特製の「レジスタ番号」です。つまり、一致しません。

(これは、DWARFがいろんなCPUアーキを対象とするため、どのアーキにもなじむよう、レジスタ番号のマッピングを決めてるため)

なので、この「DWARF特製レジスタ番号」と「実施のマシンのレジスタ番号」の関連は、各CPUアーキ別のABIで決めてねってことらしいです。
ちなみに、INTEL x86/64の場合は、AX/BX。。。なんで、そもそも番号なーいって段階で、これ調査要っす。


Location Descriptionって何?

デバッグ情報では、以下のようなことに使える情報を提供する必要があるみたいです。(というか、ないとデバッグできへん)

  • 変数の格納アドレスの検索に使える情報(検索方法)
  • 動的配列や文字列の境界の情報(決定方法)
  • サブルーチン(関数)のスタックフレームのベースアドレスやリターンアドレスの情報(検索方法)

さらに、デバッグ情報では上記の情報を提供できるだけじゃなくって、オブジェクト(変数やら関数やら、など)が生きている(メモリ上にある)間、ころころ変わってしまうlocation(格納場所)を指し続けられるようにする必要があるみたいです。(つーか、あるわな)

と、いうことでDWARFでは、これらの場所情報を情報としてもっているみたいです。んで、この情報を「Location Description」と読んでるっぽいです。
なお、このLocation Descriptionsのデータの表現方法は、DWARF expressionsは密月関係っぽいんで、ここで書きますです。

(が、このLocation Description、英語がムズカシイムズカシイ。。。どっかのコンニャク屋に「ホンヤクコンニャク」とかいうもの売ってへんかなぁ。)


Location Descriptionのパターン

Location Descriptionは、プログラム中のオブジェクト(変数、など)の格納場所の情報は、オブジェクトの種類や内容次第で、以下の2パターンがあるです。

  • 1. Locaiton expressions
    • DWARF expressionsで表現されちゃいます。
    • 通常、.debug_info内のあるDW_AT_xxxのデータの中身、として使われますです。んで「block」クラス(確か、頭にデータの長さを示すuLEB128が来て、その後ろのデータ(DWARF expressions)が続く)で表現される。
    • 「アドレス固定」のものや、そのオブジェクトの語彙を含むブロックと同じライフタイムをもつオブジェクトの格納場所を表現するにはDWARF expressionsで十分表現できる。(←この行は、もうむちゃくちゃな直訳です。意味はさっぱり、わからん!)
    • これで表現されるオブジェクト(の格納場所)は、ライフタイムの最初から最後まで(格納場所が)移動しないものを対象とするです。
      (※ライフタイム:もちろん人生じゃないですけど、変数とかにも生存期間があるんです。はい。関数内の自動変数(よーは関数内でふつーに宣言しちゃったやつらは、関数が生きてるうちしかないですね。(スタックにある変数なんで)。 一方、グローバルな変数は、プログラムが終了するまで生き延びます。んで、中には格納場所が移動してあるく奴もいます。(特に、自動変数とか))
    • このLocation Expressionsには、さらに以下2種類があります。
      • 単純配置表現 (Simple Location Expressions)
        • オブジェクトの全ての部分が、一箇所に固まって格納されているもの。(詳細は、次の節参照)
        • 通常の変数は、これ。
      • 復号配置
        • オブジェクトのある部分と、別の部分が、それぞれ別の場所(例えば、レジスタとメモリ、とか)に引き離されて突っ込まれているもの。(構造体、とか?)
        • これも、次の次の節参照。
  • 2. location lists
    • (オブジェクトの)ライフタイムが限られていたり、ライフタイム内で格納場所が変化するオブジェクトの表現に使うパターン
    • どうやら、この表現に.debug_locの情報が使われるもようで、.debug_info内では「loclistptr」(.debug_locの先頭からのオフセット)で表現される
    • 詳しくは.debug_locセクションの構造(LocationLists)を見てね。

以下では、まずLocation Descriptionを見て行くための基礎情報がまだあるのでそれを見て、から、「Location Expressions」を見て行きます。
で、「location lists」は次のページに書きますです。


単純配置表現(Simple Location Expressions)

※英語では「Simple Location Expressions」って名付けられています。てきとーな翻訳用語が見当たらないんで「単純配置表現」と勝手に命名です。

「単純配置表現」は、(あるオブジェクトの)値が(メモリ上の)一箇所に連続して配置されているものの配置場所の表現方法、です。 これ、具体的には以下のいずれかの場合にそー呼ぶみたいです。

  • 1. 値(変数など)がメモリ上のアドレスにあるもの
    • DWARF expression として表現されている
    • よーは、ふつーのデータ格納アドレス(ポインタ)ですね。
  • 2.ソースコード上には存在するが、(恐らく最適化コンパイルによって消されたため)バイナリファイル上存在しないオブジェクト(変数など)
    • DWARF expressionsとしての命令はもたない(オブジェクトが消されているので)
  • 3.レジスタ上に格納されているオブジェクト(変数など)
    • DWARF expressionsの表現としては、上記のレジスタ名命令単独で表現され、他の命令はもたない。

なお、構造体のメンバの格納場所を示す場合の注意が1つ。
構造体のメンバのLocation expressionを評価するまえには、構造体自体の格納アドレス(よーは構造体データの先頭アドレス)がDWARFスタックに(暗黙のうちに)つっこまれたこと、する決まりがあります。

ってみても、これよーわからんです。と思ってたら原文にサンプルがあったんで、以下で紹介

Hexデータ列 命令(文字列スタイル) せつめい
0x53 DW_OP_reg3 値はレジスタ3に格納されていることを示します
0x90 0x36 DW_OP_regx 54 値はレジスタ54に格納されているよ
0x03 0x5c 0x04 0xd0 0x80
(32bit)
DW_OP_addr 0x80d0045c 値はアドレス0x80d0045cに格納されていますです
0x7b 0x2c DW_OP_breg11 44 値はレジスタ11に格納されているアドレス(ベースレジスタ)に44のオフセットを加えた場所に格納されております
0x91 0xce 0x7f DW_OP_fbreg -50 (前提)この時、.debug_info内で、DW_AT_frame_baseが"DW_OP_breg31 64"だったとします
値は、FrameBaseのアドレス、すなわちreg31+64のアドレスから、-50した場所、つまり(reg31+64)-50=reg31+14のアドレスに格納されています
(あ、-50のsLEB表現のバイト0xce 0x7fは暗算エンコードなんで、間違ってたらゴメンです)
0x92 0x36 0x20 0x06 DW_OP_bregx 54 32 DW_OP_deref レジスタ54の中身にオフセット32を加えた値をアドレス値としてDWARFスタックに一旦押し込み、それを一旦引き出して、アドレスとみなして、そのアドレスの中にある値(アドレス)に、目的の値は格納されてます。(ややこしー)
0x23 0x04 DW_OP_plus_uconst 4 (前提)このLocation expressionsは構造体のメンバの格納場所を指し示すものとします。
構造体なので、構造体自体の格納アドレスはDWARFスタックに突っ込まれているもの、としますです
DWARFスタックの一番上の値に4を足したアドレスに、目的の値は格納されているのですね


原文よんでも、訳ワカメさんなんですが、この例をみるとなんとなく、ってとこだと思いますが、どーですかね。


復号配置

復号配置は、あるオブジェクト(多分、構造体とかビットフィールドとか)の前半Aはメモリに、後半Bはレジスタに、みたく、格納場所が一箇所に固まってないものの格納場所を表現する方法っす。(なお、この格納場所はDWARFスタックの結果からもって来たり、計算して求めることもできないものらしいです)

ので、この復号配置は、以下の形式で表現することで、この問題を回避しているみたいです。

<オブジェクトの一番最初のデータ部分Aの単純配置表現> <復号配置命令(による、データAがオブジェクト内のどの部分かの宣言)> 。。。(繰り返し)。。。 <オブジェクトの最後のデータ部分Dの単純配置表現> <復号配置命令(による、データDがオブジェクト内のどの部分かの宣言>

ここで、<単純配置表現>は、前の節の通り、DWARF expressionな命令(の固まり)です。
<復号配置命令>は、例えば全部で12バイトのうち、「先頭の4バイトは。。。」、「真中の4バイトは。。。」、「最後の4バイトは。。。」、みたいな感じで場所を示していく役割をもっています。
なお、このように<復号配置命令>で表現するデータの順序は、メモリアドレス上でのアドレスが小さいほうからの順になっています。
んでは、復号配置命令を見て行きます。とは言っても、2つだけしかない。

No 命令 コード 引数1 引数2 ごせつめい
1 DW_OP_piece 0x93 uLEB128
Byte size of piece addressed
- この命令の前にあるDWARF expressionsが示す場所は、格納場所を示す対象のオブジェクト前から何バイト部分であるか、を示す命令です。
この命令の前もDW_OP_pieceなら、この命令によって示されるオブジェクトの部分が最適化によって消されたことになります。

なお、この命令の説明には、なんかまだ英語でぐちゃぐちゃ書いていますが、、、英語力不足のため、何いーたいのか、さっぱりわかりませーん。ただ、以下の例見れは十分いっていることは分かるので、どーでもえーかもなのです。
2 DW_OP_bit_piece 0x9d uLEB128
値のビットサイズ
uLEB128
前のLocation expressionからのビットオフセット
まず、オフセットの解釈は、(この命令の前の)Location expressionの種類によって変わります。
Location expressionが空であれば、このオフセットは何も意味せず、この命令の引数1で指定されたビット部分のデータは「未定義」になる。
Location expression が レジスタ名命令 なら、レジスタの一番下位ビットからのオフセットになる
メモリアドレスなら、DWARFスタックの一番上のアドレス値で示される格納場所からのビットシーケンスになる。なお、ビットの数え方や方向は、言語依存で決まる

この命令は、よーはC言語のビットフィールドを表すためのもの、みたいです。が、どういう順番で表現されるか、はちゃんと実機確認要ですね。


って、出されてもこれも訳完全にワカメさんです。ので、以下にまたまた原文サンプルから例です。

Hexデータ列 命令(文字列スタイル) せつめい
0x53 0x93 0x04 0x51 0x93 0x02 DW_OP_reg3 DW_OP_piece 4 DW_OP_reg10 DW_OP_piece 2 (前提)ある6バイトのオブジェクトは2箇所に分断されて格納されていて、前半は4バイト、後半は2バイトです
前半4バイトはレジスタ3に、後半2バイトはレジスタ10にありますです
0x50 0x93 0x04 0x93 0x04 0x91 0x74 0x03 0x04 DW_OP_reg0 DW_OP_piece 4 DW_OP_piece 4 DW_OP_fbreg -12 DW_OP_piece 4 (前提)ある12バイトのオブジェクトは3箇所に分断されていて、それぞれ4バイトずつ、です
最初の4バイトは、レジスタ0にあるのです。
次の4バイトは、どこにも入ってないんです(1つめと2つめのDW_OP_piece命令の間に、レジスタとかアドレスとかの命令がないんで。これは最適化とかで消し去られたためです。
最後の4バイトは、FrameBaseのアドレスからオフセット-12を足したアドレス(メモリ)にはいってる、です


これ見ると、なんとなく分かってきたですね。
ただ、例がなりDW_OP_bit_piece命令のきょどうは別途調査要っす。(まぁ、.debug_info調査あたりでビットフィールドの例だすと一撃で出せるよーな気がするので、今は気にしない)

ということで、まだまだ不安だらけの直訳オンパレードですが、一旦Location Expressionsはこんなもんでしょうか。
(あ、念のため。このページの例は、ワタクシが愛機ThinkPadに出してもらったバイナリ解析したもんじゃなくって、DWARF3原文の「サンプル」のホンヤクです。)


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