1 /// Copyright: Copyright (c) 2017-2019 Andrey Penechko.
2 /// License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0).
3 /// Authors: Andrey Penechko.
4 
5 /// Code View format symbols
6 module vox.be.debug_info.pdb.symbol;
7 
8 import std.bitmanip : bitfields;
9 import std.format : formattedWrite;
10 import vox.be.debug_info.pdb;
11 
12 // SYM_ENUM_e
13 enum SymbolKind : ushort
14 {
15 	S_COMPILE       = 0x0001,  // Compile flags symbol
16 	S_END           = 0x0006,  // Block, procedure, "with" or thunk end
17 	S_ENDARG        = 0x000A,  // end of argument/return list
18 
19 	// 16 bit symbols omitted
20 
21 	// sym records with 32-bit types embedded instead of 16-bit
22 	// all have 0x1000 bit set for easy identification
23 	// only do the 32-bit target versions since we don't really
24 	// care about 16-bit ones anymore.
25 	S_TI16_MAX      =  0x1000,
26 
27 	S_REGISTER_ST   =  0x1001,  // Register variable
28 	S_CONSTANT_ST   =  0x1002,  // constant symbol
29 	S_UDT_ST        =  0x1003,  // User defined type
30 	S_COBOLUDT_ST   =  0x1004,  // special UDT for cobol that does not symbol pack
31 	S_MANYREG_ST    =  0x1005,  // multiple register variable
32 	S_BPREL32_ST    =  0x1006,  // BP-relative
33 	S_LDATA32_ST    =  0x1007,  // Module-local symbol
34 	S_GDATA32_ST    =  0x1008,  // Global data symbol
35 	S_PUB32_ST      =  0x1009,  // a public symbol (CV internal reserved)
36 	S_LPROC32_ST    =  0x100a,  // Local procedure start
37 	S_GPROC32_ST    =  0x100b,  // Global procedure start
38 	S_VFTABLE32     =  0x100c,  // address of virtual function table
39 	S_REGREL32_ST   =  0x100d,  // register relative address
40 	S_LTHREAD32_ST  =  0x100e,  // local thread storage
41 	S_GTHREAD32_ST  =  0x100f,  // global thread storage
42 
43 	S_LPROCMIPS_ST  =  0x1010,  // Local procedure start
44 	S_GPROCMIPS_ST  =  0x1011,  // Global procedure start
45 
46 	S_FRAMEPROC     =  0x1012,  // extra frame and proc information
47 	S_COMPILE2_ST   =  0x1013,  // extended compile flags and info
48 
49 	// new symbols necessary for 16-bit enumerates of IA64 registers
50 	// and IA64 specific symbols
51 
52 	S_MANYREG2_ST   =  0x1014,  // multiple register variable
53 	S_LPROCIA64_ST  =  0x1015,  // Local procedure start (IA64)
54 	S_GPROCIA64_ST  =  0x1016,  // Global procedure start (IA64)
55 
56 	// Local symbols for IL
57 	S_LOCALSLOT_ST  =  0x1017,  // local IL sym with field for local slot index
58 	S_PARAMSLOT_ST  =  0x1018,  // local IL sym with field for parameter slot index
59 
60 	S_ANNOTATION    =  0x1019,  // Annotation string literals
61 
62 	// symbols to support managed code debugging
63 	S_GMANPROC_ST   =  0x101a,  // Global proc
64 	S_LMANPROC_ST   =  0x101b,  // Local proc
65 	S_RESERVED1     =  0x101c,  // reserved
66 	S_RESERVED2     =  0x101d,  // reserved
67 	S_RESERVED3     =  0x101e,  // reserved
68 	S_RESERVED4     =  0x101f,  // reserved
69 	S_LMANDATA_ST   =  0x1020,
70 	S_GMANDATA_ST   =  0x1021,
71 	S_MANFRAMEREL_ST=  0x1022,
72 	S_MANREGISTER_ST=  0x1023,
73 	S_MANSLOT_ST    =  0x1024,
74 	S_MANMANYREG_ST =  0x1025,
75 	S_MANREGREL_ST  =  0x1026,
76 	S_MANMANYREG2_ST=  0x1027,
77 	S_MANTYPREF     =  0x1028,  // Index for type referenced by name from metadata
78 	S_UNAMESPACE_ST =  0x1029,  // Using namespace
79 
80 	// Symbols w/ SZ name fields. All name fields contain utf8 encoded strings.
81 	S_ST_MAX        =  0x1100,  // starting point for SZ name symbols
82 
83 	S_OBJNAME       =  0x1101,  // path to object file name
84 	S_THUNK32       =  0x1102,  // Thunk Start
85 	S_BLOCK32       =  0x1103,  // block start
86 	S_WITH32        =  0x1104,  // with start
87 	S_LABEL32       =  0x1105,  // code label
88 	S_REGISTER      =  0x1106,  // Register variable
89 	S_CONSTANT      =  0x1107,  // constant symbol
90 	S_UDT           =  0x1108,  // User defined type
91 	S_COBOLUDT      =  0x1109,  // special UDT for cobol that does not symbol pack
92 	S_MANYREG       =  0x110a,  // multiple register variable
93 	S_BPREL32       =  0x110b,  // BP-relative
94 	S_LDATA32       =  0x110c,  // Module-local symbol
95 	S_GDATA32       =  0x110d,  // Global data symbol
96 	S_PUB32         =  0x110e,  // a public symbol (CV internal reserved)
97 	S_LPROC32       =  0x110f,  // Local procedure start
98 	S_GPROC32       =  0x1110,  // Global procedure start
99 	S_REGREL32      =  0x1111,  // register relative address
100 	S_LTHREAD32     =  0x1112,  // local thread storage
101 	S_GTHREAD32     =  0x1113,  // global thread storage
102 
103 	S_LPROCMIPS     =  0x1114,  // Local procedure start
104 	S_GPROCMIPS     =  0x1115,  // Global procedure start
105 	S_COMPILE2      =  0x1116,  // extended compile flags and info
106 	S_MANYREG2      =  0x1117,  // multiple register variable
107 	S_LPROCIA64     =  0x1118,  // Local procedure start (IA64)
108 	S_GPROCIA64     =  0x1119,  // Global procedure start (IA64)
109 	S_LOCALSLOT     =  0x111a,  // local IL sym with field for local slot index
110 	S_SLOT          = S_LOCALSLOT,  // alias for LOCALSLOT
111 	S_PARAMSLOT     =  0x111b,  // local IL sym with field for parameter slot index
112 
113 	// symbols to support managed code debugging
114 	S_LMANDATA      =  0x111c,
115 	S_GMANDATA      =  0x111d,
116 	S_MANFRAMEREL   =  0x111e,
117 	S_MANREGISTER   =  0x111f,
118 	S_MANSLOT       =  0x1120,
119 	S_MANMANYREG    =  0x1121,
120 	S_MANREGREL     =  0x1122,
121 	S_MANMANYREG2   =  0x1123,
122 	S_UNAMESPACE    =  0x1124,  // Using namespace
123 
124 	// ref symbols with name fields
125 	S_PROCREF       =  0x1125,  // Reference to a procedure
126 	S_DATAREF       =  0x1126,  // Reference to data
127 	S_LPROCREF      =  0x1127,  // Local Reference to a procedure
128 	S_ANNOTATIONREF =  0x1128,  // Reference to an S_ANNOTATION symbol
129 	S_TOKENREF      =  0x1129,  // Reference to one of the many MANPROCSYM's
130 
131 	// continuation of managed symbols
132 	S_GMANPROC      =  0x112a,  // Global proc
133 	S_LMANPROC      =  0x112b,  // Local proc
134 
135 	// short, light-weight thunks
136 	S_TRAMPOLINE    =  0x112c,  // trampoline thunks
137 	S_MANCONSTANT   =  0x112d,  // constants with metadata type info
138 
139 	// native attributed local/parms
140 	S_ATTR_FRAMEREL =  0x112e,  // relative to virtual frame ptr
141 	S_ATTR_REGISTER =  0x112f,  // stored in a register
142 	S_ATTR_REGREL   =  0x1130,  // relative to register (alternate frame ptr)
143 	S_ATTR_MANYREG  =  0x1131,  // stored in >1 register
144 
145 	// Separated code (from the compiler) support
146 	S_SEPCODE       =  0x1132,
147 
148 	S_LOCAL_2005    =  0x1133,  // defines a local symbol in optimized code
149 	S_DEFRANGE_2005 =  0x1134,  // defines a single range of addresses in which symbol can be evaluated
150 	S_DEFRANGE2_2005 =  0x1135,  // defines ranges of addresses in which symbol can be evaluated
151 
152 	S_SECTION       =  0x1136,  // A COFF section in a PE executable
153 	S_COFFGROUP     =  0x1137,  // A COFF group
154 	S_EXPORT        =  0x1138,  // A export
155 
156 	S_CALLSITEINFO  =  0x1139,  // Indirect call site information
157 	S_FRAMECOOKIE   =  0x113a,  // Security cookie information
158 
159 	S_DISCARDED     =  0x113b,  // Discarded by LINK /OPT:REF (experimental, see richards)
160 
161 	S_COMPILE3      =  0x113c,  // Replacement for S_COMPILE2
162 	S_ENVBLOCK      =  0x113d,  // Environment block split off from S_COMPILE2
163 
164 	S_LOCAL         =  0x113e,  // defines a local symbol in optimized code
165 	S_DEFRANGE      =  0x113f,  // defines a single range of addresses in which symbol can be evaluated
166 	S_DEFRANGE_SUBFIELD =  0x1140,           // ranges for a subfield
167 
168 	S_DEFRANGE_REGISTER =  0x1141,           // ranges for en-registered symbol
169 	S_DEFRANGE_FRAMEPOINTER_REL =  0x1142,   // range for stack symbol.
170 	S_DEFRANGE_SUBFIELD_REGISTER =  0x1143,  // ranges for en-registered field of symbol
171 	S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE =  0x1144, // range for stack symbol span valid full scope of function body, gap might apply.
172 	S_DEFRANGE_REGISTER_REL =  0x1145, // range for symbol address as register + offset.
173 
174 	// S_PROC symbols that reference ID instead of type
175 	S_LPROC32_ID     =  0x1146,
176 	S_GPROC32_ID     =  0x1147,
177 	S_LPROCMIPS_ID   =  0x1148,
178 	S_GPROCMIPS_ID   =  0x1149,
179 	S_LPROCIA64_ID   =  0x114a,
180 	S_GPROCIA64_ID   =  0x114b,
181 
182 	S_BUILDINFO      = 0x114c, // build information.
183 	S_INLINESITE     = 0x114d, // inlined function callsite.
184 	S_INLINESITE_END = 0x114e,
185 	S_PROC_ID_END    = 0x114f,
186 
187 	S_DEFRANGE_HLSL  = 0x1150,
188 	S_GDATA_HLSL     = 0x1151,
189 	S_LDATA_HLSL     = 0x1152,
190 
191 	S_FILESTATIC     = 0x1153,
192 
193 	S_LOCAL_DPC_GROUPSHARED = 0x1154, // DPC groupshared variable
194 	S_LPROC32_DPC = 0x1155, // DPC local procedure start
195 	S_LPROC32_DPC_ID =  0x1156,
196 	S_DEFRANGE_DPC_PTR_TAG =  0x1157, // DPC pointer tag definition range
197 	S_DPC_SYM_TAG_MAP = 0x1158, // DPC pointer tag value to symbol record map
198 
199 	S_ARMSWITCHTABLE  = 0x1159,
200 	S_CALLEES = 0x115a,
201 	S_CALLERS = 0x115b,
202 	S_POGODATA = 0x115c,
203 	S_INLINESITE2 = 0x115d,      // extended inline site information
204 
205 	S_HEAPALLOCSITE = 0x115e,    // heap allocation site
206 
207 	S_MOD_TYPEREF = 0x115f,      // only generated at link time
208 
209 	S_REF_MINIPDB = 0x1160,      // only generated at link time for mini PDB
210 	S_PDBMAP      = 0x1161,      // only generated at link time for mini PDB
211 
212 	S_GDATA_HLSL32 = 0x1162,
213 	S_LDATA_HLSL32 = 0x1163,
214 
215 	S_GDATA_HLSL32_EX = 0x1164,
216 	S_LDATA_HLSL32_EX = 0x1165,
217 
218 	S_INLINEES = 0x1168,
219 
220 	S_RECTYPE_MAX,               // one greater than last
221 	S_RECTYPE_LAST  = S_RECTYPE_MAX - 1,
222 	S_RECTYPE_PAD   = S_RECTYPE_MAX + 0x100 // Used *only* to verify symbol record types so that current PDB code can potentially read
223 	                                        // future PDBs (assuming no format change, etc).
224 }
225 
226 // struct UDTSYM
227 // S_UDT, S_COBOLUDT
228 align(1) struct UdtSym {
229 	TypeIndex type;
230 	// next follows zero-terminated string (name)
231 }
232 
233 // struct INLINESITESYM
234 // S_INLINESITE
235 align(1) struct InlineSiteSym {
236 	uint parent; // pointer to the inliner
237 	uint end;    // pointer to this block's end of S_INLINESITE_END kind
238 	TypeIndex inlinee;
239 	// next follows an array of compressed binary annotations
240 }
241 
242 // S_PUB32
243 align(2) struct PublicSym32
244 {
245 	PublicSymFlags flags;
246 	uint offset;
247 	ushort segment;
248 	// next follows zero-terminated string (name)
249 }
250 static assert(PublicSym32.sizeof == 10);
251 
252 // CV_PUBSYMFLAGS
253 enum PublicSymFlags : uint {
254 	none = 0,
255 	code = 1 << 0,
256 	func = 1 << 1,
257 	managed = 1 << 2,
258 	MSIL = 1 << 3,
259 }
260 
261 // S_PROCREF, S_LPROCREF
262 align(2) struct ProcRefSym
263 {
264 	uint   sumName; // SUC of the name
265 	uint symOffset; // Offset of actual symbol in $$Symbols
266 	ushort     mod; // Module containing the actual symbol
267 	// next follows zero-terminated string
268 }
269 static assert(ProcRefSym.sizeof == 10);
270 
271 // struct PROCSYM32
272 // S_GPROC32, S_LPROC32, S_GPROC32_ID, S_LPROC32_ID, S_LPROC32_DPC, S_LPROC32_DPC_ID
273 align(1) struct ProcSym {
274 	uint parent;         // pointer to the parent
275 	uint end;            // pointer to this blocks end (S_END)
276 	uint next;           // pointer to next symbol
277 	uint length;         // Proc length
278 	uint dbgStart;       // Debug start offset
279 	uint dbgEnd;         // Debug end offset
280 	TypeIndex typeIndex; // Type index or ID
281 	uint offset;
282 	ushort segment;
283 	ubyte flags;         // set of ProcSymFlags
284 	// next follows zero-terminated string
285 }
286 
287 enum ProcSymFlags : ubyte {
288 	none = 0,
289 	hasFP = 1 << 0,
290 	hasIRET = 1 << 1,
291 	hasFRET = 1 << 2,
292 	isNoReturn = 1 << 3,
293 	isUnreachable = 1 << 4,
294 	hasCustomCallingConv = 1 << 5,
295 	isNoInline = 1 << 6,
296 	hasOptimizedDebugInfo = 1 << 7,
297 }
298 
299 void printProcSymFlags(ubyte set)
300 {
301 	import std.stdio : write;
302 	if (set == 0) {
303 		write(" none");
304 		return;
305 	}
306 	if (set & ProcSymFlags.hasFP) write(" hasFP");
307 	if (set & ProcSymFlags.hasIRET) write(" hasIRET");
308 	if (set & ProcSymFlags.hasFRET) write(" hasFRET");
309 	if (set & ProcSymFlags.isNoReturn) write(" isNoReturn");
310 	if (set & ProcSymFlags.isUnreachable) write(" isUnreachable");
311 	if (set & ProcSymFlags.hasCustomCallingConv) write(" hasCustomCallingConv");
312 	if (set & ProcSymFlags.isNoInline) write(" isNoInline");
313 	if (set & ProcSymFlags.hasOptimizedDebugInfo) write(" hasOptimizedDebugInfo");
314 }
315 
316 // S_REGREL32
317 align(1) struct RegRelativeSym {
318 	uint offset; // offset of symbol
319 	TypeIndex type; // Type index or metadata token
320 	RegisterId register; // register index for symbol
321 	// next follows zero-terminated string (name)
322 }
323 
324 // S_LDATA32, S_GDATA32, S_LMANDATA, S_GMANDATA
325 align(1) struct DataSym {
326 	TypeIndex type;
327 	uint dataOffset;
328 	ushort segment;
329 	// next follows zero-terminated string (name)
330 }
331 
332 // S_LTHREAD32, S_GTHREAD32
333 align(1) struct ThreadDataSym {
334 	TypeIndex type;
335 	uint dataOffset;
336 	ushort segment;
337 	// next follows zero-terminated string (name)
338 }
339 
340 // struct FRAMEPROCSYM
341 // S_FRAMEPROC
342 align(1) struct FrameProcSym {
343 	uint totalFrameBytes;
344 	uint paddingFrameBytes;
345 	uint offsetToPadding;
346 	uint bytesOfCalleeSavedRegisters;
347 	uint offsetOfExceptionHandler;
348 	ushort sectionIdOfExceptionHandler;
349 
350 	mixin(bitfields!(
351 		bool,  "hasAlloca",                        1, // function uses _alloca()
352 		bool,  "hasSetJmp",                        1, // function uses setjmp()
353 		bool,  "hasLongJmp",                       1, // function uses longjmp()
354 		bool,  "hasInlineAssembly",                1, // function uses inline asm
355 		bool,  "hasExceptionHandling",             1, // function has EH states
356 		bool,  "markedInline",                     1, // function was speced as inline
357 		bool,  "hasStructuredExceptionHandling",   1, // function has SEH
358 		bool,  "naked",                            1, // function is __declspec(naked)
359 		bool,  "securityChecks",                   1, // function has buffer security check introduced by /GS.
360 		bool,  "asynchronousExceptionHandling",    1, // function compiled with /EHa
361 		bool,  "noStackOrderingForSecurityChecks", 1, // function has /GS buffer checks, but stack ordering couldn't be done
362 		bool,  "inlined",                          1, // function was inlined within another function
363 		bool,  "strictSecurityChecks",             1, // function is __declspec(strict_gs_check)
364 		bool,  "safeBuffers",                      1, // function is __declspec(safebuffers)
365 		uint,  "encodedLocalBasePointer",          2, // record function's local pointer explicitly.
366 		uint,  "encodedParamBasePointer",          2, // record function's parameter pointer explicitly.
367 		bool,  "profileGuidedOptimization",        1, // function was compiled with PGO/PGU
368 		bool,  "validProfileCounts",               1, // Do we have valid Pogo counts?
369 		bool,  "optimizedForSpeed",                1, // Did we optimize for speed?
370 		bool,  "guardCfg",                         1, // function contains CFG checks (and no write checks)
371 		bool,  "guardCfw",                         1, // function contains CFW checks and/or instrumentation
372 
373 		uint,  "pad",                              9,
374 	));
375 };
376 
377 // struct THUNKSYM32
378 // S_THUNK32
379 struct ThunkSym {
380 	uint parent; // pointer to the parent
381 	uint end;    // pointer to this blocks end
382 	uint next;   // pointer to next symbol
383 	uint offset;
384 	ushort segment;
385 	ushort length;
386 	ThunkOrdinal type;
387 	// next follows zero-terminated string (name)
388 	// next follows variant portion of thunk (ubyte[])
389 }
390 
391 // struct TRAMPOLINESYM
392 // S_TRAMPOLINE
393 align(1) struct TrampolineSym {
394 	TrampolineType type;  // trampoline sym subtype
395 	ushort size;          // size of the thunk
396 	uint   thunkOffset;   // offset of the thunk
397 	uint   targetOffset;  // offset of the target of the thunk
398 	ushort thunkSection;  // section index of the thunk
399 	ushort targetSection; // section index of the target of the thunk
400 }
401 
402 // struct CFLAGSYM
403 // S_COMPILE
404 align(1) struct CompileSym {
405 	align(1):
406 	ubyte machine; // target processor CV_CPUType
407 	mixin(bitfields!(
408 		CV_SourceLanguage, "sourceLanguage", 8, // language index
409 		bool,   "pcode",       1, // true if pcode present
410 		ubyte,  "floatprec",   2, // floating precision
411 		ubyte,  "floatpkg",    2, // float package
412 		ubyte,  "ambdata",     3, // ambient data model
413 		ubyte,  "ambcode",     3, // ambient code model
414 		bool,   "mode32",      1, // true if compiled 32 bit mode
415 		uint,   "pad",        12, // reserved
416 	));
417 	// next follows compiler version string
418 }
419 static assert(CompileSym.sizeof == 5);
420 
421 // struct COMPILESYM
422 // S_COMPILE2
423 struct CompileSym2 {
424 	mixin(bitfields!(
425 		CV_SourceLanguage, "sourceLanguage",  8, // Language index
426 		bool,  "EC",              1, // Compiled for edit and continue
427 		bool,  "NoDbgInfo",       1, // Compiled without debugging info
428 		bool,  "LTCG",            1, // Compiled with LTCG
429 		bool,  "NoDataAlign",     1, // Compiled with /bzalign
430 		bool,  "ManagedPresent",  1, // Managed code/data present
431 		bool,  "SecurityChecks",  1, // Compiled with /GS
432 		bool,  "HotPatch",        1, // Compiled with /hotpatch
433 		bool,  "CVTCIL",          1, // Converted with CVTCIL
434 		bool,  "MSILModule",      1, // MSIL module
435 		uint,  "padding",        15, // padding
436 	));
437 	CV_CPUType machine;    // Target processor
438 	ushort verFEMajor; // Front end major version #
439 	ushort verFEMinor; // Front end minor version #
440 	ushort verFEBuild; // Front end build version #
441 	ushort verMajor;   // Back end major version #
442 	ushort verMinor;   // Back end minor version #
443 	ushort verBuild;   // Back end build version #
444 	// next follows sequence of pairs of zero-terminated strings, terminated by empty string
445 }
446 
447 // struct COMPILESYM3
448 // S_COMPILE3
449 align(1) struct CompileSym3 {
450 	mixin(bitfields!(
451 		CV_SourceLanguage, "sourceLanguage",  8, // Language index
452 		bool,  "EC",              1, // Compiled for edit and continue
453 		bool,  "NoDbgInfo",       1, // Compiled without debugging info
454 		bool,  "LTCG",            1, // Compiled with LTCG
455 		bool,  "NoDataAlign",     1, // Compiled with /bzalign
456 		bool,  "ManagedPresent",  1, // Managed code/data present
457 		bool,  "SecurityChecks",  1, // Compiled with /GS
458 		bool,  "HotPatch",        1, // Compiled with /hotpatch
459 		bool,  "CVTCIL",          1, // Converted with CVTCIL
460 		bool,  "MSILModule",      1, // MSIL module
461 		bool,  "Sdl",             1, // Compiled with /sdl
462 		bool,  "PGO",             1, // Compiled with /ltcg:pgo or pgu
463 		bool,  "Exp",             1, // .exp module
464 		uint,  "padding",        12, // padding
465 	));
466 	CV_CPUType machine;    // Target processor
467 	ushort verFEMajor; // Front end major version #
468 	ushort verFEMinor; // Front end minor version #
469 	ushort verFEBuild; // Front end build version #
470 	ushort verFEQFE;   // Front end QFE version #
471 	ushort verMajor;   // Back end major version #
472 	ushort verMinor;   // Back end minor version #
473 	ushort verBuild;   // Back end build version #
474 	ushort verQFE;     // Back end QFE version #
475 	// next follows Version string
476 }
477 
478 // struct ENVBLOCKSYM
479 // S_ENVBLOCK
480 align(1) struct EnvBlockSym {
481 	mixin(bitfields!(
482 		bool,  "EC",        1, // Compiled for edit and continue
483 		uint,  "pad",       7, // padding
484 	));
485 	// next follows sequence of pairs of zero-terminated strings, terminated by empty string
486 }
487 
488 // struct BUILDINFOSYM
489 // S_BUILDINFO
490 align(1) struct BuildInfoSym {
491 	TypeIndex buildId; // CV_ItemId of Build Info.
492 }
493 
494 // struct LOCALSYM
495 // S_LOCAL
496 align(1) struct LocalSym
497 {
498 	TypeIndex typeIndex; // type index
499 	ushort    flags;     // set of LocalSymFlags
500 	// next follows zero-terminated string
501 }
502 
503 enum LocalSymFlags : ushort {
504 	none = 0,
505 	isParameter = 1 << 0,
506 	isAddressTaken = 1 << 1,
507 	isCompilerGenerated = 1 << 2,
508 	isAggregate = 1 << 3,
509 	isAggregated = 1 << 4,
510 	isAliased = 1 << 5,
511 	isAlias = 1 << 6,
512 	isReturnValue = 1 << 7,
513 	isOptimizedOut = 1 << 8,
514 	isEnregisteredGlobal = 1 << 9,
515 	isEnregisteredStatic = 1 << 10,
516 }
517 
518 void printLocalSymFlags(ushort set)
519 {
520 	import std.stdio : write;
521 	if (set == 0) {
522 		write(" none");
523 		return;
524 	}
525 	if (set & LocalSymFlags.isParameter) write(" isParameter");
526 	if (set & LocalSymFlags.isAddressTaken) write(" isAddressTaken");
527 	if (set & LocalSymFlags.isCompilerGenerated) write(" isCompilerGenerated");
528 	if (set & LocalSymFlags.isAggregate) write(" isAggregate");
529 	if (set & LocalSymFlags.isAggregated) write(" isAggregated");
530 	if (set & LocalSymFlags.isAliased) write(" isAliased");
531 	if (set & LocalSymFlags.isAlias) write(" isAlias");
532 	if (set & LocalSymFlags.isReturnValue) write(" isReturnValue");
533 	if (set & LocalSymFlags.isOptimizedOut) write(" isOptimizedOut");
534 	if (set & LocalSymFlags.isEnregisteredGlobal) write(" isEnregisteredGlobal");
535 	if (set & LocalSymFlags.isEnregisteredStatic) write(" isEnregisteredStatic");
536 }
537 
538 // struct LABELSYM32
539 // S_LABEL32
540 struct LabelSym {
541 	uint offset;
542 	ushort segment;
543 	ubyte flags; // set of ProcSymFlags
544 	// next follows zero-terminated string
545 }
546 
547 // struct BLOCKSYM32
548 // S_BLOCK32
549 align(1) struct BlockSym {
550 	uint parent;     // pointer to the parent
551 	uint end;        // pointer to this blocks end (S_END)
552 	uint codeSize;   // Block length
553 	uint codeOffset; // Offset in code segment
554 	ushort codeSegment;  // segment of block
555 	// next follows string (name)
556 }
557 
558 // struct OBJNAMESYM
559 // S_OBJNAME
560 align(1) struct ObjNameSym {
561 	uint signature;
562 	// next follows zero-terminated string (name)
563 }
564 
565 // struct SECTIONSYM
566 // S_SECTION
567 align(1) struct SectionSym
568 {
569 	ushort section;   // Section number
570 	ubyte  alignmentPower; // Alignment of this section == (1 << alignmentPower)
571 	ubyte  reserved;  // Reserved.  Must be zero.
572 	uint   rva;
573 	uint   length;
574 	uint   characteristics;
575 	// next follows zero-terminated string (name)
576 }
577 
578 // struct COFFGROUPSYM
579 // S_COFFGROUP
580 align(1) struct CoffGroupSym {
581 	uint      length;
582 	uint      characteristics;
583 	uint      symbolOffset;
584 	ushort    symbolSegment;
585 	// next follows zero-terminated string (name)
586 }
587 
588 // Comes from .exp files
589 // struct EXPORTSYM
590 // S_EXPORT
591 struct ExportSym {
592     ushort ordinal;
593     mixin(bitfields!(
594 		bool,  "isConstant",         1, // CONSTANT
595 		bool,  "isData",             1, // DATA
596 		bool,  "isPrivate",          1, // PRIVATE
597 		bool,  "hasNoName",          1, // NONAME
598 		bool,  "hasExplicitOrdinal", 1, // Ordinal was explicitly assigned
599 		bool,  "isForwarder",        1, // This is a forwarder
600 		uint,  "pad",               10,
601 	));
602 	// next follows zero-terminated string (name)
603 }
604 
605 // struct CV_LVAR_ADDR_RANGE
606 // represents an address range, used for optimized code debug info
607 struct LocalVariableAddrRange {
608 	uint offsetStart;
609 	ushort iSectStart;
610 	ushort range;
611 
612 	void toString(scope void delegate(const(char)[]) sink) {
613 		sink.formattedWrite("[%04X:%08X] - [%04X:%08X]",
614 			iSectStart, offsetStart, iSectStart, offsetStart+range);
615 	}
616 }
617 
618 // struct CV_LVAR_ADDR_GAP
619 struct LocalVariableAddrGap {
620 	ushort startOffset; // relative offset from the beginning of the live range.
621 	ushort length; // length of this gap.
622 }
623 
624 // struct DEFRANGESYMREGISTER
625 // S_DEFRANGE_REGISTER
626 struct DefRangeRegisterSym {
627 	RegisterId register; // Register to hold the value of the symbol
628 	ushort mayHaveNoName; // May have no user name on one of control flow path.
629 	LocalVariableAddrRange range; // Range of addresses where this program is valid
630 	// The value is not available in following gaps.
631 	// LocalVariableAddrGap[] gaps; follows
632 }
633 
634 // struct DEFRANGESYMSUBFIELDREGISTER
635 // S_DEFRANGE_SUBFIELD_REGISTER
636 struct DefRangeSubfieldRegisterSym {
637 	RegisterId register;
638 	ushort mayHaveNoName; // May have no user name on one of control flow path.
639 	uint offsetInParent;
640 	LocalVariableAddrRange range;
641 	// LocalVariableAddrGap[] gaps; follows
642 }
643 
644 // struct DEFRANGESYMREGISTERREL
645 // S_DEFRANGE_REGISTER_REL
646 align(1) struct DefRangeRegisterRelSym {
647 	RegisterId baseReg;                // Register to hold the base pointer of the symbol
648 	mixin(bitfields!(
649 		bool,  "spilledUdtMember",  1, // Spilled member for s.i.
650 		ubyte, "",                  3,
651 		uint,  "offsetInParent",   12, // Offset in parent variable.
652 	));
653 	int basePointerOffset;             // offset to register
654 
655 	LocalVariableAddrRange range;      // Range of addresses where this program is valid
656 }
657 
658 // struct DEFRANGESYM
659 // S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE
660 struct DefRangeFramePointerRelFullScopeSym { // A frame variable valid in all function scope
661 	int offFramePointer;  // offset to frame pointer
662 }
663 
664 // struct DEFRANGESYM
665 // S_DEFRANGE_FRAMEPOINTER_REL
666 struct DefRangeFramePointerRelSym { // A live range of frame variable
667 	int offFramePointer;  // offset to frame pointer
668 	LocalVariableAddrRange range; // Range of addresses where this program is valid
669 	// The value is not available in following gaps.
670 	// LocalVariableAddrGap[] follows
671 }
672 
673 
674 // struct FUNCTIONLIST
675 // S_CALLERS, S_CALLEES, S_INLINEES
676 struct FunctionListSym {
677 	uint numFuncs; // Number of functions
678 	// TypeIndex[numFuncs] funcs;
679 	// uint[] numInvocations; numInvocations.length may be < numFuncs.
680 	// For all (i >= numInvocations.length && i < numFuncs), numInvocations[i] == 0
681 }
682 
683 // S_CONSTANT, S_MANCONSTANT
684 align(2) struct ConstSym {
685 	TypeIndex type; // Type index (containing enum if enumerate) or metadata token
686 	ushort value; // numeric leaf containing value (if less than LF_NUMERIC) or CV_TYPE otherwise
687 	// followed by payload, depending on `value` content
688 	// followed by zero-terminated string (name)
689 }
690 static assert(ConstSym.sizeof == 6);
691 
692 // struct FRAMECOOKIE
693 // S_FRAMECOOKIE
694 struct FrameCookieSym {
695     uint offset;          // Frame relative offset
696     RegisterId reg;       // Register index
697     FrameCookieType type; // Type of the cookie
698     ubyte flags;          // Flags describing this cookie
699 }
700 
701 // struct HEAPALLOCSITE
702 // S_HEAPALLOCSITE
703 struct HeapAllocationSiteSym {
704 	uint offset;       // offset of call site
705 	ushort section;    // section index of call site
706 	ushort instrBytes; // length of heap allocation call instruction
707 	TypeIndex type;    // type index describing function signature
708 }
709 
710 //
711 // Symbol for describing indirect calls when they are using
712 // a function pointer cast on some other type or temporary.
713 // Typical content will be an LF_POINTER to an LF_PROCEDURE
714 // type record that should mimic an actual variable with the
715 // function pointer type in question.
716 //
717 // Since the compiler can sometimes tail-merge a function call
718 // through a function pointer, there may be more than one
719 // S_CALLSITEINFO record at an address.  This is similar to what
720 // you could do in your own code by:
721 //
722 //  if (expr)
723 //      pfn = &function1;
724 //  else
725 //      pfn = &function2;
726 //
727 //  (*pfn)(arg list);
728 //
729 // struct CALLSITEINFO
730 // S_CALLSITEINFO
731 struct CallSiteInfoSym {
732 	uint offset;      // offset of call site
733 	ushort section;   // section index of call site
734 	ushort reserved0; // alignment padding field, must be zero
735 	TypeIndex type;   // type index describing function signature
736 }
737 
738 // struct UNAMESPACE
739 // S_UNAMESPACE
740 struct UNamespaceSym { // using namespace
741     // followed by zero-terminated string (name)
742 }
743 
744 // struct FILESTATICSYM
745 // S_FILESTATIC
746 struct FileStaticSym {
747 	align(2):
748     TypeIndex type; // type index
749     uint modOffset; // index of mod filename in stringtable
750     ushort flags;   // set of LocalSymFlags
751     // followed by zero-terminated string (name)
752 }
753 static assert(FileStaticSym.sizeof == 10);