2477 lines · 90.3 KB · C
RawDownload
/* * Mach-O file handling for TCC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */#include "tcc.h" /* In order to make life easy for us we are generating Mach-O files which   don't make use of some modern features, but which aren't entirely classic   either in that they do use some modern features.  We're also only   generating 64bit Mach-O files, and only native endian at that.    In particular we're generating executables that don't make use of   DYLD_INFO for dynamic linking info, as that requires us building a   trie of exported names.  We're simply using classic symbol tables which   are still supported by modern dyld.    But we do use LC_MAIN, which is a "modern" feature in order to not have   to setup our own crt code.  We're not using lazy linking, so even function   calls are resolved at startup.  */ #if !defined TCC_TARGET_X86_64 && !defined TCC_TARGET_ARM64#error Platform not supported#endif /* XXX: this file uses tcc_error() to the effect of exit(1) */#undef _tcc_error #define DEBUG_MACHO 0#define dprintf if (DEBUG_MACHO) printf #define MH_EXECUTE              (0x2)#define MH_DYLDLINK             (0x4)#define MH_DYLIB                (0x6)#define MH_PIE                  (0x200000) #define CPU_SUBTYPE_LIB64       (0x80000000)#define CPU_SUBTYPE_X86_ALL     (3)#define CPU_SUBTYPE_ARM64_ALL   (0) #define CPU_ARCH_ABI64          (0x01000000) #define CPU_TYPE_X86            (7)#define CPU_TYPE_X86_64         (CPU_TYPE_X86 | CPU_ARCH_ABI64)#define CPU_TYPE_ARM            (12)#define CPU_TYPE_ARM64          (CPU_TYPE_ARM | CPU_ARCH_ABI64) struct fat_header {    uint32_t        magic;          /* FAT_MAGIC or FAT_MAGIC_64 */    uint32_t        nfat_arch;      /* number of structs that follow */}; struct fat_arch {    int             cputype;        /* cpu specifier (int) */    int             cpusubtype;     /* machine specifier (int) */    uint32_t        offset;         /* file offset to this object file */    uint32_t        size;           /* size of this object file */    uint32_t        align;          /* alignment as a power of 2 */}; #define FAT_MAGIC       0xcafebabe#define FAT_CIGAM       0xbebafeca#define FAT_MAGIC_64    0xcafebabf#define FAT_CIGAM_64    0xbfbafeca struct mach_header {    uint32_t        magic;          /* mach magic number identifier */    int             cputype;        /* cpu specifier */    int             cpusubtype;     /* machine specifier */    uint32_t        filetype;       /* type of file */    uint32_t        ncmds;          /* number of load commands */    uint32_t        sizeofcmds;     /* the size of all the load commands */    uint32_t        flags;          /* flags */}; struct mach_header_64 {    struct mach_header  mh;    uint32_t            reserved;       /* reserved, pad to 64bit */}; /* Constant for the magic field of the mach_header (32-bit architectures) */#define MH_MAGIC        0xfeedface      /* the mach magic number */#define MH_CIGAM        0xcefaedfe      /* NXSwapInt(MH_MAGIC) */#define MH_MAGIC_64     0xfeedfacf      /* the 64-bit mach magic number */#define MH_CIGAM_64     0xcffaedfe      /* NXSwapInt(MH_MAGIC_64) */ struct load_command {    uint32_t        cmd;            /* type of load command */    uint32_t        cmdsize;        /* total size of command in bytes */}; #define LC_REQ_DYLD 0x80000000#define LC_SYMTAB        0x2#define LC_DYSYMTAB      0xb#define LC_LOAD_DYLIB    0xc#define LC_ID_DYLIB      0xd#define LC_LOAD_DYLINKER 0xe#define LC_SEGMENT_64    0x19#define LC_RPATH (0x1c | LC_REQ_DYLD)#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD)#define LC_DYLD_INFO_ONLY (0x22|LC_REQ_DYLD)#define LC_MAIN (0x28|LC_REQ_DYLD)#define LC_SOURCE_VERSION 0x2A#define LC_BUILD_VERSION 0x32#define LC_DYLD_EXPORTS_TRIE (0x33 | LC_REQ_DYLD)#define LC_DYLD_CHAINED_FIXUPS (0x34 | LC_REQ_DYLD) #define SG_READ_ONLY    0x10 /* This segment is made read-only after fixups */ typedef int vm_prot_t; struct segment_command_64 { /* for 64-bit architectures */    uint32_t        cmd;            /* LC_SEGMENT_64 */    uint32_t        cmdsize;        /* includes sizeof section_64 structs */    char            segname[16];    /* segment name */    uint64_t        vmaddr;         /* memory address of this segment */    uint64_t        vmsize;         /* memory size of this segment */    uint64_t        fileoff;        /* file offset of this segment */    uint64_t        filesize;       /* amount to map from the file */    vm_prot_t       maxprot;        /* maximum VM protection */    vm_prot_t       initprot;       /* initial VM protection */    uint32_t        nsects;         /* number of sections in segment */    uint32_t        flags;          /* flags */}; struct section_64 { /* for 64-bit architectures */    char            sectname[16];   /* name of this section */    char            segname[16];    /* segment this section goes in */    uint64_t        addr;           /* memory address of this section */    uint64_t        size;           /* size in bytes of this section */    uint32_t        offset;         /* file offset of this section */    uint32_t        align;          /* section alignment (power of 2) */    uint32_t        reloff;         /* file offset of relocation entries */    uint32_t        nreloc;         /* number of relocation entries */    uint32_t        flags;          /* flags (section type and attributes)*/    uint32_t        reserved1;      /* reserved (for offset or index) */    uint32_t        reserved2;      /* reserved (for count or sizeof) */    uint32_t        reserved3;      /* reserved */}; enum {    DYLD_CHAINED_IMPORT          = 1,}; struct dyld_chained_fixups_header {    uint32_t    fixups_version; ///< 0    uint32_t    starts_offset;  ///< Offset of dyld_chained_starts_in_image.    uint32_t    imports_offset; ///< Offset of imports table in chain_data.    uint32_t    symbols_offset; ///< Offset of symbol strings in chain_data.    uint32_t    imports_count;  ///< Number of imported symbol names.    uint32_t    imports_format; ///< DYLD_CHAINED_IMPORT*    uint32_t    symbols_format; ///< 0 => uncompressed, 1 => zlib compressed}; struct dyld_chained_starts_in_image{    uint32_t    seg_count;    uint32_t    seg_info_offset[1];  // each entry is offset into this struct for that segment    // followed by pool of dyld_chain_starts_in_segment data}; enum {    DYLD_CHAINED_PTR_64                     =  2,    // target is vmaddr    DYLD_CHAINED_PTR_64_OFFSET              =  6,    // target is vm offset}; enum {    DYLD_CHAINED_PTR_START_NONE   = 0xFFFF, // used in page_start[] to denote a page with no fixups}; #define SEG_PAGE_SIZE 16384 struct dyld_chained_starts_in_segment{    uint32_t    size;               // size of this (amount kernel needs to copy)    uint16_t    page_size;          // 0x1000 or 0x4000    uint16_t    pointer_format;     // DYLD_CHAINED_PTR_*    uint64_t    segment_offset;     // offset in memory to start of segment    uint32_t    max_valid_pointer;  // for 32-bit OS, any value beyond this is not a pointer    uint16_t    page_count;         // how many pages are in array    uint16_t    page_start[1];      // each entry is offset in each page of first element in chain                                    // or DYLD_CHAINED_PTR_START_NONE if no fixups on page}; enum BindSpecialDylib {  BIND_SPECIAL_DYLIB_FLAT_LOOKUP = -2,}; struct dyld_chained_import{    uint32_t    lib_ordinal :  8,                weak_import :  1,                name_offset : 23;}; struct dyld_chained_ptr_64_rebase{    uint64_t    target    : 36,    // vmaddr, 64GB max image size                high8     :  8,    // top 8 bits set to this after slide added                reserved  :  7,    // all zeros                next      : 12,    // 4-byte stride                bind      :  1;    // == 0}; struct dyld_chained_ptr_64_bind{    uint64_t    ordinal   : 24,                addend    :  8,   // 0 thru 255                reserved  : 19,   // all zeros                next      : 12,   // 4-byte stride                bind      :  1;   // == 1}; #define S_REGULAR                       0x0#define S_ZEROFILL                      0x1#define S_NON_LAZY_SYMBOL_POINTERS      0x6#define S_LAZY_SYMBOL_POINTERS          0x7#define S_SYMBOL_STUBS                  0x8#define S_MOD_INIT_FUNC_POINTERS        0x9#define S_MOD_TERM_FUNC_POINTERS        0xa #define S_ATTR_PURE_INSTRUCTIONS        0x80000000#define S_ATTR_SOME_INSTRUCTIONS        0x00000400#define S_ATTR_DEBUG             	0x02000000  typedef uint32_t lc_str; struct dylib_command {    uint32_t cmd;                   /* LC_ID_DYLIB, LC_LOAD_{,WEAK_}DYLIB,                                       LC_REEXPORT_DYLIB */    uint32_t cmdsize;               /* includes pathname string */    lc_str   name;                  /* library's path name */    uint32_t timestamp;             /* library's build time stamp */    uint32_t current_version;       /* library's current version number */    uint32_t compatibility_version; /* library's compatibility vers number*/}; struct rpath_command {    uint32_t     cmd;           /* LC_RPATH */    uint32_t     cmdsize;       /* includes string */    lc_str       path;          /* path to add to run path */}; struct dylinker_command {    uint32_t        cmd;            /* LC_ID_DYLINKER, LC_LOAD_DYLINKER or                                       LC_DYLD_ENVIRONMENT */    uint32_t        cmdsize;        /* includes pathname string */    lc_str          name;           /* dynamic linker's path name */}; struct linkedit_data_command {    uint32_t    cmd;            /* LC_CODE_SIGNATURE, LC_SEGMENT_SPLIT_INFO,                                   LC_FUNCTION_STARTS, LC_DATA_IN_CODE,                                   LC_DYLIB_CODE_SIGN_DRS,                                   LC_LINKER_OPTIMIZATION_HINT,                                   LC_DYLD_EXPORTS_TRIE, or                                   LC_DYLD_CHAINED_FIXUPS. */    uint32_t    cmdsize;        /* sizeof(struct linkedit_data_command) */    uint32_t    dataoff;        /* file offset of data in __LINKEDIT segment */    uint32_t    datasize;       /* file size of data in __LINKEDIT segment  */}; #define PLATFORM_MACOS 1 struct build_version_command {    uint32_t    cmd;            /* LC_BUILD_VERSION */    uint32_t    cmdsize;        /* sizeof(struct build_version_command) plus */                                /* 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 source_version_command {    uint32_t  cmd;      /* LC_SOURCE_VERSION */    uint32_t  cmdsize;  /* 16 */    uint64_t  version;  /* A.B.C.D.E packed as a24.b10.c10.d10.e10 */}; struct symtab_command {    uint32_t        cmd;            /* LC_SYMTAB */    uint32_t        cmdsize;        /* sizeof(struct symtab_command) */    uint32_t        symoff;         /* symbol table offset */    uint32_t        nsyms;          /* number of symbol table entries */    uint32_t        stroff;         /* string table offset */    uint32_t        strsize;        /* string table size in bytes */}; struct dysymtab_command {    uint32_t cmd;       /* LC_DYSYMTAB */    uint32_t cmdsize;   /* sizeof(struct dysymtab_command) */     uint32_t ilocalsym; /* index to local symbols */    uint32_t nlocalsym; /* number of local symbols */     uint32_t iextdefsym;/* index to externally defined symbols */    uint32_t nextdefsym;/* number of externally defined symbols */     uint32_t iundefsym; /* index to undefined symbols */    uint32_t nundefsym; /* number of undefined symbols */     uint32_t tocoff;    /* file offset to table of contents */    uint32_t ntoc;      /* number of entries in table of contents */     uint32_t modtaboff; /* file offset to module table */    uint32_t nmodtab;   /* number of module table entries */     uint32_t extrefsymoff;  /* offset to referenced symbol table */    uint32_t nextrefsyms;   /* number of referenced symbol table entries */     uint32_t indirectsymoff;/* file offset to the indirect symbol table */    uint32_t nindirectsyms; /* number of indirect symbol table entries */     uint32_t extreloff; /* offset to external relocation entries */    uint32_t nextrel;   /* number of external relocation entries */    uint32_t locreloff; /* offset to local relocation entries */    uint32_t nlocrel;   /* number of local relocation entries */}; #define BIND_OPCODE_DONE                                        0x00#define BIND_OPCODE_SET_DYLIB_SPECIAL_IMM                       0x30#define BIND_OPCODE_SET_SYMBOL_TRAILING_FLAGS_IMM               0x40#define BIND_OPCODE_SET_TYPE_IMM                                0x50#define BIND_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB                 0x70#define BIND_OPCODE_DO_BIND                                     0x90 #define BIND_SYMBOL_FLAGS_WEAK_IMPORT                           0x1 #define BIND_TYPE_POINTER                                       1#define BIND_SPECIAL_DYLIB_FLAT_LOOKUP                          -2 #define REBASE_OPCODE_DONE                                      0x00#define REBASE_OPCODE_SET_TYPE_IMM                              0x10#define REBASE_OPCODE_SET_SEGMENT_AND_OFFSET_ULEB               0x20#define REBASE_OPCODE_DO_REBASE_IMM_TIMES                       0x50 #define REBASE_TYPE_POINTER                                     1 #define EXPORT_SYMBOL_FLAGS_KIND_REGULAR                        0x00#define EXPORT_SYMBOL_FLAGS_KIND_ABSOLUTE                       0x02#define EXPORT_SYMBOL_FLAGS_WEAK_DEFINITION                     0x04 struct dyld_info_command {    uint32_t cmd;             /* LC_DYLD_INFO or LC_DYLD_INFO_ONLY */    uint32_t cmdsize;         /* sizeof(struct dyld_info_command) */    uint32_t rebase_off;      /* file offset to rebase info  */    uint32_t rebase_size;     /* size of rebase info   */    uint32_t bind_off;        /* file offset to binding info   */    uint32_t bind_size;       /* size of binding info  */    uint32_t weak_bind_off;   /* file offset to weak binding info   */    uint32_t weak_bind_size;  /* size of weak binding info  */    uint32_t lazy_bind_off;   /* file offset to lazy binding info */    uint32_t lazy_bind_size;  /* size of lazy binding infs */    uint32_t export_off;      /* file offset to lazy binding info */    uint32_t export_size;     /* size of lazy binding infs */}; #define INDIRECT_SYMBOL_LOCAL   0x80000000 struct entry_point_command {    uint32_t  cmd;      /* LC_MAIN only used in MH_EXECUTE filetypes */    uint32_t  cmdsize;  /* 24 */    uint64_t  entryoff; /* file (__TEXT) offset of main() */    uint64_t  stacksize;/* if not zero, initial stack size */}; enum skind {    sk_unknown = 0,    sk_discard,    sk_text,    sk_stubs,    sk_stub_helper,    sk_ro_data,    sk_uw_info,    sk_nl_ptr,  // non-lazy pointers, aka GOT    sk_debug_info,    sk_debug_abbrev,    sk_debug_line,    sk_debug_aranges,    sk_debug_str,    sk_debug_line_str,    sk_stab,    sk_stab_str,    sk_la_ptr,  // lazy pointers    sk_init,    sk_fini,    sk_rw_data,    sk_bss,    sk_linkedit,    sk_last}; struct nlist_64 {    uint32_t  n_strx;      /* index into the string table */    uint8_t n_type;        /* type flag, see below */    uint8_t n_sect;        /* section number or NO_SECT */    uint16_t n_desc;       /* see <mach-o/stab.h> */    uint64_t n_value;      /* value of this symbol (or stab offset) */}; #define N_UNDF  0x0#define N_ABS   0x2#define N_EXT   0x1#define N_SECT  0xe #define N_WEAK_REF      0x0040#define N_WEAK_DEF      0x0080 struct macho {    struct mach_header_64 mh;    int *seg2lc, nseg;    struct load_command **lc;    struct entry_point_command *ep;    int nlc;    struct {        Section *s;        int machosect;    } sk_to_sect[sk_last];    int *elfsectomacho;    int *e2msym;    Section *symtab, *strtab, *indirsyms, *stubs, *exports;    uint32_t ilocal, iextdef, iundef;    int stubsym, n_got, nr_plt;    int segment[sk_last];#ifdef CONFIG_NEW_MACHO    Section *chained_fixups;    int n_bind;    int n_bind_rebase;    struct bind_rebase {	int section;	int bind;	ElfW_Rel rel;    } *bind_rebase;#else    Section *rebase, *binding, *weak_binding, *lazy_binding;    Section *stub_helper, *la_symbol_ptr;    struct dyld_info_command *dyldinfo;    int helpsym, lasym, dyld_private, dyld_stub_binder;    int n_lazy_bind;        struct s_lazy_bind {        int section;        int bind_offset;        int la_symbol_offset;        ElfW_Rel rel;    } *s_lazy_bind;    int n_rebase;        struct s_rebase {        int section;        ElfW_Rel rel;    } *s_rebase;    int n_bind;     struct bind {        int section;        ElfW_Rel rel;    } *bind;#endif}; #define SHT_LINKEDIT (SHT_LOOS + 42)#define SHN_FROMDLL  (SHN_LOOS + 2)  /* Symbol is undefined, comes from a DLL */ static void * add_lc(struct macho *mo, uint32_t cmd, uint32_t cmdsize){    struct load_command *lc = tcc_mallocz(cmdsize);    lc->cmd = cmd;    lc->cmdsize = cmdsize;    mo->lc = tcc_realloc(mo->lc, sizeof(mo->lc[0]) * (mo->nlc + 1));    mo->lc[mo->nlc++] = lc;    return lc;} static struct segment_command_64 * add_segment(struct macho *mo, const char *name){    struct segment_command_64 *sc = add_lc(mo, LC_SEGMENT_64, sizeof(*sc));    strncpy(sc->segname, name, 16);    mo->seg2lc = tcc_realloc(mo->seg2lc, sizeof(*mo->seg2lc) * (mo->nseg + 1));    mo->seg2lc[mo->nseg++] = mo->nlc - 1;    return sc;} static struct segment_command_64 * get_segment(struct macho *mo, int i){    return (struct segment_command_64 *) (mo->lc[mo->seg2lc[i]]);} static int add_section(struct macho *mo, struct segment_command_64 **_seg, const char *name)

Lines 1–500 of 2477.