おしながき

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な表現:その1

DWARF expressionって何?

DWARFフォーマット内で、(デバッグ中の)値の計算方法やデバッグ対象の関数や変数のアドレスの表現方法です。

(雑談: 原文に "DWARF expressions describe how to compute a value or name a location"ってあって、nameは名前って名詞、って思い込んで訳そうとして、偉いくろーしました。これ、辞書ったら、nameにはなんと「指定する」なんて動詞用法もあるっぽいです。こんなん分かるか!)
といってても、まぁぼやっとしか分からんです。が、.debug_infoの解読には必ず突破要のもので、かつわからーんて言ってても何にもすすまんので、とにかくまずは読み進めて、メモっていきます。
ので、このページ、しばらく(相当?)な間、適当なメモなんで、読み飛ばしてくださいです。(2013/5/20)

とりあえず、Libgoblinへの実装は終わったので、ちょっとずづ内容ましにしていきます(2019/09/24) でけたーって時が来たら、でけたよーって書きますです。


DWARF expressionの構造 (by DWARF3の2.5頭書き+7.7.1)

  • DWARF expressionはDWARF内部の仮想的にもつスタックの操作で表現するです。
  • スタックは、(推測だけど)マシンのアドレス幅のデータ(=64bitマシンなら8Byte)の値×最大深さ256個分
  • 全てのDWARF expression命令は、まず、<命令opcode>が来て、そのあと0以上の引数をもつ。引数もつかどうかは、命令次第。
  • 一般的な操作は原文2.5章に書かれている。ただ、レジスタが変数の存在場所になるケースは、2.6.1章に記載。
  • DWARF2では、アドレスの計算だけにDWARF expressionを使ってましたが、DWARF3では基本原則として場所の指定にも使います。
  • DWARF expressionは連続するByteブロックなのです。
  • location命令は、1Byteの命令をもつ。このあと、追加のデータが1〜複数バイトふっつく。
  • DWARF expressionの全ての命令は、左から右へ、くっついている。


一般操作の説明 (by 2.5.1)

一般演算命令は、単純なスタックマシンに(値を)つっこむ動作(=push)する
スタックにpushするデータは、ターゲットマシンのアドレスサイズとする。
DWARF expressionの演算実行後のスタックの一番上の値は、オブジェクトのアドレス、配列の値、文字列などの結果になってる。

定数命令

こいつらは、DWARFスタックに値をつっこむ役目みたいです。以下表に、整理

No. 命令名称 命令Byte 引数1 引数2 せつめい
1 DW_OP_lit0 0x30 - - 値 0 をDWARFスタックにつっこむ(push)みたいです。
2 DW_OP_lit1 0x31 - - 値 1 をDWARFスタックにつっこむ(push)みたいです。
: ... ... - - (上記が2,3,4と続く。。。)
32 DW_OP_lit31 0x4f - - 値 31 をDWARFスタックにつっこむ(push)みたいです。
33 DW_OP_addr 0x03 アドレス
ターゲットマシンのアドレスサイズ
- 引数1で指定されたアドレス値をスタックにつっこむみたいです。
引数1のサイズは、動作マシンのアドレスサイズ、すなわち64bitなら8Byteですね。
34 DW_OP_const1u 0x08 1Byte
unsigned char
- 引数1をDWARFスタックにつっこむ(push)みたいです。
35 DW_OP_const1s 0x09 1Byte
signed char
- 引数1をDWARFスタックにつっこむ(push)みたいです。
36 DW_OP_const2u 0x0a 2Byte
unsigned short
- 引数1をDWARFスタックにつっこむ(push)みたいです。
37 DW_OP_const2s 0x0b 2Byte
signed short
- 引数1をDWARFスタックにつっこむ(push)みたいです。
38 DW_OP_const4u 0x0c 4Byte
unsigned int
- 引数1をDWARFスタックにつっこむ(push)みたいです。
39 DW_OP_const4s 0x0d 4Byte
signed int
- 引数1をDWARFスタックにつっこむ(push)みたいです。
40 DW_OP_const8u 0x0e 8Byte
unsigned long
- 引数1をDWARFスタックにつっこむ(push)みたいです。
41 DW_OP_const8s 0x0f 8Byte
unsigned long
- 引数1をDWARFスタックにつっこむ(push)みたいです。
42 DW_OP_constu 0x10 uLEB128 - 引数1をDWARFスタックにつっこむ(push)みたいです。
43 DW_OP_consts 0x11 sLEB128 - 引数1をDWARFスタックにつっこむ(push)みたいです。


レジスタベースドアドレス命令

