diff --git a/llvm/include/llvm/BinaryFormat/MachO.h b/llvm/include/llvm/BinaryFormat/MachO.h index dc8d10f6129b..e84ed8b643cb 100644 --- a/llvm/include/llvm/BinaryFormat/MachO.h +++ b/llvm/include/llvm/BinaryFormat/MachO.h @@ -1,2010 +1,2011 @@ //===-- llvm/BinaryFormat/MachO.h - The MachO file format -------*- C++/-*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file defines manifest constants for the MachO object file format. // //===----------------------------------------------------------------------===// #ifndef LLVM_BINARYFORMAT_MACHO_H #define LLVM_BINARYFORMAT_MACHO_H #include "llvm/Support/Compiler.h" #include "llvm/Support/DataTypes.h" #include "llvm/Support/Error.h" #include "llvm/Support/SwapByteOrder.h" namespace llvm { class Triple; namespace MachO { // Enums from enum : uint32_t { // Constants for the "magic" field in llvm::MachO::mach_header and // llvm::MachO::mach_header_64 MH_MAGIC = 0xFEEDFACEu, MH_CIGAM = 0xCEFAEDFEu, MH_MAGIC_64 = 0xFEEDFACFu, MH_CIGAM_64 = 0xCFFAEDFEu, FAT_MAGIC = 0xCAFEBABEu, FAT_CIGAM = 0xBEBAFECAu, FAT_MAGIC_64 = 0xCAFEBABFu, FAT_CIGAM_64 = 0xBFBAFECAu }; enum HeaderFileType { // Constants for the "filetype" field in llvm::MachO::mach_header and // llvm::MachO::mach_header_64 MH_OBJECT = 0x1u, MH_EXECUTE = 0x2u, MH_FVMLIB = 0x3u, MH_CORE = 0x4u, MH_PRELOAD = 0x5u, MH_DYLIB = 0x6u, MH_DYLINKER = 0x7u, MH_BUNDLE = 0x8u, MH_DYLIB_STUB = 0x9u, MH_DSYM = 0xAu, MH_KEXT_BUNDLE = 0xBu }; enum { // Constant bits for the "flags" field in llvm::MachO::mach_header and // llvm::MachO::mach_header_64 MH_NOUNDEFS = 0x00000001u, MH_INCRLINK = 0x00000002u, MH_DYLDLINK = 0x00000004u, MH_BINDATLOAD = 0x00000008u, MH_PREBOUND = 0x00000010u, MH_SPLIT_SEGS = 0x00000020u, MH_LAZY_INIT = 0x00000040u, MH_TWOLEVEL = 0x00000080u, MH_FORCE_FLAT = 0x00000100u, MH_NOMULTIDEFS = 0x00000200u, MH_NOFIXPREBINDING = 0x00000400u, MH_PREBINDABLE = 0x00000800u, MH_ALLMODSBOUND = 0x00001000u, MH_SUBSECTIONS_VIA_SYMBOLS = 0x00002000u, MH_CANONICAL = 0x00004000u, MH_WEAK_DEFINES = 0x00008000u, MH_BINDS_TO_WEAK = 0x00010000u, MH_ALLOW_STACK_EXECUTION = 0x00020000u, MH_ROOT_SAFE = 0x00040000u, MH_SETUID_SAFE = 0x00080000u, MH_NO_REEXPORTED_DYLIBS = 0x00100000u, MH_PIE = 0x00200000u, MH_DEAD_STRIPPABLE_DYLIB = 0x00400000u, MH_HAS_TLV_DESCRIPTORS = 0x00800000u, MH_NO_HEAP_EXECUTION = 0x01000000u, MH_APP_EXTENSION_SAFE = 0x02000000u, MH_NLIST_OUTOFSYNC_WITH_DYLDINFO = 0x04000000u, MH_DYLIB_IN_CACHE = 0x80000000u, }; enum : uint32_t { // Flags for the "cmd" field in llvm::MachO::load_command LC_REQ_DYLD = 0x80000000u }; #define HANDLE_LOAD_COMMAND(LCName, LCValue, LCStruct) LCName = LCValue, enum LoadCommandType : uint32_t { #include "llvm/BinaryFormat/MachO.def" }; #undef HANDLE_LOAD_COMMAND enum : uint32_t { // Constant bits for the "flags" field in llvm::MachO::segment_command SG_HIGHVM = 0x1u, SG_FVMLIB = 0x2u, SG_NORELOC = 0x4u, SG_PROTECTED_VERSION_1 = 0x8u, // Constant masks for the "flags" field in llvm::MachO::section and // llvm::MachO::section_64 SECTION_TYPE = 0x000000ffu, // SECTION_TYPE SECTION_ATTRIBUTES = 0xffffff00u, // SECTION_ATTRIBUTES SECTION_ATTRIBUTES_USR = 0xff000000u, // SECTION_ATTRIBUTES_USR SECTION_ATTRIBUTES_SYS = 0x00ffff00u // SECTION_ATTRIBUTES_SYS }; /// These are the section type and attributes fields. A MachO section can /// have only one Type, but can have any of the attributes specified. enum SectionType : uint32_t { // Constant masks for the "flags[7:0]" field in llvm::MachO::section and // llvm::MachO::section_64 (mask "flags" with SECTION_TYPE) /// S_REGULAR - Regular section. S_REGULAR = 0x00u, /// S_ZEROFILL - Zero fill on demand section. S_ZEROFILL = 0x01u, /// S_CSTRING_LITERALS - Section with literal C strings. S_CSTRING_LITERALS = 0x02u, /// S_4BYTE_LITERALS - Section with 4 byte literals. S_4BYTE_LITERALS = 0x03u, /// S_8BYTE_LITERALS - Section with 8 byte literals. S_8BYTE_LITERALS = 0x04u, /// S_LITERAL_POINTERS - Section with pointers to literals. S_LITERAL_POINTERS = 0x05u, /// S_NON_LAZY_SYMBOL_POINTERS - Section with non-lazy symbol pointers. S_NON_LAZY_SYMBOL_POINTERS = 0x06u, /// S_LAZY_SYMBOL_POINTERS - Section with lazy symbol pointers. S_LAZY_SYMBOL_POINTERS = 0x07u, /// S_SYMBOL_STUBS - Section with symbol stubs, byte size of stub in /// the Reserved2 field. S_SYMBOL_STUBS = 0x08u, /// S_MOD_INIT_FUNC_POINTERS - Section with only function pointers for /// initialization. S_MOD_INIT_FUNC_POINTERS = 0x09u, /// S_MOD_TERM_FUNC_POINTERS - Section with only function pointers for /// termination. S_MOD_TERM_FUNC_POINTERS = 0x0au, /// S_COALESCED - Section contains symbols that are to be coalesced. S_COALESCED = 0x0bu, /// S_GB_ZEROFILL - Zero fill on demand section (that can be larger than 4 /// gigabytes). S_GB_ZEROFILL = 0x0cu, /// S_INTERPOSING - Section with only pairs of function pointers for /// interposing. S_INTERPOSING = 0x0du, /// S_16BYTE_LITERALS - Section with only 16 byte literals. S_16BYTE_LITERALS = 0x0eu, /// S_DTRACE_DOF - Section contains DTrace Object Format. S_DTRACE_DOF = 0x0fu, /// S_LAZY_DYLIB_SYMBOL_POINTERS - Section with lazy symbol pointers to /// lazy loaded dylibs. S_LAZY_DYLIB_SYMBOL_POINTERS = 0x10u, /// S_THREAD_LOCAL_REGULAR - Thread local data section. S_THREAD_LOCAL_REGULAR = 0x11u, /// S_THREAD_LOCAL_ZEROFILL - Thread local zerofill section. S_THREAD_LOCAL_ZEROFILL = 0x12u, /// S_THREAD_LOCAL_VARIABLES - Section with thread local variable /// structure data. S_THREAD_LOCAL_VARIABLES = 0x13u, /// S_THREAD_LOCAL_VARIABLE_POINTERS - Section with pointers to thread /// local structures. S_THREAD_LOCAL_VARIABLE_POINTERS = 0x14u, /// S_THREAD_LOCAL_INIT_FUNCTION_POINTERS - Section with thread local /// variable initialization pointers to functions. S_THREAD_LOCAL_INIT_FUNCTION_POINTERS = 0x15u, LAST_KNOWN_SECTION_TYPE = S_THREAD_LOCAL_INIT_FUNCTION_POINTERS }; enum : uint32_t { // Constant masks for the "flags[31:24]" field in llvm::MachO::section and // llvm::MachO::section_64 (mask "flags" with SECTION_ATTRIBUTES_USR) /// S_ATTR_PURE_INSTRUCTIONS - Section contains only true machine /// instructions. S_ATTR_PURE_INSTRUCTIONS = 0x80000000u, /// S_ATTR_NO_TOC - Section contains coalesced symbols that are not to be /// in a ranlib table of contents. S_ATTR_NO_TOC = 0x40000000u, /// S_ATTR_STRIP_STATIC_SYMS - Ok to strip static symbols in this section /// in files with the MY_DYLDLINK flag. S_ATTR_STRIP_STATIC_SYMS = 0x20000000u, /// S_ATTR_NO_DEAD_STRIP - No dead stripping. S_ATTR_NO_DEAD_STRIP = 0x10000000u, /// S_ATTR_LIVE_SUPPORT - Blocks are live if they reference live blocks. S_ATTR_LIVE_SUPPORT = 0x08000000u, /// S_ATTR_SELF_MODIFYING_CODE - Used with i386 code stubs written on by /// dyld. S_ATTR_SELF_MODIFYING_CODE = 0x04000000u, /// S_ATTR_DEBUG - A debug section. S_ATTR_DEBUG = 0x02000000u, // Constant masks for the "flags[23:8]" field in llvm::MachO::section and // llvm::MachO::section_64 (mask "flags" with SECTION_ATTRIBUTES_SYS) /// S_ATTR_SOME_INSTRUCTIONS - Section contains some machine instructions. S_ATTR_SOME_INSTRUCTIONS = 0x00000400u, /// S_ATTR_EXT_RELOC - Section has external relocation entries. S_ATTR_EXT_RELOC = 0x00000200u, /// S_ATTR_LOC_RELOC - Section has local relocation entries. S_ATTR_LOC_RELOC = 0x00000100u, // Constant masks for the value of an indirect symbol in an indirect // symbol table INDIRECT_SYMBOL_LOCAL = 0x80000000u, INDIRECT_SYMBOL_ABS = 0x40000000u }; enum DataRegionType { // Constants for the "kind" field in a data_in_code_entry structure DICE_KIND_DATA = 1u, DICE_KIND_JUMP_TABLE8 = 2u, DICE_KIND_JUMP_TABLE16 = 3u, DICE_KIND_JUMP_TABLE32 = 4u, DICE_KIND_ABS_JUMP_TABLE32 = 5u }; enum RebaseType { REBASE_TYPE_POINTER = 1u, REBASE_TYPE_TEXT_ABSOLUTE32 = 2u, REBASE_TYPE_TEXT_PCREL32 = 3u }; enum { REBASE_OPCODE_MASK = 0xF0u, REBASE_IMMEDIATE_MASK = 0x0Fu }; enum RebaseOpcode { REBASE_OPCODE_DONE = 0x00u, REBASE_OPCODE_SET_TYPE_IMM = 0x10u, REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x20u, REBASE_OPCODE_ADD_ADDR_ULEB = 0x30u, REBASE_OPCODE_ADD_ADDR_IMM_SCALED = 0x40u, REBASE_OPCODE_DO_REBASE_IMM_TIMES = 0x50u, REBASE_OPCODE_DO_REBASE_ULEB_TIMES = 0x60u, REBASE_OPCODE_DO_REBASE_ADD_ADDR_ULEB = 0x70u, REBASE_OPCODE_DO_REBASE_ULEB_TIMES_SKIPPING_ULEB = 0x80u }; enum BindType { BIND_TYPE_POINTER = 1u, BIND_TYPE_TEXT_ABSOLUTE32 = 2u, BIND_TYPE_TEXT_PCREL32 = 3u }; enum BindSpecialDylib { BIND_SPECIAL_DYLIB_SELF = 0, BIND_SPECIAL_DYLIB_MAIN_EXECUTABLE = -1, BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2 }; enum { BIND_SYMBOL_FLAGS_WEAK_IMPORT = 0x1u, BIND_SYMBOL_FLAGS_NON_WEAK_DEFINITION = 0x8u, BIND_OPCODE_MASK = 0xF0u, BIND_IMMEDIATE_MASK = 0x0Fu }; enum BindOpcode { BIND_OPCODE_DONE = 0x00u, BIND_OPCODE_SET_DYLIB_ORDINAL_IMM = 0x10u, BIND_OPCODE_SET_DYLIB_ORDINAL_ULEB = 0x20u, BIND_OPCODE_SET_DYLIB_SPECIAL_IMM = 0x30u, BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM = 0x40u, BIND_OPCODE_SET_TYPE_IMM = 0x50u, BIND_OPCODE_SET_ADDEND_SLEB = 0x60u, BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB = 0x70u, BIND_OPCODE_ADD_ADDR_ULEB = 0x80u, BIND_OPCODE_DO_BIND = 0x90u, BIND_OPCODE_DO_BIND_ADD_ADDR_ULEB = 0xA0u, BIND_OPCODE_DO_BIND_ADD_ADDR_IMM_SCALED = 0xB0u, BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB = 0xC0u }; enum { EXPORT_SYMBOL_FLAGS_KIND_MASK = 0x03u, EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION = 0x04u, EXPORT_SYMBOL_FLAGS_REEXPORT = 0x08u, EXPORT_SYMBOL_FLAGS_STUB_AND_RESOLVER = 0x10u }; enum ExportSymbolKind { EXPORT_SYMBOL_FLAGS_KIND_REGULAR = 0x00u, EXPORT_SYMBOL_FLAGS_KIND_THREAD_LOCAL = 0x01u, EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE = 0x02u }; enum { // Constant masks for the "n_type" field in llvm::MachO::nlist and // llvm::MachO::nlist_64 N_STAB = 0xe0, N_PEXT = 0x10, N_TYPE = 0x0e, N_EXT = 0x01 }; enum NListType : uint8_t { // Constants for the "n_type & N_TYPE" llvm::MachO::nlist and // llvm::MachO::nlist_64 N_UNDF = 0x0u, N_ABS = 0x2u, N_SECT = 0xeu, N_PBUD = 0xcu, N_INDR = 0xau }; enum SectionOrdinal { // Constants for the "n_sect" field in llvm::MachO::nlist and // llvm::MachO::nlist_64 NO_SECT = 0u, MAX_SECT = 0xffu }; enum { // Constant masks for the "n_desc" field in llvm::MachO::nlist and // llvm::MachO::nlist_64 // The low 3 bits are the for the REFERENCE_TYPE. REFERENCE_TYPE = 0x7, REFERENCE_FLAG_UNDEFINED_NON_LAZY = 0, REFERENCE_FLAG_UNDEFINED_LAZY = 1, REFERENCE_FLAG_DEFINED = 2, REFERENCE_FLAG_PRIVATE_DEFINED = 3, REFERENCE_FLAG_PRIVATE_UNDEFINED_NON_LAZY = 4, REFERENCE_FLAG_PRIVATE_UNDEFINED_LAZY = 5, // Flag bits (some overlap with the library ordinal bits). N_ARM_THUMB_DEF = 0x0008u, REFERENCED_DYNAMICALLY = 0x0010u, N_NO_DEAD_STRIP = 0x0020u, N_WEAK_REF = 0x0040u, N_WEAK_DEF = 0x0080u, N_SYMBOL_RESOLVER = 0x0100u, N_ALT_ENTRY = 0x0200u, N_COLD_FUNC = 0x0400u, // For undefined symbols coming from libraries, see GET_LIBRARY_ORDINAL() // as these are in the top 8 bits. SELF_LIBRARY_ORDINAL = 0x0, MAX_LIBRARY_ORDINAL = 0xfd, DYNAMIC_LOOKUP_ORDINAL = 0xfe, EXECUTABLE_ORDINAL = 0xff }; enum StabType { // Constant values for the "n_type" field in llvm::MachO::nlist and // llvm::MachO::nlist_64 when "(n_type & N_STAB) != 0" N_GSYM = 0x20u, N_FNAME = 0x22u, N_FUN = 0x24u, N_STSYM = 0x26u, N_LCSYM = 0x28u, N_BNSYM = 0x2Eu, N_PC = 0x30u, N_AST = 0x32u, N_OPT = 0x3Cu, N_RSYM = 0x40u, N_SLINE = 0x44u, N_ENSYM = 0x4Eu, N_SSYM = 0x60u, N_SO = 0x64u, N_OSO = 0x66u, N_LSYM = 0x80u, N_BINCL = 0x82u, N_SOL = 0x84u, N_PARAMS = 0x86u, N_VERSION = 0x88u, N_OLEVEL = 0x8Au, N_PSYM = 0xA0u, N_EINCL = 0xA2u, N_ENTRY = 0xA4u, N_LBRAC = 0xC0u, N_EXCL = 0xC2u, N_RBRAC = 0xE0u, N_BCOMM = 0xE2u, N_ECOMM = 0xE4u, N_ECOML = 0xE8u, N_LENG = 0xFEu }; enum : uint32_t { // Constant values for the r_symbolnum field in an // llvm::MachO::relocation_info structure when r_extern is 0. R_ABS = 0, // Constant bits for the r_address field in an // llvm::MachO::relocation_info structure. R_SCATTERED = 0x80000000 }; enum RelocationInfoType { // Constant values for the r_type field in an // llvm::MachO::relocation_info or llvm::MachO::scattered_relocation_info // structure. GENERIC_RELOC_VANILLA = 0, GENERIC_RELOC_PAIR = 1, GENERIC_RELOC_SECTDIFF = 2, GENERIC_RELOC_PB_LA_PTR = 3, GENERIC_RELOC_LOCAL_SECTDIFF = 4, GENERIC_RELOC_TLV = 5, // Constant values for the r_type field in a PowerPC architecture // llvm::MachO::relocation_info or llvm::MachO::scattered_relocation_info // structure. PPC_RELOC_VANILLA = GENERIC_RELOC_VANILLA, PPC_RELOC_PAIR = GENERIC_RELOC_PAIR, PPC_RELOC_BR14 = 2, PPC_RELOC_BR24 = 3, PPC_RELOC_HI16 = 4, PPC_RELOC_LO16 = 5, PPC_RELOC_HA16 = 6, PPC_RELOC_LO14 = 7, PPC_RELOC_SECTDIFF = 8, PPC_RELOC_PB_LA_PTR = 9, PPC_RELOC_HI16_SECTDIFF = 10, PPC_RELOC_LO16_SECTDIFF = 11, PPC_RELOC_HA16_SECTDIFF = 12, PPC_RELOC_JBSR = 13, PPC_RELOC_LO14_SECTDIFF = 14, PPC_RELOC_LOCAL_SECTDIFF = 15, // Constant values for the r_type field in an ARM architecture // llvm::MachO::relocation_info or llvm::MachO::scattered_relocation_info // structure. ARM_RELOC_VANILLA = GENERIC_RELOC_VANILLA, ARM_RELOC_PAIR = GENERIC_RELOC_PAIR, ARM_RELOC_SECTDIFF = GENERIC_RELOC_SECTDIFF, ARM_RELOC_LOCAL_SECTDIFF = 3, ARM_RELOC_PB_LA_PTR = 4, ARM_RELOC_BR24 = 5, ARM_THUMB_RELOC_BR22 = 6, ARM_THUMB_32BIT_BRANCH = 7, // obsolete ARM_RELOC_HALF = 8, ARM_RELOC_HALF_SECTDIFF = 9, // Constant values for the r_type field in an ARM64 architecture // llvm::MachO::relocation_info or llvm::MachO::scattered_relocation_info // structure. // For pointers. ARM64_RELOC_UNSIGNED = 0, // Must be followed by an ARM64_RELOC_UNSIGNED ARM64_RELOC_SUBTRACTOR = 1, // A B/BL instruction with 26-bit displacement. ARM64_RELOC_BRANCH26 = 2, // PC-rel distance to page of target. ARM64_RELOC_PAGE21 = 3, // Offset within page, scaled by r_length. ARM64_RELOC_PAGEOFF12 = 4, // PC-rel distance to page of GOT slot. ARM64_RELOC_GOT_LOAD_PAGE21 = 5, // Offset within page of GOT slot, scaled by r_length. ARM64_RELOC_GOT_LOAD_PAGEOFF12 = 6, // For pointers to GOT slots. ARM64_RELOC_POINTER_TO_GOT = 7, // PC-rel distance to page of TLVP slot. ARM64_RELOC_TLVP_LOAD_PAGE21 = 8, // Offset within page of TLVP slot, scaled by r_length. ARM64_RELOC_TLVP_LOAD_PAGEOFF12 = 9, // Must be followed by ARM64_RELOC_PAGE21 or ARM64_RELOC_PAGEOFF12. ARM64_RELOC_ADDEND = 10, // Constant values for the r_type field in an x86_64 architecture // llvm::MachO::relocation_info or llvm::MachO::scattered_relocation_info // structure X86_64_RELOC_UNSIGNED = 0, X86_64_RELOC_SIGNED = 1, X86_64_RELOC_BRANCH = 2, X86_64_RELOC_GOT_LOAD = 3, X86_64_RELOC_GOT = 4, X86_64_RELOC_SUBTRACTOR = 5, X86_64_RELOC_SIGNED_1 = 6, X86_64_RELOC_SIGNED_2 = 7, X86_64_RELOC_SIGNED_4 = 8, X86_64_RELOC_TLV = 9 }; // Values for segment_command.initprot. // From enum { VM_PROT_READ = 0x1, VM_PROT_WRITE = 0x2, VM_PROT_EXECUTE = 0x4 }; // Values for platform field in build_version_command. enum PlatformType { PLATFORM_MACOS = 1, PLATFORM_IOS = 2, PLATFORM_TVOS = 3, PLATFORM_WATCHOS = 4, PLATFORM_BRIDGEOS = 5, PLATFORM_MACCATALYST = 6, PLATFORM_IOSSIMULATOR = 7, PLATFORM_TVOSSIMULATOR = 8, - PLATFORM_WATCHOSSIMULATOR = 9 + PLATFORM_WATCHOSSIMULATOR = 9, + PLATFORM_DRIVERKIT = 10, }; // Values for tools enum in build_tool_version. enum { TOOL_CLANG = 1, TOOL_SWIFT = 2, TOOL_LD = 3 }; // Structs from struct mach_header { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; }; struct mach_header_64 { uint32_t magic; uint32_t cputype; uint32_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; uint32_t reserved; }; struct load_command { uint32_t cmd; uint32_t cmdsize; }; struct segment_command { uint32_t cmd; uint32_t cmdsize; char segname[16]; uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; uint32_t filesize; uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; }; struct segment_command_64 { uint32_t cmd; uint32_t cmdsize; char segname[16]; uint64_t vmaddr; uint64_t vmsize; uint64_t fileoff; uint64_t filesize; uint32_t maxprot; uint32_t initprot; uint32_t nsects; uint32_t flags; }; struct section { char sectname[16]; char segname[16]; uint32_t addr; uint32_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; }; struct section_64 { char sectname[16]; char segname[16]; uint64_t addr; uint64_t size; uint32_t offset; uint32_t align; uint32_t reloff; uint32_t nreloc; uint32_t flags; uint32_t reserved1; uint32_t reserved2; uint32_t reserved3; }; inline bool isVirtualSection(uint8_t type) { return (type == MachO::S_ZEROFILL || type == MachO::S_GB_ZEROFILL || type == MachO::S_THREAD_LOCAL_ZEROFILL); } struct fvmlib { uint32_t name; uint32_t minor_version; uint32_t header_addr; }; // The fvmlib_command is obsolete and no longer supported. struct fvmlib_command { uint32_t cmd; uint32_t cmdsize; struct fvmlib fvmlib; }; struct dylib { uint32_t name; uint32_t timestamp; uint32_t current_version; uint32_t compatibility_version; }; struct dylib_command { uint32_t cmd; uint32_t cmdsize; struct dylib dylib; }; struct sub_framework_command { uint32_t cmd; uint32_t cmdsize; uint32_t umbrella; }; struct sub_client_command { uint32_t cmd; uint32_t cmdsize; uint32_t client; }; struct sub_umbrella_command { uint32_t cmd; uint32_t cmdsize; uint32_t sub_umbrella; }; struct sub_library_command { uint32_t cmd; uint32_t cmdsize; uint32_t sub_library; }; // The prebound_dylib_command is obsolete and no longer supported. struct prebound_dylib_command { uint32_t cmd; uint32_t cmdsize; uint32_t name; uint32_t nmodules; uint32_t linked_modules; }; struct dylinker_command { uint32_t cmd; uint32_t cmdsize; uint32_t name; }; struct thread_command { uint32_t cmd; uint32_t cmdsize; }; struct routines_command { uint32_t cmd; uint32_t cmdsize; uint32_t init_address; uint32_t init_module; uint32_t reserved1; uint32_t reserved2; uint32_t reserved3; uint32_t reserved4; uint32_t reserved5; uint32_t reserved6; }; struct routines_command_64 { uint32_t cmd; uint32_t cmdsize; uint64_t init_address; uint64_t init_module; uint64_t reserved1; uint64_t reserved2; uint64_t reserved3; uint64_t reserved4; uint64_t reserved5; uint64_t reserved6; }; struct symtab_command { uint32_t cmd; uint32_t cmdsize; uint32_t symoff; uint32_t nsyms; uint32_t stroff; uint32_t strsize; }; struct dysymtab_command { uint32_t cmd; uint32_t cmdsize; uint32_t ilocalsym; uint32_t nlocalsym; uint32_t iextdefsym; uint32_t nextdefsym; uint32_t iundefsym; uint32_t nundefsym; uint32_t tocoff; uint32_t ntoc; uint32_t modtaboff; uint32_t nmodtab; uint32_t extrefsymoff; uint32_t nextrefsyms; uint32_t indirectsymoff; uint32_t nindirectsyms; uint32_t extreloff; uint32_t nextrel; uint32_t locreloff; uint32_t nlocrel; }; struct dylib_table_of_contents { uint32_t symbol_index; uint32_t module_index; }; struct dylib_module { uint32_t module_name; uint32_t iextdefsym; uint32_t nextdefsym; uint32_t irefsym; uint32_t nrefsym; uint32_t ilocalsym; uint32_t nlocalsym; uint32_t iextrel; uint32_t nextrel; uint32_t iinit_iterm; uint32_t ninit_nterm; uint32_t objc_module_info_addr; uint32_t objc_module_info_size; }; struct dylib_module_64 { uint32_t module_name; uint32_t iextdefsym; uint32_t nextdefsym; uint32_t irefsym; uint32_t nrefsym; uint32_t ilocalsym; uint32_t nlocalsym; uint32_t iextrel; uint32_t nextrel; uint32_t iinit_iterm; uint32_t ninit_nterm; uint32_t objc_module_info_size; uint64_t objc_module_info_addr; }; struct dylib_reference { uint32_t isym : 24, flags : 8; }; // The twolevel_hints_command is obsolete and no longer supported. struct twolevel_hints_command { uint32_t cmd; uint32_t cmdsize; uint32_t offset; uint32_t nhints; }; // The twolevel_hints_command is obsolete and no longer supported. struct twolevel_hint { uint32_t isub_image : 8, itoc : 24; }; // The prebind_cksum_command is obsolete and no longer supported. struct prebind_cksum_command { uint32_t cmd; uint32_t cmdsize; uint32_t cksum; }; struct uuid_command { uint32_t cmd; uint32_t cmdsize; uint8_t uuid[16]; }; struct rpath_command { uint32_t cmd; uint32_t cmdsize; uint32_t path; }; struct linkedit_data_command { uint32_t cmd; uint32_t cmdsize; uint32_t dataoff; uint32_t datasize; }; struct data_in_code_entry { uint32_t offset; uint16_t length; uint16_t kind; }; struct source_version_command { uint32_t cmd; uint32_t cmdsize; uint64_t version; }; struct encryption_info_command { uint32_t cmd; uint32_t cmdsize; uint32_t cryptoff; uint32_t cryptsize; uint32_t cryptid; }; struct encryption_info_command_64 { uint32_t cmd; uint32_t cmdsize; uint32_t cryptoff; uint32_t cryptsize; uint32_t cryptid; uint32_t pad; }; struct version_min_command { uint32_t cmd; // LC_VERSION_MIN_MACOSX or // LC_VERSION_MIN_IPHONEOS uint32_t cmdsize; // sizeof(struct version_min_command) uint32_t version; // X.Y.Z is encoded in nibbles xxxx.yy.zz uint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz }; struct note_command { uint32_t cmd; // LC_NOTE uint32_t cmdsize; // sizeof(struct note_command) char data_owner[16]; // owner name for this LC_NOTE uint64_t offset; // file offset of this data uint64_t size; // length of data region }; struct build_tool_version { uint32_t tool; // enum for the tool uint32_t version; // version of the tool }; struct build_version_command { uint32_t cmd; // LC_BUILD_VERSION uint32_t cmdsize; // sizeof(struct build_version_command) + // ntools * sizeof(struct build_tool_version) uint32_t platform; // platform uint32_t minos; // X.Y.Z is encoded in nibbles xxxx.yy.zz uint32_t sdk; // X.Y.Z is encoded in nibbles xxxx.yy.zz uint32_t ntools; // number of tool entries following this }; struct dyld_info_command { uint32_t cmd; uint32_t cmdsize; uint32_t rebase_off; uint32_t rebase_size; uint32_t bind_off; uint32_t bind_size; uint32_t weak_bind_off; uint32_t weak_bind_size; uint32_t lazy_bind_off; uint32_t lazy_bind_size; uint32_t export_off; uint32_t export_size; }; struct linker_option_command { uint32_t cmd; uint32_t cmdsize; uint32_t count; }; // The symseg_command is obsolete and no longer supported. struct symseg_command { uint32_t cmd; uint32_t cmdsize; uint32_t offset; uint32_t size; }; // The ident_command is obsolete and no longer supported. struct ident_command { uint32_t cmd; uint32_t cmdsize; }; // The fvmfile_command is obsolete and no longer supported. struct fvmfile_command { uint32_t cmd; uint32_t cmdsize; uint32_t name; uint32_t header_addr; }; struct tlv_descriptor_32 { uint32_t thunk; uint32_t key; uint32_t offset; }; struct tlv_descriptor_64 { uint64_t thunk; uint64_t key; uint64_t offset; }; struct tlv_descriptor { uintptr_t thunk; uintptr_t key; uintptr_t offset; }; struct entry_point_command { uint32_t cmd; uint32_t cmdsize; uint64_t entryoff; uint64_t stacksize; }; // Structs from struct fat_header { uint32_t magic; uint32_t nfat_arch; }; struct fat_arch { uint32_t cputype; uint32_t cpusubtype; uint32_t offset; uint32_t size; uint32_t align; }; struct fat_arch_64 { uint32_t cputype; uint32_t cpusubtype; uint64_t offset; uint64_t size; uint32_t align; uint32_t reserved; }; // Structs from struct relocation_info { int32_t r_address; uint32_t r_symbolnum : 24, r_pcrel : 1, r_length : 2, r_extern : 1, r_type : 4; }; struct scattered_relocation_info { #if defined(BYTE_ORDER) && defined(BIG_ENDIAN) && (BYTE_ORDER == BIG_ENDIAN) uint32_t r_scattered : 1, r_pcrel : 1, r_length : 2, r_type : 4, r_address : 24; #else uint32_t r_address : 24, r_type : 4, r_length : 2, r_pcrel : 1, r_scattered : 1; #endif int32_t r_value; }; // Structs NOT from , but that make LLVM's life easier struct any_relocation_info { uint32_t r_word0, r_word1; }; // Structs from struct nlist_base { uint32_t n_strx; uint8_t n_type; uint8_t n_sect; uint16_t n_desc; }; struct nlist { uint32_t n_strx; uint8_t n_type; uint8_t n_sect; int16_t n_desc; uint32_t n_value; }; struct nlist_64 { uint32_t n_strx; uint8_t n_type; uint8_t n_sect; uint16_t n_desc; uint64_t n_value; }; // Byte order swapping functions for MachO structs inline void swapStruct(fat_header &mh) { sys::swapByteOrder(mh.magic); sys::swapByteOrder(mh.nfat_arch); } inline void swapStruct(fat_arch &mh) { sys::swapByteOrder(mh.cputype); sys::swapByteOrder(mh.cpusubtype); sys::swapByteOrder(mh.offset); sys::swapByteOrder(mh.size); sys::swapByteOrder(mh.align); } inline void swapStruct(fat_arch_64 &mh) { sys::swapByteOrder(mh.cputype); sys::swapByteOrder(mh.cpusubtype); sys::swapByteOrder(mh.offset); sys::swapByteOrder(mh.size); sys::swapByteOrder(mh.align); sys::swapByteOrder(mh.reserved); } inline void swapStruct(mach_header &mh) { sys::swapByteOrder(mh.magic); sys::swapByteOrder(mh.cputype); sys::swapByteOrder(mh.cpusubtype); sys::swapByteOrder(mh.filetype); sys::swapByteOrder(mh.ncmds); sys::swapByteOrder(mh.sizeofcmds); sys::swapByteOrder(mh.flags); } inline void swapStruct(mach_header_64 &H) { sys::swapByteOrder(H.magic); sys::swapByteOrder(H.cputype); sys::swapByteOrder(H.cpusubtype); sys::swapByteOrder(H.filetype); sys::swapByteOrder(H.ncmds); sys::swapByteOrder(H.sizeofcmds); sys::swapByteOrder(H.flags); sys::swapByteOrder(H.reserved); } inline void swapStruct(load_command &lc) { sys::swapByteOrder(lc.cmd); sys::swapByteOrder(lc.cmdsize); } inline void swapStruct(symtab_command &lc) { sys::swapByteOrder(lc.cmd); sys::swapByteOrder(lc.cmdsize); sys::swapByteOrder(lc.symoff); sys::swapByteOrder(lc.nsyms); sys::swapByteOrder(lc.stroff); sys::swapByteOrder(lc.strsize); } inline void swapStruct(segment_command_64 &seg) { sys::swapByteOrder(seg.cmd); sys::swapByteOrder(seg.cmdsize); sys::swapByteOrder(seg.vmaddr); sys::swapByteOrder(seg.vmsize); sys::swapByteOrder(seg.fileoff); sys::swapByteOrder(seg.filesize); sys::swapByteOrder(seg.maxprot); sys::swapByteOrder(seg.initprot); sys::swapByteOrder(seg.nsects); sys::swapByteOrder(seg.flags); } inline void swapStruct(segment_command &seg) { sys::swapByteOrder(seg.cmd); sys::swapByteOrder(seg.cmdsize); sys::swapByteOrder(seg.vmaddr); sys::swapByteOrder(seg.vmsize); sys::swapByteOrder(seg.fileoff); sys::swapByteOrder(seg.filesize); sys::swapByteOrder(seg.maxprot); sys::swapByteOrder(seg.initprot); sys::swapByteOrder(seg.nsects); sys::swapByteOrder(seg.flags); } inline void swapStruct(section_64 §) { sys::swapByteOrder(sect.addr); sys::swapByteOrder(sect.size); sys::swapByteOrder(sect.offset); sys::swapByteOrder(sect.align); sys::swapByteOrder(sect.reloff); sys::swapByteOrder(sect.nreloc); sys::swapByteOrder(sect.flags); sys::swapByteOrder(sect.reserved1); sys::swapByteOrder(sect.reserved2); } inline void swapStruct(section §) { sys::swapByteOrder(sect.addr); sys::swapByteOrder(sect.size); sys::swapByteOrder(sect.offset); sys::swapByteOrder(sect.align); sys::swapByteOrder(sect.reloff); sys::swapByteOrder(sect.nreloc); sys::swapByteOrder(sect.flags); sys::swapByteOrder(sect.reserved1); sys::swapByteOrder(sect.reserved2); } inline void swapStruct(dyld_info_command &info) { sys::swapByteOrder(info.cmd); sys::swapByteOrder(info.cmdsize); sys::swapByteOrder(info.rebase_off); sys::swapByteOrder(info.rebase_size); sys::swapByteOrder(info.bind_off); sys::swapByteOrder(info.bind_size); sys::swapByteOrder(info.weak_bind_off); sys::swapByteOrder(info.weak_bind_size); sys::swapByteOrder(info.lazy_bind_off); sys::swapByteOrder(info.lazy_bind_size); sys::swapByteOrder(info.export_off); sys::swapByteOrder(info.export_size); } inline void swapStruct(dylib_command &d) { sys::swapByteOrder(d.cmd); sys::swapByteOrder(d.cmdsize); sys::swapByteOrder(d.dylib.name); sys::swapByteOrder(d.dylib.timestamp); sys::swapByteOrder(d.dylib.current_version); sys::swapByteOrder(d.dylib.compatibility_version); } inline void swapStruct(sub_framework_command &s) { sys::swapByteOrder(s.cmd); sys::swapByteOrder(s.cmdsize); sys::swapByteOrder(s.umbrella); } inline void swapStruct(sub_umbrella_command &s) { sys::swapByteOrder(s.cmd); sys::swapByteOrder(s.cmdsize); sys::swapByteOrder(s.sub_umbrella); } inline void swapStruct(sub_library_command &s) { sys::swapByteOrder(s.cmd); sys::swapByteOrder(s.cmdsize); sys::swapByteOrder(s.sub_library); } inline void swapStruct(sub_client_command &s) { sys::swapByteOrder(s.cmd); sys::swapByteOrder(s.cmdsize); sys::swapByteOrder(s.client); } inline void swapStruct(routines_command &r) { sys::swapByteOrder(r.cmd); sys::swapByteOrder(r.cmdsize); sys::swapByteOrder(r.init_address); sys::swapByteOrder(r.init_module); sys::swapByteOrder(r.reserved1); sys::swapByteOrder(r.reserved2); sys::swapByteOrder(r.reserved3); sys::swapByteOrder(r.reserved4); sys::swapByteOrder(r.reserved5); sys::swapByteOrder(r.reserved6); } inline void swapStruct(routines_command_64 &r) { sys::swapByteOrder(r.cmd); sys::swapByteOrder(r.cmdsize); sys::swapByteOrder(r.init_address); sys::swapByteOrder(r.init_module); sys::swapByteOrder(r.reserved1); sys::swapByteOrder(r.reserved2); sys::swapByteOrder(r.reserved3); sys::swapByteOrder(r.reserved4); sys::swapByteOrder(r.reserved5); sys::swapByteOrder(r.reserved6); } inline void swapStruct(thread_command &t) { sys::swapByteOrder(t.cmd); sys::swapByteOrder(t.cmdsize); } inline void swapStruct(dylinker_command &d) { sys::swapByteOrder(d.cmd); sys::swapByteOrder(d.cmdsize); sys::swapByteOrder(d.name); } inline void swapStruct(uuid_command &u) { sys::swapByteOrder(u.cmd); sys::swapByteOrder(u.cmdsize); } inline void swapStruct(rpath_command &r) { sys::swapByteOrder(r.cmd); sys::swapByteOrder(r.cmdsize); sys::swapByteOrder(r.path); } inline void swapStruct(source_version_command &s) { sys::swapByteOrder(s.cmd); sys::swapByteOrder(s.cmdsize); sys::swapByteOrder(s.version); } inline void swapStruct(entry_point_command &e) { sys::swapByteOrder(e.cmd); sys::swapByteOrder(e.cmdsize); sys::swapByteOrder(e.entryoff); sys::swapByteOrder(e.stacksize); } inline void swapStruct(encryption_info_command &e) { sys::swapByteOrder(e.cmd); sys::swapByteOrder(e.cmdsize); sys::swapByteOrder(e.cryptoff); sys::swapByteOrder(e.cryptsize); sys::swapByteOrder(e.cryptid); } inline void swapStruct(encryption_info_command_64 &e) { sys::swapByteOrder(e.cmd); sys::swapByteOrder(e.cmdsize); sys::swapByteOrder(e.cryptoff); sys::swapByteOrder(e.cryptsize); sys::swapByteOrder(e.cryptid); sys::swapByteOrder(e.pad); } inline void swapStruct(dysymtab_command &dst) { sys::swapByteOrder(dst.cmd); sys::swapByteOrder(dst.cmdsize); sys::swapByteOrder(dst.ilocalsym); sys::swapByteOrder(dst.nlocalsym); sys::swapByteOrder(dst.iextdefsym); sys::swapByteOrder(dst.nextdefsym); sys::swapByteOrder(dst.iundefsym); sys::swapByteOrder(dst.nundefsym); sys::swapByteOrder(dst.tocoff); sys::swapByteOrder(dst.ntoc); sys::swapByteOrder(dst.modtaboff); sys::swapByteOrder(dst.nmodtab); sys::swapByteOrder(dst.extrefsymoff); sys::swapByteOrder(dst.nextrefsyms); sys::swapByteOrder(dst.indirectsymoff); sys::swapByteOrder(dst.nindirectsyms); sys::swapByteOrder(dst.extreloff); sys::swapByteOrder(dst.nextrel); sys::swapByteOrder(dst.locreloff); sys::swapByteOrder(dst.nlocrel); } inline void swapStruct(any_relocation_info &reloc) { sys::swapByteOrder(reloc.r_word0); sys::swapByteOrder(reloc.r_word1); } inline void swapStruct(nlist_base &S) { sys::swapByteOrder(S.n_strx); sys::swapByteOrder(S.n_desc); } inline void swapStruct(nlist &sym) { sys::swapByteOrder(sym.n_strx); sys::swapByteOrder(sym.n_desc); sys::swapByteOrder(sym.n_value); } inline void swapStruct(nlist_64 &sym) { sys::swapByteOrder(sym.n_strx); sys::swapByteOrder(sym.n_desc); sys::swapByteOrder(sym.n_value); } inline void swapStruct(linkedit_data_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.dataoff); sys::swapByteOrder(C.datasize); } inline void swapStruct(linker_option_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.count); } inline void swapStruct(version_min_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.version); sys::swapByteOrder(C.sdk); } inline void swapStruct(note_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.offset); sys::swapByteOrder(C.size); } inline void swapStruct(build_version_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.platform); sys::swapByteOrder(C.minos); sys::swapByteOrder(C.sdk); sys::swapByteOrder(C.ntools); } inline void swapStruct(build_tool_version &C) { sys::swapByteOrder(C.tool); sys::swapByteOrder(C.version); } inline void swapStruct(data_in_code_entry &C) { sys::swapByteOrder(C.offset); sys::swapByteOrder(C.length); sys::swapByteOrder(C.kind); } inline void swapStruct(uint32_t &C) { sys::swapByteOrder(C); } // The prebind_cksum_command is obsolete and no longer supported. inline void swapStruct(prebind_cksum_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.cksum); } // The twolevel_hints_command is obsolete and no longer supported. inline void swapStruct(twolevel_hints_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.offset); sys::swapByteOrder(C.nhints); } // The prebound_dylib_command is obsolete and no longer supported. inline void swapStruct(prebound_dylib_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.name); sys::swapByteOrder(C.nmodules); sys::swapByteOrder(C.linked_modules); } // The fvmfile_command is obsolete and no longer supported. inline void swapStruct(fvmfile_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.name); sys::swapByteOrder(C.header_addr); } // The symseg_command is obsolete and no longer supported. inline void swapStruct(symseg_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); sys::swapByteOrder(C.offset); sys::swapByteOrder(C.size); } // The ident_command is obsolete and no longer supported. inline void swapStruct(ident_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); } inline void swapStruct(fvmlib &C) { sys::swapByteOrder(C.name); sys::swapByteOrder(C.minor_version); sys::swapByteOrder(C.header_addr); } // The fvmlib_command is obsolete and no longer supported. inline void swapStruct(fvmlib_command &C) { sys::swapByteOrder(C.cmd); sys::swapByteOrder(C.cmdsize); swapStruct(C.fvmlib); } // Get/Set functions from inline uint16_t GET_LIBRARY_ORDINAL(uint16_t n_desc) { return (((n_desc) >> 8u) & 0xffu); } inline void SET_LIBRARY_ORDINAL(uint16_t &n_desc, uint8_t ordinal) { n_desc = (((n_desc)&0x00ff) | (((ordinal)&0xff) << 8)); } inline uint8_t GET_COMM_ALIGN(uint16_t n_desc) { return (n_desc >> 8u) & 0x0fu; } inline void SET_COMM_ALIGN(uint16_t &n_desc, uint8_t align) { n_desc = ((n_desc & 0xf0ffu) | ((align & 0x0fu) << 8u)); } // Enums from enum : uint32_t { // Capability bits used in the definition of cpu_type. CPU_ARCH_MASK = 0xff000000, // Mask for architecture bits CPU_ARCH_ABI64 = 0x01000000, // 64 bit ABI CPU_ARCH_ABI64_32 = 0x02000000, // ILP32 ABI on 64-bit hardware }; // Constants for the cputype field. enum CPUType { CPU_TYPE_ANY = -1, CPU_TYPE_X86 = 7, CPU_TYPE_I386 = CPU_TYPE_X86, CPU_TYPE_X86_64 = CPU_TYPE_X86 | CPU_ARCH_ABI64, /* CPU_TYPE_MIPS = 8, */ CPU_TYPE_MC98000 = 10, // Old Motorola PowerPC CPU_TYPE_ARM = 12, CPU_TYPE_ARM64 = CPU_TYPE_ARM | CPU_ARCH_ABI64, CPU_TYPE_ARM64_32 = CPU_TYPE_ARM | CPU_ARCH_ABI64_32, CPU_TYPE_SPARC = 14, CPU_TYPE_POWERPC = 18, CPU_TYPE_POWERPC64 = CPU_TYPE_POWERPC | CPU_ARCH_ABI64 }; enum : uint32_t { // Capability bits used in the definition of cpusubtype. CPU_SUBTYPE_MASK = 0xff000000, // Mask for architecture bits CPU_SUBTYPE_LIB64 = 0x80000000, // 64 bit libraries // Special CPU subtype constants. CPU_SUBTYPE_MULTIPLE = ~0u }; // Constants for the cpusubtype field. enum CPUSubTypeX86 { CPU_SUBTYPE_I386_ALL = 3, CPU_SUBTYPE_386 = 3, CPU_SUBTYPE_486 = 4, CPU_SUBTYPE_486SX = 0x84, CPU_SUBTYPE_586 = 5, CPU_SUBTYPE_PENT = CPU_SUBTYPE_586, CPU_SUBTYPE_PENTPRO = 0x16, CPU_SUBTYPE_PENTII_M3 = 0x36, CPU_SUBTYPE_PENTII_M5 = 0x56, CPU_SUBTYPE_CELERON = 0x67, CPU_SUBTYPE_CELERON_MOBILE = 0x77, CPU_SUBTYPE_PENTIUM_3 = 0x08, CPU_SUBTYPE_PENTIUM_3_M = 0x18, CPU_SUBTYPE_PENTIUM_3_XEON = 0x28, CPU_SUBTYPE_PENTIUM_M = 0x09, CPU_SUBTYPE_PENTIUM_4 = 0x0a, CPU_SUBTYPE_PENTIUM_4_M = 0x1a, CPU_SUBTYPE_ITANIUM = 0x0b, CPU_SUBTYPE_ITANIUM_2 = 0x1b, CPU_SUBTYPE_XEON = 0x0c, CPU_SUBTYPE_XEON_MP = 0x1c, CPU_SUBTYPE_X86_ALL = 3, CPU_SUBTYPE_X86_64_ALL = 3, CPU_SUBTYPE_X86_ARCH1 = 4, CPU_SUBTYPE_X86_64_H = 8 }; inline int CPU_SUBTYPE_INTEL(int Family, int Model) { return Family | (Model << 4); } inline int CPU_SUBTYPE_INTEL_FAMILY(CPUSubTypeX86 ST) { return ((int)ST) & 0x0f; } inline int CPU_SUBTYPE_INTEL_MODEL(CPUSubTypeX86 ST) { return ((int)ST) >> 4; } enum { CPU_SUBTYPE_INTEL_FAMILY_MAX = 15, CPU_SUBTYPE_INTEL_MODEL_ALL = 0 }; enum CPUSubTypeARM { CPU_SUBTYPE_ARM_ALL = 0, CPU_SUBTYPE_ARM_V4T = 5, CPU_SUBTYPE_ARM_V6 = 6, CPU_SUBTYPE_ARM_V5 = 7, CPU_SUBTYPE_ARM_V5TEJ = 7, CPU_SUBTYPE_ARM_XSCALE = 8, CPU_SUBTYPE_ARM_V7 = 9, // unused ARM_V7F = 10, CPU_SUBTYPE_ARM_V7S = 11, CPU_SUBTYPE_ARM_V7K = 12, CPU_SUBTYPE_ARM_V6M = 14, CPU_SUBTYPE_ARM_V7M = 15, CPU_SUBTYPE_ARM_V7EM = 16 }; enum CPUSubTypeARM64 { CPU_SUBTYPE_ARM64_ALL = 0, CPU_SUBTYPE_ARM64_V8 = 1, CPU_SUBTYPE_ARM64E = 2, }; enum CPUSubTypeARM64_32 { CPU_SUBTYPE_ARM64_32_V8 = 1 }; enum CPUSubTypeSPARC { CPU_SUBTYPE_SPARC_ALL = 0 }; enum CPUSubTypePowerPC { CPU_SUBTYPE_POWERPC_ALL = 0, CPU_SUBTYPE_POWERPC_601 = 1, CPU_SUBTYPE_POWERPC_602 = 2, CPU_SUBTYPE_POWERPC_603 = 3, CPU_SUBTYPE_POWERPC_603e = 4, CPU_SUBTYPE_POWERPC_603ev = 5, CPU_SUBTYPE_POWERPC_604 = 6, CPU_SUBTYPE_POWERPC_604e = 7, CPU_SUBTYPE_POWERPC_620 = 8, CPU_SUBTYPE_POWERPC_750 = 9, CPU_SUBTYPE_POWERPC_7400 = 10, CPU_SUBTYPE_POWERPC_7450 = 11, CPU_SUBTYPE_POWERPC_970 = 100, CPU_SUBTYPE_MC980000_ALL = CPU_SUBTYPE_POWERPC_ALL, CPU_SUBTYPE_MC98601 = CPU_SUBTYPE_POWERPC_601 }; Expected getCPUType(const Triple &T); Expected getCPUSubType(const Triple &T); struct x86_thread_state32_t { uint32_t eax; uint32_t ebx; uint32_t ecx; uint32_t edx; uint32_t edi; uint32_t esi; uint32_t ebp; uint32_t esp; uint32_t ss; uint32_t eflags; uint32_t eip; uint32_t cs; uint32_t ds; uint32_t es; uint32_t fs; uint32_t gs; }; struct x86_thread_state64_t { uint64_t rax; uint64_t rbx; uint64_t rcx; uint64_t rdx; uint64_t rdi; uint64_t rsi; uint64_t rbp; uint64_t rsp; uint64_t r8; uint64_t r9; uint64_t r10; uint64_t r11; uint64_t r12; uint64_t r13; uint64_t r14; uint64_t r15; uint64_t rip; uint64_t rflags; uint64_t cs; uint64_t fs; uint64_t gs; }; enum x86_fp_control_precis { x86_FP_PREC_24B = 0, x86_FP_PREC_53B = 2, x86_FP_PREC_64B = 3 }; enum x86_fp_control_rc { x86_FP_RND_NEAR = 0, x86_FP_RND_DOWN = 1, x86_FP_RND_UP = 2, x86_FP_CHOP = 3 }; struct fp_control_t { unsigned short invalid : 1, denorm : 1, zdiv : 1, ovrfl : 1, undfl : 1, precis : 1, : 2, pc : 2, rc : 2, : 1, : 3; }; struct fp_status_t { unsigned short invalid : 1, denorm : 1, zdiv : 1, ovrfl : 1, undfl : 1, precis : 1, stkflt : 1, errsumm : 1, c0 : 1, c1 : 1, c2 : 1, tos : 3, c3 : 1, busy : 1; }; struct mmst_reg_t { char mmst_reg[10]; char mmst_rsrv[6]; }; struct xmm_reg_t { char xmm_reg[16]; }; struct x86_float_state64_t { int32_t fpu_reserved[2]; fp_control_t fpu_fcw; fp_status_t fpu_fsw; uint8_t fpu_ftw; uint8_t fpu_rsrv1; uint16_t fpu_fop; uint32_t fpu_ip; uint16_t fpu_cs; uint16_t fpu_rsrv2; uint32_t fpu_dp; uint16_t fpu_ds; uint16_t fpu_rsrv3; uint32_t fpu_mxcsr; uint32_t fpu_mxcsrmask; mmst_reg_t fpu_stmm0; mmst_reg_t fpu_stmm1; mmst_reg_t fpu_stmm2; mmst_reg_t fpu_stmm3; mmst_reg_t fpu_stmm4; mmst_reg_t fpu_stmm5; mmst_reg_t fpu_stmm6; mmst_reg_t fpu_stmm7; xmm_reg_t fpu_xmm0; xmm_reg_t fpu_xmm1; xmm_reg_t fpu_xmm2; xmm_reg_t fpu_xmm3; xmm_reg_t fpu_xmm4; xmm_reg_t fpu_xmm5; xmm_reg_t fpu_xmm6; xmm_reg_t fpu_xmm7; xmm_reg_t fpu_xmm8; xmm_reg_t fpu_xmm9; xmm_reg_t fpu_xmm10; xmm_reg_t fpu_xmm11; xmm_reg_t fpu_xmm12; xmm_reg_t fpu_xmm13; xmm_reg_t fpu_xmm14; xmm_reg_t fpu_xmm15; char fpu_rsrv4[6 * 16]; uint32_t fpu_reserved1; }; struct x86_exception_state64_t { uint16_t trapno; uint16_t cpu; uint32_t err; uint64_t faultvaddr; }; inline void swapStruct(x86_thread_state32_t &x) { sys::swapByteOrder(x.eax); sys::swapByteOrder(x.ebx); sys::swapByteOrder(x.ecx); sys::swapByteOrder(x.edx); sys::swapByteOrder(x.edi); sys::swapByteOrder(x.esi); sys::swapByteOrder(x.ebp); sys::swapByteOrder(x.esp); sys::swapByteOrder(x.ss); sys::swapByteOrder(x.eflags); sys::swapByteOrder(x.eip); sys::swapByteOrder(x.cs); sys::swapByteOrder(x.ds); sys::swapByteOrder(x.es); sys::swapByteOrder(x.fs); sys::swapByteOrder(x.gs); } inline void swapStruct(x86_thread_state64_t &x) { sys::swapByteOrder(x.rax); sys::swapByteOrder(x.rbx); sys::swapByteOrder(x.rcx); sys::swapByteOrder(x.rdx); sys::swapByteOrder(x.rdi); sys::swapByteOrder(x.rsi); sys::swapByteOrder(x.rbp); sys::swapByteOrder(x.rsp); sys::swapByteOrder(x.r8); sys::swapByteOrder(x.r9); sys::swapByteOrder(x.r10); sys::swapByteOrder(x.r11); sys::swapByteOrder(x.r12); sys::swapByteOrder(x.r13); sys::swapByteOrder(x.r14); sys::swapByteOrder(x.r15); sys::swapByteOrder(x.rip); sys::swapByteOrder(x.rflags); sys::swapByteOrder(x.cs); sys::swapByteOrder(x.fs); sys::swapByteOrder(x.gs); } inline void swapStruct(x86_float_state64_t &x) { sys::swapByteOrder(x.fpu_reserved[0]); sys::swapByteOrder(x.fpu_reserved[1]); // TODO swap: fp_control_t fpu_fcw; // TODO swap: fp_status_t fpu_fsw; sys::swapByteOrder(x.fpu_fop); sys::swapByteOrder(x.fpu_ip); sys::swapByteOrder(x.fpu_cs); sys::swapByteOrder(x.fpu_rsrv2); sys::swapByteOrder(x.fpu_dp); sys::swapByteOrder(x.fpu_ds); sys::swapByteOrder(x.fpu_rsrv3); sys::swapByteOrder(x.fpu_mxcsr); sys::swapByteOrder(x.fpu_mxcsrmask); sys::swapByteOrder(x.fpu_reserved1); } inline void swapStruct(x86_exception_state64_t &x) { sys::swapByteOrder(x.trapno); sys::swapByteOrder(x.cpu); sys::swapByteOrder(x.err); sys::swapByteOrder(x.faultvaddr); } struct x86_state_hdr_t { uint32_t flavor; uint32_t count; }; struct x86_thread_state_t { x86_state_hdr_t tsh; union { x86_thread_state64_t ts64; x86_thread_state32_t ts32; } uts; }; struct x86_float_state_t { x86_state_hdr_t fsh; union { x86_float_state64_t fs64; } ufs; }; struct x86_exception_state_t { x86_state_hdr_t esh; union { x86_exception_state64_t es64; } ues; }; inline void swapStruct(x86_state_hdr_t &x) { sys::swapByteOrder(x.flavor); sys::swapByteOrder(x.count); } enum X86ThreadFlavors { x86_THREAD_STATE32 = 1, x86_FLOAT_STATE32 = 2, x86_EXCEPTION_STATE32 = 3, x86_THREAD_STATE64 = 4, x86_FLOAT_STATE64 = 5, x86_EXCEPTION_STATE64 = 6, x86_THREAD_STATE = 7, x86_FLOAT_STATE = 8, x86_EXCEPTION_STATE = 9, x86_DEBUG_STATE32 = 10, x86_DEBUG_STATE64 = 11, x86_DEBUG_STATE = 12 }; inline void swapStruct(x86_thread_state_t &x) { swapStruct(x.tsh); if (x.tsh.flavor == x86_THREAD_STATE64) swapStruct(x.uts.ts64); } inline void swapStruct(x86_float_state_t &x) { swapStruct(x.fsh); if (x.fsh.flavor == x86_FLOAT_STATE64) swapStruct(x.ufs.fs64); } inline void swapStruct(x86_exception_state_t &x) { swapStruct(x.esh); if (x.esh.flavor == x86_EXCEPTION_STATE64) swapStruct(x.ues.es64); } const uint32_t x86_THREAD_STATE32_COUNT = sizeof(x86_thread_state32_t) / sizeof(uint32_t); const uint32_t x86_THREAD_STATE64_COUNT = sizeof(x86_thread_state64_t) / sizeof(uint32_t); const uint32_t x86_FLOAT_STATE64_COUNT = sizeof(x86_float_state64_t) / sizeof(uint32_t); const uint32_t x86_EXCEPTION_STATE64_COUNT = sizeof(x86_exception_state64_t) / sizeof(uint32_t); const uint32_t x86_THREAD_STATE_COUNT = sizeof(x86_thread_state_t) / sizeof(uint32_t); const uint32_t x86_FLOAT_STATE_COUNT = sizeof(x86_float_state_t) / sizeof(uint32_t); const uint32_t x86_EXCEPTION_STATE_COUNT = sizeof(x86_exception_state_t) / sizeof(uint32_t); struct arm_thread_state32_t { uint32_t r[13]; uint32_t sp; uint32_t lr; uint32_t pc; uint32_t cpsr; }; inline void swapStruct(arm_thread_state32_t &x) { for (int i = 0; i < 13; i++) sys::swapByteOrder(x.r[i]); sys::swapByteOrder(x.sp); sys::swapByteOrder(x.lr); sys::swapByteOrder(x.pc); sys::swapByteOrder(x.cpsr); } struct arm_thread_state64_t { uint64_t x[29]; uint64_t fp; uint64_t lr; uint64_t sp; uint64_t pc; uint32_t cpsr; uint32_t pad; }; inline void swapStruct(arm_thread_state64_t &x) { for (int i = 0; i < 29; i++) sys::swapByteOrder(x.x[i]); sys::swapByteOrder(x.fp); sys::swapByteOrder(x.lr); sys::swapByteOrder(x.sp); sys::swapByteOrder(x.pc); sys::swapByteOrder(x.cpsr); } struct arm_state_hdr_t { uint32_t flavor; uint32_t count; }; struct arm_thread_state_t { arm_state_hdr_t tsh; union { arm_thread_state32_t ts32; } uts; }; inline void swapStruct(arm_state_hdr_t &x) { sys::swapByteOrder(x.flavor); sys::swapByteOrder(x.count); } enum ARMThreadFlavors { ARM_THREAD_STATE = 1, ARM_VFP_STATE = 2, ARM_EXCEPTION_STATE = 3, ARM_DEBUG_STATE = 4, ARN_THREAD_STATE_NONE = 5, ARM_THREAD_STATE64 = 6, ARM_EXCEPTION_STATE64 = 7 }; inline void swapStruct(arm_thread_state_t &x) { swapStruct(x.tsh); if (x.tsh.flavor == ARM_THREAD_STATE) swapStruct(x.uts.ts32); } const uint32_t ARM_THREAD_STATE_COUNT = sizeof(arm_thread_state32_t) / sizeof(uint32_t); const uint32_t ARM_THREAD_STATE64_COUNT = sizeof(arm_thread_state64_t) / sizeof(uint32_t); struct ppc_thread_state32_t { uint32_t srr0; uint32_t srr1; uint32_t r0; uint32_t r1; uint32_t r2; uint32_t r3; uint32_t r4; uint32_t r5; uint32_t r6; uint32_t r7; uint32_t r8; uint32_t r9; uint32_t r10; uint32_t r11; uint32_t r12; uint32_t r13; uint32_t r14; uint32_t r15; uint32_t r16; uint32_t r17; uint32_t r18; uint32_t r19; uint32_t r20; uint32_t r21; uint32_t r22; uint32_t r23; uint32_t r24; uint32_t r25; uint32_t r26; uint32_t r27; uint32_t r28; uint32_t r29; uint32_t r30; uint32_t r31; uint32_t ct; uint32_t xer; uint32_t lr; uint32_t ctr; uint32_t mq; uint32_t vrsave; }; inline void swapStruct(ppc_thread_state32_t &x) { sys::swapByteOrder(x.srr0); sys::swapByteOrder(x.srr1); sys::swapByteOrder(x.r0); sys::swapByteOrder(x.r1); sys::swapByteOrder(x.r2); sys::swapByteOrder(x.r3); sys::swapByteOrder(x.r4); sys::swapByteOrder(x.r5); sys::swapByteOrder(x.r6); sys::swapByteOrder(x.r7); sys::swapByteOrder(x.r8); sys::swapByteOrder(x.r9); sys::swapByteOrder(x.r10); sys::swapByteOrder(x.r11); sys::swapByteOrder(x.r12); sys::swapByteOrder(x.r13); sys::swapByteOrder(x.r14); sys::swapByteOrder(x.r15); sys::swapByteOrder(x.r16); sys::swapByteOrder(x.r17); sys::swapByteOrder(x.r18); sys::swapByteOrder(x.r19); sys::swapByteOrder(x.r20); sys::swapByteOrder(x.r21); sys::swapByteOrder(x.r22); sys::swapByteOrder(x.r23); sys::swapByteOrder(x.r24); sys::swapByteOrder(x.r25); sys::swapByteOrder(x.r26); sys::swapByteOrder(x.r27); sys::swapByteOrder(x.r28); sys::swapByteOrder(x.r29); sys::swapByteOrder(x.r30); sys::swapByteOrder(x.r31); sys::swapByteOrder(x.ct); sys::swapByteOrder(x.xer); sys::swapByteOrder(x.lr); sys::swapByteOrder(x.ctr); sys::swapByteOrder(x.mq); sys::swapByteOrder(x.vrsave); } struct ppc_state_hdr_t { uint32_t flavor; uint32_t count; }; struct ppc_thread_state_t { ppc_state_hdr_t tsh; union { ppc_thread_state32_t ts32; } uts; }; inline void swapStruct(ppc_state_hdr_t &x) { sys::swapByteOrder(x.flavor); sys::swapByteOrder(x.count); } enum PPCThreadFlavors { PPC_THREAD_STATE = 1, PPC_FLOAT_STATE = 2, PPC_EXCEPTION_STATE = 3, PPC_VECTOR_STATE = 4, PPC_THREAD_STATE64 = 5, PPC_EXCEPTION_STATE64 = 6, PPC_THREAD_STATE_NONE = 7 }; inline void swapStruct(ppc_thread_state_t &x) { swapStruct(x.tsh); if (x.tsh.flavor == PPC_THREAD_STATE) swapStruct(x.uts.ts32); } const uint32_t PPC_THREAD_STATE_COUNT = sizeof(ppc_thread_state32_t) / sizeof(uint32_t); // Define a union of all load command structs #define LOAD_COMMAND_STRUCT(LCStruct) LCStruct LCStruct##_data; LLVM_PACKED_START union alignas(4) macho_load_command { #include "llvm/BinaryFormat/MachO.def" }; LLVM_PACKED_END } // end namespace MachO } // end namespace llvm #endif diff --git a/llvm/include/llvm/Object/MachO.h b/llvm/include/llvm/Object/MachO.h index f48e0f1dcd58..7eb017397846 100644 --- a/llvm/include/llvm/Object/MachO.h +++ b/llvm/include/llvm/Object/MachO.h @@ -1,738 +1,739 @@ //===- MachO.h - MachO object file implementation ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // This file declares the MachOObjectFile class, which implement the ObjectFile // interface for MachO files. // //===----------------------------------------------------------------------===// #ifndef LLVM_OBJECT_MACHO_H #define LLVM_OBJECT_MACHO_H #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/iterator_range.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/MC/SubtargetFeature.h" #include "llvm/Object/Binary.h" #include "llvm/Object/ObjectFile.h" #include "llvm/Object/SymbolicFile.h" #include "llvm/Support/Error.h" #include "llvm/Support/Format.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include namespace llvm { namespace object { /// DiceRef - This is a value type class that represents a single /// data in code entry in the table in a Mach-O object file. class DiceRef { DataRefImpl DicePimpl; const ObjectFile *OwningObject = nullptr; public: DiceRef() = default; DiceRef(DataRefImpl DiceP, const ObjectFile *Owner); bool operator==(const DiceRef &Other) const; bool operator<(const DiceRef &Other) const; void moveNext(); std::error_code getOffset(uint32_t &Result) const; std::error_code getLength(uint16_t &Result) const; std::error_code getKind(uint16_t &Result) const; DataRefImpl getRawDataRefImpl() const; const ObjectFile *getObjectFile() const; }; using dice_iterator = content_iterator; /// ExportEntry encapsulates the current-state-of-the-walk used when doing a /// non-recursive walk of the trie data structure. This allows you to iterate /// across all exported symbols using: /// Error Err = Error::success(); /// for (const llvm::object::ExportEntry &AnExport : Obj->exports(&Err)) { /// } /// if (Err) { report error ... class ExportEntry { public: ExportEntry(Error *Err, const MachOObjectFile *O, ArrayRef Trie); StringRef name() const; uint64_t flags() const; uint64_t address() const; uint64_t other() const; StringRef otherName() const; uint32_t nodeOffset() const; bool operator==(const ExportEntry &) const; void moveNext(); private: friend class MachOObjectFile; void moveToFirst(); void moveToEnd(); uint64_t readULEB128(const uint8_t *&p, const char **error); void pushDownUntilBottom(); void pushNode(uint64_t Offset); // Represents a node in the mach-o exports trie. struct NodeState { NodeState(const uint8_t *Ptr); const uint8_t *Start; const uint8_t *Current; uint64_t Flags = 0; uint64_t Address = 0; uint64_t Other = 0; const char *ImportName = nullptr; unsigned ChildCount = 0; unsigned NextChildIndex = 0; unsigned ParentStringLength = 0; bool IsExportNode = false; }; using NodeList = SmallVector; using node_iterator = NodeList::const_iterator; Error *E; const MachOObjectFile *O; ArrayRef Trie; SmallString<256> CumulativeString; NodeList Stack; bool Done = false; iterator_range nodes() const { return make_range(Stack.begin(), Stack.end()); } }; using export_iterator = content_iterator; // Segment info so SegIndex/SegOffset pairs in a Mach-O Bind or Rebase entry // can be checked and translated. Only the SegIndex/SegOffset pairs from // checked entries are to be used with the segmentName(), sectionName() and // address() methods below. class BindRebaseSegInfo { public: BindRebaseSegInfo(const MachOObjectFile *Obj); // Used to check a Mach-O Bind or Rebase entry for errors when iterating. const char* checkSegAndOffsets(int32_t SegIndex, uint64_t SegOffset, uint8_t PointerSize, uint32_t Count=1, uint32_t Skip=0); // Used with valid SegIndex/SegOffset values from checked entries. StringRef segmentName(int32_t SegIndex); StringRef sectionName(int32_t SegIndex, uint64_t SegOffset); uint64_t address(uint32_t SegIndex, uint64_t SegOffset); private: struct SectionInfo { uint64_t Address; uint64_t Size; StringRef SectionName; StringRef SegmentName; uint64_t OffsetInSegment; uint64_t SegmentStartAddress; int32_t SegmentIndex; }; const SectionInfo &findSection(int32_t SegIndex, uint64_t SegOffset); SmallVector Sections; int32_t MaxSegIndex; }; /// MachORebaseEntry encapsulates the current state in the decompression of /// rebasing opcodes. This allows you to iterate through the compressed table of /// rebasing using: /// Error Err = Error::success(); /// for (const llvm::object::MachORebaseEntry &Entry : Obj->rebaseTable(&Err)) { /// } /// if (Err) { report error ... class MachORebaseEntry { public: MachORebaseEntry(Error *Err, const MachOObjectFile *O, ArrayRef opcodes, bool is64Bit); int32_t segmentIndex() const; uint64_t segmentOffset() const; StringRef typeName() const; StringRef segmentName() const; StringRef sectionName() const; uint64_t address() const; bool operator==(const MachORebaseEntry &) const; void moveNext(); private: friend class MachOObjectFile; void moveToFirst(); void moveToEnd(); uint64_t readULEB128(const char **error); Error *E; const MachOObjectFile *O; ArrayRef Opcodes; const uint8_t *Ptr; uint64_t SegmentOffset = 0; int32_t SegmentIndex = -1; uint64_t RemainingLoopCount = 0; uint64_t AdvanceAmount = 0; uint8_t RebaseType = 0; uint8_t PointerSize; bool Done = false; }; using rebase_iterator = content_iterator; /// MachOBindEntry encapsulates the current state in the decompression of /// binding opcodes. This allows you to iterate through the compressed table of /// bindings using: /// Error Err = Error::success(); /// for (const llvm::object::MachOBindEntry &Entry : Obj->bindTable(&Err)) { /// } /// if (Err) { report error ... class MachOBindEntry { public: enum class Kind { Regular, Lazy, Weak }; MachOBindEntry(Error *Err, const MachOObjectFile *O, ArrayRef Opcodes, bool is64Bit, MachOBindEntry::Kind); int32_t segmentIndex() const; uint64_t segmentOffset() const; StringRef typeName() const; StringRef symbolName() const; uint32_t flags() const; int64_t addend() const; int ordinal() const; StringRef segmentName() const; StringRef sectionName() const; uint64_t address() const; bool operator==(const MachOBindEntry &) const; void moveNext(); private: friend class MachOObjectFile; void moveToFirst(); void moveToEnd(); uint64_t readULEB128(const char **error); int64_t readSLEB128(const char **error); Error *E; const MachOObjectFile *O; ArrayRef Opcodes; const uint8_t *Ptr; uint64_t SegmentOffset = 0; int32_t SegmentIndex = -1; StringRef SymbolName; bool LibraryOrdinalSet = false; int Ordinal = 0; uint32_t Flags = 0; int64_t Addend = 0; uint64_t RemainingLoopCount = 0; uint64_t AdvanceAmount = 0; uint8_t BindType = 0; uint8_t PointerSize; Kind TableKind; bool Done = false; }; using bind_iterator = content_iterator; class MachOObjectFile : public ObjectFile { public: struct LoadCommandInfo { const char *Ptr; // Where in memory the load command is. MachO::load_command C; // The command itself. }; using LoadCommandList = SmallVector; using load_command_iterator = LoadCommandList::const_iterator; static Expected> create(MemoryBufferRef Object, bool IsLittleEndian, bool Is64Bits, uint32_t UniversalCputype = 0, uint32_t UniversalIndex = 0); void moveSymbolNext(DataRefImpl &Symb) const override; uint64_t getNValue(DataRefImpl Sym) const; Expected getSymbolName(DataRefImpl Symb) const override; // MachO specific. Error checkSymbolTable() const; std::error_code getIndirectName(DataRefImpl Symb, StringRef &Res) const; unsigned getSectionType(SectionRef Sec) const; Expected getSymbolAddress(DataRefImpl Symb) const override; uint32_t getSymbolAlignment(DataRefImpl Symb) const override; uint64_t getCommonSymbolSizeImpl(DataRefImpl Symb) const override; Expected getSymbolType(DataRefImpl Symb) const override; Expected getSymbolFlags(DataRefImpl Symb) const override; Expected getSymbolSection(DataRefImpl Symb) const override; unsigned getSymbolSectionID(SymbolRef Symb) const; unsigned getSectionID(SectionRef Sec) const; void moveSectionNext(DataRefImpl &Sec) const override; Expected getSectionName(DataRefImpl Sec) const override; uint64_t getSectionAddress(DataRefImpl Sec) const override; uint64_t getSectionIndex(DataRefImpl Sec) const override; uint64_t getSectionSize(DataRefImpl Sec) const override; ArrayRef getSectionContents(uint32_t Offset, uint64_t Size) const; Expected> getSectionContents(DataRefImpl Sec) const override; uint64_t getSectionAlignment(DataRefImpl Sec) const override; Expected getSection(unsigned SectionIndex) const; Expected getSection(StringRef SectionName) const; bool isSectionCompressed(DataRefImpl Sec) const override; bool isSectionText(DataRefImpl Sec) const override; bool isSectionData(DataRefImpl Sec) const override; bool isSectionBSS(DataRefImpl Sec) const override; bool isSectionVirtual(DataRefImpl Sec) const override; bool isSectionBitcode(DataRefImpl Sec) const override; bool isDebugSection(StringRef SectionName) const override; /// When dsymutil generates the companion file, it strips all unnecessary /// sections (e.g. everything in the _TEXT segment) by omitting their body /// and setting the offset in their corresponding load command to zero. /// /// While the load command itself is valid, reading the section corresponds /// to reading the number of bytes specified in the load command, starting /// from offset 0 (i.e. the Mach-O header at the beginning of the file). bool isSectionStripped(DataRefImpl Sec) const override; relocation_iterator section_rel_begin(DataRefImpl Sec) const override; relocation_iterator section_rel_end(DataRefImpl Sec) const override; relocation_iterator extrel_begin() const; relocation_iterator extrel_end() const; iterator_range external_relocations() const { return make_range(extrel_begin(), extrel_end()); } relocation_iterator locrel_begin() const; relocation_iterator locrel_end() const; void moveRelocationNext(DataRefImpl &Rel) const override; uint64_t getRelocationOffset(DataRefImpl Rel) const override; symbol_iterator getRelocationSymbol(DataRefImpl Rel) const override; section_iterator getRelocationSection(DataRefImpl Rel) const; uint64_t getRelocationType(DataRefImpl Rel) const override; void getRelocationTypeName(DataRefImpl Rel, SmallVectorImpl &Result) const override; uint8_t getRelocationLength(DataRefImpl Rel) const; // MachO specific. std::error_code getLibraryShortNameByIndex(unsigned Index, StringRef &) const; uint32_t getLibraryCount() const; section_iterator getRelocationRelocatedSection(relocation_iterator Rel) const; // TODO: Would be useful to have an iterator based version // of the load command interface too. basic_symbol_iterator symbol_begin() const override; basic_symbol_iterator symbol_end() const override; // MachO specific. symbol_iterator getSymbolByIndex(unsigned Index) const; uint64_t getSymbolIndex(DataRefImpl Symb) const; section_iterator section_begin() const override; section_iterator section_end() const override; uint8_t getBytesInAddress() const override; StringRef getFileFormatName() const override; Triple::ArchType getArch() const override; SubtargetFeatures getFeatures() const override { return SubtargetFeatures(); } Triple getArchTriple(const char **McpuDefault = nullptr) const; relocation_iterator section_rel_begin(unsigned Index) const; relocation_iterator section_rel_end(unsigned Index) const; dice_iterator begin_dices() const; dice_iterator end_dices() const; load_command_iterator begin_load_commands() const; load_command_iterator end_load_commands() const; iterator_range load_commands() const; /// For use iterating over all exported symbols. iterator_range exports(Error &Err) const; /// For use examining a trie not in a MachOObjectFile. static iterator_range exports(Error &Err, ArrayRef Trie, const MachOObjectFile *O = nullptr); /// For use iterating over all rebase table entries. iterator_range rebaseTable(Error &Err); /// For use examining rebase opcodes in a MachOObjectFile. static iterator_range rebaseTable(Error &Err, MachOObjectFile *O, ArrayRef Opcodes, bool is64); /// For use iterating over all bind table entries. iterator_range bindTable(Error &Err); /// For use iterating over all lazy bind table entries. iterator_range lazyBindTable(Error &Err); /// For use iterating over all weak bind table entries. iterator_range weakBindTable(Error &Err); /// For use examining bind opcodes in a MachOObjectFile. static iterator_range bindTable(Error &Err, MachOObjectFile *O, ArrayRef Opcodes, bool is64, MachOBindEntry::Kind); // Given a SegIndex, SegOffset, and PointerSize, verify a valid section exists // that fully contains a pointer at that location. Multiple fixups in a bind // (such as with the BIND_OPCODE_DO_BIND_ULEB_TIMES_SKIPPING_ULEB opcode) can // be tested via the Count and Skip parameters. // // This is used by MachOBindEntry::moveNext() to validate a MachOBindEntry. const char *BindEntryCheckSegAndOffsets(int32_t SegIndex, uint64_t SegOffset, uint8_t PointerSize, uint32_t Count=1, uint32_t Skip=0) const { return BindRebaseSectionTable->checkSegAndOffsets(SegIndex, SegOffset, PointerSize, Count, Skip); } // Given a SegIndex, SegOffset, and PointerSize, verify a valid section exists // that fully contains a pointer at that location. Multiple fixups in a rebase // (such as with the REBASE_OPCODE_DO_*_TIMES* opcodes) can be tested via the // Count and Skip parameters. // // This is used by MachORebaseEntry::moveNext() to validate a MachORebaseEntry const char *RebaseEntryCheckSegAndOffsets(int32_t SegIndex, uint64_t SegOffset, uint8_t PointerSize, uint32_t Count=1, uint32_t Skip=0) const { return BindRebaseSectionTable->checkSegAndOffsets(SegIndex, SegOffset, PointerSize, Count, Skip); } /// For use with the SegIndex of a checked Mach-O Bind or Rebase entry to /// get the segment name. StringRef BindRebaseSegmentName(int32_t SegIndex) const { return BindRebaseSectionTable->segmentName(SegIndex); } /// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or /// Rebase entry to get the section name. StringRef BindRebaseSectionName(uint32_t SegIndex, uint64_t SegOffset) const { return BindRebaseSectionTable->sectionName(SegIndex, SegOffset); } /// For use with a SegIndex,SegOffset pair from a checked Mach-O Bind or /// Rebase entry to get the address. uint64_t BindRebaseAddress(uint32_t SegIndex, uint64_t SegOffset) const { return BindRebaseSectionTable->address(SegIndex, SegOffset); } // In a MachO file, sections have a segment name. This is used in the .o // files. They have a single segment, but this field specifies which segment // a section should be put in the final object. StringRef getSectionFinalSegmentName(DataRefImpl Sec) const; // Names are stored as 16 bytes. These returns the raw 16 bytes without // interpreting them as a C string. ArrayRef getSectionRawName(DataRefImpl Sec) const; ArrayRef getSectionRawFinalSegmentName(DataRefImpl Sec) const; // MachO specific Info about relocations. bool isRelocationScattered(const MachO::any_relocation_info &RE) const; unsigned getPlainRelocationSymbolNum( const MachO::any_relocation_info &RE) const; bool getPlainRelocationExternal(const MachO::any_relocation_info &RE) const; bool getScatteredRelocationScattered( const MachO::any_relocation_info &RE) const; uint32_t getScatteredRelocationValue( const MachO::any_relocation_info &RE) const; uint32_t getScatteredRelocationType( const MachO::any_relocation_info &RE) const; unsigned getAnyRelocationAddress(const MachO::any_relocation_info &RE) const; unsigned getAnyRelocationPCRel(const MachO::any_relocation_info &RE) const; unsigned getAnyRelocationLength(const MachO::any_relocation_info &RE) const; unsigned getAnyRelocationType(const MachO::any_relocation_info &RE) const; SectionRef getAnyRelocationSection(const MachO::any_relocation_info &RE) const; // MachO specific structures. MachO::section getSection(DataRefImpl DRI) const; MachO::section_64 getSection64(DataRefImpl DRI) const; MachO::section getSection(const LoadCommandInfo &L, unsigned Index) const; MachO::section_64 getSection64(const LoadCommandInfo &L,unsigned Index) const; MachO::nlist getSymbolTableEntry(DataRefImpl DRI) const; MachO::nlist_64 getSymbol64TableEntry(DataRefImpl DRI) const; MachO::linkedit_data_command getLinkeditDataLoadCommand(const LoadCommandInfo &L) const; MachO::segment_command getSegmentLoadCommand(const LoadCommandInfo &L) const; MachO::segment_command_64 getSegment64LoadCommand(const LoadCommandInfo &L) const; MachO::linker_option_command getLinkerOptionLoadCommand(const LoadCommandInfo &L) const; MachO::version_min_command getVersionMinLoadCommand(const LoadCommandInfo &L) const; MachO::note_command getNoteLoadCommand(const LoadCommandInfo &L) const; MachO::build_version_command getBuildVersionLoadCommand(const LoadCommandInfo &L) const; MachO::build_tool_version getBuildToolVersion(unsigned index) const; MachO::dylib_command getDylibIDLoadCommand(const LoadCommandInfo &L) const; MachO::dyld_info_command getDyldInfoLoadCommand(const LoadCommandInfo &L) const; MachO::dylinker_command getDylinkerCommand(const LoadCommandInfo &L) const; MachO::uuid_command getUuidCommand(const LoadCommandInfo &L) const; MachO::rpath_command getRpathCommand(const LoadCommandInfo &L) const; MachO::source_version_command getSourceVersionCommand(const LoadCommandInfo &L) const; MachO::entry_point_command getEntryPointCommand(const LoadCommandInfo &L) const; MachO::encryption_info_command getEncryptionInfoCommand(const LoadCommandInfo &L) const; MachO::encryption_info_command_64 getEncryptionInfoCommand64(const LoadCommandInfo &L) const; MachO::sub_framework_command getSubFrameworkCommand(const LoadCommandInfo &L) const; MachO::sub_umbrella_command getSubUmbrellaCommand(const LoadCommandInfo &L) const; MachO::sub_library_command getSubLibraryCommand(const LoadCommandInfo &L) const; MachO::sub_client_command getSubClientCommand(const LoadCommandInfo &L) const; MachO::routines_command getRoutinesCommand(const LoadCommandInfo &L) const; MachO::routines_command_64 getRoutinesCommand64(const LoadCommandInfo &L) const; MachO::thread_command getThreadCommand(const LoadCommandInfo &L) const; MachO::any_relocation_info getRelocation(DataRefImpl Rel) const; MachO::data_in_code_entry getDice(DataRefImpl Rel) const; const MachO::mach_header &getHeader() const; const MachO::mach_header_64 &getHeader64() const; uint32_t getIndirectSymbolTableEntry(const MachO::dysymtab_command &DLC, unsigned Index) const; MachO::data_in_code_entry getDataInCodeTableEntry(uint32_t DataOffset, unsigned Index) const; MachO::symtab_command getSymtabLoadCommand() const; MachO::dysymtab_command getDysymtabLoadCommand() const; MachO::linkedit_data_command getDataInCodeLoadCommand() const; MachO::linkedit_data_command getLinkOptHintsLoadCommand() const; ArrayRef getDyldInfoRebaseOpcodes() const; ArrayRef getDyldInfoBindOpcodes() const; ArrayRef getDyldInfoWeakBindOpcodes() const; ArrayRef getDyldInfoLazyBindOpcodes() const; ArrayRef getDyldInfoExportsTrie() const; ArrayRef getUuid() const; StringRef getStringTableData() const; bool is64Bit() const; void ReadULEB128s(uint64_t Index, SmallVectorImpl &Out) const; static StringRef guessLibraryShortName(StringRef Name, bool &isFramework, StringRef &Suffix); static Triple::ArchType getArch(uint32_t CPUType, uint32_t CPUSubType); static Triple getArchTriple(uint32_t CPUType, uint32_t CPUSubType, const char **McpuDefault = nullptr, const char **ArchFlag = nullptr); static bool isValidArch(StringRef ArchFlag); static ArrayRef getValidArchs(); static Triple getHostArch(); bool isRelocatableObject() const override; StringRef mapDebugSectionName(StringRef Name) const override; bool hasPageZeroSegment() const { return HasPageZeroSegment; } static bool classof(const Binary *v) { return v->isMachO(); } static uint32_t getVersionMinMajor(MachO::version_min_command &C, bool SDK) { uint32_t VersionOrSDK = (SDK) ? C.sdk : C.version; return (VersionOrSDK >> 16) & 0xffff; } static uint32_t getVersionMinMinor(MachO::version_min_command &C, bool SDK) { uint32_t VersionOrSDK = (SDK) ? C.sdk : C.version; return (VersionOrSDK >> 8) & 0xff; } static uint32_t getVersionMinUpdate(MachO::version_min_command &C, bool SDK) { uint32_t VersionOrSDK = (SDK) ? C.sdk : C.version; return VersionOrSDK & 0xff; } static std::string getBuildPlatform(uint32_t platform) { switch (platform) { case MachO::PLATFORM_MACOS: return "macos"; case MachO::PLATFORM_IOS: return "ios"; case MachO::PLATFORM_TVOS: return "tvos"; case MachO::PLATFORM_WATCHOS: return "watchos"; case MachO::PLATFORM_BRIDGEOS: return "bridgeos"; case MachO::PLATFORM_MACCATALYST: return "macCatalyst"; case MachO::PLATFORM_IOSSIMULATOR: return "iossimulator"; case MachO::PLATFORM_TVOSSIMULATOR: return "tvossimulator"; case MachO::PLATFORM_WATCHOSSIMULATOR: return "watchossimulator"; + case MachO::PLATFORM_DRIVERKIT: return "driverkit"; default: std::string ret; raw_string_ostream ss(ret); ss << format_hex(platform, 8, true); return ss.str(); } } static std::string getBuildTool(uint32_t tools) { switch (tools) { case MachO::TOOL_CLANG: return "clang"; case MachO::TOOL_SWIFT: return "swift"; case MachO::TOOL_LD: return "ld"; default: std::string ret; raw_string_ostream ss(ret); ss << format_hex(tools, 8, true); return ss.str(); } } static std::string getVersionString(uint32_t version) { uint32_t major = (version >> 16) & 0xffff; uint32_t minor = (version >> 8) & 0xff; uint32_t update = version & 0xff; SmallString<32> Version; Version = utostr(major) + "." + utostr(minor); if (update != 0) Version += "." + utostr(update); return std::string(std::string(Version.str())); } private: MachOObjectFile(MemoryBufferRef Object, bool IsLittleEndian, bool Is64Bits, Error &Err, uint32_t UniversalCputype = 0, uint32_t UniversalIndex = 0); uint64_t getSymbolValueImpl(DataRefImpl Symb) const override; union { MachO::mach_header_64 Header64; MachO::mach_header Header; }; using SectionList = SmallVector; SectionList Sections; using LibraryList = SmallVector; LibraryList Libraries; LoadCommandList LoadCommands; using LibraryShortName = SmallVector; using BuildToolList = SmallVector; BuildToolList BuildTools; mutable LibraryShortName LibrariesShortNames; std::unique_ptr BindRebaseSectionTable; const char *SymtabLoadCmd = nullptr; const char *DysymtabLoadCmd = nullptr; const char *DataInCodeLoadCmd = nullptr; const char *LinkOptHintsLoadCmd = nullptr; const char *DyldInfoLoadCmd = nullptr; const char *UuidLoadCmd = nullptr; bool HasPageZeroSegment = false; }; /// DiceRef inline DiceRef::DiceRef(DataRefImpl DiceP, const ObjectFile *Owner) : DicePimpl(DiceP) , OwningObject(Owner) {} inline bool DiceRef::operator==(const DiceRef &Other) const { return DicePimpl == Other.DicePimpl; } inline bool DiceRef::operator<(const DiceRef &Other) const { return DicePimpl < Other.DicePimpl; } inline void DiceRef::moveNext() { const MachO::data_in_code_entry *P = reinterpret_cast(DicePimpl.p); DicePimpl.p = reinterpret_cast(P + 1); } // Since a Mach-O data in code reference, a DiceRef, can only be created when // the OwningObject ObjectFile is a MachOObjectFile a static_cast<> is used for // the methods that get the values of the fields of the reference. inline std::error_code DiceRef::getOffset(uint32_t &Result) const { const MachOObjectFile *MachOOF = static_cast(OwningObject); MachO::data_in_code_entry Dice = MachOOF->getDice(DicePimpl); Result = Dice.offset; return std::error_code(); } inline std::error_code DiceRef::getLength(uint16_t &Result) const { const MachOObjectFile *MachOOF = static_cast(OwningObject); MachO::data_in_code_entry Dice = MachOOF->getDice(DicePimpl); Result = Dice.length; return std::error_code(); } inline std::error_code DiceRef::getKind(uint16_t &Result) const { const MachOObjectFile *MachOOF = static_cast(OwningObject); MachO::data_in_code_entry Dice = MachOOF->getDice(DicePimpl); Result = Dice.kind; return std::error_code(); } inline DataRefImpl DiceRef::getRawDataRefImpl() const { return DicePimpl; } inline const ObjectFile *DiceRef::getObjectFile() const { return OwningObject; } } // end namespace object } // end namespace llvm #endif // LLVM_OBJECT_MACHO_H diff --git a/llvm/include/llvm/TextAPI/MachO/Platform.h b/llvm/include/llvm/TextAPI/MachO/Platform.h index a22aae9b7dce..fc59b8678af7 100644 --- a/llvm/include/llvm/TextAPI/MachO/Platform.h +++ b/llvm/include/llvm/TextAPI/MachO/Platform.h @@ -1,45 +1,46 @@ //===- llvm/TextAPI/MachO/Platform.h - Platform -----------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Defines the Platforms supported by Tapi and helpers. // //===----------------------------------------------------------------------===// #ifndef LLVM_TEXTAPI_MACHO_PLATFORM_H #define LLVM_TEXTAPI_MACHO_PLATFORM_H #include "llvm/ADT/SmallSet.h" #include "llvm/BinaryFormat/MachO.h" namespace llvm { namespace MachO { /// Defines the list of MachO platforms. enum class PlatformKind : unsigned { unknown, macOS = MachO::PLATFORM_MACOS, iOS = MachO::PLATFORM_IOS, tvOS = MachO::PLATFORM_TVOS, watchOS = MachO::PLATFORM_WATCHOS, bridgeOS = MachO::PLATFORM_BRIDGEOS, macCatalyst = MachO::PLATFORM_MACCATALYST, iOSSimulator = MachO::PLATFORM_IOSSIMULATOR, tvOSSimulator = MachO::PLATFORM_TVOSSIMULATOR, - watchOSSimulator = MachO::PLATFORM_WATCHOSSIMULATOR + watchOSSimulator = MachO::PLATFORM_WATCHOSSIMULATOR, + driverKit = MachO::PLATFORM_DRIVERKIT, }; using PlatformSet = SmallSet; PlatformKind mapToPlatformKind(PlatformKind Platform, bool WantSim); PlatformKind mapToPlatformKind(const Triple &Target); PlatformSet mapToPlatformSet(ArrayRef Targets); StringRef getPlatformName(PlatformKind Platform); } // end namespace MachO. } // end namespace llvm. -#endif // LLVM_TEXTAPI_MACHO_PLATFORM_H \ No newline at end of file +#endif // LLVM_TEXTAPI_MACHO_PLATFORM_H diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp index b8da7f1615c9..db0ed9a73d22 100644 --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -1,2141 +1,2142 @@ //===- lib/MC/MCAsmStreamer.cpp - Text Assembly Output ----------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/Optional.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/DebugInfo/CodeView/SymbolRecord.h" #include "llvm/MC/MCAsmBackend.h" #include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCAssembler.h" #include "llvm/MC/MCCodeEmitter.h" #include "llvm/MC/MCCodeView.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCExpr.h" #include "llvm/MC/MCFixupKindInfo.h" #include "llvm/MC/MCInst.h" #include "llvm/MC/MCInstPrinter.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCObjectWriter.h" #include "llvm/MC/MCRegister.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbolXCOFF.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/Format.h" #include "llvm/Support/FormattedStream.h" #include "llvm/Support/LEB128.h" #include "llvm/Support/MathExtras.h" #include "llvm/Support/Path.h" #include "llvm/Support/TargetRegistry.h" #include using namespace llvm; namespace { class MCAsmStreamer final : public MCStreamer { std::unique_ptr OSOwner; formatted_raw_ostream &OS; const MCAsmInfo *MAI; std::unique_ptr InstPrinter; std::unique_ptr Assembler; SmallString<128> ExplicitCommentToEmit; SmallString<128> CommentToEmit; raw_svector_ostream CommentStream; raw_null_ostream NullStream; unsigned IsVerboseAsm : 1; unsigned ShowInst : 1; unsigned UseDwarfDirectory : 1; void EmitRegisterName(int64_t Register); void emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) override; void emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) override; public: MCAsmStreamer(MCContext &Context, std::unique_ptr os, bool isVerboseAsm, bool useDwarfDirectory, MCInstPrinter *printer, std::unique_ptr emitter, std::unique_ptr asmbackend, bool showInst) : MCStreamer(Context), OSOwner(std::move(os)), OS(*OSOwner), MAI(Context.getAsmInfo()), InstPrinter(printer), Assembler(std::make_unique( Context, std::move(asmbackend), std::move(emitter), (asmbackend) ? asmbackend->createObjectWriter(NullStream) : nullptr)), CommentStream(CommentToEmit), IsVerboseAsm(isVerboseAsm), ShowInst(showInst), UseDwarfDirectory(useDwarfDirectory) { assert(InstPrinter); if (IsVerboseAsm) InstPrinter->setCommentStream(CommentStream); if (Assembler->getBackendPtr()) setAllowAutoPadding(Assembler->getBackend().allowAutoPadding()); Context.setUseNamesOnTempLabels(true); } MCAssembler &getAssembler() { return *Assembler; } MCAssembler *getAssemblerPtr() override { return nullptr; } inline void EmitEOL() { // Dump Explicit Comments here. emitExplicitComments(); // If we don't have any comments, just emit a \n. if (!IsVerboseAsm) { OS << '\n'; return; } EmitCommentsAndEOL(); } void emitSyntaxDirective() override; void EmitCommentsAndEOL(); /// Return true if this streamer supports verbose assembly at all. bool isVerboseAsm() const override { return IsVerboseAsm; } /// Do we support EmitRawText? bool hasRawTextSupport() const override { return true; } /// Add a comment that can be emitted to the generated .s file to make the /// output of the compiler more readable. This only affects the MCAsmStreamer /// and only when verbose assembly output is enabled. void AddComment(const Twine &T, bool EOL = true) override; /// Add a comment showing the encoding of an instruction. void AddEncodingComment(const MCInst &Inst, const MCSubtargetInfo &); /// Return a raw_ostream that comments can be written to. /// Unlike AddComment, you are required to terminate comments with \n if you /// use this method. raw_ostream &GetCommentOS() override { if (!IsVerboseAsm) return nulls(); // Discard comments unless in verbose asm mode. return CommentStream; } void emitRawComment(const Twine &T, bool TabPrefix = true) override; void addExplicitComment(const Twine &T) override; void emitExplicitComments() override; /// Emit a blank line to a .s file to pretty it up. void AddBlankLine() override { EmitEOL(); } /// @name MCStreamer Interface /// @{ void changeSection(MCSection *Section, const MCExpr *Subsection) override; void emitELFSymverDirective(StringRef AliasName, const MCSymbol *Aliasee) override; void emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) override; void emitLabel(MCSymbol *Symbol, SMLoc Loc = SMLoc()) override; void emitAssemblerFlag(MCAssemblerFlag Flag) override; void emitLinkerOptions(ArrayRef Options) override; void emitDataRegion(MCDataRegionType Kind) override; void emitVersionMin(MCVersionMinType Kind, unsigned Major, unsigned Minor, unsigned Update, VersionTuple SDKVersion) override; void emitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor, unsigned Update, VersionTuple SDKVersion) override; void emitThumbFunc(MCSymbol *Func) override; void emitAssignment(MCSymbol *Symbol, const MCExpr *Value) override; void emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) override; bool emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) override; void emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) override; void BeginCOFFSymbolDef(const MCSymbol *Symbol) override; void EmitCOFFSymbolStorageClass(int StorageClass) override; void EmitCOFFSymbolType(int Type) override; void EndCOFFSymbolDef() override; void EmitCOFFSafeSEH(MCSymbol const *Symbol) override; void EmitCOFFSymbolIndex(MCSymbol const *Symbol) override; void EmitCOFFSectionIndex(MCSymbol const *Symbol) override; void EmitCOFFSecRel32(MCSymbol const *Symbol, uint64_t Offset) override; void EmitCOFFImgRel32(MCSymbol const *Symbol, int64_t Offset) override; void emitXCOFFLocalCommonSymbol(MCSymbol *LabelSym, uint64_t Size, MCSymbol *CsectSym, unsigned ByteAlign) override; void emitXCOFFSymbolLinkageWithVisibility(MCSymbol *Symbol, MCSymbolAttr Linakge, MCSymbolAttr Visibility) override; void emitXCOFFRenameDirective(const MCSymbol *Name, StringRef Rename) override; void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) override; /// Emit a local common (.lcomm) symbol. /// /// @param Symbol - The common symbol to emit. /// @param Size - The size of the common symbol. /// @param ByteAlignment - The alignment of the common symbol in bytes. void emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) override; void emitZerofill(MCSection *Section, MCSymbol *Symbol = nullptr, uint64_t Size = 0, unsigned ByteAlignment = 0, SMLoc Loc = SMLoc()) override; void emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment = 0) override; void emitBinaryData(StringRef Data) override; void emitBytes(StringRef Data) override; void emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc = SMLoc()) override; void emitIntValue(uint64_t Value, unsigned Size) override; void emitIntValueInHex(uint64_t Value, unsigned Size) override; void emitIntValueInHexWithPadding(uint64_t Value, unsigned Size) override; void emitULEB128Value(const MCExpr *Value) override; void emitSLEB128Value(const MCExpr *Value) override; void emitDTPRel32Value(const MCExpr *Value) override; void emitDTPRel64Value(const MCExpr *Value) override; void emitTPRel32Value(const MCExpr *Value) override; void emitTPRel64Value(const MCExpr *Value) override; void emitGPRel64Value(const MCExpr *Value) override; void emitGPRel32Value(const MCExpr *Value) override; void emitFill(const MCExpr &NumBytes, uint64_t FillValue, SMLoc Loc = SMLoc()) override; void emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr, SMLoc Loc = SMLoc()) override; void emitValueToAlignment(unsigned ByteAlignment, int64_t Value = 0, unsigned ValueSize = 1, unsigned MaxBytesToEmit = 0) override; void emitCodeAlignment(unsigned ByteAlignment, unsigned MaxBytesToEmit = 0) override; void emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) override; void emitFileDirective(StringRef Filename) override; Expected tryEmitDwarfFileDirective(unsigned FileNo, StringRef Directory, StringRef Filename, Optional Checksum = None, Optional Source = None, unsigned CUID = 0) override; void emitDwarfFile0Directive(StringRef Directory, StringRef Filename, Optional Checksum, Optional Source, unsigned CUID = 0) override; void emitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags, unsigned Isa, unsigned Discriminator, StringRef FileName) override; MCSymbol *getDwarfLineTableSymbol(unsigned CUID) override; bool EmitCVFileDirective(unsigned FileNo, StringRef Filename, ArrayRef Checksum, unsigned ChecksumKind) override; bool EmitCVFuncIdDirective(unsigned FuncId) override; bool EmitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, unsigned IAFile, unsigned IALine, unsigned IACol, SMLoc Loc) override; void emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, StringRef FileName, SMLoc Loc) override; void emitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, const MCSymbol *FnEnd) override; void emitCVInlineLinetableDirective(unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) override; void PrintCVDefRangePrefix( ArrayRef> Ranges); void emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeRegisterRelHeader DRHdr) override; void emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeSubfieldRegisterHeader DRHdr) override; void emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeRegisterHeader DRHdr) override; void emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeFramePointerRelHeader DRHdr) override; void emitCVStringTableDirective() override; void emitCVFileChecksumsDirective() override; void emitCVFileChecksumOffsetDirective(unsigned FileNo) override; void EmitCVFPOData(const MCSymbol *ProcSym, SMLoc L) override; void emitIdent(StringRef IdentString) override; void emitCFIBKeyFrame() override; void emitCFISections(bool EH, bool Debug) override; void emitCFIDefCfa(int64_t Register, int64_t Offset) override; void emitCFIDefCfaOffset(int64_t Offset) override; void emitCFIDefCfaRegister(int64_t Register) override; void emitCFIOffset(int64_t Register, int64_t Offset) override; void emitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) override; void emitCFILsda(const MCSymbol *Sym, unsigned Encoding) override; void emitCFIRememberState() override; void emitCFIRestoreState() override; void emitCFIRestore(int64_t Register) override; void emitCFISameValue(int64_t Register) override; void emitCFIRelOffset(int64_t Register, int64_t Offset) override; void emitCFIAdjustCfaOffset(int64_t Adjustment) override; void emitCFIEscape(StringRef Values) override; void emitCFIGnuArgsSize(int64_t Size) override; void emitCFISignalFrame() override; void emitCFIUndefined(int64_t Register) override; void emitCFIRegister(int64_t Register1, int64_t Register2) override; void emitCFIWindowSave() override; void emitCFINegateRAState() override; void emitCFIReturnColumn(int64_t Register) override; void EmitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) override; void EmitWinCFIEndProc(SMLoc Loc) override; void EmitWinCFIFuncletOrFuncEnd(SMLoc Loc) override; void EmitWinCFIStartChained(SMLoc Loc) override; void EmitWinCFIEndChained(SMLoc Loc) override; void EmitWinCFIPushReg(MCRegister Register, SMLoc Loc) override; void EmitWinCFISetFrame(MCRegister Register, unsigned Offset, SMLoc Loc) override; void EmitWinCFIAllocStack(unsigned Size, SMLoc Loc) override; void EmitWinCFISaveReg(MCRegister Register, unsigned Offset, SMLoc Loc) override; void EmitWinCFISaveXMM(MCRegister Register, unsigned Offset, SMLoc Loc) override; void EmitWinCFIPushFrame(bool Code, SMLoc Loc) override; void EmitWinCFIEndProlog(SMLoc Loc) override; void EmitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except, SMLoc Loc) override; void EmitWinEHHandlerData(SMLoc Loc) override; void emitCGProfileEntry(const MCSymbolRefExpr *From, const MCSymbolRefExpr *To, uint64_t Count) override; void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) override; void emitBundleAlignMode(unsigned AlignPow2) override; void emitBundleLock(bool AlignToEnd) override; void emitBundleUnlock() override; Optional> emitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc Loc, const MCSubtargetInfo &STI) override; void emitAddrsig() override; void emitAddrsigSym(const MCSymbol *Sym) override; /// If this file is backed by an assembly streamer, this dumps the specified /// string in the output .s file. This capability is indicated by the /// hasRawTextSupport() predicate. void emitRawTextImpl(StringRef String) override; void finishImpl() override; }; } // end anonymous namespace. void MCAsmStreamer::AddComment(const Twine &T, bool EOL) { if (!IsVerboseAsm) return; T.toVector(CommentToEmit); if (EOL) CommentToEmit.push_back('\n'); // Place comment in a new line. } void MCAsmStreamer::EmitCommentsAndEOL() { if (CommentToEmit.empty() && CommentStream.GetNumBytesInBuffer() == 0) { OS << '\n'; return; } StringRef Comments = CommentToEmit; assert(Comments.back() == '\n' && "Comment array not newline terminated"); do { // Emit a line of comments. OS.PadToColumn(MAI->getCommentColumn()); size_t Position = Comments.find('\n'); OS << MAI->getCommentString() << ' ' << Comments.substr(0, Position) <<'\n'; Comments = Comments.substr(Position+1); } while (!Comments.empty()); CommentToEmit.clear(); } static inline int64_t truncateToSize(int64_t Value, unsigned Bytes) { assert(Bytes > 0 && Bytes <= 8 && "Invalid size!"); return Value & ((uint64_t) (int64_t) -1 >> (64 - Bytes * 8)); } void MCAsmStreamer::emitRawComment(const Twine &T, bool TabPrefix) { if (TabPrefix) OS << '\t'; OS << MAI->getCommentString() << T; EmitEOL(); } void MCAsmStreamer::addExplicitComment(const Twine &T) { StringRef c = T.getSingleStringRef(); if (c.equals(StringRef(MAI->getSeparatorString()))) return; if (c.startswith(StringRef("//"))) { ExplicitCommentToEmit.append("\t"); ExplicitCommentToEmit.append(MAI->getCommentString()); // drop // ExplicitCommentToEmit.append(c.slice(2, c.size()).str()); } else if (c.startswith(StringRef("/*"))) { size_t p = 2, len = c.size() - 2; // emit each line in comment as separate newline. do { size_t newp = std::min(len, c.find_first_of("\r\n", p)); ExplicitCommentToEmit.append("\t"); ExplicitCommentToEmit.append(MAI->getCommentString()); ExplicitCommentToEmit.append(c.slice(p, newp).str()); // If we have another line in this comment add line if (newp < len) ExplicitCommentToEmit.append("\n"); p = newp + 1; } while (p < len); } else if (c.startswith(StringRef(MAI->getCommentString()))) { ExplicitCommentToEmit.append("\t"); ExplicitCommentToEmit.append(c.str()); } else if (c.front() == '#') { ExplicitCommentToEmit.append("\t"); ExplicitCommentToEmit.append(MAI->getCommentString()); ExplicitCommentToEmit.append(c.slice(1, c.size()).str()); } else assert(false && "Unexpected Assembly Comment"); // full line comments immediately output if (c.back() == '\n') emitExplicitComments(); } void MCAsmStreamer::emitExplicitComments() { StringRef Comments = ExplicitCommentToEmit; if (!Comments.empty()) OS << Comments; ExplicitCommentToEmit.clear(); } void MCAsmStreamer::changeSection(MCSection *Section, const MCExpr *Subsection) { assert(Section && "Cannot switch to a null section!"); if (MCTargetStreamer *TS = getTargetStreamer()) { TS->changeSection(getCurrentSectionOnly(), Section, Subsection, OS); } else { Section->PrintSwitchToSection( *MAI, getContext().getObjectFileInfo()->getTargetTriple(), OS, Subsection); } } void MCAsmStreamer::emitELFSymverDirective(StringRef AliasName, const MCSymbol *Aliasee) { OS << ".symver "; Aliasee->print(OS, MAI); OS << ", " << AliasName; EmitEOL(); } void MCAsmStreamer::emitLabel(MCSymbol *Symbol, SMLoc Loc) { MCStreamer::emitLabel(Symbol, Loc); Symbol->print(OS, MAI); OS << MAI->getLabelSuffix(); EmitEOL(); } void MCAsmStreamer::emitLOHDirective(MCLOHType Kind, const MCLOHArgs &Args) { StringRef str = MCLOHIdToName(Kind); #ifndef NDEBUG int NbArgs = MCLOHIdToNbArgs(Kind); assert(NbArgs != -1 && ((size_t)NbArgs) == Args.size() && "Malformed LOH!"); assert(str != "" && "Invalid LOH name"); #endif OS << "\t" << MCLOHDirectiveName() << " " << str << "\t"; bool IsFirst = true; for (const MCSymbol *Arg : Args) { if (!IsFirst) OS << ", "; IsFirst = false; Arg->print(OS, MAI); } EmitEOL(); } void MCAsmStreamer::emitAssemblerFlag(MCAssemblerFlag Flag) { switch (Flag) { case MCAF_SyntaxUnified: OS << "\t.syntax unified"; break; case MCAF_SubsectionsViaSymbols: OS << ".subsections_via_symbols"; break; case MCAF_Code16: OS << '\t'<< MAI->getCode16Directive();break; case MCAF_Code32: OS << '\t'<< MAI->getCode32Directive();break; case MCAF_Code64: OS << '\t'<< MAI->getCode64Directive();break; } EmitEOL(); } void MCAsmStreamer::emitLinkerOptions(ArrayRef Options) { assert(!Options.empty() && "At least one option is required!"); OS << "\t.linker_option \"" << Options[0] << '"'; for (ArrayRef::iterator it = Options.begin() + 1, ie = Options.end(); it != ie; ++it) { OS << ", " << '"' << *it << '"'; } EmitEOL(); } void MCAsmStreamer::emitDataRegion(MCDataRegionType Kind) { if (!MAI->doesSupportDataRegionDirectives()) return; switch (Kind) { case MCDR_DataRegion: OS << "\t.data_region"; break; case MCDR_DataRegionJT8: OS << "\t.data_region jt8"; break; case MCDR_DataRegionJT16: OS << "\t.data_region jt16"; break; case MCDR_DataRegionJT32: OS << "\t.data_region jt32"; break; case MCDR_DataRegionEnd: OS << "\t.end_data_region"; break; } EmitEOL(); } static const char *getVersionMinDirective(MCVersionMinType Type) { switch (Type) { case MCVM_WatchOSVersionMin: return ".watchos_version_min"; case MCVM_TvOSVersionMin: return ".tvos_version_min"; case MCVM_IOSVersionMin: return ".ios_version_min"; case MCVM_OSXVersionMin: return ".macosx_version_min"; } llvm_unreachable("Invalid MC version min type"); } static void EmitSDKVersionSuffix(raw_ostream &OS, const VersionTuple &SDKVersion) { if (SDKVersion.empty()) return; OS << '\t' << "sdk_version " << SDKVersion.getMajor(); if (auto Minor = SDKVersion.getMinor()) { OS << ", " << *Minor; if (auto Subminor = SDKVersion.getSubminor()) { OS << ", " << *Subminor; } } } void MCAsmStreamer::emitVersionMin(MCVersionMinType Type, unsigned Major, unsigned Minor, unsigned Update, VersionTuple SDKVersion) { OS << '\t' << getVersionMinDirective(Type) << ' ' << Major << ", " << Minor; if (Update) OS << ", " << Update; EmitSDKVersionSuffix(OS, SDKVersion); EmitEOL(); } static const char *getPlatformName(MachO::PlatformType Type) { switch (Type) { case MachO::PLATFORM_MACOS: return "macos"; case MachO::PLATFORM_IOS: return "ios"; case MachO::PLATFORM_TVOS: return "tvos"; case MachO::PLATFORM_WATCHOS: return "watchos"; case MachO::PLATFORM_BRIDGEOS: return "bridgeos"; case MachO::PLATFORM_MACCATALYST: return "macCatalyst"; case MachO::PLATFORM_IOSSIMULATOR: return "iossimulator"; case MachO::PLATFORM_TVOSSIMULATOR: return "tvossimulator"; case MachO::PLATFORM_WATCHOSSIMULATOR: return "watchossimulator"; + case MachO::PLATFORM_DRIVERKIT: return "driverkit"; } llvm_unreachable("Invalid Mach-O platform type"); } void MCAsmStreamer::emitBuildVersion(unsigned Platform, unsigned Major, unsigned Minor, unsigned Update, VersionTuple SDKVersion) { const char *PlatformName = getPlatformName((MachO::PlatformType)Platform); OS << "\t.build_version " << PlatformName << ", " << Major << ", " << Minor; if (Update) OS << ", " << Update; EmitSDKVersionSuffix(OS, SDKVersion); EmitEOL(); } void MCAsmStreamer::emitThumbFunc(MCSymbol *Func) { // This needs to emit to a temporary string to get properly quoted // MCSymbols when they have spaces in them. OS << "\t.thumb_func"; // Only Mach-O hasSubsectionsViaSymbols() if (MAI->hasSubsectionsViaSymbols()) { OS << '\t'; Func->print(OS, MAI); } EmitEOL(); } void MCAsmStreamer::emitAssignment(MCSymbol *Symbol, const MCExpr *Value) { // Do not emit a .set on inlined target assignments. bool EmitSet = true; if (auto *E = dyn_cast(Value)) if (E->inlineAssignedExpr()) EmitSet = false; if (EmitSet) { OS << ".set "; Symbol->print(OS, MAI); OS << ", "; Value->print(OS, MAI); EmitEOL(); } MCStreamer::emitAssignment(Symbol, Value); } void MCAsmStreamer::emitWeakReference(MCSymbol *Alias, const MCSymbol *Symbol) { OS << ".weakref "; Alias->print(OS, MAI); OS << ", "; Symbol->print(OS, MAI); EmitEOL(); } bool MCAsmStreamer::emitSymbolAttribute(MCSymbol *Symbol, MCSymbolAttr Attribute) { switch (Attribute) { case MCSA_Invalid: llvm_unreachable("Invalid symbol attribute"); case MCSA_ELF_TypeFunction: /// .type _foo, STT_FUNC # aka @function case MCSA_ELF_TypeIndFunction: /// .type _foo, STT_GNU_IFUNC case MCSA_ELF_TypeObject: /// .type _foo, STT_OBJECT # aka @object case MCSA_ELF_TypeTLS: /// .type _foo, STT_TLS # aka @tls_object case MCSA_ELF_TypeCommon: /// .type _foo, STT_COMMON # aka @common case MCSA_ELF_TypeNoType: /// .type _foo, STT_NOTYPE # aka @notype case MCSA_ELF_TypeGnuUniqueObject: /// .type _foo, @gnu_unique_object if (!MAI->hasDotTypeDotSizeDirective()) return false; // Symbol attribute not supported OS << "\t.type\t"; Symbol->print(OS, MAI); OS << ',' << ((MAI->getCommentString()[0] != '@') ? '@' : '%'); switch (Attribute) { default: return false; case MCSA_ELF_TypeFunction: OS << "function"; break; case MCSA_ELF_TypeIndFunction: OS << "gnu_indirect_function"; break; case MCSA_ELF_TypeObject: OS << "object"; break; case MCSA_ELF_TypeTLS: OS << "tls_object"; break; case MCSA_ELF_TypeCommon: OS << "common"; break; case MCSA_ELF_TypeNoType: OS << "notype"; break; case MCSA_ELF_TypeGnuUniqueObject: OS << "gnu_unique_object"; break; } EmitEOL(); return true; case MCSA_Global: // .globl/.global OS << MAI->getGlobalDirective(); break; case MCSA_LGlobal: OS << "\t.lglobl\t"; break; case MCSA_Hidden: OS << "\t.hidden\t"; break; case MCSA_IndirectSymbol: OS << "\t.indirect_symbol\t"; break; case MCSA_Internal: OS << "\t.internal\t"; break; case MCSA_LazyReference: OS << "\t.lazy_reference\t"; break; case MCSA_Local: OS << "\t.local\t"; break; case MCSA_NoDeadStrip: if (!MAI->hasNoDeadStrip()) return false; OS << "\t.no_dead_strip\t"; break; case MCSA_SymbolResolver: OS << "\t.symbol_resolver\t"; break; case MCSA_AltEntry: OS << "\t.alt_entry\t"; break; case MCSA_PrivateExtern: OS << "\t.private_extern\t"; break; case MCSA_Protected: OS << "\t.protected\t"; break; case MCSA_Reference: OS << "\t.reference\t"; break; case MCSA_Extern: OS << "\t.extern\t"; break; case MCSA_Weak: OS << MAI->getWeakDirective(); break; case MCSA_WeakDefinition: OS << "\t.weak_definition\t"; break; // .weak_reference case MCSA_WeakReference: OS << MAI->getWeakRefDirective(); break; case MCSA_WeakDefAutoPrivate: OS << "\t.weak_def_can_be_hidden\t"; break; case MCSA_Cold: // Assemblers currently do not support a .cold directive. return false; } Symbol->print(OS, MAI); EmitEOL(); return true; } void MCAsmStreamer::emitSymbolDesc(MCSymbol *Symbol, unsigned DescValue) { OS << ".desc" << ' '; Symbol->print(OS, MAI); OS << ',' << DescValue; EmitEOL(); } void MCAsmStreamer::emitSyntaxDirective() { if (MAI->getAssemblerDialect() == 1) { OS << "\t.intel_syntax noprefix"; EmitEOL(); } // FIXME: Currently emit unprefix'ed registers. // The intel_syntax directive has one optional argument // with may have a value of prefix or noprefix. } void MCAsmStreamer::BeginCOFFSymbolDef(const MCSymbol *Symbol) { OS << "\t.def\t "; Symbol->print(OS, MAI); OS << ';'; EmitEOL(); } void MCAsmStreamer::EmitCOFFSymbolStorageClass (int StorageClass) { OS << "\t.scl\t" << StorageClass << ';'; EmitEOL(); } void MCAsmStreamer::EmitCOFFSymbolType (int Type) { OS << "\t.type\t" << Type << ';'; EmitEOL(); } void MCAsmStreamer::EndCOFFSymbolDef() { OS << "\t.endef"; EmitEOL(); } void MCAsmStreamer::EmitCOFFSafeSEH(MCSymbol const *Symbol) { OS << "\t.safeseh\t"; Symbol->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::EmitCOFFSymbolIndex(MCSymbol const *Symbol) { OS << "\t.symidx\t"; Symbol->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::EmitCOFFSectionIndex(MCSymbol const *Symbol) { OS << "\t.secidx\t"; Symbol->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::EmitCOFFSecRel32(MCSymbol const *Symbol, uint64_t Offset) { OS << "\t.secrel32\t"; Symbol->print(OS, MAI); if (Offset != 0) OS << '+' << Offset; EmitEOL(); } void MCAsmStreamer::EmitCOFFImgRel32(MCSymbol const *Symbol, int64_t Offset) { OS << "\t.rva\t"; Symbol->print(OS, MAI); if (Offset > 0) OS << '+' << Offset; else if (Offset < 0) OS << '-' << -Offset; EmitEOL(); } // We need an XCOFF-specific version of this directive as the AIX syntax // requires a QualName argument identifying the csect name and storage mapping // class to appear before the alignment if we are specifying it. void MCAsmStreamer::emitXCOFFLocalCommonSymbol(MCSymbol *LabelSym, uint64_t Size, MCSymbol *CsectSym, unsigned ByteAlignment) { assert(MAI->getLCOMMDirectiveAlignmentType() == LCOMM::Log2Alignment && "We only support writing log base-2 alignment format with XCOFF."); assert(isPowerOf2_32(ByteAlignment) && "Alignment must be a power of 2."); OS << "\t.lcomm\t"; LabelSym->print(OS, MAI); OS << ',' << Size << ','; CsectSym->print(OS, MAI); OS << ',' << Log2_32(ByteAlignment); EmitEOL(); } void MCAsmStreamer::emitXCOFFSymbolLinkageWithVisibility( MCSymbol *Symbol, MCSymbolAttr Linkage, MCSymbolAttr Visibility) { switch (Linkage) { case MCSA_Global: OS << MAI->getGlobalDirective(); break; case MCSA_Weak: OS << MAI->getWeakDirective(); break; case MCSA_Extern: OS << "\t.extern\t"; break; case MCSA_LGlobal: OS << "\t.lglobl\t"; break; default: report_fatal_error("unhandled linkage type"); } Symbol->print(OS, MAI); switch (Visibility) { case MCSA_Invalid: // Nothing to do. break; case MCSA_Hidden: OS << ",hidden"; break; case MCSA_Protected: OS << ",protected"; break; default: report_fatal_error("unexpected value for Visibility type"); } EmitEOL(); // Print symbol's rename (original name contains invalid character(s)) if // there is one. if (cast(Symbol)->hasRename()) emitXCOFFRenameDirective(Symbol, cast(Symbol)->getSymbolTableName()); } void MCAsmStreamer::emitXCOFFRenameDirective(const MCSymbol *Name, StringRef Rename) { OS << "\t.rename\t"; Name->print(OS, MAI); const char DQ = '"'; OS << ',' << DQ; for (char C : Rename) { // To escape a double quote character, the character should be doubled. if (C == DQ) OS << DQ; OS << C; } OS << DQ; EmitEOL(); } void MCAsmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) { assert(MAI->hasDotTypeDotSizeDirective()); OS << "\t.size\t"; Symbol->print(OS, MAI); OS << ", "; Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) { OS << "\t.comm\t"; Symbol->print(OS, MAI); OS << ',' << Size; if (ByteAlignment != 0) { if (MAI->getCOMMDirectiveAlignmentIsInBytes()) OS << ',' << ByteAlignment; else OS << ',' << Log2_32(ByteAlignment); } EmitEOL(); // Print symbol's rename (original name contains invalid character(s)) if // there is one. MCSymbolXCOFF *XSym = dyn_cast(Symbol); if (XSym && XSym->hasRename()) emitXCOFFRenameDirective(XSym, XSym->getSymbolTableName()); } void MCAsmStreamer::emitLocalCommonSymbol(MCSymbol *Symbol, uint64_t Size, unsigned ByteAlign) { OS << "\t.lcomm\t"; Symbol->print(OS, MAI); OS << ',' << Size; if (ByteAlign > 1) { switch (MAI->getLCOMMDirectiveAlignmentType()) { case LCOMM::NoAlignment: llvm_unreachable("alignment not supported on .lcomm!"); case LCOMM::ByteAlignment: OS << ',' << ByteAlign; break; case LCOMM::Log2Alignment: assert(isPowerOf2_32(ByteAlign) && "alignment must be a power of 2"); OS << ',' << Log2_32(ByteAlign); break; } } EmitEOL(); } void MCAsmStreamer::emitZerofill(MCSection *Section, MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment, SMLoc Loc) { if (Symbol) AssignFragment(Symbol, &Section->getDummyFragment()); // Note: a .zerofill directive does not switch sections. OS << ".zerofill "; assert(Section->getVariant() == MCSection::SV_MachO && ".zerofill is a Mach-O specific directive"); // This is a mach-o specific directive. const MCSectionMachO *MOSection = ((const MCSectionMachO*)Section); OS << MOSection->getSegmentName() << "," << MOSection->getName(); if (Symbol) { OS << ','; Symbol->print(OS, MAI); OS << ',' << Size; if (ByteAlignment != 0) OS << ',' << Log2_32(ByteAlignment); } EmitEOL(); } // .tbss sym, size, align // This depends that the symbol has already been mangled from the original, // e.g. _a. void MCAsmStreamer::emitTBSSSymbol(MCSection *Section, MCSymbol *Symbol, uint64_t Size, unsigned ByteAlignment) { AssignFragment(Symbol, &Section->getDummyFragment()); assert(Symbol && "Symbol shouldn't be NULL!"); // Instead of using the Section we'll just use the shortcut. assert(Section->getVariant() == MCSection::SV_MachO && ".zerofill is a Mach-O specific directive"); // This is a mach-o specific directive and section. OS << ".tbss "; Symbol->print(OS, MAI); OS << ", " << Size; // Output align if we have it. We default to 1 so don't bother printing // that. if (ByteAlignment > 1) OS << ", " << Log2_32(ByteAlignment); EmitEOL(); } static inline char toOctal(int X) { return (X&7)+'0'; } static void PrintQuotedString(StringRef Data, raw_ostream &OS) { OS << '"'; for (unsigned i = 0, e = Data.size(); i != e; ++i) { unsigned char C = Data[i]; if (C == '"' || C == '\\') { OS << '\\' << (char)C; continue; } if (isPrint((unsigned char)C)) { OS << (char)C; continue; } switch (C) { case '\b': OS << "\\b"; break; case '\f': OS << "\\f"; break; case '\n': OS << "\\n"; break; case '\r': OS << "\\r"; break; case '\t': OS << "\\t"; break; default: OS << '\\'; OS << toOctal(C >> 6); OS << toOctal(C >> 3); OS << toOctal(C >> 0); break; } } OS << '"'; } void MCAsmStreamer::emitBytes(StringRef Data) { assert(getCurrentSectionOnly() && "Cannot emit contents before setting section!"); if (Data.empty()) return; // If only single byte is provided or no ascii or asciz directives is // supported, emit as vector of 8bits data. if (Data.size() == 1 || !(MAI->getAscizDirective() || MAI->getAsciiDirective())) { if (MCTargetStreamer *TS = getTargetStreamer()) { TS->emitRawBytes(Data); } else { const char *Directive = MAI->getData8bitsDirective(); for (const unsigned char C : Data.bytes()) { OS << Directive << (unsigned)C; EmitEOL(); } } return; } // If the data ends with 0 and the target supports .asciz, use it, otherwise // use .ascii if (MAI->getAscizDirective() && Data.back() == 0) { OS << MAI->getAscizDirective(); Data = Data.substr(0, Data.size()-1); } else { OS << MAI->getAsciiDirective(); } PrintQuotedString(Data, OS); EmitEOL(); } void MCAsmStreamer::emitBinaryData(StringRef Data) { // This is binary data. Print it in a grid of hex bytes for readability. const size_t Cols = 4; for (size_t I = 0, EI = alignTo(Data.size(), Cols); I < EI; I += Cols) { size_t J = I, EJ = std::min(I + Cols, Data.size()); assert(EJ > 0); OS << MAI->getData8bitsDirective(); for (; J < EJ - 1; ++J) OS << format("0x%02x", uint8_t(Data[J])) << ", "; OS << format("0x%02x", uint8_t(Data[J])); EmitEOL(); } } void MCAsmStreamer::emitIntValue(uint64_t Value, unsigned Size) { emitValue(MCConstantExpr::create(Value, getContext()), Size); } void MCAsmStreamer::emitIntValueInHex(uint64_t Value, unsigned Size) { emitValue(MCConstantExpr::create(Value, getContext(), true), Size); } void MCAsmStreamer::emitIntValueInHexWithPadding(uint64_t Value, unsigned Size) { emitValue(MCConstantExpr::create(Value, getContext(), true, Size), Size); } void MCAsmStreamer::emitValueImpl(const MCExpr *Value, unsigned Size, SMLoc Loc) { assert(Size <= 8 && "Invalid size"); assert(getCurrentSectionOnly() && "Cannot emit contents before setting section!"); const char *Directive = nullptr; switch (Size) { default: break; case 1: Directive = MAI->getData8bitsDirective(); break; case 2: Directive = MAI->getData16bitsDirective(); break; case 4: Directive = MAI->getData32bitsDirective(); break; case 8: Directive = MAI->getData64bitsDirective(); break; } if (!Directive) { int64_t IntValue; if (!Value->evaluateAsAbsolute(IntValue)) report_fatal_error("Don't know how to emit this value."); // We couldn't handle the requested integer size so we fallback by breaking // the request down into several, smaller, integers. // Since sizes greater or equal to "Size" are invalid, we use the greatest // power of 2 that is less than "Size" as our largest piece of granularity. bool IsLittleEndian = MAI->isLittleEndian(); for (unsigned Emitted = 0; Emitted != Size;) { unsigned Remaining = Size - Emitted; // The size of our partial emission must be a power of two less than // Size. unsigned EmissionSize = PowerOf2Floor(std::min(Remaining, Size - 1)); // Calculate the byte offset of our partial emission taking into account // the endianness of the target. unsigned ByteOffset = IsLittleEndian ? Emitted : (Remaining - EmissionSize); uint64_t ValueToEmit = IntValue >> (ByteOffset * 8); // We truncate our partial emission to fit within the bounds of the // emission domain. This produces nicer output and silences potential // truncation warnings when round tripping through another assembler. uint64_t Shift = 64 - EmissionSize * 8; assert(Shift < static_cast( std::numeric_limits::digits) && "undefined behavior"); ValueToEmit &= ~0ULL >> Shift; emitIntValue(ValueToEmit, EmissionSize); Emitted += EmissionSize; } return; } assert(Directive && "Invalid size for machine code value!"); OS << Directive; if (MCTargetStreamer *TS = getTargetStreamer()) { TS->emitValue(Value); } else { Value->print(OS, MAI); EmitEOL(); } } void MCAsmStreamer::emitULEB128Value(const MCExpr *Value) { int64_t IntValue; if (Value->evaluateAsAbsolute(IntValue)) { emitULEB128IntValue(IntValue); return; } OS << "\t.uleb128 "; Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitSLEB128Value(const MCExpr *Value) { int64_t IntValue; if (Value->evaluateAsAbsolute(IntValue)) { emitSLEB128IntValue(IntValue); return; } OS << "\t.sleb128 "; Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitDTPRel64Value(const MCExpr *Value) { assert(MAI->getDTPRel64Directive() != nullptr); OS << MAI->getDTPRel64Directive(); Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitDTPRel32Value(const MCExpr *Value) { assert(MAI->getDTPRel32Directive() != nullptr); OS << MAI->getDTPRel32Directive(); Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitTPRel64Value(const MCExpr *Value) { assert(MAI->getTPRel64Directive() != nullptr); OS << MAI->getTPRel64Directive(); Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitTPRel32Value(const MCExpr *Value) { assert(MAI->getTPRel32Directive() != nullptr); OS << MAI->getTPRel32Directive(); Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitGPRel64Value(const MCExpr *Value) { assert(MAI->getGPRel64Directive() != nullptr); OS << MAI->getGPRel64Directive(); Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitGPRel32Value(const MCExpr *Value) { assert(MAI->getGPRel32Directive() != nullptr); OS << MAI->getGPRel32Directive(); Value->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitFill(const MCExpr &NumBytes, uint64_t FillValue, SMLoc Loc) { int64_t IntNumBytes; const bool IsAbsolute = NumBytes.evaluateAsAbsolute(IntNumBytes); if (IsAbsolute && IntNumBytes == 0) return; if (const char *ZeroDirective = MAI->getZeroDirective()) { if (MAI->doesZeroDirectiveSupportNonZeroValue() || FillValue == 0) { // FIXME: Emit location directives OS << ZeroDirective; NumBytes.print(OS, MAI); if (FillValue != 0) OS << ',' << (int)FillValue; EmitEOL(); } else { if (!IsAbsolute) report_fatal_error( "Cannot emit non-absolute expression lengths of fill."); for (int i = 0; i < IntNumBytes; ++i) { OS << MAI->getData8bitsDirective() << (int)FillValue; EmitEOL(); } } return; } MCStreamer::emitFill(NumBytes, FillValue); } void MCAsmStreamer::emitFill(const MCExpr &NumValues, int64_t Size, int64_t Expr, SMLoc Loc) { // FIXME: Emit location directives OS << "\t.fill\t"; NumValues.print(OS, MAI); OS << ", " << Size << ", 0x"; OS.write_hex(truncateToSize(Expr, 4)); EmitEOL(); } void MCAsmStreamer::emitValueToAlignment(unsigned ByteAlignment, int64_t Value, unsigned ValueSize, unsigned MaxBytesToEmit) { if (MAI->useDotAlignForAlignment()) { if (!isPowerOf2_32(ByteAlignment)) report_fatal_error("Only power-of-two alignments are supported " "with .align."); OS << "\t.align\t"; OS << Log2_32(ByteAlignment); EmitEOL(); return; } // Some assemblers don't support non-power of two alignments, so we always // emit alignments as a power of two if possible. if (isPowerOf2_32(ByteAlignment)) { switch (ValueSize) { default: llvm_unreachable("Invalid size for machine code value!"); case 1: OS << "\t.p2align\t"; break; case 2: OS << ".p2alignw "; break; case 4: OS << ".p2alignl "; break; case 8: llvm_unreachable("Unsupported alignment size!"); } OS << Log2_32(ByteAlignment); if (Value || MaxBytesToEmit) { OS << ", 0x"; OS.write_hex(truncateToSize(Value, ValueSize)); if (MaxBytesToEmit) OS << ", " << MaxBytesToEmit; } EmitEOL(); return; } // Non-power of two alignment. This is not widely supported by assemblers. // FIXME: Parameterize this based on MAI. switch (ValueSize) { default: llvm_unreachable("Invalid size for machine code value!"); case 1: OS << ".balign"; break; case 2: OS << ".balignw"; break; case 4: OS << ".balignl"; break; case 8: llvm_unreachable("Unsupported alignment size!"); } OS << ' ' << ByteAlignment; OS << ", " << truncateToSize(Value, ValueSize); if (MaxBytesToEmit) OS << ", " << MaxBytesToEmit; EmitEOL(); } void MCAsmStreamer::emitCodeAlignment(unsigned ByteAlignment, unsigned MaxBytesToEmit) { // Emit with a text fill value. emitValueToAlignment(ByteAlignment, MAI->getTextAlignFillValue(), 1, MaxBytesToEmit); } void MCAsmStreamer::emitValueToOffset(const MCExpr *Offset, unsigned char Value, SMLoc Loc) { // FIXME: Verify that Offset is associated with the current section. OS << ".org "; Offset->print(OS, MAI); OS << ", " << (unsigned)Value; EmitEOL(); } void MCAsmStreamer::emitFileDirective(StringRef Filename) { assert(MAI->hasSingleParameterDotFile()); OS << "\t.file\t"; PrintQuotedString(Filename, OS); EmitEOL(); } static void printDwarfFileDirective(unsigned FileNo, StringRef Directory, StringRef Filename, Optional Checksum, Optional Source, bool UseDwarfDirectory, raw_svector_ostream &OS) { SmallString<128> FullPathName; if (!UseDwarfDirectory && !Directory.empty()) { if (sys::path::is_absolute(Filename)) Directory = ""; else { FullPathName = Directory; sys::path::append(FullPathName, Filename); Directory = ""; Filename = FullPathName; } } OS << "\t.file\t" << FileNo << ' '; if (!Directory.empty()) { PrintQuotedString(Directory, OS); OS << ' '; } PrintQuotedString(Filename, OS); if (Checksum) OS << " md5 0x" << Checksum->digest(); if (Source) { OS << " source "; PrintQuotedString(*Source, OS); } } Expected MCAsmStreamer::tryEmitDwarfFileDirective( unsigned FileNo, StringRef Directory, StringRef Filename, Optional Checksum, Optional Source, unsigned CUID) { assert(CUID == 0 && "multiple CUs not supported by MCAsmStreamer"); MCDwarfLineTable &Table = getContext().getMCDwarfLineTable(CUID); unsigned NumFiles = Table.getMCDwarfFiles().size(); Expected FileNoOrErr = Table.tryGetFile(Directory, Filename, Checksum, Source, getContext().getDwarfVersion(), FileNo); if (!FileNoOrErr) return FileNoOrErr.takeError(); FileNo = FileNoOrErr.get(); if (NumFiles == Table.getMCDwarfFiles().size()) return FileNo; SmallString<128> Str; raw_svector_ostream OS1(Str); printDwarfFileDirective(FileNo, Directory, Filename, Checksum, Source, UseDwarfDirectory, OS1); if (MCTargetStreamer *TS = getTargetStreamer()) TS->emitDwarfFileDirective(OS1.str()); else emitRawText(OS1.str()); return FileNo; } void MCAsmStreamer::emitDwarfFile0Directive(StringRef Directory, StringRef Filename, Optional Checksum, Optional Source, unsigned CUID) { assert(CUID == 0); // .file 0 is new for DWARF v5. if (getContext().getDwarfVersion() < 5) return; // Inform MCDwarf about the root file. getContext().setMCLineTableRootFile(CUID, Directory, Filename, Checksum, Source); SmallString<128> Str; raw_svector_ostream OS1(Str); printDwarfFileDirective(0, Directory, Filename, Checksum, Source, UseDwarfDirectory, OS1); if (MCTargetStreamer *TS = getTargetStreamer()) TS->emitDwarfFileDirective(OS1.str()); else emitRawText(OS1.str()); } void MCAsmStreamer::emitDwarfLocDirective(unsigned FileNo, unsigned Line, unsigned Column, unsigned Flags, unsigned Isa, unsigned Discriminator, StringRef FileName) { OS << "\t.loc\t" << FileNo << " " << Line << " " << Column; if (MAI->supportsExtendedDwarfLocDirective()) { if (Flags & DWARF2_FLAG_BASIC_BLOCK) OS << " basic_block"; if (Flags & DWARF2_FLAG_PROLOGUE_END) OS << " prologue_end"; if (Flags & DWARF2_FLAG_EPILOGUE_BEGIN) OS << " epilogue_begin"; unsigned OldFlags = getContext().getCurrentDwarfLoc().getFlags(); if ((Flags & DWARF2_FLAG_IS_STMT) != (OldFlags & DWARF2_FLAG_IS_STMT)) { OS << " is_stmt "; if (Flags & DWARF2_FLAG_IS_STMT) OS << "1"; else OS << "0"; } if (Isa) OS << " isa " << Isa; if (Discriminator) OS << " discriminator " << Discriminator; } if (IsVerboseAsm) { OS.PadToColumn(MAI->getCommentColumn()); OS << MAI->getCommentString() << ' ' << FileName << ':' << Line << ':' << Column; } EmitEOL(); this->MCStreamer::emitDwarfLocDirective(FileNo, Line, Column, Flags, Isa, Discriminator, FileName); } MCSymbol *MCAsmStreamer::getDwarfLineTableSymbol(unsigned CUID) { // Always use the zeroth line table, since asm syntax only supports one line // table for now. return MCStreamer::getDwarfLineTableSymbol(0); } bool MCAsmStreamer::EmitCVFileDirective(unsigned FileNo, StringRef Filename, ArrayRef Checksum, unsigned ChecksumKind) { if (!getContext().getCVContext().addFile(*this, FileNo, Filename, Checksum, ChecksumKind)) return false; OS << "\t.cv_file\t" << FileNo << ' '; PrintQuotedString(Filename, OS); if (!ChecksumKind) { EmitEOL(); return true; } OS << ' '; PrintQuotedString(toHex(Checksum), OS); OS << ' ' << ChecksumKind; EmitEOL(); return true; } bool MCAsmStreamer::EmitCVFuncIdDirective(unsigned FuncId) { OS << "\t.cv_func_id " << FuncId << '\n'; return MCStreamer::EmitCVFuncIdDirective(FuncId); } bool MCAsmStreamer::EmitCVInlineSiteIdDirective(unsigned FunctionId, unsigned IAFunc, unsigned IAFile, unsigned IALine, unsigned IACol, SMLoc Loc) { OS << "\t.cv_inline_site_id " << FunctionId << " within " << IAFunc << " inlined_at " << IAFile << ' ' << IALine << ' ' << IACol << '\n'; return MCStreamer::EmitCVInlineSiteIdDirective(FunctionId, IAFunc, IAFile, IALine, IACol, Loc); } void MCAsmStreamer::emitCVLocDirective(unsigned FunctionId, unsigned FileNo, unsigned Line, unsigned Column, bool PrologueEnd, bool IsStmt, StringRef FileName, SMLoc Loc) { // Validate the directive. if (!checkCVLocSection(FunctionId, FileNo, Loc)) return; OS << "\t.cv_loc\t" << FunctionId << " " << FileNo << " " << Line << " " << Column; if (PrologueEnd) OS << " prologue_end"; if (IsStmt) OS << " is_stmt 1"; if (IsVerboseAsm) { OS.PadToColumn(MAI->getCommentColumn()); OS << MAI->getCommentString() << ' ' << FileName << ':' << Line << ':' << Column; } EmitEOL(); } void MCAsmStreamer::emitCVLinetableDirective(unsigned FunctionId, const MCSymbol *FnStart, const MCSymbol *FnEnd) { OS << "\t.cv_linetable\t" << FunctionId << ", "; FnStart->print(OS, MAI); OS << ", "; FnEnd->print(OS, MAI); EmitEOL(); this->MCStreamer::emitCVLinetableDirective(FunctionId, FnStart, FnEnd); } void MCAsmStreamer::emitCVInlineLinetableDirective(unsigned PrimaryFunctionId, unsigned SourceFileId, unsigned SourceLineNum, const MCSymbol *FnStartSym, const MCSymbol *FnEndSym) { OS << "\t.cv_inline_linetable\t" << PrimaryFunctionId << ' ' << SourceFileId << ' ' << SourceLineNum << ' '; FnStartSym->print(OS, MAI); OS << ' '; FnEndSym->print(OS, MAI); EmitEOL(); this->MCStreamer::emitCVInlineLinetableDirective( PrimaryFunctionId, SourceFileId, SourceLineNum, FnStartSym, FnEndSym); } void MCAsmStreamer::PrintCVDefRangePrefix( ArrayRef> Ranges) { OS << "\t.cv_def_range\t"; for (std::pair Range : Ranges) { OS << ' '; Range.first->print(OS, MAI); OS << ' '; Range.second->print(OS, MAI); } } void MCAsmStreamer::emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeRegisterRelHeader DRHdr) { PrintCVDefRangePrefix(Ranges); OS << ", reg_rel, "; OS << DRHdr.Register << ", " << DRHdr.Flags << ", " << DRHdr.BasePointerOffset; EmitEOL(); } void MCAsmStreamer::emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeSubfieldRegisterHeader DRHdr) { PrintCVDefRangePrefix(Ranges); OS << ", subfield_reg, "; OS << DRHdr.Register << ", " << DRHdr.OffsetInParent; EmitEOL(); } void MCAsmStreamer::emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeRegisterHeader DRHdr) { PrintCVDefRangePrefix(Ranges); OS << ", reg, "; OS << DRHdr.Register; EmitEOL(); } void MCAsmStreamer::emitCVDefRangeDirective( ArrayRef> Ranges, codeview::DefRangeFramePointerRelHeader DRHdr) { PrintCVDefRangePrefix(Ranges); OS << ", frame_ptr_rel, "; OS << DRHdr.Offset; EmitEOL(); } void MCAsmStreamer::emitCVStringTableDirective() { OS << "\t.cv_stringtable"; EmitEOL(); } void MCAsmStreamer::emitCVFileChecksumsDirective() { OS << "\t.cv_filechecksums"; EmitEOL(); } void MCAsmStreamer::emitCVFileChecksumOffsetDirective(unsigned FileNo) { OS << "\t.cv_filechecksumoffset\t" << FileNo; EmitEOL(); } void MCAsmStreamer::EmitCVFPOData(const MCSymbol *ProcSym, SMLoc L) { OS << "\t.cv_fpo_data\t"; ProcSym->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitIdent(StringRef IdentString) { assert(MAI->hasIdentDirective() && ".ident directive not supported"); OS << "\t.ident\t"; PrintQuotedString(IdentString, OS); EmitEOL(); } void MCAsmStreamer::emitCFISections(bool EH, bool Debug) { MCStreamer::emitCFISections(EH, Debug); OS << "\t.cfi_sections "; if (EH) { OS << ".eh_frame"; if (Debug) OS << ", .debug_frame"; } else if (Debug) { OS << ".debug_frame"; } EmitEOL(); } void MCAsmStreamer::emitCFIStartProcImpl(MCDwarfFrameInfo &Frame) { OS << "\t.cfi_startproc"; if (Frame.IsSimple) OS << " simple"; EmitEOL(); } void MCAsmStreamer::emitCFIEndProcImpl(MCDwarfFrameInfo &Frame) { MCStreamer::emitCFIEndProcImpl(Frame); OS << "\t.cfi_endproc"; EmitEOL(); } void MCAsmStreamer::EmitRegisterName(int64_t Register) { if (!MAI->useDwarfRegNumForCFI()) { // User .cfi_* directives can use arbitrary DWARF register numbers, not // just ones that map to LLVM register numbers and have known names. // Fall back to using the original number directly if no name is known. const MCRegisterInfo *MRI = getContext().getRegisterInfo(); if (Optional LLVMRegister = MRI->getLLVMRegNum(Register, true)) { InstPrinter->printRegName(OS, *LLVMRegister); return; } } OS << Register; } void MCAsmStreamer::emitCFIDefCfa(int64_t Register, int64_t Offset) { MCStreamer::emitCFIDefCfa(Register, Offset); OS << "\t.cfi_def_cfa "; EmitRegisterName(Register); OS << ", " << Offset; EmitEOL(); } void MCAsmStreamer::emitCFIDefCfaOffset(int64_t Offset) { MCStreamer::emitCFIDefCfaOffset(Offset); OS << "\t.cfi_def_cfa_offset " << Offset; EmitEOL(); } static void PrintCFIEscape(llvm::formatted_raw_ostream &OS, StringRef Values) { OS << "\t.cfi_escape "; if (!Values.empty()) { size_t e = Values.size() - 1; for (size_t i = 0; i < e; ++i) OS << format("0x%02x", uint8_t(Values[i])) << ", "; OS << format("0x%02x", uint8_t(Values[e])); } } void MCAsmStreamer::emitCFIEscape(StringRef Values) { MCStreamer::emitCFIEscape(Values); PrintCFIEscape(OS, Values); EmitEOL(); } void MCAsmStreamer::emitCFIGnuArgsSize(int64_t Size) { MCStreamer::emitCFIGnuArgsSize(Size); uint8_t Buffer[16] = { dwarf::DW_CFA_GNU_args_size }; unsigned Len = encodeULEB128(Size, Buffer + 1) + 1; PrintCFIEscape(OS, StringRef((const char *)&Buffer[0], Len)); EmitEOL(); } void MCAsmStreamer::emitCFIDefCfaRegister(int64_t Register) { MCStreamer::emitCFIDefCfaRegister(Register); OS << "\t.cfi_def_cfa_register "; EmitRegisterName(Register); EmitEOL(); } void MCAsmStreamer::emitCFIOffset(int64_t Register, int64_t Offset) { this->MCStreamer::emitCFIOffset(Register, Offset); OS << "\t.cfi_offset "; EmitRegisterName(Register); OS << ", " << Offset; EmitEOL(); } void MCAsmStreamer::emitCFIPersonality(const MCSymbol *Sym, unsigned Encoding) { MCStreamer::emitCFIPersonality(Sym, Encoding); OS << "\t.cfi_personality " << Encoding << ", "; Sym->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitCFILsda(const MCSymbol *Sym, unsigned Encoding) { MCStreamer::emitCFILsda(Sym, Encoding); OS << "\t.cfi_lsda " << Encoding << ", "; Sym->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::emitCFIRememberState() { MCStreamer::emitCFIRememberState(); OS << "\t.cfi_remember_state"; EmitEOL(); } void MCAsmStreamer::emitCFIRestoreState() { MCStreamer::emitCFIRestoreState(); OS << "\t.cfi_restore_state"; EmitEOL(); } void MCAsmStreamer::emitCFIRestore(int64_t Register) { MCStreamer::emitCFIRestore(Register); OS << "\t.cfi_restore "; EmitRegisterName(Register); EmitEOL(); } void MCAsmStreamer::emitCFISameValue(int64_t Register) { MCStreamer::emitCFISameValue(Register); OS << "\t.cfi_same_value "; EmitRegisterName(Register); EmitEOL(); } void MCAsmStreamer::emitCFIRelOffset(int64_t Register, int64_t Offset) { MCStreamer::emitCFIRelOffset(Register, Offset); OS << "\t.cfi_rel_offset "; EmitRegisterName(Register); OS << ", " << Offset; EmitEOL(); } void MCAsmStreamer::emitCFIAdjustCfaOffset(int64_t Adjustment) { MCStreamer::emitCFIAdjustCfaOffset(Adjustment); OS << "\t.cfi_adjust_cfa_offset " << Adjustment; EmitEOL(); } void MCAsmStreamer::emitCFISignalFrame() { MCStreamer::emitCFISignalFrame(); OS << "\t.cfi_signal_frame"; EmitEOL(); } void MCAsmStreamer::emitCFIUndefined(int64_t Register) { MCStreamer::emitCFIUndefined(Register); OS << "\t.cfi_undefined "; EmitRegisterName(Register); EmitEOL(); } void MCAsmStreamer::emitCFIRegister(int64_t Register1, int64_t Register2) { MCStreamer::emitCFIRegister(Register1, Register2); OS << "\t.cfi_register "; EmitRegisterName(Register1); OS << ", "; EmitRegisterName(Register2); EmitEOL(); } void MCAsmStreamer::emitCFIWindowSave() { MCStreamer::emitCFIWindowSave(); OS << "\t.cfi_window_save"; EmitEOL(); } void MCAsmStreamer::emitCFINegateRAState() { MCStreamer::emitCFINegateRAState(); OS << "\t.cfi_negate_ra_state"; EmitEOL(); } void MCAsmStreamer::emitCFIReturnColumn(int64_t Register) { MCStreamer::emitCFIReturnColumn(Register); OS << "\t.cfi_return_column "; EmitRegisterName(Register); EmitEOL(); } void MCAsmStreamer::emitCFIBKeyFrame() { MCStreamer::emitCFIBKeyFrame(); OS << "\t.cfi_b_key_frame"; EmitEOL(); } void MCAsmStreamer::EmitWinCFIStartProc(const MCSymbol *Symbol, SMLoc Loc) { MCStreamer::EmitWinCFIStartProc(Symbol, Loc); OS << ".seh_proc "; Symbol->print(OS, MAI); EmitEOL(); } void MCAsmStreamer::EmitWinCFIEndProc(SMLoc Loc) { MCStreamer::EmitWinCFIEndProc(Loc); OS << "\t.seh_endproc"; EmitEOL(); } // TODO: Implement void MCAsmStreamer::EmitWinCFIFuncletOrFuncEnd(SMLoc Loc) { } void MCAsmStreamer::EmitWinCFIStartChained(SMLoc Loc) { MCStreamer::EmitWinCFIStartChained(Loc); OS << "\t.seh_startchained"; EmitEOL(); } void MCAsmStreamer::EmitWinCFIEndChained(SMLoc Loc) { MCStreamer::EmitWinCFIEndChained(Loc); OS << "\t.seh_endchained"; EmitEOL(); } void MCAsmStreamer::EmitWinEHHandler(const MCSymbol *Sym, bool Unwind, bool Except, SMLoc Loc) { MCStreamer::EmitWinEHHandler(Sym, Unwind, Except, Loc); OS << "\t.seh_handler "; Sym->print(OS, MAI); if (Unwind) OS << ", @unwind"; if (Except) OS << ", @except"; EmitEOL(); } void MCAsmStreamer::EmitWinEHHandlerData(SMLoc Loc) { MCStreamer::EmitWinEHHandlerData(Loc); // Switch sections. Don't call SwitchSection directly, because that will // cause the section switch to be visible in the emitted assembly. // We only do this so the section switch that terminates the handler // data block is visible. WinEH::FrameInfo *CurFrame = getCurrentWinFrameInfo(); // Do nothing if no frame is open. MCStreamer should've already reported an // error. if (!CurFrame) return; MCSection *TextSec = &CurFrame->Function->getSection(); MCSection *XData = getAssociatedXDataSection(TextSec); SwitchSectionNoChange(XData); OS << "\t.seh_handlerdata"; EmitEOL(); } void MCAsmStreamer::EmitWinCFIPushReg(MCRegister Register, SMLoc Loc) { MCStreamer::EmitWinCFIPushReg(Register, Loc); OS << "\t.seh_pushreg "; InstPrinter->printRegName(OS, Register); EmitEOL(); } void MCAsmStreamer::EmitWinCFISetFrame(MCRegister Register, unsigned Offset, SMLoc Loc) { MCStreamer::EmitWinCFISetFrame(Register, Offset, Loc); OS << "\t.seh_setframe "; InstPrinter->printRegName(OS, Register); OS << ", " << Offset; EmitEOL(); } void MCAsmStreamer::EmitWinCFIAllocStack(unsigned Size, SMLoc Loc) { MCStreamer::EmitWinCFIAllocStack(Size, Loc); OS << "\t.seh_stackalloc " << Size; EmitEOL(); } void MCAsmStreamer::EmitWinCFISaveReg(MCRegister Register, unsigned Offset, SMLoc Loc) { MCStreamer::EmitWinCFISaveReg(Register, Offset, Loc); OS << "\t.seh_savereg "; InstPrinter->printRegName(OS, Register); OS << ", " << Offset; EmitEOL(); } void MCAsmStreamer::EmitWinCFISaveXMM(MCRegister Register, unsigned Offset, SMLoc Loc) { MCStreamer::EmitWinCFISaveXMM(Register, Offset, Loc); OS << "\t.seh_savexmm "; InstPrinter->printRegName(OS, Register); OS << ", " << Offset; EmitEOL(); } void MCAsmStreamer::EmitWinCFIPushFrame(bool Code, SMLoc Loc) { MCStreamer::EmitWinCFIPushFrame(Code, Loc); OS << "\t.seh_pushframe"; if (Code) OS << " @code"; EmitEOL(); } void MCAsmStreamer::EmitWinCFIEndProlog(SMLoc Loc) { MCStreamer::EmitWinCFIEndProlog(Loc); OS << "\t.seh_endprologue"; EmitEOL(); } void MCAsmStreamer::emitCGProfileEntry(const MCSymbolRefExpr *From, const MCSymbolRefExpr *To, uint64_t Count) { OS << "\t.cg_profile "; From->getSymbol().print(OS, MAI); OS << ", "; To->getSymbol().print(OS, MAI); OS << ", " << Count; EmitEOL(); } void MCAsmStreamer::AddEncodingComment(const MCInst &Inst, const MCSubtargetInfo &STI) { raw_ostream &OS = GetCommentOS(); SmallString<256> Code; SmallVector Fixups; raw_svector_ostream VecOS(Code); // If we have no code emitter, don't emit code. if (!getAssembler().getEmitterPtr()) return; getAssembler().getEmitter().encodeInstruction(Inst, VecOS, Fixups, STI); // If we are showing fixups, create symbolic markers in the encoded // representation. We do this by making a per-bit map to the fixup item index, // then trying to display it as nicely as possible. SmallVector FixupMap; FixupMap.resize(Code.size() * 8); for (unsigned i = 0, e = Code.size() * 8; i != e; ++i) FixupMap[i] = 0; for (unsigned i = 0, e = Fixups.size(); i != e; ++i) { MCFixup &F = Fixups[i]; const MCFixupKindInfo &Info = getAssembler().getBackend().getFixupKindInfo(F.getKind()); for (unsigned j = 0; j != Info.TargetSize; ++j) { unsigned Index = F.getOffset() * 8 + Info.TargetOffset + j; assert(Index < Code.size() * 8 && "Invalid offset in fixup!"); FixupMap[Index] = 1 + i; } } // FIXME: Note the fixup comments for Thumb2 are completely bogus since the // high order halfword of a 32-bit Thumb2 instruction is emitted first. OS << "encoding: ["; for (unsigned i = 0, e = Code.size(); i != e; ++i) { if (i) OS << ','; // See if all bits are the same map entry. uint8_t MapEntry = FixupMap[i * 8 + 0]; for (unsigned j = 1; j != 8; ++j) { if (FixupMap[i * 8 + j] == MapEntry) continue; MapEntry = uint8_t(~0U); break; } if (MapEntry != uint8_t(~0U)) { if (MapEntry == 0) { OS << format("0x%02x", uint8_t(Code[i])); } else { if (Code[i]) { // FIXME: Some of the 8 bits require fix up. OS << format("0x%02x", uint8_t(Code[i])) << '\'' << char('A' + MapEntry - 1) << '\''; } else OS << char('A' + MapEntry - 1); } } else { // Otherwise, write out in binary. OS << "0b"; for (unsigned j = 8; j--;) { unsigned Bit = (Code[i] >> j) & 1; unsigned FixupBit; if (MAI->isLittleEndian()) FixupBit = i * 8 + j; else FixupBit = i * 8 + (7-j); if (uint8_t MapEntry = FixupMap[FixupBit]) { assert(Bit == 0 && "Encoder wrote into fixed up bit!"); OS << char('A' + MapEntry - 1); } else OS << Bit; } } } OS << "]\n"; for (unsigned i = 0, e = Fixups.size(); i != e; ++i) { MCFixup &F = Fixups[i]; const MCFixupKindInfo &Info = getAssembler().getBackend().getFixupKindInfo(F.getKind()); OS << " fixup " << char('A' + i) << " - " << "offset: " << F.getOffset() << ", value: " << *F.getValue() << ", kind: " << Info.Name << "\n"; } } void MCAsmStreamer::emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI) { assert(getCurrentSectionOnly() && "Cannot emit contents before setting section!"); // Show the encoding in a comment if we have a code emitter. AddEncodingComment(Inst, STI); // Show the MCInst if enabled. if (ShowInst) { Inst.dump_pretty(GetCommentOS(), InstPrinter.get(), "\n "); GetCommentOS() << "\n"; } if(getTargetStreamer()) getTargetStreamer()->prettyPrintAsm(*InstPrinter, 0, Inst, STI, OS); else InstPrinter->printInst(&Inst, 0, "", STI, OS); StringRef Comments = CommentToEmit; if (Comments.size() && Comments.back() != '\n') GetCommentOS() << "\n"; EmitEOL(); } void MCAsmStreamer::emitBundleAlignMode(unsigned AlignPow2) { OS << "\t.bundle_align_mode " << AlignPow2; EmitEOL(); } void MCAsmStreamer::emitBundleLock(bool AlignToEnd) { OS << "\t.bundle_lock"; if (AlignToEnd) OS << " align_to_end"; EmitEOL(); } void MCAsmStreamer::emitBundleUnlock() { OS << "\t.bundle_unlock"; EmitEOL(); } Optional> MCAsmStreamer::emitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc, const MCSubtargetInfo &STI) { OS << "\t.reloc "; Offset.print(OS, MAI); OS << ", " << Name; if (Expr) { OS << ", "; Expr->print(OS, MAI); } EmitEOL(); return None; } void MCAsmStreamer::emitAddrsig() { OS << "\t.addrsig"; EmitEOL(); } void MCAsmStreamer::emitAddrsigSym(const MCSymbol *Sym) { OS << "\t.addrsig_sym "; Sym->print(OS, MAI); EmitEOL(); } /// EmitRawText - If this file is backed by an assembly streamer, this dumps /// the specified string in the output .s file. This capability is /// indicated by the hasRawTextSupport() predicate. void MCAsmStreamer::emitRawTextImpl(StringRef String) { if (!String.empty() && String.back() == '\n') String = String.substr(0, String.size()-1); OS << String; EmitEOL(); } void MCAsmStreamer::finishImpl() { // If we are generating dwarf for assembly source files dump out the sections. if (getContext().getGenDwarfForAssembly()) MCGenDwarfInfo::Emit(this); // Emit the label for the line table, if requested - since the rest of the // line table will be defined by .loc/.file directives, and not emitted // directly, the label is the only work required here. const auto &Tables = getContext().getMCDwarfLineTables(); if (!Tables.empty()) { assert(Tables.size() == 1 && "asm output only supports one line table"); if (auto *Label = Tables.begin()->second.getLabel()) { SwitchSection(getContext().getObjectFileInfo()->getDwarfLineSection()); emitLabel(Label); } } } MCStreamer *llvm::createAsmStreamer(MCContext &Context, std::unique_ptr OS, bool isVerboseAsm, bool useDwarfDirectory, MCInstPrinter *IP, std::unique_ptr &&CE, std::unique_ptr &&MAB, bool ShowInst) { return new MCAsmStreamer(Context, std::move(OS), isVerboseAsm, useDwarfDirectory, IP, std::move(CE), std::move(MAB), ShowInst); } diff --git a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp index b670355a392b..926483451259 100644 --- a/llvm/lib/MC/MCParser/DarwinAsmParser.cpp +++ b/llvm/lib/MC/MCParser/DarwinAsmParser.cpp @@ -1,1208 +1,1209 @@ //===- DarwinAsmParser.cpp - Darwin (Mach-O) Assembly Parser --------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Triple.h" #include "llvm/ADT/Twine.h" #include "llvm/BinaryFormat/MachO.h" #include "llvm/MC/MCContext.h" #include "llvm/MC/MCDirectives.h" #include "llvm/MC/MCObjectFileInfo.h" #include "llvm/MC/MCParser/MCAsmLexer.h" #include "llvm/MC/MCParser/MCAsmParser.h" #include "llvm/MC/MCParser/MCAsmParserExtension.h" #include "llvm/MC/MCSectionMachO.h" #include "llvm/MC/MCStreamer.h" #include "llvm/MC/MCSymbol.h" #include "llvm/MC/SectionKind.h" #include "llvm/Support/FileSystem.h" #include "llvm/Support/MemoryBuffer.h" #include "llvm/Support/SMLoc.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/raw_ostream.h" #include #include #include #include #include #include using namespace llvm; namespace { /// Implementation of directive handling which is shared across all /// Darwin targets. class DarwinAsmParser : public MCAsmParserExtension { template void addDirectiveHandler(StringRef Directive) { MCAsmParser::ExtensionDirectiveHandler Handler = std::make_pair( this, HandleDirective); getParser().addDirectiveHandler(Directive, Handler); } bool parseSectionSwitch(StringRef Segment, StringRef Section, unsigned TAA = 0, unsigned ImplicitAlign = 0, unsigned StubSize = 0); SMLoc LastVersionDirective; public: DarwinAsmParser() = default; void Initialize(MCAsmParser &Parser) override { // Call the base implementation. this->MCAsmParserExtension::Initialize(Parser); addDirectiveHandler<&DarwinAsmParser::parseDirectiveAltEntry>(".alt_entry"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveDesc>(".desc"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveIndirectSymbol>( ".indirect_symbol"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveLsym>(".lsym"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveSubsectionsViaSymbols>( ".subsections_via_symbols"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveDumpOrLoad>(".dump"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveDumpOrLoad>(".load"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveSection>(".section"); addDirectiveHandler<&DarwinAsmParser::parseDirectivePushSection>( ".pushsection"); addDirectiveHandler<&DarwinAsmParser::parseDirectivePopSection>( ".popsection"); addDirectiveHandler<&DarwinAsmParser::parseDirectivePrevious>(".previous"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveSecureLogUnique>( ".secure_log_unique"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveSecureLogReset>( ".secure_log_reset"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveTBSS>(".tbss"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveZerofill>(".zerofill"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveDataRegion>( ".data_region"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveDataRegionEnd>( ".end_data_region"); // Special section directives. addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveBss>(".bss"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveConst>(".const"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveConstData>( ".const_data"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveConstructor>( ".constructor"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveCString>( ".cstring"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveData>(".data"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveDestructor>( ".destructor"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveDyld>(".dyld"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveFVMLibInit0>( ".fvmlib_init0"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveFVMLibInit1>( ".fvmlib_init1"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveLazySymbolPointers>( ".lazy_symbol_pointer"); addDirectiveHandler<&DarwinAsmParser::parseDirectiveLinkerOption>( ".linker_option"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveLiteral16>( ".literal16"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveLiteral4>( ".literal4"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveLiteral8>( ".literal8"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveModInitFunc>( ".mod_init_func"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveModTermFunc>( ".mod_term_func"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveNonLazySymbolPointers>( ".non_lazy_symbol_pointer"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveThreadLocalVariablePointers>( ".thread_local_variable_pointer"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCCatClsMeth>( ".objc_cat_cls_meth"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCCatInstMeth>( ".objc_cat_inst_meth"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCCategory>( ".objc_category"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCClass>( ".objc_class"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCClassNames>( ".objc_class_names"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCClassVars>( ".objc_class_vars"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCClsMeth>( ".objc_cls_meth"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCClsRefs>( ".objc_cls_refs"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCInstMeth>( ".objc_inst_meth"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveObjCInstanceVars>( ".objc_instance_vars"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCMessageRefs>( ".objc_message_refs"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCMetaClass>( ".objc_meta_class"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveObjCMethVarNames>( ".objc_meth_var_names"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveObjCMethVarTypes>( ".objc_meth_var_types"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCModuleInfo>( ".objc_module_info"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCProtocol>( ".objc_protocol"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveObjCSelectorStrs>( ".objc_selector_strs"); addDirectiveHandler< &DarwinAsmParser::parseSectionDirectiveObjCStringObject>( ".objc_string_object"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveObjCSymbols>( ".objc_symbols"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectivePICSymbolStub>( ".picsymbol_stub"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveStaticConst>( ".static_const"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveStaticData>( ".static_data"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveSymbolStub>( ".symbol_stub"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveTData>(".tdata"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveText>(".text"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveThreadInitFunc>( ".thread_init_func"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveTLV>(".tlv"); addDirectiveHandler<&DarwinAsmParser::parseSectionDirectiveIdent>(".ident"); addDirectiveHandler<&DarwinAsmParser::parseWatchOSVersionMin>( ".watchos_version_min"); addDirectiveHandler<&DarwinAsmParser::parseTvOSVersionMin>( ".tvos_version_min"); addDirectiveHandler<&DarwinAsmParser::parseIOSVersionMin>( ".ios_version_min"); addDirectiveHandler<&DarwinAsmParser::parseMacOSXVersionMin>( ".macosx_version_min"); addDirectiveHandler<&DarwinAsmParser::parseBuildVersion>(".build_version"); LastVersionDirective = SMLoc(); } bool parseDirectiveAltEntry(StringRef, SMLoc); bool parseDirectiveDesc(StringRef, SMLoc); bool parseDirectiveIndirectSymbol(StringRef, SMLoc); bool parseDirectiveDumpOrLoad(StringRef, SMLoc); bool parseDirectiveLsym(StringRef, SMLoc); bool parseDirectiveLinkerOption(StringRef, SMLoc); bool parseDirectiveSection(StringRef, SMLoc); bool parseDirectivePushSection(StringRef, SMLoc); bool parseDirectivePopSection(StringRef, SMLoc); bool parseDirectivePrevious(StringRef, SMLoc); bool parseDirectiveSecureLogReset(StringRef, SMLoc); bool parseDirectiveSecureLogUnique(StringRef, SMLoc); bool parseDirectiveSubsectionsViaSymbols(StringRef, SMLoc); bool parseDirectiveTBSS(StringRef, SMLoc); bool parseDirectiveZerofill(StringRef, SMLoc); bool parseDirectiveDataRegion(StringRef, SMLoc); bool parseDirectiveDataRegionEnd(StringRef, SMLoc); // Named Section Directive bool parseSectionDirectiveBss(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__bss"); } bool parseSectionDirectiveConst(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__const"); } bool parseSectionDirectiveStaticConst(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__static_const"); } bool parseSectionDirectiveCString(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__cstring", MachO::S_CSTRING_LITERALS); } bool parseSectionDirectiveLiteral4(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__literal4", MachO::S_4BYTE_LITERALS, 4); } bool parseSectionDirectiveLiteral8(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__literal8", MachO::S_8BYTE_LITERALS, 8); } bool parseSectionDirectiveLiteral16(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__literal16", MachO::S_16BYTE_LITERALS, 16); } bool parseSectionDirectiveConstructor(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__constructor"); } bool parseSectionDirectiveDestructor(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__destructor"); } bool parseSectionDirectiveFVMLibInit0(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__fvmlib_init0"); } bool parseSectionDirectiveFVMLibInit1(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__fvmlib_init1"); } bool parseSectionDirectiveSymbolStub(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__symbol_stub", MachO::S_SYMBOL_STUBS | MachO::S_ATTR_PURE_INSTRUCTIONS, // FIXME: Different on PPC and ARM. 0, 16); } bool parseSectionDirectivePICSymbolStub(StringRef, SMLoc) { return parseSectionSwitch("__TEXT","__picsymbol_stub", MachO::S_SYMBOL_STUBS | MachO::S_ATTR_PURE_INSTRUCTIONS, 0, 26); } bool parseSectionDirectiveData(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__data"); } bool parseSectionDirectiveStaticData(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__static_data"); } bool parseSectionDirectiveNonLazySymbolPointers(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__nl_symbol_ptr", MachO::S_NON_LAZY_SYMBOL_POINTERS, 4); } bool parseSectionDirectiveLazySymbolPointers(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__la_symbol_ptr", MachO::S_LAZY_SYMBOL_POINTERS, 4); } bool parseSectionDirectiveThreadLocalVariablePointers(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__thread_ptr", MachO::S_THREAD_LOCAL_VARIABLE_POINTERS, 4); } bool parseSectionDirectiveDyld(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__dyld"); } bool parseSectionDirectiveModInitFunc(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__mod_init_func", MachO::S_MOD_INIT_FUNC_POINTERS, 4); } bool parseSectionDirectiveModTermFunc(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__mod_term_func", MachO::S_MOD_TERM_FUNC_POINTERS, 4); } bool parseSectionDirectiveConstData(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__const"); } bool parseSectionDirectiveObjCClass(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__class", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCMetaClass(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__meta_class", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCCatClsMeth(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__cat_cls_meth", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCCatInstMeth(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__cat_inst_meth", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCProtocol(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__protocol", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCStringObject(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__string_object", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCClsMeth(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__cls_meth", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCInstMeth(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__inst_meth", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCClsRefs(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__cls_refs", MachO::S_ATTR_NO_DEAD_STRIP | MachO::S_LITERAL_POINTERS, 4); } bool parseSectionDirectiveObjCMessageRefs(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__message_refs", MachO::S_ATTR_NO_DEAD_STRIP | MachO::S_LITERAL_POINTERS, 4); } bool parseSectionDirectiveObjCSymbols(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__symbols", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCCategory(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__category", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCClassVars(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__class_vars", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCInstanceVars(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__instance_vars", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCModuleInfo(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__module_info", MachO::S_ATTR_NO_DEAD_STRIP); } bool parseSectionDirectiveObjCClassNames(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__cstring", MachO::S_CSTRING_LITERALS); } bool parseSectionDirectiveObjCMethVarTypes(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__cstring", MachO::S_CSTRING_LITERALS); } bool parseSectionDirectiveObjCMethVarNames(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__cstring", MachO::S_CSTRING_LITERALS); } bool parseSectionDirectiveObjCSelectorStrs(StringRef, SMLoc) { return parseSectionSwitch("__OBJC", "__selector_strs", MachO::S_CSTRING_LITERALS); } bool parseSectionDirectiveTData(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__thread_data", MachO::S_THREAD_LOCAL_REGULAR); } bool parseSectionDirectiveText(StringRef, SMLoc) { return parseSectionSwitch("__TEXT", "__text", MachO::S_ATTR_PURE_INSTRUCTIONS); } bool parseSectionDirectiveTLV(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__thread_vars", MachO::S_THREAD_LOCAL_VARIABLES); } bool parseSectionDirectiveIdent(StringRef, SMLoc) { // Darwin silently ignores the .ident directive. getParser().eatToEndOfStatement(); return false; } bool parseSectionDirectiveThreadInitFunc(StringRef, SMLoc) { return parseSectionSwitch("__DATA", "__thread_init", MachO::S_THREAD_LOCAL_INIT_FUNCTION_POINTERS); } bool parseWatchOSVersionMin(StringRef Directive, SMLoc Loc) { return parseVersionMin(Directive, Loc, MCVM_WatchOSVersionMin); } bool parseTvOSVersionMin(StringRef Directive, SMLoc Loc) { return parseVersionMin(Directive, Loc, MCVM_TvOSVersionMin); } bool parseIOSVersionMin(StringRef Directive, SMLoc Loc) { return parseVersionMin(Directive, Loc, MCVM_IOSVersionMin); } bool parseMacOSXVersionMin(StringRef Directive, SMLoc Loc) { return parseVersionMin(Directive, Loc, MCVM_OSXVersionMin); } bool parseBuildVersion(StringRef Directive, SMLoc Loc); bool parseVersionMin(StringRef Directive, SMLoc Loc, MCVersionMinType Type); bool parseMajorMinorVersionComponent(unsigned *Major, unsigned *Minor, const char *VersionName); bool parseOptionalTrailingVersionComponent(unsigned *Component, const char *ComponentName); bool parseVersion(unsigned *Major, unsigned *Minor, unsigned *Update); bool parseSDKVersion(VersionTuple &SDKVersion); void checkVersion(StringRef Directive, StringRef Arg, SMLoc Loc, Triple::OSType ExpectedOS); }; } // end anonymous namespace bool DarwinAsmParser::parseSectionSwitch(StringRef Segment, StringRef Section, unsigned TAA, unsigned Align, unsigned StubSize) { if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in section switching directive"); Lex(); // FIXME: Arch specific. bool isText = TAA & MachO::S_ATTR_PURE_INSTRUCTIONS; getStreamer().SwitchSection(getContext().getMachOSection( Segment, Section, TAA, StubSize, isText ? SectionKind::getText() : SectionKind::getData())); // Set the implicit alignment, if any. // // FIXME: This isn't really what 'as' does; I think it just uses the implicit // alignment on the section (e.g., if one manually inserts bytes into the // section, then just issuing the section switch directive will not realign // the section. However, this is arguably more reasonable behavior, and there // is no good reason for someone to intentionally emit incorrectly sized // values into the implicitly aligned sections. if (Align) getStreamer().emitValueToAlignment(Align); return false; } /// parseDirectiveAltEntry /// ::= .alt_entry identifier bool DarwinAsmParser::parseDirectiveAltEntry(StringRef, SMLoc) { StringRef Name; if (getParser().parseIdentifier(Name)) return TokError("expected identifier in directive"); // Look up symbol. MCSymbol *Sym = getContext().getOrCreateSymbol(Name); if (Sym->isDefined()) return TokError(".alt_entry must preceed symbol definition"); if (!getStreamer().emitSymbolAttribute(Sym, MCSA_AltEntry)) return TokError("unable to emit symbol attribute"); Lex(); return false; } /// parseDirectiveDesc /// ::= .desc identifier , expression bool DarwinAsmParser::parseDirectiveDesc(StringRef, SMLoc) { StringRef Name; if (getParser().parseIdentifier(Name)) return TokError("expected identifier in directive"); // Handle the identifier as the key symbol. MCSymbol *Sym = getContext().getOrCreateSymbol(Name); if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in '.desc' directive"); Lex(); int64_t DescValue; if (getParser().parseAbsoluteExpression(DescValue)) return true; if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.desc' directive"); Lex(); // Set the n_desc field of this Symbol to this DescValue getStreamer().emitSymbolDesc(Sym, DescValue); return false; } /// parseDirectiveIndirectSymbol /// ::= .indirect_symbol identifier bool DarwinAsmParser::parseDirectiveIndirectSymbol(StringRef, SMLoc Loc) { const MCSectionMachO *Current = static_cast( getStreamer().getCurrentSectionOnly()); MachO::SectionType SectionType = Current->getType(); if (SectionType != MachO::S_NON_LAZY_SYMBOL_POINTERS && SectionType != MachO::S_LAZY_SYMBOL_POINTERS && SectionType != MachO::S_THREAD_LOCAL_VARIABLE_POINTERS && SectionType != MachO::S_SYMBOL_STUBS) return Error(Loc, "indirect symbol not in a symbol pointer or stub " "section"); StringRef Name; if (getParser().parseIdentifier(Name)) return TokError("expected identifier in .indirect_symbol directive"); MCSymbol *Sym = getContext().getOrCreateSymbol(Name); // Assembler local symbols don't make any sense here. Complain loudly. if (Sym->isTemporary()) return TokError("non-local symbol required in directive"); if (!getStreamer().emitSymbolAttribute(Sym, MCSA_IndirectSymbol)) return TokError("unable to emit indirect symbol attribute for: " + Name); if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.indirect_symbol' directive"); Lex(); return false; } /// parseDirectiveDumpOrLoad /// ::= ( .dump | .load ) "filename" bool DarwinAsmParser::parseDirectiveDumpOrLoad(StringRef Directive, SMLoc IDLoc) { bool IsDump = Directive == ".dump"; if (getLexer().isNot(AsmToken::String)) return TokError("expected string in '.dump' or '.load' directive"); Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.dump' or '.load' directive"); Lex(); // FIXME: If/when .dump and .load are implemented they will be done in the // the assembly parser and not have any need for an MCStreamer API. if (IsDump) return Warning(IDLoc, "ignoring directive .dump for now"); else return Warning(IDLoc, "ignoring directive .load for now"); } /// ParseDirectiveLinkerOption /// ::= .linker_option "string" ( , "string" )* bool DarwinAsmParser::parseDirectiveLinkerOption(StringRef IDVal, SMLoc) { SmallVector Args; while (true) { if (getLexer().isNot(AsmToken::String)) return TokError("expected string in '" + Twine(IDVal) + "' directive"); std::string Data; if (getParser().parseEscapedString(Data)) return true; Args.push_back(Data); if (getLexer().is(AsmToken::EndOfStatement)) break; if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in '" + Twine(IDVal) + "' directive"); Lex(); } getStreamer().emitLinkerOptions(Args); return false; } /// parseDirectiveLsym /// ::= .lsym identifier , expression bool DarwinAsmParser::parseDirectiveLsym(StringRef, SMLoc) { StringRef Name; if (getParser().parseIdentifier(Name)) return TokError("expected identifier in directive"); // Handle the identifier as the key symbol. MCSymbol *Sym = getContext().getOrCreateSymbol(Name); if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in '.lsym' directive"); Lex(); const MCExpr *Value; if (getParser().parseExpression(Value)) return true; if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.lsym' directive"); Lex(); // We don't currently support this directive. // // FIXME: Diagnostic location! (void) Sym; return TokError("directive '.lsym' is unsupported"); } /// parseDirectiveSection: /// ::= .section identifier (',' identifier)* bool DarwinAsmParser::parseDirectiveSection(StringRef, SMLoc) { SMLoc Loc = getLexer().getLoc(); StringRef SectionName; if (getParser().parseIdentifier(SectionName)) return Error(Loc, "expected identifier after '.section' directive"); // Verify there is a following comma. if (!getLexer().is(AsmToken::Comma)) return TokError("unexpected token in '.section' directive"); std::string SectionSpec = std::string(SectionName); SectionSpec += ","; // Add all the tokens until the end of the line, ParseSectionSpecifier will // handle this. StringRef EOL = getLexer().LexUntilEndOfStatement(); SectionSpec.append(EOL.begin(), EOL.end()); Lex(); if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.section' directive"); Lex(); StringRef Segment, Section; unsigned StubSize; unsigned TAA; bool TAAParsed; std::string ErrorStr = MCSectionMachO::ParseSectionSpecifier(SectionSpec, Segment, Section, TAA, TAAParsed, StubSize); if (!ErrorStr.empty()) return Error(Loc, ErrorStr); // Issue a warning if the target is not powerpc and Section is a *coal* section. Triple TT = getParser().getContext().getObjectFileInfo()->getTargetTriple(); Triple::ArchType ArchTy = TT.getArch(); if (ArchTy != Triple::ppc && ArchTy != Triple::ppc64) { StringRef NonCoalSection = StringSwitch(Section) .Case("__textcoal_nt", "__text") .Case("__const_coal", "__const") .Case("__datacoal_nt", "__data") .Default(Section); if (!Section.equals(NonCoalSection)) { StringRef SectionVal(Loc.getPointer()); size_t B = SectionVal.find(',') + 1, E = SectionVal.find(',', B); SMLoc BLoc = SMLoc::getFromPointer(SectionVal.data() + B); SMLoc ELoc = SMLoc::getFromPointer(SectionVal.data() + E); getParser().Warning(Loc, "section \"" + Section + "\" is deprecated", SMRange(BLoc, ELoc)); getParser().Note(Loc, "change section name to \"" + NonCoalSection + "\"", SMRange(BLoc, ELoc)); } } // FIXME: Arch specific. bool isText = Segment == "__TEXT"; // FIXME: Hack. getStreamer().SwitchSection(getContext().getMachOSection( Segment, Section, TAA, StubSize, isText ? SectionKind::getText() : SectionKind::getData())); return false; } /// ParseDirectivePushSection: /// ::= .pushsection identifier (',' identifier)* bool DarwinAsmParser::parseDirectivePushSection(StringRef S, SMLoc Loc) { getStreamer().PushSection(); if (parseDirectiveSection(S, Loc)) { getStreamer().PopSection(); return true; } return false; } /// ParseDirectivePopSection: /// ::= .popsection bool DarwinAsmParser::parseDirectivePopSection(StringRef, SMLoc) { if (!getStreamer().PopSection()) return TokError(".popsection without corresponding .pushsection"); return false; } /// ParseDirectivePrevious: /// ::= .previous bool DarwinAsmParser::parseDirectivePrevious(StringRef DirName, SMLoc) { MCSectionSubPair PreviousSection = getStreamer().getPreviousSection(); if (!PreviousSection.first) return TokError(".previous without corresponding .section"); getStreamer().SwitchSection(PreviousSection.first, PreviousSection.second); return false; } /// ParseDirectiveSecureLogUnique /// ::= .secure_log_unique ... message ... bool DarwinAsmParser::parseDirectiveSecureLogUnique(StringRef, SMLoc IDLoc) { StringRef LogMessage = getParser().parseStringToEndOfStatement(); if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.secure_log_unique' directive"); if (getContext().getSecureLogUsed()) return Error(IDLoc, ".secure_log_unique specified multiple times"); // Get the secure log path. const char *SecureLogFile = getContext().getSecureLogFile(); if (!SecureLogFile) return Error(IDLoc, ".secure_log_unique used but AS_SECURE_LOG_FILE " "environment variable unset."); // Open the secure log file if we haven't already. raw_fd_ostream *OS = getContext().getSecureLog(); if (!OS) { std::error_code EC; auto NewOS = std::make_unique( StringRef(SecureLogFile), EC, sys::fs::OF_Append | sys::fs::OF_Text); if (EC) return Error(IDLoc, Twine("can't open secure log file: ") + SecureLogFile + " (" + EC.message() + ")"); OS = NewOS.get(); getContext().setSecureLog(std::move(NewOS)); } // Write the message. unsigned CurBuf = getSourceManager().FindBufferContainingLoc(IDLoc); *OS << getSourceManager().getBufferInfo(CurBuf).Buffer->getBufferIdentifier() << ":" << getSourceManager().FindLineNumber(IDLoc, CurBuf) << ":" << LogMessage + "\n"; getContext().setSecureLogUsed(true); return false; } /// ParseDirectiveSecureLogReset /// ::= .secure_log_reset bool DarwinAsmParser::parseDirectiveSecureLogReset(StringRef, SMLoc IDLoc) { if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.secure_log_reset' directive"); Lex(); getContext().setSecureLogUsed(false); return false; } /// parseDirectiveSubsectionsViaSymbols /// ::= .subsections_via_symbols bool DarwinAsmParser::parseDirectiveSubsectionsViaSymbols(StringRef, SMLoc) { if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.subsections_via_symbols' directive"); Lex(); getStreamer().emitAssemblerFlag(MCAF_SubsectionsViaSymbols); return false; } /// ParseDirectiveTBSS /// ::= .tbss identifier, size, align bool DarwinAsmParser::parseDirectiveTBSS(StringRef, SMLoc) { SMLoc IDLoc = getLexer().getLoc(); StringRef Name; if (getParser().parseIdentifier(Name)) return TokError("expected identifier in directive"); // Handle the identifier as the key symbol. MCSymbol *Sym = getContext().getOrCreateSymbol(Name); if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in directive"); Lex(); int64_t Size; SMLoc SizeLoc = getLexer().getLoc(); if (getParser().parseAbsoluteExpression(Size)) return true; int64_t Pow2Alignment = 0; SMLoc Pow2AlignmentLoc; if (getLexer().is(AsmToken::Comma)) { Lex(); Pow2AlignmentLoc = getLexer().getLoc(); if (getParser().parseAbsoluteExpression(Pow2Alignment)) return true; } if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.tbss' directive"); Lex(); if (Size < 0) return Error(SizeLoc, "invalid '.tbss' directive size, can't be less than" "zero"); // FIXME: Diagnose overflow. if (Pow2Alignment < 0) return Error(Pow2AlignmentLoc, "invalid '.tbss' alignment, can't be less" "than zero"); if (!Sym->isUndefined()) return Error(IDLoc, "invalid symbol redefinition"); getStreamer().emitTBSSSymbol( getContext().getMachOSection("__DATA", "__thread_bss", MachO::S_THREAD_LOCAL_ZEROFILL, 0, SectionKind::getThreadBSS()), Sym, Size, 1 << Pow2Alignment); return false; } /// ParseDirectiveZerofill /// ::= .zerofill segname , sectname [, identifier , size_expression [ /// , align_expression ]] bool DarwinAsmParser::parseDirectiveZerofill(StringRef, SMLoc) { StringRef Segment; if (getParser().parseIdentifier(Segment)) return TokError("expected segment name after '.zerofill' directive"); if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in directive"); Lex(); StringRef Section; SMLoc SectionLoc = getLexer().getLoc(); if (getParser().parseIdentifier(Section)) return TokError("expected section name after comma in '.zerofill' " "directive"); // If this is the end of the line all that was wanted was to create the // the section but with no symbol. if (getLexer().is(AsmToken::EndOfStatement)) { // Create the zerofill section but no symbol getStreamer().emitZerofill( getContext().getMachOSection(Segment, Section, MachO::S_ZEROFILL, 0, SectionKind::getBSS()), /*Symbol=*/nullptr, /*Size=*/0, /*ByteAlignment=*/0, SectionLoc); return false; } if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in directive"); Lex(); SMLoc IDLoc = getLexer().getLoc(); StringRef IDStr; if (getParser().parseIdentifier(IDStr)) return TokError("expected identifier in directive"); // handle the identifier as the key symbol. MCSymbol *Sym = getContext().getOrCreateSymbol(IDStr); if (getLexer().isNot(AsmToken::Comma)) return TokError("unexpected token in directive"); Lex(); int64_t Size; SMLoc SizeLoc = getLexer().getLoc(); if (getParser().parseAbsoluteExpression(Size)) return true; int64_t Pow2Alignment = 0; SMLoc Pow2AlignmentLoc; if (getLexer().is(AsmToken::Comma)) { Lex(); Pow2AlignmentLoc = getLexer().getLoc(); if (getParser().parseAbsoluteExpression(Pow2Alignment)) return true; } if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.zerofill' directive"); Lex(); if (Size < 0) return Error(SizeLoc, "invalid '.zerofill' directive size, can't be less " "than zero"); // NOTE: The alignment in the directive is a power of 2 value, the assembler // may internally end up wanting an alignment in bytes. // FIXME: Diagnose overflow. if (Pow2Alignment < 0) return Error(Pow2AlignmentLoc, "invalid '.zerofill' directive alignment, " "can't be less than zero"); if (!Sym->isUndefined()) return Error(IDLoc, "invalid symbol redefinition"); // Create the zerofill Symbol with Size and Pow2Alignment // // FIXME: Arch specific. getStreamer().emitZerofill(getContext().getMachOSection( Segment, Section, MachO::S_ZEROFILL, 0, SectionKind::getBSS()), Sym, Size, 1 << Pow2Alignment, SectionLoc); return false; } /// ParseDirectiveDataRegion /// ::= .data_region [ ( jt8 | jt16 | jt32 ) ] bool DarwinAsmParser::parseDirectiveDataRegion(StringRef, SMLoc) { if (getLexer().is(AsmToken::EndOfStatement)) { Lex(); getStreamer().emitDataRegion(MCDR_DataRegion); return false; } StringRef RegionType; SMLoc Loc = getParser().getTok().getLoc(); if (getParser().parseIdentifier(RegionType)) return TokError("expected region type after '.data_region' directive"); int Kind = StringSwitch(RegionType) .Case("jt8", MCDR_DataRegionJT8) .Case("jt16", MCDR_DataRegionJT16) .Case("jt32", MCDR_DataRegionJT32) .Default(-1); if (Kind == -1) return Error(Loc, "unknown region type in '.data_region' directive"); Lex(); getStreamer().emitDataRegion((MCDataRegionType)Kind); return false; } /// ParseDirectiveDataRegionEnd /// ::= .end_data_region bool DarwinAsmParser::parseDirectiveDataRegionEnd(StringRef, SMLoc) { if (getLexer().isNot(AsmToken::EndOfStatement)) return TokError("unexpected token in '.end_data_region' directive"); Lex(); getStreamer().emitDataRegion(MCDR_DataRegionEnd); return false; } static bool isSDKVersionToken(const AsmToken &Tok) { return Tok.is(AsmToken::Identifier) && Tok.getIdentifier() == "sdk_version"; } /// parseMajorMinorVersionComponent ::= major, minor bool DarwinAsmParser::parseMajorMinorVersionComponent(unsigned *Major, unsigned *Minor, const char *VersionName) { // Get the major version number. if (getLexer().isNot(AsmToken::Integer)) return TokError(Twine("invalid ") + VersionName + " major version number, integer expected"); int64_t MajorVal = getLexer().getTok().getIntVal(); if (MajorVal > 65535 || MajorVal <= 0) return TokError(Twine("invalid ") + VersionName + " major version number"); *Major = (unsigned)MajorVal; Lex(); if (getLexer().isNot(AsmToken::Comma)) return TokError(Twine(VersionName) + " minor version number required, comma expected"); Lex(); // Get the minor version number. if (getLexer().isNot(AsmToken::Integer)) return TokError(Twine("invalid ") + VersionName + " minor version number, integer expected"); int64_t MinorVal = getLexer().getTok().getIntVal(); if (MinorVal > 255 || MinorVal < 0) return TokError(Twine("invalid ") + VersionName + " minor version number"); *Minor = MinorVal; Lex(); return false; } /// parseOptionalTrailingVersionComponent ::= , version_number bool DarwinAsmParser::parseOptionalTrailingVersionComponent( unsigned *Component, const char *ComponentName) { assert(getLexer().is(AsmToken::Comma) && "comma expected"); Lex(); if (getLexer().isNot(AsmToken::Integer)) return TokError(Twine("invalid ") + ComponentName + " version number, integer expected"); int64_t Val = getLexer().getTok().getIntVal(); if (Val > 255 || Val < 0) return TokError(Twine("invalid ") + ComponentName + " version number"); *Component = Val; Lex(); return false; } /// parseVersion ::= parseMajorMinorVersionComponent /// parseOptionalTrailingVersionComponent bool DarwinAsmParser::parseVersion(unsigned *Major, unsigned *Minor, unsigned *Update) { if (parseMajorMinorVersionComponent(Major, Minor, "OS")) return true; // Get the update level, if specified *Update = 0; if (getLexer().is(AsmToken::EndOfStatement) || isSDKVersionToken(getLexer().getTok())) return false; if (getLexer().isNot(AsmToken::Comma)) return TokError("invalid OS update specifier, comma expected"); if (parseOptionalTrailingVersionComponent(Update, "OS update")) return true; return false; } bool DarwinAsmParser::parseSDKVersion(VersionTuple &SDKVersion) { assert(isSDKVersionToken(getLexer().getTok()) && "expected sdk_version"); Lex(); unsigned Major, Minor; if (parseMajorMinorVersionComponent(&Major, &Minor, "SDK")) return true; SDKVersion = VersionTuple(Major, Minor); // Get the subminor version, if specified. if (getLexer().is(AsmToken::Comma)) { unsigned Subminor; if (parseOptionalTrailingVersionComponent(&Subminor, "SDK subminor")) return true; SDKVersion = VersionTuple(Major, Minor, Subminor); } return false; } void DarwinAsmParser::checkVersion(StringRef Directive, StringRef Arg, SMLoc Loc, Triple::OSType ExpectedOS) { const Triple &Target = getContext().getObjectFileInfo()->getTargetTriple(); if (Target.getOS() != ExpectedOS) Warning(Loc, Twine(Directive) + (Arg.empty() ? Twine() : Twine(' ') + Arg) + " used while targeting " + Target.getOSName()); if (LastVersionDirective.isValid()) { Warning(Loc, "overriding previous version directive"); Note(LastVersionDirective, "previous definition is here"); } LastVersionDirective = Loc; } static Triple::OSType getOSTypeFromMCVM(MCVersionMinType Type) { switch (Type) { case MCVM_WatchOSVersionMin: return Triple::WatchOS; case MCVM_TvOSVersionMin: return Triple::TvOS; case MCVM_IOSVersionMin: return Triple::IOS; case MCVM_OSXVersionMin: return Triple::MacOSX; } llvm_unreachable("Invalid mc version min type"); } /// parseVersionMin /// ::= .ios_version_min parseVersion parseSDKVersion /// | .macosx_version_min parseVersion parseSDKVersion /// | .tvos_version_min parseVersion parseSDKVersion /// | .watchos_version_min parseVersion parseSDKVersion bool DarwinAsmParser::parseVersionMin(StringRef Directive, SMLoc Loc, MCVersionMinType Type) { unsigned Major; unsigned Minor; unsigned Update; if (parseVersion(&Major, &Minor, &Update)) return true; VersionTuple SDKVersion; if (isSDKVersionToken(getLexer().getTok()) && parseSDKVersion(SDKVersion)) return true; if (parseToken(AsmToken::EndOfStatement)) return addErrorSuffix(Twine(" in '") + Directive + "' directive"); Triple::OSType ExpectedOS = getOSTypeFromMCVM(Type); checkVersion(Directive, StringRef(), Loc, ExpectedOS); getStreamer().emitVersionMin(Type, Major, Minor, Update, SDKVersion); return false; } static Triple::OSType getOSTypeFromPlatform(MachO::PlatformType Type) { switch (Type) { case MachO::PLATFORM_MACOS: return Triple::MacOSX; case MachO::PLATFORM_IOS: return Triple::IOS; case MachO::PLATFORM_TVOS: return Triple::TvOS; case MachO::PLATFORM_WATCHOS: return Triple::WatchOS; case MachO::PLATFORM_BRIDGEOS: /* silence warning */ break; case MachO::PLATFORM_MACCATALYST: return Triple::IOS; case MachO::PLATFORM_IOSSIMULATOR: /* silence warning */ break; case MachO::PLATFORM_TVOSSIMULATOR: /* silence warning */ break; case MachO::PLATFORM_WATCHOSSIMULATOR: /* silence warning */ break; + case MachO::PLATFORM_DRIVERKIT: /* silence warning */ break; } llvm_unreachable("Invalid mach-o platform type"); } /// parseBuildVersion /// ::= .build_version (macos|ios|tvos|watchos), parseVersion parseSDKVersion bool DarwinAsmParser::parseBuildVersion(StringRef Directive, SMLoc Loc) { StringRef PlatformName; SMLoc PlatformLoc = getTok().getLoc(); if (getParser().parseIdentifier(PlatformName)) return TokError("platform name expected"); unsigned Platform = StringSwitch(PlatformName) .Case("macos", MachO::PLATFORM_MACOS) .Case("ios", MachO::PLATFORM_IOS) .Case("tvos", MachO::PLATFORM_TVOS) .Case("watchos", MachO::PLATFORM_WATCHOS) .Case("macCatalyst", MachO::PLATFORM_MACCATALYST) .Default(0); if (Platform == 0) return Error(PlatformLoc, "unknown platform name"); if (getLexer().isNot(AsmToken::Comma)) return TokError("version number required, comma expected"); Lex(); unsigned Major; unsigned Minor; unsigned Update; if (parseVersion(&Major, &Minor, &Update)) return true; VersionTuple SDKVersion; if (isSDKVersionToken(getLexer().getTok()) && parseSDKVersion(SDKVersion)) return true; if (parseToken(AsmToken::EndOfStatement)) return addErrorSuffix(" in '.build_version' directive"); Triple::OSType ExpectedOS = getOSTypeFromPlatform((MachO::PlatformType)Platform); checkVersion(Directive, PlatformName, Loc, ExpectedOS); getStreamer().emitBuildVersion(Platform, Major, Minor, Update, SDKVersion); return false; } namespace llvm { MCAsmParserExtension *createDarwinAsmParser() { return new DarwinAsmParser; } } // end llvm namespace diff --git a/llvm/lib/TextAPI/MachO/Platform.cpp b/llvm/lib/TextAPI/MachO/Platform.cpp index 588ec9a4d83b..a13d569d6f23 100644 --- a/llvm/lib/TextAPI/MachO/Platform.cpp +++ b/llvm/lib/TextAPI/MachO/Platform.cpp @@ -1,91 +1,93 @@ //===- llvm/TextAPI/MachO/Platform.cpp - Platform ---------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Implementations of Platform Helper functions. // //===----------------------------------------------------------------------===// #include "llvm/ADT/ArrayRef.h" #include "llvm/ADT/Triple.h" #include "llvm/TextAPI/MachO/Platform.h" namespace llvm { namespace MachO { PlatformKind mapToPlatformKind(PlatformKind Platform, bool WantSim) { switch (Platform) { default: return Platform; case PlatformKind::iOS: return WantSim ? PlatformKind::iOSSimulator : PlatformKind::iOS; case PlatformKind::tvOS: return WantSim ? PlatformKind::tvOSSimulator : PlatformKind::tvOS; case PlatformKind::watchOS: return WantSim ? PlatformKind::watchOSSimulator : PlatformKind::watchOS; } llvm_unreachable("Unknown llvm.MachO.PlatformKind enum"); } PlatformKind mapToPlatformKind(const Triple &Target) { switch (Target.getOS()) { default: return PlatformKind::unknown; case Triple::MacOSX: return PlatformKind::macOS; case Triple::IOS: if (Target.isSimulatorEnvironment()) return PlatformKind::iOSSimulator; if (Target.getEnvironment() == Triple::MacABI) return PlatformKind::macCatalyst; return PlatformKind::iOS; case Triple::TvOS: return Target.isSimulatorEnvironment() ? PlatformKind::tvOSSimulator : PlatformKind::tvOS; case Triple::WatchOS: return Target.isSimulatorEnvironment() ? PlatformKind::watchOSSimulator : PlatformKind::watchOS; - // TODO: add bridgeOS once in llvm::Triple + // TODO: add bridgeOS & driverKit once in llvm::Triple } llvm_unreachable("Unknown Target Triple"); } PlatformSet mapToPlatformSet(ArrayRef Targets) { PlatformSet Result; for (const auto &Target : Targets) Result.insert(mapToPlatformKind(Target)); return Result; } StringRef getPlatformName(PlatformKind Platform) { switch (Platform) { case PlatformKind::unknown: return "unknown"; case PlatformKind::macOS: return "macOS"; case PlatformKind::iOS: return "iOS"; case PlatformKind::tvOS: return "tvOS"; case PlatformKind::watchOS: return "watchOS"; case PlatformKind::bridgeOS: return "bridgeOS"; case PlatformKind::macCatalyst: return "macCatalyst"; case PlatformKind::iOSSimulator: return "iOS Simulator"; case PlatformKind::tvOSSimulator: return "tvOS Simulator"; case PlatformKind::watchOSSimulator: return "watchOS Simulator"; + case PlatformKind::driverKit: + return "driverKit"; } llvm_unreachable("Unknown llvm.MachO.PlatformKind enum"); } } // end namespace MachO. } // end namespace llvm. diff --git a/llvm/lib/TextAPI/MachO/Target.cpp b/llvm/lib/TextAPI/MachO/Target.cpp index aee8ef421425..6f8d9bb4e19a 100644 --- a/llvm/lib/TextAPI/MachO/Target.cpp +++ b/llvm/lib/TextAPI/MachO/Target.cpp @@ -1,75 +1,76 @@ //===- tapi/Core/Target.cpp - Target ----------------------------*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "llvm/ADT/SmallString.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TextAPI/MachO/Target.h" namespace llvm { namespace MachO { Expected Target::create(StringRef TargetValue) { auto Result = TargetValue.split('-'); auto ArchitectureStr = Result.first; auto Architecture = getArchitectureFromName(ArchitectureStr); auto PlatformStr = Result.second; PlatformKind Platform; Platform = StringSwitch(PlatformStr) .Case("macos", PlatformKind::macOS) .Case("ios", PlatformKind::iOS) .Case("tvos", PlatformKind::tvOS) .Case("watchos", PlatformKind::watchOS) .Case("bridgeos", PlatformKind::bridgeOS) .Case("maccatalyst", PlatformKind::macCatalyst) .Case("ios-simulator", PlatformKind::iOSSimulator) .Case("tvos-simulator", PlatformKind::tvOSSimulator) .Case("watchos-simulator", PlatformKind::watchOSSimulator) + .Case("driverkit", PlatformKind::driverKit) .Default(PlatformKind::unknown); if (Platform == PlatformKind::unknown) { if (PlatformStr.startswith("<") && PlatformStr.endswith(">")) { PlatformStr = PlatformStr.drop_front().drop_back(); unsigned long long RawValue; if (!PlatformStr.getAsInteger(10, RawValue)) Platform = (PlatformKind)RawValue; } } return Target{Architecture, Platform}; } Target::operator std::string() const { return (getArchitectureName(Arch) + " (" + getPlatformName(Platform) + ")") .str(); } raw_ostream &operator<<(raw_ostream &OS, const Target &Target) { OS << std::string(Target); return OS; } PlatformSet mapToPlatformSet(ArrayRef Targets) { PlatformSet Result; for (const auto &Target : Targets) Result.insert(Target.Platform); return Result; } ArchitectureSet mapToArchitectureSet(ArrayRef Targets) { ArchitectureSet Result; for (const auto &Target : Targets) Result.set(Target.Arch); return Result; } } // end namespace MachO. } // end namespace llvm. diff --git a/llvm/lib/TextAPI/MachO/TextStub.cpp b/llvm/lib/TextAPI/MachO/TextStub.cpp index 141f897fb564..a212e8ffa620 100644 --- a/llvm/lib/TextAPI/MachO/TextStub.cpp +++ b/llvm/lib/TextAPI/MachO/TextStub.cpp @@ -1,1149 +1,1152 @@ //===- TextStub.cpp -------------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Implements the text stub file reader/writer. // //===----------------------------------------------------------------------===// #include "TextAPIContext.h" #include "TextStubCommon.h" #include "llvm/ADT/BitmaskEnum.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/YAMLTraits.h" #include "llvm/Support/raw_ostream.h" #include "llvm/TextAPI/MachO/Architecture.h" #include "llvm/TextAPI/MachO/ArchitectureSet.h" #include "llvm/TextAPI/MachO/InterfaceFile.h" #include "llvm/TextAPI/MachO/PackedVersion.h" #include "llvm/TextAPI/MachO/TextAPIReader.h" #include "llvm/TextAPI/MachO/TextAPIWriter.h" #include #include // clang-format off /* YAML Format specification. The TBD v1 format only support two level address libraries and is per definition application extension safe. --- # the tag !tapi-tbd-v1 is optional and # shouldn't be emitted to support older linker. archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are # supported by this file. platform: ios # Specifies the platform (macosx, ios, etc) install-name: /u/l/libfoo.dylib # current-version: 1.2.3 # Optional: defaults to 1.0 compatibility-version: 1.0 # Optional: defaults to 1.0 swift-version: 0 # Optional: defaults to 0 objc-constraint: none # Optional: defaults to none exports: # List of export sections ... Each export section is defined as following: - archs: [ arm64 ] # the list of architecture slices allowed-clients: [ client ] # Optional: List of clients re-exports: [ ] # Optional: List of re-exports symbols: [ _sym ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-ivars: [] # Optional: List of Objective C Instance # Variables weak-def-symbols: [] # Optional: List of weak defined symbols thread-local-symbols: [] # Optional: List of thread local symbols */ /* YAML Format specification. --- !tapi-tbd-v2 archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are # supported by this file. uuids: [ armv7:... ] # Optional: List of architecture and UUID pairs. platform: ios # Specifies the platform (macosx, ios, etc) flags: [] # Optional: install-name: /u/l/libfoo.dylib # current-version: 1.2.3 # Optional: defaults to 1.0 compatibility-version: 1.0 # Optional: defaults to 1.0 swift-version: 0 # Optional: defaults to 0 objc-constraint: retain_release # Optional: defaults to retain_release parent-umbrella: # Optional: exports: # List of export sections ... undefineds: # List of undefineds sections ... Each export section is defined as following: - archs: [ arm64 ] # the list of architecture slices allowed-clients: [ client ] # Optional: List of clients re-exports: [ ] # Optional: List of re-exports symbols: [ _sym ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-ivars: [] # Optional: List of Objective C Instance # Variables weak-def-symbols: [] # Optional: List of weak defined symbols thread-local-symbols: [] # Optional: List of thread local symbols Each undefineds section is defined as following: - archs: [ arm64 ] # the list of architecture slices symbols: [ _sym ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-ivars: [] # Optional: List of Objective C Instance Variables weak-ref-symbols: [] # Optional: List of weak defined symbols */ /* YAML Format specification. --- !tapi-tbd-v3 archs: [ armv7, armv7s, arm64 ] # the list of architecture slices that are # supported by this file. uuids: [ armv7:... ] # Optional: List of architecture and UUID pairs. platform: ios # Specifies the platform (macosx, ios, etc) flags: [] # Optional: install-name: /u/l/libfoo.dylib # current-version: 1.2.3 # Optional: defaults to 1.0 compatibility-version: 1.0 # Optional: defaults to 1.0 swift-abi-version: 0 # Optional: defaults to 0 objc-constraint: retain_release # Optional: defaults to retain_release parent-umbrella: # Optional: exports: # List of export sections ... undefineds: # List of undefineds sections ... Each export section is defined as following: - archs: [ arm64 ] # the list of architecture slices allowed-clients: [ client ] # Optional: List of clients re-exports: [ ] # Optional: List of re-exports symbols: [ _sym ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-eh-types: [] # Optional: List of Objective-C classes # with EH objc-ivars: [] # Optional: List of Objective C Instance # Variables weak-def-symbols: [] # Optional: List of weak defined symbols thread-local-symbols: [] # Optional: List of thread local symbols Each undefineds section is defined as following: - archs: [ arm64 ] # the list of architecture slices symbols: [ _sym ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-eh-types: [] # Optional: List of Objective-C classes # with EH objc-ivars: [] # Optional: List of Objective C Instance Variables weak-ref-symbols: [] # Optional: List of weak defined symbols */ /* YAML Format specification. --- !tapi-tbd tbd-version: 4 # The tbd version for format targets: [ armv7-ios, x86_64-maccatalyst ] # The list of applicable tapi supported target triples uuids: # Optional: List of target and UUID pairs. - target: armv7-ios value: ... - target: x86_64-maccatalyst value: ... flags: [] # Optional: install-name: /u/l/libfoo.dylib # current-version: 1.2.3 # Optional: defaults to 1.0 compatibility-version: 1.0 # Optional: defaults to 1.0 swift-abi-version: 0 # Optional: defaults to 0 parent-umbrella: # Optional: allowable-clients: - targets: [ armv7-ios ] # Optional: clients: [ clientA ] exports: # List of export sections ... re-exports: # List of reexport sections ... undefineds: # List of undefineds sections ... Each export and reexport section is defined as following: - targets: [ arm64-macos ] # The list of target triples associated with symbols symbols: [ _symA ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-eh-types: [] # Optional: List of Objective-C classes # with EH objc-ivars: [] # Optional: List of Objective C Instance # Variables weak-symbols: [] # Optional: List of weak defined symbols thread-local-symbols: [] # Optional: List of thread local symbols - targets: [ arm64-macos, x86_64-maccatalyst ] # Optional: Targets for applicable additional symbols symbols: [ _symB ] # Optional: List of symbols Each undefineds section is defined as following: - targets: [ arm64-macos ] # The list of target triples associated with symbols symbols: [ _symC ] # Optional: List of symbols objc-classes: [] # Optional: List of Objective-C classes objc-eh-types: [] # Optional: List of Objective-C classes # with EH objc-ivars: [] # Optional: List of Objective C Instance Variables weak-symbols: [] # Optional: List of weak defined symbols */ // clang-format on using namespace llvm; using namespace llvm::yaml; using namespace llvm::MachO; namespace { struct ExportSection { std::vector Architectures; std::vector AllowableClients; std::vector ReexportedLibraries; std::vector Symbols; std::vector Classes; std::vector ClassEHs; std::vector IVars; std::vector WeakDefSymbols; std::vector TLVSymbols; }; struct UndefinedSection { std::vector Architectures; std::vector Symbols; std::vector Classes; std::vector ClassEHs; std::vector IVars; std::vector WeakRefSymbols; }; // Sections for direct target mapping in TBDv4 struct SymbolSection { TargetList Targets; std::vector Symbols; std::vector Classes; std::vector ClassEHs; std::vector Ivars; std::vector WeakSymbols; std::vector TlvSymbols; }; struct MetadataSection { enum Option { Clients, Libraries }; std::vector Targets; std::vector Values; }; struct UmbrellaSection { std::vector Targets; std::string Umbrella; }; // UUID's for TBDv4 are mapped to target not arch struct UUIDv4 { Target TargetID; std::string Value; UUIDv4() = default; UUIDv4(const Target &TargetID, const std::string &Value) : TargetID(TargetID), Value(Value) {} }; // clang-format off enum TBDFlags : unsigned { None = 0U, FlatNamespace = 1U << 0, NotApplicationExtensionSafe = 1U << 1, InstallAPI = 1U << 2, LLVM_MARK_AS_BITMASK_ENUM(/*LargestValue=*/InstallAPI), }; // clang-format on } // end anonymous namespace. LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Architecture) LLVM_YAML_IS_SEQUENCE_VECTOR(ExportSection) LLVM_YAML_IS_SEQUENCE_VECTOR(UndefinedSection) // Specific to TBDv4 LLVM_YAML_IS_SEQUENCE_VECTOR(SymbolSection) LLVM_YAML_IS_SEQUENCE_VECTOR(MetadataSection) LLVM_YAML_IS_SEQUENCE_VECTOR(UmbrellaSection) LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(Target) LLVM_YAML_IS_SEQUENCE_VECTOR(UUIDv4) namespace llvm { namespace yaml { template <> struct MappingTraits { static void mapping(IO &IO, ExportSection &Section) { const auto *Ctx = reinterpret_cast(IO.getContext()); assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) && "File type is not set in YAML context"); IO.mapRequired("archs", Section.Architectures); if (Ctx->FileKind == FileType::TBD_V1) IO.mapOptional("allowed-clients", Section.AllowableClients); else IO.mapOptional("allowable-clients", Section.AllowableClients); IO.mapOptional("re-exports", Section.ReexportedLibraries); IO.mapOptional("symbols", Section.Symbols); IO.mapOptional("objc-classes", Section.Classes); if (Ctx->FileKind == FileType::TBD_V3) IO.mapOptional("objc-eh-types", Section.ClassEHs); IO.mapOptional("objc-ivars", Section.IVars); IO.mapOptional("weak-def-symbols", Section.WeakDefSymbols); IO.mapOptional("thread-local-symbols", Section.TLVSymbols); } }; template <> struct MappingTraits { static void mapping(IO &IO, UndefinedSection &Section) { const auto *Ctx = reinterpret_cast(IO.getContext()); assert((!Ctx || (Ctx && Ctx->FileKind != FileType::Invalid)) && "File type is not set in YAML context"); IO.mapRequired("archs", Section.Architectures); IO.mapOptional("symbols", Section.Symbols); IO.mapOptional("objc-classes", Section.Classes); if (Ctx->FileKind == FileType::TBD_V3) IO.mapOptional("objc-eh-types", Section.ClassEHs); IO.mapOptional("objc-ivars", Section.IVars); IO.mapOptional("weak-ref-symbols", Section.WeakRefSymbols); } }; template <> struct MappingTraits { static void mapping(IO &IO, SymbolSection &Section) { IO.mapRequired("targets", Section.Targets); IO.mapOptional("symbols", Section.Symbols); IO.mapOptional("objc-classes", Section.Classes); IO.mapOptional("objc-eh-types", Section.ClassEHs); IO.mapOptional("objc-ivars", Section.Ivars); IO.mapOptional("weak-symbols", Section.WeakSymbols); IO.mapOptional("thread-local-symbols", Section.TlvSymbols); } }; template <> struct MappingTraits { static void mapping(IO &IO, UmbrellaSection &Section) { IO.mapRequired("targets", Section.Targets); IO.mapRequired("umbrella", Section.Umbrella); } }; template <> struct MappingTraits { static void mapping(IO &IO, UUIDv4 &UUID) { IO.mapRequired("target", UUID.TargetID); IO.mapRequired("value", UUID.Value); } }; template <> struct MappingContextTraits { static void mapping(IO &IO, MetadataSection &Section, MetadataSection::Option &OptionKind) { IO.mapRequired("targets", Section.Targets); switch (OptionKind) { case MetadataSection::Option::Clients: IO.mapRequired("clients", Section.Values); return; case MetadataSection::Option::Libraries: IO.mapRequired("libraries", Section.Values); return; } llvm_unreachable("unexpected option for metadata"); } }; template <> struct ScalarBitSetTraits { static void bitset(IO &IO, TBDFlags &Flags) { IO.bitSetCase(Flags, "flat_namespace", TBDFlags::FlatNamespace); IO.bitSetCase(Flags, "not_app_extension_safe", TBDFlags::NotApplicationExtensionSafe); IO.bitSetCase(Flags, "installapi", TBDFlags::InstallAPI); } }; template <> struct ScalarTraits { static void output(const Target &Value, void *, raw_ostream &OS) { OS << Value.Arch << "-"; switch (Value.Platform) { default: OS << "unknown"; break; case PlatformKind::macOS: OS << "macos"; break; case PlatformKind::iOS: OS << "ios"; break; case PlatformKind::tvOS: OS << "tvos"; break; case PlatformKind::watchOS: OS << "watchos"; break; case PlatformKind::bridgeOS: OS << "bridgeos"; break; case PlatformKind::macCatalyst: OS << "maccatalyst"; break; case PlatformKind::iOSSimulator: OS << "ios-simulator"; break; case PlatformKind::tvOSSimulator: OS << "tvos-simulator"; break; case PlatformKind::watchOSSimulator: OS << "watchos-simulator"; break; + case PlatformKind::driverKit: + OS << "driverkit"; + break; } } static StringRef input(StringRef Scalar, void *, Target &Value) { auto Result = Target::create(Scalar); if (!Result) { consumeError(Result.takeError()); return "unparsable target"; } Value = *Result; if (Value.Arch == AK_unknown) return "unknown architecture"; if (Value.Platform == PlatformKind::unknown) return "unknown platform"; return {}; } static QuotingType mustQuote(StringRef) { return QuotingType::None; } }; template <> struct MappingTraits { struct NormalizedTBD { explicit NormalizedTBD(IO &IO) {} NormalizedTBD(IO &IO, const InterfaceFile *&File) { Architectures = File->getArchitectures(); UUIDs = File->uuids(); Platforms = File->getPlatforms(); InstallName = File->getInstallName(); CurrentVersion = PackedVersion(File->getCurrentVersion()); CompatibilityVersion = PackedVersion(File->getCompatibilityVersion()); SwiftABIVersion = File->getSwiftABIVersion(); ObjCConstraint = File->getObjCConstraint(); Flags = TBDFlags::None; if (!File->isApplicationExtensionSafe()) Flags |= TBDFlags::NotApplicationExtensionSafe; if (!File->isTwoLevelNamespace()) Flags |= TBDFlags::FlatNamespace; if (File->isInstallAPI()) Flags |= TBDFlags::InstallAPI; if (!File->umbrellas().empty()) ParentUmbrella = File->umbrellas().begin()->second; std::set ArchSet; for (const auto &Library : File->allowableClients()) ArchSet.insert(Library.getArchitectures()); for (const auto &Library : File->reexportedLibraries()) ArchSet.insert(Library.getArchitectures()); std::map SymbolToArchSet; for (const auto *Symbol : File->exports()) { auto Architectures = Symbol->getArchitectures(); SymbolToArchSet[Symbol] = Architectures; ArchSet.insert(Architectures); } for (auto Architectures : ArchSet) { ExportSection Section; Section.Architectures = Architectures; for (const auto &Library : File->allowableClients()) if (Library.getArchitectures() == Architectures) Section.AllowableClients.emplace_back(Library.getInstallName()); for (const auto &Library : File->reexportedLibraries()) if (Library.getArchitectures() == Architectures) Section.ReexportedLibraries.emplace_back(Library.getInstallName()); for (const auto &SymArch : SymbolToArchSet) { if (SymArch.second != Architectures) continue; const auto *Symbol = SymArch.first; switch (Symbol->getKind()) { case SymbolKind::GlobalSymbol: if (Symbol->isWeakDefined()) Section.WeakDefSymbols.emplace_back(Symbol->getName()); else if (Symbol->isThreadLocalValue()) Section.TLVSymbols.emplace_back(Symbol->getName()); else Section.Symbols.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCClass: if (File->getFileType() != FileType::TBD_V3) Section.Classes.emplace_back( copyString("_" + Symbol->getName().str())); else Section.Classes.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCClassEHType: if (File->getFileType() != FileType::TBD_V3) Section.Symbols.emplace_back( copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str())); else Section.ClassEHs.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCInstanceVariable: if (File->getFileType() != FileType::TBD_V3) Section.IVars.emplace_back( copyString("_" + Symbol->getName().str())); else Section.IVars.emplace_back(Symbol->getName()); break; } } llvm::sort(Section.Symbols.begin(), Section.Symbols.end()); llvm::sort(Section.Classes.begin(), Section.Classes.end()); llvm::sort(Section.ClassEHs.begin(), Section.ClassEHs.end()); llvm::sort(Section.IVars.begin(), Section.IVars.end()); llvm::sort(Section.WeakDefSymbols.begin(), Section.WeakDefSymbols.end()); llvm::sort(Section.TLVSymbols.begin(), Section.TLVSymbols.end()); Exports.emplace_back(std::move(Section)); } ArchSet.clear(); SymbolToArchSet.clear(); for (const auto *Symbol : File->undefineds()) { auto Architectures = Symbol->getArchitectures(); SymbolToArchSet[Symbol] = Architectures; ArchSet.insert(Architectures); } for (auto Architectures : ArchSet) { UndefinedSection Section; Section.Architectures = Architectures; for (const auto &SymArch : SymbolToArchSet) { if (SymArch.second != Architectures) continue; const auto *Symbol = SymArch.first; switch (Symbol->getKind()) { case SymbolKind::GlobalSymbol: if (Symbol->isWeakReferenced()) Section.WeakRefSymbols.emplace_back(Symbol->getName()); else Section.Symbols.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCClass: if (File->getFileType() != FileType::TBD_V3) Section.Classes.emplace_back( copyString("_" + Symbol->getName().str())); else Section.Classes.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCClassEHType: if (File->getFileType() != FileType::TBD_V3) Section.Symbols.emplace_back( copyString("_OBJC_EHTYPE_$_" + Symbol->getName().str())); else Section.ClassEHs.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCInstanceVariable: if (File->getFileType() != FileType::TBD_V3) Section.IVars.emplace_back( copyString("_" + Symbol->getName().str())); else Section.IVars.emplace_back(Symbol->getName()); break; } } llvm::sort(Section.Symbols.begin(), Section.Symbols.end()); llvm::sort(Section.Classes.begin(), Section.Classes.end()); llvm::sort(Section.ClassEHs.begin(), Section.ClassEHs.end()); llvm::sort(Section.IVars.begin(), Section.IVars.end()); llvm::sort(Section.WeakRefSymbols.begin(), Section.WeakRefSymbols.end()); Undefineds.emplace_back(std::move(Section)); } } // TBD v1 - TBD v3 files only support one platform and several // architectures. It is possible to have more than one platform for TBD v3 // files, but the architectures don't apply to all // platforms, specifically to filter out the i386 slice from // platform macCatalyst. TargetList synthesizeTargets(ArchitectureSet Architectures, const PlatformSet &Platforms) { TargetList Targets; for (auto Platform : Platforms) { Platform = mapToPlatformKind(Platform, Architectures.hasX86()); for (const auto &&Architecture : Architectures) { if ((Architecture == AK_i386) && (Platform == PlatformKind::macCatalyst)) continue; Targets.emplace_back(Architecture, Platform); } } return Targets; } const InterfaceFile *denormalize(IO &IO) { auto Ctx = reinterpret_cast(IO.getContext()); assert(Ctx); auto *File = new InterfaceFile; File->setPath(Ctx->Path); File->setFileType(Ctx->FileKind); File->addTargets(synthesizeTargets(Architectures, Platforms)); for (auto &ID : UUIDs) File->addUUID(ID.first, ID.second); File->setInstallName(InstallName); File->setCurrentVersion(CurrentVersion); File->setCompatibilityVersion(CompatibilityVersion); File->setSwiftABIVersion(SwiftABIVersion); File->setObjCConstraint(ObjCConstraint); for (const auto &Target : File->targets()) File->addParentUmbrella(Target, ParentUmbrella); if (Ctx->FileKind == FileType::TBD_V1) { File->setTwoLevelNamespace(); File->setApplicationExtensionSafe(); } else { File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace)); File->setApplicationExtensionSafe( !(Flags & TBDFlags::NotApplicationExtensionSafe)); File->setInstallAPI(Flags & TBDFlags::InstallAPI); } for (const auto &Section : Exports) { const auto Targets = synthesizeTargets(Section.Architectures, Platforms); for (const auto &Lib : Section.AllowableClients) for (const auto &Target : Targets) File->addAllowableClient(Lib, Target); for (const auto &Lib : Section.ReexportedLibraries) for (const auto &Target : Targets) File->addReexportedLibrary(Lib, Target); for (const auto &Symbol : Section.Symbols) { if (Ctx->FileKind != FileType::TBD_V3 && Symbol.value.startswith("_OBJC_EHTYPE_$_")) File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol.value.drop_front(15), Targets); else File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets); } for (auto &Symbol : Section.Classes) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets); } for (auto &Symbol : Section.ClassEHs) File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets); for (auto &Symbol : Section.IVars) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, Targets); } for (auto &Symbol : Section.WeakDefSymbols) File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, SymbolFlags::WeakDefined); for (auto &Symbol : Section.TLVSymbols) File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, SymbolFlags::ThreadLocalValue); } for (const auto &Section : Undefineds) { const auto Targets = synthesizeTargets(Section.Architectures, Platforms); for (auto &Symbol : Section.Symbols) { if (Ctx->FileKind != FileType::TBD_V3 && Symbol.value.startswith("_OBJC_EHTYPE_$_")) File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol.value.drop_front(15), Targets, SymbolFlags::Undefined); else File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, SymbolFlags::Undefined); } for (auto &Symbol : Section.Classes) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(SymbolKind::ObjectiveCClass, Name, Targets, SymbolFlags::Undefined); } for (auto &Symbol : Section.ClassEHs) File->addSymbol(SymbolKind::ObjectiveCClassEHType, Symbol, Targets, SymbolFlags::Undefined); for (auto &Symbol : Section.IVars) { auto Name = Symbol.value; if (Ctx->FileKind != FileType::TBD_V3) Name = Name.drop_front(); File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, Name, Targets, SymbolFlags::Undefined); } for (auto &Symbol : Section.WeakRefSymbols) File->addSymbol(SymbolKind::GlobalSymbol, Symbol, Targets, SymbolFlags::Undefined | SymbolFlags::WeakReferenced); } return File; } llvm::BumpPtrAllocator Allocator; StringRef copyString(StringRef String) { if (String.empty()) return {}; void *Ptr = Allocator.Allocate(String.size(), 1); memcpy(Ptr, String.data(), String.size()); return StringRef(reinterpret_cast(Ptr), String.size()); } std::vector Architectures; std::vector UUIDs; PlatformSet Platforms; StringRef InstallName; PackedVersion CurrentVersion; PackedVersion CompatibilityVersion; SwiftVersion SwiftABIVersion{0}; ObjCConstraintType ObjCConstraint{ObjCConstraintType::None}; TBDFlags Flags{TBDFlags::None}; StringRef ParentUmbrella; std::vector Exports; std::vector Undefineds; }; static void setFileTypeForInput(TextAPIContext *Ctx, IO &IO) { if (IO.mapTag("!tapi-tbd", false)) Ctx->FileKind = FileType::TBD_V4; else if (IO.mapTag("!tapi-tbd-v3", false)) Ctx->FileKind = FileType::TBD_V3; else if (IO.mapTag("!tapi-tbd-v2", false)) Ctx->FileKind = FileType::TBD_V2; else if (IO.mapTag("!tapi-tbd-v1", false) || IO.mapTag("tag:yaml.org,2002:map", false)) Ctx->FileKind = FileType::TBD_V1; else { Ctx->FileKind = FileType::Invalid; return; } } static void mapping(IO &IO, const InterfaceFile *&File) { auto *Ctx = reinterpret_cast(IO.getContext()); assert((!Ctx || !IO.outputting() || (Ctx && Ctx->FileKind != FileType::Invalid)) && "File type is not set in YAML context"); if (!IO.outputting()) { setFileTypeForInput(Ctx, IO); switch (Ctx->FileKind) { default: break; case FileType::TBD_V4: mapKeysToValuesV4(IO, File); return; case FileType::Invalid: IO.setError("unsupported file type"); return; } } else { // Set file type when writing. switch (Ctx->FileKind) { default: llvm_unreachable("unexpected file type"); case FileType::TBD_V4: mapKeysToValuesV4(IO, File); return; case FileType::TBD_V3: IO.mapTag("!tapi-tbd-v3", true); break; case FileType::TBD_V2: IO.mapTag("!tapi-tbd-v2", true); break; case FileType::TBD_V1: // Don't write the tag into the .tbd file for TBD v1 break; } } mapKeysToValues(Ctx->FileKind, IO, File); } using SectionList = std::vector; struct NormalizedTBD_V4 { explicit NormalizedTBD_V4(IO &IO) {} NormalizedTBD_V4(IO &IO, const InterfaceFile *&File) { auto Ctx = reinterpret_cast(IO.getContext()); assert(Ctx); TBDVersion = Ctx->FileKind >> 1; Targets.insert(Targets.begin(), File->targets().begin(), File->targets().end()); for (const auto &IT : File->uuids()) UUIDs.emplace_back(IT.first, IT.second); InstallName = File->getInstallName(); CurrentVersion = File->getCurrentVersion(); CompatibilityVersion = File->getCompatibilityVersion(); SwiftABIVersion = File->getSwiftABIVersion(); Flags = TBDFlags::None; if (!File->isApplicationExtensionSafe()) Flags |= TBDFlags::NotApplicationExtensionSafe; if (!File->isTwoLevelNamespace()) Flags |= TBDFlags::FlatNamespace; if (File->isInstallAPI()) Flags |= TBDFlags::InstallAPI; { std::map valueToTargetList; for (const auto &it : File->umbrellas()) valueToTargetList[it.second].emplace_back(it.first); for (const auto &it : valueToTargetList) { UmbrellaSection CurrentSection; CurrentSection.Targets.insert(CurrentSection.Targets.begin(), it.second.begin(), it.second.end()); CurrentSection.Umbrella = it.first; ParentUmbrellas.emplace_back(std::move(CurrentSection)); } } assignTargetsToLibrary(File->allowableClients(), AllowableClients); assignTargetsToLibrary(File->reexportedLibraries(), ReexportedLibraries); auto handleSymbols = [](SectionList &CurrentSections, InterfaceFile::const_filtered_symbol_range Symbols, std::function Pred) { std::set TargetSet; std::map SymbolToTargetList; for (const auto *Symbol : Symbols) { if (!Pred(Symbol)) continue; TargetList Targets(Symbol->targets()); SymbolToTargetList[Symbol] = Targets; TargetSet.emplace(std::move(Targets)); } for (const auto &TargetIDs : TargetSet) { SymbolSection CurrentSection; CurrentSection.Targets.insert(CurrentSection.Targets.begin(), TargetIDs.begin(), TargetIDs.end()); for (const auto &IT : SymbolToTargetList) { if (IT.second != TargetIDs) continue; const auto *Symbol = IT.first; switch (Symbol->getKind()) { case SymbolKind::GlobalSymbol: if (Symbol->isWeakDefined()) CurrentSection.WeakSymbols.emplace_back(Symbol->getName()); else if (Symbol->isThreadLocalValue()) CurrentSection.TlvSymbols.emplace_back(Symbol->getName()); else CurrentSection.Symbols.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCClass: CurrentSection.Classes.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCClassEHType: CurrentSection.ClassEHs.emplace_back(Symbol->getName()); break; case SymbolKind::ObjectiveCInstanceVariable: CurrentSection.Ivars.emplace_back(Symbol->getName()); break; } } sort(CurrentSection.Symbols); sort(CurrentSection.Classes); sort(CurrentSection.ClassEHs); sort(CurrentSection.Ivars); sort(CurrentSection.WeakSymbols); sort(CurrentSection.TlvSymbols); CurrentSections.emplace_back(std::move(CurrentSection)); } }; handleSymbols(Exports, File->exports(), [](const Symbol *Symbol) { return !Symbol->isReexported(); }); handleSymbols(Reexports, File->exports(), [](const Symbol *Symbol) { return Symbol->isReexported(); }); handleSymbols(Undefineds, File->undefineds(), [](const Symbol *Symbol) { return true; }); } const InterfaceFile *denormalize(IO &IO) { auto Ctx = reinterpret_cast(IO.getContext()); assert(Ctx); auto *File = new InterfaceFile; File->setPath(Ctx->Path); File->setFileType(Ctx->FileKind); for (auto &id : UUIDs) File->addUUID(id.TargetID, id.Value); File->addTargets(Targets); File->setInstallName(InstallName); File->setCurrentVersion(CurrentVersion); File->setCompatibilityVersion(CompatibilityVersion); File->setSwiftABIVersion(SwiftABIVersion); for (const auto &CurrentSection : ParentUmbrellas) for (const auto &target : CurrentSection.Targets) File->addParentUmbrella(target, CurrentSection.Umbrella); File->setTwoLevelNamespace(!(Flags & TBDFlags::FlatNamespace)); File->setApplicationExtensionSafe( !(Flags & TBDFlags::NotApplicationExtensionSafe)); File->setInstallAPI(Flags & TBDFlags::InstallAPI); for (const auto &CurrentSection : AllowableClients) { for (const auto &lib : CurrentSection.Values) for (const auto &Target : CurrentSection.Targets) File->addAllowableClient(lib, Target); } for (const auto &CurrentSection : ReexportedLibraries) { for (const auto &Lib : CurrentSection.Values) for (const auto &Target : CurrentSection.Targets) File->addReexportedLibrary(Lib, Target); } auto handleSymbols = [File](const SectionList &CurrentSections, SymbolFlags Flag = SymbolFlags::None) { for (const auto &CurrentSection : CurrentSections) { for (auto &sym : CurrentSection.Symbols) File->addSymbol(SymbolKind::GlobalSymbol, sym, CurrentSection.Targets, Flag); for (auto &sym : CurrentSection.Classes) File->addSymbol(SymbolKind::ObjectiveCClass, sym, CurrentSection.Targets); for (auto &sym : CurrentSection.ClassEHs) File->addSymbol(SymbolKind::ObjectiveCClassEHType, sym, CurrentSection.Targets); for (auto &sym : CurrentSection.Ivars) File->addSymbol(SymbolKind::ObjectiveCInstanceVariable, sym, CurrentSection.Targets); for (auto &sym : CurrentSection.WeakSymbols) File->addSymbol(SymbolKind::GlobalSymbol, sym, CurrentSection.Targets, SymbolFlags::WeakDefined); for (auto &sym : CurrentSection.TlvSymbols) File->addSymbol(SymbolKind::GlobalSymbol, sym, CurrentSection.Targets, SymbolFlags::ThreadLocalValue); } }; handleSymbols(Exports); handleSymbols(Reexports, SymbolFlags::Rexported); handleSymbols(Undefineds, SymbolFlags::Undefined); return File; } unsigned TBDVersion; std::vector UUIDs; TargetList Targets; StringRef InstallName; PackedVersion CurrentVersion; PackedVersion CompatibilityVersion; SwiftVersion SwiftABIVersion{0}; std::vector AllowableClients; std::vector ReexportedLibraries; TBDFlags Flags{TBDFlags::None}; std::vector ParentUmbrellas; SectionList Exports; SectionList Reexports; SectionList Undefineds; private: void assignTargetsToLibrary(const std::vector &Libraries, std::vector &Section) { std::set targetSet; std::map valueToTargetList; for (const auto &library : Libraries) { TargetList targets(library.targets()); valueToTargetList[&library] = targets; targetSet.emplace(std::move(targets)); } for (const auto &targets : targetSet) { MetadataSection CurrentSection; CurrentSection.Targets.insert(CurrentSection.Targets.begin(), targets.begin(), targets.end()); for (const auto &it : valueToTargetList) { if (it.second != targets) continue; CurrentSection.Values.emplace_back(it.first->getInstallName()); } llvm::sort(CurrentSection.Values); Section.emplace_back(std::move(CurrentSection)); } } }; static void mapKeysToValues(FileType FileKind, IO &IO, const InterfaceFile *&File) { MappingNormalization Keys(IO, File); IO.mapRequired("archs", Keys->Architectures); if (FileKind != FileType::TBD_V1) IO.mapOptional("uuids", Keys->UUIDs); IO.mapRequired("platform", Keys->Platforms); if (FileKind != FileType::TBD_V1) IO.mapOptional("flags", Keys->Flags, TBDFlags::None); IO.mapRequired("install-name", Keys->InstallName); IO.mapOptional("current-version", Keys->CurrentVersion, PackedVersion(1, 0, 0)); IO.mapOptional("compatibility-version", Keys->CompatibilityVersion, PackedVersion(1, 0, 0)); if (FileKind != FileType::TBD_V3) IO.mapOptional("swift-version", Keys->SwiftABIVersion, SwiftVersion(0)); else IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion, SwiftVersion(0)); IO.mapOptional("objc-constraint", Keys->ObjCConstraint, (FileKind == FileType::TBD_V1) ? ObjCConstraintType::None : ObjCConstraintType::Retain_Release); if (FileKind != FileType::TBD_V1) IO.mapOptional("parent-umbrella", Keys->ParentUmbrella, StringRef()); IO.mapOptional("exports", Keys->Exports); if (FileKind != FileType::TBD_V1) IO.mapOptional("undefineds", Keys->Undefineds); } static void mapKeysToValuesV4(IO &IO, const InterfaceFile *&File) { MappingNormalization Keys(IO, File); IO.mapTag("!tapi-tbd", true); IO.mapRequired("tbd-version", Keys->TBDVersion); IO.mapRequired("targets", Keys->Targets); IO.mapOptional("uuids", Keys->UUIDs); IO.mapOptional("flags", Keys->Flags, TBDFlags::None); IO.mapRequired("install-name", Keys->InstallName); IO.mapOptional("current-version", Keys->CurrentVersion, PackedVersion(1, 0, 0)); IO.mapOptional("compatibility-version", Keys->CompatibilityVersion, PackedVersion(1, 0, 0)); IO.mapOptional("swift-abi-version", Keys->SwiftABIVersion, SwiftVersion(0)); IO.mapOptional("parent-umbrella", Keys->ParentUmbrellas); auto OptionKind = MetadataSection::Option::Clients; IO.mapOptionalWithContext("allowable-clients", Keys->AllowableClients, OptionKind); OptionKind = MetadataSection::Option::Libraries; IO.mapOptionalWithContext("reexported-libraries", Keys->ReexportedLibraries, OptionKind); IO.mapOptional("exports", Keys->Exports); IO.mapOptional("reexports", Keys->Reexports); IO.mapOptional("undefineds", Keys->Undefineds); } }; template <> struct DocumentListTraits> { static size_t size(IO &IO, std::vector &Seq) { return Seq.size(); } static const InterfaceFile *& element(IO &IO, std::vector &Seq, size_t Index) { if (Index >= Seq.size()) Seq.resize(Index + 1); return Seq[Index]; } }; } // end namespace yaml. } // namespace llvm static void DiagHandler(const SMDiagnostic &Diag, void *Context) { auto *File = static_cast(Context); SmallString<1024> Message; raw_svector_ostream S(Message); SMDiagnostic NewDiag(*Diag.getSourceMgr(), Diag.getLoc(), File->Path, Diag.getLineNo(), Diag.getColumnNo(), Diag.getKind(), Diag.getMessage(), Diag.getLineContents(), Diag.getRanges(), Diag.getFixIts()); NewDiag.print(nullptr, S); File->ErrorMessage = ("malformed file\n" + Message).str(); } Expected> TextAPIReader::get(MemoryBufferRef InputBuffer) { TextAPIContext Ctx; Ctx.Path = std::string(InputBuffer.getBufferIdentifier()); yaml::Input YAMLIn(InputBuffer.getBuffer(), &Ctx, DiagHandler, &Ctx); // Fill vector with interface file objects created by parsing the YAML file. std::vector Files; YAMLIn >> Files; // YAMLIn dynamically allocates for Interface file and in case of error, // memory leak will occur unless wrapped around unique_ptr auto File = std::unique_ptr( const_cast(Files.front())); for (auto Iter = std::next(Files.begin()); Iter != Files.end(); ++Iter) File->addDocument( std::shared_ptr(const_cast(*Iter))); if (YAMLIn.error()) return make_error(Ctx.ErrorMessage, YAMLIn.error()); return std::move(File); } Error TextAPIWriter::writeToStream(raw_ostream &OS, const InterfaceFile &File) { TextAPIContext Ctx; Ctx.Path = std::string(File.getPath()); Ctx.FileKind = File.getFileType(); llvm::yaml::Output YAMLOut(OS, &Ctx, /*WrapColumn=*/80); std::vector Files; Files.emplace_back(&File); for (auto Document : File.documents()) Files.emplace_back(Document.get()); // Stream out yaml. YAMLOut << Files; return Error::success(); } diff --git a/llvm/lib/TextAPI/MachO/TextStubCommon.cpp b/llvm/lib/TextAPI/MachO/TextStubCommon.cpp index 4a82df6beac1..0d3614b0a24c 100644 --- a/llvm/lib/TextAPI/MachO/TextStubCommon.cpp +++ b/llvm/lib/TextAPI/MachO/TextStubCommon.cpp @@ -1,235 +1,238 @@ //===- TextStubCommon.cpp -------------------------------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// // // Implememts common Text Stub YAML mappings. // //===----------------------------------------------------------------------===// #include "TextStubCommon.h" #include "TextAPIContext.h" #include "llvm/ADT/StringSwitch.h" using namespace llvm::MachO; namespace llvm { namespace yaml { void ScalarTraits::output(const FlowStringRef &Value, void *Ctx, raw_ostream &OS) { ScalarTraits::output(Value, Ctx, OS); } StringRef ScalarTraits::input(StringRef Value, void *Ctx, FlowStringRef &Out) { return ScalarTraits::input(Value, Ctx, Out.value); } QuotingType ScalarTraits::mustQuote(StringRef Name) { return ScalarTraits::mustQuote(Name); } void ScalarEnumerationTraits::enumeration( IO &IO, ObjCConstraintType &Constraint) { IO.enumCase(Constraint, "none", ObjCConstraintType::None); IO.enumCase(Constraint, "retain_release", ObjCConstraintType::Retain_Release); IO.enumCase(Constraint, "retain_release_for_simulator", ObjCConstraintType::Retain_Release_For_Simulator); IO.enumCase(Constraint, "retain_release_or_gc", ObjCConstraintType::Retain_Release_Or_GC); IO.enumCase(Constraint, "gc", ObjCConstraintType::GC); } void ScalarTraits::output(const PlatformSet &Values, void *IO, raw_ostream &OS) { const auto *Ctx = reinterpret_cast(IO); assert((!Ctx || Ctx->FileKind != FileType::Invalid) && "File type is not set in context"); if (Ctx && Ctx->FileKind == TBD_V3 && Values.count(PlatformKind::macOS) && Values.count(PlatformKind::macCatalyst)) { OS << "zippered"; return; } assert(Values.size() == 1U); switch (*Values.begin()) { default: llvm_unreachable("unexpected platform"); break; case PlatformKind::macOS: OS << "macosx"; break; case PlatformKind::iOSSimulator: LLVM_FALLTHROUGH; case PlatformKind::iOS: OS << "ios"; break; case PlatformKind::watchOSSimulator: LLVM_FALLTHROUGH; case PlatformKind::watchOS: OS << "watchos"; break; case PlatformKind::tvOSSimulator: LLVM_FALLTHROUGH; case PlatformKind::tvOS: OS << "tvos"; break; case PlatformKind::bridgeOS: OS << "bridgeos"; break; case PlatformKind::macCatalyst: OS << "iosmac"; break; + case PlatformKind::driverKit: + OS << "driverkit"; + break; } } StringRef ScalarTraits::input(StringRef Scalar, void *IO, PlatformSet &Values) { const auto *Ctx = reinterpret_cast(IO); assert((!Ctx || Ctx->FileKind != FileType::Invalid) && "File type is not set in context"); if (Scalar == "zippered") { if (Ctx && Ctx->FileKind == FileType::TBD_V3) { Values.insert(PlatformKind::macOS); Values.insert(PlatformKind::macCatalyst); return {}; } return "invalid platform"; } auto Platform = StringSwitch(Scalar) .Case("unknown", PlatformKind::unknown) .Case("macosx", PlatformKind::macOS) .Case("ios", PlatformKind::iOS) .Case("watchos", PlatformKind::watchOS) .Case("tvos", PlatformKind::tvOS) .Case("bridgeos", PlatformKind::bridgeOS) .Case("iosmac", PlatformKind::macCatalyst) .Default(PlatformKind::unknown); if (Platform == PlatformKind::macCatalyst) if (Ctx && Ctx->FileKind != FileType::TBD_V3) return "invalid platform"; if (Platform == PlatformKind::unknown) return "unknown platform"; Values.insert(Platform); return {}; } QuotingType ScalarTraits::mustQuote(StringRef) { return QuotingType::None; } void ScalarBitSetTraits::bitset(IO &IO, ArchitectureSet &Archs) { #define ARCHINFO(arch, type, subtype, numbits) \ IO.bitSetCase(Archs, #arch, 1U << static_cast(AK_##arch)); #include "llvm/TextAPI/MachO/Architecture.def" #undef ARCHINFO } void ScalarTraits::output(const Architecture &Value, void *, raw_ostream &OS) { OS << Value; } StringRef ScalarTraits::input(StringRef Scalar, void *, Architecture &Value) { Value = getArchitectureFromName(Scalar); return {}; } QuotingType ScalarTraits::mustQuote(StringRef) { return QuotingType::None; } void ScalarTraits::output(const PackedVersion &Value, void *, raw_ostream &OS) { OS << Value; } StringRef ScalarTraits::input(StringRef Scalar, void *, PackedVersion &Value) { if (!Value.parse32(Scalar)) return "invalid packed version string."; return {}; } QuotingType ScalarTraits::mustQuote(StringRef) { return QuotingType::None; } void ScalarTraits::output(const SwiftVersion &Value, void *, raw_ostream &OS) { switch (Value) { case 1: OS << "1.0"; break; case 2: OS << "1.1"; break; case 3: OS << "2.0"; break; case 4: OS << "3.0"; break; default: OS << (unsigned)Value; break; } } StringRef ScalarTraits::input(StringRef Scalar, void *IO, SwiftVersion &Value) { const auto *Ctx = reinterpret_cast(IO); assert((!Ctx || Ctx->FileKind != FileType::Invalid) && "File type is not set in context"); if (Ctx->FileKind == FileType::TBD_V4) { if (Scalar.getAsInteger(10, Value)) return "invalid Swift ABI version."; return {}; } else { Value = StringSwitch(Scalar) .Case("1.0", 1) .Case("1.1", 2) .Case("2.0", 3) .Case("3.0", 4) .Default(0); } if (Value != SwiftVersion(0)) return {}; if (Scalar.getAsInteger(10, Value)) return "invalid Swift ABI version."; return StringRef(); } QuotingType ScalarTraits::mustQuote(StringRef) { return QuotingType::None; } void ScalarTraits::output(const UUID &Value, void *, raw_ostream &OS) { OS << Value.first << ": " << Value.second; } StringRef ScalarTraits::input(StringRef Scalar, void *, UUID &Value) { auto Split = Scalar.split(':'); auto Arch = Split.first.trim(); auto UUID = Split.second.trim(); if (UUID.empty()) return "invalid uuid string pair"; Value.second = std::string(UUID); Value.first = Target{getArchitectureFromName(Arch), PlatformKind::unknown}; return {}; } QuotingType ScalarTraits::mustQuote(StringRef) { return QuotingType::Single; } } // end namespace yaml. } // end namespace llvm. diff --git a/llvm/unittests/TextAPI/TextStubV4Tests.cpp b/llvm/unittests/TextAPI/TextStubV4Tests.cpp index e8addf1ae7a3..d4b2ef623eac 100644 --- a/llvm/unittests/TextAPI/TextStubV4Tests.cpp +++ b/llvm/unittests/TextAPI/TextStubV4Tests.cpp @@ -1,925 +1,949 @@ //===-- TextStubV4Tests.cpp - TBD V4 File Test ----------------------------===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===-----------------------------------------------------------------------===/ #include "TextStubHelpers.h" #include "llvm/TextAPI/MachO/InterfaceFile.h" #include "llvm/TextAPI/MachO/TextAPIReader.h" #include "llvm/TextAPI/MachO/TextAPIWriter.h" #include "gtest/gtest.h" #include #include using namespace llvm; using namespace llvm::MachO; namespace TBDv4 { TEST(TBDv4, ReadFile) { static const char TBDv4File[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n" "uuids:\n" " - target: i386-macos\n" " value: 00000000-0000-0000-0000-000000000000\n" " - target: x86_64-macos\n" " value: 11111111-1111-1111-1111-111111111111\n" " - target: x86_64-ios\n" " value: 11111111-1111-1111-1111-111111111111\n" "flags: [ flat_namespace, installapi ]\n" "install-name: Umbrella.framework/Umbrella\n" "current-version: 1.2.3\n" "compatibility-version: 1.2\n" "swift-abi-version: 5\n" "parent-umbrella:\n" " - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n" " umbrella: System\n" "allowable-clients:\n" " - targets: [ i386-macos, x86_64-macos, x86_64-ios ]\n" " clients: [ ClientA ]\n" "reexported-libraries:\n" " - targets: [ i386-macos ]\n" " libraries: [ /System/Library/Frameworks/A.framework/A ]\n" "exports:\n" " - targets: [ i386-macos ]\n" " symbols: [ _symA ]\n" " objc-classes: []\n" " objc-eh-types: []\n" " objc-ivars: []\n" " weak-symbols: []\n" " thread-local-symbols: []\n" " - targets: [ x86_64-ios ]\n" " symbols: [_symB]\n" " - targets: [ x86_64-macos, x86_64-ios ]\n" " symbols: [_symAB]\n" "reexports:\n" " - targets: [ i386-macos ]\n" " symbols: [_symC]\n" " objc-classes: []\n" " objc-eh-types: []\n" " objc-ivars: []\n" " weak-symbols: []\n" " thread-local-symbols: []\n" "undefineds:\n" " - targets: [ i386-macos ]\n" " symbols: [ _symD ]\n" " objc-classes: []\n" " objc-eh-types: []\n" " objc-ivars: []\n" " weak-symbols: []\n" " thread-local-symbols: []\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4File, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); PlatformSet Platforms; Platforms.insert(PlatformKind::macOS); Platforms.insert(PlatformKind::iOS); auto Archs = AK_i386 | AK_x86_64; TargetList Targets = { Target(AK_i386, PlatformKind::macOS), Target(AK_x86_64, PlatformKind::macOS), Target(AK_x86_64, PlatformKind::iOS), }; UUIDs uuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"}, {Targets[1], "11111111-1111-1111-1111-111111111111"}, {Targets[2], "11111111-1111-1111-1111-111111111111"}}; EXPECT_EQ(Archs, File->getArchitectures()); EXPECT_EQ(uuids, File->uuids()); EXPECT_EQ(Platforms.size(), File->getPlatforms().size()); for (auto Platform : File->getPlatforms()) EXPECT_EQ(Platforms.count(Platform), 1U); EXPECT_EQ(std::string("Umbrella.framework/Umbrella"), File->getInstallName()); EXPECT_EQ(PackedVersion(1, 2, 3), File->getCurrentVersion()); EXPECT_EQ(PackedVersion(1, 2, 0), File->getCompatibilityVersion()); EXPECT_EQ(5U, File->getSwiftABIVersion()); EXPECT_FALSE(File->isTwoLevelNamespace()); EXPECT_TRUE(File->isApplicationExtensionSafe()); EXPECT_TRUE(File->isInstallAPI()); InterfaceFileRef client("ClientA", Targets); InterfaceFileRef reexport("/System/Library/Frameworks/A.framework/A", {Targets[0]}); EXPECT_EQ(1U, File->allowableClients().size()); EXPECT_EQ(client, File->allowableClients().front()); EXPECT_EQ(1U, File->reexportedLibraries().size()); EXPECT_EQ(reexport, File->reexportedLibraries().front()); ExportedSymbolSeq Exports, Reexports, Undefineds; ExportedSymbol temp; for (const auto *Sym : File->symbols()) { temp = ExportedSymbol{Sym->getKind(), std::string(Sym->getName()), Sym->isWeakDefined(), Sym->isThreadLocalValue()}; EXPECT_FALSE(Sym->isWeakReferenced()); if (Sym->isUndefined()) Undefineds.emplace_back(std::move(temp)); else Sym->isReexported() ? Reexports.emplace_back(std::move(temp)) : Exports.emplace_back(std::move(temp)); } llvm::sort(Exports.begin(), Exports.end()); llvm::sort(Reexports.begin(), Reexports.end()); llvm::sort(Undefineds.begin(), Undefineds.end()); static ExportedSymbol ExpectedExportedSymbols[] = { {SymbolKind::GlobalSymbol, "_symA", false, false}, {SymbolKind::GlobalSymbol, "_symAB", false, false}, {SymbolKind::GlobalSymbol, "_symB", false, false}, }; static ExportedSymbol ExpectedReexportedSymbols[] = { {SymbolKind::GlobalSymbol, "_symC", false, false}, }; static ExportedSymbol ExpectedUndefinedSymbols[] = { {SymbolKind::GlobalSymbol, "_symD", false, false}, }; EXPECT_EQ(sizeof(ExpectedExportedSymbols) / sizeof(ExportedSymbol), Exports.size()); EXPECT_EQ(sizeof(ExpectedReexportedSymbols) / sizeof(ExportedSymbol), Reexports.size()); EXPECT_EQ(sizeof(ExpectedUndefinedSymbols) / sizeof(ExportedSymbol), Undefineds.size()); EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExportedSymbols))); EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(), std::begin(ExpectedReexportedSymbols))); EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(), std::begin(ExpectedUndefinedSymbols))); } TEST(TBDv4, ReadMultipleDocuments) { static const char TBDv4Inlines[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-macos, i386-maccatalyst, x86_64-macos, " "x86_64-maccatalyst ]\n" "uuids:\n" " - target: i386-macos\n" " value: 00000000-0000-0000-0000-000000000000\n" " - target: i386-maccatalyst\n" " value: 00000000-0000-0000-0000-000000000002\n" " - target: x86_64-macos\n" " value: 11111111-1111-1111-1111-111111111111\n" " - target: x86_64-maccatalyst\n" " value: 11111111-1111-1111-1111-111111111112\n" "install-name: /System/Library/Frameworks/Umbrella.framework/Umbrella\n" "parent-umbrella:\n" " - targets: [ i386-macos, x86_64-macos ]\n" " umbrella: System\n" "reexported-libraries:\n" " - targets: [ i386-macos, x86_64-macos ]\n" " libraries: [ /System/Library/Frameworks/A.framework/A ]\n" "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-macos, x86_64-macos ]\n" "uuids:\n" " - target: i386-macos\n" " value: 20000000-0000-0000-0000-000000000000\n" " - target: x86_64-macos\n" " value: 21111111-1111-1111-1111-111111111111\n" "flags: [ flat_namespace ]\n" "install-name: /System/Library/Frameworks/A.framework/A\n" "current-version: 1.2.3\n" "compatibility-version: 1.2\n" "swift-abi-version: 5\n" "exports:\n" " - targets: [ i386-macos ]\n" " symbols: [ _symA ]\n" " objc-classes: []\n" " objc-eh-types: []\n" " objc-ivars: []\n" " weak-symbols: []\n" " thread-local-symbols: []\n" " - targets: [ x86_64-macos ]\n" " symbols: [_symAB]\n" "reexports:\n" " - targets: [ i386-macos ]\n" " symbols: [_symC]\n" " objc-classes: []\n" " objc-eh-types: []\n" " objc-ivars: []\n" " weak-symbols: []\n" " thread-local-symbols: []\n" "undefineds:\n" " - targets: [ i386-macos ]\n" " symbols: [ _symD ]\n" " objc-classes: []\n" " objc-eh-types: []\n" " objc-ivars: []\n" " weak-symbols: []\n" " thread-local-symbols: []\n" "...\n"; PlatformSet Platforms; Platforms.insert(PlatformKind::macOS); Platforms.insert(PlatformKind::macCatalyst); ArchitectureSet Archs = AK_i386 | AK_x86_64; TargetList Targets; for (auto &&Arch : Archs) for (auto &&Platform : Platforms) Targets.emplace_back(Target(Arch, Platform)); UUIDs Uuids = { {Targets[0], "00000000-0000-0000-0000-000000000000"}, {Targets[1], "00000000-0000-0000-0000-000000000002"}, {Targets[2], "11111111-1111-1111-1111-111111111111"}, {Targets[3], "11111111-1111-1111-1111-111111111112"}, }; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4Inlines, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(Archs, File->getArchitectures()); EXPECT_EQ(Uuids, File->uuids()); EXPECT_EQ(Platforms, File->getPlatforms()); EXPECT_EQ( std::string("/System/Library/Frameworks/Umbrella.framework/Umbrella"), File->getInstallName()); EXPECT_TRUE(File->isTwoLevelNamespace()); EXPECT_TRUE(File->isApplicationExtensionSafe()); EXPECT_FALSE(File->isInstallAPI()); EXPECT_EQ(PackedVersion(1, 0, 0), File->getCurrentVersion()); EXPECT_EQ(PackedVersion(1, 0, 0), File->getCompatibilityVersion()); InterfaceFileRef reexport("/System/Library/Frameworks/A.framework/A", {Targets[0], Targets[2]}); EXPECT_EQ(1U, File->reexportedLibraries().size()); EXPECT_EQ(reexport, File->reexportedLibraries().front()); ExportedSymbolSeq Exports; for (const auto *Sym : File->symbols()) { EXPECT_FALSE(Sym->isWeakReferenced()); EXPECT_FALSE(Sym->isUndefined()); Exports.emplace_back(ExportedSymbol{Sym->getKind(), Sym->getName().str(), Sym->isWeakDefined(), Sym->isThreadLocalValue()}); } EXPECT_EQ(0U, Exports.size()); // Check Inlined Document Exports.clear(); Targets.clear(); Uuids.clear(); PlatformKind Platform = PlatformKind::macOS; for (auto &&Arch : Archs) Targets.emplace_back(Target(Arch, Platform)); Uuids = { {Targets[0], "20000000-0000-0000-0000-000000000000"}, {Targets[1], "21111111-1111-1111-1111-111111111111"}, }; auto Document = File->documents().front(); EXPECT_EQ(FileType::TBD_V4, Document->getFileType()); EXPECT_EQ(Archs, Document->getArchitectures()); EXPECT_EQ(Uuids, Document->uuids()); EXPECT_EQ(1U, Document->getPlatforms().size()); EXPECT_EQ(Platform, *(Document->getPlatforms().begin())); EXPECT_EQ(std::string("/System/Library/Frameworks/A.framework/A"), Document->getInstallName()); EXPECT_EQ(PackedVersion(1, 2, 3), Document->getCurrentVersion()); EXPECT_EQ(PackedVersion(1, 2, 0), Document->getCompatibilityVersion()); EXPECT_EQ(5U, Document->getSwiftABIVersion()); EXPECT_FALSE(Document->isTwoLevelNamespace()); EXPECT_TRUE(Document->isApplicationExtensionSafe()); EXPECT_FALSE(Document->isInstallAPI()); ExportedSymbolSeq Reexports, Undefineds; for (const auto *Sym : Document->symbols()) { ExportedSymbol Temp = ExportedSymbol{Sym->getKind(), std::string(Sym->getName()), Sym->isWeakDefined(), Sym->isThreadLocalValue()}; EXPECT_FALSE(Sym->isWeakReferenced()); if (Sym->isUndefined()) Undefineds.emplace_back(std::move(Temp)); else Sym->isReexported() ? Reexports.emplace_back(std::move(Temp)) : Exports.emplace_back(std::move(Temp)); } llvm::sort(Exports.begin(), Exports.end()); llvm::sort(Reexports.begin(), Reexports.end()); llvm::sort(Undefineds.begin(), Undefineds.end()); static ExportedSymbol ExpectedExportedSymbols[] = { {SymbolKind::GlobalSymbol, "_symA", false, false}, {SymbolKind::GlobalSymbol, "_symAB", false, false}, }; static ExportedSymbol ExpectedReexportedSymbols[] = { {SymbolKind::GlobalSymbol, "_symC", false, false}, }; static ExportedSymbol ExpectedUndefinedSymbols[] = { {SymbolKind::GlobalSymbol, "_symD", false, false}, }; EXPECT_EQ(sizeof(ExpectedExportedSymbols) / sizeof(ExportedSymbol), Exports.size()); EXPECT_EQ(sizeof(ExpectedReexportedSymbols) / sizeof(ExportedSymbol), Reexports.size()); EXPECT_EQ(sizeof(ExpectedUndefinedSymbols) / sizeof(ExportedSymbol), Undefineds.size()); EXPECT_TRUE(std::equal(Exports.begin(), Exports.end(), std::begin(ExpectedExportedSymbols))); EXPECT_TRUE(std::equal(Reexports.begin(), Reexports.end(), std::begin(ExpectedReexportedSymbols))); EXPECT_TRUE(std::equal(Undefineds.begin(), Undefineds.end(), std::begin(ExpectedUndefinedSymbols))); } TEST(TBDv4, WriteFile) { static const char TBDv4File[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-macos, x86_64-ios-simulator ]\n" "uuids:\n" " - target: i386-macos\n" " value: 00000000-0000-0000-0000-000000000000\n" " - target: x86_64-ios-simulator\n" " value: 11111111-1111-1111-1111-111111111111\n" "flags: [ installapi ]\n" "install-name: 'Umbrella.framework/Umbrella'\n" "current-version: 1.2.3\n" "compatibility-version: 0\n" "swift-abi-version: 5\n" "parent-umbrella:\n" " - targets: [ i386-macos, x86_64-ios-simulator ]\n" " umbrella: System\n" "allowable-clients:\n" " - targets: [ i386-macos ]\n" " clients: [ ClientA ]\n" "exports:\n" " - targets: [ i386-macos ]\n" " symbols: [ _symA ]\n" " objc-classes: [ Class1 ]\n" " weak-symbols: [ _symC ]\n" " - targets: [ x86_64-ios-simulator ]\n" " symbols: [ _symB ]\n" "...\n"; InterfaceFile File; TargetList Targets = { Target(AK_i386, PlatformKind::macOS), Target(AK_x86_64, PlatformKind::iOSSimulator), }; UUIDs uuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"}, {Targets[1], "11111111-1111-1111-1111-111111111111"}}; File.setInstallName("Umbrella.framework/Umbrella"); File.setFileType(FileType::TBD_V4); File.addTargets(Targets); File.addUUID(uuids[0].first, uuids[0].second); File.addUUID(uuids[1].first, uuids[1].second); File.setCurrentVersion(PackedVersion(1, 2, 3)); File.setTwoLevelNamespace(); File.setInstallAPI(true); File.setApplicationExtensionSafe(true); File.setSwiftABIVersion(5); File.addAllowableClient("ClientA", Targets[0]); File.addParentUmbrella(Targets[0], "System"); File.addParentUmbrella(Targets[1], "System"); File.addSymbol(SymbolKind::GlobalSymbol, "_symA", {Targets[0]}); File.addSymbol(SymbolKind::GlobalSymbol, "_symB", {Targets[1]}); File.addSymbol(SymbolKind::GlobalSymbol, "_symC", {Targets[0]}, SymbolFlags::WeakDefined); File.addSymbol(SymbolKind::ObjectiveCClass, "Class1", {Targets[0]}); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); Error Result = TextAPIWriter::writeToStream(OS, File); EXPECT_FALSE(Result); EXPECT_STREQ(TBDv4File, Buffer.c_str()); } TEST(TBDv4, WriteMultipleDocuments) { static const char TBDv4Inlines[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n" "uuids:\n" " - target: i386-maccatalyst\n" " value: 00000000-0000-0000-0000-000000000002\n" " - target: x86_64-maccatalyst\n" " value: 11111111-1111-1111-1111-111111111112\n" "install-name: " "'/System/Library/Frameworks/Umbrella.framework/Umbrella'\n" "reexported-libraries:\n" " - targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n" " libraries: [ '/System/Library/Frameworks/A.framework/A' ]\n" "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n" "uuids:\n" " - target: i386-maccatalyst\n" " value: 00000000-0000-0000-0000-000000000000\n" " - target: x86_64-maccatalyst\n" " value: 11111111-1111-1111-1111-111111111111\n" "install-name: '/System/Library/Frameworks/A.framework/A'\n" "exports:\n" " - targets: [ i386-maccatalyst ]\n" " weak-symbols: [ _symC ]\n" " - targets: [ i386-maccatalyst, x86_64-maccatalyst ]\n" " symbols: [ _symA ]\n" " objc-classes: [ Class1 ]\n" " - targets: [ x86_64-maccatalyst ]\n" " symbols: [ _symAB ]\n" "...\n"; InterfaceFile File; PlatformKind Platform = PlatformKind::macCatalyst; TargetList Targets = { Target(AK_i386, Platform), Target(AK_x86_64, Platform), }; UUIDs Uuids = {{Targets[0], "00000000-0000-0000-0000-000000000002"}, {Targets[1], "11111111-1111-1111-1111-111111111112"}}; File.setInstallName("/System/Library/Frameworks/Umbrella.framework/Umbrella"); File.setFileType(FileType::TBD_V4); File.addTargets(Targets); File.addUUID(Uuids[0].first, Uuids[0].second); File.addUUID(Uuids[1].first, Uuids[1].second); File.setCompatibilityVersion(PackedVersion(1, 0, 0)); File.setCurrentVersion(PackedVersion(1, 0, 0)); File.setTwoLevelNamespace(); File.setApplicationExtensionSafe(true); File.addReexportedLibrary("/System/Library/Frameworks/A.framework/A", Targets[0]); File.addReexportedLibrary("/System/Library/Frameworks/A.framework/A", Targets[1]); // Write Second Document Uuids = {{Targets[0], "00000000-0000-0000-0000-000000000000"}, {Targets[1], "11111111-1111-1111-1111-111111111111"}}; InterfaceFile Document; Document.setInstallName("/System/Library/Frameworks/A.framework/A"); Document.setFileType(FileType::TBD_V4); Document.addTargets(Targets); Document.addUUID(Uuids[0].first, Uuids[0].second); Document.addUUID(Uuids[1].first, Uuids[1].second); Document.setCompatibilityVersion(PackedVersion(1, 0, 0)); Document.setCurrentVersion(PackedVersion(1, 0, 0)); Document.setTwoLevelNamespace(); Document.setApplicationExtensionSafe(true); Document.addSymbol(SymbolKind::GlobalSymbol, "_symA", Targets); Document.addSymbol(SymbolKind::GlobalSymbol, "_symAB", {Targets[1]}); Document.addSymbol(SymbolKind::GlobalSymbol, "_symC", {Targets[0]}, SymbolFlags::WeakDefined); Document.addSymbol(SymbolKind::ObjectiveCClass, "Class1", Targets); File.addDocument(std::make_shared(std::move(Document))); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); Error Result = TextAPIWriter::writeToStream(OS, File); EXPECT_FALSE(Result); EXPECT_STREQ(TBDv4Inlines, Buffer.c_str()); } TEST(TBDv4, MultipleTargets) { static const char TBDv4MultipleTargets[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-maccatalyst, x86_64-tvos, arm64-ios ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4MultipleTargets, "Test.tbd")); EXPECT_TRUE(!!Result); PlatformSet Platforms; Platforms.insert(PlatformKind::macCatalyst); Platforms.insert(PlatformKind::tvOS); Platforms.insert(PlatformKind::iOS); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(AK_x86_64 | AK_arm64 | AK_i386, File->getArchitectures()); EXPECT_EQ(Platforms.size(), File->getPlatforms().size()); for (auto Platform : File->getPlatforms()) EXPECT_EQ(Platforms.count(Platform), 1U); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4MultipleTargets), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, MultipleTargetsSameArch) { static const char TBDv4TargetsSameArch[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-tvos , x86_64-maccatalyst ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4TargetsSameArch, "Test.tbd")); EXPECT_TRUE(!!Result); PlatformSet Platforms; Platforms.insert(PlatformKind::tvOS); Platforms.insert(PlatformKind::macCatalyst); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures()); EXPECT_EQ(Platforms.size(), File->getPlatforms().size()); for (auto Platform : File->getPlatforms()) EXPECT_EQ(Platforms.count(Platform), 1U); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4TargetsSameArch), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, MultipleTargetsSamePlatform) { static const char TBDv4MultipleTargetsSamePlatform[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ armv7k-ios , arm64-ios]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get( MemoryBufferRef(TBDv4MultipleTargetsSamePlatform, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(AK_arm64 | AK_armv7k, File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::iOS, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4MultipleTargetsSamePlatform), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_maccatalyst) { static const char TBDv4TargetMacCatalyst[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-maccatalyst ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4TargetMacCatalyst, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::macCatalyst, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4TargetMacCatalyst), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_x86_ios) { static const char TBDv4Targetx86iOS[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-ios ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4Targetx86iOS, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::iOS, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4Targetx86iOS), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_arm_bridgeOS) { static const char TBDv4PlatformBridgeOS[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ armv7k-bridgeos ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4PlatformBridgeOS, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::bridgeOS, *File->getPlatforms().begin()); EXPECT_EQ(ArchitectureSet(AK_armv7k), File->getArchitectures()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4PlatformBridgeOS), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_arm_iOS) { static const char TBDv4ArchArm64e[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ arm64e-ios ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4ArchArm64e, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::iOS, *File->getPlatforms().begin()); EXPECT_EQ(ArchitectureSet(AK_arm64e), File->getArchitectures()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4ArchArm64e), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_x86_macos) { static const char TBDv4Targetx86MacOS[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4Targetx86MacOS, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::macOS, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4Targetx86MacOS), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_x86_ios_simulator) { static const char TBDv4Targetx86iOSSim[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-ios-simulator ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4Targetx86iOSSim, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::iOSSimulator, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4Targetx86iOSSim), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_x86_tvos_simulator) { static const char TBDv4x86tvOSSim[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-tvos-simulator ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4x86tvOSSim, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_x86_64), File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::tvOSSimulator, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4x86tvOSSim), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Target_i386_watchos_simulator) { static const char TBDv4i386watchOSSim[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ i386-watchos-simulator ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4i386watchOSSim, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(ArchitectureSet(AK_i386), File->getArchitectures()); EXPECT_EQ(File->getPlatforms().size(), 1U); EXPECT_EQ(PlatformKind::watchOSSimulator, *File->getPlatforms().begin()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4i386watchOSSim), stripWhitespace(Buffer.c_str())); } +TEST(TBDv4, Target_i386_driverkit) { + static const char TBDv4i386DriverKit[] = "--- !tapi-tbd\n" + "tbd-version: 4\n" + "targets: [ i386-driverkit ]\n" + "install-name: Test.dylib\n" + "...\n"; + + Expected Result = + TextAPIReader::get(MemoryBufferRef(TBDv4i386DriverKit, "Test.tbd")); + EXPECT_TRUE(!!Result); + TBDFile File = std::move(Result.get()); + EXPECT_EQ(FileType::TBD_V4, File->getFileType()); + EXPECT_EQ(ArchitectureSet(AK_i386), File->getArchitectures()); + EXPECT_EQ(File->getPlatforms().size(), 1U); + EXPECT_EQ(PlatformKind::driverKit, *File->getPlatforms().begin()); + + SmallString<4096> Buffer; + raw_svector_ostream OS(Buffer); + auto WriteResult = TextAPIWriter::writeToStream(OS, *File); + EXPECT_TRUE(!WriteResult); + EXPECT_EQ(stripWhitespace(TBDv4i386DriverKit), + stripWhitespace(Buffer.c_str())); +} + TEST(TBDv4, Swift_1) { static const char TBDv4SwiftVersion1[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "swift-abi-version: 1\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4SwiftVersion1, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(1U, File->getSwiftABIVersion()); // No writer test because we emit "swift-abi-version:1.0". } TEST(TBDv4, Swift_2) { static const char TBDv4Swift2[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "swift-abi-version: 2\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4Swift2, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(2U, File->getSwiftABIVersion()); // No writer test because we emit "swift-abi-version:2.0". } TEST(TBDv4, Swift_5) { static const char TBDv4SwiftVersion5[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "swift-abi-version: 5\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4SwiftVersion5, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(5U, File->getSwiftABIVersion()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4SwiftVersion5), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, Swift_99) { static const char TBDv4SwiftVersion99[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "swift-abi-version: 99\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4SwiftVersion99, "Test.tbd")); EXPECT_TRUE(!!Result); TBDFile File = std::move(Result.get()); EXPECT_EQ(FileType::TBD_V4, File->getFileType()); EXPECT_EQ(99U, File->getSwiftABIVersion()); SmallString<4096> Buffer; raw_svector_ostream OS(Buffer); auto WriteResult = TextAPIWriter::writeToStream(OS, *File); EXPECT_TRUE(!WriteResult); EXPECT_EQ(stripWhitespace(TBDv4SwiftVersion99), stripWhitespace(Buffer.c_str())); } TEST(TBDv4, InvalidArchitecture) { static const char TBDv4UnknownArch[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ foo-macos ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4UnknownArch, "Test.tbd")); EXPECT_FALSE(!!Result); std::string ErrorMessage = toString(Result.takeError()); EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown " "architecture\ntargets: [ foo-macos ]\n" " ^~~~~~~~~~\n", ErrorMessage); } TEST(TBDv4, InvalidPlatform) { static const char TBDv4FInvalidPlatform[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-maos ]\n" "install-name: Test.dylib\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4FInvalidPlatform, "Test.tbd")); EXPECT_FALSE(!!Result); std::string ErrorMessage = toString(Result.takeError()); EXPECT_EQ("malformed file\nTest.tbd:3:12: error: unknown platform\ntargets: " "[ x86_64-maos ]\n" " ^~~~~~~~~~~~\n", ErrorMessage); } TEST(TBDv4, MalformedFile1) { static const char TBDv4MalformedFile1[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4MalformedFile1, "Test.tbd")); EXPECT_FALSE(!!Result); std::string ErrorMessage = toString(Result.takeError()); ASSERT_EQ("malformed file\nTest.tbd:2:1: error: missing required key " "'targets'\ntbd-version: 4\n^\n", ErrorMessage); } TEST(TBDv4, MalformedFile2) { static const char TBDv4MalformedFile2[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "foobar: \"unsupported key\"\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4MalformedFile2, "Test.tbd")); EXPECT_FALSE(!!Result); std::string ErrorMessage = toString(Result.takeError()); ASSERT_EQ( "malformed file\nTest.tbd:5:9: error: unknown key 'foobar'\nfoobar: " "\"unsupported key\"\n ^~~~~~~~~~~~~~~~~\n", ErrorMessage); } TEST(TBDv4, MalformedFile3) { static const char TBDv4MalformedSwift[] = "--- !tapi-tbd\n" "tbd-version: 4\n" "targets: [ x86_64-macos ]\n" "install-name: Test.dylib\n" "swift-abi-version: 1.1\n" "...\n"; Expected Result = TextAPIReader::get(MemoryBufferRef(TBDv4MalformedSwift, "Test.tbd")); EXPECT_FALSE(!!Result); std::string ErrorMessage = toString(Result.takeError()); EXPECT_EQ("malformed file\nTest.tbd:5:20: error: invalid Swift ABI " "version.\nswift-abi-version: 1.1\n ^~~\n", ErrorMessage); } } // end namespace TBDv4