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