こやつらは、(指定された)レジスタに与えられた符号付きオフセットを足しこんだ結果(アドレス)をDWARFスタックにつっこむ命令みたいです。

No. 命令名称 命令Byte 引数1 引数2 せつめい
1 DW_OP_fbreg 0x91 sLEB128
offset
- 現在(実行中の)関数のDW_AT_frame_base属性値で表現されるアドレスに引数1のオフセットを足しこんだアドレスをスタックにつっこむ
2 DW_OP_breg0 0x70 sLEB128
offset
- reg0の値に引数1のオフセットを足しこんだアドレスをスタックにつっこむみたいです
: ... ... - - (上記が2,3,4と続く。。。)
34 DW_OP_breg31 0x8f sLEB128
offset
- reg31の値に引数1のオフセットを足しこんだアドレスをスタックにつっこむみたいです
35 DW_OP_bregx 0x92 uLEB128
Reg
sLEB128
offset
引数1で指定されたregに引数2のオフセットを足しこんだアドレスをスタックにつっこむみたいです


スタック操作命令

これらのもんは、DWARFスタックの操作をするもん。なお、スタックの一番のindexを0として説明。

No. 命令名称 命令Byte 引数1 引数2 せつめい
1 DW_OP_dup 0x12 - - スタックの一番上の値を複製する。
とあるが、これ複製した値はいずこへ。。。(多分、スタックの一番上にpushだよきっと)
2 DW_OP_drop 0x13 - - スタックの一番上の値をpopします。つまり、一番上の値を取り出して、上から2番目の値を一番上にするのね。
3 DW_OP_pick 0x15 1Byte
unsigned char
stack index
- 引数1で指定されたスタック番号のデータを、スタックの一番上にpushする。だから、pick(up)するって意味ね
4 DW_OP_over 0x14 - - スタックの上から2番目の値を、スタックの一番上にコピーする。
これは、No. DW_OP_pickで引数1に1を指定したときと同じふるまい
5 DW_OP_swap 0x16 - - スタックの一番上と、上から2番目を逆にする。だけ。
6 DW_OP_rot 0x17 - - スタックの上から3つをグルグルしちゃう。つまり、一番上→3番目に、2番目→一番上に、3番目→2番目にする。
残念ながら、首位陥落する命令
7 DW_OP_deref 0x06 - - これは、ちと複雑。スタックの一番上の値をまずpop(取り出す)→その値を「アドレス」とみなして→そのアドレスのメモリにある値を、スタックの一番上につむ、命令。
なお、みなしたアドレスから値をもって来る際のサイズは、実行しているマシンのアドレス幅でもってくる。よーは、64bitなら8byteもってきてしまう。
8 DW_OP_deref_size 0x94 1 Byte
unsigned char
data size
- これは、No.7 DW_OP_derefと動きはほぼ、同じ。ただし、こっちは、スタックから取り出したアドレス、からちょっぱってくる値のサイズを引数1で指定された値にしちゃう、点が違う。
なお、ちょっぱってきたデータは、スタックに突っ込む前に、実行マシンのアドレスサイズ幅まで、0で拡張してしまう。。。
なお、引数1は、実行マシンのアドレスサイズを越えない、です。
9 DW_OP_xderef 0x18 - - こいつは、No.7 DW_OP_derefの変種。まず、スタックの一番上の値を取り出して、アドレスとしてみなす。(これは、同じ)
そして、上から2番目のスタック(命令開始時点での話、この段階ではイッコ取り出してるので、一番上)も取り出して、「アドレス空間指定子?」とみなす。
そして、この「アドレス空間指定子」と、元スタック一番上の、みなしたアドレス、の組合せから計算される、実際のアドレスにある値をちょっぱって、スタックの一番上につっこむ、命令っす。
さて、「アドレス空間指定子」ってのが、怪しすぎますがね。。。たぶん、これ、「セグメントアドレス」的なもんだと思います。(もしくは、スーパーファミコンなどでいう「バンク指定」みたいな感じ?)
なお、「アドレス空間指定子」とみなしたアドレスから、実際のアドレスを計算する方法は、「実装で定義された計算方法」って書いてあって、特定してません。マシン依存だと。
10 DW_OP_xderef_size 0x95 1 Byte
unsigned char
data size
- これは、No.9のDW_OP_xderefと同じふるまい。
ただし、計算されたアドレスから値引っ張って来る時のサイズは、引数1で指定されます。この指定の考え方は、No.8と同じです
11 DW_OP_push_object_address 0x97 - - (これ、英語よくワカリマセン。。。。)
ユーザが(デバッガ上で与えた)評価式内のオブジェクトのアドレスをスタックに突っ込む。
この「オブジェクト」は、この命令が使われたDIE(Debug Information Entry)か、ユーザ指定の評価式を評価する前段階で動的に(アドレスが)決定される配列/構造体/クラスによって表現される、独立した変数なのであーる。(←規約書、マニュアルの類の英文としては、長すぎじゃー)
なお、こいつはDWARF3新参組選手

