Compressed integers
Blob and user string lengths, and the numbers inside signatures, use a compressed unsigned integer: if the first byte is below 0x80 it is the value (1 byte); if the top two bits are 10 the value is the low 6 bits of the
first byte and the whole second byte (2 bytes, up to 0x3FFF); if the top three bits are 110 it is the low 5 bits and three more bytes (4 bytes, up to 0x1FFFFFFF). Big-endian within the value. A first byte
of 0xE0 or more is invalid. Signed values (array lower bounds) rotate the sign into the low bit.
#Strings
A column holds the offset of a string. Strings are UTF-8 and end at a zero byte; offset 0 is the empty string, so the heap starts with a zero. The same string may be shared, and a string may be the tail of another (the offset points into it). The heap ends in zero padding to a multiple of 4. The explorer lists each string at its offset and reports an offset past the end and a string with no terminator.
#US
User strings, referred to by ldstr tokens (0x70……) in the code, not by a table column. Each is a compressed byte length, the UTF-16 characters, and one final byte: 1 if any character is above 0x7E or one of a few control characters, otherwise 0
(the length is therefore always odd, except for the empty string at offset 0).
#GUID
16-byte GUIDs; a column holds the 1-based number, not a byte offset. Shown in the usual text form (the first three groups are little-endian in the file).
#Blob
Each blob is a compressed length and that many bytes; offset 0 is the empty blob. What a blob contains depends on the column: signatures, constants, custom attribute values, marshalling descriptors, permission sets, public keys, hashes, and in a portable PDB sequence points and import lists. The explorer decodes signatures and shows the others as bytes.
Coded indexes
A coded index packs a tag (which table) into the low bits and the row number above it: value = (row << bits) | tag. The column is 2 bytes unless one of the tables it can name has at least 216 − bits rows, then 4. These
are the kinds, with the table each tag stands for; a tag that names no table is invalid and reported.
| Kind | Bits | Tag 0, 1, 2, … |
|---|---|---|
| TypeDefOrRef | 2 | TypeDef, TypeRef, TypeSpec |
| HasConstant | 2 | Field, Param, Property |
| HasCustomAttribute | 5 | MethodDef, Field, TypeRef, TypeDef, Param, InterfaceImpl, MemberRef, Module, DeclSecurity, Property, Event, StandAloneSig, ModuleRef, TypeSpec, Assembly, AssemblyRef, File, ExportedType, ManifestResource, GenericParam, GenericParamConstraint, MethodSpec |
| HasFieldMarshal | 1 | Field, Param |
| HasDeclSecurity | 2 | TypeDef, MethodDef, Assembly |
| MemberRefParent | 3 | TypeDef, TypeRef, ModuleRef, MethodDef, TypeSpec |
| HasSemantics | 1 | Event, Property |
| MethodDefOrRef | 1 | MethodDef, MemberRef |
| MemberForwarded | 1 | Field, MethodDef |
| Implementation | 2 | File, AssemblyRef, ExportedType |
| CustomAttributeType | 3 | (unused), (unused), MethodDef, MemberRef, (unused) |
| ResolutionScope | 2 | Module, ModuleRef, AssemblyRef, TypeRef |
| TypeOrMethodDef | 1 | TypeDef, MethodDef |
| HasCustomDebugInformation | 5 | (portable PDB) the HasCustomAttribute list, then Document, LocalScope, LocalVariable, LocalConstant, ImportScope |
The explorer shows a coded value as the table and row it names, the token (table << 24 | row) and the name of the row. A token is how code and attributes refer to a row: 0x02000005 is TypeDef row 5, 0x06000012 MethodDef row 18.
Not described
Known, not yet covered
- Viewer: the explorer lists the heaps entry by entry from offset 0 and does not show which entries nothing refers to or where entries overlap other entries (tail sharing).
Links
Reference
- ECMA-335 – partition II sections 23.1 and 24.2: heaps, coded indexes and compressed integers