windowsでデバッグ情報を扱う場合には、このdbghelpというライブラリを使います。シンボル情報の読み込みや型情報の取得などはすべてこのライブラリが担っています。
このライブラリには多少扱いづらいところがあるため、一番最初にはまりやすい注意事項について書いてあります。
参考
実は、似たようなライブラリとしてimagehlpというのもあるのですが、これは古いバージョンのdbghelpだと考えていいでしょう。昔はimagehlpといえばデバッグ情報やイメージ情報を統合的に扱うライブラリだったのですが、ちょっと巨大すぎるということで一部の分割&切り出しが行われる予定でした。dbghelpがデバッグ情報部分のみを扱うライブラリになるはずだったのだと思います。名前にもそういう雰囲気がありますね。
現在のimagehlpとdbghelpを比較する限り、両者はバージョンが違う同じライブラリです。dllの中身もヘッダファイルの内容もほぼ同じです。ヘッダファイルの場合、APIの内容が同じでもインクルードガード部分が違うので、両者を同じプロジェクトで使うと確実にエラーを引き起こします。これは深刻な問題ですので、windowsではimagehlpは使わずに必ずdbghelpを使うようにしましょう。
dbghelpは今でも頻繁なアップデートが繰り返されています。理由は違うページにも書いてありますが、本質的にデバッグ情報はコンパイラやリンカと共にあるものだからです。このため古いOSにデフォルトで付属されているものを使ってしまうと、満足な結果が得られない場合があります。新しい関数が使えないのはもちろん、場合によっては正しくエクスポートされている古い関数ですら失敗することがあります。例えばwindows2000では、ほとんどのシンボル関連関数が引数によらず失敗します。
そのためdbghelp.dllについては、必ず新しいバージョンのdllを使う必要があります。ダウンロードは
などから行うことができます。ところが理由は不明なんですが、バージョンを調べるための関数ImagehlpApiVersionの値は当てになりません。dbghelp.dllが正しいバージョンかどうかは、比較的新しい関数をエクスポートしているかどうかで判断するとよいでしょう。
具体的には、このライブラリを使う場合は、
ということを守る必要があります。少し(?)面倒なんですが、これをしない場合、「WinXPでもWin2000でも初期化には成功するが、なぜかWin2000だと動かない」といった非常に分かりにくいバグを生む原因になります。気をつけましょう。
ローカル変数情報が欲しい場合には、SymEnumSymbolsを行う前にSymSetContextで関数アドレスを指定します。するとローカル変数の一覧を列挙することができます。
関数名の最後に"(64)"とついているものは、64ビット対応の関数が存在するという意味です。32ビットコンピュータでも、たまに64ビットバージョンを使った方がいい場合もあるので、可能な限り常にこちらを使うようにしましょう。ちなみに、ついていないものは最初から32/64ビットのどちらにも対応しているAPIです。
具体的な扱い方についてはmsdnやpdbdumpのソースを参考にしてください。
型情報は、SymGetTypeInfoAPIで取得できます。ただし、型情報を構成する葉構造は少し分かりにくいと思うので軽く紹介しておきます。
詳しくはこちらHow to use DbgHelp (SymGetTypeInfo) to access type information
ざっくりといえば、以下のような感じで型が葉構造を形成しています。「次の型」というのがなんともいえないのですが、型を示す上で必要な情報がリスト状、もしくは木構造となっていて、デバッガはこの情報から型を文字列に変えたりします。例えば、int*型はint型へのリンクをもっています。また、関数型は仮引数を子要素としてもっています。かなり恣意的な構造なので、気をつけましょう。
|-------------------------------------| | Tag(型の大まかな種類) | |-------------------------------------| | Index(型のインデックス) | |-------------------------------------| | (Next)Type (型リスト上の「次の型」) | | Name (型名) | |-------------------------------------|
int i; |-----------------| |------------------| | Data | | BaseType | |-----------------| |------------------| | Index = 15 | (Next)Type | Index = 16 | |-----------------| -----------> |------------------| | (Next)Type = 16 | | BaseType = btInt | | Name = "i" | | Length = 4 | |-----------------| |------------------|
int *p; |-----------------| |-----------------| |------------------| | Data | | PointerType | | BaseType | |-----------------| (Next)Type |-----------------| |------------------| | Index = 15 | -----------> | Index = 16 | (Next)Type | Index = 17 | |-----------------| |-----------------| -----------> |------------------| | (Next)Type = 16 | | (Next)Type = 17 | | BaseType = btInt | | Name = "p" | | Length = 4 | | Length = 4 | |-----------------| |-----------------| |------------------|
int array[3][4]; |-----------------| |-----------------| |------------------| |------------------| | Data | | ArrayType | | BaseType | | BaseType | |-----------------| (Next)Type |-----------------| |------------------| |------------------| | Index = 15 | -----------> | Index = 16 | (Next)Type | Index = 17 | | Index = 18 | |-----------------| |-----------------| -----------> |------------------| (Next)Type |------------------| | (Next)Type = 16 | | (Next)Type = 17 | | (Next)Type = 18 | -----------> | BaseType = btInt | | Name = "array" | | Length = 48 | | Length = 16 | | Length = 4 | |-----------------| | Count = 3 | | Count = 4 | |------------------| |-----------------| |------------------|
int CText::DoText(long Num); |------------------------| |--------------------| |----------------------| | Function | | FunctionType | | UDT(UserDefinedType) | |------------------------| (Next)Type |--------------------| |----------------------| | Index = 15 | -----------> | Index = 16 | ClassParent | Index = 18 | |------------------------| |--------------------| -----------> |----------------------| | (Next)Type = 16 | | (Next)Type = 17 | | Name = "CTest" | | Name = "CTest::DoTest" | | ClassParent = 18 |------- |----------------------| |------------------------| | Count = 2 | | (これは本当はメソッドリストなどを持ちます) |--------------------| | ↑ | (Next)Type | | | parent/child | |------------------| | |-------| BaseType | ↓ |------------------| |-------------------| | Index = 17 | | FunctionArgType | |------------------| |-------------------| | BaseType = btInt | | Index = 19 | | Length = 4 | |-------------------| |------------------| | (Next)Type = 20 | (これが戻り値となります) |-------------------| (仮引数が複数あれば、この要素は複数存在します) ↑ | | (Next)Type ↓ |-------------------| | BaseType | |-------------------| | Index = 17 | |-------------------| | BaseType = btLong | | Length = 4 | |-------------------|
サンプルとして、pdbに含まれているシンボル情報と型情報をそのまま出力するツールを作りました。何かの参考になれば幸いです。
使い方 pdbdump [イメージファイル名(.exe .dll など)] とすると、そのイメージがあるディレクトリに 1, [inputname].symbols.xml 2, [inputname].types.xml という二つのファイルが作られます。 1,のファイルがシンボル情報を、2,のファイルが型情報を含んでいます。 出力がxmlなのは単なる趣味です。 オプションを全く用意していないため日常的に使うツールとしては不十分ですが、 サンプルとしては十分でしょう。
BOOL SymInitialize(HANDLE hProcess, PSTR UserSearchPath, BOOL fInvadeProcess)
BOOL SymCleanup(HANDLE hProcess)
DWORD SymLoadModule(HANDLE hProcess, HANDLE hFile, PSTR ImageName, PSTR ModuleName, DWORD BaseOfDll, DWORD SizeOfDll)
BOOL SymUnloadModule(HANDLE hProcess, DWORD BaseOfDll)
BOOL SymGetTypeInfo(HANDLE hProcess, DWORD64 ModBase, ULONG TypeId, IMAGEHLP_SYMBOL_TYPE_INFO GetType, PVOID pInfo)
BOOL SymGetLineFromAddr(HANDLE hProcess, DWORD dwAddr, DWORD *pdwDisplacement, IMAGEHLP_LINE *Line)