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(-)
This commit is contained in:
grischka
2025-03-28 13:19:38 +01:00
parent b3381269d7
commit f10ab130ec
9 changed files with 561 additions and 622 deletions

891
libtcc.c

File diff suppressed because it is too large Load Diff

View File

@@ -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:

10
tcc.c
View File

@@ -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;

23
tcc.h
View File

@@ -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 ----------------- */

210
tccelf.c
View File

@@ -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 */

View File

@@ -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++;

View File

@@ -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;

View File

@@ -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)

View File

@@ -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;