Skip to content

PdbSymbolProvider silently disabled without pdbparse (GPL, unmaintained); llvm-pdbutil is a permissive alternative #230

Description

@danielplohmann

Summary

PdbSymbolProvider disables itself silently when pdbparse is absent, and pdbparse is both GPL-licensed (SMDA is BSD-2-Clause) and unmaintained since 2020. In practice this means PDB-bearing PEs get no names at all, with no indication that anything was skipped.

Current behaviour

try:
    import pdbparse
    from pdbparse.undname import undname
except ImportError:
    pdbparse = None
    LOGGER.debug("3rd party library pdbparse (use fork @ ...) not installed - ...")

pdbparse is not a declared dependency, so a default install never has it. The message is at debug level, so the default logging configuration hides it. Result: a PE with a perfectly good PDB beside it yields 1 named function (original_entry_point), and nothing says why.

The dependency itself is problematic

PyPI pdbparse licence GPL (License :: OSI Approved :: GNU General Public License (GPL))
SMDA licence BSD 2-Clause
Last pdbparse release 2020-02-01 (v1.5)
Upstream state the working variant is a third-party fork (VPaulV), not the PyPI package

An optional import softens the licence question, but recommending a GPL library in the docstring of a BSD project is still worth a second look.

I checked whether anything better exists in 2026 — it does not

You mentioned the original goal was pure-Python with no native dependency. Re-surveying:

  • pdbpy ("Pure python implementation of parsing PDB debug information files") — a single 0.0.1 release, and it does not import: ModuleNotFoundError: No module named 'dtypes'. Not viable.
  • lief.pdb — the API exists (lief.pdb.load, DebugInfo, Function, PublicSymbol), which would be ideal since LIEF is already a dependency. But it requires LIEF "extended", the commercial build; the open PyPI wheel returns None with "Debug info not available for this build."
  • libpdb (shareef12) — C, Linux, would need bindings.
  • pdbparse — as above.

So there is still no maintained, permissively-licensed, pure-Python option.

Suggestion: an optional llvm-pdbutil provider

llvm-pdbutil is maintained, ships in distro LLVM packages, and reads rust-lld-produced PDBs correctly (verified). Parsing its output requires no new Python dependency and no GPL code:

  • llvm-pdbutil dump --publics <pdb>S_PUB32 records, raw mangled names, with addr = SECT:OFFSET
  • llvm-pdbutil dump --symbols <pdb>S_GPROC32 / S_LPROC32 records, with addr = SECT:OFFSET, code size = N

Virtual address = imagebase + sections[sect-1].virtual_address + offset.

Two findings from implementing this that are worth having in SMDA regardless of the parser used:

  1. Publics alone are a poor source. On a Rust msvc binary, publics gave 31.4% naming; adding the S_GPROC32/S_LPROC32 procedure records took it to 54.8%. The publics stream includes imports and data and omits many functions.

  2. Most of the remainder are fragments, not anonymous functions. MSVC-style codegen outlines cold paths and EH funclets into separate chunks, and SMDA reports each chunk as its own function. Of 1470 functions with no symbol at their entry, 1439 (97.9%) fall inside a procedure's [addr, addr+code size) range. Labelling those from the containing procedure took naming from 54.8% to 99.0% (99.0% of functions above the 10-instruction threshold).

    Worth noting for any implementation: label such a fragment relative to its parent (<parent>$+0x<delta>) rather than with an absolute offset, so the label is stable across builds and does not break MCRIT's (pic_hash, function_name) dedup.

Measured impact

Same binary (Rust regex + flate2, x86_64-pc-windows-msvc, debuginfo=2, linked with rust-lld), 3391 functions:

named
SMDA 4.4.4 as shipped 1 (0.0%)
publics only 1066 (31.4%)
publics + proc records 1858 (54.8%)
+ fragment attribution 3356 (99.0%)

Minimal ask

Even without changing the parser, raising the "pdbparse not installed" message from debug to warning — or warning when a .pdb sits next to the input and no PDB provider is active — would turn a silent no-op into something diagnosable.

Versions: SMDA 4.4.4, Python 3.12.13, LLVM 14, Linux x86_64.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions