デバッガ:デバッグ情報

デバッガ:デバッグ情報

デバッグ情報

ここではコンパイラやリンカが出力するデバッグ情報について簡単にまとめています。

参考


デバッグ情報に含まれているもの

デバッグ情報に含められている情報の代表的なものとして、

などがあります。

CやC++では言語的なリフレクションが行えないために、内部インスペクションをする場合には必ず何らかの外部情報が必要になります。ありていに言えばこれが「デバッグ情報」と呼ばれているものになります。例えば関数や変数のアドレス、クラスメンバのオフセットなどが含まれます。これはpdbやdbgのように外部ファイルで提供されることもあれば、gccのようにイメージに統合してしまう場合もあります。どちらの場合もアドレスはリロケータブルな形で扱われ、デバッグ開始時に適切な形で読み込まれます。

このデバッグ情報には例えば、「基底クラスのオフセット情報」や「virtual継承した場合の基底クラスのオフセット情報」など、コンパイラの実装に極度に依存した情報が含まれる場合があります。このため、デバッグ情報というのは言語やコンパイラ、下手をするとコンパイラのバージョンによっても詳細情報が変わることがあります。実際、windowsのpdb形式は頻繁にアップデートが行われていますし、それを読み込むためのライブラリ=dbghelpの実装も随時変更されています。というか、今でも絶賛アップデート中です。また、言語的な違いで言えば、例えばC#などのリフレクションを含んだ言語では型情報は根本的に存在する必要ありません。デバッグ情報がなくともリフレクションによって情報を取得できてしまうからです。場合によっては他の不要になる場合もあるでしょう。これはほんとに場合によって異なります。

デバッグ情報にはこういった特徴があるために、詳細なフォーマット情報が提供されることはあまり無いようです。少なくともwindowsの世界ではそうです。その代わりデバッグ情報を扱うライブラリがそういった詳細を隠す形で、各フォーマットに従い適切な処理を行うことが多いです。実際、windows(dbghelp), linux(dwarf)ではこのような実装が行われています。dwarfではバックエンドと呼ばれるレイヤを用意し、各コンパイラや環境間の差異を吸収しています。またこういった事情があるために、デバッグ情報の統一的なフォーマットを作成することが難しくなっています。

シンボル情報へのアクセス

以下にはネイティブデバッガが作る典型的なデータ群を紹介します。

  1. 命令アドレス -> スコープ へのマップ
  2. スコープ -> それを静的に含む親スコープ へのマップ
  3. スコープ + 識別子 -> そのシンボルの型と位置 へのマップ
  4. コードまたはデータのアドレス -> 変数や関数 へのマップ
  5. 命令アドレス -> ソースステートメント へのマップ
  6. ソースステートメント -> 命令アドレス へのマップ

ある調査によると、典型的なデバッグセッションではデバッグ情報全体の約15%程度しか参照されないようです。pdbファイルのサイズを見れば分かりますが(Gaucheの場合、releaseビルドで約3MB)、結構肥大化することがあります。こういう場合の戦略としてはキャッシュが最適です。事前にすべての情報をかき集めるような真似は止めましょう。

典型的なデバッグ情報の内容

ここでは、各デバッグフォーマットの基礎とも言うべきCodeView(C7)形式の概略を示します。実際にはデバッガの理論と実装からのパクリですが、イメージを伝えるため良しとします^^

その前に

PEやELFなどの実行バイナリ形式では、含まれている情報の種類に応じてセクションを分けています。例えば、コードは'.text'セクションに、データは'.data'セクションに入れることで、.textセクションの書き換えだけを不可にすることができます。悪名高い(?)自己書き換えコードの実行もこれで防ぐことができます。しかしながら、.dataセクションにある実行コードが実行できないかといえば、全然そんなことは無くて、実行できるかどうかは多分環境による(?)と思います。

「シンボル情報」や「型情報」などの区切りも、必ずしも100%綺麗に分かれているわけではなくて、まあそんな分類をすると分かりやすいかな程度の分類です。例えばクラスのメンバフィールドなんかは型情報に含まれるのですが、見方によってはシンボルにも見えると思います。

$$SYMBOLS セクション

このセクションには、可変長レコードによりシンボル情報が記されてします。各レコードは最初に自身のレコード長と各識別子をもち、これによって可変長での扱いを可能にしています。関数やブロックはその親スコープも保持します。

$$TYPES セクション

このセクションでは、型情報が記述されています。この保存の仕方は若干複雑で、型同士が葉構造を形成しています。例えば、*int型はint型への参照をお持ち、Class型は各メソッドやフィールドへの参照を持ちます。

他の情報

デバッグ情報には他にも、アドレスとソースステートメントの対応をつけるための情報などが含まれています。

More ...