982 lines · 35.6 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.  */ #define DEBUG_MACHO 0#define dprintf if (DEBUG_MACHO) printf 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_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD)#define LC_MAIN (0x28|LC_REQ_DYLD) 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 */}; #define S_REGULAR                       0x0#define S_ZEROFILL                      0x1#define S_NON_LAZY_SYMBOL_POINTERS      0x6#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 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 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 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 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_ro_data,    sk_uw_info,    sk_nl_ptr,  // non-lazy pointers, aka GOT    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[4], 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, *wdata, *indirsyms, *stubs;    int stubsym;    uint32_t ilocal, iextdef, iundef;}; #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, char *name){    struct segment_command_64 *sc = add_lc(mo, LC_SEGMENT_64, sizeof(*sc));    strncpy(sc->segname, name, 16);    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, char *name){    struct segment_command_64 *seg = *_seg;    int ret = seg->nsects;    struct section_64 *sec;    seg->nsects++;    seg->cmdsize += sizeof(*sec);    seg = tcc_realloc(seg, sizeof(*seg) + seg->nsects * sizeof(*sec));    sec = (struct section_64*)((char*)seg + sizeof(*seg)) + ret;    memset(sec, 0, sizeof(*sec));    strncpy(sec->sectname, name, 16);    strncpy(sec->segname, seg->segname, 16);    *_seg = seg;    return ret;} static struct section_64 *get_section(struct segment_command_64 *seg, int i){    return (struct section_64*)((char*)seg + sizeof(*seg)) + i;} static void * add_dylib(struct macho *mo, char *name){    struct dylib_command *lc;    int sz = (sizeof(*lc) + strlen(name) + 1 + 7) & -8;    lc = add_lc(mo, LC_LOAD_DYLIB, sz);    lc->name = sizeof(*lc);    strcpy((char*)lc + lc->name, name);    lc->timestamp = 2;    lc->current_version = 1 << 16;    lc->compatibility_version = 1 << 16;    return lc;} static void check_relocs(TCCState *s1, struct macho *mo){    Section *s;    ElfW_Rel *rel;    ElfW(Sym) *sym;    int i, type, gotplt_entry, sym_index, for_code;    struct sym_attr *attr;     s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE);    mo->indirsyms = new_section(s1, "LEINDIR", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE);    for (i = 1; i < s1->nb_sections; i++) {        s = s1->sections[i];        if (s->sh_type != SHT_RELX)            continue;        for_each_elem(s, 0, rel, ElfW_Rel) {            type = ELFW(R_TYPE)(rel->r_info);            gotplt_entry = gotplt_entry_type(type);            for_code = code_reloc(type);            /* We generate a non-lazy pointer for used undefined symbols               and for defined symbols that must have a place for their               address due to codegen (i.e. a reloc requiring a got slot).  */            sym_index = ELFW(R_SYM)(rel->r_info);            sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];            if (sym->st_shndx == SHN_UNDEF                || gotplt_entry == ALWAYS_GOTPLT_ENTRY) {                attr = get_sym_attr(s1, sym_index, 1);                if (!attr->dyn_index) {                    uint32_t *pi = section_ptr_add(mo->indirsyms, sizeof(*pi));                    attr->got_offset = s1->got->data_offset;                    attr->plt_offset = -1;                    attr->dyn_index = 1; /* used as flag */                    section_ptr_add(s1->got, PTR_SIZE);                    if (ELFW(ST_BIND)(sym->st_info) == STB_LOCAL) {                        if (sym->st_shndx == SHN_UNDEF)                          tcc_error("undefined local symbol???");                        *pi = INDIRECT_SYMBOL_LOCAL;                        /* The pointer slot we generated must point to the                           symbol, whose address is only known after layout,                           so register a simple relocation for that.  */                        put_elf_reloc(s1->symtab, s1->got, attr->got_offset,                                      R_DATA_PTR, sym_index);                    } else                      *pi = mo->e2msym[sym_index];                }                if (for_code) {                    if (attr->plt_offset == -1) {                        uint8_t *jmp;                        attr->plt_offset = mo->stubs->data_offset;                        jmp = section_ptr_add(mo->stubs, 6);                        jmp[0] = 0xff;  /* jmpq *ofs(%rip) */                        jmp[1] = 0x25;                        put_elf_reloc(s1->symtab, mo->stubs,                                      attr->plt_offset + 2,                                      R_X86_64_GOTPCREL, sym_index);                    }                    rel->r_info = ELFW(R_INFO)(mo->stubsym, type);                    rel->r_addend += attr->plt_offset;                }            }        }    }} static int check_symbols(TCCState *s1, struct macho *mo){    int sym_index, sym_end;    int ret = 0;     mo->ilocal = mo->iextdef = mo->iundef = -1;    sym_end = symtab_section->data_offset / sizeof(ElfW(Sym));    for (sym_index = 1; sym_index < sym_end; ++sym_index) {        int elf_index = ((struct nlist_64 *)mo->symtab->data + sym_index - 1)->n_value;        ElfW(Sym) *sym = (ElfW(Sym) *)symtab_section->data + elf_index;        const char *name = (char*)symtab_section->link->data + sym->st_name;        unsigned type = ELFW(ST_TYPE)(sym->st_info);        unsigned bind = ELFW(ST_BIND)(sym->st_info);        unsigned vis  = ELFW(ST_VISIBILITY)(sym->st_other);         dprintf("%4d (%4d): %09llx %4d %4d %4d %3d %s\n",                sym_index, elf_index, sym->st_value,                type, bind, vis, sym->st_shndx, name);        if (bind == STB_LOCAL) {            if (mo->ilocal == -1)              mo->ilocal = sym_index - 1;            if (mo->iextdef != -1 || mo->iundef != -1)              tcc_error("local syms after global ones");        } else if (sym->st_shndx != SHN_UNDEF) {            if (mo->iextdef == -1)              mo->iextdef = sym_index - 1;            if (mo->iundef != -1)              tcc_error("external defined symbol after undefined");        } else if (sym->st_shndx == SHN_UNDEF) {            if (mo->iundef == -1)              mo->iundef = sym_index - 1;            if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK                || find_elf_sym(s1->dynsymtab_section, name)) {                /* Mark the symbol as coming from a dylib so that                   relocate_syms doesn't complain.  Normally bind_exe_dynsyms                   would do this check, and place the symbol into dynsym                   which is checked by relocate_syms.  But Mach-O doesn't use                   bind_exe_dynsyms.  */                sym->st_shndx = SHN_FROMDLL;                continue;            }            tcc_error_noabort("undefined symbol '%s'", name);            ret = -1;        }    }    return ret;} static void convert_symbol(TCCState *s1, struct macho *mo, struct nlist_64 *pn){    struct nlist_64 n = *pn;    ElfSym *sym = (ElfW(Sym) *)symtab_section->data + pn->n_value;    const char *name = (char*)symtab_section->link->data + sym->st_name;    switch(ELFW(ST_TYPE)(sym->st_info)) {    case STT_NOTYPE:    case STT_OBJECT:    case STT_FUNC:    case STT_SECTION:        n.n_type = N_SECT;        break;    case STT_FILE:        n.n_type = N_ABS;        break;    default:        tcc_error("unhandled ELF symbol type %d %s",                  ELFW(ST_TYPE)(sym->st_info), name);    }    if (sym->st_shndx == SHN_UNDEF)      tcc_error("should have been rewritten to SHN_FROMDLL: %s", name);    else if (sym->st_shndx == SHN_FROMDLL)      n.n_type = N_UNDF, n.n_sect = 0;    else if (sym->st_shndx == SHN_ABS)      n.n_type = N_ABS, n.n_sect = 0;    else if (sym->st_shndx >= SHN_LORESERVE)      tcc_error("unhandled ELF symbol section %d %s", sym->st_shndx, name);    else if (!mo->elfsectomacho[sym->st_shndx])      tcc_error("ELF section %d not mapped into Mach-O for symbol %s",                sym->st_shndx, name);    else      n.n_sect = mo->elfsectomacho[sym->st_shndx];    if (ELFW(ST_BIND)(sym->st_info) == STB_GLOBAL)      n.n_type |=  N_EXT;    else if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK)      n.n_desc |= N_WEAK_REF | (n.n_type != N_UNDF ? N_WEAK_DEF : 0);    n.n_strx = pn->n_strx;    n.n_value = sym->st_value;    *pn = n;} static void convert_symbols(TCCState *s1, struct macho *mo){    struct nlist_64 *pn;    for_each_elem(mo->symtab, 0, pn, struct nlist_64)        convert_symbol(s1, mo, pn);} static int machosymcmp(const void *_a, const void *_b){    TCCState *s1 = tcc_state;    int ea = ((struct nlist_64 *)_a)->n_value;    int eb = ((struct nlist_64 *)_b)->n_value;    ElfSym *sa = (ElfSym *)symtab_section->data + ea;    ElfSym *sb = (ElfSym *)symtab_section->data + eb;    int r;    /* locals, then defined externals, then undefined externals, the       last two sections also by name, otherwise stable sort */    r = (ELFW(ST_BIND)(sb->st_info) == STB_LOCAL)        - (ELFW(ST_BIND)(sa->st_info) == STB_LOCAL);    if (r)      return r;    r = (sa->st_shndx == SHN_UNDEF) - (sb->st_shndx == SHN_UNDEF);    if (r)      return r;    if (ELFW(ST_BIND)(sa->st_info) != STB_LOCAL) {        const char * na = (char*)symtab_section->link->data + sa->st_name;        const char * nb = (char*)symtab_section->link->data + sb->st_name;        r = strcmp(na, nb);        if (r)          return r;    }    return ea - eb;} static void create_symtab(TCCState *s1, struct macho *mo){    int sym_index, sym_end;    struct nlist_64 *pn;     /* Stub creation belongs to check_relocs, but we need to create

Lines 1–500 of 982.