From f10ab130ecbefc99177f5569c69ebe7f375a3d44 Mon Sep 17 00:00:00 2001 From: grischka Date: Fri, 28 Mar 2025 13:19:38 +0100 Subject: [PATCH] linker options & tcc_load_ldscript() reworked cleanup libtcc.c:tcc_set_linker() cleanup tccelf.c:tcc_load_ldscript() Also - tccrun.c, tccelf.c:relocate_syms(): with tcc -run -nostdlib, do resolve but only from explicitly on the command-line given libraries. - tccgen.c: optimize UMOD x % c -> x & (c-1) for c = 2^n - tcc-doc.texi: cleanup - tcc.h, tccpp.c, libtcc.c: add 'size' arg to pstrncpy() Also reorder functions in libtcc.c a bit. 9 files changed, 556 insertions(+), 617 deletions(-) --- libtcc.c | 891 ++++++++++++++++++++++++--------------------------- tcc-doc.texi | 22 +- tcc.c | 10 +- tcc.h | 23 +- tccelf.c | 210 ++++++------ tccgen.c | 7 +- tccmacho.c | 12 +- tccpp.c | 4 +- tccrun.c | 4 +- 9 files changed, 561 insertions(+), 622 deletions(-) diff --git a/libtcc.c b/libtcc.c index b163e865..33781e21 100644 --- a/libtcc.c +++ b/libtcc.c @@ -175,9 +175,11 @@ ST_FUNC char *pstrcat(char *buf, size_t buf_size, const char *s) return buf; } -ST_FUNC char *pstrncpy(char *out, const char *in, size_t num) +ST_FUNC char *pstrncpy(char *out, size_t buf_size, const char *s, size_t num) { - memcpy(out, in, num); + if (num >= buf_size) + num = buf_size - 1; + memcpy(out, s, num); out[num] = '\0'; return out; } @@ -206,11 +208,26 @@ ST_FUNC char *tcc_load_text(int fd) { int len = lseek(fd, 0, SEEK_END); char *buf = load_data(fd, 0, len + 1); - if (buf) - buf[len] = 0; + buf[len] = 0; return buf; } +/* replace *pp by copy of 'str' or NULL */ +static void tcc_set_str(char **pp, const char *str) +{ + tcc_free(*pp); + *pp = str ? tcc_strdup(str) : NULL; +} + +/* set/append 'str' to *pp (separated by 'sep' unless 0) */ +static void tcc_concat_str(char **pp, const char *str, int sep) +{ + int l = *pp ? strlen(*pp) + !!sep : 0; + *pp = tcc_realloc(*pp, l + strlen(str) + 1); + if (l && sep) ((*pp)[l - 1] = sep); + strcpy(*pp + l, str); +} + /********************************************************/ /* memory management */ @@ -519,6 +536,41 @@ ST_FUNC void dynarray_reset(void *pp, int *n) *(void**)pp = NULL; } +static void dynarray_split(char ***argv, int *argc, const char *p, int sep) +{ + int qot, c; + CString str; + for(;;) { + while (c = (unsigned char)*p, c <= ' ' && c != '\0') + ++p; + if (c == '\0') + break; + cstr_new(&str); + qot = 0; + do { + ++p; + if (sep) { /* e.g. to split -Wl,-opt,arg */ + if (c == sep) + break; + } else { + /* e.g. for tcc_set_options() or "tcc @listfile" */ + if (c == '\\' && (*p == '"' || *p == '\\')) { + c = *p++; + } else if (c == '"') { + qot ^= 1; + continue; + } else if (c <= ' ' && !qot) { + break; + } + } + cstr_ccat(&str, c); + } while (c = (unsigned char)*p, c != '\0'); + cstr_ccat(&str, '\0'); + //printf("<%s>\n", str.data); + dynarray_add(argv, argc, str.data); + } +} + static void tcc_split_path(TCCState *s, void *p_ary, int *p_nb_ary, const char *in) { const char *p; @@ -885,9 +937,9 @@ LIBTCCAPI void tcc_delete(TCCState *s1) dynarray_reset(&s1->target_deps, &s1->nb_target_deps); dynarray_reset(&s1->pragma_libs, &s1->nb_pragma_libs); dynarray_reset(&s1->argv, &s1->argc); + dynarray_reset(&s1->link_argv, &s1->link_argc); cstr_free(&s1->cmdline_defs); cstr_free(&s1->cmdline_incl); - cstr_free(&s1->linker_arg); tcc_free(s1->dState); #ifdef TCC_IS_NATIVE /* free runtime memory */ @@ -962,6 +1014,17 @@ LIBTCCAPI int tcc_add_sysinclude_path(TCCState *s, const char *pathname) return 0; } +LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname) +{ + tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname); + return 0; +} + +LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path) +{ + tcc_set_str(&s->tcc_lib_path, path); +} + /* add/update a 'DLLReference', Just find if level == -1 */ ST_FUNC DLLReference *tcc_add_dllref(TCCState *s1, const char *dllname, int level) { @@ -988,6 +1051,109 @@ ST_FUNC DLLReference *tcc_add_dllref(TCCState *s1, const char *dllname, int leve return ref; } +static int tcc_add_binary(TCCState *s1, int flags, const char *filename, int fd) +{ + ElfW(Ehdr) ehdr; + int obj_type; + const char *saved_filename = s1->current_filename; + int ret = 0; + + s1->current_filename = filename; + obj_type = tcc_object_type(fd, &ehdr); + lseek(fd, 0, SEEK_SET); + + switch (obj_type) { + + case AFF_BINTYPE_REL: + ret = tcc_load_object_file(s1, fd, 0); + break; + + case AFF_BINTYPE_AR: + ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE)); + break; + +#if defined TCC_TARGET_UNIX + case AFF_BINTYPE_DYN: + if (s1->output_type == TCC_OUTPUT_MEMORY) { +#ifdef TCC_IS_NATIVE + void* dl = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); + if (dl) + tcc_add_dllref(s1, filename, 0)->handle = dl; + else + ret = FILE_NOT_RECOGNIZED; +#endif + } else + ret = tcc_load_dll(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); + break; + + default: + /* as GNU ld, consider it is an ld script if not recognized */ + ret = tcc_load_ldscript(s1, fd); + break; + +#elif defined TCC_TARGET_MACHO + case AFF_BINTYPE_DYN: + case_dyn_or_tbd: + if (s1->output_type == TCC_OUTPUT_MEMORY) { +#ifdef TCC_IS_NATIVE + void* dl; + const char* soname = filename; + char *tmp = 0; + if (obj_type != AFF_BINTYPE_DYN) { + tmp = macho_tbd_soname(fd); + if (tmp) + soname = tmp; + } + dl = dlopen(soname, RTLD_GLOBAL | RTLD_LAZY); + if (dl) + tcc_add_dllref(s1, soname, 0)->handle = dl; + else + ret = FILE_NOT_RECOGNIZED; + tcc_free(tmp); +#endif + } else if (obj_type == AFF_BINTYPE_DYN) { + ret = macho_load_dll(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); + } else { + ret = macho_load_tbd(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); + } + if (ret) + ret = FILE_NOT_RECOGNIZED; + break; + + default: + { + const char *ext = tcc_fileextension(filename); + if (!strcmp(ext, ".tbd")) + goto case_dyn_or_tbd; + if (!strcmp(ext, ".dylib")) { + obj_type = AFF_BINTYPE_DYN; + goto case_dyn_or_tbd; + } + ret = FILE_NOT_RECOGNIZED; + break; + } + +#elif defined TCC_TARGET_PE + default: + if (pe_load_file(s1, fd, filename)) + ret = FILE_NOT_RECOGNIZED; + break; +#endif + +#ifdef TCC_TARGET_COFF + case AFF_BINTYPE_C67: + ret = tcc_load_coff(s1, fd); + break; +#endif + } + + close(fd); + s1->current_filename = saved_filename; + if (ret == FILE_NOT_RECOGNIZED) + return tcc_error_noabort("%s: unrecognized file type", filename); + return ret; +} + /* OpenBSD: choose latest from libxxx.so.x.y versions */ #if defined TARGETOS_OpenBSD && !defined _WIN32 #include @@ -1013,130 +1179,6 @@ static int tcc_glob_so(TCCState *s1, const char *pattern, char *buf, int size) } #endif -static int guess_filetype(const char *filename); - -ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) -{ - int fd, ret = -1; - -#if defined TARGETOS_OpenBSD && !defined _WIN32 - char buf[1024]; - if (tcc_glob_so(s1, filename, buf, sizeof buf) >= 0) - filename = buf; -#endif - - if (0 == (flags & AFF_TYPE_MASK)) - flags |= guess_filetype(filename); - - /* ignore binary files with -E */ - if (s1->output_type == TCC_OUTPUT_PREPROCESS - && (flags & AFF_TYPE_BIN)) - return 0; - - /* open the file */ - fd = _tcc_open(s1, filename); - if (fd < 0) { - if (flags & AFF_PRINT_ERROR) - tcc_error_noabort("file '%s' not found", filename); - return FILE_NOT_FOUND; - } - - s1->current_filename = filename; - if (flags & AFF_TYPE_BIN) { - ElfW(Ehdr) ehdr; - int obj_type; - - obj_type = tcc_object_type(fd, &ehdr); - lseek(fd, 0, SEEK_SET); - - switch (obj_type) { - - case AFF_BINTYPE_REL: - ret = tcc_load_object_file(s1, fd, 0); - break; - - case AFF_BINTYPE_AR: - ret = tcc_load_archive(s1, fd, !(flags & AFF_WHOLE_ARCHIVE)); - break; - -#ifdef TCC_TARGET_PE - default: - ret = pe_load_file(s1, fd, filename); - goto check_success; - -#elif defined TCC_TARGET_MACHO - case AFF_BINTYPE_DYN: - case_dyn_or_tbd: - if (s1->output_type == TCC_OUTPUT_MEMORY) { -#ifdef TCC_IS_NATIVE - void* dl; - const char* soname = filename; - if (obj_type != AFF_BINTYPE_DYN) - soname = macho_tbd_soname(filename); - dl = dlopen(soname, RTLD_GLOBAL | RTLD_LAZY); - if (dl) - tcc_add_dllref(s1, soname, 0)->handle = dl, ret = 0; - if (filename != soname) - tcc_free((void *)soname); -#endif - } else if (obj_type == AFF_BINTYPE_DYN) { - ret = macho_load_dll(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); - } else { - ret = macho_load_tbd(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); - } - goto check_success; - default: - { - const char *ext = tcc_fileextension(filename); - if (!strcmp(ext, ".tbd")) - goto case_dyn_or_tbd; - if (!strcmp(ext, ".dylib")) { - obj_type = AFF_BINTYPE_DYN; - goto case_dyn_or_tbd; - } - goto check_success; - } - -#else /* unix */ - case AFF_BINTYPE_DYN: - if (s1->output_type == TCC_OUTPUT_MEMORY) { -#ifdef TCC_IS_NATIVE - void* dl = dlopen(filename, RTLD_GLOBAL | RTLD_LAZY); - if (dl) - tcc_add_dllref(s1, filename, 0)->handle = dl, ret = 0; -#endif - } else - ret = tcc_load_dll(s1, fd, filename, (flags & AFF_REFERENCED_DLL) != 0); - break; - - default: - /* as GNU ld, consider it is an ld script if not recognized */ - ret = tcc_load_ldscript(s1, fd); - goto check_success; - -#endif /* pe / macos / unix */ - -check_success: - if (ret < 0) - tcc_error_noabort("%s: unrecognized file type", filename); - break; - -#ifdef TCC_TARGET_COFF - case AFF_BINTYPE_C67: - ret = tcc_load_coff(s1, fd); - break; -#endif - } - close(fd); - } else { - /* update target deps */ - dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename)); - ret = tcc_compile(s1, flags, filename, fd); - } - s1->current_filename = NULL; - return ret; -} - static int guess_filetype(const char *filename) { int filetype = 0; @@ -1162,17 +1204,44 @@ static int guess_filetype(const char *filename) return filetype; } +ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) +{ + int fd; + +#if defined TARGETOS_OpenBSD && !defined _WIN32 + char buf[1024]; + if (tcc_glob_so(s1, filename, buf, sizeof buf) >= 0) + filename = buf; +#endif + + if (0 == (flags & AFF_TYPE_MASK)) + flags |= guess_filetype(filename); + + /* ignore binary files with -E */ + if (s1->output_type == TCC_OUTPUT_PREPROCESS + && (flags & AFF_TYPE_BIN)) + return 0; + + /* open the file */ + fd = _tcc_open(s1, filename); + if (fd < 0) { + if (flags & AFF_PRINT_ERROR) + tcc_error_noabort("file '%s' not found", filename); + return FILE_NOT_FOUND; + } + + if (flags & AFF_TYPE_BIN) + return tcc_add_binary(s1, flags, filename, fd); + + dynarray_add(&s1->target_deps, &s1->nb_target_deps, tcc_strdup(filename)); + return tcc_compile(s1, flags, filename, fd); +} + LIBTCCAPI int tcc_add_file(TCCState *s, const char *filename) { return tcc_add_file_internal(s, filename, s->filetype | AFF_PRINT_ERROR); } -LIBTCCAPI int tcc_add_library_path(TCCState *s, const char *pathname) -{ - tcc_split_path(s, &s->library_paths, &s->nb_library_paths, pathname); - return 0; -} - static int tcc_add_library_internal(TCCState *s1, const char *fmt, const char *filename, int flags, char **paths, int nb_paths) { @@ -1186,7 +1255,8 @@ static int tcc_add_library_internal(TCCState *s1, const char *fmt, return ret; } if (flags & AFF_PRINT_ERROR) - tcc_error_noabort("library '%s' not found", filename); + tcc_error_noabort("%s '%s' not found", + flags & AFF_TYPE_LIB ? "library" : "file", filename); return FILE_NOT_FOUND; } @@ -1206,7 +1276,7 @@ ST_FUNC int tcc_add_support(TCCState *s1, const char *filename) return tcc_add_dll(s1, filename, AFF_PRINT_ERROR); } -#if !defined TCC_TARGET_PE && !defined TCC_TARGET_MACHO +#ifdef TCC_TARGET_UNIX ST_FUNC int tcc_add_crt(TCCState *s1, const char *filename) { return tcc_add_library_internal(s1, "%s/%s", @@ -1230,12 +1300,12 @@ LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) "%s/lib%s.a", NULL }; + int flags = AFF_TYPE_LIB | (s->filetype & AFF_WHOLE_ARCHIVE); /* if libraryname begins with a colon, it means search lib paths for exactly the following file, without lib prefix or anything */ if (*libraryname == ':') { libraryname++; } else { - int flags = s->filetype & AFF_WHOLE_ARCHIVE; const char * const *pp = libs; if (s->static_link) pp += sizeof(libs) / sizeof(*libs) - 2; /* only "%s/lib%s.a" */ @@ -1247,7 +1317,8 @@ LIBTCCAPI int tcc_add_library(TCCState *s, const char *libraryname) ++pp; } } - return tcc_add_dll(s, libraryname, AFF_PRINT_ERROR); + /* fallback to try file without pre- or sufffix */ + return tcc_add_dll(s, libraryname, flags | AFF_PRINT_ERROR); } /* handle #pragma comment(lib,) */ @@ -1258,30 +1329,6 @@ ST_FUNC void tcc_add_pragma_libs(TCCState *s1) tcc_add_library(s1, s1->pragma_libs[i]); } -LIBTCCAPI int tcc_add_symbol(TCCState *s1, const char *name, const void *val) -{ -#ifdef TCC_TARGET_PE - /* On x86_64 'val' might not be reachable with a 32bit offset. - So it is handled here as if it were in a DLL. */ - pe_putimport(s1, 0, name, (uintptr_t)val); -#else - char buf[256]; - if (s1->leading_underscore) { - buf[0] = '_'; - pstrcpy(buf + 1, sizeof(buf) - 1, name); - name = buf; - } - set_global_sym(s1, name, NULL, (addr_t)(uintptr_t)val); /* NULL: SHN_ABS */ -#endif - return 0; -} - -LIBTCCAPI void tcc_set_lib_path(TCCState *s, const char *path) -{ - tcc_free(s->tcc_lib_path); - s->tcc_lib_path = tcc_strdup(path); -} - /********************************************************/ /* options parser */ @@ -1300,224 +1347,177 @@ static int strstart(const char *val, const char **str) return 1; } -/* Like strstart, but automatically takes into account that ld options can - * - * - start with double or single dash (e.g. '--soname' or '-soname') - * - arguments can be given as separate or after '=' (e.g. '-Wl,-soname,x.so' - * or '-Wl,-soname=x.so') - * - * you provide `val` always in 'option[=]' form (no leading -) - */ -static int link_option(const char *str, const char *val, const char **ptr) +struct lopt { + TCCState *s; + const char *opt, *arg; + int match; +}; + +/* match linker option */ +static int link_option(struct lopt *o, const char *q) { - const char *p, *q; - int ret; + const char *p = o->opt; + int c; /* there should be 1 or 2 dashes */ - if (*str++ != '-') + if (*p++ != '-') return 0; - if (*str == '-') - str++; - - /* then str & val should match (potentially up to '=') */ - p = str; - q = val; - - ret = 1; - if (q[0] == '?') { + if (*p == '-') + p++; + while ((c = *q) == *p) { + if (c == '\0') + goto succ; /* -Wl,-opt */ + ++p; + if (c == '=') + goto succ; /* -Wl,-opt=arg */ ++q; - if (strstart("no-", &p)) - ret = -1; } - - while (*q != '\0' && *q != '=') { - if (*p != *q) - return 0; - p++; - q++; + if (c == '=' || c == ':') { + if (*p == '\0') { + if (o->s->link_optind + 1 < o->s->link_argc) { + p = o->s->link_argv[++o->s->link_optind]; + goto succ; /* -Wl,-opt,arg */ + } + o->match = 1; /* -Wl,-opt -Wl,arg */ + } else if (c == ':') + goto succ; /* -Wl,-Iarg */ } - - /* '=' near eos means ',' or '=' is ok */ - if (*q == '=') { - if (*p == 0) - *ptr = p; - if (*p != ',' && *p != '=') - return 0; - p++; - } else if (*p && *p != ',') { - return 0; - } - *ptr = p; - return ret; + return 0; +succ: + o->arg = p; + //printf("set %s '%s'\n", o->opt, o->arg); + return 1; } -static int link_arg(const char *opt, const char *str) -{ - int l = strlen(opt); - return 0 == strncmp(opt, str, l) && (str[l] == '\0' || str[l] == ','); -} - -static const char *skip_linker_arg(const char **str) -{ - const char *s1 = *str; - const char *s2 = strchr(s1, ','); - *str = s2 ? s2++ : (s2 = s1 + strlen(s1)); - return s2; -} - -static void copy_linker_arg(char **pp, const char *s, int sep) -{ - const char *q = s; - char *p = *pp; - int l = 0; - if (p && sep) - p[l = strlen(p)] = sep, ++l; - skip_linker_arg(&q); - pstrncpy(l + (*pp = tcc_realloc(p, q - s + l + 1)), s, q - s); -} - -static void args_parser_add_file(TCCState *s, const char* filename, int filetype) -{ - struct filespec *f = tcc_malloc(sizeof *f + strlen(filename)); - f->type = filetype; - strcpy(f->name, filename); - dynarray_add(&s->files, &s->nb_files, f); -} +static void args_parser_add_file(TCCState *s, const char* filename, int filetype); /* set linker options */ -static int tcc_set_linker(TCCState *s, const char *option) +static int tcc_set_linker(TCCState *s, const char *optarg) { TCCState *s1 = s; - while (*option) { - const char *p = NULL; + dynarray_split(&s1->link_argv, &s1->link_argc, optarg, ','); + + while (s->link_optind < s->link_argc) { char *end = NULL; int ignoring = 0; - int ret; + struct lopt o = {0}; + o.s = s; + o.opt = s->link_argv[s->link_optind]; - if (link_option(option, "Bsymbolic", &p)) { + if (link_option(&o, "Bsymbolic")) { s->symbolic = 1; - } else if (link_option(option, "nostdlib", &p)) { + } else if (link_option(&o, "nostdlib")) { s->nostdlib_paths = 1; - } else if (link_option(option, "e=", &p) - || link_option(option, "entry=", &p)) { - copy_linker_arg(&s->elf_entryname, p, 0); - } else if (link_option(option, "fini=", &p)) { - copy_linker_arg(&s->fini_symbol, p, 0); - ignoring = 1; - } else if (link_option(option, "image-base=", &p) - || link_option(option, "Ttext=", &p)) { - s->text_addr = strtoull(p, &end, 16); + } else if (link_option(&o, "e=") || link_option(&o, "entry=")) { + tcc_set_str(&s->elf_entryname, o.arg); + } else if (link_option(&o, "image-base=") || link_option(&o, "Ttext=")) { + s->text_addr = strtoull(o.arg, &end, 16); s->has_text_addr = 1; - } else if (link_option(option, "init=", &p)) { - copy_linker_arg(&s->init_symbol, p, 0); + } else if (link_option(&o, "init=")) { + tcc_set_str(&s->init_symbol, o.arg); ignoring = 1; - } else if (link_option(option, "Map=", &p)) { - copy_linker_arg(&s->mapfile, p, 0); + } else if (link_option(&o, "fini=")) { + tcc_set_str(&s->fini_symbol, o.arg); ignoring = 1; - } else if (link_option(option, "oformat=", &p)) { -#if defined(TCC_TARGET_PE) - if (strstart("pe-", &p)) { + } else if (link_option(&o, "Map=")) { + tcc_set_str(&s->mapfile, o.arg); + ignoring = 1; + } else if (link_option(&o, "oformat=")) { +#if defined TCC_TARGET_PE + if (0 == strncmp("pe-", o.arg, 3)) #elif PTR_SIZE == 8 - if (strstart("elf64-", &p)) { + if (0 == strncmp("elf64-", o.arg, 6)) #else - if (strstart("elf32-", &p)) { + if (0 == strncmp("elf32-", o.arg, 6)) #endif s->output_format = TCC_OUTPUT_FORMAT_ELF; - } else if (link_arg("binary", p)) { + else if (0==strcmp("binary", o.arg)) s->output_format = TCC_OUTPUT_FORMAT_BINARY; -#ifdef TCC_TARGET_COFF - } else if (link_arg("coff", p)) { +#if defined TCC_TARGET_COFF + else if (0==strcmp("coff", o.arg)) s->output_format = TCC_OUTPUT_FORMAT_COFF; #endif - } else + else goto err; - - } else if (link_option(option, "as-needed", &p)) { - ignoring = 1; - } else if (link_option(option, "O", &p)) { - ignoring = 1; - } else if (link_option(option, "export-all-symbols", &p)) { + } else if (link_option(&o, "export-all-symbols") + || link_option(&o, "export-dynamic")) { s->rdynamic = 1; - } else if (link_option(option, "export-dynamic", &p)) { - s->rdynamic = 1; - } else if (link_option(option, "rpath=", &p)) { - copy_linker_arg(&s->rpath, p, ':'); - } else if (link_option(option, "dynamic-linker=", &p) - || link_option(option, "I=", &p)) { - copy_linker_arg(&s->elfint, p, 0); - } else if (strncmp("-I/", option, 3) == 0 - || strncmp("-I.", option, 3) == 0) { - p = option; - copy_linker_arg(&s->elfint, option + 2, 0); - } else if (link_option(option, "enable-new-dtags", &p)) { + } else if (link_option(&o, "rpath=")) { + tcc_concat_str(&s->rpath, o.arg, ':'); + } else if (link_option(&o, "dynamic-linker=") || link_option(&o, "I:")) { + tcc_set_str(&s->elfint, o.arg); + } else if (link_option(&o, "enable-new-dtags")) { s->enable_new_dtags = 1; - } else if (link_option(option, "section-alignment=", &p)) { - s->section_align = strtoul(p, &end, 16); - } else if (link_option(option, "soname=", &p)) { - copy_linker_arg(&s->soname, p, 0); - } else if (link_option(option, "install_name=", &p)) { - copy_linker_arg(&s->soname, p, 0); + } else if (link_option(&o, "section-alignment=")) { + s->section_align = strtoul(o.arg, &end, 16); + } else if (link_option(&o, "soname=") || link_option(&o, "install_name=")) { + tcc_set_str(&s->soname, o.arg); + } else if (link_option(&o, "whole-archive")) { + s->filetype |= AFF_WHOLE_ARCHIVE; + } else if (link_option(&o, "no-whole-archive")) { + s->filetype &= ~AFF_WHOLE_ARCHIVE; #ifdef TCC_TARGET_PE - } else if (link_option(option, "large-address-aware", &p)) { + } else if (link_option(&o, "large-address-aware")) { s->pe_characteristics |= 0x20; - } else if (link_option(option, "file-alignment=", &p)) { - s->pe_file_align = strtoul(p, &end, 16); - } else if (link_option(option, "stack=", &p)) { - s->pe_stack_size = strtoul(p, &end, 10); - } else if (link_option(option, "subsystem=", &p)) { + } else if (link_option(&o, "file-alignment=")) { + s->pe_file_align = strtoul(o.arg, &end, 16); + } else if (link_option(&o, "stack=")) { + s->pe_stack_size = strtoul(o.arg, &end, 10); + } else if (link_option(&o, "subsystem=")) { #if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) - if (link_arg("native", p)) { + if (0==strcmp("native", o.arg)) { s->pe_subsystem = 1; - } else if (link_arg("console", p)) { + } else if (0==strcmp("console", o.arg)) { s->pe_subsystem = 3; - } else if (link_arg("gui", p) || link_arg("windows", p)) { + } else if (0==strcmp("gui", o.arg) || 0==strcmp("windows", o.arg)) { s->pe_subsystem = 2; - } else if (link_arg("posix", p)) { + } else if (0==strcmp("posix", o.arg)) { s->pe_subsystem = 7; - } else if (link_arg("efiapp", p)) { + } else if (0==strcmp("efiapp", o.arg)) { s->pe_subsystem = 10; - } else if (link_arg("efiboot", p)) { + } else if (0==strcmp("efiboot", o.arg)) { s->pe_subsystem = 11; - } else if (link_arg("efiruntime", p)) { + } else if (0==strcmp("efiruntime", o.arg)) { s->pe_subsystem = 12; - } else if (link_arg("efirom", p)) { + } else if (0==strcmp("efirom", o.arg)) { s->pe_subsystem = 13; #elif defined(TCC_TARGET_ARM) - if (link_arg("wince", p)) { + if (0==strcmp("wince", o.arg)) { s->pe_subsystem = 9; #endif } else goto err; -#endif +#endif /* PE */ #ifdef TCC_TARGET_MACHO - } else if (link_option(option, "all_load", &p)) { + } else if (link_option(&o, "all_load")) { s->filetype |= AFF_WHOLE_ARCHIVE; - } else if (link_option(option, "force_load", &p)) { - s->filetype |= AFF_WHOLE_ARCHIVE; - args_parser_add_file(s, p, AFF_TYPE_LIB | (s->filetype & ~AFF_TYPE_MASK)); - s->nb_libraries++; - } else if (link_option(option, "single_module", &p)) { + } else if (link_option(&o, "force_load=")) { + args_parser_add_file(s, o.arg, AFF_TYPE_LIB | AFF_WHOLE_ARCHIVE); + } else if (link_option(&o, "single_module")) { ignoring = 1; #endif - } else if (ret = link_option(option, "?whole-archive", &p), ret) { - if (ret > 0) - s->filetype |= AFF_WHOLE_ARCHIVE; - else - s->filetype &= ~AFF_WHOLE_ARCHIVE; - } else if (link_option(option, "z=", &p)) { + } else if (link_option(&o, "as-needed")) { ignoring = 1; - } else if (p) { - return 0; + } else if (link_option(&o, "O")) { + ignoring = 1; + } else if (link_option(&o, "z=")) { + ignoring = 1; + } else if (link_option(&o, "L:")) { + tcc_add_library_path(s, o.arg); + } else if (link_option(&o, "l:")) { + args_parser_add_file(s, o.arg, AFF_TYPE_LIB | (s->filetype & ~AFF_TYPE_MASK)); + } else if (o.match) { + return 0; /* expecting argument with next '-Wl,' */ } else { err: - return tcc_error_noabort("unsupported linker option '%s'", option); + return tcc_error_noabort("unsupported linker option '%s'", o.opt); } if (ignoring) - tcc_warning_c(warn_unsupported)("unsupported linker option '%s'", option); - option = skip_linker_arg(&p); + tcc_warning_c(warn_unsupported)("unsupported linker option '%s'", o.opt); + ++s->link_optind; } - return 1; + return 0; } typedef struct TCCOption { @@ -1541,7 +1541,6 @@ enum { TCC_OPTION_bench, TCC_OPTION_bt, TCC_OPTION_b, - TCC_OPTION_ba, TCC_OPTION_g, TCC_OPTION_c, TCC_OPTION_dumpmachine, @@ -1580,6 +1579,7 @@ enum { TCC_OPTION_x, TCC_OPTION_ar, TCC_OPTION_impdef, + /* macho */ TCC_OPTION_dynamiclib, TCC_OPTION_flat_namespace, TCC_OPTION_two_levelnamespace, @@ -1617,11 +1617,13 @@ static const TCCOption tcc_options[] = { #ifdef TCC_TARGET_MACHO { "compatibility_version", TCC_OPTION_compatibility_version, TCC_OPTION_HAS_ARG }, { "current_version", TCC_OPTION_current_version, TCC_OPTION_HAS_ARG }, + { "dynamiclib", TCC_OPTION_dynamiclib, 0 }, + { "flat_namespace", TCC_OPTION_flat_namespace, 0 }, + { "install_name", TCC_OPTION_install_name, TCC_OPTION_HAS_ARG }, + { "two_levelnamespace", TCC_OPTION_two_levelnamespace, 0 }, + { "undefined", TCC_OPTION_undefined, TCC_OPTION_HAS_ARG }, #endif { "c", TCC_OPTION_c, 0 }, -#ifdef TCC_TARGET_MACHO - { "dynamiclib", TCC_OPTION_dynamiclib, 0 }, -#endif { "dumpmachine", TCC_OPTION_dumpmachine, 0}, { "dumpversion", TCC_OPTION_dumpversion, 0}, { "d", TCC_OPTION_d, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, @@ -1642,9 +1644,6 @@ static const TCCOption tcc_options[] = { { "mfloat-abi", TCC_OPTION_mfloat_abi, TCC_OPTION_HAS_ARG }, #endif { "m", TCC_OPTION_m, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, -#ifdef TCC_TARGET_MACHO - { "flat_namespace", TCC_OPTION_flat_namespace, 0 }, -#endif { "f", TCC_OPTION_f, TCC_OPTION_HAS_ARG | TCC_OPTION_NOSEP }, { "isystem", TCC_OPTION_isystem, TCC_OPTION_HAS_ARG }, { "include", TCC_OPTION_include, TCC_OPTION_HAS_ARG }, @@ -1660,14 +1659,10 @@ static const TCCOption tcc_options[] = { { "MMD", TCC_OPTION_MMD, 0}, { "MP", TCC_OPTION_MP, 0}, { "x", TCC_OPTION_x, TCC_OPTION_HAS_ARG }, + /* tcctools */ { "ar", TCC_OPTION_ar, 0}, #ifdef TCC_TARGET_PE { "impdef", TCC_OPTION_impdef, 0}, -#endif -#ifdef TCC_TARGET_MACHO - { "install_name", TCC_OPTION_install_name, TCC_OPTION_HAS_ARG }, - { "two_levelnamespace", TCC_OPTION_two_levelnamespace, 0 }, - { "undefined", TCC_OPTION_undefined, TCC_OPTION_HAS_ARG }, #endif /* ignored (silently, except after -Wunsupported) */ { "arch", 0, TCC_OPTION_HAS_ARG}, @@ -1788,65 +1783,6 @@ static const char dumpmachine_str[] = #endif ; -static int args_parser_make_argv(const char *r, int *argc, char ***argv) -{ - int ret = 0, q, c; - CString str; - for(;;) { - while (c = (unsigned char)*r, c && c <= ' ') - ++r; - if (c == 0) - break; - q = 0; - cstr_new(&str); - while (c = (unsigned char)*r, c) { - ++r; - if (c == '\\' && (*r == '"' || *r == '\\')) { - c = *r++; - } else if (c == '"') { - q = !q; - continue; - } else if (q == 0 && c <= ' ') { - break; - } - cstr_ccat(&str, c); - } - cstr_ccat(&str, 0); - //printf("<%s>\n", str.data), fflush(stdout); - dynarray_add(argv, argc, tcc_strdup(str.data)); - cstr_free(&str); - ++ret; - } - return ret; -} - -/* read list file */ -static int args_parser_listfile(TCCState *s, - const char *filename, int optind, int *pargc, char ***pargv) -{ - TCCState *s1 = s; - int fd, i; - char *p; - int argc = 0; - char **argv = NULL; - - fd = open(filename, O_RDONLY | O_BINARY); - if (fd < 0) - return tcc_error_noabort("listfile '%s' not found", filename); - - p = tcc_load_text(fd); - for (i = 0; i < *pargc; ++i) - if (i == optind) - args_parser_make_argv(p, &argc, &argv); - else - dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i])); - - tcc_free(p); - dynarray_reset(&s->argv, &s->argc); - *pargc = s->argc = argc, *pargv = s->argv = argv; - return 0; -} - #if defined TCC_TARGET_MACHO static uint32_t parse_version(TCCState *s1, const char *version) { @@ -1867,46 +1803,79 @@ static uint32_t parse_version(TCCState *s1, const char *version) } #endif -PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv, int optind) +/* insert args from 'p' (separated by sep or ' ') into argv at position 'optind' */ +static void insert_args(TCCState *s1, char ***pargv, int *pargc, int optind, const char *p, int sep) +{ + int argc = 0; + char **argv = NULL; + int i; + for (i = 0; i < *pargc; ++i) + if (i == optind) + dynarray_split(&argv, &argc, p, sep); + else + dynarray_add(&argv, &argc, tcc_strdup((*pargv)[i])); + dynarray_reset(&s1->argv, &s1->argc); + *pargc = s1->argc = argc; + *pargv = s1->argv = argv; +} + +static void args_parser_add_file(TCCState *s, const char* filename, int filetype) +{ + struct filespec *f = tcc_malloc(sizeof *f + strlen(filename)); + f->type = filetype; + strcpy(f->name, filename); + dynarray_add(&s->files, &s->nb_files, f); + if (filetype & AFF_TYPE_LIB) + ++s->nb_libraries; +} + +/* using * to argc/argv to let "tcc -ar" benefit from @listfile expansion */ +PUB_FUNC int tcc_parse_args(TCCState *s, int *pargc, char ***pargv) { TCCState *s1 = s; const TCCOption *popt; const char *optarg, *r; const char *run = NULL; int x; - int tool = 0, arg_start = 0, noaction = optind; + int tool = 0, arg_start = 0, not_empty = 0, optind = 1; char **argv = *pargv; int argc = *pargc; - cstr_reset(&s->linker_arg); + s->link_optind = s->link_argc; while (optind < argc) { r = argv[optind]; - if (r[0] == '@' && r[1] != '\0') { - if (args_parser_listfile(s, r + 1, optind, &argc, &argv)) - return -1; + if (r[0] == '@' && r[1] != '\0') { /* read @listfile */ + int fd; char *p; + fd = open(++r, O_RDONLY | O_BINARY); + if (fd < 0) + return tcc_error_noabort("listfile '%s' not found", r); + p = tcc_load_text(fd); + insert_args(s1, &argv, &argc, optind, p, 0); + close(fd), tcc_free(p); continue; } optind++; - if (tool) { - if (r[0] == '-' && r[1] == 'v' && r[2] == 0) - ++s->verbose; + if (tool) { /* ignore all except -v and @listfile */ + s->verbose += !strcmp(r, "-v"); continue; } - if (r[0] != '-' || r[1] == '\0') { + + if (r[0] != '-' || r[1] == '\0') { /* file or '-' (stdin) */ args_parser_add_file(s, r, s->filetype); + not_empty = 1; + dorun: if (run) { -dorun: + /* tcc -run */ if (tcc_set_options(s, run)) return -1; - arg_start = optind - 1; + arg_start = optind - 1; /* argv[0] will be */ break; } continue; } - - /* allow "tcc files... -run -- args ..." */ - if (r[1] == '-' && r[2] == '\0' && run) + /* Also allow "tcc -run -- " */ + if (r[1] == '-' && r[2] == '\0') goto dorun; /* find option in table */ @@ -1921,7 +1890,6 @@ dorun: if (popt->flags & TCC_OPTION_HAS_ARG) { if (*r1 == '\0' && !(popt->flags & TCC_OPTION_NOSEP)) { if (optind >= argc) - arg_err: return tcc_error_noabort("argument to '%s' is missing", r); optarg = argv[optind++]; } @@ -1931,12 +1899,6 @@ dorun: } switch(popt->index) { - case TCC_OPTION_HELP: - x = OPT_HELP; - goto extra_action; - case TCC_OPTION_HELP2: - x = OPT_HELP2; - goto extra_action; case TCC_OPTION_I: tcc_add_include_path(s, optarg); break; @@ -1952,11 +1914,9 @@ dorun: case TCC_OPTION_B: /* set tcc utilities path (mainly for tcc development) */ tcc_set_lib_path(s, optarg); - ++noaction; - break; + continue; case TCC_OPTION_l: args_parser_add_file(s, optarg, AFF_TYPE_LIB | (s->filetype & ~AFF_TYPE_MASK)); - s->nb_libraries++; break; case TCC_OPTION_pthread: s->option_pthread = 1; @@ -2027,14 +1987,14 @@ dorun: x = TCC_OUTPUT_DLL; goto set_output_type; case TCC_OPTION_soname: - s->soname = tcc_strdup(optarg); + tcc_set_str(&s->soname, optarg); break; case TCC_OPTION_o: if (s->outfile) { tcc_warning("multiple -o option"); tcc_free(s->outfile); } - s->outfile = tcc_strdup(optarg); + tcc_set_str(&s->outfile, optarg); break; case TCC_OPTION_r: /* generate a .o merging several output files */ @@ -2054,17 +2014,18 @@ dorun: s->nostdlib = 1; break; case TCC_OPTION_run: -#ifndef TCC_IS_NATIVE - return tcc_error_noabort("-run is not available in a cross compiler"); -#else +#ifdef TCC_IS_NATIVE + /* When from script "#!/usr/bin/tcc -run ", + argv[1] is "-run " and argv[2] is */ run = optarg; x = TCC_OUTPUT_MEMORY; goto set_output_type; +#else + return tcc_error_noabort("-run is not available in a cross compiler"); #endif case TCC_OPTION_v: do ++s->verbose; while (*optarg++ == 'v'); - ++noaction; - break; + continue; case TCC_OPTION_f: if (set_flag(s, options_f, optarg) < 0) goto unsupported_option; @@ -2086,7 +2047,7 @@ dorun: goto unsupported_option; if (PTR_SIZE != x/8) return x; - ++noaction; + continue; } break; case TCC_OPTION_W: @@ -2101,25 +2062,13 @@ dorun: s->rdynamic = 1; break; case TCC_OPTION_Wl: - if (s->linker_arg.size) - ((char*)s->linker_arg.data)[s->linker_arg.size - 1] = ','; - cstr_cat(&s->linker_arg, optarg, 0); - x = tcc_set_linker(s, s->linker_arg.data); - if (x) - cstr_reset(&s->linker_arg); - if (x < 0) + if (tcc_set_linker(s, optarg) < 0) return -1; break; case TCC_OPTION_Wp: - { - char *p = tcc_strdup(optarg), *q = p; - while (!!(q = strchr(q, ','))) *q++ = ' '; - x = tcc_set_options(s, p); - tcc_free(p); - if (x < 0) - return -1; + if (argv[0]) /* not with tcc_set_options() */ + insert_args(s, &argv, &argc, --optind, optarg, ','); break; - } case TCC_OPTION_E: x = TCC_OUTPUT_PREPROCESS; goto set_output_type; @@ -2132,7 +2081,7 @@ dorun: case TCC_OPTION_MM: s->just_deps = 1; if(!s->deps_outfile) - s->deps_outfile = tcc_strdup("-"); + tcc_set_str(&s->deps_outfile, "-"); // fall through case TCC_OPTION_MMD: s->gen_deps = 1; @@ -2142,7 +2091,7 @@ dorun: s->include_sys_deps = 1; break; case TCC_OPTION_MF: - s->deps_outfile = tcc_strdup(optarg); + tcc_set_str(&s->deps_outfile, optarg); break; case TCC_OPTION_MP: s->gen_phony_deps = 1; @@ -2170,12 +2119,6 @@ dorun: case TCC_OPTION_O: s->optimize = isnum(optarg[0]) ? optarg[0]-'0' : 1 /* -O -Os */; break; - case TCC_OPTION_print_search_dirs: - x = OPT_PRINT_DIRS; - goto extra_action; - case TCC_OPTION_impdef: - x = OPT_IMPDEF; - goto extra_action; #if defined TCC_TARGET_MACHO case TCC_OPTION_dynamiclib: x = TCC_OUTPUT_DLL; @@ -2187,7 +2130,7 @@ dorun: case TCC_OPTION_undefined: break; case TCC_OPTION_install_name: - s->install_name = tcc_strdup(optarg); + tcc_set_str(&s->install_name, optarg); break; case TCC_OPTION_compatibility_version: s->compatibility_version = parse_version(s, optarg); @@ -2196,11 +2139,23 @@ dorun: s->current_version = parse_version(s, optarg);; break; #endif + case TCC_OPTION_HELP: + x = OPT_HELP; + goto extra_action; + case TCC_OPTION_HELP2: + x = OPT_HELP2; + goto extra_action; + case TCC_OPTION_print_search_dirs: + x = OPT_PRINT_DIRS; + goto extra_action; + case TCC_OPTION_impdef: + x = OPT_IMPDEF; + goto extra_action; case TCC_OPTION_ar: x = OPT_AR; extra_action: arg_start = optind - 1; - if (arg_start != noaction) + if (not_empty) return tcc_error_noabort("cannot parse %s here", r); tool = x; break; @@ -2209,16 +2164,19 @@ unsupported_option: tcc_warning_c(warn_unsupported)("unsupported option '%s'", r); break; } + not_empty = 1; } - if (s->linker_arg.size) { - r = s->linker_arg.data; - goto arg_err; - } - *pargc = argc - arg_start; - *pargv = argv + arg_start; - if (tool) + + if (s->link_optind < s->link_argc) + return tcc_error_noabort("argument to '-Wl,%s' is missing", s->link_argv[s->link_optind]); + if (NULL == argv[0]) /* from tcc_set_options() */ + return 0; + if (arg_start) { + *pargc = argc - arg_start; + *pargv = argv + arg_start; return tool; - if (optind != noaction) + } + if (not_empty) return 0; if (s->verbose == 2) return OPT_PRINT_DIRS; @@ -2231,10 +2189,11 @@ LIBTCCAPI int tcc_set_options(TCCState *s, const char *r) { char **argv = NULL; int argc = 0, ret; - args_parser_make_argv(r, &argc, &argv); - ret = tcc_parse_args(s, &argc, &argv, 0); + dynarray_add(&argv, &argc, 0); + dynarray_split(&argv, &argc, r, 0); + ret = tcc_parse_args(s, &argc, &argv); dynarray_reset(&argv, &argc); - return ret < 0 ? ret : 0; + return ret; } PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time) @@ -2267,9 +2226,3 @@ PUB_FUNC void tcc_print_stats(TCCState *s1, unsigned total_time) fprintf(stderr, " %d max (bytes)\n", mem_max_size); #endif } - -#if ONE_SOURCE -# undef malloc -# undef realloc -# undef free -#endif diff --git a/tcc-doc.texi b/tcc-doc.texi index d9df2d1a..9a9695f7 100644 --- a/tcc-doc.texi +++ b/tcc-doc.texi @@ -185,16 +185,6 @@ Show included files. As sole argument, print search dirs. -vvv shows tries too @item -bench Display compilation statistics. -@item -pthread -Preprocess with @option{-D_REENTRANT}, link with @option{-lpthread}. - -@item -On -Pretend to optimise: set @option{-D__OPTIMIZE__} to the numeric value of n. -Note that TCC performs no additional optimisation. - -@item -dt -With @option{-run}/@option{-E}: auto-define 'test_...' macros - @end table Preprocessor options: @@ -441,6 +431,12 @@ gnu11; @code{199901} otherwise. @item -x[c|a|b|n] Specify content of next input file: respectively C, assembly, binary, or none. +@item -O[n] +Same as @option{-D__OPTIMIZE__} except for -O0. + +@item -pthread +Preprocess with @option{-D_REENTRANT}, link with @option{-lpthread}. + @item -M Just output makefile fragment with dependencies @@ -456,6 +452,9 @@ Like -MD except mention only user header files, not system header files. @item -MF depfile Use @file{depfile} as output for -MD. +@item -MP +Mention all dependencies as targets too. + @item -print-search-dirs Print the configured installation directory and a list of library and include directories tcc will search. @@ -463,6 +462,9 @@ and include directories tcc will search. @item -dumpversion Print version. +@item -dt +With @option{-run}/@option{-E}: auto-define 'test_...' macros + @end table Target specific options: diff --git a/tcc.c b/tcc.c index c6217499..3221ed42 100644 --- a/tcc.c +++ b/tcc.c @@ -158,7 +158,7 @@ static const char help2[] = #endif " -Bsymbolic set DT_SYMBOLIC elf tag\n" " -oformat=[elf32/64-* binary] set executable output format\n" - " -init= -fini= -Map= -as-needed -O (ignored)\n" + " -init= -fini= -Map= -as-needed -O -z= (ignored)\n" "Predefined macros:\n" " tcc -E -dM - < /dev/null\n" #endif @@ -221,7 +221,7 @@ static void print_search_dirs(TCCState *s) print_dirs("include", s->sysinclude_paths, s->nb_sysinclude_paths); print_dirs("libraries", s->library_paths, s->nb_library_paths); printf("libtcc1:\n %s/%s\n", s->library_paths[0], CONFIG_TCC_CROSSPREFIX TCC_LIBTCC1); -#if !defined TCC_TARGET_PE && !defined TCC_TARGET_MACHO +#ifdef TCC_TARGET_UNIX print_dirs("crt", s->crt_paths, s->nb_crt_paths); printf("elfinterp:\n %s\n", DEFAULT_ELFINTERP(s)); #endif @@ -253,7 +253,9 @@ static char *default_outputfile(TCCState *s, const char *first_file) if (first_file && strcmp(first_file, "-")) name = tcc_basename(first_file); - snprintf(buf, sizeof(buf), "%s", name); + if (strlen(name) + 4 >= sizeof buf) + name = "a"; + strcpy(buf, name); ext = tcc_fileextension(buf); #ifdef TCC_TARGET_PE if (s->output_type == TCC_OUTPUT_DLL) @@ -293,7 +295,7 @@ int main(int argc0, char **argv0) redo: argc = argc0, argv = argv0; s = s1 = tcc_new(); - opt = tcc_parse_args(s, &argc, &argv, 1); + opt = tcc_parse_args(s, &argc, &argv); if (opt < 0) return 1; diff --git a/tcc.h b/tcc.h index 45e7b5b7..452a723a 100644 --- a/tcc.h +++ b/tcc.h @@ -217,6 +217,8 @@ extern long double strtold (const char *__nptr, char **__endptr); #if defined TCC_TARGET_PE || defined TCC_TARGET_MACHO # define ELF_OBJ_ONLY /* create elf .o but native executables */ +#else +# define TCC_TARGET_UNIX 1 #endif /* No ten-byte long doubles on window and macos except in @@ -919,8 +921,6 @@ struct TCCState { /* debug state */ struct _tccdbg *dState; - /* Is there a new undefined sym since last new_undef_sym() */ - int new_undef_sym; /* extra attributes (eg. GOT/PLT value) for symtab symbols */ struct sym_attr *sym_attrs; int nb_sym_attrs; @@ -988,7 +988,7 @@ struct TCCState { unsigned int total_output[4]; /* used by tcc_load_ldscript */ - int fd, cc; + unsigned char *ld_p; /* text pointer */ /* for warnings/errors for object files */ const char *current_filename; @@ -1001,7 +1001,9 @@ struct TCCState { char *deps_outfile; /* option -MF */ int argc; char **argv; - CString linker_arg; /* collect -Wl options */ + /* -Wl options */ + char **link_argv; + int link_argc, link_optind; }; struct filespec { @@ -1195,7 +1197,7 @@ ST_DATA int g_debug; /* public functions currently used by the tcc main function */ ST_FUNC char *pstrcpy(char *buf, size_t buf_size, const char *s); ST_FUNC char *pstrcat(char *buf, size_t buf_size, const char *s); -ST_FUNC char *pstrncpy(char *out, const char *in, size_t num); +ST_FUNC char *pstrncpy(char *out, size_t buf_size, const char *s, size_t num); PUB_FUNC char *tcc_basename(const char *name); PUB_FUNC char *tcc_fileextension (const char *name); @@ -1265,7 +1267,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) #define AFF_TYPE_ASM 2 #define AFF_TYPE_ASMPP 4 #define AFF_TYPE_LIB 8 -#define AFF_TYPE_MASK (15 | AFF_TYPE_BIN) +#define AFF_TYPE_MASK (7 | AFF_TYPE_BIN) /* values from tcc_object_type(...) */ #define AFF_BINTYPE_REL 1 #define AFF_BINTYPE_DYN 2 @@ -1274,6 +1276,7 @@ ST_FUNC int tcc_add_file_internal(TCCState *s1, const char *filename, int flags) /* return value of tcc_add_file_internal(): 0, -1, or FILE_NOT_FOUND */ #define FILE_NOT_FOUND -2 +#define FILE_NOT_RECOGNIZED -3 /* unrecognized file type */ #ifndef ELF_OBJ_ONLY ST_FUNC int tcc_add_crt(TCCState *s, const char *filename); @@ -1289,7 +1292,7 @@ ST_FUNC void tcc_add_btstub(TCCState *s1); ST_FUNC void tcc_add_pragma_libs(TCCState *s1); PUB_FUNC int tcc_add_library_err(TCCState *s, const char *f); PUB_FUNC void tcc_print_stats(TCCState *s, unsigned total_time); -PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv, int optind); +PUB_FUNC int tcc_parse_args(TCCState *s, int *argc, char ***argv); #ifdef _WIN32 ST_FUNC char *normalize_slashes(char *path); #endif @@ -1571,7 +1574,7 @@ ST_FUNC void tcc_add_runtime(TCCState *s1); /* ------------ xxx-link.c ------------ */ -#if !defined ELF_OBJ_ONLY || defined TCC_TARGET_MACHO +#ifndef TCC_TARGET_PE ST_FUNC int code_reloc (int reloc_type); ST_FUNC int gotplt_entry_type (int reloc_type); /* Whether to generate a GOT/PLT entry and when. NO_GOTPLT_ENTRY is first so @@ -1583,13 +1586,11 @@ enum gotplt_entry { ALWAYS_GOTPLT_ENTRY /* always generate (eg. PLTOFF relocs) */ }; #define NEED_RELOC_TYPE - #if !defined TCC_TARGET_MACHO || defined TCC_IS_NATIVE ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr); ST_FUNC void relocate_plt(TCCState *s1); ST_FUNC void build_got_entries(TCCState *s1, int got_sym); /* in tccelf.c */ #define NEED_BUILD_GOT - #endif #endif @@ -1809,7 +1810,7 @@ ST_FUNC int macho_load_dll(TCCState *s1, int fd, const char *filename, int lev); ST_FUNC int macho_load_tbd(TCCState *s1, int fd, const char *filename, int lev); #ifdef TCC_IS_NATIVE ST_FUNC void tcc_add_macos_sdkpath(TCCState* s); -ST_FUNC const char* macho_tbd_soname(const char* filename); +ST_FUNC char* macho_tbd_soname(int fd); #endif #endif /* ------------ tccrun.c ----------------- */ diff --git a/tccelf.c b/tccelf.c index ee3e6360..e19bc202 100644 --- a/tccelf.c +++ b/tccelf.c @@ -526,6 +526,24 @@ LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name) return addr == -1 ? NULL : (void*)(uintptr_t)addr; } +LIBTCCAPI int tcc_add_symbol(TCCState *s1, const char *name, const void *val) +{ +#ifdef TCC_TARGET_PE + /* On x86_64 'val' might not be reachable with a 32bit offset. + So it is handled here as if it were in a DLL. */ + pe_putimport(s1, 0, name, (uintptr_t)val); +#else + char buf[256]; + if (s1->leading_underscore) { + buf[0] = '_'; + pstrcpy(buf + 1, sizeof(buf) - 1, name); + name = buf; + } + set_global_sym(s1, name, NULL, (addr_t)(uintptr_t)val); /* NULL: SHN_ABS */ +#endif + return 0; +} + /* list elf symbol names and values */ ST_FUNC void list_elf_symbols(TCCState *s, void *ctx, void (*symbol_cb)(void *ctx, const char *name, const void *val)) @@ -733,7 +751,6 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size, do_patch: esym->st_info = ELFW(ST_INFO)(sym_bind, sym_type); esym->st_shndx = shndx; - s1->new_undef_sym = 1; esym->st_value = value; esym->st_size = size; } @@ -1058,15 +1075,16 @@ ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve) if (do_resolve) { #if defined TCC_IS_NATIVE && !defined TCC_TARGET_PE /* dlsym() needs the undecorated name. */ - void *addr = dlsym(RTLD_DEFAULT, &name[s1->leading_underscore]); -#if TARGETOS_OpenBSD || TARGETOS_FreeBSD || TARGETOS_NetBSD || TARGETOS_ANDROID + const char *name_ud = &name[s1->leading_underscore]; + void *addr = NULL; + if (!s1->nostdlib) + addr = dlsym(RTLD_DEFAULT, name_ud); if (addr == NULL) { int i; for (i = 0; i < s1->nb_loaded_dlls; i++) - if ((addr = dlsym(s1->loaded_dlls[i]->handle, name))) + if ((addr = dlsym(s1->loaded_dlls[i]->handle, name_ud))) break; } -#endif if (addr) { sym->st_value = (addr_t) addr; #ifdef DEBUG_RELOC @@ -1691,7 +1709,7 @@ static void tcc_tcov_add_file(TCCState *s1, const char *filename) set_local_sym(s1, &"___tcov_data"[!s1->leading_underscore], tcov_section, 0); } -#if !defined TCC_TARGET_PE && !defined TCC_TARGET_MACHO +#ifdef TCC_TARGET_UNIX /* add libc crt1/crti objects */ ST_FUNC void tccelf_add_crtbegin(TCCState *s1) { @@ -1750,7 +1768,7 @@ ST_FUNC void tccelf_add_crtend(TCCState *s1) tcc_add_crt(s1, "crtn.o"); #endif } -#endif /* !defined TCC_TARGET_PE && !defined TCC_TARGET_MACHO */ +#endif /* TCC_TARGET_UNIX */ #ifndef TCC_TARGET_PE /* add tcc runtime libraries */ @@ -3789,19 +3807,15 @@ ST_FUNC int tcc_load_dll(TCCState *s1, int fd, const char *filename, int level) #define LD_TOK_NAME 256 #define LD_TOK_EOF (-1) - static int ld_inp(TCCState *s1) { - char b; - if (s1->cc != -1) { - int c = s1->cc; - s1->cc = -1; - return c; - } - if (1 == read(s1->fd, &b, 1)) - return b; - return CH_EOF; + int c = *s1->ld_p; + if (c == 0) + return CH_EOF; + ++s1->ld_p; + return c; } +#define ld_unget(s1, ch) if (ch != CH_EOF) --s1->ld_p /* return next ld script token */ static int ld_next(TCCState *s1, char *name, int name_size) @@ -3811,6 +3825,7 @@ static int ld_next(TCCState *s1, char *name, int name_size) redo: ch = ld_inp(s1); + q = name, *q++ = ch; switch(ch) { case ' ': case '\t': @@ -3829,8 +3844,6 @@ static int ld_next(TCCState *s1, char *name, int name_size) } goto redo; } else { - q = name; - *q++ = '/'; goto parse_name; } break; @@ -3889,13 +3902,14 @@ static int ld_next(TCCState *s1, char *name, int name_size) case 'X': case 'Y': case 'Z': + case '-': case '_': case '.': case '$': case '~': - q = name; - parse_name: for(;;) { + ch = ld_inp(s1); + parse_name: if (!((ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || @@ -3904,10 +3918,8 @@ static int ld_next(TCCState *s1, char *name, int name_size) if ((q - name) < name_size - 1) { *q++ = ch; } - ch = ld_inp(s1); } - s1->cc = ch; - *q = '\0'; + ld_unget(s1, ch); c = LD_TOK_NAME; break; case CH_EOF: @@ -3917,93 +3929,71 @@ static int ld_next(TCCState *s1, char *name, int name_size) c = ch; break; } + *q = '\0'; return c; } static int ld_add_file(TCCState *s1, const char filename[]) { - if (filename[0] == '/') { - if (CONFIG_SYSROOT[0] == '\0' - && tcc_add_file_internal(s1, filename, AFF_TYPE_BIN) == 0) - return 0; - filename = tcc_basename(filename); + if (filename[0] == '-' && filename[1] == 'l') + return tcc_add_library(s1, filename + 2); + if (CONFIG_SYSROOT[0] != '\0') { + /* lookup via library paths */ + int ret = tcc_add_dll(s1, tcc_basename(filename), 0); + if (ret != FILE_NOT_FOUND) + return ret; } - return tcc_add_dll(s1, filename, AFF_PRINT_ERROR); + return tcc_add_file_internal(s1, filename, AFF_PRINT_ERROR); } -static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed) +/* did static libraries add new undefined symbols? */ +static int new_undef_sym(TCCState *s1, int sym_offset) { - char filename[1024], libname[1016]; - int t, group, nblibs = 0, ret = 0; - char **libs = NULL; - - group = !strcmp(cmd, "GROUP"); - if (!as_needed) - s1->new_undef_sym = 0; - t = ld_next(s1, filename, sizeof(filename)); - if (t != '(') { - ret = tcc_error_noabort("( expected"); - goto lib_parse_error; + while (sym_offset < s1->symtab->data_offset) { + ElfW(Sym) *esym = (void*)(s1->symtab->data + sym_offset); + if (esym->st_shndx == SHN_UNDEF) + return 1; + sym_offset += sizeof (ElfW(Sym)); } + return 0; +} + +static int ld_add_file_list(TCCState *s1, const char *cmd) +{ + char filename[1024]; + int t, c, sym_offset, ret = 0; + unsigned char *pos = s1->ld_p; + +repeat: + s1->ld_p = pos; + sym_offset = s1->symtab->data_offset; + c = cmd[0]; + + t = ld_next(s1, filename, sizeof(filename)); + if (t != '(') + return tcc_error_noabort("expected '(' after %s", cmd); t = ld_next(s1, filename, sizeof(filename)); for(;;) { - libname[0] = '\0'; if (t == LD_TOK_EOF) { - ret = tcc_error_noabort("unexpected end of file"); - goto lib_parse_error; + return tcc_error_noabort("unexpected end of file"); } else if (t == ')') { break; - } else if (t == '-') { - t = ld_next(s1, filename, sizeof(filename)); - if ((t != LD_TOK_NAME) || (filename[0] != 'l')) { - ret = tcc_error_noabort("library name expected"); - goto lib_parse_error; - } - pstrcpy(libname, sizeof libname, &filename[1]); - if (s1->static_link) { - snprintf(filename, sizeof filename, "lib%s.a", libname); - } else { - snprintf(filename, sizeof filename, "lib%s.so", libname); - } } else if (t != LD_TOK_NAME) { - ret = tcc_error_noabort("filename expected"); - goto lib_parse_error; - } - if (!strcmp(filename, "AS_NEEDED")) { - ret = ld_add_file_list(s1, cmd, 1); - if (ret) - goto lib_parse_error; - } else { - /* TODO: Implement AS_NEEDED support. */ - /* DT_NEEDED is not used any more so ignore as_needed */ - if (1 || !as_needed) { - ret = ld_add_file(s1, filename); - if (ret) - goto lib_parse_error; - if (group) { - /* Add the filename *and* the libname to avoid future conversions */ - dynarray_add(&libs, &nblibs, tcc_strdup(filename)); - if (libname[0] != '\0') - dynarray_add(&libs, &nblibs, tcc_strdup(libname)); - } - } + return tcc_error_noabort("unexpected token '%c'", t); + } else if (!strcmp(filename, "AS_NEEDED")) { + ret = ld_add_file_list(s1, filename); + } else if (c == 'I' || c == 'G' || c == 'A') { + ret = ld_add_file(s1, filename); } + if (ret) + return -1; t = ld_next(s1, filename, sizeof(filename)); - if (t == ',') { + if (t == ',') t = ld_next(s1, filename, sizeof(filename)); - } } - if (group && !as_needed) { - while (s1->new_undef_sym) { - int i; - s1->new_undef_sym = 0; - for (i = 0; i < nblibs; i ++) - ld_add_file(s1, libs[i]); - } - } -lib_parse_error: - dynarray_reset(&libs, &nblibs); - return ret; + if (c == 'G' && new_undef_sym(s1, sym_offset)) + goto repeat; + return 0; } /* interpret a subset of GNU ldscripts to handle the dummy libc.so @@ -4011,40 +4001,30 @@ lib_parse_error: ST_FUNC int tcc_load_ldscript(TCCState *s1, int fd) { char cmd[64]; - char filename[1024]; - int t, ret; + int t, ret = FILE_NOT_RECOGNIZED; + unsigned char *text_ptr, *saved_ptr; - s1->fd = fd; - s1->cc = -1; + saved_ptr = s1->ld_p; + s1->ld_p = text_ptr = (void*)tcc_load_text(fd); for(;;) { t = ld_next(s1, cmd, sizeof(cmd)); if (t == LD_TOK_EOF) - return 0; - else if (t != LD_TOK_NAME) - return -1; + break; if (!strcmp(cmd, "INPUT") || !strcmp(cmd, "GROUP")) { - ret = ld_add_file_list(s1, cmd, 0); - if (ret) - return ret; + ret = ld_add_file_list(s1, cmd); } else if (!strcmp(cmd, "OUTPUT_FORMAT") || !strcmp(cmd, "TARGET")) { /* ignore some commands */ - t = ld_next(s1, cmd, sizeof(cmd)); - if (t != '(') - return tcc_error_noabort("( expected"); - for(;;) { - t = ld_next(s1, filename, sizeof(filename)); - if (t == LD_TOK_EOF) { - return tcc_error_noabort("unexpected end of file"); - } else if (t == ')') { - break; - } - } - } else { - return -1; + ret = ld_add_file_list(s1, cmd); + } else if (0 == ret) { + ret = tcc_error_noabort("unexpected '%s'", cmd); } + if (ret) + break; } - return 0; + tcc_free(text_ptr); + s1->ld_p = saved_ptr; + return ret; } #endif /* !ELF_OBJ_ONLY */ diff --git a/tccgen.c b/tccgen.c index 9bed2297..d4106ac2 100644 --- a/tccgen.c +++ b/tccgen.c @@ -2401,10 +2401,15 @@ static void gen_opic(int op) (l2 == -1 || (l2 == 0xFFFFFFFF && t2 != VT_LLONG))))) { /* filter out NOP operations like x*1, x-0, x&-1... */ vtop--; - } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV)) { + } else if (c2 && (op == '*' || op == TOK_PDIV || op == TOK_UDIV || op == TOK_UMOD)) { /* try to use shifts instead of muls or divs */ if (l2 > 0 && (l2 & (l2 - 1)) == 0) { int n = -1; + if (op == TOK_UMOD) { + vtop->c.i = l2 - 1; + op = '&'; + goto general_case; + } while (l2) { l2 >>= 1; n++; diff --git a/tccmacho.c b/tccmacho.c index 0b70457c..2661f929 100644 --- a/tccmacho.c +++ b/tccmacho.c @@ -2195,9 +2195,6 @@ ST_FUNC int macho_output_file(TCCState *s1, const char *filename) tcc_error_noabort("could not write '%s: %s'", filename, strerror(errno)); return -1; } - if (s1->verbose) - printf("<- %s\n", filename); - tcc_add_runtime(s1); tcc_macho_add_destructor(s1); resolve_common_syms(s1); @@ -2222,6 +2219,8 @@ ST_FUNC int macho_output_file(TCCState *s1, const char *filename) bind_rebase_import(s1, &mo); #endif convert_symbols(s1, &mo); + if (s1->verbose) + printf("<- %s\n", filename); macho_write(s1, &mo, fp); } @@ -2291,12 +2290,9 @@ ST_FUNC void tcc_add_macos_sdkpath(TCCState* s) cstr_free(&path); } -ST_FUNC const char* macho_tbd_soname(const char* filename) { +ST_FUNC char* macho_tbd_soname(int fd) { char *soname, *data, *pos; - const char *ret = filename; - - int fd = open(filename,O_RDONLY); - if (fd<0) return ret; + char *ret = 0; pos = data = tcc_load_text(fd); if (!tbd_parse_movepast("install-name: ")) goto the_end; tbd_parse_skipws; diff --git a/tccpp.c b/tccpp.c index f2d534cd..380fb665 100644 --- a/tccpp.c +++ b/tccpp.c @@ -1354,7 +1354,7 @@ static int parse_include(TCCState *s1, int do_next, int test) cstr_reset(&tokcstr); file->buf_ptr = parse_pp_string(file->buf_ptr, c == '<' ? '>' : c, &tokcstr); i = tokcstr.size; - pstrncpy(name, tokcstr.data, i >= sizeof name ? sizeof name - 1 : i); + pstrncpy(name, sizeof name, tokcstr.data, i); next_nomacro(); } else { /* computed #include : concatenate tokens until result is one of @@ -1395,7 +1395,7 @@ static int parse_include(TCCState *s1, int do_next, int test) if (c != '\"') continue; p = file->true_filename; - pstrncpy(buf, p, tcc_basename(p) - p); + pstrncpy(buf, sizeof buf, p, tcc_basename(p) - p); } else { int j = i - 2, k = j - s1->nb_include_paths; if (k < 0) diff --git a/tccrun.c b/tccrun.c index 28c7379f..6c0dab6d 100644 --- a/tccrun.c +++ b/tccrun.c @@ -221,7 +221,7 @@ LIBTCCAPI int tcc_run(TCCState *s1, int argc, char **argv) tcc_add_symbol(s1, "__rt_exit", rt_exit); if (s1->nostdlib) { - s1->run_main = top_sym = "_start"; + s1->run_main = top_sym = s1->elf_entryname ? s1->elf_entryname : "_start"; } else { tcc_add_support(s1, "runmain.o"); s1->run_main = "_runmain"; @@ -421,7 +421,7 @@ redo: } /* relocate symbols */ - relocate_syms(s1, s1->symtab, !(s1->nostdlib)); + relocate_syms(s1, s1->symtab, 1); /* relocate sections */ #ifdef TCC_TARGET_PE s1->pe_imagebase = mem;