/* * 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 createLines 1–500 of 982. Load more
/* * 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