| # CodeView Symbol Records |
| |
| (symbols_intro)= |
| |
| ## Introduction |
| |
| This document describes the usage and serialization format of the various |
| CodeView symbol records that LLVM understands. Like |
| {doc}`CodeView Type Records <CodeViewTypes>`, we describe only the important |
| types which are generated by modern C++ toolchains. |
| |
| ## Record Categories |
| |
| Symbol records share one major similarity with {doc}`type records <CodeViewTypes>`: |
| They start with the same {ref}`record prefix <leaf_types>`, which we will not describe |
| again (refer to the previous link for a description). As a result of this, a sequence |
| of symbol records can be processed with largely the same code as that which processes |
| type records. There are several important differences between symbol and type records: |
| |
| - Symbol records only appear in the {doc}`PublicStream`, {doc}`GlobalStream`, and |
| {doc}`Module Info Streams <ModiStream>`. |
| - Type records only appear in the {doc}`TPI & IPI streams <TpiStream>`. |
| - While types are referenced from other CodeView records via {ref}`type indices <type_indices>`, |
| symbol records are referenced by the byte offset of the record in the stream that it appears |
| in. |
| - Types can reference types (via type indices), and symbols can reference both types (via type |
| indices) and symbols (via offsets), but types can never reference symbols. |
| - There is no notion of {ref}`Leaf Records <leaf_types>` and {ref}`Member Records <member_types>` |
| as there are with types. Every symbol record describes is own length. |
| - Certain special symbol records begin a "scope". For these records, all following records |
| up until the next `S_END` record are "children" of this symbol record. For example, |
| given a symbol record which describes a certain function, all local variables of this |
| function would appear following the function up until the corresponding `S_END` record. |
| |
| Finally, there are three general categories of symbol record, grouped by where they are legal |
| to appear in a PDB file. Public Symbols (which appear only in the |
| {doc}`publics stream <PublicStream>`), Global Symbols (which appear only in the |
| {doc}`globals stream <GlobalStream>`) and module symbols (which appear in the |
| {doc}`module info stream <ModiStream>`). |
| |
| (public_symbols)= |
| |
| ### Public Symbols |
| |
| Public symbols are the CodeView equivalent of DWARF `.debug_pubnames`. There |
| is one public symbol record for every function or variable in the program that |
| has a mangled name. The {doc}`Publics Stream <PublicStream>`, which contains these |
| records, additionally contains a hash table that allows one to quickly locate a |
| record by mangled name. |
| |
| #### S_PUB32 (0x110e) |
| |
| There is only type of public symbol, an `S_PUB32` which describes a mangled |
| name, a flag indicating what kind of symbol it is (e.g. function, variable), and |
| the symbol's address. The {ref}`dbi_section_map_substream` of the |
| {doc}`DBI Stream <DbiStream>` can be consulted to determine what module this address |
| corresponds to, and from there that module's {doc}`module debug stream <ModiStream>` |
| can be consulted to locate full information for the symbol with the given address. |
| |
| (global_symbols)= |
| |
| ### Global Symbols |
| |
| While there is one {ref}`public symbol <public_symbols>` for every symbol in the |
| program with `external` linkage, there is one global symbol for every symbol in the |
| program with linkage (including internal linkage). As a result, global symbols do |
| not describe a mangled name *or* an address, since symbols with internal linkage |
| need not have any mangling at all, and also may not have an address. Thus, all |
| global symbols simply refer directly to the full symbol record via a module/offset |
| combination. |
| |
| Similarly to {ref}`public symbols <public_symbols>`, all global symbols are contained |
| in a single {doc}`Globals Stream <GlobalStream>`, which contains a hash table mapping |
| fully qualified name to the corresponding record in the globals stream (which as |
| mentioned, then contains information allowing one to locate the full record in the |
| corresponding module symbol stream). |
| |
| Note that a consequence and limitation of this design is that program-wide lookup |
| by anything other than an exact textually matching fully-qualified name of whatever |
| the compiler decided to emit is impractical. This differs from DWARF, where even |
| though we don't necessarily have O(1) lookup by basename within a given scope (including |
| O(1) scope, we at least have O(n) access within a given scope). |
| |
| :::{important} |
| Program-wide lookup of names by anything other than an exact textually matching fully |
| qualified name is not possible. |
| ::: |
| |
| #### S_GDATA32 |
| |
| #### S_GTHREAD32 (0x1113) |
| |
| #### S_PROCREF (0x1125) |
| |
| #### S_LPROCREF (0x1127) |
| |
| #### S_GMANDATA (0x111d) |
| |
| (module_symbols)= |
| |
| ### Module Symbols |
| |
| #### S_END (0x0006) |
| |
| #### S_FRAMEPROC (0x1012) |
| |
| #### S_OBJNAME (0x1101) |
| |
| #### S_THUNK32 (0x1102) |
| |
| #### S_BLOCK32 (0x1103) |
| |
| #### S_LABEL32 (0x1105) |
| |
| #### S_REGISTER (0x1106) |
| |
| #### S_BPREL32 (0x110b) |
| |
| #### S_LPROC32 (0x110f) |
| |
| #### S_GPROC32 (0x1110) |
| |
| #### S_REGREL32 (0x1111) |
| |
| #### S_COMPILE2 (0x1116) |
| |
| #### S_UNAMESPACE (0x1124) |
| |
| #### S_TRAMPOLINE (0x112c) |
| |
| #### S_SECTION (0x1136) |
| |
| #### S_COFFGROUP (0x1137) |
| |
| #### S_EXPORT (0x1138) |
| |
| #### S_CALLSITEINFO (0x1139) |
| |
| #### S_FRAMECOOKIE (0x113a) |
| |
| #### S_COMPILE3 (0x113c) |
| |
| #### S_ENVBLOCK (0x113d) |
| |
| (s_local)= |
| |
| #### S_LOCAL (0x113e) |
| |
| Defines a local variable. |
| This record is followed by a series of `S_DEFRANGE*` records that define the |
| live range of this variable. |
| |
| ```cpp |
| struct LocalSym { |
| /// The type of this variable (TPI type index). |
| uint32_t Type; |
| /// See enum below. |
| uint16_t Flags; |
| /// Name of the variable. A zero terminated string. |
| char Name[]; |
| }; |
| enum class LocalSymFlags : uint16_t { |
| None = 0, |
| IsParameter = 1 << 0, |
| IsAddressTaken = 1 << 1, |
| IsCompilerGenerated = 1 << 2, |
| IsAggregate = 1 << 3, |
| IsAggregated = 1 << 4, |
| IsAliased = 1 << 5, |
| IsAlias = 1 << 6, |
| IsReturnValue = 1 << 7, |
| IsOptimizedOut = 1 << 8, |
| IsEnregisteredGlobal = 1 << 9, |
| IsEnregisteredStatic = 1 << 10, |
| }; |
| ``` |
| |
| All `S_DEFRANGE*` records consist of a header followed by |
| `LocalVariableAddrRange` and a list of `LocalVariableAddrGap` (until the |
| record length is reached) except for the |
| {ref}`S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE |
| <defrange_framepointer_rel_full_scope>` record. |
| |
| ```cpp |
| /// A live range of a variable. |
| struct LocalVariableAddrRange { |
| /// Starting offset in the section |
| uint32_t OffsetStart; |
| /// Index of the section |
| uint16_t ISectStart; |
| /// Size of the range |
| uint16_t Range; |
| }; |
| /// A subrange of a `LocalVariableAddrRange` where a variable is _not_ live. |
| struct LocalVariableAddrGap { |
| /// Start of the range, relative to `LocalVariableAddrRange::OffsetStart` |
| uint16_t GapStartOffset; |
| /// Size of the range |
| uint16_t Range; |
| }; |
| ``` |
| |
| The following records only describe the header. |
| |
| #### S_DEFRANGE (0x113f) |
| |
| % FIXME: Document the DIA programs. Are these the same as FPO programs? |
| |
| A live range expressed as a DIA program. |
| |
| ```cpp |
| struct DefrangeSymHeader { |
| /// DIA program to evaluate the value of the symbol |
| uint32_t Program; |
| }; |
| ``` |
| |
| #### S_DEFRANGE_SUBFIELD (0x1140) |
| |
| A live range of sub field of variable (e.g. `local.i`). |
| |
| ```cpp |
| struct DefrangeSubfieldSymHeader { |
| /// DIA program to evaluate the value of the symbol |
| uint32_t Program; |
| /// Offset in parent variable. |
| uint32_t OffsetInParent; |
| }; |
| ``` |
| |
| #### S_DEFRANGE_REGISTER (0x1141) |
| |
| A live range of a variable living in a register. |
| |
| ```cpp |
| struct DefrangeRegisterSymHeader { |
| /// Register to hold the value of the symbol |
| uint16_t Register; |
| /// May have no user name on one control flow path |
| uint16_t MayHaveNoName : 1; |
| uint16_t Padding : 15; |
| }; |
| ``` |
| |
| #### S_DEFRANGE_FRAMEPOINTER_REL (0x1142) |
| |
| A live range of frame variable. The register for the frame pointer is specified in `S_FRAMEPROC`. |
| |
| ```cpp |
| struct DefrangeFramepointerSymHeader { |
| /// Offset from the frame pointer |
| int32_t Offset; |
| }; |
| ``` |
| |
| #### S_DEFRANGE_SUBFIELD_REGISTER (0x1143) |
| |
| A live range of sub field of variable (e.g. `local.i`). |
| |
| ```cpp |
| struct DefrangeSubfieldRegisterSymHeader { |
| /// Register to hold the value of the symbol |
| uint16_t Register; |
| /// May have no user name on one of control flow path |
| uint16_t MayHaveNoName : 1; |
| uint16_t Padding1 : 15; |
| /// Offset in parent variable |
| uint32_t OffsetInParent : 12; |
| uint32_t Padding2 : 20; |
| }; |
| ``` |
| |
| (defrange_framepointer_rel_full_scope)= |
| |
| #### S_DEFRANGE_FRAMEPOINTER_REL_FULL_SCOPE (0x1144) |
| |
| A frame variable valid in all function scope. |
| |
| ```cpp |
| struct DefrangeFramepointerRelFullScopeSymHeader { |
| /// Offset from the frame pointer |
| int32_t Offset; |
| }; |
| ``` |
| |
| #### S_DEFRANGE_REGISTER_REL (0x1145) |
| |
| A live range of variable relative to a register (range version of `S_REGREL32`). |
| |
| ```cpp |
| struct DefrangeRegisterRelSymHeader { |
| /// Register to hold the base pointer of the symbol |
| uint16_t Register; |
| /// Spilled member for s.i |
| uint16_t SpilledUdtMember : 1; |
| uint16_t Padding : 3; |
| /// Offset in parent variable. |
| uint16_t OffsetInParent : 12; |
| /// Offset to register |
| int32_t BasePointerOffset; |
| }; |
| ``` |
| |
| #### S_DEFRANGE_REGISTER_REL_INDIR (0x1177) |
| |
| A live range of variable indirectly relative to a register (range version of `S_REGREL32_INDIR`). |
| |
| ```cpp |
| struct DefrangeRegisterRelIndirSymHeader { |
| /// Register to hold the base pointer of the symbol |
| uint16_t Register; |
| /// Spilled member for s.i |
| uint16_t SpilledUdtMember : 1; |
| uint16_t Padding : 3; |
| /// Offset in parent variable. |
| uint16_t OffsetInParent : 12; |
| /// Offset to register |
| int32_t BasePointerOffset; |
| /// Offset to add after dereferencing `Register + BasePointerOffset` |
| int32_t OffsetInUdt; |
| }; |
| ``` |
| |
| #### S_LPROC32_ID (0x1146) |
| |
| #### S_GPROC32_ID (0x1147) |
| |
| #### S_BUILDINFO (0x114c) |
| |
| #### S_INLINESITE (0x114d) |
| |
| #### S_INLINESITE_END (0x114e) |
| |
| #### S_PROC_ID_END (0x114f) |
| |
| #### S_FILESTATIC (0x1153) |
| |
| #### S_LPROC32_DPC (0x1155) |
| |
| #### S_LPROC32_DPC_ID (0x1156) |
| |
| #### S_CALLEES (0x115a) |
| |
| #### S_CALLERS (0x115b) |
| |
| #### S_HEAPALLOCSITE (0x115e) |
| |
| #### S_FASTLINK (0x1167) |
| |
| #### S_INLINEES (0x1168) |
| |
| #### S_REGREL32_INDIR (0x1171) |
| |
| This encodes a variable at the location `*($Register + Offset) + OffsetInUdt`. |
| It's equivalent to the following DWARF location expression: |
| |
| ``` |
| DW_OP_breg{corresponding DWARF register} {Offset} |
| DW_OP_deref |
| DW_OP_plus_uconst {OffsetInUdt} |
| ``` |
| |
| It's used in C++ 17 structured bindings for example: |
| |
| ```cpp |
| struct Foo { int a, b; }; |
| |
| void fn() { |
| Foo f = {1, 2}; |
| // ╰─ S_REGREL32{ reg = rsp, offset = 0 } |
| auto &[x, y] = f; |
| // │ ╰─ S_REGREL32_INDIR{ reg = rsp, offset = 8, offset-in-udt = 4, type = int } |
| // ╰─ S_REGREL32_INDIR{ reg = rsp, offset = 8, offset-in-udt = 0, type = int } |
| } |
| ``` |
| |
| The `S_REGREL32_INDIR` symbol for `y` from above looks like this: |
| |
| | Offset | Type | OffsetInUdt | Register | Name | |
| | ---------- | ---------- | ----------- | -------- | ------ | |
| | `08000000` | `74000000` | `04000000` | `4F01` | `7900` | |
| | 8 | int | 4 | RSP | "a" | |
| |
| (module_and_global_symbols)= |
| |
| ### Symbols which can go in either/both of the module info stream & global stream |
| |
| #### S_CONSTANT (0x1107) |
| |
| #### S_UDT (0x1108) |
| |
| #### S_LDATA32 (0x110c) |
| |
| #### S_LTHREAD32 (0x1112) |
| |
| #### S_LMANDATA (0x111c) |
| |
| #### S_MANCONSTANT (0x112d) |