あ、あと斜体文字の注釈がこれ原文にありますが、ナニカイテイルカ、ワタシワカリマセーン!(つか、この規約書の全体的な英語レベルから見て、このブロックだけ難解過ぎ→DWARF3新参なんで、そのうちに。)
12 DW_OP_form_tls_address 0x9b - - こいつは、スタック(の一番上)から値をpopして → 現在のスレッドのTLS(Thread-Local Storage)のアドレスに変換して → 変換したアドレスをスタックにつっこむ、命令。
もし、子スレッドじゃなくって、メインのスレッド(もしくは、丸値スレッドにしてない)場合は、メインのスレッドに所属するもの、として処理。
また、もし共有ライブラリのDWARF情報内にこいつがいた場合は、共有ライブラリのTLSのアドレスに変換する。
<注釈>これ、英訳もそのまんま、ですし、書くのも簡単ですが。。。実装どーすんだろ、これ。どうやって変換するんだろ。。。(そーとー大変なよーなー)
あ、これもDWARF3新参
13 DW_OP_call_frame_cfa 0x9c - - Call Frame Information(.debug_frameセクション)から得られるCall Frame Address(CFA)をスタックにpushする命令
これも、DWARF3新参組っす

にしても、いやぁ最後3つの英語はスーパー難解でごわす。(ワタクシには。。。)


算術、論理命令

これらは、タイトルの字のごとく、です。

No. 命令名称 命令Byte 引数1 引数2 せつめい
1 DW_OP_abs 0x19 - - スタックの一番上の値をpop(取り出)して、符号月数と解釈してその絶対値をpushしちゃいます。
もし、絶対値を表現することができなければ、結果は「未定義」です。って、絶対が表現できないってどーいうケースを指しているんだろ?
2 DW_OP_and 0x1a - - 上から2つのスタックをpopして、それらの値を"and"なbit演算をして、結果をスタックにつっこむ、です
3 DW_OP_div 0x1b - - 上から2つのスタックをpopして、上から2番目の値を一番上の値で、符号付きで割算して、結果をスタックにpush
4 DW_OP_minus 0x1c - - 上から2つのスタックをpopして、上から2番目だった値から、一番上の値を引き、結果をスタックにpush
5 DW_OP_mod 0x1d - - 上から2つのスタックをpopして、上から2番目だった値を一番上の値で割った「余り」を結果として、スタックにpush
6 DW_OP_mul 0x1e - - 上から2つのスタックをpopして、2つのスタック値をかけ算して、結果をスタックにpush
7 DW_OP_neg 0x1f - - 一番上のスタック値をpopって、符号を逆にし、またスタックへpush。(マイナスなら、プラスにしちゃう)
8 DW_OP_not 0x20 - - 一番上のスタック値をpop→ビット反転して→またまたスタックへぷーっしゅ!
9 DW_OP_or 0x21 - - 上から2つのスタックをpopして、or演算してすたっくへぷっしゅです
10 DW_OP_plus 0x22 - - 上から2つのスタックをpopして、お互いを足して、結果をスタックへpush
11 DW_OP_plus_uconst 0x23 uLEB128 - 一番上のスタックをpopして、引数1の値を足して、結果をスタックへpush
こいつは、"DW_OP_limX DW_OP_plus"の組で表せられないケースに使うっぽい
12 DW_OP_shl 0x24 - - 上から2つのスタック値をpopって、元2番目のスタック値を元一番上のスタック値のビット数分、左シフトです。で、結果はいつもどおりスタックへまたプッシュ
13 DW_OP_shr 0x25 - - 上から2つスタック値をpopして、元2番目スタック値を元一番上スタック値のビット数分、右シフトします。
このとき、左側は0で埋めて行きます。すなわち、符号は保存されません。
14 DW_OP_shra 0x26 - - 上から2つのスタック値をpopして、元番目スタック値を元一番上スタック値のビット数分、右シフトします。
なお、こっちは、2よりでっかい数値で割る、すなわち1ビットでも右シフトするとき、元2番目スタック値の一番左のビットの値で左を埋めて行きます。
よーは、符号が保存されます。(だから、shraの最後のaはarithmetically、数学的、いかにも?)
15 DW_OP_xor 0x27 - - 上から2つのスタック値をpopして、互いのxorを結果としてスタックにpush


フロー制御命令

って、オーバー?なタイトルになってます(原文も)が、よーはif文とかジャンプとかです。

