PDB


Introduction

Unlike DWARF debug information, PDB debug information is always stored externally from the original binary. Nevertheless, the original binary keeps the path of the PDB file in the attribute.
pe: lief.PE.Binary

if (debug_info := pe.debug_info) is not None:
    assert isinstance(debug_info, lief.pdb.DebugInfo)
    print(f"PDB Debug handler: {debug_info}")

# Or you can load the PDB directly:
pdb = lief.pdb.load("some.pdb")
At this point, the PDB instance () can be used to explore the PDB debug information:
pdb: lief.pdb.DebugInfo

print("arg={}, guid={}".format(pdb.age, pdb.guid))

for sym in pdb.public_symbols:
    print("name={}, section={}, RVA={}".format(sym.name, sym.section_name, sym.RVA))

for ty in pdb.types:
    if isinstance(ty, lief.pdb.types.Class):
        print(f"Class[name]={ty.name}")

for cu in pdb.compilation_units:
    print(f"module={cu.module_name}")
    for src in cu.sources:
        print(f"  - {src}")

    for func in cu.functions:
        print(
            "name={}, section={}, RVA={}, code_size={}".format(
                func.name,
                func.section_name,
                func.RVA,
                func.code_size,
            )
        )
binary: lief.Binary

dbg = binary.load_debug_info(r"C:\Users\romain\LIEF.pdb")
Note that can also attach an external DWARF file to a PE binary, even though this is not a typical use case. For instance, the BinaryNinja and Ghidra DWARF export plugins can generate a DWARF file for a PE binary based on analyses performed by these frameworks.
This external loading API is useful for adding debug information that might not already be present in the binary. For instance, the function can leverage this additional debug information to disassemble functions defined in the debug file previously loaded:
binary: lief.Binary

dbg = binary.load_debug_info(r"C:\Users\romain\LIEF.pdb")

# The location (address/size) of `my_function` is defined in LIEF.pdb
for inst in binary.disassemble("my_function"):
    print(inst)

Generating C/C++ Definitions

PDB types, functions and compilation units can be turned into C/C++ definitions using the to_decl() function:

The generated output can be configured with a structure:
pdb: lief.pdb.DebugInfo

opt = lief.DeclOpt()
opt.is_cpp = True

for ty in pdb.types:
    print(ty.to_decl(opt))

for cu in pdb.compilation_units:
    # Emit the definition of the functions of the compilation unit
    print(cu.to_decl(opt))

    for func in cu.functions:
        print(func.to_decl(opt))

API

You can find the documentation of the API for the different languages here:

Python API

C++ API

Rust API: lief::pdb