Skip to content

Pdb

Description

Parsed PDB file. Holds the raw bytes (always parser-owned) plus decoded indices into them. All three PdbOpen* constructors leave the parser as sole owner of data – see the L / R semantics on the FromMemory / FromMemoryCopy constructors (mirrors VecInsertL / VecInsertR).

Fields

Name Description
data Raw PDB bytes as a Buf (owned). Carries its own length and allocator – read via BufLength / BufData / BufAllocator.
block_size MSF page size (read from the superblock; usually 4096 but can be 512/1024/2048).
num_streams Stream count from the directory.
info Decoded PDB Info stream (#1).
functions Sorted-by-rva list of public function names from the Publics stream. Populated by PdbOpen[FromMemory].

Usage example (Cross-references)

Usage examples (Cross-references)
    #define MISRA_SYS_PDB_CACHE_H
    
    #include <Misra/Parsers/Pdb.h>
    #include <Misra/Parsers/Pe.h>
    #include <Misra/Std/Allocator.h>
        Str  module_path; // owned; cleaned via StrDeinit
        Pe   pe;
        Pdb  pdb;
        bool pe_open;
        bool pdb_open;
    #define MISRA_PARSERS_PDB_H
    
    #include <Misra/Parsers/Pdb/Private.h>
    #include <Misra/Std/Allocator.h>
    #include <Misra/Std/Container/Buf.h>
        // together in `PdbDeinit`.
        Str name_pool;
    } Pdb;
    
    ///
    /// TAGS: Parser, PDB, Memory, Ownership
    ///
    bool PdbOpenFromMemory(Pdb *out, Buf *in);
    
    ///
    /// TAGS: Parser, PDB, Deinit, Lifecycle
    ///
    void PdbDeinit(Pdb *self);
    
    ///
    /// TAGS: Parser, PDB, Function, Resolve
    ///
    const PdbFunction *PdbResolveRva(const Pdb *self, u32 rva);
    
    #endif // MISRA_PARSERS_PDB_H
    
    #include <Misra/Std/Container/Buf.h>
    #include <Misra/Parsers/Pdb.h>
    #include <Misra/Std.h>
    #include <Misra/Std/File.h>
    // Return a pointer to the first byte of block `block_id` inside the
    // PDB file. NULL on out-of-range.
    static const u8 *block_ptr(const Pdb *self, u32 block_id) {
        u64 off = (u64)block_id * self->block_size;
        if (off + self->block_size > BufLength(&self->data))
    // Read `n` bytes from stream `idx` starting at byte offset `offset`
    // into `dest`. Walks the stream's block chain.
    static bool stream_read(const Pdb *self, u32 idx, u64 offset, u8 *dest, u64 n) {
        if (idx >= self->num_streams)
            return false;
    // ---------------------------------------------------------------------------
    
    static bool parse_superblock(Pdb *self, u32 *out_num_dir_bytes, u32 *out_block_map_addr) {
        if (BufLength(&self->data) < SUPERBLOCK_SIZE) {
            LOG_ERROR("PDB: file too small for MSF superblock");
    
    // Reconstruct the stream directory into a contiguous buffer.
    static bool reconstruct_directory(Pdb *self, u32 num_dir_bytes, u32 block_map_addr) {
        // The block_map_addr page holds an array of u32 block indices,
        // one per `block_size` chunk of the directory. The number of
    
    // Parse the reconstructed directory bytes into per-stream metadata.
    static bool parse_directory(Pdb *self) {
        if (self->stream_dir_size < 4) {
            LOG_ERROR("PDB: directory truncated (no stream count)");
    // ---------------------------------------------------------------------------
    
    static bool parse_pdb_info(Pdb *self) {
        if (self->num_streams <= 1)
            return true; // no info stream
    } DbiSubstreamInfo;
    
    static DbiSubstreamInfo parse_dbi_header(const Pdb *self) {
        DbiSubstreamInfo r = {0};
        if (DBI_STREAM_INDEX >= self->num_streams)
    // return a small allocator-backed array of (RVA, VSize) pairs. Caller
    // frees via AllocatorFree.
    static SectionRva *load_section_table(const Pdb *self, u16 section_hdr_stream, u32 *out_count) {
        *out_count = 0;
        if (section_hdr_stream >= self->num_streams)
    
    static bool walk_publics(
        const Pdb        *self,
        u16               symrec_stream,
        const SectionRva *sections,
    
    // Top-level: pull DBI -> SectionHdr table + SymRecord stream -> publics.
    static bool parse_pdb_functions(Pdb *self) {
        DbiSubstreamInfo dbi = parse_dbi_header(self);
        if (!dbi.ok)
    // then MemSets the caller's view. On exit `*in` is zeroed (success
    // or failure); allocator and bytes are carried by `taken`.
    bool PdbOpenFromMemory(Pdb *out, Buf *in) {
        if (!out || !in || !BufData(in) || !BufAllocator(in)) {
            LOG_FATAL("PdbOpenFromMemory: NULL argument (contract violation)");
    
    // R-value form: allocate Buf, copy, hand `&copy` to the L-form.
    bool pdb_open_from_memory_copy(Pdb *out, const u8 *data, size data_size, Allocator *alloc) {
        if (!out || !data || !alloc) {
            LOG_FATAL("PdbOpenFromMemoryCopy: NULL argument (contract violation)");
    }
    
    bool pdb_open(Pdb *out, Zstr path, Allocator *alloc) {
        if (!out || !path || !alloc) {
            LOG_FATAL("PdbOpen: NULL argument (contract violation)");
    }
    
    void PdbDeinit(Pdb *self) {
        if (!self)
            return;
    }
    
    const PdbFunction *PdbResolveRva(const Pdb *self, u32 rva) {
        if (!self || VecLen(&self->functions) == 0)
            return NULL;
    
    #include <Misra.h>
    #include <Misra/Parsers/Pdb.h>
    #include <Misra/Std/Zstr.h>
    #include <Misra/Std/Allocator/Default.h>
        build_msf_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, blob, sizeof(blob), base);
        if (!ok) {
        MemSet(garbage, 0xCC, sizeof(garbage));
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, garbage, sizeof(garbage), base);
        build_full_pdb_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, fblob, sizeof(fblob), base);
        if (!ok) {
        build_multi_pdb_blob(&symrec_size);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, mblob, sizeof(mblob), base);
        if (!ok) {
        wr_u32(&buf[52], 0);
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, buf, sizeof(buf), base);
        MemCopy(buf, kMagic, 16); // partial magic, far short of 56-byte superblock
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, buf, sizeof(buf), base);
    // rva of the first function when count >= 1.
    static int open_and_count(Allocator *base, u32 *out_rva0) {
        Pdb pdb;
        if (!PdbOpenFromMemoryCopy(&pdb, gblob, sizeof(gblob), base))
            return -1;
        u32 blob_len = build_blob_m2(B_BS, sizes, 3, &dir_bytes, &num_pages);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        u32              sizes[2] = {0, 28};
        u32              blob_len = build_blob_m2(bs, sizes, 2, NULL, NULL);
        Pdb              pdb;
        bool             ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        wr_u32(&buf[52], 0);
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, buf, sizeof(buf), base);
        DefaultAllocatorDeinit(&alloc);
            u32 blob_len  = build_blob_m2(B_BS, sizes, 2, NULL, NULL);
            g_blob[b]    ^= 0xFF; // corrupt one magic byte
            Pdb  pdb;
            bool opened = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
            if (opened) {
        wr_u32(&buf[32], B_BS);
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, buf, sizeof(buf), base);
        DefaultAllocatorDeinit(&alloc);
        wr_u32(&g_blob[B_BLOCK_MAP * B_BS], num_pages);
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        DefaultAllocatorDeinit(&alloc);
        u32 blob_len = build_blob_m2(B_BS, sizes, 2, NULL, NULL);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        u32 blob_len = build_blob_m2(B_BS, sizes, 2, NULL, NULL);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        build_two_block_dir();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_tblob, sizeof(g_tblob), base);
        if (ok) {
        u32 blob_len = build_blob_multiblock(sizes, 5);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        u32 blob_len = build_blob_multiblock(sizes, 3);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        u32 blob_len = build_blob_multiblock(sizes, 1);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        wr_u32(&dir[0], 1000); // claims 1000 streams; dir page can't hold them
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        DefaultAllocatorDeinit(&alloc);
        wr_u32(&dir[4 + 1 * 4], 100 * 512); // 100 blocks claimed, none listed
    
        Pdb  pdb;
        bool ok = !PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        DefaultAllocatorDeinit(&alloc);
        wr_u32(&g_blob[Z_DIR * Z_BS], 0); // num_streams = 0
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, Z_PAGES * Z_BS, base);
        if (ok) {
        u32 blob_len = build_blob_m2(B_BS, sizes, 3, NULL, NULL);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        u32 blob_len = build_blob_multiblock(sizes, 2);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob, blob_len, base);
        if (ok) {
        wr_u32(&dir0[4], PDB_NIL); // stream 0 is NIL -> no blocks
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_hblob, sizeof(g_hblob), base);
        if (ok) {
        build_filter_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_filter, sizeof(g_blob_filter), base);
        if (!ok) {
        build_sect_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_sect, sizeof(g_blob_sect), base);
        if (!ok) {
        build_oob_seg_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_oob, sizeof(g_blob_oob), base);
        if (!ok) {
        build_size_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_size, sizeof(g_blob_size), base);
        if (!ok) {
        build_sort_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_sort, sizeof(g_blob_sort), base);
        if (!ok) {
        build_mal_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_mal, sizeof(g_blob_mal), base);
        if (!ok) {
        build_ovf_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_ovf, sizeof(g_blob_ovf), base);
        if (!ok) {
        build_crossblock_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, xblob, sizeof(xblob), base);
        if (!ok) {
        wr_u32(&info[8], 0x090A0B0C); // age
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, xblob, sizeof(xblob), base);
        if (!ok) {
        build_info_blob(999);
    
        Pdb  pdb;
        bool open_ok = PdbOpenFromMemoryCopy(&pdb, sblob, sizeof(sblob), base);
        // Real code: rejected.
        build_info_blob(S_INFO_PAGE);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, sblob, sizeof(sblob), base);
        if (!ok) {
        BufResize(&in, (size)sizeof(sblob));
    
        Pdb  pdb;
        bool open_ok = PdbOpenFromMemory(&pdb, &in);
        // Expect failure (bogus info block id).
        build_crossblock_blob(); // populates name_pool with two function names
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, xblob, sizeof(xblob), base);
        if (!ok) {
        }
    
        Pdb  pdb;
        bool ok = PdbOpen(&pdb, X_TMP_VALID, base);
        if (ok) {
        }
    
        Pdb  pdb;
        bool ok = !PdbOpen(&pdb, X_TMP_JUNK, base);
        build_crossblock_blob();
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, xblob, sizeof(xblob), base);
        if (!ok) {
        build_pdb(g_blob_m5, secs, 6, pubs, 1, 0, 0);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_m5, P5_BLOB_SIZE, base);
        if (ok) {
        build_pdb(g_blob_m5, secs, 3, pubs, 1, /*sec_block_id_override=*/100, 0);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_m5, P5_BLOB_SIZE, base);
        if (ok) {
        build_pdb(g_blob_m5, secs, 2, pubs, 2, 0, 0);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_m5, P5_BLOB_SIZE, base);
        if (ok) {
        build_pdb(g_blob_m5, secs, 1, pubs, 3, 0, 0);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_m5, P5_BLOB_SIZE, base);
        if (ok) {
        build_pdb(g_blob_m5, secs, 1, pubs, 1, 0, 0);
    
        Pdb  pdb;
        bool ok = PdbOpenFromMemoryCopy(&pdb, g_blob_m5, P5_BLOB_SIZE, base);
        if (ok) {
        build_pdb(g_blob_m5, secs, 1, pubs, 1, 0, /*symrec_override=*/99);
    
        Pdb  pdb;
        bool rejected = !PdbOpenFromMemoryCopy(&pdb, g_blob_m5, P5_BLOB_SIZE, base);
        if (!rejected)
    
    static int open_count(Allocator *base, Blob *b) {
        Pdb pdb;
        if (!PdbOpenFromMemoryCopy(&pdb, b->bytes, b->len, base))
            return -1;
        put_pub32(stream_page(&b, 4), 0x100, 1, "deepblk");
    
        Pdb pdb;
        if (!PdbOpenFromMemoryCopy(&pdb, b.bytes, b.len, base)) {
            DefaultAllocatorDeinit(&alloc);
        put_pub32(stream_page(&b, 4), 0x100, 1, "deepfn");
    
        Pdb  pdb;
        bool opened = PdbOpenFromMemoryCopy(&pdb, b.bytes, b.len, base);
        bool ok     = opened && VecLen(&pdb.functions) == 1 && pdb.num_streams == N;
        put_pub32(stream_page(&b, 4), 0x100, 1, "fn");
    
        Pdb  pdb;
        bool opened = PdbOpenFromMemoryCopy(&pdb, b.bytes, b.len, base);
        bool ok     = opened && VecLen(&pdb.functions) == 1;
    
    int main(void) {
        WriteFmt("[INFO] Starting Pdb tests\n\n");
    
        TestFunction tests[] = {
        };
    
        return run_test_suite(tests, sizeof(tests) / sizeof(tests[0]), NULL, 0, "Pdb");
    }
Last updated on