No. 命令名称 命令Byte 引数1 引数2 せつめい
1 DW_OP_eq 0x29 - - まず、上から2つスタックからpop。で、以下の比較をやって、真なら1をスタックpush。偽なら0をスタックpush
<元2番目スタック値> <元一番上スタック値>
なお、元2番目、一番上スタック値は共に符号付き扱いで判定
2 DW_OP_ge 0x2a - - まず、上から2つスタックからpop。で、以下の比較をやって、真なら1をスタックpush。偽なら0をスタックpush
<元2番目スタック値> <元一番上スタック値>
なお、元2番目、一番上スタック値は共に符号付き扱いで判定
3 DW_OP_gt 0x2b - - まず、上から2つスタックからpop。で、以下の比較をやって、真なら1をスタックpush。偽なら0をスタックpush
<元2番目スタック値> <元一番上スタック値>
なお、元2番目、一番上スタック値は共に符号付き扱いで判定
4 DW_OP_le 0x2c - - まず、上から2つスタックからpop。で、以下の比較をやって、真なら1をスタックpush。偽なら0をスタックpush
<元2番目スタック値> <元一番上スタック値>
なお、元2番目、一番上スタック値は共に符号付き扱いで判定
5 DW_OP_lt 0x2d - - まず、上から2つスタックからpop。で、以下の比較をやって、真なら1をスタックpush。偽なら0をスタックpush
<元2番目スタック値> <元一番上スタック値>
なお、元2番目、一番上スタック値は共に符号付き扱いで判定
6 DW_OP_ne 0x2e - - まず、上から2つスタックからpop。で、以下の比較をやって、真なら1をスタックpush。偽なら0をスタックpush
<元2番目スタック値> != <元一番上スタック値>
なお、元2番目、一番上スタック値は共に符号付き扱いで判定
7 DW_OP_skip 0x2f 2 Byte
signed short
- DWARF expression 内での強制ジャンプ命令(よーするに、goto)
現在の命令(DW_OP_skip)から、引数2の値の分、前か後ろにジャンプ。
(ちょっとホンヤク自身なし→)前に行く場合は現在の命令の箇所から、後ろにジャンプの場合は、引数2の後ろ、すなわち2バイト後ろからカウントする(らしい)
8 DW_OP_bra 0x28 2 Byte
signed short
- 条件判定後の結果を加味したジャンプ命令。
まず、一番上のスタックをpopして、もしpopした値が0じゃなかったら、DW_OP_skipと同様の方法でジャンプする。
popった値が0(偽)なら、なーんにもせんで命令終り
9 DW_OP_call2 0x98 2 Byte
unsigned short
offset of DIE
- (これ、英語ムズカシすぎて、ワカリマッセーン!以下、とりあえずバカな直訳)
この命令は、DWARFexpressionの(式の)評価の制御を、参照されたDIE内のDW_AT_location属性値が指し示すところへ移動させます。
もし参照先DIEにDW_AT_locationがなかったら、意味をなさない。
DW_AT_location内でのDWARF expressionの実行はスタックの値をふやしたり、へらしたりする。
(DW_AT_location属性値先を)呼び出した時のスタックの値は、呼び出したDWARF expressionへの引数となり、呼び出された表現によって残されたスタックの値は、呼び出す側/呼び出された側の事前の合意によって、戻り値として扱われる。
なんか、よーは「関数」のcallであり、DW_AT_locationに呼び出される関数がある、と読めます。ただ、まだDW_AT_locationが未調査なので、これはそこ判明したから補足ですね。
あと、引数2は2Byteの、現在の.debug_info内のCUの先頭からのoffsetで、このoffset先には、演算子を含んでいない実行可能な共有オブジェクトを含んでいるDIE
ちなみに、DWARF3新参選手
10 DW_OP_call4 0x99 4 Byte
unsgined int
offset of DIE
- No.9 DW_OP_call2の引数4byte版。あとは全部No.9とおんなじ。
11 DW_OP_call_ref 0x9a 4 or 8 Byte
unsigned
offset of DIE
- 基本は、No.9のDW_OP_call2と同じ。相違点は以下
引数1は、32bit DWARFなら4byte、64bit DWARFなら8byteのoffset
このオフセットは、.debug_infoセクションの先頭からのオフセット(これが、上2つとは違う)

表にも書きましたが、No.9/10/11は、DW_AT_locationを要調査ですね。


特別命令

No. 命令名称 命令Byte 引数1 引数2 せつめい
1 DW_OP_nop 0x96 - - 読んで字のごとく、なんにも、しません。なまけます。。。じゃなくって、パディング用です。
無視でOK


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