「WDK に付属する symstore.exe で Linux/Android の ELF シンボル(.so の .debug や Breakpad の .sym)を登録したい」──クロスプラットフォームのダンプ解析を内製シンボルサーバーで統一したい現場で、非常に頻出の相談です。本記事では結論と回避策を先に提示し、そのうえで なぜ できないのか、どう作れば 既存の WinDbg や dotnet 系ツールから普通に取れる SSQP 構造になるのかを、手順と例で徹底解説します。
ELFシンボルファイルを symstore.exe に追加できない問題
質問の前提
- 対象のツール:Windows Driver Kit(WDK)に同梱される symstore.exe(例:v10.0.26100.4654 など)
- やりたいこと:
symstore addで Linux/Android 用の ELF シンボル(例:libcoreclr.soの.debug、Breakpad 形式の.sym)を社内シンボルサーバーへ登録 - 要件:プライベート記号や NuGet 由来のアーティファクトを SSQP(Simple Symbol Query Protocol) 互換でホスト
結論と概要
| 結論 | 詳細 | 代替策・補足 |
|---|---|---|
| WDK 版 symstore.exe は ELF を登録できない | サポート対象は Windows の PDB/EXE/DLL に限定。ELF を symstore add に渡すと「未サポート」系のエラーで失敗する。 | 現時点で ELF を登録できる 公式の symstore.exe は提供されていない。 |
| 「ELF をダウンロードできる」≠「symstore で登録できる」 | WinDbg や dotnet-symbol 等は SSQP 形状(<キー>/<識別子>/<ファイル> の固定パス)に配置された ELF を取得・読込できる。 | 登録(配置)自体は 別のツールやスクリプト で行う必要がある。 |
| Python 版の symstore 相当ツールが実用解 | OSS の python‑symstore は Windows/Linux/macOS で動作し、add サブコマンドで ELF を SSQP へ自動配置できる。 | 例:pip install symstoresymstore add -f libcoreclr.so -s \\MySymbolServer -t "CoreCLR" |
| SSQP を手動生成するスクリプトでも可 | ELF の Build ID をキーにし、先頭 2 桁ディレクトリ+残り(例:01/2345.../libfoo.so.debug)の階層でコピーすれば各デバッガが解決可能。 | python‑symstore でカバーしきれないケースも、シンプルなコピー規則だけで運用できる。 |
なぜ WDK の symstore.exe では ELF を登録できないのか
WDK 版 symstore.exe は dbghelp/symsrv スタックに対して Windows 由来のインデックス(PDB の GUID+Age、PE の TimeDateStamp+SizeOfImage など)を前提にメタデータを作成・配置します。ELF や Mach-O は インデックス規則が根本的に異なるため、ファイルを渡しても「対象外」と判定されてしまいます。特に Linux/Android の ELF は Build ID(NT_GNU_BUILD_ID)で同定される慣習があり、Windows の PDB キーとは互換性がありません。
その一方で、WinDbg/dotnet-dump/dotnet-symbol/LLDB などのツール群は SSQP のパス規則に沿って配置されたファイルを検索する機構を持ちます。つまり「配置規則さえ正しければ」取得はできる──問題は「その配置を WDK の symstore.exe では作れない」ことです。
SSQP(Simple Symbol Query Protocol)の最小知識
SSQP は HTTP/ファイル共有経由でシンボルを「鍵付きの固定パス」から取得するための約束事です。代表的なキーとパスの形は次のとおりです(概念図)。
| 対象 | キー | SSQP パス例(概念) | 備考 |
|---|---|---|---|
| PDB | GUID + Age | foo.pdb/<GUIDAge>/foo.pdb | Windows で一般的 |
| PE(EXE/DLL) | TimeDateStamp + SizeOfImage | foo.dll/<TDS><Size>/foo.dll | Win32 バイナリ |
| ELF(本体) | Build ID | elf-buildid/<2桁>/<残り>/libfoo.so | Build ID を 2 桁+残りで分割 |
| ELF(debug ファイル) | Build ID | elf-buildid/<2桁>/<残り>/libfoo.so.debug | .gnu_debuglink を利用する構成でも解決可能 |
| Breakpad シンボル | モジュール ID | foo.sym/<ModuleID>/foo.sym | Crashpad/Breakpad 系 |
要するに、正しいキーを計算し、正しいパスにファイルを置くことがすべてです。配置の作成は必ずしも WDK の symstore.exe に拘る必要はありません。
解決策1:Python 版 symstore(相当ツール)で一気に SSQP 化
もっとも手軽なのは、クロスプラットフォームで動く python‑symstore を使って SSQP 構造へ自動配置する方法です。Windows サーバー上でも Linux ビルドマシン上でも同じコマンドで動かせます。
セットアップ
- Python 3.x を導入(Windows でも Microsoft Store 版や公式インストーラーで可)。
- ビルドマシンまたは登録サーバーで次を実行:
pip install symstore
基本コマンド
ELF を SSQP へ登録:
symstore add ^
-f D:\build\out\libcoreclr.so ^
-s \\fileserver\symbols ^
-t "CoreCLR" ^
-c "Build 2025-11-04 #8421"
-f:登録するファイル(.soや.debug、.sym)。ワイルドカード可。-s:シンボルサーバーのルートパス(UNC/ローカル/マウント先)。-t:製品名タグ。後述のクリーンアップ時に便利。-c:コメント。Git SHA、ビルド番号、ブランチ名などを推奨。
別体の .debug を併せて登録したい場合:
symstore add -f D:\build\out\libcoreclr.so.debug -s \\fileserver\symbols -t "CoreCLR"
Breakpad の .sym を登録:
symstore add -f D:\symbols\breakpad\libfoo.sym -s \\fileserver\symbols -t "AndroidApp"
登録後のディレクトリ例
\\fileserver\symbols\
└─ elf-buildid\
└─ 7b\
└─ 1a2b3c4d5e6f7890abcdef1234567890abcdef\
├─ libcoreclr.so
└─ libcoreclr.so.debug
このように 2桁 + 残り の階層で Build ID に紐づく固定パスが作られます。以降、WinDbg や dotnet-symbol は SSQP 規則に従ってこの場所を参照し、ELF 本体とデバッグ情報を引けるようになります。
解決策2:SSQP 風のフォルダを手作りする(スクリプト例あり)
もし python‑symstore の導入が難しい、あるいは一部のケースで期待する配置にならない場合は、Build ID からパスを作成してコピーするだけのシンプルなスクリプト運用も現実的です。
前提:Build ID の取得
ELF の Build ID は readelf -n や llvm-readelf -n で確認できます。
# Linux/macOS(LLVM ツールチェーン)
llvm-readelf -n ./libfoo.so | grep -i "Build ID"
# もしくは binutils
readelf -n ./libfoo.so | grep -i "Build ID"
Windows でも Build ID を抜く
Windows 上であれば、WSL で上記コマンドを流すか、LLVM for Windows を導入して llvm-readelf.exe を使用します。
PowerShell の一例(SSQP 生成)
$SymRoot = "\\fileserver\symbols"
$Elf = "D:\build\out\libfoo.so"
$Debug = "D:\build\out\libfoo.so.debug"
# 1) Build ID を取得(例:llvm-readelf が PATH にある想定)
$buildId = & llvm-readelf -n $Elf | Select-String -Pattern "Build ID" | ForEach-Object {
($_ -split "Build ID:\s*")[1].Trim()
}
# 2) 先頭 2 桁と残りに分割
$prefix = $buildId.Substring(0,2)
$suffix = $buildId.Substring(2)
# 3) 目的ディレクトリ
$dest = Join-Path -Path $SymRoot -ChildPath "elf-buildid$prefix$suffix"
New-Item -ItemType Directory -Path $dest -Force | Out-Null
# 4) ファイルを配置
Copy-Item $Elf (Join-Path $dest ([System.IO.Path]::GetFileName($Elf))) -Force
Copy-Item $Debug (Join-Path $dest ([System.IO.Path]::GetFileName($Debug))) -Force
Linux/Bash の一例
SYMROOT=/srv/symbols
ELF=./libfoo.so
DBG=./libfoo.so.debug
BUILDID=$(readelf -n "$ELF" | sed -n 's/.*Build ID: ([0-9a-f]+).*/\1/p')
PFX=${BUILDID:0:2}
SFX=${BUILDID:2}
DEST="$SYMROOT/elf-buildid/$PFX/$SFX"
mkdir -p "$DEST"
cp "$ELF" "$DEST/"
cp "$DBG" "$DEST/"
ここまでで SSQP 構造は完成。以降のクライアント設定次第で、ELF とデバッグ情報は自動的に解決されます。
運用 Tips(社内シンボルサーバー構築)
シンプルな公開形態(IIS/Nginx/静的ホスティング)
- ファイル共有+IIS:
\\fileserver\symbolsを IIS の仮想ディレクトリ/download/symbolsとして公開。静的コンテンツが配れる設定で十分です(ディレクトリ一覧は不要)。 - Nginx:
root /srv/symbols;のみでホスト可能。autoindexは無効でもツールはパス直叩きで取得します。 - クラウドストレージ:静的 Web サイト機能やオブジェクトストレージでも OK。HTTP で SSQP パスが引ければ ツールから利用できます。
セキュリティ
- プライベート記号は基本的に 社内 VPN または閉域網 のみで公開。
- 認証が必要な場合は Basic 認証や署名 URL(SAS 等)でアクセスを制御。
- 誤公開対策として「公開先は 読み取り専用」「登録はビルドパイプライン経由でのみ許可」を徹底。
バージョン管理とクリーンアップ
-t(タグ)や-c(コメント)に「ビルド番号/Git SHA/ブランチ」を入れておくと整理が容易。- 保持ポリシー:主要リリースは恒久保持、CI スナップショットは 90〜180 日などの期限で自動削除。
- クリーンアップ時は「参照カウントが 0 のもの」のみ削除する運用に。Crash/ダンプ解析で逆引きできなくなる事故を防げます。
クライアント側の設定(取得する人の観点)
WinDbg / CDB / Visual Studio
Windows のデバッガは環境変数 _NT_SYMBOL_PATH またはセッションごとのシンボルパスで SSQP を参照できます。代表的な指定例:
srv*C:\SymCache*http://intranet.example.com/download/symbols;\\fileserver\symbols
- 左から順に「キャッシュ」「HTTP の SSQP」「フォールバックに UNC」を指定。
- ELF 解決はダンプ解析やクロスプラットフォームデバッグ機能から自動的に行われます。
dotnet 系ツール(例:dotnet-dump / dotnet-symbol)
Linux のコアダンプや libcoreclr.so の記号を引く際も、ホストに SSQP を足すだけで機能します。キャッシュを用意しておくとネットワーク負荷を軽減できます。
LLDB / Crashpad / Breakpad
LLDB は target.binary-path や symbols.load の仕組みで Build ID を基準に探索します。Breakpad/Crashpad はモジュール ID に基づく .sym を引けるよう、SSQP 配下に foo.sym/ModuleID/foo.sym を置いておくと便利です。
ELF 側の前提条件(ビルド時の注意)
- Build ID を必ず埋め込む:多くのツールチェーンで既定有効ですが、念のためリンカに
-Wl,--build-idを渡すか、ビルド設定を確認。 - 分離デバッグ:本体からシンボルを分離する場合は
objcopy --only-keep-debug libfoo.so libfoo.so.debug objcopy --strip-debug libfoo.so objcopy --add-gnu-debuglink=libfoo.so.debug libfoo.soの順に処理します。.gnu_debuglinkと Build ID のどちらでも各種ツールは解決できますが、SSQP では Build ID パスのほうが衝突が少なく再現性が高いです。 - リプロデューサブルビルド:同じソースから同じ Build ID を得たい場合は、タイムスタンプや絶対パスが埋め込まれないように設定を見直します。
NuGet/snupkg と SSQP の関係
NuGet の .snupkg は主に Windows 用の PDB 配布を前提とした仕組みです。Linux/Android の ELF 記号を配る用途では、NuGet とは独立に SSQP をホストしたほうが実務的です。パッケージが生成した成果物(.so と .debug、または .sym)を CI の最終段で python‑symstore もしくは手製スクリプトで SSQP に配置するフローにしておくと、NuGet の配布と衝突せずに運用できます。
よくある落とし穴と対策
| 症状 | 原因 | 対処 |
|---|---|---|
| ELF が解決されない/見つからない | Build ID が無い、または SSQP パスの 2 桁分割が誤っている | readelf -n で Build ID を確認し、elf-buildid/XX/YYYY.../ファイル の構造になっているか点検 |
.debug を置いたのにデバッグ情報が効かない | ファイル名や配置場所が本体と合っていない/.gnu_debuglink を付与していない | 本体と同じディレクトリに .debug を置くか、--add-gnu-debuglink を使う。SSQP 側にも .debug を置く |
| WDK の symstore で登録しようとして失敗 | ELF 非対応 | python‑symstore か手製スクリプトで SSQP を生成し、取得側のツールに SSQP の URL/パスを設定 |
| ダウンロードは成功するが別バージョンを掴む | 別ビルドの Build ID に一致(キャッシュ汚染) | クライアントのローカルキャッシュをクリアし、SSQP 側の重複を精査。CI で Build ID の衝突チェックを追加 |
社内導入の実践レシピ(最短コース)
- シンボル共有用のフォルダ(例:
\\fileserver\symbols)を作る。 - CI の最後に次の 2 ステップを追加:
- ELF 本体を
--only-keep-debugで分離し.debugを作成、--add-gnu-debuglinkを付与。 - python‑symstore で
symstore add -f *.so -f *.debug -s \\fileserver\symbols -t <製品> -c <GitSHA>を実行。
- ELF 本体を
- IIS または Nginx から
/download/symbolsとして静的公開(認証は社内ルールに従う)。 - 解析端末の
_NT_SYMBOL_PATH(またはツール設定)にsrv*C:\SymCache*http://intranet.example.com/download/symbolsを追加。 - 最初のクラッシュダンプで ELF 記号が引けるか(関数名やソース行が出るか)を確認し、成功したらフローをテンプレート化。
検証の観点(チェックリスト)
- ELF に Build ID が存在する(
readelf -nで確認)。 - SSQP のフォルダ構造が 2 桁 + 残り の分割になっている。
.debugが必要なら SSQP 同階層に置いてある。- HTTP でそのパスにアクセスできる(認証付きの場合、クライアントが正しく資格情報を持つ)。
- クライアントのシンボルパスに キャッシュ と SSQP の両方が正しく設定されている。
まとめ
要点: 現行の WDK 版
symstore.exeは ELF の登録に非対応。しかし、SSQP に沿ってファイルを配置すれば、WinDbg や dotnet 系ツールは問題なく ELF 記号を取得・活用できる。最短は python‑symstore の導入、代替として Build ID ベースの手動配置でも十分に運用可能。社内サーバーは IIS/Nginx の静的配信で足り、セキュリティとクリーンアップのルール化が安定運用の鍵になる。
補遺:トラブルシューティングの実例(libcoreclr.so)
例えば libcoreclr.so(.NET ランタイムのネイティブ層)でスタックが「?」だらけになる場合、以下の順に確認すると解決が早いです。
- Build ID の照合:ダンプに埋め込まれた Build ID と SSQP のパスが一致しているか。
- 分離デバッグの整合:
libcoreclr.soとlibcoreclr.so.debugを SSQP 同階層に置いたか。 - キャッシュのクリア:解析端末の
C:\SymCache(またはツール既定のキャッシュ)を一時退避して再取得。 - アクセス制御:HTTP 認証/プロキシで 401/403 が出ていないか(取得ログで確認)。
FAQ
Q. WDK の今後のバージョンで ELF 対応の symstore.exe が出る可能性は?
A. 公開情報ベースでは現時点において公式の ELF 対応版は出ていません。ELF 登録は python‑symstore かスクリプトで対応する前提で、CI/CD に組み込むのが現実解です。
Q. SSQP に配置したら WinDbg で必ず読めますか?
A. 読み込み可否は Build ID の一致と パス構造の正確さに依存します。逆にいえば、ここさえ合っていれば特別な拡張は不要です。
Q. Breakpad の .sym だけ運用したい場合は?
A. foo.sym/ModuleID/foo.sym のディレクトリを作って置くだけで十分です。アプリのクラッシュレポート基盤(Crashpad など)から解決されます。
Q. Windows 側の PDB と Linux 側の ELF を同じサーバーで混在できますか?
A. できます。SSQP はキーごとにディレクトリが分かれるため、foo.pdb/GUIDAge と elf-buildid/xx/yy... が共存しても問題ありません。
本記事の要点(再掲)
- WDK の
symstore.exeは ELF の登録に対応していない。 - ただし SSQP 構造に ELF を置けば、WinDbg や dotnet ツールは「取得・読込」できる。
- 実装は python‑symstore が最短。難しい場合は Build ID からの手動配置でも動く。
- 公開は IIS/Nginx の静的配信で充分。セキュリティとクリーンアップをポリシー化する。

コメント