| @@ -18,6 +18,7 @@ · 17 unmodified lines |
| 18 | #include "tcc.h" | 18 | #include "tcc.h" |
| 19 | | 19 | |
| 20 | #define DEBUG_MACHO 0 | 20 | #define DEBUG_MACHO 0 |
| | 21 | #define dprintf if (DEBUG_MACHO) printf |
| 21 | | 22 | |
| 22 | struct mach_header { | 23 | struct mach_header { |
| 23 | uint32_t magic; /* mach magic number identifier */ | 24 | uint32_t magic; /* mach magic number identifier */ |
| @@ -231,7 +232,9 @@ struct macho { · 207 unmodified lines |
| 231 | #define LC_SYMTAB 0x2 | 232 | #define LC_SYMTAB 0x2 |
| 232 | #define LC_DYSYMTAB 0xb | 233 | #define LC_DYSYMTAB 0xb |
| 233 | #define LC_LOAD_DYLIB 0xc | 234 | #define LC_LOAD_DYLIB 0xc |
| | 235 | #define LC_ID_DYLIB 0xd |
| 234 | #define LC_LOAD_DYLINKER 0xe | 236 | #define LC_LOAD_DYLINKER 0xe |
| | 237 | #define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD) |
| 235 | #define LC_MAIN (0x28|LC_REQ_DYLD) | 238 | #define LC_MAIN (0x28|LC_REQ_DYLD) |
| 236 | | 239 | |
| 237 | /* Hack for now, 46_grep.c needs fopen, but due to aliasing games | 240 | /* Hack for now, 46_grep.c needs fopen, but due to aliasing games |
| @@ -375,10 +378,9 @@ static int check_symbols(TCCState *s1, struct macho *mo) · 137 unmodified lines |
| 375 | unsigned bind = ELFW(ST_BIND)(sym->st_info); | 378 | unsigned bind = ELFW(ST_BIND)(sym->st_info); |
| 376 | unsigned vis = ELFW(ST_VISIBILITY)(sym->st_other); | 379 | unsigned vis = ELFW(ST_VISIBILITY)(sym->st_other); |
| 377 | | 380 | |
| 378 | if (DEBUG_MACHO) | 381 | dprintf("%4d (%4d): %09llx %4d %4d %4d %3d %s\n", |
| 379 | printf("%4d (%4d): %09llx %4d %4d %4d %3d %s\n", | 382 | sym_index, elf_index, sym->st_value, |
| 380 | sym_index, elf_index, sym->st_value, | 383 | type, bind, vis, sym->st_shndx, name); |
| 381 | type, bind, vis, sym->st_shndx, name); | | |
| 382 | if (bind == STB_LOCAL) { | 384 | if (bind == STB_LOCAL) { |
| 383 | if (mo->ilocal == -1) | 385 | if (mo->ilocal == -1) |
| 384 | mo->ilocal = sym_index - 1; | 386 | mo->ilocal = sym_index - 1; |
| @@ -394,15 +396,12 @@ static int check_symbols(TCCState *s1, struct macho *mo) · 9 unmodified lines |
| 394 | mo->iundef = sym_index - 1; | 396 | mo->iundef = sym_index - 1; |
| 395 | if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) | 397 | if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) |
| 396 | continue; | 398 | continue; |
| 397 | if (get_sym_attr(s1, elf_index, 0)) { | 399 | if (find_elf_sym(s1->dynsymtab_section, name)) { |
| 398 | /* Mark the symbol as coming from a dylib so that | 400 | /* Mark the symbol as coming from a dylib so that |
| 399 | relocate_syms doesn't complain. Normally we would have | 401 | relocate_syms doesn't complain. Normally bind_exe_dynsyms |
| 400 | checked that any dylib in fact exports this one, and listed | 402 | would do this check, and place the symbol into dynsym |
| 401 | those in s1->dynsymtab, which would cause us to enter this | 403 | which is checked by relocate_syms. But Mach-O doesn't use |
| 402 | symbol into s1->dynsym, which is checked by relocate_syms. | 404 | bind_exe_dynsyms. */ |
| 403 | But for now we fake this and regard all undefined as | | |
| 404 | coming from a dylib, and we don't use the dynsymtab/dynsym | | |
| 405 | scheme. */ | | |
| 406 | sym->st_shndx = SHN_FROMDLL; | 405 | sym->st_shndx = SHN_FROMDLL; |
| 407 | continue; | 406 | continue; |
| 408 | } | 407 | } |
| @@ -589,7 +588,11 @@ static void collect_sections(TCCState *s1, struct macho *mo) · 180 unmodified lines |
| 589 | dysymlc->cmdsize = sizeof(*dysymlc); | 588 | dysymlc->cmdsize = sizeof(*dysymlc); |
| 590 | add_lc(mo, dysymlc); | 589 | add_lc(mo, dysymlc); |
| 591 | | 590 | |
| 592 | add_dylib(mo, "/usr/lib/libSystem.B.dylib"); | 591 | for(i = 0; i < s1->nb_loaded_dlls; i++) { |
| | 592 | DLLReference *dllref = s1->loaded_dlls[i]; |
| | 593 | if (dllref->level == 0) |
| | 594 | add_dylib(mo, dllref->name); |
| | 595 | } |
| 593 | | 596 | |
| 594 | mo->linkedit = new_section(s1, "LINKEDIT", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); | 597 | mo->linkedit = new_section(s1, "LINKEDIT", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE); |
| 595 | /* LINKEDIT can't be empty (XXX remove once we have symbol table) */ | 598 | /* LINKEDIT can't be empty (XXX remove once we have symbol table) */ |
| @@ -683,16 +686,14 @@ static void collect_sections(TCCState *s1, struct macho *mo) · 87 unmodified lines |
| 683 | for (s = mo->sk_to_sect[sk].s; s; s = s->prev) { | 686 | for (s = mo->sk_to_sect[sk].s; s; s = s->prev) { |
| 684 | al = s->sh_addralign; | 687 | al = s->sh_addralign; |
| 685 | curaddr = (curaddr + al - 1) & -al; | 688 | curaddr = (curaddr + al - 1) & -al; |
| 686 | if (DEBUG_MACHO) | 689 | dprintf("curaddr now 0x%llx\n", curaddr); |
| 687 | printf("curaddr now 0x%llx\n", curaddr); | | |
| 688 | s->sh_addr = curaddr; | 690 | s->sh_addr = curaddr; |
| 689 | curaddr += s->sh_size; | 691 | curaddr += s->sh_size; |
| 690 | if (s->sh_type != SHT_NOBITS) { | 692 | if (s->sh_type != SHT_NOBITS) { |
| 691 | fileofs = (fileofs + al - 1) & -al; | 693 | fileofs = (fileofs + al - 1) & -al; |
| 692 | s->sh_offset = fileofs; | 694 | s->sh_offset = fileofs; |
| 693 | fileofs += s->sh_size; | 695 | fileofs += s->sh_size; |
| 694 | if (DEBUG_MACHO) | 696 | dprintf("fileofs now %lld\n", fileofs); |
| 695 | printf("fileofs now %lld\n", fileofs); | | |
| 696 | } | 697 | } |
| 697 | if (sec) | 698 | if (sec) |
| 698 | mo->elfsectomacho[s->sh_num] = numsec; | 699 | mo->elfsectomacho[s->sh_num] = numsec; |
| @@ -799,7 +800,7 @@ ST_FUNC int macho_output_file(TCCState *s1, const char *filename) · 100 unmodified lines |
| 799 | int fd, mode, file_type; | 800 | int fd, mode, file_type; |
| 800 | FILE *fp; | 801 | FILE *fp; |
| 801 | int ret = -1; | 802 | int ret = -1; |
| 802 | struct macho mo = {0,}; | 803 | struct macho mo = {}; |
| 803 | | 804 | |
| 804 | file_type = s1->output_type; | 805 | file_type = s1->output_type; |
| 805 | if (file_type == TCC_OUTPUT_OBJ) | 806 | if (file_type == TCC_OUTPUT_OBJ) |
| @@ -845,3 +846,145 @@ ST_FUNC int macho_output_file(TCCState *s1, const char *filename) · 39 unmodified lines |
| 845 | fclose(fp); | 846 | fclose(fp); |
| 846 | return ret; | 847 | return ret; |
| 847 | } | 848 | } |
| | 849 | |
| | 850 | #define FAT_MAGIC 0xcafebabe |
| | 851 | #define FAT_CIGAM 0xbebafeca |
| | 852 | #define FAT_MAGIC_64 0xcafebabf |
| | 853 | #define FAT_CIGAM_64 0xbfbafeca |
| | 854 | |
| | 855 | struct fat_header { |
| | 856 | uint32_t magic; /* FAT_MAGIC or FAT_MAGIC_64 */ |
| | 857 | uint32_t nfat_arch; /* number of structs that follow */ |
| | 858 | }; |
| | 859 | |
| | 860 | struct fat_arch { |
| | 861 | int cputype; /* cpu specifier (int) */ |
| | 862 | int cpusubtype; /* machine specifier (int) */ |
| | 863 | uint32_t offset; /* file offset to this object file */ |
| | 864 | uint32_t size; /* size of this object file */ |
| | 865 | uint32_t align; /* alignment as a power of 2 */ |
| | 866 | }; |
| | 867 | |
| | 868 | #define SWAP(x) (swap ? ntohl(x) : (x)) |
| | 869 | ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev) |
| | 870 | { |
| | 871 | unsigned char buf[sizeof(struct mach_header_64)]; |
| | 872 | void *buf2; |
| | 873 | uint32_t machofs = 0; |
| | 874 | struct fat_header fh; |
| | 875 | struct mach_header mh; |
| | 876 | struct load_command *lc; |
| | 877 | int i, swap = 0; |
| | 878 | const char *soname = filename; |
| | 879 | struct nlist_64 *symtab = 0; |
| | 880 | uint32_t nsyms = 0; |
| | 881 | char *strtab = 0; |
| | 882 | uint32_t strsize = 0; |
| | 883 | uint32_t iextdef = 0; |
| | 884 | uint32_t nextdef = 0; |
| | 885 | DLLReference *dllref; |
| | 886 | |
| | 887 | again: |
| | 888 | if (full_read(fd, buf, sizeof(buf)) != sizeof(buf)) |
| | 889 | return -1; |
| | 890 | memcpy(&fh, buf, sizeof(fh)); |
| | 891 | if (fh.magic == FAT_MAGIC || fh.magic == FAT_CIGAM) { |
| | 892 | struct fat_arch *fa = load_data(fd, sizeof(fh), |
| | 893 | fh.nfat_arch * sizeof(*fa)); |
| | 894 | swap = fh.magic == FAT_CIGAM; |
| | 895 | for (i = 0; i < SWAP(fh.nfat_arch); i++) |
| | 896 | if (SWAP(fa[i].cputype) == 0x01000007 /* CPU_TYPE_X86_64 */ |
| | 897 | && SWAP(fa[i].cpusubtype) == 3) /* CPU_SUBTYPE_X86_ALL */ |
| | 898 | break; |
| | 899 | if (i == SWAP(fh.nfat_arch)) { |
| | 900 | tcc_free(fa); |
| | 901 | return -1; |
| | 902 | } |
| | 903 | machofs = SWAP(fa[i].offset); |
| | 904 | tcc_free(fa); |
| | 905 | lseek(fd, machofs, SEEK_SET); |
| | 906 | goto again; |
| | 907 | } else if (fh.magic == FAT_MAGIC_64 || fh.magic == FAT_CIGAM_64) { |
| | 908 | tcc_warning("%s: Mach-O fat 64bit files of type 0x%x not handled", |
| | 909 | filename, fh.magic); |
| | 910 | return -1; |
| | 911 | } |
| | 912 | |
| | 913 | memcpy(&mh, buf, sizeof(mh)); |
| | 914 | if (mh.magic != MH_MAGIC_64) |
| | 915 | return -1; |
| | 916 | dprintf("found Mach-O at %d\n", machofs); |
| | 917 | buf2 = load_data(fd, machofs + sizeof(struct mach_header_64), mh.sizeofcmds); |
| | 918 | for (i = 0, lc = buf2; i < mh.ncmds; i++) { |
| | 919 | dprintf("lc %2d: 0x%08x\n", i, lc->cmd); |
| | 920 | if (lc->cmd == LC_SYMTAB) { |
| | 921 | struct symtab_command *sc = (struct symtab_command*)lc; |
| | 922 | nsyms = sc->nsyms; |
| | 923 | symtab = load_data(fd, machofs + sc->symoff, nsyms * sizeof(*symtab)); |
| | 924 | strsize = sc->strsize; |
| | 925 | strtab = load_data(fd, machofs + sc->stroff, strsize); |
| | 926 | } else if (lc->cmd == LC_ID_DYLIB) { |
| | 927 | struct dylib_command *dc = (struct dylib_command*)lc; |
| | 928 | soname = (char*)lc + dc->name; |
| | 929 | dprintf(" ID_DYLIB %d 0x%x 0x%x %s\n", |
| | 930 | dc->timestamp, dc->current_version, |
| | 931 | dc->compatibility_version, soname); |
| | 932 | } else if (lc->cmd == LC_REEXPORT_DYLIB) { |
| | 933 | struct dylib_command *dc = (struct dylib_command*)lc; |
| | 934 | char *name = (char*)lc + dc->name; |
| | 935 | dprintf(" REEXPORT %s\n", name); |
| | 936 | int subfd = open(name, O_RDONLY | O_BINARY); |
| | 937 | if (subfd < 0) |
| | 938 | tcc_warning("can't open %s (reexported from %s)", name, filename); |
| | 939 | else { |
| | 940 | /* Hopefully the REEXPORTs never form a cycle, we don't check |
| | 941 | for that! */ |
| | 942 | macho_load_dll(s1, subfd, name, lev + 1); |
| | 943 | close(subfd); |
| | 944 | } |
| | 945 | } else if (lc->cmd == LC_DYSYMTAB) { |
| | 946 | struct dysymtab_command *dc = (struct dysymtab_command*)lc; |
| | 947 | iextdef = dc->iextdefsym; |
| | 948 | nextdef = dc->nextdefsym; |
| | 949 | } |
| | 950 | lc = (struct load_command*) ((char*)lc + lc->cmdsize); |
| | 951 | } |
| | 952 | |
| | 953 | /* if the dll is already loaded, do not load it */ |
| | 954 | for(i = 0; i < s1->nb_loaded_dlls; i++) { |
| | 955 | dllref = s1->loaded_dlls[i]; |
| | 956 | if (!strcmp(soname, dllref->name)) { |
| | 957 | /* but update level if needed */ |
| | 958 | if (lev < dllref->level) |
| | 959 | dllref->level = lev; |
| | 960 | goto the_end; |
| | 961 | } |
| | 962 | } |
| | 963 | dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname)); |
| | 964 | dllref->level = lev; |
| | 965 | strcpy(dllref->name, soname); |
| | 966 | dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); |
| | 967 | |
| | 968 | if (!nsyms || !nextdef) |
| | 969 | tcc_warning("%s doesn't export any symbols?", filename); |
| | 970 | |
| | 971 | //dprintf("symbols (all):\n"); |
| | 972 | dprintf("symbols (exported):\n"); |
| | 973 | dprintf(" n: typ sec desc value name\n"); |
| | 974 | //for (i = 0; i < nsyms; i++) { |
| | 975 | for (i = iextdef; i < iextdef + nextdef; i++) { |
| | 976 | struct nlist_64 *sym = symtab + i; |
| | 977 | dprintf("%5d: %3d %3d 0x%04x 0x%016llx %s\n", |
| | 978 | i, sym->n_type, sym->n_sect, sym->n_desc, sym->n_value, |
| | 979 | strtab + sym->n_strx); |
| | 980 | set_elf_sym(s1->dynsymtab_section, 0, 0, |
| | 981 | ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), |
| | 982 | 0, SHN_UNDEF, strtab + sym->n_strx); |
| | 983 | } |
| | 984 | |
| | 985 | the_end: |
| | 986 | tcc_free(strtab); |
| | 987 | tcc_free(symtab); |
| | 988 | tcc_free(buf2); |
| | 989 | return 0; |
| | 990 | } |