Home / Alt manpages / llvm-pdbutil-20(1)

  • llvm-pdbutil-20(1)
  • User command
  • linux

Inspect a PDB File Safely with llvm-pdbutil-20

You will finish with a repeatable read-only workflow for examining a Windows PDB file on Linux: identify its streams, inspect symbols and types, and dump a narrow byte range when a low-level check is needed. The examples use the installed LLVM 20 package, version 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139.

Allow about fifteen minutes for a first pass. You need a shell, llvm-pdbutil-20 from the llvm-20 package, and a readable PDB file. The commands below do not need sudo, and they do not alter the input. Work on a copy if the file is evidence whose integrity must be preserved.

1. Confirm the installed tool

Check the executable and package version before relying on an option. This is an ordinary, read-only check:

$ command -v llvm-pdbutil-20
/usr/bin/llvm-pdbutil-20
$ dpkg-query -W -f='${Package} ${Version}\n' llvm-20
llvm-20 1:20.1.8~++20250804090239+87f0227cb601-1~exp1~20250804210352.139
$ llvm-pdbutil-20 --version
LLVM version 20.1.8

Checkpoint: the command name includes the LLVM major version. Keep it in scripts so an upgrade does not silently select a different binary.

2. Check the input without opening it for writing

Set a shell variable to the real file, then check that it is a regular, readable file. Replace the example path, but keep the quotes:

$ PDB='/path/to/program.pdb'
$ test -f "$PDB" && test -r "$PDB" && echo 'readable PDB path'
readable PDB path
$ stat --printf='size=%s bytes mode=%A\n' "$PDB"
size=123456 bytes mode=-rw-r--r--

The size and mode will differ. If this check fails, fix the path or read permission first. Do not make a forensic input writable merely to get past a permissions error.

3. Start with the PDB and MSF summaries

The dump subcommand is the useful first stop for file structure. Ask for the PDB and MSF header, then list the streams:

$ llvm-pdbutil-20 dump -summary "$PDB"
$ llvm-pdbutil-20 dump -streams "$PDB"
Stream 0
  Size: ...
  Name: ...

The exact headers and stream numbers depend on the file. The first command is a compact health and identity check; the second shows the container's streams. Add -stream-blocks to the stream query when block allocation matters:

$ llvm-pdbutil-20 dump -streams -stream-blocks "$PDB"

Checkpoint: save the output to a separate report if you need to compare runs. For example, llvm-pdbutil-20 dump -summary "$PDB" > pdb-summary.txt writes a new report and leaves the PDB untouched. Avoid redirecting over the input file.

4. Inspect modules, symbols and source files

Use the low-level dump switches that answer a specific question. This sample records module information, contributing files and public symbols:

$ llvm-pdbutil-20 dump -modules -files -publics "$PDB" > pdb-symbol-report.txt
$ test -s pdb-symbol-report.txt && echo 'report written'
report written

Use -globals for global symbol records, or -symbols for symbols belonging to each dumped module. These are not the same view: public symbols are the public-symbol records, while module symbols are tied to individual compilands. If the report is too large, select one module with -modi=NUMBER, using the module number shown by the module output.

Do not treat a non-empty report as proof that every symbol is present. PDB producers can omit information, and the command is exposing what the file contains rather than reconstructing missing debug data.

5. Trace one type record

Type records live in the TPI stream and use hexadecimal type indexes. First request the type records, or use an index already reported by a previous inspection:

$ llvm-pdbutil-20 dump -types "$PDB" > pdb-types.txt
$ rg -n '0x[0-9A-Fa-f]+' pdb-types.txt | head

Substitute a real index for TYPE_INDEX. The value must be accepted as an unsigned number; the manual documents hexadecimal indexes for the dependent-record example:

$ TYPE_INDEX=0x4000
$ llvm-pdbutil-20 dump -type-index="$TYPE_INDEX" "$PDB"
$ llvm-pdbutil-20 dump -type-index="$TYPE_INDEX" -dependents "$PDB"

The first command shows one TPI record. The second follows the record's dependency graph, which is useful for a function whose return type or members are stored under other indexes. For an IPI record, use -id-index=INDEX and -dependents in the same way.

6. Dump bytes only when the structure is known

The bytes subcommand is for deeper forensics, not a general replacement for dump. It can read file byte ranges, MSF blocks or a stream slice. A narrow stream request is easier to review than a whole-file hex dump:

$ llvm-pdbutil-20 bytes -stream-data=7:3@12 "$PDB"
00000000: ...

This asks for 12 bytes from stream 7, starting at offset 3 in that stream. Confirm the stream number and offset from dump -streams first. For a file offset, use -byte-range=START-END; for MSF blocks, use -block-range=START-END. The ranges identify data to display, not data to modify.

Raw output is easy to misread. Keep the command, input hash and chosen offsets with your report:

$ sha256sum "$PDB"
$ llvm-pdbutil-20 bytes -stream-data=7:3@12 "$PDB" > pdb-stream-7-offset-3.hex

7. Keep conversion and merging behind a backup

pdb2yaml, yaml2pdb and merge are different from inspection because they create output files. Do not point their output at an original or valuable PDB. Generate into a new path, then inspect the result:

$ llvm-pdbutil-20 pdb2yaml "$PDB" > program.yaml
$ llvm-pdbutil-20 yaml2pdb program.yaml -pdb=program-rebuilt.pdb
$ llvm-pdbutil-20 dump -summary program-rebuilt.pdb

The manual describes the YAML syntax as something to learn from pdb2yaml output. Treat hand edits as experimental. If a generated PDB is not useful, remove only the named generated files after checking that no report depends on them. The original input remains the recovery copy because none of these examples overwrites it.

Common traps

  • Using pretty on Linux: the installed manual says this subcommand is built on the Windows DIA SDK and is not supported on non-Windows platforms. Use dump for Linux-side inspection.
  • Starting with -all: both dump and the semantic views expose broad option sets. Begin with one question and one narrow switch so reports stay reviewable.
  • Confusing symbols and types: -symbols and -globals inspect symbol records; -types and -type-index inspect CodeView type records in TPI.
  • Assuming output is stable: stream layout, indexes and displayed detail belong to the particular PDB. Record the tool version and input hash with findings.

Done means

  • The installed LLVM package and executable version are recorded.
  • The PDB was checked for readability without changing its permissions or contents.
  • MSF, stream, module and symbol information was queried with focused commands.
  • A real type index was inspected, with dependencies when needed.
  • Raw bytes were limited to a known stream or range and saved separately.
  • Any generated YAML or PDB output has a new name, leaving the original recoverable.