パブリック シンボルとプライベート シンボル

フルサイズの .pdb または .dbg シンボル ファイルがリンカーによって構築されると、そのファイルにはプライベート シンボル データとパブリック シンボル テーブルという 2 つの異なる情報コレクションが含まれます。 これらのコレクションは、含まれるアイテムのリストと各アイテムについて保存される情報が異なります。

プライベート シンボル データには次の項目が含まれます。

  • 関数

  • グローバル変数

  • ローカル変数

  • ユーザー定義の構造体、クラス、およびデータ型に関する情報

  • ソース ファイルの名前と、各バイナリ命令に対応するファイル内の行番号

パブリック シンボル テーブルに含まれる項目は少なくなります。

  • 関数 (静的に宣言された関数を除く)

  • extern として指定されたグローバル変数 (および複数のオブジェクト ファイルにわたって表示されるその他のグローバル変数)

一般に、パブリック シンボル テーブルには、あるソース ファイルから別のソース ファイルにアクセスできる項目が正確に含まれます。 静的関数、単一のソース ファイル内でのみグローバルな変数、ローカル変数など、1 つのオブジェクト ファイル内でのみ表示される項目は、パブリック シンボル テーブルには含まれません。

これら 2 つのデータのコレクションは、各項目に含まれる情報も異なります。 通常、プライベート シンボル データに含まれる各項目には次の情報が含まれます。

  • アイテムの名前

  • 仮想メモリ内の項目のアドレス

  • 各変数のデータ型、構造体、関数

  • 各関数のパラメータの種類と名前

  • 各ローカル変数のスコープ

  • 各ソースファイルの各行に関連付けられたシンボル

  • スタックへのアクセスに使用される各関数のフレーム ポインター省略 (FPO) レコード

一方、パブリック シンボル テーブルには、そこに含まれる各項目に関する次の情報のみが保存されます。

  • 項目の名前。

  • モジュールの仮想メモリ空間内の項目のアドレス。 関数の場合、これはそのエントリ ポイントのアドレスです。

  • 各機能のフレーム ポインター省略 (FPO) レコード。

  • 装飾と呼ばれるシンボルの接頭辞/接尾辞が含まれる場合があります。

パブリック シンボル データは、2 つの点でプライベート シンボル データのサブセットとして考えることができます。1 つは短いアイテムのリストが含まれること、もう 1 つは各アイテムに関する情報が少ないことです。 たとえば、パブリック シンボル データにはローカル変数がまったく含まれません。

各ローカル変数は、そのアドレス、データ型、スコープとともにプライベート シンボル データにのみ含まれます。 一方、関数はプライベート シンボル データとパブリック シンボル テーブルの両方に含まれますが、プライベート シンボル データには関数名、アドレス、FPO レコード、入力パラメーターの名前とタイプ、出力タイプが含まれるのに対し、パブリック シンボル テーブルには 関数名、アドレス、および FPO レコードのみが含まれます。

プライベート シンボル データとパブリック シンボル テーブルの間には、もう 1 つの違いがあります。 パブリック シンボル テーブル内の項目の多くは、接頭辞、接尾辞、またはその両方で修飾された名前を持っています。 これらの装飾は、C コンパイラー、C++ コンパイラー、および MASM アセンブラーによって追加されます。 一般的なプレフィックスには、一連のアンダースコアまたは文字列 __imp_ (インポートされた関数を指定) が含まれます。 一般的なサフィックスには、1 つ以上のアットマーク ( @) と、それに続くアドレスまたはその他の識別文字列が含まれます。 これらの装飾は、関数名またはグローバル変数名が異なるモジュール間で繰り返される可能性があるため、シンボルの曖昧さを解消するためにリンカによって使用されます。 これらの装飾は、パブリック シンボル テーブルがプライベート シンボル データのサブセットであるという一般規則の例外です。

完全なシンボル ファイルとストリップされたシンボル ファイル

完全なシンボル ファイルには、プライベート シンボル データとパブリック シンボル テーブルの両方が含まれます。 この種のファイルはプライベート シンボル ファイルと呼ばれることもありますが、このようなファイルにはプライベート シンボルとパブリック シンボルの両方が含まれるため、この名前は誤解を招きます。

ストリップされたシンボル ファイルは、パブリック シンボル テーブルのみ、または場合によってはパブリック シンボル テーブルのサブセットのみを含む小さなファイルです。 このファイルはパブリック シンボル ファイルと呼ばれることもあります。

完全なシンボル ファイルとストリップされたシンボル ファイルの作成

