おしながき

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_lineセクションの構造と読み方

ヘッダ編からずいぶん長くなっちゃったので、分割。

んで、次に中身------------

さてさて、ようやっと、中身ですな。んで、これが厄介。 ので、ちょっとずずメモメモ。
  • まず概要
    • ヘッダの後は、Cソースとアセンブラの関係を示す「命令」がバイト単位で並んでる。
    • DWARFでの、Cソースとアセンブラの関係は、簡単に言うと「バイトな命令で書かれたDWARF言語でのプログラム」で表現されている。
    • このプログラムは、DWARFが勝手に決めた「DWARFなんちゃってコンピュータ?」の上で動作する。(もちろん、仮想な世界)
    • この「DWARFなんちゃってコンピュータ」は、レジスタをもっている。(いくつも)
    • このレジスタ、詳細は後述だが、確実に覚えとかにゃらんのは「Cソースの行番号」「アセンブラのアドレス」がある。
    • で、この「DWARF言語の命令」でもって、「Cソースの行番号」「アセンブラのアドレス」をつらつらいじってくわけみたいです。(x86のeipレジスタみたいなもんですね。あ、よーはプログラムカウンタか)
    • すなわち、解析したいなら、またしても「DWARF命令逆アセンブラ」を作らにゃならん。(もぉINTEL64の逆アセンブラでこりごりじゃー)
  • 次にレジスター
    • とりあえず、分かってるレジスタを以下にカキコ
      • "line" : こいつは、Cソースの行番号っす。
      • "address" : こいつは、アセンブラのアドレスです。
      • "file" : 今対象としてる、Cソースのファイル名。ヘッダで決まっちゃうらしいです。
      • "basic_block" : Boolean型 Cソースの複数行がアセンブラの複数行に対応してそれ以上分割無理ってケースの場合の、対応する最初のアセンブラ命令かどうかってフラグみたい。Breakpoint向けか?
      • "prologue_end" : Boolean型 関数にBreakpointを仕掛けた場合の、関数内の最初のアセンブラ命令の1つ前、の時Trueっぽい。それ以外は全部false <2011/10/20判明追記>
      • "epilogue_begin" : Boolean型 関数内の最後の命令の「1つ次」のアセンブラ命令ならTrueらしい。関数から戻る際には、コンパイラさんが泣けて来るほどいろいろ関数脱出のためのお世話してくれますが、このお世話命令の最初を示すためで、やっぱり関数Breakpoint設定時の対応っぽい。<2011/10/20判明追記>
      • "column" : 符号なし整数。ソースコード中の、今着目している列番号が入るレジスタみたい。なお、最初の列番号は1です。(0じゃねーぞって、僕のとっておき(=reserve)使うんじゃねって書いてあります) <2011/10/20判明追記>
      • "isa" : Instruction Set Architecture の略らしい=(今のアドレスの)アセンブラ命令のアーキテクチャ、を示す値。通常、1つのCPUでは1つのアーキテクチャ(アセンブラの文法)だから、0固定だよねきっと。(でもPlayStation3なcellとかNintedoDSとか、ヘテロジニアスマルチコアCPUはこれ値持つのかな?) <2011/10/20追記>
      • 他にもあるっぽい。(鋭意規格書探検中)
  • んでもって、文法?
    • まず、命令には"Special Opcodes君"と"Standard Opcodesちゃん"の2種類ある。
    • "Special Opocodes君"
      • これこそが、Cソースとアセンブラの対応、すなわち、レジスタ"line"/"address"をいじくりまわす=値を足しまくる命令。
      • "Special=特別"とか言ってるけど、実はこいつはフツーの命令。
      • で、凄いのが、コレ!なんと、この"Special Opcodes"は、ヘッダの"line_base","line_range","opcode_base"の値から、Cソースファイル毎に計算式で命令作っちゃてるのです。
      • 数式はコレ。
        • "Special Opcodes" = ( Cソースの行数増分 - "line_base" ) + ( "line_range" * アセンブラアドレスの増分 ) + "opcode_base"
        • 要は、例えば、
            • Cソースは今13行目の intvalue = 5; な行として、次の行7行目に移ったとする。(6行目はお決まり?の空行)
              • この場合、"line"=5 から 7 になるので、Cソースの行数増分=2じゃ。
            • アセンブラアドレスの増分は、call 0x80040564 の命令から、mov rax,r16へ移るとして、5バイト増えるとする。(callが1byteかどうか正確には忘れたが....)
              • この場合、"address" = 0x80040564 から、0x80040569 へ5Byte増える。
            • "line_base"=2, "line_range"=8, "opcode_base"=13 (0dh), "minimun instruction length" = 1とする。
            • この場合の命令Byte = ( 2 - 2 ) + ( 8 * 5 ) + 0dh = 0 + 28h + 0dh = 35h になる。
            • すなわち、中身に35hが登場したら、その時は、Cのソースを2行進めて、アセンブラは5バイト進めるってことになる。(逆算)
      • 良く考えてるよねこれ。(実は、値設定次第では、なんとアセンブラを戻すこともできる。これはCPU投機実行対策。ま、CPUだってたまには投げやりに仕事したいだろーから許してあげよう)
      • ホンで、逆算する場合の式です。(頭のえー人は上の式で逆算できるんかもね。僕はむりー。
        • アセンブラアドレス増分 = ( ( 中身のByte - "opcode_base" ) / "line_range" ) * "minimum_instruction_length"
        • Cソース行数増分 = "line_base" + ( ( 中身のByte - "opcode_base" ) % "line_range" ) ※ %は割算余りっす。
        • 物は試し。検算です。
          • アセンブラアドレス増分 = ( ( 35h - 0dh ) / 8 ) * 1 = 5 ! (せーかい)
          • Cソース行数増分 = 2 + ( ( 35h - 0dh ) % 8 ) = 2! (せーかい)
    • "Standard Opcodesちゃん"
      • 名前は「標準」なんだけど、こいつらこそある意味、特殊な命令
      • 0x01〜"opcode_base"-1 の値までに、それぞれ命令が割り振られていて、命令コードは固定です。
      • ということで、しゃーないか、イッコづつ見るのだ。
        • DW_LNS_copy : 命令コード:0x01、引数: ありませーん
          • 規格書には「今のレジスタ値でもって(Cソースとアセンブラアドレスの関係表に)1行追加する。さらに"basic_block"、"prologue_end""epilogue_begin"をfalseにする」とあるが、実際どー振舞ってくれるのかが、意味不明。
          • なんか、ダミー命令っぽいんかなぁ。これ。
        • DW_LNS_advance_pc : 命令コード: 0x02、引数: uLEB128
          • これは簡単。アセンブラアドレス"address"に対して、引数uLEB128 * "minimun_instruction_length" 分を強制的に加える命令。要するに、アセンブラアドレスだけ強制的に進めたいときのおまじない。
        • DW_LNS_advance_line : 命令コード: 0x03、引数: sLEB128
          • これも簡単。今度は、Cソースの行数"line"に、引数の値を足す。ポイントは、sLEB128=signed! なことで、Cソースは投機実行のせいで前に戻ることもできるよ(負数もあり)ってこと。
        • DW_LNS_set_file : 命令コード: 0x04、引数: uLEB128
          • これは、今着目しているファイルの番号を操作する時に使うもので、レジスタ"file"に引数uLEBを格納する。
          • ちなみに、ファイルの番号は、ヘッダのおケツのファイル名指定で複数ファイル名指定があったときの番号らしいです。
          • これの引数"unsigned"でした+引数はレジスタに足すのではなく、そのまま格納じゃった(2011/10/30修正)
        • DW_LNS_set_column : 命令コード : 0x05、引数: sLEB128
          • レジスタ"column"に引数の値を格納する命令。
          • 要するに、Cソースの着目する列番号操作できるみたい。(でも何に使うんだろCの場合。。。。COBOLとか向けなのかな)
          • これも、引数はunsignedで、レジスタ足しではなく、「格納」でした。。。(2011/10/30修正)
        • DW_LNS_negate_stmt : 命令コード : 0x06、引数: なし
          • 今のアセンブラアドレスはBreakpointになれるかどうかを示す"stmt"レジスタの値を逆にする命令。今falseならTrueになって、Breakpoint張ってもえーよって教えてくれる。(逆もしかり)
        • DW_LNS_set_basic_block : 命令コード 0x07、引数: なし
          • レジスタ"basic_block"の値を強制的に"True"にして、複数ソース行が複数アセンブラ命令に対応しているケースの場合に発行して、先頭を示すのだ。じゃ、どうやってfalseにするんだ!これはSpecialOpcodesは発行したら全部falseになることでやるのです。
        • DW_LNS_const_add_pc : 命令コード : 0x08、引数: なし
          • これはちと複雑で、「上のSpecial Opcodeが0xff(255)の時に"address"に加算される値」*「"minimum_instruction_length"」で計算される値を、レジスタ"address"に足し込む命令。でも目的は簡単で、Special Opcodesで対応できる"address"の増加分よりももっと大きな値を一時的に"address"に足したくなった場合、この命令をつかって増やせるってこと。
        • DW_LNS_fixed_advance_pc : 命令コード : 0x09、引数: uhalf = unsigned short(16bit)
          • レジスタ"address"に引数uhalfの値を強制的に足し込みます。意味的にはDW_LNS_advance_pcとおんなじ。(表現方法の違い)
        • DW_LNS_set_prologue_end : 命令コード : 0x0a、引数: なし
          • レジスタ"prologue_end"にTrueをセットします。意味はレジスタ説明の欄で。ちなみに、falseにするのはSpecial Opcodes発行でなっちゃいます。
        • DW_LNS_set_epilogue_begin: 命令コード: 0x0b、引数: なし
          • レジスタ"epilogue_begin"をTrueにします。意味はレジスタ説明欄で。
        • DW_LNS_set_isa : 命令コード: 0x0c、引数: uLEB128
          • レジスタ"isa"に、引数のuLEB128を足し込む命令。通常は使わないっぽいね。

(2011/10/30 ちょろっと修正)

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