From cc161607ea7d7a2ea3b25040266ec4b42ed708bd Mon Sep 17 00:00:00 2001
From: dd86k
Date: Wed, 24 Jan 2024 17:37:01 -0500
Subject: [PATCH] Continue support for PDB
---
app/dump/pdb70.d | 241 +++++++++-----
app/dumper.d | 6 +
app/main.d | 2 +
src/adbg/debugger/process.d | 38 ++-
src/adbg/object/format/pdb.d | 605 ++++++++++++++++++++++++++++++-----
src/adbg/object/server.d | 14 +
src/adbg/object/types/cv.d | 72 +++++
7 files changed, 812 insertions(+), 166 deletions(-)
create mode 100644 src/adbg/object/types/cv.d
diff --git a/app/dump/pdb70.d b/app/dump/pdb70.d
index 85992782..ac46d3db 100644
--- a/app/dump/pdb70.d
+++ b/app/dump/pdb70.d
@@ -9,8 +9,10 @@ import adbg.disassembler.core;
import adbg.object.server;
import adbg.object.machines;
import adbg.object.format.pdb;
+import adbg.object.format.pe : adbg_object_pe_machine_string;
import adbg.utils.uid;
import adbg.utils.date;
+import adbg.include.c.stdio : printf, snprintf, putchar;
import dumper;
extern (C):
@@ -19,8 +21,8 @@ int dump_pdb70(ref Dumper dump, adbg_object_t *o) {
if (dump.selected_headers())
dump_pdb70_header(dump, o);
- //if (dump.selected_debug())
- // dump_pdb70_debug(dump, o);
+ if (dump.selected_debug())
+ dump_pdb70_debug(dump, o);
return 0;
}
@@ -32,93 +34,184 @@ void dump_pdb70_header(ref Dumper dump, adbg_object_t *o) {
pdb70_file_header *header = adbg_object_pdb70_header(o);
- if (header.PageCount * header.PageSize != o.file_size) {
- print_string("error", "Reported size isn't same as actual file size.");
- return;
- }
-
print_stringl("Magic", header.Magic.ptr, 24);
print_x32("PageSize", header.PageSize);
print_x32("FreeIndex", header.FreeIndex);
print_x32("PageCount", header.PageCount);
print_x32("DirectorySize", header.DirectorySize);
- print_x32("Unknown", header.Unknown2);
+ print_x32("Unknown", header.Unknown);
print_x32("DirectoryOffset", header.DirectoryOffset);
+
+ print_header("FPM information");
+ uint bitcnt = header.PageCount / 8;
+ for (uint biti; biti < bitcnt; ++biti) {
+ char[48] buf = void;
+ uint blocknum = biti * 8;
+ snprintf(buf.ptr, 48, "Block %u-%u", blocknum, blocknum + 7);
+ print_x8(buf.ptr, o.i.pdb70.fpm[biti]);
+ }
+
+ print_header("Stream information");
+ print_u32("Stream count", o.i.pdb70.strcnt);
+ uint strcnt = o.i.pdb70.strcnt;
+ uint blkoffi;
+ // NOTE: Avoid using internal section map in case it changes
+ for (uint stri; stri < strcnt; ++stri) {
+ uint size = o.i.pdb70.strsize[stri];
+
+ // Print field name
+ char[48] buf = void;
+ snprintf(buf.ptr, 48, "Stream %u", stri);
+ print_name(buf.ptr);
+
+ // Skip if empty
+ //TODO: Check with FPM?
+ if (size == 0 || size == PDB_BLOCK_SIZE_UNUSED) {
+ putchar('\n');
+ continue;
+ }
+
+ // Print size + associated blocks
+ printf("%u\t(", size);
+ uint blkcnt = (size + header.PageSize - 1) / header.PageSize;
+ for (uint blki; blki < blkcnt; ++blki) {
+ if (blki) putchar(',');
+ printf("%u", o.i.pdb70.stroff[blkoffi++]);
+ }
+ printf(")\n");
+ }
}
-/+
+
void dump_pdb70_debug(ref Dumper dump, adbg_object_t *o) {
print_header("Debug");
- pdb70_file_header *header = adbg_object_pdb70_header(o);
-
- uint diroff = header.DirectoryOffset * header.PageSize;
- uint dircnt = (header.DirectorySize / header.PageSize) + 1;
-
- uint *dir = void;
- if (adbg_object_offset(o, cast(void**)&dir, diroff)) {
- print_string("error", "Directory offset out of bounds.");
+ print_section(1, "PDB Stream", 10);
+ pdb70_pdb_header *pdb = void;
+ uint s1sz = void;
+ if (adbg_object_pdb70_stream_open(o, cast(void**)&pdb, &s1sz, PdbStream.pdb)) {
+ print_string("error", "Couldn't read Stream 1");
return;
}
+ if (s1sz) {
+ const(char) *vcver = void;
+ switch (pdb.Version) with (PdbRaw_PdbVersion) {
+ case vc2: vcver = "VC2"; break;
+ case vc4: vcver = "VC4"; break;
+ case vc41: vcver = "VC41"; break;
+ case vc50: vcver = "VC50"; break;
+ case vc98: vcver = "VC98"; break;
+ case vc70_old: vcver = "VC70_OLD"; break;
+ case vc70: vcver = "VC70"; break;
+ case vc80: vcver = "VC80"; break;
+ case vc110: vcver = "VC110"; break;
+ case vc140: vcver = "VC140"; break;
+ default: vcver = "Unknown";
+ }
+
+ char[UID_TEXTLEN] uidstr = void;
+ int uidlen = uid_string(pdb.UniqueId, uidstr.ptr, UID_TEXTLEN, UID_GUID);
+ print_u32("Version", pdb.Version, vcver);
+ print_x32("Signature", pdb.Signature);
+ print_u32("Age", pdb.Age);
+ print_stringl("UniqueID", uidstr.ptr, uidlen);
+ }
+ adbg_object_pdb70_stream_close(o, cast(void**)&pdb);
- uint nstreams = *dir; /// Number of stream sizes
- uint *psize = dir + 1; // Start at first size
- uint *ploc = psize + nstreams; // Start at first loc
-
- for (size_t i; i < nstreams; ++i) {
- int stream_size = cast(int)psize[i];
+ print_section(2, "TPI Stream", 10);
+ pdb70_tpi_header *tpi = void;
+ uint s2sz = void;
+ if (adbg_object_pdb70_stream_open(o, cast(void**)&tpi, &s2sz, PdbStream.tpi)) {
+ print_string("error", "Couldn't read Stream 2");
+ return;
+ }
+ if (s2sz) {
+ const(char) *vcver = void;
+ switch (tpi.Version) with (PdbRaw_TpiVer) {
+ case v40: vcver = "v40"; break;
+ case v41: vcver = "v41"; break;
+ case v50: vcver = "v50"; break;
+ case v70: vcver = "v70"; break;
+ case v80: vcver = "v80"; break;
+ default: vcver = "Unknown";
+ }
- print_section(cast(uint)i);
- print_x32("Size", stream_size);
+ print_u32("Version", tpi.Version, vcver);
+ print_u32("HeaderSize", tpi.HeaderSize);
+ print_u32("TypeIndexBegin", tpi.TypeIndexBegin);
+ print_u32("TypeIndexEnd", tpi.TypeIndexEnd);
+ print_u32("TypeRecordBytes", tpi.TypeRecordBytes);
- if (stream_size > 0) {
- print_x32("Page", ploc[i]);
-
- uint sloc = ploc[i] * header.PageSize;
- void *stream = void; // Stream count
- if (adbg_object_offsetl(o, &stream, sloc, 4)) {
- print_string("warning", "Stream directory outside of bounds.");
- continue;
- }
-
- int type = cast(int)i + 1;
- switch (type) {
- case 1:
- print_string("Stream Type", "PDB Header");
- uint dataoff = (*cast(uint*)stream) * header.PageSize;
-
- pdb70_stream_header *pdbhdr = void;
- if (adbg_object_offsetl(o, cast(void**)&pdbhdr, dataoff, 4)) {
- print_string("warning", "Stream outside of bounds.");
- continue;
- }
- with (pdbhdr) {
- char[UID_TEXTLEN] guid = void;
- uid_text(pdbhdr.UniqueId, guid, UID_GUID);
-
- print_x32("Version", Version);
- print_x32("Signature", Signature, ctime32(Signature));
- print_u32("Age", Age);
- print_stringl("UniqueId", guid.ptr, UID_TEXTLEN);
- }
- break;
- case 2:
- print_string("Stream Type", "Type manager");
- break;
- case 3:
- print_string("Stream Type", "Debug information");
- break;
- case 4:
- print_string("Stream Type", "NameMap");
- break;
- default: // >4 -> symbol
- print_string("Stream Type", "Unknown");
- }
- }
+ print_u16("HashStreamIndex", tpi.HashStreamIndex);
+ print_u16("HashAuxStreamIndex", tpi.HashAuxStreamIndex);
+ print_u32("HashKeySize", tpi.HashKeySize);
+ print_u32("NumHashBuckets", tpi.NumHashBuckets);
+
+ print_u32("HashValueBufferOffset", tpi.HashValueBufferOffset);
+ print_u32("HashValueBufferLength", tpi.HashValueBufferLength);
+
+ print_u32("IndexOffsetBufferOffset", tpi.IndexOffsetBufferOffset);
+ print_u32("IndexOffsetBufferLength", tpi.IndexOffsetBufferLength);
- uint nlocs = (stream_size / header.PageSize) + 1;
- for (size_t n; n < nlocs; ++n) {
- ++ploc;
+ print_u32("HashAdjBufferOffset", tpi.HashAdjBufferOffset);
+ print_u32("HashAdjBufferLength", tpi.HashAdjBufferLength);
+ }
+ adbg_object_pdb70_stream_close(o, cast(void**)&tpi);
+
+ print_section(3, "DBI Stream", 10);
+ pdb70_dbi_header *dbi = void;
+ uint s3sz = void;
+ if (adbg_object_pdb70_stream_open(o, cast(void**)&dbi, &s3sz, PdbStream.dbi)) {
+ print_string("error", "Couldn't read Stream 3");
+ return;
+ }
+ if (s3sz) {
+ const(char) *vcver = void;
+ switch (dbi.VersionHeader) with (PdbRaw_DbiVer) {
+ case v41: vcver = "v41"; break;
+ case v50: vcver = "v50"; break;
+ case v60: vcver = "v60"; break;
+ case v70: vcver = "v70"; break;
+ case v110: vcver = "v110"; break;
+ default: vcver = "Unknown";
}
+
+ // 255.127-1
+ char[16] buildnum = void;
+ snprintf(buildnum.ptr, 16, "%u.%u-%u",
+ dbi.BuildNumber >> 8 & 0x7f, // MajorVersion
+ cast(ubyte)dbi.BuildNumber, // MinorVersion
+ dbi.BuildNumber >> 15); // NewVersionFormat
+
+ print_x32("VersonSignature", dbi.VersonSignature);
+ print_u32("VersionHeader", dbi.VersionHeader, vcver);
+ print_u32("Age", dbi.Age);
+ print_u16("GlobalStreamIndex", dbi.GlobalStreamIndex);
+ print_x16("BuildNumber", dbi.BuildNumber, buildnum.ptr);
+ print_u16("PublicStreamIndex", dbi.PublicStreamIndex);
+ print_u16("PdbDllVersion", dbi.PdbDllVersion);
+ print_u16("SymRecordStream", dbi.SymRecordStream);
+ print_u16("PdbDllRbld", dbi.PdbDllRbld);
+ print_u32("ModInfoSize", dbi.ModInfoSize);
+ print_u32("SectionContributionSize", dbi.SectionContributionSize);
+ print_u32("SectionMapSize", dbi.SectionMapSize);
+ print_u32("SourceInfoSize", dbi.SourceInfoSize);
+ print_u32("TypeServerMapSize", dbi.TypeServerMapSize);
+ print_u32("MFCTypeServerIndex", dbi.MFCTypeServerIndex);
+ print_u32("OptionalDbgHeaderSize", dbi.OptionalDbgHeaderSize);
+ print_u32("ECSubstreamSize", dbi.ECSubstreamSize);
+ print_flags16("Flags", dbi.Flags,
+ "IncrementallyLinked".ptr, PdbRaw_DbiFlags.IncrementallyLinked,
+ "PrivateSymbolsStripped".ptr, PdbRaw_DbiFlags.PrivateSymbolsStripped,
+ "ConflictingTypes".ptr, PdbRaw_DbiFlags.ConflictingTypes,
+ null);
+ print_x16("Machine", dbi.Machine, adbg_object_pe_machine_string(dbi.Machine));
+ print_u32("Padding", dbi.Padding);
}
-}
-+/
\ No newline at end of file
+ adbg_object_pdb70_stream_close(o, cast(void**)&dbi);
+
+ /*uint strcnt = o.i.pdb70.strcnt;
+ for (uint stridx = 5; stridx < strcnt; ++stridx) {
+ char[32] buf = void;
+ int l = snprintf(buf.ptr, 32, "Stream %u", stridx);
+ }*/
+}
\ No newline at end of file
diff --git a/app/dumper.d b/app/dumper.d
index 3ccb4bd3..3ff0dea5 100644
--- a/app/dumper.d
+++ b/app/dumper.d
@@ -165,6 +165,11 @@ void print_header(const(char)* name) {
printf("\n# %s\n", name);
}
+// Field name only
+void print_name(const(char)* name) {
+ printf("%*s: ", __field_padding, name);
+}
+
void print_section(uint i, const(char) *name = null, int len = 0) {
putchar('\n');
print_u32("index", i);
@@ -263,6 +268,7 @@ void print_string(const(char)* name, const(char)* val) {
void print_stringl(const(char)* name, const(char)* val, int len) {
printf("%*s: %.*s\n", __field_padding, name, len, val);
}
+//TODO: print_stringf
void print_flags16(const(char) *section, ushort flags, ...) {
printf("%*s: 0x%04x\t(", __field_padding, section, flags);
diff --git a/app/main.d b/app/main.d
index a1ba7d6f..fc209133 100644
--- a/app/main.d
+++ b/app/main.d
@@ -87,6 +87,8 @@ struct option_t {
//TODO: --dump-length/--dump-end: Length or end
//TODO: --dump-imports-all: Dependency walker
//TODO: --dump-section=name: Hex or raw dump section
+//TODO: --dump-stats: File statistics?
+// pdb: stream count, positions, etc.
immutable option_t[] options = [
// general
{ 'a', "arch", "Select architecture for disassembler (default=platform)", true, fa: &cli_march },
diff --git a/src/adbg/debugger/process.d b/src/adbg/debugger/process.d
index 0caae8f7..7560fcd4 100644
--- a/src/adbg/debugger/process.d
+++ b/src/adbg/debugger/process.d
@@ -834,32 +834,38 @@ int adbg_process_get_pid(adbg_process_t *tracee) {
}
/// Get the process file path.
+///
+/// The string is null-terminated.
/// Params:
/// pid = Process ID.
/// buffer = Buffer.
/// bufsize = Size of the buffer.
/// basename = Request for basename; Otherwise full file path.
-/// Returns: Error code.
-int adbg_process_get_name(int pid, char *buffer, size_t bufsize, bool basename) {
+/// Returns: String length; Or zero on error.
+size_t adbg_process_get_name(int pid, char *buffer, size_t bufsize, bool basename) {
if (pid <= 0 || buffer == null || bufsize == 0)
return adbg_oops(AdbgError.nullArgument);
version (Windows) {
+ // Get process handle
HANDLE hand = OpenProcess(
PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
FALSE, pid);
- if (hand == null)
- return adbg_oops(AdbgError.os);
+ if (hand == null) {
+ adbg_oops(AdbgError.os);
+ return 0;
+ }
scope(exit) CloseHandle(hand);
+ // Get filename or basename
uint bf = cast(uint)bufsize;
uint r = basename ?
GetModuleBaseNameA(hand, null, buffer, bf) :
GetModuleFileNameA(hand, buffer, bf);
buffer[r] = 0;
if (r == 0)
- return adbg_oops(AdbgError.os);
- return 0;
+ adbg_oops(AdbgError.os);
+ return r;
} else version (linux) {
enum TBUFSZ = 32;
char[TBUFSZ] pathbuf = void; // Path buffer
@@ -877,12 +883,16 @@ int adbg_process_get_name(int pid, char *buffer, size_t bufsize, bool basename)
if (r <= 0) {
snprintf(pathbuf.ptr, TBUFSZ, "/proc/%d/comm", pid);
int commfd = open(pathbuf.ptr, O_RDONLY);
- if (commfd == -1)
- return adbg_oops(AdbgError.os);
+ if (commfd == -1) {
+ adbg_oops(AdbgError.os);
+ return 0;
+ }
scope(exit) close(commfd);
r = read(commfd, buffer, bufsize);
- if (r < 0)
- return adbg_oops(AdbgError.os);
+ if (r < 0) {
+ adbg_oops(AdbgError.os);
+ return 0;
+ }
buffer[r - 1] = 0; // Delete newline
}
@@ -905,9 +915,11 @@ int adbg_process_get_name(int pid, char *buffer, size_t bufsize, bool basename)
}*/
+ return r;
+ } else {
+ adbg_oops(AdbgError.unimplemented);
return 0;
- } else
- return adbg_oops(AdbgError.unimplemented);
+ }
}
/// Get the current runtime machine platform.
@@ -957,7 +969,7 @@ struct adbg_process_list_t {
}
//TODO: Redo adbg_process_enumerate
-// int adbg_process_list(adbg_process_list_t*)
+// int* adbg_process_list(size_t *count, ...)
// - List of PIDs instead, use adbg_process_get_name for file path.
// NOTE: For the C vararg to work, list is a parameter instead of a return value.
diff --git a/src/adbg/object/format/pdb.d b/src/adbg/object/format/pdb.d
index 887c4fa3..6f8f7b37 100644
--- a/src/adbg/object/format/pdb.d
+++ b/src/adbg/object/format/pdb.d
@@ -9,13 +9,22 @@ import adbg.error;
import adbg.object.server;
import adbg.utils.uid;
import adbg.utils.bit;
+import core.stdc.stdlib : malloc, free;
+import core.stdc.string : memcpy;
// Sources:
// - https://llvm.org/docs/PDB/MsfFile.html
+// - llvm/include/llvm/DebugInfo/PDB/ and llvm-pdbutil(1)
// - https://github.com/microsoft/microsoft-pdb
// - https://github.com/ziglang/zig/blob/master/lib/std/pdb.zig
// - https://github.com/MolecularMatters/raw_pdb
+//TODO: Find better memory management strategies
+// Since the PDB file is loaded entirely in memory (by us),
+// instead of allocating memory and copying blocks,
+// it could make sense to move blocks and modify the indexes,
+// but would require a *lot* of maintenance...
+
//
// Windows PDB
//
@@ -51,102 +60,456 @@ pdb20_file_header* adbg_object_pdb20_header(adbg_object_t *o) {
// Microsoft PDB 7.0
//
-// Structure:
-// file header
-// +- stream directory
-// +- count of streams
-// stream size (repeats n times)
-// +- size
-// stream offsets (dir size - ((num streams + 1) * 4))
-// +- page location
-// +- type?
-
-/* LLDB: llvm/include/llvm/DebugInfo/PDB/Native/RawConstants.h
-enum PdbRaw_ImplVer : uint32_t {
- PdbImplVC2 = 19941610,
- PdbImplVC4 = 19950623,
- PdbImplVC41 = 19950814,
- PdbImplVC50 = 19960307,
- PdbImplVC98 = 19970604,
- PdbImplVC70Dep = 19990604, // deprecated
- PdbImplVC70 = 20000404,
- PdbImplVC80 = 20030901,
- PdbImplVC110 = 20091201,
- PdbImplVC140 = 20140508,
-};
-
-enum PdbRaw_Features : uint32_t {
- PdbFeatureNone = 0x0,
- PdbFeatureContainsIdStream = 0x1,
- PdbFeatureMinimalDebugInfo = 0x2,
- PdbFeatureNoTypeMerging = 0x4,
-};
-
-enum PdbRaw_DbiVer : uint32_t {
- PdbDbiVC41 = 930803,
- PdbDbiV50 = 19960307,
- PdbDbiV60 = 19970606,
- PdbDbiV70 = 19990903,
- PdbDbiV110 = 20091201
-};
-
-enum PdbRaw_TpiVer : uint32_t {
- PdbTpiV40 = 19950410,
- PdbTpiV41 = 19951122,
- PdbTpiV50 = 19961031,
- PdbTpiV70 = 19990903,
- PdbTpiV80 = 20040203,
-};
-
-enum PdbRaw_DbiSecContribVer : uint32_t {
- DbiSecContribVer60 = 0xeffe0000 + 19970605,
- DbiSecContribV2 = 0xeffe0000 + 20140516
-};
-*/
+// # Glossary
+//
+// Block
+// Building blocks (also known as pages). The size of the block is given
+// by the Superblock (the very first block of the file).
+//
+// Stream
+// A Stream is contained in multiple blocks.
+//
+// # Structure
+//
+// 1. The Superblock is read, going to block directory
+// Directory offset: DirectoryOffset * PageSize
+// Directory block count: ceil(DirectorySize / PageSize)
+// Selects FPM by index
+// 2. Directory blocks are read (by n count), giving Stream 0
+// 3. Stream 0 entries contains n count of streams, their sizes, and offsets
+//
+// Blocks
+// vvv
+// 1 +---+ -+
+// +----- | | +- B[0]: Superblock
+// | +---+ -+ Contains FPM index used, BlockSize, and directory offset (by page)
+// | | | |
+// | +---+ +- B[1..2]: Two FPM blocks, acts as a huge array of bitfields
+// | | | | 1-bit/block: 0=unallocated/unused, 1=allocated/used
+// | 2 +---+ -+
+// | +--> | | |
+// | | 3 +---+ +- B[3..4095]: Data blocks (1 or more or any block)
+// | | +- | | | Stream 0 will redirect to other streams
+// | | | +---+ |
+// | | +> | | |
+// | | +---+ -+
+// | | ... If there are more than 4096 blocks:
+// | | +---+ -+
+// | | | | +- B[4096]: Data
+// | | +---+ -+
+// | | | | |
+// | | +---+ +- B[4097..4098]: FPM blocks. Kept for compatibility
+// | | | | |
+// | | +---+ -+
+// | | | | |
+// | | +---+ +- B[4099..8191]: Data blocks (1 or more)
+// | | | | |
+// | | +---+ -+
+// | | ...
+// | | +---+ -+- Block directory, usually at the end
+// | +--- | | | Contains block IDs pointing to Stream 0:
+// | +---+ + - uint streamcount;
+// +----> | | | - uint[streamcount] sizes; (by stream)
+// +---+ -+ - uint[streamcount] offsets;
+//
immutable string PDB70_MAGIC = "Microsoft C/C++ MSF 7.00\r\n\x1aDS\0\0\0";
-enum {
- /// Pdb (header)
- PDB_STREAM_HEADER = 1,
- /// Tpi (Type manager)
- PDB_STREAM_TPI = 2,
- /// Dbi (Debug information)
- PDB_STREAM_DBI = 3,
- /// NameMap
- PDB_STREAM_NAMEMAP = 4,
+// MSF container
+struct pdb70_file_header {
+ char[32] Magic; /// Magic string
+ uint PageSize; /// Usually 0x1000
+ uint FreeIndex; /// FPM index
+ uint PageCount; /// Block count * PAGESIZE = Byte size
+ uint DirectorySize; /// Size of block directory
+ uint Unknown; /// Reserved
+ uint DirectoryOffset; /// Offset (in block) * PAGESIZE = Byte offset in file
}
-//
-struct pdb70_file_header {
- char[32] Magic;
- uint PageSize; // Usually 0x400
- uint FreeIndex; // Index where block is free, only 1 or 2
- uint PageCount; // * PAGESIZE = Byte size
- uint DirectorySize;
- uint Unknown2;
- uint DirectoryOffset; // * PAGESIZE = Byte offset in file
+/// Meta structure used internally
+struct pdb70_stream {
+ /// Number of blocks stream occupies
+ uint count;
+ /// Points to stream's first block from directory (Stream 0)
+ uint *blocks;
+}
+
+/// Fixed streams
+enum PdbStream : uint {
+ /// PDB fixed stream 1
+ ///
+ /// Contains: Basic file information, named streams
+ pdb = 1,
+ /// TPI fixed stream 2
+ ///
+ /// Contains: CodeView type records, TPI hash stream
+ tpi = 2,
+ /// DBI fixed stream 3
+ ///
+ /// Contains: Module info and streams, section contribs, source, FPO/PGO
+ dbi = 3,
+ /// PIP fixed stream 4
+ ///
+ /// Contains: CodeView type records, index of ipi hash stream
+ ipi = 4,
}
-struct pdb70_stream_header {
+enum : uint {
+ /// Unallocated block size.
+ PDB_BLOCK_SIZE_UNUSED = 0xffff_ffff,
+}
+
+//
+// Stream 1 (PDB) structures
+//
+
+/// Stream 1 PDB header::version
+enum PdbRaw_PdbVersion : uint { // PdbRaw_ImplVer
+ vc2 = 19941610,
+ vc4 = 19950623,
+ vc41 = 19950814,
+ vc50 = 19960307,
+ vc98 = 19970604,
+ vc70_old = 19990604, // deprecated
+ vc70 = 20000404,
+ vc80 = 20030901,
+ vc110 = 20091201,
+ vc140 = 20140508,
+}
+
+/// Stream 1 PDB feature codes (after named stream map)
+enum PdbRaw_PdbFeatures : uint {
+ none = 0x0,
+ containsIdStream = 0x1,
+ minimalDebugInfo = 0x2,
+ noTypeMerging = 0x4,
+}
+
+/// Stream 1 structure
+struct pdb70_pdb_header {
+ /// Contains VC version
uint Version;
+ /// Timestamp (Using time(3))
uint Signature;
+ /// Incremental number
uint Age;
+ /// Unique GUID, used to match PDB and EXE
UID UniqueId;
}
+//
+// Stream 2 (TPI) structures
+//
+
+/// Stream 2 TPI
+enum PdbRaw_TpiVer : uint {
+ v40 = 19950410,
+ v41 = 19951122,
+ v50 = 19961031,
+ v70 = 19990903,
+ v80 = 20040203,
+}
+
+/// Stream 2 TPI header
+struct pdb70_tpi_header {
+ /// Maps to PdbRaw_TpiVer, usually v80.
+ uint Version;
+ /// Usually size of this header.
+ uint HeaderSize;
+ /// First index of first type record.
+ ///
+ /// Usually 0x1000 (page size?), since lower is reserved.
+ uint TypeIndexBegin;
+ /// Last index for the last type record.
+ ///
+ /// To get total count: TypeIndexEnd - TypeIndexBegin.
+ uint TypeIndexEnd;
+ /// Size of type record data following header.
+ uint TypeRecordBytes;
+
+ /// Index of a stream containing list of hashes for every
+ /// type record.
+ ///
+ /// If -1 (0xffff), unused.
+ ushort HashStreamIndex;
+ ///
+ ushort HashAuxStreamIndex;
+ /// Size of a hash, usually 4 (bytes).
+ uint HashKeySize;
+ ///
+ uint NumHashBuckets;
+
+ int HashValueBufferOffset;
+ // Malformed: HashBufferLength != (TypeIndexEnd - TypeEndBegin) * HashKeySize
+ uint HashValueBufferLength;
+
+ int IndexOffsetBufferOffset;
+ uint IndexOffsetBufferLength;
+
+ int HashAdjBufferOffset;
+ uint HashAdjBufferLength;
+}
+
+
+//
+// Stream 3 (DBI)
+//
+
+// Stream 3 DBI header::version
+enum PdbRaw_DbiVer : uint {
+ v41 = 930803,
+ v50 = 19960307,
+ v60 = 19970606,
+ v70 = 19990903,
+ v110 = 20091201,
+}
+
+// Stream DBI
+enum PdbRaw_DbiSecContribVer : uint {
+ ver60 = 0xeffe0000 + 19970605,
+ v2 = 0xeffe0000 + 20140516
+}
+
+//
+enum PdbRaw_DbiFlags : ushort {
+ IncrementallyLinked = 1, /// WasIncrementallyLinked
+ PrivateSymbolsStripped = 2, /// ArePrivateSymbolsStripped
+ ConflictingTypes = 4, /// HasConflictingTypes
+}
+
+/// Stream 3 DBI header
+struct pdb70_dbi_header {
+ /// Seems to be always -1.
+ int VersonSignature;
+ /// Maps to PdbRaw_DbiVersion.
+ uint VersionHeader;
+ /// Incremental age.
+ uint Age;
+ /// Global Symbol Stream index;
+ ushort GlobalStreamIndex;
+ /// Toolchain version.
+ ///
+ /// bits 15-8: MinorVersion
+ /// bits 7-1: MajorVersion
+ /// bits 0: NewVersionFormat, assume to be set, or consult source.
+ ushort BuildNumber;
+ /// Public Symbol Stream index.
+ ushort PublicStreamIndex;
+ /// Version for mspdbXXXX.dll.
+ ushort PdbDllVersion;
+ /// Deduplication stream containing CodeView symbols.
+ ushort SymRecordStream;
+ ///
+ ushort PdbDllRbld;
+
+ // Substream info
+
+ /// The length of the Module Info Substream. (Substream 1)
+ int ModInfoSize;
+ /// The length of the Section Contribution Substream. (Substream 2)
+ int SectionContributionSize;
+ /// The length of the Section Map Substream. (Substream 3)
+ int SectionMapSize;
+ /// The length of the File Info Substream. (Substream 4)
+ int SourceInfoSize;
+ /// The length of the Type Server Map Substream. (Substream 5)
+ int TypeServerMapSize;
+ /// MFC type server in Type Server Map Substream.
+ uint MFCTypeServerIndex;
+ /// The length of the Optional Debug Header Stream. (Substream 6)
+ int OptionalDbgHeaderSize;
+ /// The length of the EC Substream. (Substream 7)
+ int ECSubstreamSize;
+
+ /// Program information bit field.
+ ///
+ /// uint16_t WasIncrementallyLinked : 1;
+ /// uint16_t ArePrivateSymbolsStripped : 1;
+ /// uint16_t HasConflictingTypes : 1;
+ /// uint16_t Reserved : 13;
+ ushort Flags;
+ /// A PE32 Machine value. from the CV_CPU_TYPE_e enumeration.
+ ///
+ /// LLVM says "A value from the CV_CPU_TYPE_e enumeration.
+ /// Common values are 0x8664 (x86-64) and 0x14C (x86).", but these are
+ /// PE32 Machine values.
+ ushort Machine;
+ /// ?
+ uint Padding;
+}
+
+/// Follows the DBI header, substream information
+struct pdb70_dbi_modinfo {
+ ///
+ uint Unused1;
+ struct pdb70_dbi_mod_contrib_entry {
+ uint Section;
+ char[2] Padding1;
+ int Offset;
+ int Size;
+ uint Characteristics;
+ ushort ModuleIndex;
+ char[2] Padding2;
+ uint DataCrc;
+ uint RelocCrc;
+ }
+ /// Matches Characteristics from IMAGE_SECTION_HEADER
+ pdb70_dbi_mod_contrib_entry SectionContr;
+ // int16_t Dirty : 1; // Likely due to incremental linking.
+ // int16_t EC : 1; // Edit & Continue
+ // int16_t Unused : 6;
+ // int16_t TSM : 8; // Type Server Index for module.
+ /// Flags.
+ ushort Flags;
+ ushort ModuleSysStream;
+ uint SymByteSize;
+ uint C11ByteSize;
+ uint C13ByteSize;
+ ushort SourceFileCount;
+ char[2] Padding;
+ uint Unused2;
+ uint SourceFileNameIndex;
+ uint PdbFilePathNameIndex;
+ // char[] ModuleName
+ // char[] ObjFileName
+}
+
+//
+// Stream 4 (IPI)
+//
+
+enum PdbSubsectionKind : uint {
+ none = 0,
+ symbols = 0xf1,
+ lines = 0xf2,
+ stringTable = 0xf3,
+ fileChecksums = 0xf4,
+ frameData = 0xf5,
+ inlineeLines = 0xf6,
+ crossScopeImports = 0xf7,
+ crossScopeExports = 0xf8,
+
+ // Related to .NET
+ illines = 0xf9, // CIL lines
+ funcMDTokenMap = 0xfa,
+ typeMDTokenMap = 0xfb,
+ mergedAssemblyInput = 0xfc,
+
+ coffSymbolRVA = 0xfd,
+}
+
+struct pdb70_subsection_header {
+ PdbSubsectionKind Kind;
+ uint Length;
+}
+
+struct pdb70_stringtable_header {
+ uint Signature;
+ uint HashVersion;
+ uint ByteSize;
+}
+
int adbg_object_pdb70_load(adbg_object_t *o, size_t offset = 0) {
o.format = AdbgObject.pdb70;
o.p.debug_offset = offset;
- with (o.i.pdb70.header)
- if (PageSize < 512 || // Cannot be lower than 512 bytes
- PageSize > 4096 || // Not observed to be higher than 4,096 bytes
- PageSize % 512 != 0 || // Must be a multiple of "sectors"
- PageCount * PageSize != o.file_size || // Must fit file length
- ((FreeIndex == 1 || FreeIndex == 2) == false))
+ with (o.i.pdb70) {
+
+ // Check SuperBlock
+ if (header.PageSize < 512 || // Cannot be lower than 512 bytes
+ header.PageSize > 4096 || // Not observed to be higher than 4,096 bytes
+ header.PageSize % 512 != 0 || // Must be a multiple of "sectors"
+ header.PageCount * header.PageSize != o.file_size || // Must fit file length
+ header.Unknown || // Must be empty (for now)
+ ((header.FreeIndex == 1 || header.FreeIndex == 2) == false)) // Can only be block 1 or 2
+ return adbg_oops(AdbgError.assertion);
+
+ // Cache FPM pointer
+ with (o.i.pdb70)
+ if (adbg_object_offsetl(o, cast(void**)&fpm,
+ header.FreeIndex * header.PageSize, header.PageSize))
+ return adbg_oops(AdbgError.assertion);
+
+ //
+ // Load Stream 0 into memory
+ //
+
+ // block count used by block directory = ceil(DirectorySize / PageSize)
+ uint dircnt = (header.DirectorySize + header.PageSize - 1) / header.PageSize;
+ // block id ptr = Superblock::DirectoryOffset * ::PageSize
+ uint diroff = header.DirectoryOffset * header.PageSize;
+
+ version (Trace) trace("dircnt=%u diroff=%u", dircnt, diroff);
+
+ //TODO: dircnt > PageSize / uint.sizeof -> Unhandled BigDirectoryStream
+
+ // Allocate buffer for block directory
+ // DirectorySize is smaller but makes it harder to copy blocks
+ dir = cast(uint*)malloc(dircnt * header.PageSize);
+ if (dir == null)
+ return adbg_oops(AdbgError.crt);
+
+ //
+ uint *dirblk = void;
+ if (adbg_object_offsetl(o, cast(void**)&dirblk, diroff, header.PageSize)) {
+ free(dir);
return adbg_oops(AdbgError.assertion);
+ }
+
+ // Load stream directory blocks into the memory buffer
+ for (uint dirblkidx; dirblkidx < dircnt; ++dirblkidx) {
+ version (Trace) trace("dirblk[%u]=%u", dirblkidx, dirblk[dirblkidx]);
+ uint blockoff = dirblk[dirblkidx] * header.PageSize;
+ void *block = void;
+ if (adbg_object_offsetl(o, &block, blockoff, header.PageSize)) {
+ free(dir);
+ return adbg_oops(AdbgError.assertion);
+ }
+
+ memcpy(dir + (dirblkidx * header.PageSize), block, header.PageSize);
+ }
+
+ // Setup stream directory information
+ uint *direntry = cast(uint*)dir;
+ strcnt = *direntry;
+ strsize = direntry + 1;
+ stroff = direntry + 1 + strcnt;
+
+ //
+ // Map stream information (count and IDs)
+ // This helps to avoid recalculating the block offsets everytime
+ //
+
+ // Allocate buffer for the stream map
+ strmap = cast(pdb70_stream*)malloc(strcnt * pdb70_stream.sizeof);
+ if (strmap == null) {
+ free(dir);
+ return adbg_oops(AdbgError.crt);
+ }
+ // Map stream block IDs to memory buffer
+ uint *blkcur = stroff;
+ for (uint stri; stri < strcnt; ++stri) {
+ pdb70_stream *stream = &strmap[stri];
+
+ uint size = strsize[stri];
+ if (size == 0 || size == 0xffff_ffff) {
+ stream.count = 0;
+ continue;
+ }
+
+ // Block count for stream
+ stream.count = (size + header.PageSize - 1) / header.PageSize;
+ stream.blocks = blkcur;
+
+ version (Trace)
+ trace("stream[%u] count=%u blk0=%u",
+ stri, stream.count, *stream.blocks);
+
+ blkcur += stream.count;
+ }
+
+ } // with (o.i.pdb70)
return 0;
}
@@ -155,8 +518,92 @@ pdb70_file_header* adbg_object_pdb70_header(adbg_object_t *o) {
return o.i.pdb70.header;
}
-/+ubyte* adbg_object_pdb70_get_stream(adbg_object_t *o, int num) {
-}+/
+// true=unallocated
+bool adbg_object_pdb70_block_free(adbg_object_t *o, uint num) {
+ if (o == null)
+ return true;
+
+ uint bi = num / 8; // block byte index
+ uint br = 7 - (num % 8); // block reminder shift
+ bool free = (o.i.pdb70.fpm[bi] & 1 << br) == 0;
+ return free == 0;
+}
+
+ubyte* adbg_object_pdb70_get_block(adbg_object_t *o, uint num) {
+ if (o == null)
+ return null;
+
+ pdb70_file_header *header = o.i.pdb70.header;
+
+ // Check with selected FPM if block is allocated
+ if (adbg_object_pdb70_block_free(o, num))
+ return null;
+
+ // Get block
+ ubyte *block = void;
+ if (adbg_object_offsetl(o, cast(void**)&block,
+ num * header.PageSize, header.PageSize))
+ return null;
+
+ return block;
+}
+
+int adbg_object_pdb70_stream_open(adbg_object_t *o, void **ubuffer, uint *usize, uint num) {
+ if (o == null || ubuffer == null || usize == null)
+ return adbg_oops(AdbgError.invalidArgument);
+ if (num >= o.i.pdb70.strcnt)
+ return adbg_oops(AdbgError.assertion);
+
+ // Get stream size and availability
+ uint ssize = *usize = o.i.pdb70.strsize[num];
+ version (Trace) trace("stream size=%u", ssize);
+ if (ssize == 0 || ssize == PDB_BLOCK_SIZE_UNUSED) {
+ *ubuffer = null;
+ return 0;
+ }
+
+ // Allocate buffer
+ void *sbuffer = *ubuffer = malloc(ssize);
+ if (sbuffer == null)
+ return adbg_oops(AdbgError.crt);
+
+ // Read blocks into buffer
+ pdb70_stream *stream = &o.i.pdb70.strmap[num];
+ uint pagesize = o.i.pdb70.header.PageSize;
+ uint offset; // read count
+ uint readsz = pagesize;
+ version (Trace) trace("stream counts %u blocks", stream.count);
+ for (uint blki; blki < stream.count; ++blki) {
+ version (Trace) trace("stream block[%u]=%u", blki, stream.blocks[blki]);
+
+ //TODO: Check if block is allocated !!!
+ uint fileoff = stream.blocks[blki] * pagesize;
+ void *block = void;
+ if (adbg_object_offsetl(o, &block, fileoff, readsz)) {
+ free(sbuffer);
+ *ubuffer = null;
+ return adbg_oops(AdbgError.assertion);
+ }
+
+ // Adjust read size on last block
+ if (offset + readsz > ssize) readsz = ssize - offset;
+
+ version (Trace) trace("offset=%p", sbuffer);
+ memcpy(sbuffer + offset, block, readsz);
+
+ offset += readsz;
+ }
+
+ return 0;
+}
+void adbg_object_pdb70_stream_close(adbg_object_t *o, void **ubuffer) {
+ if (o == null || ubuffer == null)
+ return;
+
+ if (*ubuffer) {
+ free(*ubuffer);
+ }
+}
//
// Portable PDB and CILDB
diff --git a/src/adbg/object/server.d b/src/adbg/object/server.d
index 4a76e3d1..bfa79874 100644
--- a/src/adbg/object/server.d
+++ b/src/adbg/object/server.d
@@ -265,6 +265,16 @@ struct adbg_object_t {
struct pdb70_t {
pdb70_file_header *header;
+ ubyte *fpm; /// Points to used FPM block
+ void *dir; /// Directory buffer (Stream 0)
+
+ // Stream 0 meta
+ uint strcnt; /// Stream count
+ uint *strsize; /// Stream size (Points to Stream[0].size)
+ uint *stroff; /// Block IDs (Points to Stream[0].block[0])
+
+ // Lookup table made from Stream 0
+ pdb70_stream *strmap; /// Stream mapping
}
pdb70_t pdb70;
}
@@ -449,6 +459,10 @@ void adbg_object_close(adbg_object_t *o) {
default:
}
break;
+ case pdb70:
+ with (o.i.pdb70) if (dir) free(dir);
+ with (o.i.pdb70) if (strmap) free(strmap);
+ break;
default:
}
if (o.file_handle)
diff --git a/src/adbg/object/types/cv.d b/src/adbg/object/types/cv.d
new file mode 100644
index 00000000..6613d6fd
--- /dev/null
+++ b/src/adbg/object/types/cv.d
@@ -0,0 +1,72 @@
+/// CodeView type records and information.
+///
+/// Authors: dd86k
+/// Copyright: © dd86k
+/// License: BSD-3-Clause
+module adbg.object.types.cv;
+
+alias CV_CPU_TYPE_e = ushort;
+enum : CV_CPU_TYPE_e {
+ CV_CFL_8080 = 0x00,
+ CV_CFL_8086 = 0x01,
+ CV_CFL_80286 = 0x02,
+ CV_CFL_80386 = 0x03,
+ CV_CFL_80486 = 0x04,
+ CV_CFL_PENTIUM = 0x05,
+ CV_CFL_PENTIUMII = 0x06,
+ CV_CFL_PENTIUMPRO = CV_CFL_PENTIUMII,
+ CV_CFL_PENTIUMIII = 0x07,
+ CV_CFL_MIPS = 0x10,
+ CV_CFL_MIPSR4000 = CV_CFL_MIPS,
+ CV_CFL_MIPS16 = 0x11,
+ CV_CFL_MIPS32 = 0x12,
+ CV_CFL_MIPS64 = 0x13,
+ CV_CFL_MIPSI = 0x14,
+ CV_CFL_MIPSII = 0x15,
+ CV_CFL_MIPSIII = 0x16,
+ CV_CFL_MIPSIV = 0x17,
+ CV_CFL_MIPSV = 0x18,
+ CV_CFL_M68000 = 0x20,
+ CV_CFL_M68010 = 0x21,
+ CV_CFL_M68020 = 0x22,
+ CV_CFL_M68030 = 0x23,
+ CV_CFL_M68040 = 0x24,
+ CV_CFL_ALPHA = 0x30,
+ CV_CFL_ALPHA_21064 = CV_CFL_ALPHA,
+ CV_CFL_ALPHA_21164 = 0x31,
+ CV_CFL_ALPHA_21164A = 0x32,
+ CV_CFL_ALPHA_21264 = 0x33,
+ CV_CFL_ALPHA_21364 = 0x34,
+ CV_CFL_PPC601 = 0x40,
+ CV_CFL_PPC603 = 0x41,
+ CV_CFL_PPC604 = 0x42,
+ CV_CFL_PPC620 = 0x43,
+ CV_CFL_PPCFP = 0x44,
+ CV_CFL_SH3 = 0x50,
+ CV_CFL_SH3E = 0x51,
+ CV_CFL_SH3DSP = 0x52,
+ CV_CFL_SH4 = 0x53,
+ CV_CFL_SHMEDIA = 0x54, /// SuperH 4A?
+ CV_CFL_ARM3 = 0x60,
+ CV_CFL_ARM4 = 0x61,
+ CV_CFL_ARM4T = 0x62,
+ CV_CFL_ARM5 = 0x63,
+ CV_CFL_ARM5T = 0x64,
+ CV_CFL_ARM6 = 0x65,
+ CV_CFL_ARM_XMAC = 0x66, /// Arm with XMAC instruction
+ CV_CFL_ARM_WMMX = 0x67, /// Intel XScale Wireless MMX
+ CV_CFL_OMNI = 0x70, /// Intel Omni-Path Architecture?
+ CV_CFL_IA64 = 0x80,
+ CV_CFL_IA64_1 = 0x80,
+ CV_CFL_IA64_2 = 0x81,
+ CV_CFL_CEE = 0x90, /// COM+ EE (.NET IL instructions)
+ CV_CFL_AM33 = 0xA0,
+ CV_CFL_M32R = 0xB0,
+ CV_CFL_TRICORE = 0xC0,
+ CV_CFL_X64 = 0xD0,
+ CV_CFL_AMD64 = CV_CFL_X64,
+ CV_CFL_EBC = 0xE0, /// EFI Byte Code
+ CV_CFL_THUMB = 0xF0,
+ CV_CFL_ARMNT = 0xF4,
+ CV_CFL_D3D11_SHADER = 0x100,
+}
\ No newline at end of file