Visual Studio を使用してバイナリをビルドする場合は、完全なシンボル ファイルまたはストリップされたシンボル ファイルを作成できます。 ストリップされたシンボルの構築については、「/PDBSTRIPPED (プライベート シンボルのストリップ)」を参照してください。

BinPlace ツールを使用すると、完全なシンボル ファイルからストリップされたシンボル ファイルを作成できます。 最も一般的な BinPlace オプション (-a -x -s -n) が使用される場合、除去されたシンボル ファイルは -s スイッチの後にリストされているディレクトリに配置され、完全なシンボル ファイルは-n スイッチの後にリストされているディレクトリに配置されます。 BinPlace がシンボル ファイルを削除すると、ファイルの削除されたバージョンと完全なバージョンには同一の署名とその他の識別情報が与えられます。 これにより、デバッグにどちらのバージョンも使用できるようになります。 BinPlace の詳細については、「BinPlace」を参照してください。

PDBCopy ツールを使用すると、プライベート シンボル データを削除して完全なシンボル ファイルからストリップ シンボル ファイルを作成できます。 PDBCopy は、パブリック シンボル テーブルの指定されたサブセットを削除することもできます。 詳細については、「PDBCopy」を参照してください。

SymChk ツールを使用すると、シンボル ファイルにプライベート シンボルが含まれているかどうかを確認できます。 詳細については、「SymChk」を参照してください。

デバッガでのパブリック シンボルとプライベート シンボルの表示

WinDbg、KD、または CDB を使用してシンボルを表示できます。 これらのデバッガの 1 つが完全なシンボル ファイルにアクセスできる場合、プライベート シンボル データにリストされている情報とパブリック シンボル テーブルにリストされている情報の両方が含まれます。 公開シンボルデータにはシンボル装飾が含まれています。

プライベート シンボルにアクセスする場合、プライベート シンボル データは常に使用されます。これは、これらのシンボルがパブリック シンボル テーブルに含まれていないためです。 これらのシンボルは決して装飾されません。

.symopt (シンボル オプションの設定) コマンドを使用すると、デバッガーによるパブリック シンボルとプライベート シンボルの使用方法を決定するシンボル オプションを制御できます。 たとえば、このコマンドはシンボルのデバッグ情報をオンにします。

 .symopt+ 0x80000000

次のオプションは、デバッガでのパブリック シンボルとプライベート シンボルの使用方法を変更します。

  • SYMOPT_UNDNAME オプションがオンの場合、パブリック シンボルの名前が表示されるときに装飾は含まれません。 また、シンボルを検索する場合、装飾は無視されます。 このオプションをオフにすると、パブリック シンボルを表示するときに装飾が表示され、装飾が検索に使用されます。 プライベートシンボルはいかなる状況においても装飾されることはありません。 このオプションは、すべてのデバッガでデフォルトでオンになっています。

  • SYMOPT_PUBLICS_ONLY オプションがオンの場合、プライベート シンボル データは無視され、パブリック シンボル テーブルのみが使用されます。 このオプションは、すべてのデバッガーでデフォルトでオフになっています。

  • SYMOPT_NO_PUBLICS オプションがオンの場合、パブリック シンボル テーブルは無視され、検索とシンボル情報ではプライベート シンボル データのみが使用されます。 このオプションは、すべてのデバッガーでデフォルトでオフになっています。

  • SYMOPT_AUTO_PUBLICS オプションがオンの場合 (SYMOPT_PUBLICS_ONLY と SYMOPT_NO_PUBLICS の両方がオフの場合)、最初のシンボル検索はプライベート シンボル データで実行されます。 目的のシンボルがそこで見つかった場合、検索は終了します。 そうでない場合は、公開シンボル テーブルが検索されます。 パブリック シンボル テーブルにはプライベート データ内のシンボルのサブセットが含まれているため、通常、これによりパブリック シンボル テーブルは無視されます。

  • SYMOPT_PUBLICS_ONLY、SYMOPT_NO_PUBLICS、および SYMOPT_AUTO_PUBLICS オプションがすべてオフの場合、シンボルが必要になるたびに、プライベート シンボル データとパブリック シンボル テーブルの両方が検索されます。 ただし、両方の場所で一致が見つかった場合は、プライベート シンボル データ内の一致が使用されます。 したがって、このインスタンスの動作は、SYMOPT_AUTO_PUBLICS を使用するとシンボル検索がわずかに高速になる可能性があることを除いて、SYMOPT_AUTO_PUBLICS がオンの場合と同じです。

