From f6fb4d0cf1597ecf18b82edd235fe11240357485 Mon Sep 17 00:00:00 2001 From: Sushant Pandurangi Date: Sun, 4 Jul 2021 11:06:55 +0200 Subject: [PATCH 1/4] macOS 11: link using dyld shared cache Supports linking using .tbd definitions found in the default installed command-line tools SDK or Xcode.app SDK. Only for creating executables (not yet for `tcc -run`). --- libtcc.c | 8 +++-- tcc.h | 18 +++++++++- tccmacho.c | 97 +++++++++++++++++++++++++++++++++++++++++++++--------- 3 files changed, 103 insertions(+), 20 deletions(-) diff --git a/libtcc.c b/libtcc.c index b8c4b534..b05ebf06 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1006,7 +1006,9 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) lseek(fd, 0, SEEK_SET); #ifdef TCC_TARGET_MACHO - if (0 == obj_type && 0 == strcmp(tcc_fileextension(filename), ".dylib")) + if (0 == obj_type + && (0 == strcmp(tcc_fileextension(filename), ".dylib") + || 0 == strcmp(tcc_fileextension(filename), ".tbd"))) obj_type = AFF_BINTYPE_DYN; #endif @@ -1142,8 +1144,8 @@ LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) static const char * const libs[] = { "%s/%s.def", "%s/lib%s.def", "%s/%s.dll", "%s/lib%s.dll", "%s/lib%s.a", NULL }; const char * const *pp = s->static_link ? libs + 4 : libs; #elif defined TCC_TARGET_MACHO - static const char * const libs[] = { "%s/lib%s.dylib", "%s/lib%s.a", NULL }; - const char * const *pp = s->static_link ? libs + 1 : libs; + static const char * const libs[] = { "%s/lib%s.dylib", "%s/lib%s.tbd", "%s/lib%s.a", NULL }; + const char * const *pp = s->static_link ? libs + 2 : libs; #elif defined TARGETOS_OpenBSD static const char * const libs[] = { "%s/lib%s.so.*", "%s/lib%s.a", NULL }; const char * const *pp = s->static_link ? libs + 1 : libs; diff --git a/tcc.h b/tcc.h index e21471e7..4dad5265 100644 --- a/tcc.h +++ b/tcc.h @@ -42,6 +42,7 @@ #ifndef _WIN32 # include # include +# include # ifndef CONFIG_TCC_STATIC # include # endif @@ -246,6 +247,12 @@ extern long double strtold (const char *__nptr, char **__endptr); # define ALSO_TRIPLET(s) s #endif +// FIXME: do this at runtime instead; check output of `xcode-select -p`. libxcselect provides this stuff +#ifndef CONFIG_OSX_SDK1 +# define CONFIG_OSX_SDK1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" +# define CONFIG_OSX_SDK2 "/Applications/Xcode.app/Developer/SDKs/MacOSX.sdk" +#endif + /* path to find crt1.o, crti.o and crtn.o */ #ifndef CONFIG_TCC_CRTPREFIX # define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) @@ -274,10 +281,19 @@ extern long double strtold (const char *__nptr, char **__endptr); # ifdef TCC_TARGET_PE # define CONFIG_TCC_LIBPATHS "{B}/lib" # else -# define CONFIG_TCC_LIBPATHS \ +# ifdef TCC_TARGET_MACHO +# define CONFIG_TCC_LIBPATHS \ + ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \ + ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \ + ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)\ + ":" ALSO_TRIPLET(CONFIG_OSX_SDK1 "/usr/" CONFIG_LDDIR) \ + ":" ALSO_TRIPLET(CONFIG_OSX_SDK2 "/usr/" CONFIG_LDDIR) +# else +# define CONFIG_TCC_LIBPATHS \ ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \ ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \ ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR) +# endif # endif #endif diff --git a/tccmacho.c b/tccmacho.c index bf6b5f69..ce61bdb8 100644 --- a/tccmacho.c +++ b/tccmacho.c @@ -837,7 +837,78 @@ static uint32_t macho_swap32(uint32_t x) } #define SWAP(x) (swap ? macho_swap32(x) : (x)) -ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev) +ST_FUNC int macho_add_dllref(TCCState* s1, int lev, const char* soname) +{ + /* if the dll is already loaded, do not load it */ + DLLReference *dllref; + for(int i = 0; i < s1->nb_loaded_dlls; i++) { + dllref = s1->loaded_dlls[i]; + if (!strcmp(soname, dllref->name)) { + /* but update level if needed */ + if (lev < dllref->level) + dllref->level = lev; + return -1; + } + } + dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname)); + dllref->level = lev; + strcpy(dllref->name, soname); + dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); + return 0; +} + +#define tbd_parse_movepast(s) \ + (pos = (pos = strstr(pos, s)) ? pos + strlen(s) : NULL) +#define tbd_parse_movetoany(cs) (pos = strpbrk(pos, cs)) +#define tbd_parse_skipws while (*pos && (*pos==' '||*pos=='\n')) ++pos +#define tbd_parse_tramplequote if(*pos=='\''||*pos=='"') tbd_parse_trample +#define tbd_parse_tramplespace if(*pos==' ') tbd_parse_trample +#define tbd_parse_trample *pos++=0 + +ST_FUNC int macho_load_tbd(TCCState* s1, int fd, const char* filename, int lev) +{ + char* soname; + + struct stat sb; + fstat(fd,&sb); + char* data = load_data(fd, 0, sb.st_size+1); + data[sb.st_size]=0; + char* pos = data; + + if (!tbd_parse_movepast("install-name: ")) return -1; + tbd_parse_skipws; + tbd_parse_tramplequote; + soname = pos; + if (!tbd_parse_movetoany("\n \"'")) return -1; + tbd_parse_trample; + if (macho_add_dllref(s1, lev, soname) != 0) goto the_end; + + while(pos) { + char* sym = NULL; + if (!tbd_parse_movepast("symbols: ")) break; + if (!tbd_parse_movepast("[")) break; + int cont = 1; + while (cont) { + tbd_parse_skipws; + tbd_parse_tramplequote; + sym = pos; + if (!tbd_parse_movetoany(",] \"'")) break; + tbd_parse_tramplequote; + tbd_parse_tramplespace; + tbd_parse_skipws; + if (*pos==0||*pos==']') cont=0; + tbd_parse_trample; + set_elf_sym(s1->dynsymtab_section, 0, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, SHN_UNDEF, sym); + } + } + +the_end: + tcc_free(data); + return 0; +} + +ST_FUNC int macho_load_dylib(TCCState * s1, int fd, const char* filename, int lev) { unsigned char buf[sizeof(struct mach_header_64)]; void *buf2; @@ -853,7 +924,6 @@ ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev) uint32_t strsize = 0; uint32_t iextdef = 0; uint32_t nextdef = 0; - DLLReference *dllref; again: if (full_read(fd, buf, sizeof(buf)) != sizeof(buf)) @@ -934,20 +1004,8 @@ ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev) lc = (struct load_command*) ((char*)lc + lc->cmdsize); } - /* if the dll is already loaded, do not load it */ - for(i = 0; i < s1->nb_loaded_dlls; i++) { - dllref = s1->loaded_dlls[i]; - if (!strcmp(soname, dllref->name)) { - /* but update level if needed */ - if (lev < dllref->level) - dllref->level = lev; - goto the_end; - } - } - dllref = tcc_mallocz(sizeof(DLLReference) + strlen(soname)); - dllref->level = lev; - strcpy(dllref->name, soname); - dynarray_add(&s1->loaded_dlls, &s1->nb_loaded_dlls, dllref); + if (0 != macho_add_dllref(s1, lev, soname)) + goto the_end; if (!nsyms || !nextdef) tcc_warning("%s doesn't export any symbols?", filename); @@ -972,3 +1030,10 @@ ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev) tcc_free(buf2); return 0; } + +ST_FUNC int macho_load_dll(TCCState * s1, int fd, const char* filename, int lev) +{ + return strcmp(tcc_fileextension(filename), ".tbd") == 0 + ? macho_load_tbd(s1, fd, filename, lev) + : macho_load_dylib(s1, fd, filename, lev); +} From 33fa3a4d41bb9cba9997dc030e3c8f01ccaa51a9 Mon Sep 17 00:00:00 2001 From: Sushant Pandurangi Date: Sun, 4 Jul 2021 11:07:48 +0200 Subject: [PATCH 2/4] macOS 11: `tcc -run` using dyld shared cache --- libtcc.c | 13 +++++++++++-- tccmacho.c | 23 +++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/libtcc.c b/libtcc.c index b05ebf06..6786ba11 100644 --- a/libtcc.c +++ b/libtcc.c @@ -979,6 +979,10 @@ static int tcc_glob_so(TCCState *s1, const char *pattern, char *buf, int size) } #endif +#ifdef TCC_TARGET_MACHO +ST_FUNC const char* macho_tbd_soname(const char* filename); +#endif + ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) { int fd, ret = -1; @@ -1029,9 +1033,14 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) case AFF_BINTYPE_DYN: if (s1->output_type == TCC_OUTPUT_MEMORY) { #ifdef TCC_IS_NATIVE - void *dl = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); + char* soname = filename; +# ifdef TCC_TARGET_MACHO + if (!strcmp(tcc_fileextension(filename), ".tbd")) + soname = macho_tbd_soname(filename); +# endif + void* dl = dlopen(soname, RTLD_GLOBAL | RTLD_LAZY); if (dl) { - tcc_add_dllref(s1, filename)->handle = dl; + tcc_add_dllref(s1, soname)->handle = dl; ret = 0; } #endif diff --git a/tccmacho.c b/tccmacho.c index ce61bdb8..fe670e8f 100644 --- a/tccmacho.c +++ b/tccmacho.c @@ -865,6 +865,29 @@ ST_FUNC int macho_add_dllref(TCCState* s1, int lev, const char* soname) #define tbd_parse_tramplespace if(*pos==' ') tbd_parse_trample #define tbd_parse_trample *pos++=0 +ST_FUNC const char* macho_tbd_soname(const char* filename) { + char* soname; + int fd = open(filename,O_RDONLY); + if (fd<0) return filename; + struct stat sb; + fstat(fd,&sb); + char* data = load_data(fd, 0, sb.st_size+1); + data[sb.st_size]=0; + char* pos = data; + + if (!tbd_parse_movepast("install-name: ")) return filename; + tbd_parse_skipws; + tbd_parse_tramplequote; + soname = pos; + if (!tbd_parse_movetoany("\n \"'")) return filename; + tbd_parse_trample; + char* ret = tcc_mallocz(strlen(soname)+1); + strcpy(ret, soname); + // soname = strdup(soname); + tcc_free(data); + return ret; +} + ST_FUNC int macho_load_tbd(TCCState* s1, int fd, const char* filename, int lev) { char* soname; From d5e4b258e14d0df5511a820df9ea9d4899134221 Mon Sep 17 00:00:00 2001 From: Sushant Pandurangi Date: Tue, 6 Jul 2021 15:59:12 +0200 Subject: [PATCH 3/4] Minor fixes for gnu90 compatibility --- libtcc.c | 5 +++-- tcc.h | 2 +- tccmacho.c | 21 +++++++++++---------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/libtcc.c b/libtcc.c index 6786ba11..db109886 100644 --- a/libtcc.c +++ b/libtcc.c @@ -1033,12 +1033,13 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) case AFF_BINTYPE_DYN: if (s1->output_type == TCC_OUTPUT_MEMORY) { #ifdef TCC_IS_NATIVE - char* soname = filename; + void* dl; + const char* soname = filename; # ifdef TCC_TARGET_MACHO if (!strcmp(tcc_fileextension(filename), ".tbd")) soname = macho_tbd_soname(filename); # endif - void* dl = dlopen(soname, RTLD_GLOBAL | RTLD_LAZY); + dl = dlopen(soname, RTLD_GLOBAL | RTLD_LAZY); if (dl) { tcc_add_dllref(s1, soname)->handle = dl; ret = 0; diff --git a/tcc.h b/tcc.h index 4dad5265..5650aee9 100644 --- a/tcc.h +++ b/tcc.h @@ -247,7 +247,7 @@ extern long double strtold (const char *__nptr, char **__endptr); # define ALSO_TRIPLET(s) s #endif -// FIXME: do this at runtime instead; check output of `xcode-select -p`. libxcselect provides this stuff +/* FIXME: do this at runtime instead; check output of `xcode-select -p`. libxcselect provides this stuff */ #ifndef CONFIG_OSX_SDK1 # define CONFIG_OSX_SDK1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" # define CONFIG_OSX_SDK2 "/Applications/Xcode.app/Developer/SDKs/MacOSX.sdk" diff --git a/tccmacho.c b/tccmacho.c index fe670e8f..bc0b0937 100644 --- a/tccmacho.c +++ b/tccmacho.c @@ -841,7 +841,8 @@ ST_FUNC int macho_add_dllref(TCCState* s1, int lev, const char* soname) { /* if the dll is already loaded, do not load it */ DLLReference *dllref; - for(int i = 0; i < s1->nb_loaded_dlls; i++) { + int i; + for(i = 0; i < s1->nb_loaded_dlls; i++) { dllref = s1->loaded_dlls[i]; if (!strcmp(soname, dllref->name)) { /* but update level if needed */ @@ -866,14 +867,14 @@ ST_FUNC int macho_add_dllref(TCCState* s1, int lev, const char* soname) #define tbd_parse_trample *pos++=0 ST_FUNC const char* macho_tbd_soname(const char* filename) { - char* soname; + char *soname, *data, *pos, *ret; + struct stat sb; int fd = open(filename,O_RDONLY); if (fd<0) return filename; - struct stat sb; fstat(fd,&sb); - char* data = load_data(fd, 0, sb.st_size+1); + data = load_data(fd, 0, sb.st_size+1); data[sb.st_size]=0; - char* pos = data; + pos = data; if (!tbd_parse_movepast("install-name: ")) return filename; tbd_parse_skipws; @@ -881,7 +882,7 @@ ST_FUNC const char* macho_tbd_soname(const char* filename) { soname = pos; if (!tbd_parse_movetoany("\n \"'")) return filename; tbd_parse_trample; - char* ret = tcc_mallocz(strlen(soname)+1); + ret = tcc_mallocz(strlen(soname)+1); strcpy(ret, soname); // soname = strdup(soname); tcc_free(data); @@ -890,13 +891,13 @@ ST_FUNC const char* macho_tbd_soname(const char* filename) { ST_FUNC int macho_load_tbd(TCCState* s1, int fd, const char* filename, int lev) { - char* soname; + char *soname, *data, *pos; struct stat sb; fstat(fd,&sb); - char* data = load_data(fd, 0, sb.st_size+1); + data = load_data(fd, 0, sb.st_size+1); data[sb.st_size]=0; - char* pos = data; + pos = data; if (!tbd_parse_movepast("install-name: ")) return -1; tbd_parse_skipws; @@ -908,9 +909,9 @@ ST_FUNC int macho_load_tbd(TCCState* s1, int fd, const char* filename, int lev) while(pos) { char* sym = NULL; + int cont = 1; if (!tbd_parse_movepast("symbols: ")) break; if (!tbd_parse_movepast("[")) break; - int cont = 1; while (cont) { tbd_parse_skipws; tbd_parse_tramplequote; From cca4ece0a8910fd032fb9219687c6516e331a6cc Mon Sep 17 00:00:00 2001 From: Sushant Pandurangi Date: Tue, 6 Jul 2021 22:48:38 +0200 Subject: [PATCH 4/4] macOS: get active SDK path from xcode-select --- libtcc.c | 29 ++++++++++++++++++++++++++++- tcc.h | 24 +++++++++--------------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/libtcc.c b/libtcc.c index db109886..11999d2c 100644 --- a/libtcc.c +++ b/libtcc.c @@ -855,6 +855,29 @@ LIBTCCAPI void tcc_delete(TCCState *s1) #endif } +/* Looks for the active developer SDK set by xcode-select (or the default + one set during installation.) */ +#define SZPAIR(s) s "", sizeof(s)-1 +ST_FUNC int tcc_add_macos_sdkpath(TCCState* s) +{ + char *sdkroot = NULL, *pos = NULL; + void* xcs = dlopen("libxcselect.dylib", RTLD_GLOBAL | RTLD_LAZY); + CString path = {}; + int (*f)(unsigned int, char**) = dlsym(xcs, "xcselect_host_sdk_path"); + + if (f) f(1, &sdkroot); + if (!sdkroot) return -1; + pos = strstr(sdkroot,"SDKs/MacOSX"); + if (!pos) return -1; + cstr_cat(&path, sdkroot, pos-sdkroot); + cstr_cat(&path, SZPAIR("SDKs/MacOSX.sdk/usr/lib\0") ); + tcc_add_library_path(s, (char*)path.data); + cstr_free(&path); + tcc_free(sdkroot); + return 0; +} +#undef SZPAIR + LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) { s->output_type = output_type; @@ -881,7 +904,11 @@ LIBTCCAPI int tcc_set_output_type(TCCState *s, int output_type) } tcc_add_library_path(s, CONFIG_TCC_LIBPATHS); - +#ifdef TCC_TARGET_MACHO + if (tcc_add_macos_sdkpath(s) != 0) { + tcc_add_library_path(s, CONFIG_OSX_SDK_FALLBACK); + } +#endif #ifdef TCC_TARGET_PE # ifdef _WIN32 if (!s->nostdlib && output_type != TCC_OUTPUT_OBJ) diff --git a/tcc.h b/tcc.h index 5650aee9..cc8a2f82 100644 --- a/tcc.h +++ b/tcc.h @@ -247,12 +247,6 @@ extern long double strtold (const char *__nptr, char **__endptr); # define ALSO_TRIPLET(s) s #endif -/* FIXME: do this at runtime instead; check output of `xcode-select -p`. libxcselect provides this stuff */ -#ifndef CONFIG_OSX_SDK1 -# define CONFIG_OSX_SDK1 "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" -# define CONFIG_OSX_SDK2 "/Applications/Xcode.app/Developer/SDKs/MacOSX.sdk" -#endif - /* path to find crt1.o, crti.o and crtn.o */ #ifndef CONFIG_TCC_CRTPREFIX # define CONFIG_TCC_CRTPREFIX USE_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) @@ -281,18 +275,18 @@ extern long double strtold (const char *__nptr, char **__endptr); # ifdef TCC_TARGET_PE # define CONFIG_TCC_LIBPATHS "{B}/lib" # else -# ifdef TCC_TARGET_MACHO -# define CONFIG_TCC_LIBPATHS \ - ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \ - ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \ - ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR)\ - ":" ALSO_TRIPLET(CONFIG_OSX_SDK1 "/usr/" CONFIG_LDDIR) \ - ":" ALSO_TRIPLET(CONFIG_OSX_SDK2 "/usr/" CONFIG_LDDIR) -# else -# define CONFIG_TCC_LIBPATHS \ +# define CONFIG_TCC_LIBPATHS \ ALSO_TRIPLET(CONFIG_SYSROOT "/usr/" CONFIG_LDDIR) \ ":" ALSO_TRIPLET(CONFIG_SYSROOT "/" CONFIG_LDDIR) \ ":" ALSO_TRIPLET(CONFIG_SYSROOT "/usr/local/" CONFIG_LDDIR) +# ifdef TCC_TARGET_MACHO +# define CONFIG_OSX_SDK1 \ + "/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk" +# define CONFIG_OSX_SDK2 \ + "/Applications/Xcode.app/Developer/SDKs/MacOSX.sdk" +# define CONFIG_OSX_SDK_FALLBACK \ + ALSO_TRIPLET(CONFIG_OSX_SDK1 "/usr/" CONFIG_LDDIR) \ + ":" ALSO_TRIPLET(CONFIG_OSX_SDK2 "/usr/" CONFIG_LDDIR) # endif # endif #endif