Mercurial > projects > ddmd
view dbg/symbol/CodeView.d @ 161:584dc990e12f
type fixed
author | korDen |
---|---|
date | Mon, 20 Sep 2010 01:19:36 +0400 |
parents | 10317f0c89a5 |
children |
line wrap: on
line source
/** This module is used to extract CodeView symbolic debugging information and to perform queries upon that information. TODO: * Add support for CodeView 5.0 and PDB formats. * Add support to extract type information. Authors: Jeremie Pelletier References: $(LINK http://www.x86.org/ftp/manuals/tools/sym.pdf) $(LINK http://undocumented.rawol.com/sbs-w2k-1-windows-2000-debugging-support.pdf) $(LINK http://www.microsoft.com/msj/0399/hood/hood0399.aspx) $(LINK http://source.winehq.org/source/include/wine/mscvpdb.h) $(LINK http://www.digitalmars.com/d/2.0/abi.html) License: Public Domain */ module dbg.symbol.CodeView; import dbg.Debug; class CodeViewDebugInfo : ISymbolicDebugInfo { /** Load CodeView data from the given memory view. */ this(in void[] view) in { assert(view.length && view.ptr); } body { _view = view; auto header = cast(CV_HEADER*)_view.ptr; CheckOffset(header.offset); // TODO: Only supporting NB09 (CodeView 4.10) right now if(!header.signature == CV_SIGNATURE_NB09) throw new CodeViewUnsupportedException(this); auto dir = cast(CV_DIRECTORY*)(view.ptr + header.offset); if(dir.dirSize != CV_DIRECTORY.sizeof || dir.entrySize != CV_ENTRY.sizeof) throw new CodeViewCorruptedException(this); CvModule globalModule; _modules ~= globalModule; foreach(ref e; dir.entries) { CheckOffset(e.offset); switch(e.sst) { case sstModule: ParseModule(&e); break; case sstLibraries: ParseLibraries(&e); break; case sstAlignSym: ParseAlignSymbols(&e); break; case sstSrcModule: ParseSrcModule(&e); break; case sstGlobalPub: case sstStaticSym: case sstGlobalSym: ParseHashSymbols(&e); break; case sstGlobalTypes: ParseGlobalTypes(&e); break; // TODO: /*case sstFileIndex: case sstSegMap: case sstSegName:*/ default: } } } /** Get the procedure symbol matching the given address. */ SymbolInfo ResolveSymbol(size_t rva) const in { assert(rva); } body { SymbolInfo symbol; foreach(ref m; _modules[0 .. _maxSymModule + 1]) if(m.symbols.QueryProc(rva, &symbol)) goto Found; foreach(ref m; _modules[0 .. _maxSymModule + 1]) if(m.symbols.QueryCodeData(rva, &symbol)) goto Found; Found: return symbol; } /** Get the file/line mapping corresponding to the given relative address. */ FileLineInfo ResolveFileLine(size_t rva) const in { assert(rva); } body { FileLineInfo fileLine; if(_maxSrcModule) foreach(m; _modules[1 .. _maxSrcModule + 1]) if(m.src.Query(rva, &fileLine)) break; return fileLine; } private: void ParseModule(in CV_ENTRY* e) { auto mod = cast(CV_MODULE*)(_view.ptr + e.offset); if(e.modIndex != _modules.length || mod.style != CV_MOD_STYLE) throw new CodeViewCorruptedException(this); with(*mod) _modules ~= CvModule(overlay, lib, segments, name.name); } void ParseLibraries(in CV_ENTRY* e) { if(e.modIndex != ushort.max) throw new CodeViewCorruptedException(this); auto name = cast(OMF_NAME*)(_view.ptr + e.offset); auto end = cast(const(void)*)name + e.size; while(name < end) { if(name.len) _libraries ~= name.name; name = cast(OMF_NAME*)(cast(void*)name + 1 + name.len); } } void ParseAlignSymbols(in CV_ENTRY* e) { if(e.modIndex == ushort.max || e.modIndex <= 0 || e.modIndex >= _modules.length) throw new CodeViewCorruptedException(this); if(e.modIndex > _maxSymModule) _maxSymModule = e.modIndex; auto sym = cast(CV_SYMBOL*)(_view.ptr + e.offset); if(sym.header.type == 0) sym = cast(CV_SYMBOL*)(cast(void*)sym + 4); _modules[e.modIndex].symbols.Init(sym, cast(void*)sym + e.size); } void ParseHashSymbols(in CV_ENTRY* e) { if(e.modIndex != ushort.max) throw new CodeViewCorruptedException(this); auto hash = cast(CV_SYMHASH*)(_view.ptr + e.offset); auto p = cast(void*)hash + CV_SYMHASH.sizeof; _modules[0].symbols.Init(cast(CV_SYMBOL*)p, p + hash.symInfoSize); } void ParseSrcModule(in CV_ENTRY* e) { if(e.modIndex == ushort.max || e.modIndex <= 0 || e.modIndex >= _modules.length) throw new CodeViewCorruptedException(this); if(e.modIndex > _maxSrcModule) _maxSrcModule = e.modIndex; auto src = cast(CV_SRCMODULE*)(_view.ptr + e.offset); with(_modules[e.modIndex].src) { data = src; fileOffsets = src.fileOffsets; codeOffsets = src.codeOffsets; segmentIds = src.segmentIds; } } void ParseGlobalTypes(in CV_ENTRY* e) { if(e.modIndex != ushort.max) throw new CodeViewCorruptedException(this); // TODO: this currently crash stuff randomly /*auto header = cast(CV_GLOBALTYPES*)(_view.ptr + e.offset); _types.Init(header, cast(void*)header + e.size);*/ } void CheckOffset(int offset) { if(offset > _view.length) throw new CodeViewCorruptedException(this); } const(void)[] _view; CvModule[] _modules; uint _maxSymModule; uint _maxSrcModule; string[] _libraries; CvTypes _types; } abstract class CodeViewException : Exception { this(string msg) { super(msg); } } class CodeViewUnsupportedException : CodeViewException { this(in CodeViewDebugInfo cv) { super("CodeView version unsupported."); } } class CodeViewCorruptedException : CodeViewException { this(in CodeViewDebugInfo cv) { super("Corrupted CodeView data."); } } private: alias int cmp_t; uint BinarySearch(scope cmp_t delegate(uint i) dg, uint low, uint high) { if(high < low) return uint.max; uint mid = low + ((high - low) / 2); cmp_t cmp = dg(mid); if(cmp > 0) return BinarySearch(dg, low, mid - 1); if(cmp < 0) return BinarySearch(dg, mid + 1, high); return mid; } uint BinarySearch(in uint[] a, uint value, uint low, uint high) { if(high < low) return uint.max; uint mid = low + ((high - low) / 2); if(a[mid] > value) return BinarySearch(a, value, low, mid - 1); if(a[mid] < value) return BinarySearch(a, value, mid + 1, high); return mid; } struct CvModule { ushort overlay; ushort lib; CV_SEGMENT[] segments; string name; CvSymbols symbols; CvSrcModule src; } struct CvSymbols { ubyte compileMachine; ubyte compileLanguage; ushort compileFlags; string compileName; ushort segment; CvProc[] procSymbols; CvData[] codeSymbols; void Init(const(CV_SYMBOL)* sym, in void* end) { int i = 0; while(sym < end && i < 100) { ++i; switch(sym.header.type) { case S_COMPILE_V1: with(sym.compile_v1) { compileMachine = machine; compileLanguage = language; compileFlags = flags; compileName = name.name; } break; case S_SSEARCH_V1: if(!segment) segment = sym.ssearch.segment; break; case S_UDT_V1: break; case S_BPREL_V1: break; case S_LDATA_V1: case S_GDATA_V1: case S_PUB_V1: CvData data = void; with(sym.data_v1) { // TODO: its bad to assume 2 to always be the only code segment! if(segment != 2) break; data.offset = offset; data.name = name.name; } codeSymbols ~= data; break; case S_LPROC_V1: case S_GPROC_V1: CvProc proc = void; with(sym.proc_v1) { proc.offset = offset; proc.length = procLength; proc.name = name.name; } procSymbols ~= proc; break; case S_PROCREF_V1: case S_DATAREF_V1: case S_ALIGN_V1: break; case S_END_V1: case S_ENDARG_V1: case S_RETURN_V1: break; default: } sym = cast(CV_SYMBOL*)(cast(void*)sym + sym.header.size + 2); } codeSymbols.sort; } bool QueryProc(uint rva, SymbolInfo* symbol) const { if(!procSymbols.length) return false; cmp_t CmpProc(uint i) { if(i >= procSymbols.length) return 0; uint offset = procSymbols[i].offset; if(offset > rva) return 1; if(offset + procSymbols[i].length < rva) return -1; return 0; } uint index = BinarySearch(&CmpProc, 0, procSymbols.length - 1); if(index < procSymbols.length) with(procSymbols[index]) { symbol.name = name.idup; symbol.offset = rva - offset; return true; } return false; } bool QueryCodeData(uint rva, SymbolInfo* symbol) const { if(!codeSymbols.length) return false; cmp_t CmpData(uint i) { if(i >= codeSymbols.length) return 0; if(codeSymbols[i].offset > rva) return 1; if(i + 1 != codeSymbols.length && codeSymbols[i + 1].offset < rva) return -1; return 0; } uint index = BinarySearch(&CmpData, 0, codeSymbols.length - 1); if(index < codeSymbols.length) with(codeSymbols[index]) { symbol.name = name.idup; symbol.offset = rva - offset; return true; } return false; } } struct CvProc { uint offset; uint length; string name; } struct CvData { uint offset; string name; cmp_t opCmp(ref const CvData data) const { if(data.offset < offset) return -1; return data.offset > offset; } } struct CvSrcModule { bool Query(uint rva, FileLineInfo* fileLine) const { if(!codeOffsets.length || rva < codeOffsets[0][0] || rva > codeOffsets[$ - 1][1]) return false; uint fIndex; // Get the next CV_SRCFILE record having rva within it's code range // The code offsets here may overlap over file records, we have to walk // through them and possibly keep walking if the next section doesn't // find a matching line record. NextFile: if(fIndex == fileOffsets.length) return false; CV_SRCFILE* srcFile = cast(CV_SRCFILE*)(data + fileOffsets[fIndex++]); uint[2][] offsets = srcFile.codeOffsets; if(rva < offsets[0][0] || rva > offsets[$ - 1][1]) goto NextFile; CV_SRCSEGMENT* srcSeg; // Address is possibly within this file, now get the CV_SEGMENT record. cmp_t CmpFile(uint i) { if(i >= offsets.length) return 0; if(offsets[i][0] > rva) return 1; if(offsets[i][1] < rva) return -1; srcSeg = cast(CV_SRCSEGMENT*)(data + srcFile.lineOffsets[i]); return 0; } // Ignore the return value from BinarySearch, if CmpSegment matched, we // already have srcSeg set. In some rare cases there may not be a // matching segment record even if the file's segment range said so. BinarySearch(&CmpFile, 0, offsets.length - 1); if(!srcSeg) goto NextFile; // Finally look within the segment's offsets for a matching record. uint[] segOffsets = srcSeg.offsets; ushort[] lineNumbers = srcSeg.lineNumbers; cmp_t CmpSegment(uint i) { if(i >= segOffsets.length) return 0; if(segOffsets[i] > rva) return 1; if(i + 1 < segOffsets.length && segOffsets[i + 1] < rva) return -1; return 0; } uint sIndex = BinarySearch(&CmpSegment, 0, segOffsets.length - 1); if(sIndex >= lineNumbers.length) goto NextFile; // Found our record fileLine.file = srcFile.name.name.idup; fileLine.line = srcSeg.lineNumbers[sIndex]; return true; } const(void)* data; const(uint)[] fileOffsets; const(uint[2])[] codeOffsets; const(ushort)[] segmentIds; } // TODO! struct CvTypes { void Init(in CV_GLOBALTYPES* gtypes, in void* end) { debug(CodeView) TraceA("CvTypes[%p].Init(gtypes=%p, end=%p)", &this, gtypes, end); offsets = gtypes.typeOffsets[0 .. gtypes.nTypes].idup; void* dataStart = gtypes.types; data = dataStart[0 .. end - dataStart].idup; } void GetType(ushort index) { /+ CheckOffset(typeOffsets[index]); CV_TYPE* type = cast(CV_TYPE*)(p + typeOffsets[i]); switch(type.header.type) { case LF_MODIFIER_V1: break; case LF_POINTER_V1: break; case LF_ARRAY_V1: break; case LF_CLASS_V1: break; case LF_STRUCTURE_V1: break; case LF_UNION_V1: break; case LF_ENUM_V1: break; case LF_PROCEDURE_V1: break; case LF_MFUNCTION_V1: break; case LF_VTSHAPE_V1: break; case LF_OEM_V1: with(type.oem_v1) { // Ignore unknown OEMs if(oem != OEM_DIGITALMARS || nIndices != 2) break; switch(rec) { case D_DYN_ARRAY: break; case D_ASSOC_ARRAY: break; case D_DELEGATE: break; default: } } break; case LF_ARGLIST_V1: break; case LF_FIELDLIST_V1: break; case LF_DERIVED_V1: break; case LF_METHODLIST_V1: break; default: TraceA("New leaf %x", cast(uint)type.header.type); Pause; } +/ } const(uint)[] offsets; const(void)[] data; } // ---------------------------------------------------------------------------- // O M F S t r u c t u r e s // ---------------------------------------------------------------------------- align(1): /** Packed variant header */ struct OMF_HEADER { short size; short type; } /** Packed name, may be 0 padded to maintain alignment */ struct OMF_NAME { ubyte len; //char[1] name; string name() const { return (cast(immutable(char)*)(&len + 1))[0 .. len]; } } // ---------------------------------------------------------------------------- // C o d e V i e w C o m m o n S t r u c t u r e s // ---------------------------------------------------------------------------- /** Version signatures */ enum : uint { CV_SIGNATURE_NB09 = 0x3930424E, /// CodeView 4.10 CV_SIGNATURE_NB11 = 0x3131424E, /// CodeView 5.0 CV_SIGNATURE_NB10 = 0x3130424E, /// CodeView PDB 2.0 CV_SIGNATURE_RSDS = 0x53445352 /// CodeView PDB 7.0 } /** SubSection Types */ enum : ushort { sstModule = 0x0120, sstTypes = 0x0121, sstPublic = 0x0122, sstPublicSym = 0x0123, sstSymbols = 0x0124, sstAlignSym = 0x0125, sstSrcLnSeg = 0x0126, sstSrcModule = 0x0127, sstLibraries = 0x0128, sstGlobalSym = 0x0129, sstGlobalPub = 0x012A, sstGlobalTypes = 0x012B, sstMPC = 0x012C, sstSegMap = 0x012D, sstSegName = 0x012E, sstPreComp = 0x012F, sstPreCompMap = 0x0130, sstOffsetMap16 = 0x0131, sstOffsetMap32 = 0x0132, sstFileIndex = 0x0133, sstStaticSym = 0x0134 } /** Header used with "NB09" and "NB11" */ struct CV_HEADER { uint signature; int offset; } /** Header used with "NB10" */ struct CV_HEADER_NB10 { uint signature; int offset; uint timestamp; uint age; OMF_NAME name; } /** Header used with "RSDS" */ /*struct CV_HEADER_RSDS { uint signature; GUID guid; uint age; OMF_NAME name; }*/ /** Directory header */ struct CV_DIRECTORY { ushort dirSize; ushort entrySize; uint nEntries; int offset; uint flags; //CV_ENTRY[1] entries; CV_ENTRY[] entries() const { return (cast(CV_ENTRY*)(&this + 1))[0 .. nEntries]; } } /** Subsection record */ struct CV_ENTRY { ushort sst; ushort modIndex; int offset; uint size; } // ---------------------------------------------------------------------------- // sstModule // ---------------------------------------------------------------------------- /** Module style, always "CV" */ enum CV_MOD_STYLE = 0x5643; /** Module */ struct CV_MODULE { ushort overlay; ushort lib; ushort nSegments; ushort style; //CV_SEGMENT[1] segments; //OMF_NAME name; CV_SEGMENT[] segments() const { return (cast(CV_SEGMENT*)(&style + 1))[0 .. nSegments]; } OMF_NAME name() const { return *cast(OMF_NAME*)(cast(void*)segments + nSegments * CV_SEGMENT.sizeof); } } /** Module segment */ struct CV_SEGMENT { ushort segIndex; ushort padding; uint offset; uint size; } // ---------------------------------------------------------------------------- // sstGlobalPub, sstStaticSym, sstGlobalSym, sstAlignSym // ---------------------------------------------------------------------------- /** Symbol IDs, used by CV_SYMBOL.header.type */ enum : ushort { S_COMPILE_V1 = 0x0001, S_REGISTER_V1 = 0x0002, S_CONSTANT_V1 = 0x0003, S_UDT_V1 = 0x0004, S_SSEARCH_V1 = 0x0005, S_END_V1 = 0x0006, S_SKIP_V1 = 0x0007, S_CVRESERVE_V1 = 0x0008, S_OBJNAME_V1 = 0x0009, S_ENDARG_V1 = 0x000A, S_COBOLUDT_V1 = 0x000B, S_MANYREG_V1 = 0x000C, S_RETURN_V1 = 0x000D, S_ENTRYTHIS_V1 = 0x000E, S_BPREL_V1 = 0x0200, S_LDATA_V1 = 0x0201, S_GDATA_V1 = 0x0202, S_PUB_V1 = 0x0203, S_LPROC_V1 = 0x0204, S_GPROC_V1 = 0x0205, S_THUNK_V1 = 0x0206, S_BLOCK_V1 = 0x0207, S_WITH_V1 = 0x0208, S_LABEL_V1 = 0x0209, S_CEXMODEL_V1 = 0x020A, S_VFTPATH_V1 = 0x020B, S_REGREL_V1 = 0x020C, S_LTHREAD_V1 = 0x020D, S_GTHREAD_V1 = 0x020E, S_PROCREF_V1 = 0x0400, S_DATAREF_V1 = 0x0401, S_ALIGN_V1 = 0x0402, S_LPROCREF_V1 = 0x0403, // Variants with 32bit type indices S_REGISTER_V2 = 0x1001, /// CV_REGISTER_V2 S_CONSTANT_V2 = 0x1002, /// CV_CONSTANT_V2 S_UDT_V2 = 0x1003, /// CV_UDT_V2 S_COBOLUDT_V2 = 0x1004, S_MANYREG_V2 = 0x1005, S_BPREL_V2 = 0x1006, /// CV_BPREL_V2 S_LDATA_V2 = 0x1007, /// CV_DATA_V2 S_GDATA_V2 = 0x1008, /// CV_DATA_V2 S_PUB_V2 = 0x1009, /// CV_DATA_V2 S_LPROC_V2 = 0x100A, /// CV_PROC_V2 S_GPROC_V2 = 0x100B, /// CV_PROC_V2 S_VFTTABLE_V2 = 0x100C, S_REGREL_V2 = 0x100D, S_LTHREAD_V2 = 0x100E, S_GTHREAD_V2 = 0x100F, S_FUNCINFO_V2 = 0x1012, S_COMPILAND_V2 = 0x1013, /// CV_COMPILE_V2 S_COMPILAND_V3 = 0x1101, S_THUNK_V3 = 0x1102, S_BLOCK_V3 = 0x1103, S_LABEL_V3 = 0x1105, S_REGISTER_V3 = 0x1106, S_CONSTANT_V3 = 0x1107, S_UDT_V3 = 0x1108, S_BPREL_V3 = 0x110B, S_LDATA_V3 = 0x110C, S_GDATA_V3 = 0x110D, S_PUB_V3 = 0x110E, S_LPROC_V3 = 0x110F, S_GPROC_V3 = 0x1110, S_BPREL_XXXX_V3 = 0x1111, /* not really understood, but looks like bprel... */ S_MSTOOL_V3 = 0x1116, /* compiler command line options and build information */ S_PUB_FUNC1_V3 = 0x1125, /* didn't get the difference between the two */ S_PUB_FUNC2_V3 = 0x1127, S_SECTINFO_V3 = 0x1136, S_SUBSECTINFO_V3= 0x1137, S_ENTRYPOINT_V3 = 0x1138, S_SECUCOOKIE_V3 = 0x113A, S_MSTOOLINFO_V3 = 0x113C, S_MSTOOLENV_V3 = 0x113D } /** Packed symbols header */ struct CV_SYMHASH { ushort symIndex; ushort addrIndex; uint symInfoSize; uint symHashSize; uint addrHashSize; } /** Symbol variant record */ struct CV_SYMBOL { OMF_HEADER header; union { CV_COMPILE_V1 compile_v1; CV_COMPILE_V2 compile_v2; CV_REGISTER_V1 register_v1; CV_REGISTER_V2 register_v2; CV_CONSTANT_V1 constant_v1; CV_CONSTANT_V2 constant_v2; CV_UDT_V1 udt_v1; CV_UDT_V2 udt_v2; CV_SSEARCH ssearch; CV_STACK_V1 stack_v1; CV_STACK_V2 stack_v2; CV_DATA_V1 data_v1; CV_DATA_V2 data_v2; CV_PROC_V1 proc_v1; CV_PROC_V2 proc_v2; CV_THUNK thunk; CV_BLOCK block; CV_LABEL label; } } /** Compiler information symbol */ struct CV_COMPILE_V1 { ubyte machine; ubyte language; ushort flags; OMF_NAME name; } struct CV_COMPILE_V2 { uint[4] unknown1; ushort unknown2; OMF_NAME name; } /** Register data symbol */ struct CV_REGISTER_V1 { ushort typeIndex; ushort reg; OMF_NAME name; } struct CV_REGISTER_V2 { uint typeIndex; ushort reg; OMF_NAME name; } /** Constant data symbol */ struct CV_CONSTANT_V1 { ushort typeIndex; ushort value; OMF_NAME name; } struct CV_CONSTANT_V2 { uint typeIndex; ushort value; OMF_NAME name; } /** User defined type Symbol */ struct CV_UDT_V1 { ushort typeIndex; OMF_NAME name; } struct CV_UDT_V2 { uint typeIndex; OMF_NAME name; } /** Start of Search symbol */ struct CV_SSEARCH { uint offset; ushort segment; } /** Object name symbol */ struct CV_OBJNAME { uint signature; OMF_NAME name; } /** Stack data symbol */ struct CV_STACK_V1 { uint offset; ushort typeIndex; OMF_NAME name; } struct CV_STACK_V2 { uint offset; uint typeIndex; OMF_NAME name; } /** Data symbol */ struct CV_DATA_V1 { uint offset; short segment; short typeIndex; OMF_NAME name; } struct CV_DATA_V2 { uint typeIndex; uint offset; short segment; OMF_NAME name; } /** Procedure symbol */ struct CV_PROC_V1 { uint parent; uint end; uint next; uint procLength; uint dbgStart; uint dbgEnd; uint offset; ushort segment; ushort procType; ubyte flags; OMF_NAME name; } struct CV_PROC_V2 { uint parent; uint end; uint next; uint procLength; uint dbgStart; uint dbgEnd; uint procType; uint offset; ushort segment; ubyte flags; OMF_NAME name; } /** Thunk symbol */ struct CV_THUNK { uint parent; uint end; uint next; uint offset; ushort segment; ushort size; ubyte type; OMF_NAME name; } /** Block symbol */ struct CV_BLOCK { uint parent; uint end; uint length; uint offset; ushort segment; OMF_NAME name; } /** Label symbol */ struct CV_LABEL { uint offset; ushort segment; ubyte flags; OMF_NAME name; } // ---------------------------------------------------------------------------- // sstSrcModule // ---------------------------------------------------------------------------- /** Source module header */ struct CV_SRCMODULE { ushort nFiles; /// number of CV_SRCFILE records ushort nSegments; /// number of segments in module //uint[] fileOffsets; //uint[2][] codeOffsets; //ushort[] segmentIds; /// array of offsets to every CV_SRCFILE record uint[] fileOffsets() const { return (cast(uint*)(&nSegments + 1))[0 .. nFiles]; } /// array of segment start/end pairs, length = nSegments uint[2][] codeOffsets() const { return (cast(uint[2]*)(cast(void*)fileOffsets + nFiles * uint.sizeof))[0 .. nSegments]; } /// array of linker indices, length = nSegments ushort[] segmentIds() const { return (cast(ushort*)(cast(void*)codeOffsets + nSegments * (uint[2]).sizeof))[0 .. nSegments]; } } /** Source file record */ struct CV_SRCFILE { ushort nSegments; /// number of CV_SRCSEGMENT records ushort reserved; //uint[] lineOffsets; //uint[2][] codeOffsets; //OMF_NAME name; // array of offsets to every CV_SRCSEGMENT record, length = nSegments uint[] lineOffsets() const { return (cast(uint*)(&reserved + 1))[0 .. nSegments]; } /// array of segment start/end pairs, length = nSegments uint[2][] codeOffsets() const { return (cast(uint[2]*)(cast(void*)lineOffsets + nSegments * uint.sizeof))[0 .. nSegments]; } /// name of file padded to long boundary OMF_NAME* name() const { return cast(OMF_NAME*)(cast(void*)codeOffsets + nSegments * (uint[2]).sizeof); } } /** Source segment record */ struct CV_SRCSEGMENT { ushort segment; /// linker segment index ushort nPairs; /// count of line/offset pairs //uint[] offsets; //ushort[] lineNumbers; /// array of offsets in segment, length = nPairs uint[] offsets() const { return (cast(uint*)(&nPairs + 1))[0 .. nPairs]; } /// array of line lumber in source, length = nPairs ushort[] lineNumbers() const { return (cast(ushort*)(cast(void*)offsets + nPairs * uint.sizeof))[0 .. nPairs]; } } // ---------------------------------------------------------------------------- // sstGlobalTypes // ---------------------------------------------------------------------------- /** Basic types Official MS documentation says that type (< 0x4000, so 12 bits) is made of: +----------+------+------+----------+------+ | 11 | 10-8 | 7-4 | 3 | 2-0 | +----------+------+------+----------+------+ | reserved | mode | type | reserved | size | +----------+------+------+----------+------+ */ /** Basic type: Type bits */ enum : ubyte { T_SPECIAL_BITS = 0x00, /// Special T_SIGNED_BITS = 0x10, /// Signed integral value T_UNSIGNED_BITS = 0x20, /// Unsigned integral value T_BOOLEAN_BITS = 0x30, /// Boolean T_REAL_BITS = 0x40, /// Real T_COMPLEX_BITS = 0x50, /// Complex T_SPECIAL2_BITS = 0x60, /// Special2 T_INT_BITS = 0x70, /// Real int value } /** Basic type: Size bits */ enum : ubyte { // Special types T_NOTYPE_BITS = 0x00, /// No type T_ABS_BITS = 0x01, /// Absolute symbol T_SEGMENT_BITS = 0x02, /// Segment T_VOID_BITS = 0x03, /// Void T_CURRENCY_BITS = 0x04, /// Basic 8-byte currency value T_NBASICSTR_BITS = 0x05, /// Near Basic string T_FBASICSTR_BITS = 0x06, /// Far Basic string T_NOTRANS_BITS = 0x07, /// Untranslated type from previous Microsoft symbol formats // Signed/Unsigned/Boolean types T_INT08_BITS = 0x00, /// 1 byte T_INT16_BITS = 0x01, /// 2 byte T_INT32_BITS = 0x02, /// 4 byte T_INT64_BITS = 0x03, /// 8 byte // Real/Complex types T_REAL32_BITS = 0x00, /// 32 bit T_REAL64_BITS = 0x01, /// 64 bit T_REAL80_BITS = 0x02, /// 80 bit T_REAL128_BITS = 0x03, /// 128 bit T_REAL48_BITS = 0x04, /// 48 bit // Special2 types T_BIT_BITS = 0x00, /// Bit T_PASCHAR_BITS = 0x01, /// Pascal CHAR // Real Int types T_CHAR_BITS = 0x00, /// Char T_WCHAR_BITS = 0x01, /// Wide character T_INT2_BITS = 0x02, /// 2-byte signed integer T_UINT2_BITS = 0x03, /// 2-byte unsigned integer T_INT4_BITS = 0x04, /// 4-byte signed integer T_UINT4_BITS = 0x05, /// 4-byte unsigned integer T_INT8_BITS = 0x06, /// 8-byte signed integer T_UINT8_BITS = 0x07, /// 8-byte unsigned integer T_DCHAR_BITS = 0x08, /// dchar, DigitalMars D extension } /** Basic type: Mode bits */ enum : ushort { T_DIRECT_BITS = 0x0000, /// Direct; not a pointer T_NEARPTR_BITS = 0x0100, /// Near pointer T_FARPTR_BITS = 0x0200, /// Far pointer T_HUGEPTR_BITS = 0x0300, /// Huge pointer T_NEAR32PTR_BITS = 0x0400, /// 32-bit near pointer T_FAR32PTR_BITS = 0x0500, /// 32-bit far pointer T_NEAR64PTR_BITS = 0x0600, /// 64-bit near pointer } /** Basic type bit masks */ enum : ushort { T_TYPE_MASK = 0x00F0, /// type type mask (data treatment mode) T_SIZE_MASK = 0x000F, /// type size mask (depends on 'type' value) T_MODE_MASK = 0x0700, /// type mode mask (ptr/non-ptr) } /** Leaf types, used by CV_TYPE.header.type */ enum : ushort { // Can be referenced from symbols LF_MODIFIER_V1 = 0x0001, LF_POINTER_V1 = 0x0002, LF_ARRAY_V1 = 0x0003, LF_CLASS_V1 = 0x0004, LF_STRUCTURE_V1 = 0x0005, LF_UNION_V1 = 0x0006, LF_ENUM_V1 = 0x0007, LF_PROCEDURE_V1 = 0x0008, LF_MFUNCTION_V1 = 0x0009, LF_VTSHAPE_V1 = 0x000A, LF_COBOL0_V1 = 0x000B, LF_COBOL1_V1 = 0x000C, LF_BARRAY_V1 = 0x000D, LF_LABEL_V1 = 0x000E, LF_NULL_V1 = 0x000F, LF_NOTTRAN_V1 = 0x0010, LF_DIMARRAY_V1 = 0x0011, LF_VFTPATH_V1 = 0x0012, LF_PRECOMP_V1 = 0x0013, LF_ENDPRECOMP_V1 = 0x0014, LF_OEM_V1 = 0x0015, LF_TYPESERVER_V1 = 0x0016, LF_MODIFIER_V2 = 0x1001, LF_POINTER_V2 = 0x1002, LF_ARRAY_V2 = 0x1003, LF_CLASS_V2 = 0x1004, LF_STRUCTURE_V2 = 0x1005, LF_UNION_V2 = 0x1006, LF_ENUM_V2 = 0x1007, LF_PROCEDURE_V2 = 0x1008, LF_MFUNCTION_V2 = 0x1009, LF_COBOL0_V2 = 0x100A, LF_BARRAY_V2 = 0x100B, LF_DIMARRAY_V2 = 0x100C, LF_VFTPATH_V2 = 0x100D, LF_PRECOMP_V2 = 0x100E, LF_OEM_V2 = 0x100F, // Can be referenced from other type records LF_SKIP_V1 = 0x0200, LF_ARGLIST_V1 = 0x0201, LF_DEFARG_V1 = 0x0202, LF_LIST_V1 = 0x0203, LF_FIELDLIST_V1 = 0x0204, LF_DERIVED_V1 = 0x0205, LF_BITFIELD_V1 = 0x0206, LF_METHODLIST_V1 = 0x0207, LF_DIMCONU_V1 = 0x0208, LF_DIMCONLU_V1 = 0x0209, LF_DIMVARU_V1 = 0x020A, LF_DIMVARLU_V1 = 0x020B, LF_REFSYM_V1 = 0x020C, LF_SKIP_V2 = 0x1200, LF_ARGLIST_V2 = 0x1201, LF_DEFARG_V2 = 0x1202, LF_FIELDLIST_V2 = 0x1203, LF_DERIVED_V2 = 0x1204, LF_BITFIELD_V2 = 0x1205, LF_METHODLIST_V2 = 0x1206, LF_DIMCONU_V2 = 0x1207, LF_DIMCONLU_V2 = 0x1208, LF_DIMVARU_V2 = 0x1209, LF_DIMVARLU_V2 = 0x120A, // Field lists LF_BCLASS_V1 = 0x0400, LF_VBCLASS_V1 = 0x0401, LF_IVBCLASS_V1 = 0x0402, LF_ENUMERATE_V1 = 0x0403, LF_FRIENDFCN_V1 = 0x0404, LF_INDEX_V1 = 0x0405, LF_MEMBER_V1 = 0x0406, LF_STMEMBER_V1 = 0x0407, LF_METHOD_V1 = 0x0408, LF_NESTTYPE_V1 = 0x0409, LF_VFUNCTAB_V1 = 0x040A, LF_FRIENDCLS_V1 = 0x040B, LF_ONEMETHOD_V1 = 0x040C, LF_VFUNCOFF_V1 = 0x040D, LF_NESTTYPEEX_V1 = 0x040E, LF_MEMBERMODIFY_V1 = 0x040F, LF_BCLASS_V2 = 0x1400, LF_VBCLASS_V2 = 0x1401, LF_IVBCLASS_V2 = 0x1402, LF_FRIENDFCN_V2 = 0x1403, LF_INDEX_V2 = 0x1404, LF_MEMBER_V2 = 0x1405, LF_STMEMBER_V2 = 0x1406, LF_METHOD_V2 = 0x1407, LF_NESTTYPE_V2 = 0x1408, LF_VFUNCTAB_V2 = 0x1409, LF_FRIENDCLS_V2 = 0x140A, LF_ONEMETHOD_V2 = 0x140B, LF_VFUNCOFF_V2 = 0x140C, LF_NESTTYPEEX_V2 = 0x140D, LF_ENUMERATE_V3 = 0x1502, LF_ARRAY_V3 = 0x1503, LF_CLASS_V3 = 0x1504, LF_STRUCTURE_V3 = 0x1505, LF_UNION_V3 = 0x1506, LF_ENUM_V3 = 0x1507, LF_MEMBER_V3 = 0x150D, LF_STMEMBER_V3 = 0x150E, LF_METHOD_V3 = 0x150F, LF_NESTTYPE_V3 = 0x1510, LF_ONEMETHOD_V3 = 0x1511, // Numeric leaf types LF_NUMERIC = 0x8000, LF_CHAR = 0x8000, LF_SHORT = 0x8001, LF_USHORT = 0x8002, LF_LONG = 0x8003, LF_ULONG = 0x8004, LF_REAL32 = 0x8005, LF_REAL64 = 0x8006, LF_REAL80 = 0x8007, LF_REAL128 = 0x8008, LF_QUADWORD = 0x8009, LF_UQUADWORD = 0x800A, LF_REAL48 = 0x800B, LF_COMPLEX32 = 0x800C, LF_COMPLEX64 = 0x800D, LF_COMPLEX80 = 0x800E, LF_COMPLEX128 = 0x800F, LF_VARSTRING = 0x8010, LF_DCHAR = 0x8011 } /** Global types header */ struct CV_GLOBALTYPES { ubyte[3] unused; ubyte flags; uint nTypes; //uint[1] typeOffsets; //CV_TYPE[1] types; /// array of offsets to CV_TYPE records uint* typeOffsets() const { return cast(uint*)(&nTypes + 1); } // Get the first CV_TYPE record CV_TYPE* types() const { return cast(CV_TYPE*)(cast(void*)(&nTypes + 1) + nTypes * uint.sizeof); } } /** Type variant record */ struct CV_TYPE { OMF_HEADER header; union { // Types CV_MODIFIER_V1 modifier_v1; CV_MODIFIER_V2 modifier_v2; CV_POINTER_V1 pointer_v1; CV_POINTER_V2 pointer_v2; CV_ARRAY_V1 array_v1; CV_ARRAY_V2 array_v2; CV_STRUCT_V1 struct_v1; CV_STRUCT_V2 struct_v2; CV_UNION_V1 union_v1; CV_UNION_V2 union_v2; CV_ENUM_V1 enum_v1; CV_ENUM_V2 enum_v2; CV_PROCEDURE_V1 proc_v1; CV_PROCEDURE_V2 proc_v2; CV_MFUNCTION_V1 method_v1; CV_MFUNCTION_V2 method_v2; CV_OEM_V1 oem_v1; CV_OEM_V2 oem_v2; // Referenced types CV_FIELDLIST fieldlist; CV_BITFIELD_V1 bitfield_v1; CV_BITFIELD_V2 bitfield_v2; CV_ARGLIST_V1 arglist_v1; CV_ARGLIST_V2 arglist_v2; CV_DERIVED_V1 derived_v1; CV_DERIVED_V2 derived_v2; // Field types } } /** Modifier type */ struct CV_MODIFIER_V1 { ushort attribute; ushort type; } struct CV_MODIFIER_V2 { uint type; ushort attribute; } /** Pointer type */ struct CV_POINTER_V1 { ushort attribute; ushort type; OMF_NAME name; } struct CV_POINTER_V2 { uint type; uint attribute; OMF_NAME name; } /** Array type */ struct CV_ARRAY_V1 { ushort elemType; ushort indexType; ushort length; /// numeric leaf OMF_NAME name; } struct CV_ARRAY_V2 { uint elemType; uint indexType; ushort length; /// numeric leaf OMF_NAME name; } /** Struct type */ struct CV_STRUCT_V1 { ushort nElement; ushort fieldlist; ushort property; ushort derived; ushort vshape; ushort length; /// numeric leaf OMF_NAME name; } struct CV_STRUCT_V2 { ushort nElement; ushort property; uint fieldlist; uint derived; uint vshape; ushort length; /// numeric leaf OMF_NAME name; } /** Union type */ struct CV_UNION_V1 { ushort count; ushort fieldlist; ushort property; ushort length; /// numeric leaf OMF_NAME name; } struct CV_UNION_V2 { ushort count; ushort property; uint fieldlist; ushort length; /// numeric leaf OMF_NAME name; } /** Enumeration type */ struct CV_ENUM_V1 { ushort length; ushort id; ushort count; ushort type; ushort fieldlist; ushort property; OMF_NAME p_name; } struct CV_ENUM_V2 { ushort length; ushort id; ushort count; ushort property; uint type; uint fieldlist; OMF_NAME p_name; } /** Procedure type */ struct CV_PROCEDURE_V1 { ushort retType; ubyte call; ubyte reserved; ushort nParams; ushort argList; } struct CV_PROCEDURE_V2 { uint retType; ubyte call; ubyte reserved; ushort nParams; uint argList; } /** Method type */ struct CV_MFUNCTION_V1 { ushort retType; ushort classType; ushort thisType; ubyte call; ubyte reserved; ushort nParams; ushort arglist; uint thisAdjust; } struct CV_MFUNCTION_V2 { uint retType; uint classType; uint thisType; ubyte call; ubyte reserved; ushort nParams; uint arglist; uint thisAdjust; } /** OEM type */ struct CV_OEM_V1 { ushort oem; ushort rec; ushort nIndices; //ushort[1] indices; ushort* indices() const { return cast(ushort*)(&nIndices + 1); } } struct CV_OEM_V2 { // UNKNOWN! } enum { OEM_DIGITALMARS = 0x0042, D_DYN_ARRAY = 0x0001, D_ASSOC_ARRAY = 0x0002, D_DELEGATE = 0x0003 } struct CV_D_DYNARRAY { ushort indexType; ushort elemType; } struct CV_D_ASSOCARRAY { ushort keyType; ushort elemType; } struct CV_D_DELEGATE { ushort thisType; ushort funcType; } /** Field list */ struct CV_FIELDLIST { ubyte[1] list; } /** Bit field */ struct CV_BITFIELD_V1 { ubyte nBits; ubyte bitOffset; ushort type; } struct CV_BITFIELD_V2 { uint type; ubyte nBits; ubyte bitOffset; } /** Arguments list */ struct CV_ARGLIST_V1 { ushort count; ushort[1] args; } struct CV_ARGLIST_V2 { uint count; uint[1] args; } /** Derived */ struct CV_DERIVED_V1 { ushort count; ushort[1] derivedClasses; } struct CV_DERIVED_V2 { uint count; uint[1] derivedClasses; } /** Class type */ struct CV_CLASS_V1 { ushort type; ushort attribute; ushort offset; /// numeric leaf } struct CV_CLASS_V2 { ushort attribute; uint type; ushort offset; /// numeric leaf } struct CvTypeClass { ushort count; ushort fieldList; ushort flags; ushort dList; ushort vShape; // length // name } // ---------------------------------------------------------------------------- // sstSegMap // ---------------------------------------------------------------------------- struct CV_SEGMAP { ushort total; ushort logical; //CV_SEGMAPDESC[1] descriptors; CV_SEGMAPDESC* descriptors() const { return cast(CV_SEGMAPDESC*)(&logical + 1); } } struct CV_SEGMAPDESC { ushort flags; ushort overlay; ushort group; ushort frame; ushort name; ushort className; uint offset; uint size; } // ---------------------------------------------------------------------------- // sstPreCompMap // ---------------------------------------------------------------------------- struct OMFPreCompMap { ushort FirstType; // first precompiled type index ushort cTypes; // number of precompiled types uint signature; // precompiled types signature ushort padding; //CV_typ_t[] map; // mapping of precompiled types } // ---------------------------------------------------------------------------- // sstOffsetMap16, sstOffsetMap32 // ---------------------------------------------------------------------------- struct OMFOffsetMap16 { uint csegment; // Count of physical segments // The next six items are repeated for each segment //uint crangeLog; // Count of logical offset ranges //ushort[] rgoffLog; // Array of logical offsets //short[] rgbiasLog; // Array of logical->physical bias //uint crangePhys; // Count of physical offset ranges //ushort[] rgoffPhys; // Array of physical offsets //short[] rgbiasPhys; // Array of physical->logical bias } struct OMFOffsetMap32 { uint csection; // Count of physical sections // The next six items are repeated for each section //uint crangeLog; // Count of logical offset ranges //uint[] rgoffLog; // Array of logical offsets //int[] rgbiasLog; // Array of logical->physical bias //uint crangePhys; // Count of physical offset ranges //uint[] rgoffPhys; // Array of physical offsets //int[] rgbiasPhys; // Array of physical->logical bias } // ---------------------------------------------------------------------------- // sstFileIndex // ---------------------------------------------------------------------------- struct OMFFileIndex { ushort cmodules; // Number of modules ushort cfilerefs; // Number of file references //ushort[] modulelist; // Index to beginning of list of files // for module i. (0 for module w/o files) //ushort[] cfiles; // Number of file names associated // with module i. //uint[] ulNames; // Offsets from the beginning of this // table to the file names //char[] Names; // The length prefixed names of files } struct OMFMpcDebugInfo { ushort cSeg; // number of segments in module //ushort[] mpSegFrame; // map seg (zero based) to frame } // Procedure flags enum { PROC_FPO = 1 << 0, // Frame pointer omitted PROC_INTERRUPT = 1 << 1, // Interrupt PROC_RETURN = 1 << 2, // Far return PROC_NEVER = 1 << 3, // Never returns } // Procedure calling conventions enum { CALL_C_NEAR = 0x00, CALL_C_FAR = 0x01, CALL_PASCAL_NEAR = 0x02, CALL_PASCAL_FAR = 0x03, CALL_FASTCALL_NEAR = 0x04, CALL_FASTCALL_FAR = 0x05, CALL_STDCALL_NEAR = 0x07, CALL_STDCALL_FAR = 0x08, CALL_SYSCALL_NEAR = 0x09, CALL_SYSCALL_FAR = 0x10, CALL_THIS = 0x11, CALL_MIPS = 0x12, CALL_GENERIC = 0x13 } enum { STRUCT_PACKED = 1 << 0, STRUCT_CTOR = 1 << 1, STRUCT_OVERLOADS = 1 << 2, STRUCT_IS_NESTED = 1 << 3, STRUCT_HAS_NESTED = 1 << 4, STRUCT_OPASSIGN = 1 << 5, STRUCT_OPCAST = 1 << 6, STRUCT_FWDREF = 1 << 7, STRUCT_SCOPED = 1 << 8 }