Mercurial > projects > ddmd
diff dbg/symbol/CodeView.d @ 0:10317f0c89a5
Initial commit
author | korDen |
---|---|
date | Sat, 24 Oct 2009 08:42:06 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/dbg/symbol/CodeView.d Sat Oct 24 08:42:06 2009 +0400 @@ -0,0 +1,1759 @@ +/** + 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 +}