| // Copyright (c) 2026, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #ifndef RUNTIME_PLATFORM_COFF_H_ |
| #define RUNTIME_PLATFORM_COFF_H_ |
| |
| #include "platform/globals.h" |
| |
| #if defined(DART_PRECOMPILER) |
| |
| namespace dart { |
| namespace coff { |
| |
| #pragma pack(push, 1) |
| |
| // ----------------------------------------------------------------------------- |
| // PE/COFF file format constants and structures |
| // |
| // Reference: Microsoft PE/COFF specification (winnt.h definitions). This |
| // header contains the minimum subset required by the COFF object writer used |
| // by gen_snapshot. |
| // ----------------------------------------------------------------------------- |
| |
| // File header machine values (we only support x64 here). |
| static constexpr uint16_t PE_FILE_MACHINE_UNKNOWN = 0x0000; |
| static constexpr uint16_t PE_FILE_MACHINE_AMD64 = 0x8664; |
| |
| // PE_FILE_HEADER.Characteristics flags. |
| static constexpr uint16_t PE_FILE_RELOCS_STRIPPED = 0x0001; |
| static constexpr uint16_t PE_FILE_EXECUTABLE_IMAGE = 0x0002; |
| static constexpr uint16_t PE_FILE_LINE_NUMS_STRIPPED = 0x0004; |
| static constexpr uint16_t PE_FILE_LARGE_ADDRESS_AWARE = 0x0020; |
| |
| struct ImageFileHeader { |
| uint16_t Machine; |
| uint16_t NumberOfSections; |
| uint32_t TimeDateStamp; |
| uint32_t PointerToSymbolTable; |
| uint32_t NumberOfSymbols; |
| uint16_t SizeOfOptionalHeader; |
| uint16_t Characteristics; |
| }; |
| |
| // PE_SECTION_HEADER.Characteristics flags (subset we use). |
| static constexpr uint32_t PE_SCN_CNT_CODE = 0x00000020; |
| static constexpr uint32_t PE_SCN_CNT_INITIALIZED_DATA = 0x00000040; |
| static constexpr uint32_t PE_SCN_CNT_UNINITIALIZED_DATA = 0x00000080; |
| static constexpr uint32_t PE_SCN_LNK_INFO = 0x00000200; |
| static constexpr uint32_t PE_SCN_LNK_REMOVE = 0x00000800; |
| static constexpr uint32_t PE_SCN_LNK_COMDAT = 0x00001000; |
| static constexpr uint32_t PE_SCN_LNK_NRELOC_OVFL = 0x01000000; |
| static constexpr uint32_t PE_SCN_ALIGN_1BYTES = 0x00100000; |
| static constexpr uint32_t PE_SCN_ALIGN_2BYTES = 0x00200000; |
| static constexpr uint32_t PE_SCN_ALIGN_4BYTES = 0x00300000; |
| static constexpr uint32_t PE_SCN_ALIGN_8BYTES = 0x00400000; |
| static constexpr uint32_t PE_SCN_ALIGN_16BYTES = 0x00500000; |
| static constexpr uint32_t PE_SCN_ALIGN_32BYTES = 0x00600000; |
| static constexpr uint32_t PE_SCN_ALIGN_64BYTES = 0x00700000; |
| static constexpr uint32_t PE_SCN_ALIGN_MASK = 0x00F00000; |
| static constexpr uint32_t PE_SCN_MEM_DISCARDABLE = 0x02000000; |
| static constexpr uint32_t PE_SCN_MEM_NOT_CACHED = 0x04000000; |
| static constexpr uint32_t PE_SCN_MEM_NOT_PAGED = 0x08000000; |
| static constexpr uint32_t PE_SCN_MEM_SHARED = 0x10000000; |
| static constexpr uint32_t PE_SCN_MEM_EXECUTE = 0x20000000; |
| static constexpr uint32_t PE_SCN_MEM_READ = 0x40000000; |
| static constexpr uint32_t PE_SCN_MEM_WRITE = 0x80000000; |
| |
| struct ImageSectionHeader { |
| char Name[8]; |
| uint32_t VirtualSize; |
| uint32_t VirtualAddress; |
| uint32_t SizeOfRawData; |
| uint32_t PointerToRawData; |
| uint32_t PointerToRelocations; |
| uint32_t PointerToLinenumbers; |
| uint16_t NumberOfRelocations; |
| uint16_t NumberOfLinenumbers; |
| uint32_t Characteristics; |
| }; |
| |
| // Symbol storage classes. |
| static constexpr uint8_t PE_SYM_CLASS_NULL = 0; |
| static constexpr uint8_t PE_SYM_CLASS_EXTERNAL = 2; |
| static constexpr uint8_t PE_SYM_CLASS_STATIC = 3; |
| static constexpr uint8_t PE_SYM_CLASS_LABEL = 6; |
| static constexpr uint8_t PE_SYM_CLASS_FUNCTION = 101; |
| static constexpr uint8_t PE_SYM_CLASS_FILE = 103; |
| static constexpr uint8_t PE_SYM_CLASS_SECTION = 104; |
| |
| // Symbol special section numbers. |
| static constexpr int16_t PE_SYM_UNDEFINED = 0; |
| static constexpr int16_t PE_SYM_ABSOLUTE = -1; |
| static constexpr int16_t PE_SYM_DEBUG = -2; |
| |
| // Symbol base types. |
| static constexpr uint16_t PE_SYM_TYPE_NULL = 0x0000; |
| // Microsoft tooling expects function symbols to have DT_FUNCTION in the high |
| // byte of the Type field, regardless of base type. The full value commonly |
| // emitted is 0x20. |
| static constexpr uint16_t PE_SYM_DTYPE_FUNCTION = 0x0020; |
| |
| struct ImageSymbol { |
| union { |
| char ShortName[8]; |
| struct { |
| uint32_t Zeroes; // 0 means use string table offset. |
| uint32_t Offset; // Offset into string table. |
| } LongName; |
| } N; |
| int32_t Value; |
| int16_t SectionNumber; |
| uint16_t Type; |
| uint8_t StorageClass; |
| uint8_t NumberOfAuxSymbols; |
| }; |
| |
| // Auxiliary symbol record for PE_SYM_CLASS_STATIC section symbols. |
| struct ImageAuxSymbolSection { |
| uint32_t Length; |
| uint16_t NumberOfRelocations; |
| uint16_t NumberOfLinenumbers; |
| uint32_t CheckSum; |
| int16_t Number; |
| uint8_t Selection; |
| uint8_t Reserved[3]; |
| }; |
| |
| // Auxiliary symbol record for PE_SYM_CLASS_EXTERNAL function symbols. |
| struct ImageAuxSymbolFuncDef { |
| uint32_t TagIndex; |
| uint32_t TotalSize; |
| uint32_t PointerToLinenumber; |
| uint32_t PointerToNextFunction; |
| uint16_t Unused; |
| }; |
| |
| // Relocation record. |
| struct ImageRelocation { |
| uint32_t VirtualAddress; |
| uint32_t SymbolTableIndex; |
| uint16_t Type; |
| }; |
| |
| // x64 relocation types we use. |
| static constexpr uint16_t PE_REL_AMD64_ABSOLUTE = 0x0000; |
| static constexpr uint16_t PE_REL_AMD64_ADDR64 = 0x0001; |
| static constexpr uint16_t PE_REL_AMD64_ADDR32 = 0x0002; |
| static constexpr uint16_t PE_REL_AMD64_ADDR32NB = 0x0003; |
| static constexpr uint16_t PE_REL_AMD64_REL32 = 0x0004; |
| static constexpr uint16_t PE_REL_AMD64_REL32_1 = 0x0005; |
| static constexpr uint16_t PE_REL_AMD64_REL32_2 = 0x0006; |
| static constexpr uint16_t PE_REL_AMD64_REL32_3 = 0x0007; |
| static constexpr uint16_t PE_REL_AMD64_REL32_4 = 0x0008; |
| static constexpr uint16_t PE_REL_AMD64_REL32_5 = 0x0009; |
| static constexpr uint16_t PE_REL_AMD64_SECTION = 0x000A; |
| static constexpr uint16_t PE_REL_AMD64_SECREL = 0x000B; |
| |
| // ----------------------------------------------------------------------------- |
| // SEH / unwind info (x64) |
| // |
| // Locally redeclared to avoid pulling in runtime/platform/unwinding_records.h, |
| // which is built for the JIT and not appropriate for inclusion from the |
| // snapshot writer. |
| // ----------------------------------------------------------------------------- |
| |
| static constexpr uint8_t kUnwindVersion = 1; |
| |
| // UNWIND_OP_CODES (subset). |
| enum UnwindOpCode : uint8_t { |
| UWOP_PUSH_NONVOL = 0, |
| UWOP_ALLOC_LARGE = 1, |
| UWOP_ALLOC_SMALL = 2, |
| UWOP_SET_FPREG = 3, |
| UWOP_SAVE_NONVOL = 4, |
| UWOP_SAVE_NONVOL_FAR = 5, |
| UWOP_SAVE_XMM128 = 8, |
| UWOP_SAVE_XMM128_FAR = 9, |
| UWOP_PUSH_MACHFRAME = 10, |
| }; |
| |
| // Register encodings for unwind ops. |
| static constexpr uint8_t kRegRax = 0; |
| static constexpr uint8_t kRegRcx = 1; |
| static constexpr uint8_t kRegRdx = 2; |
| static constexpr uint8_t kRegRbx = 3; |
| static constexpr uint8_t kRegRsp = 4; |
| static constexpr uint8_t kRegRbp = 5; |
| static constexpr uint8_t kRegRsi = 6; |
| static constexpr uint8_t kRegRdi = 7; |
| |
| struct UnwindCode { |
| uint8_t CodeOffset; |
| uint8_t UnwindOp : 4; |
| uint8_t OpInfo : 4; |
| }; |
| |
| struct UnwindInfoHeader { |
| uint8_t Version : 3; |
| uint8_t Flags : 5; |
| uint8_t SizeOfProlog; |
| uint8_t CountOfCodes; |
| uint8_t FrameRegister : 4; |
| uint8_t FrameOffset : 4; |
| }; |
| |
| struct RuntimeFunction { |
| uint32_t BeginAddress; |
| uint32_t EndAddress; |
| uint32_t UnwindData; |
| }; |
| |
| // ----------------------------------------------------------------------------- |
| // CodeView debug records (MSVC PDB-compatible). |
| // |
| // Reference: Microsoft cvinfo.h, LLVM include/llvm/DebugInfo/CodeView. We |
| // declare only what the writer emits. |
| // ----------------------------------------------------------------------------- |
| |
| // .debug$S leading signature. |
| static constexpr uint32_t CV_SIGNATURE_C13 = 4; |
| |
| // Debug subsection kinds. |
| static constexpr uint32_t DEBUG_S_SYMBOLS = 0xF1; |
| static constexpr uint32_t DEBUG_S_LINES = 0xF2; |
| static constexpr uint32_t DEBUG_S_STRINGTABLE = 0xF3; |
| static constexpr uint32_t DEBUG_S_FILECHKSMS = 0xF4; |
| static constexpr uint32_t DEBUG_S_INLINEELINES = 0xF6; |
| |
| // File checksum kinds. |
| static constexpr uint8_t CHKSUM_TYPE_NONE = 0; |
| static constexpr uint8_t CHKSUM_TYPE_MD5 = 1; |
| static constexpr uint8_t CHKSUM_TYPE_SHA1 = 2; |
| static constexpr uint8_t CHKSUM_TYPE_SHA256 = 3; |
| |
| // Symbol record kinds (subset). |
| static constexpr uint16_t S_END = 0x0006; |
| static constexpr uint16_t S_FRAMEPROC = 0x1012; |
| static constexpr uint16_t S_OBJNAME = 0x1101; |
| static constexpr uint16_t S_BLOCK32 = 0x1103; |
| static constexpr uint16_t S_COMPILE2 = 0x1116; |
| static constexpr uint16_t S_GPROC32 = 0x1110; |
| static constexpr uint16_t S_LPROC32 = 0x110F; |
| static constexpr uint16_t S_COMPILE3 = 0x113C; |
| static constexpr uint16_t S_GPROC32_ID = 0x1147; |
| static constexpr uint16_t S_LPROC32_ID = 0x1146; |
| static constexpr uint16_t S_PROC_ID_END = 0x114F; |
| |
| // Type record kinds (subset). |
| static constexpr uint16_t LF_POINTER = 0x1002; |
| static constexpr uint16_t LF_PROCEDURE = 0x1008; |
| static constexpr uint16_t LF_ARGLIST = 0x1201; |
| static constexpr uint16_t LF_FUNC_ID = 0x1601; |
| static constexpr uint16_t LF_STRING_ID = 0x1605; |
| |
| // Primitive type indices used by us. |
| static constexpr uint32_t T_VOID = 0x0003; |
| static constexpr uint32_t T_NOTYPE = 0x0000; |
| |
| // Calling conventions. |
| static constexpr uint8_t CV_CALL_NEAR_C = 0x00; |
| |
| // Language for S_COMPILE3 (we use CV_CFL_C as a closest reasonable proxy for |
| // Dart-AOT generated code). |
| static constexpr uint8_t CV_CFL_C = 0x00; |
| static constexpr uint8_t CV_CFL_CXX = 0x01; |
| static constexpr uint8_t CV_CFL_RUST = 0x15; |
| |
| // Machine for S_COMPILE3. |
| static constexpr uint16_t CV_CFL_X64 = 0xD0; |
| |
| // Subsection (kind/size) header. Each subsection in .debug$S starts with this |
| // followed by `length` bytes of payload, aligned to 4 on completion. |
| struct DebugSubsectionHeader { |
| uint32_t Kind; // DEBUG_S_* |
| uint32_t Length; // size of payload (not including header) |
| }; |
| |
| // Per-function block at the start of a DEBUG_S_LINES payload. |
| struct CvLineSectionHeader { |
| uint32_t Offset; // SECREL relocation target |
| uint16_t Section; // SECTION relocation target (1-based section index) |
| uint16_t Flags; // 0 (no columns) or 1 (with columns) |
| uint32_t CodeSize; // length of code described by this block |
| }; |
| |
| // Header for a contiguous line block within a function (one per source file). |
| struct CvLineBlockHeader { |
| uint32_t NameIndex; // offset into file checksum subsection |
| uint32_t NumLines; |
| uint32_t BlockSize; // total bytes including this header |
| }; |
| |
| // Single line entry. `Flags` packs: |
| // bits 0-23: line start |
| // bits 24-30: delta line end |
| // bit 31: is statement |
| struct CvLine { |
| static constexpr uint32_t kLineMask = 0x00ffffffu; |
| static constexpr uint32_t kIsStatementBit = 1u << 31; |
| |
| uint32_t Offset; // byte offset from function start |
| uint32_t Flags; |
| }; |
| |
| // File checksum entry header in a DEBUG_S_FILECHKSMS subsection. |
| struct CvFileChecksumHeader { |
| uint32_t NameOffset; // offset into DEBUG_S_STRINGTABLE |
| uint8_t ChecksumSize; |
| uint8_t ChecksumKind; // CHKSUM_TYPE_* |
| // Followed by `ChecksumSize` bytes, padded to 4-byte alignment. |
| }; |
| |
| // S_GPROC32_ID record (followed by null-terminated name). |
| struct ProcSym32 { |
| uint16_t RecordLength; // size of record excluding the length field itself |
| uint16_t RecordKind; // S_GPROC32_ID |
| uint32_t Parent; // 0 |
| uint32_t End; // patched to point at matching S_PROC_ID_END |
| uint32_t Next; // 0 |
| uint32_t CodeSize; |
| uint32_t DebugStart; // 0 |
| uint32_t DebugEnd; // 0 |
| uint32_t FunctionType; // LF_FUNC_ID index |
| uint32_t CodeOffset; // patched by SECREL relocation |
| uint16_t Segment; // patched by SECTION relocation |
| uint8_t Flags; |
| // Followed by name (NUL-terminated). |
| }; |
| |
| // S_FRAMEPROC record. |
| struct FrameProcSym { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // S_FRAMEPROC |
| uint32_t FrameSize; // total frame |
| uint32_t PaddingSize; |
| uint32_t OffsetToPadding; |
| uint32_t BytesOfCalleeSavedRegisters; |
| uint32_t OffsetOfExceptionHandler; |
| uint16_t SectionOfExceptionHandler; |
| uint32_t Flags; |
| }; |
| |
| // S_COMPILE3 record (followed by null-terminated compiler name). |
| struct Compile3Sym { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // S_COMPILE3 |
| uint32_t Flags; // low byte = language, high bytes = misc flags |
| uint16_t Machine; // CV_CFL_X64 |
| uint16_t FrontendMajor; |
| uint16_t FrontendMinor; |
| uint16_t FrontendBuild; |
| uint16_t FrontendQFE; |
| uint16_t BackendMajor; |
| uint16_t BackendMinor; |
| uint16_t BackendBuild; |
| uint16_t BackendQFE; |
| // Followed by VersionString (NUL-terminated). |
| }; |
| |
| // S_OBJNAME record (followed by null-terminated name). |
| struct ObjNameSym { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // S_OBJNAME |
| uint32_t Signature; // 0 |
| // Followed by name (NUL-terminated). |
| }; |
| |
| // LF_ARGLIST type record body. |
| struct ArgListType { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // LF_ARGLIST |
| uint32_t Count; // followed by `Count` uint32_t argument type indices |
| }; |
| |
| // LF_PROCEDURE type record. |
| struct ProcedureType { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // LF_PROCEDURE |
| uint32_t ReturnType; |
| uint8_t CallType; // CV_CALL_NEAR_C |
| uint8_t FuncAttrs; // 0 |
| uint16_t ParameterCount; |
| uint32_t ArgListType; |
| }; |
| |
| // LF_FUNC_ID type record (followed by NUL-terminated name). |
| struct FuncIdType { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // LF_FUNC_ID |
| uint32_t ParentScope; // 0 |
| uint32_t FunctionType; |
| // Followed by Name (NUL-terminated). |
| }; |
| |
| // LF_STRING_ID type record (followed by NUL-terminated string). |
| struct StringIdType { |
| uint16_t RecordLength; |
| uint16_t RecordKind; // LF_STRING_ID |
| uint32_t Substring; // 0 |
| // Followed by String (NUL-terminated). |
| }; |
| |
| #pragma pack(pop) |
| |
| // ----------------------------------------------------------------------------- |
| // Compile-time size assertions |
| // ----------------------------------------------------------------------------- |
| |
| static_assert(sizeof(ImageFileHeader) == 20, "PE/COFF file header layout"); |
| static_assert(sizeof(ImageSectionHeader) == 40, |
| "PE/COFF section header layout"); |
| static_assert(sizeof(ImageSymbol) == 18, "PE/COFF symbol layout"); |
| static_assert(sizeof(ImageAuxSymbolSection) == 18, "Aux section symbol layout"); |
| static_assert(sizeof(ImageAuxSymbolFuncDef) == 18, "Aux funcdef layout"); |
| static_assert(sizeof(ImageRelocation) == 10, "PE/COFF relocation layout"); |
| static_assert(sizeof(UnwindInfoHeader) == 4, "Unwind info header layout"); |
| static_assert(sizeof(UnwindCode) == 2, "Unwind code layout"); |
| static_assert(sizeof(RuntimeFunction) == 12, "RUNTIME_FUNCTION layout"); |
| static_assert(sizeof(DebugSubsectionHeader) == 8, "CodeView subsection header"); |
| static_assert(sizeof(CvLineSectionHeader) == 12, |
| "CodeView line section header"); |
| static_assert(sizeof(CvLineBlockHeader) == 12, "CodeView line block header"); |
| static_assert(sizeof(CvLine) == 8, "CodeView line entry"); |
| static_assert(sizeof(CvFileChecksumHeader) == 6, "CodeView file checksum"); |
| static_assert(sizeof(ProcSym32) == 39, "CodeView S_GPROC32_ID prefix"); |
| // FrameProcSym is 30 bytes when packed-1 (2+2+4+4+4+4+4+2+4). Microsoft's |
| // natural-aligned layout in cvinfo.h ends up at 32 bytes; the on-wire CodeView |
| // record is the packed 30-byte form, which is what gets emitted by our byte- |
| // stream helpers in codeview.cc. |
| static_assert(sizeof(FrameProcSym) == 30, "CodeView S_FRAMEPROC layout"); |
| static_assert(sizeof(Compile3Sym) == 26, "CodeView S_COMPILE3 prefix"); |
| static_assert(sizeof(ObjNameSym) == 8, "CodeView S_OBJNAME prefix"); |
| static_assert(sizeof(ArgListType) == 8, "CodeView LF_ARGLIST prefix"); |
| // ProcedureType: 2-byte RecordLength + 14-byte LF_PROCEDURE body = 16 bytes. |
| static_assert(sizeof(ProcedureType) == 16, "CodeView LF_PROCEDURE layout"); |
| static_assert(sizeof(FuncIdType) == 12, "CodeView LF_FUNC_ID prefix"); |
| static_assert(sizeof(StringIdType) == 8, "CodeView LF_STRING_ID prefix"); |
| |
| } // namespace coff |
| } // namespace dart |
| |
| #endif // DART_PRECOMPILER |
| |
| #endif // RUNTIME_PLATFORM_COFF_H_ |