以下は、コマンド x (シンボルの検査) を 3 回使用する例です。 初めて、デフォルトのシンボル オプションが使用されるため、情報はプライベート シンボル データから取得されます。 これには、配列 typingString のアドレス、サイズ、データ型に関する情報が含まれることに注意してください。 次に、コマンド .symopt+ 4000 が使用され、デバッガーがプライベート シンボル データを無視します。 x コマンドを再度実行すると、パブリック シンボル テーブルが使用されます。 今回は、typingString のサイズとデータ型の情報がありません。 最後に、コマンド .symopt-2 が使用されます。これにより、デバッガーに装飾が組み込まれます。 最後に x コマンドを実行すると、関数名の装飾バージョンである _typingString が表示されます。

0:000> x /t /d *!*typingstring* 
00434420 char [128] TimeTest!typingString = char [128] ""

0:000> .symopt+ 4000

0:000> x /t /d *!*typingstring* 
00434420 <NoType> TimeTest!typingString = <no type information>

0:000> .symopt- 2

0:000> x /t /d *!*typingstring* 
00434420 <NoType> TimeTest!_typingString = <no type information> 

DBH ツールを使用したパブリック シンボルとプライベート シンボルの表示

シンボルを表示するもう 1 つの方法は、DBH ツールを使用することです。 /?オプションを使用してヘルプ オプションを表示します。

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64>dbh /?
dbh dbghelp shell
usage: dbh [-n] [-c] [-d] [-?] [-??] [-p] [targetmodule] [command]
       [-n]             display noisy symbol spew
       [-d]             use decorated publics
       [-p:XXXX]        attaches to process ID XXXX
       [-s:SSSS]        set symbol path to SSSS
       [-c]             callbacks return false
       [targetmodule]   load symbols for specified module
       [command]        execute command and exit
       [-?]             display these usage instructions
       [-??]            display detailed usage instructions

Tlist などのツールを使用してプロセス ID をリストし、-p オプションを使用して既存のプロセスにアタッチします。

C:\Program Files (x86)\Windows Kits\10\Debuggers\x64>dbh -p:4308

DBH は、デバッガと同じシンボル オプションを使用します。 デバッガーと同様に、DBH は SYMOPT_PUBLICS_ONLYSYMOPT_NO_PUBLICS をデフォルトでオフのままにし、SYMOPT_UNDNAMESYMOPT_AUTO_PUBLICS をデフォルトでオンにします。 これらのデフォルトは、コマンドライン オプションまたは DBH コマンドによって上書きできます。

ここでは、DBH コマンド addr 414fe0 を 3 回使用する例を示します。 初めて、デフォルトのシンボル オプションが使用されるため、情報はプライベート シンボル データから取得されます。 これには、関数 fgets のアドレス、サイズ、データ型に関する情報が含まれることに注意してください。 次に、コマンド symopt +4000 が使用されます。これにより、DBH はプライベート シンボル データを無視します。 その後、addr 414fe0 が再度実行されると、パブリック シンボル テーブルが使用されます。 今回は、関数 fgets のサイズとデータ型の情報はありません。 最後に、コマンド symopt -2 が使用されます。これにより、DBH に装飾が組み込まれます。 addr 414fe0 が最後に実行されると、関数名 _fgets の装飾されたバージョンが表示されます。

pid:4308 mod:TimeTest[400000]: addr 414fe0

fgets
   name : fgets
   addr :   414fe0
   size : 113
  flags : 0
   type : 7e
modbase :   400000
  value :        0
    reg : 0
  scope : SymTagNull (0)
    tag : SymTagFunction (5)
  index : 7d

pid:4308 mod:TimeTest[400000]: symopt +4000

Symbol Options: 0x10c13
Symbol Options: 0x14c13

pid:4308 mod:TimeTest[400000]: addr 414fe0

fgets
   name : fgets
   addr :   414fe0
   size : 0
  flags : 0
   type : 0
modbase :   400000
  value :        0
    reg : 0
  scope : SymTagNull (0)
    tag : SymTagPublicSymbol (a)
  index : 7f

pid:4308 mod:TimeTest[400000]: symopt -2

Symbol Options: 0x14c13
Symbol Options: 0x14c11

pid:4308 mod:TimeTest[400000]: addr 414fe0

_fgets
   name : _fgets
   addr :   414fe0
   size : 0
  flags : 0
   type : 0
modbase :   400000
  value :        0
    reg : 0
  scope : SymTagNull (0)
    tag : SymTagPublicSymbol (a)
  index : 7f 

追加情報

シンボルの詳細については、「シンボル構文とシンボル マッチング」「シンボル オプション」「シンボル ステータスの略語」、および「シンボルの遅延読み込み」を参照してください。