History

macos: Read exported symbols from dylibs

up to now we simply assumed that any undefined symbols will be provided by some shared libs on the cmdline. Now we check this.

Michael Matz committed 6 years ago

Commit c86760c1 parent fbfe6204 files · +168 −21Browse files

Unified · Split

4 files
  1. libtcc.c +2 −1
  2. tcc.h +3 −0
  3. tccelf.c +2 −2
  4. tccmacho.c +161 −18
libtcc.c+2−1
@@ -1124,7 +1124,8 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) · 1123 unmodified lines
11241124 ret = tcc_load_dll(s1, fd, filename,
11251125 (flags & AFF_REFERENCED_DLL) != 0);
11261126 #else
1127− ret = 0;
1127+ ret = macho_load_dll(s1, fd, filename,
1128+ (flags & AFF_REFERENCED_DLL) != 0);
11281129 #endif
11291130 }
11301131 break;
tcc.h+3−0
@@ -1531,6 +1531,8 @@ ST_FUNC void resolve_common_syms(TCCState *s1); · 1530 unmodified lines
15311531 ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve);
15321532 ST_FUNC void relocate_section(TCCState *s1, Section *s);
15331533
1534+ST_FUNC ssize_t full_read(int fd, void *buf, size_t count);
1535+ST_FUNC void *load_data(int fd, unsigned long file_offset, unsigned long size);
15341536 ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h);
15351537 ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset);
15361538 ST_FUNC int tcc_load_archive(TCCState *s1, int fd, int alacarte);
@@ -1747,6 +1749,7 @@ PUB_FUNC int tcc_get_dllexports(const char *filename, char **pp); · 210 unmodified lines
17471749 /* ------------ tccmacho.c ----------------- */
17481750 #ifdef TCC_TARGET_MACHO
17491751 ST_FUNC int macho_output_file(TCCState * s1, const char *filename);
1752+ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev);
17501753 #endif
17511754 /* ------------ tccrun.c ----------------- */
17521755 #ifdef TCC_IS_NATIVE
tccelf.c+2−2
@@ -2547,7 +2547,7 @@ LIBTCCAPI int tcc_output_file(TCCState *s, const char *filename) · 2546 unmodified lines
25472547 return ret;
25482548 }
25492549
2550−ssize_t full_read(int fd, void *buf, size_t count) {
2550+ST_FUNC ssize_t full_read(int fd, void *buf, size_t count) {
25512551 char *cbuf = buf;
25522552 size_t rnum = 0;
25532553 while (1) {
@@ -2559,7 +2559,7 @@ ssize_t full_read(int fd, void *buf, size_t count) { · 5 unmodified lines
25592559 }
25602560 }
25612561
2562−static void *load_data(int fd, unsigned long file_offset, unsigned long size)
2562+ST_FUNC void *load_data(int fd, unsigned long file_offset, unsigned long size)
25632563 {
25642564 void *data;
25652565
tccmacho.c+161−18
@@ -18,6 +18,7 @@ · 17 unmodified lines
1818 #include "tcc.h"
1919
2020 #define DEBUG_MACHO 0
21+#define dprintf if (DEBUG_MACHO) printf
2122
2223 struct mach_header {
2324 uint32_t magic; /* mach magic number identifier */
@@ -231,7 +232,9 @@ struct macho { · 207 unmodified lines
231232 #define LC_SYMTAB 0x2
232233 #define LC_DYSYMTAB 0xb
233234 #define LC_LOAD_DYLIB 0xc
235+#define LC_ID_DYLIB 0xd
234236 #define LC_LOAD_DYLINKER 0xe
237+#define LC_REEXPORT_DYLIB (0x1f | LC_REQ_DYLD)
235238 #define LC_MAIN (0x28|LC_REQ_DYLD)
236239
237240 /* 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
375378 unsigned bind = ELFW(ST_BIND)(sym->st_info);
376379 unsigned vis = ELFW(ST_VISIBILITY)(sym->st_other);
377380
378− if (DEBUG_MACHO)
379− printf("%4d (%4d): %09llx %4d %4d %4d %3d %s\n",
380− sym_index, elf_index, sym->st_value,
381− type, bind, vis, sym->st_shndx, name);
381+ dprintf("%4d (%4d): %09llx %4d %4d %4d %3d %s\n",
382+ sym_index, elf_index, sym->st_value,
383+ type, bind, vis, sym->st_shndx, name);
382384 if (bind == STB_LOCAL) {
383385 if (mo->ilocal == -1)
384386 mo->ilocal = sym_index - 1;
@@ -394,15 +396,12 @@ static int check_symbols(TCCState *s1, struct macho *mo) · 9 unmodified lines
394396 mo->iundef = sym_index - 1;
395397 if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
396398 continue;
397− if (get_sym_attr(s1, elf_index, 0)) {
399+ if (find_elf_sym(s1->dynsymtab_section, name)) {
398400 /* Mark the symbol as coming from a dylib so that
399− relocate_syms doesn't complain. Normally we would have
400− checked that any dylib in fact exports this one, and listed
401− those in s1->dynsymtab, which would cause us to enter this
402− symbol into s1->dynsym, which is checked by relocate_syms.
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. */
401+ relocate_syms doesn't complain. Normally bind_exe_dynsyms
402+ would do this check, and place the symbol into dynsym
403+ which is checked by relocate_syms. But Mach-O doesn't use
404+ bind_exe_dynsyms. */
406405 sym->st_shndx = SHN_FROMDLL;
407406 continue;
408407 }
@@ -589,7 +588,11 @@ static void collect_sections(TCCState *s1, struct macho *mo) · 180 unmodified lines
589588 dysymlc->cmdsize = sizeof(*dysymlc);
590589 add_lc(mo, dysymlc);
591590
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+ }
593596
594597 mo->linkedit = new_section(s1, "LINKEDIT", SHT_LINKEDIT, SHF_ALLOC | SHF_WRITE);
595598 /* 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
683686 for (s = mo->sk_to_sect[sk].s; s; s = s->prev) {
684687 al = s->sh_addralign;
685688 curaddr = (curaddr + al - 1) & -al;
686− if (DEBUG_MACHO)
687− printf("curaddr now 0x%llx\n", curaddr);
689+ dprintf("curaddr now 0x%llx\n", curaddr);
688690 s->sh_addr = curaddr;
689691 curaddr += s->sh_size;
690692 if (s->sh_type != SHT_NOBITS) {
691693 fileofs = (fileofs + al - 1) & -al;
692694 s->sh_offset = fileofs;
693695 fileofs += s->sh_size;
694− if (DEBUG_MACHO)
695− printf("fileofs now %lld\n", fileofs);
696+ dprintf("fileofs now %lld\n", fileofs);
696697 }
697698 if (sec)
698699 mo->elfsectomacho[s->sh_num] = numsec;
@@ -799,7 +800,7 @@ ST_FUNC int macho_output_file(TCCState *s1, const char *filename) · 100 unmodified lines
799800 int fd, mode, file_type;
800801 FILE *fp;
801802 int ret = -1;
802− struct macho mo = {0,};
803+ struct macho mo = {};
803804
804805 file_type = s1->output_type;
805806 if (file_type == TCC_OUTPUT_OBJ)
@@ -845,3 +846,145 @@ ST_FUNC int macho_output_file(TCCState *s1, const char *filename) · 39 unmodified lines
845846 fclose(fp);
846847 return ret;
847848 }
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+}

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t