tccelf: rework linker_sym (_end/end) logic
Some checks failed
build and test / test-x86_64-linux (push) Has been cancelled
build and test / test-x86_64-osx (push) Has been cancelled
build and test / test-aarch64-osx (push) Has been cancelled
build and test / test-x86_64-win32 (push) Has been cancelled
build and test / test-i386-win32 (push) Has been cancelled
build and test / test-arm64-win32 (push) Has been cancelled
build and test / test-armv7-linux (push) Has been cancelled
build and test / test-aarch64-linux (push) Has been cancelled
build and test / test-riscv64-linux (push) Has been cancelled
build and test / test-i386-linux (push) Has been cancelled

- tcc_add_linker_symbols():
    don't add for dlls
- bind_exe_dynsym():
    update _end symbol when done
    allow data definition in the .so override a bss symbol
    in the exe. For example
       exe: int x;
       .so: int x = 123;
- set_elf_sym():
    cannot compare section numbers with symbols from dlls
  tcc_load_obj():
    return -1 from symbol conflict errors

tests/tests2/148_linker_symbols:
- use only one file
- make it print useful output rather than just "2 errors."
  In general, the tests2 setup with .expect files is meant
  to compare text output.  For example:
      printf("val = %d\n", val);

  .expect file can be generated or refreshed using
       make tests2.nnn+
   or in order to force generation with tcc itself
       make tests2.nnn+ GEN-CC='$(GEN-TCC)'

tccgen.c:
- __attribute__((section(...))) : set section type/flags
    according to first usage
- fix bcheck bounds for -fcommon
This commit is contained in:
grischka 2026-08-03 19:02:10 +02:00
parent 128e78930e
commit 64552b3faa
25 changed files with 336 additions and 365 deletions

View File

@ -336,7 +336,7 @@ FORCE:
# some versions of gnu-make do not recognize 'command' as a shell builtin # some versions of gnu-make do not recognize 'command' as a shell builtin
WHICH = sh -c 'command -v $1' WHICH = sh -c 'command -v $1'
run-if = $(if $(shell $(call WHICH,$1)),$S $1 $2,echo "(skipping $@ - no $1)") run-if = $(if $(shell $(call WHICH,$1)),$S $1 $2,@echo "(skipping $@ - no $1)")
S = $(if $(findstring yes,$(SILENT)),@$(info * $@)) S = $(if $(findstring yes,$(SILENT)),@$(info * $@))
# -------------------------------------------------------------------------- # --------------------------------------------------------------------------

1
tcc.h
View File

@ -675,6 +675,7 @@ typedef struct AttributeDef {
int alias_target; /* token */ int alias_target; /* token */
int asm_label; /* associated asm label */ int asm_label; /* associated asm label */
char attr_mode; /* __attribute__((__mode__(...))) */ char attr_mode; /* __attribute__((__mode__(...))) */
char new_section; /* section is new */
} AttributeDef; } AttributeDef;
/* inline functions */ /* inline functions */

180
tccelf.c
View File

@ -742,6 +742,8 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
/* keep first-found weak definition, ignore subsequents */ /* keep first-found weak definition, ignore subsequents */
} else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) { } else if (sym_vis == STV_HIDDEN || sym_vis == STV_INTERNAL) {
/* ignore hidden symbols after */ /* ignore hidden symbols after */
} else if (s->sh_flags & SHF_DYNSYM) {
/* we accept that two DLL define the same symbol */
} else if ((esym->st_shndx == SHN_COMMON } else if ((esym->st_shndx == SHN_COMMON
|| esym->st_shndx == bss_section->sh_num) || esym->st_shndx == bss_section->sh_num)
&& (shndx < SHN_LORESERVE && (shndx < SHN_LORESERVE
@ -750,8 +752,6 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
goto do_patch; goto do_patch;
} else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) { } else if (shndx == SHN_COMMON || shndx == bss_section->sh_num) {
/* data symbol keeps precedence over common/bss */ /* data symbol keeps precedence over common/bss */
} else if (s->sh_flags & SHF_DYNSYM) {
/* we accept that two DLL define the same symbol */
} else if (esym->st_other & ST_ASM_SET) { } else if (esym->st_other & ST_ASM_SET) {
/* If the existing symbol came from an asm .set /* If the existing symbol came from an asm .set
we can override. */ we can override. */
@ -761,7 +761,7 @@ ST_FUNC int set_elf_sym(Section *s, addr_t value, unsigned long size,
printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n", printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
sym_bind, shndx, new_vis, esym_bind, esym->st_shndx, esym_vis); sym_bind, shndx, new_vis, esym_bind, esym->st_shndx, esym_vis);
#endif #endif
tcc_error_noabort("'%s' defined twice", name); tcc_error_noabort("link symbol '%s' defined twice", name);
} }
} else { } else {
esym->st_other = other; esym->st_other = other;
@ -1560,94 +1560,6 @@ ST_FUNC int set_global_sym(TCCState *s1, const char *name, Section *sec, addr_t
ELFW(ST_INFO)(name ? STB_GLOBAL : STB_LOCAL, STT_NOTYPE), 0, shn, name); ELFW(ST_INFO)(name ? STB_GLOBAL : STB_LOCAL, STT_NOTYPE), 0, shn, name);
} }
#ifndef ELF_OBJ_ONLY
static int set_linker_sym(TCCState *s1, const char *name,
Section *sec, addr_t offs)
{
int sym_index = find_elf_sym(symtab_section, name);
ElfW(Sym) *sym = sym_index
? &((ElfW(Sym) *)symtab_section->data)[sym_index] : NULL;
/* Mirror the set_elf_sym cases where the new definition wins. */
int is_linker_sym = !sym || sym->st_shndx == SHN_UNDEF
|| ELFW(ST_BIND)(sym->st_info) == STB_WEAK
|| (sec && sec != bss_section
&& ((sym->st_other & ST_ASM_SET)
|| sym->st_shndx == SHN_COMMON
|| sym->st_shndx == bss_section->sh_num));
sym_index = set_global_sym(s1, name, sec, offs);
if (is_linker_sym)
get_sym_attr(s1, sym_index, 1)->linker_sym = 1;
return sym_index;
}
static void provide_linker_sym(TCCState *s1, const char *name,
Section *sec, addr_t offs)
{
int sym_index = find_elf_sym(symtab_section, name);
if (sym_index) {
if (((ElfW(Sym) *)symtab_section->data)[sym_index].st_shndx != SHN_UNDEF)
return;
} else if (!s1->dynsymtab_section
|| !find_elf_sym(s1->dynsymtab_section, name)) {
return;
}
set_linker_sym(s1, name, sec, offs);
}
static void update_linker_sym(TCCState *s1, const char *name,
Section *sec, addr_t offs)
{
int sym_index = find_elf_sym(symtab_section, name);
if (sym_index && sym_index < s1->nb_sym_attrs
&& s1->sym_attrs[sym_index].linker_sym) {
ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
sym->st_value = offs;
sym->st_shndx = sec->sh_num;
if (s1->dynsym) {
sym_index = find_elf_sym(s1->dynsym, name);
if (sym_index) {
sym = &((ElfW(Sym) *)s1->dynsym->data)[sym_index];
sym->st_value = offs;
sym->st_shndx = sec->sh_num;
}
}
}
}
static void finalize_linker_symbols(TCCState *s1, int *sec_order)
{
Section *etext = text_section;
Section *edata = data_section;
Section *end = bss_section;
Section *s;
int i;
/* Follow the final loadable-section order rather than assuming that
.text, .data and .bss are the last sections of their regions. */
for (i = 1; i < s1->nb_sections; ++i) {
s = s1->sections[sec_order[i]];
if (!(s->sh_flags & SHF_ALLOC))
continue;
end = s;
if (!(s->sh_flags & SHF_WRITE))
etext = s;
if (s->sh_type != SHT_NOBITS)
edata = s;
}
update_linker_sym(s1, "_etext", etext, etext->sh_size);
update_linker_sym(s1, "etext", etext, etext->sh_size);
update_linker_sym(s1, "_edata", edata, edata->sh_size);
update_linker_sym(s1, "edata", edata, edata->sh_size);
update_linker_sym(s1, "_end", end, end->sh_size);
update_linker_sym(s1, "end", end, end->sh_size);
}
#endif
static void add_init_array_defines(TCCState *s1, const char *section_name) static void add_init_array_defines(TCCState *s1, const char *section_name)
{ {
Section *s; Section *s;
@ -1963,6 +1875,39 @@ ST_FUNC void tcc_add_runtime(TCCState *s1)
} }
#endif /* ndef TCC_TARGET_PE */ #endif /* ndef TCC_TARGET_PE */
/* set _etext/_edata/_end f=0:set f=1:when_needed f=2,3:just_update */
static void set_linker_sym(TCCState *s1, const char *name, Section *sec, int f)
{
int sym_index, esym_index, defined;
ElfW(Sym) *sym, *esym;
sym_index = find_elf_sym(symtab_section, name);
sym = (ElfW(Sym) *)symtab_section->data + sym_index;
esym_index = find_elf_sym(s1->dynsymtab_section, name);
esym = (ElfW(Sym) *)s1->dynsymtab_section->data + esym_index;
defined = sym->st_shndx != SHN_UNDEF
|| (esym->st_shndx != SHN_UNDEF && esym->st_size);
switch (f) {
case 1: /* old symbols w/o '_' */
if (!(sym_index || esym_index) || defined)
break;
case 0:
if (defined) {
tcc_warning("linker symbol '%s' already defined", name);
break;
}
sym_index = set_global_sym(s1, name, sec, -1);
get_sym_attr(s1, sym_index, 1)->linker_sym = 1;
break;
default: /* update (bss only) */
if (get_sym_attr(s1, sym_index, 0)->linker_sym)
sym->st_value = sec->data_offset;
}
#ifndef ELF_OBJ_ONLY
if (name[0] == '_')
set_linker_sym(s1, name + 1, sec, f + 1);
#endif
}
/* add various standard linker symbols (must be done after the /* add various standard linker symbols (must be done after the
sections are filled (for example after allocating common sections are filled (for example after allocating common
symbols)) */ symbols)) */
@ -1972,22 +1917,13 @@ static void tcc_add_linker_symbols(TCCState *s1)
int i; int i;
Section *s; Section *s;
#ifndef ELF_OBJ_ONLY set_linker_sym(s1, "_etext", text_section, 0);
set_linker_sym(s1, "_etext", text_section, -1); set_linker_sym(s1, "_edata", data_section, 0);
set_linker_sym(s1, "_edata", data_section, -1); set_linker_sym(s1, "_end", bss_section, 0);
set_linker_sym(s1, "_end", bss_section, -1);
/* These conventional ELF symbols have PROVIDE semantics. */
provide_linker_sym(s1, "etext", text_section, -1);
provide_linker_sym(s1, "edata", data_section, -1);
provide_linker_sym(s1, "end", bss_section, -1);
#else
set_global_sym(s1, "_etext", text_section, -1);
set_global_sym(s1, "_edata", data_section, -1);
set_global_sym(s1, "_end", bss_section, -1);
#endif
#if TARGETOS_OpenBSD #if TARGETOS_OpenBSD
set_global_sym(s1, "__executable_start", NULL, ELF_START_ADDR); set_global_sym(s1, "__executable_start", NULL, ELF_START_ADDR);
#endif #endif
#ifdef TCC_TARGET_RISCV64 #ifdef TCC_TARGET_RISCV64
/* XXX should be .sdata+0x800, not .data+0x800 */ /* XXX should be .sdata+0x800, not .data+0x800 */
set_global_sym(s1, "__global_pointer$", data_section, 0x800); set_global_sym(s1, "__global_pointer$", data_section, 0x800);
@ -2032,7 +1968,7 @@ ST_FUNC void resolve_common_syms(TCCState *s1)
/* Allocate common symbols in BSS. */ /* Allocate common symbols in BSS. */
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) { for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
if (sym->st_shndx == SHN_COMMON) { if (sym->st_shndx == SHN_COMMON && sym->st_size) {
/* symbol alignment is in st_value for SHN_COMMONs */ /* symbol alignment is in st_value for SHN_COMMONs */
sym->st_value = section_add(bss_section, sym->st_size, sym->st_value = section_add(bss_section, sym->st_size,
sym->st_value); sym->st_value);
@ -2041,7 +1977,8 @@ ST_FUNC void resolve_common_syms(TCCState *s1)
} }
/* Now assign linker provided symbols their value. */ /* Now assign linker provided symbols their value. */
tcc_add_linker_symbols(s1); if (s1->output_type != TCC_OUTPUT_DLL)
tcc_add_linker_symbols(s1);
} }
#ifndef ELF_OBJ_ONLY #ifndef ELF_OBJ_ONLY
@ -2129,13 +2066,16 @@ static void bind_exe_dynsyms(TCCState *s1, int is_PIE)
- if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT - if STT_FUNC or STT_GNU_IFUNC symbol -> add it in PLT
- if STT_OBJECT symbol -> add it in .bss section with suitable reloc */ - if STT_OBJECT symbol -> add it in .bss section with suitable reloc */
for_each_elem(symtab_section, 1, sym, ElfW(Sym)) { for_each_elem(symtab_section, 1, sym, ElfW(Sym)) {
if (sym->st_shndx == SHN_UNDEF) { if (sym->st_shndx == SHN_UNDEF
/* bss symbols may be initialized in the shared library */
|| sym->st_shndx == SHN_COMMON
|| sym->st_shndx == bss_section->sh_num) {
name = (char *) symtab_section->link->data + sym->st_name; name = (char *) symtab_section->link->data + sym->st_name;
sym_index = find_elf_sym(s1->dynsymtab_section, name); sym_index = find_elf_sym(s1->dynsymtab_section, name);
if (sym_index) { esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
if (sym_index && esym->st_shndx != SHN_UNDEF) {
if (is_PIE) if (is_PIE)
continue; continue;
esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
type = ELFW(ST_TYPE)(esym->st_info); type = ELFW(ST_TYPE)(esym->st_info);
if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) { if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
/* Indirect functions shall have STT_FUNC type in executable /* Indirect functions shall have STT_FUNC type in executable
@ -2154,15 +2094,19 @@ static void bind_exe_dynsyms(TCCState *s1, int is_PIE)
} else if (type == STT_OBJECT) { } else if (type == STT_OBJECT) {
unsigned long offset; unsigned long offset;
ElfW(Sym) *dynsym; ElfW(Sym) *dynsym;
offset = bss_section->data_offset; if (sym->st_shndx == bss_section->sh_num) {
/* XXX: which alignment ? */ offset = sym->st_value;
offset = (offset + 16 - 1) & -16; } else {
offset = bss_section->data_offset;
/* XXX: which alignment ? */
offset = (offset + 16 - 1) & -16;
bss_section->data_offset = offset + esym->st_size;
}
set_elf_sym (s1->symtab, offset, esym->st_size, set_elf_sym (s1->symtab, offset, esym->st_size,
esym->st_info, 0, bss_section->sh_num, name); esym->st_info, 0, bss_section->sh_num, name);
index = put_elf_sym(s1->dynsym, offset, esym->st_size, index = put_elf_sym(s1->dynsym, offset, esym->st_size,
esym->st_info, 0, bss_section->sh_num, esym->st_info, 0, bss_section->sh_num,
name); name);
/* Ensure R_COPY works for weak symbol aliases */ /* Ensure R_COPY works for weak symbol aliases */
if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) { if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) {
for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) { for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) {
@ -2177,13 +2121,10 @@ static void bind_exe_dynsyms(TCCState *s1, int is_PIE)
} }
} }
} }
put_elf_reloc(s1->dynsym, bss_section, put_elf_reloc(s1->dynsym, bss_section,
offset, R_COPY, index); offset, R_COPY, index);
offset += esym->st_size;
bss_section->data_offset = offset;
} }
} else { } else if (sym->st_shndx == SHN_UNDEF) {
/* STB_WEAK undefined symbols are accepted */ /* STB_WEAK undefined symbols are accepted */
/* XXX: _fp_hw seems to be part of the ABI, so we ignore it */ /* XXX: _fp_hw seems to be part of the ABI, so we ignore it */
if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK || if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK ||
@ -2194,6 +2135,8 @@ static void bind_exe_dynsyms(TCCState *s1, int is_PIE)
} }
} }
} }
/* update _end symbol with new bss length */
set_linker_sym(s1, "_end", bss_section, 2);
} }
/* Bind symbols of libraries: export all non local symbols of executable that /* Bind symbols of libraries: export all non local symbols of executable that
@ -3179,7 +3122,6 @@ static int elf_output_file(TCCState *s1, const char *filename)
sec_order = tcc_malloc(sizeof(int) * 2 * s1->nb_sections); sec_order = tcc_malloc(sizeof(int) * 2 * s1->nb_sections);
/* compute section to program header mapping */ /* compute section to program header mapping */
layout_sections(s1, sec_order, &dyninf); layout_sections(s1, sec_order, &dyninf);
finalize_linker_symbols(s1, sec_order);
if (dynamic) { if (dynamic) {
/* put in GOT the dynamic section address and relocate PLT */ /* put in GOT the dynamic section address and relocate PLT */
@ -3627,7 +3569,7 @@ invalid:
} }
} }
done: done:
ret = 0; ret = !s1->nb_errors - 1; /* errors possibly from set_elf_sym() */
the_end: the_end:
tcc_free(symtab); tcc_free(symtab);
tcc_free(strtab); tcc_free(strtab);

View File

@ -3966,7 +3966,9 @@ redo:
case TOK_SECTION2: case TOK_SECTION2:
skip('('); skip('(');
astr = parse_mult_str("section name")->data; astr = parse_mult_str("section name")->data;
n = tcc_state->nb_sections;
ad->section = find_section(tcc_state, astr); ad->section = find_section(tcc_state, astr);
ad->new_section = n < tcc_state->nb_sections;
skip(')'); skip(')');
break; break;
case TOK_ALIAS1: case TOK_ALIAS1:
@ -8393,17 +8395,21 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
vset(type, r, addr); vset(type, r, addr);
} }
} else { } else {
CType *tp = type;
int is_const;
while ((tp->t & (VT_BTYPE|VT_ARRAY)) == (VT_PTR|VT_ARRAY))
tp = &tp->ref->type;
is_const = tp->t & VT_CONSTANT;
/* allocate symbol in corresponding section */ /* allocate symbol in corresponding section */
sec = ad->section; sec = ad->section;
if (!sec) { if (!sec) {
CType *tp = type;
while ((tp->t & (VT_BTYPE|VT_ARRAY)) == (VT_PTR|VT_ARRAY))
tp = &tp->ref->type;
if (type->t & VT_TLS) { if (type->t & VT_TLS) {
sec = find_section(tcc_state, has_init ? ".tdata" : ".tbss"); sec = find_section(tcc_state, has_init ? ".tdata" : ".tbss");
sec->sh_flags = SHF_ALLOC | SHF_WRITE | SHF_TLS; sec->sh_flags = SHF_ALLOC | SHF_WRITE | SHF_TLS;
sec->sh_type = has_init ? SHT_PROGBITS : SHT_NOBITS; sec->sh_type = has_init ? SHT_PROGBITS : SHT_NOBITS;
} else if (tp->t & VT_CONSTANT) { } else if (is_const) {
sec = rodata_section; sec = rodata_section;
} else if (has_init) { } else if (has_init) {
sec = data_section; sec = data_section;
@ -8411,15 +8417,21 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
tcc_warning("rw data: %s", get_tok_str(v, 0));*/ tcc_warning("rw data: %s", get_tok_str(v, 0));*/
} else if (tcc_state->nocommon) } else if (tcc_state->nocommon)
sec = bss_section; sec = bss_section;
} else if (ad->new_section) {
/* XXX: DWIM logic: set section flags according to first usage */
if (!is_const) {
sec->sh_flags |= SHF_WRITE;
if (!has_init)
sec->sh_type = SHT_NOBITS;
}
} }
#ifdef CONFIG_TCC_BCHECK
if (bcheck)
size = size + 1; /* add padding */
#endif
if (sec) { if (sec) {
addr = section_add(sec, size, align); addr = section_add(sec, size, align);
#ifdef CONFIG_TCC_BCHECK
/* add padding if bound check */
if (bcheck)
section_add(sec, 1, 1);
#endif
} else { } else {
addr = align; /* SHN_COMMON is special, symbol value is align */ addr = align; /* SHN_COMMON is special, symbol value is align */
sec = common_section; sec = common_section;
@ -8449,7 +8461,7 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
/* then add global bound info */ /* then add global bound info */
bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t)); bounds_ptr = section_ptr_add(bounds_section, 2 * sizeof(addr_t));
bounds_ptr[0] = 0; /* relocated */ bounds_ptr[0] = 0; /* relocated */
bounds_ptr[1] = size; bounds_ptr[1] = size - 1;
} }
#endif #endif
} }
@ -8871,11 +8883,12 @@ static int decl(int l)
skip_or_save_block(&fn->func_str); skip_or_save_block(&fn->func_str);
} else { } else {
/* compute text section */ /* compute text section */
cur_text_section = ad.section; cur_text_section = text_section;
if (!cur_text_section) if (ad.section) {
cur_text_section = text_section; cur_text_section = ad.section;
else if (cur_text_section->sh_num > bss_section->sh_num) if (ad.new_section)
cur_text_section->sh_flags = text_section->sh_flags; ad.section->sh_flags = text_section->sh_flags;
}
gen_function(sym); gen_function(sym);
} }
break; break;

View File

@ -5,6 +5,7 @@
#include <string.h> #include <string.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <sys/time.h>
#include <unistd.h> #include <unistd.h>
#include <stdlib.h> #include <stdlib.h>
#include <errno.h> #include <errno.h>
@ -83,6 +84,17 @@ static void child_handler(int sig)
sem_post (&sem_child); sem_post (&sem_child);
} }
static unsigned getclock_ms(void)
{
#ifdef _WIN32
return GetTickCount();
#else
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec*1000 + (tv.tv_usec+500)/1000;
#endif
}
int int
main (void) main (void)
{ {
@ -112,8 +124,8 @@ main (void)
#endif #endif
/* sleep does not work !!! */ /* sleep does not work !!! */
end = time(NULL) + 2; end = getclock_ms() + 200;
while (time(NULL) < end) ; while (getclock_ms() < end) ;
run = 0; run = 0;
pthread_join(id1, NULL); pthread_join(id1, NULL);

View File

@ -6,7 +6,7 @@
#include <stdatomic.h> #include <stdatomic.h>
#define NR_THREADS 16 #define NR_THREADS 16
#define NR_STEPS ((uint32_t)UINT16_MAX) #define NR_STEPS 10000
#define BUG_ON(COND) \ #define BUG_ON(COND) \
do { \ do { \

View File

@ -1,2 +0,0 @@
__thread signed short extern_tls_short = 1234;
__thread float extern_tls_float = 1.5f;

View File

@ -1,7 +1,10 @@
#include <stdio.h> #ifdef DEFS
__thread signed short extern_tls_short = 1234;
__thread float extern_tls_float = 1.5f;
#else
extern __thread signed short extern_tls_short; extern __thread signed short extern_tls_short;
extern __thread float extern_tls_float; extern __thread float extern_tls_float;
int printf(const char *, ...);
int main(void) int main(void)
{ {
@ -20,3 +23,4 @@ int main(void)
printf("extern tls errors: %d\n", errors); printf("extern tls errors: %d\n", errors);
return errors; return errors;
} }
#endif

View File

@ -1,13 +0,0 @@
extern char etext[], _etext[];
extern char edata[], _edata[];
extern char end[], _end[];
int check_linker_symbols(void)
{
int errors = 0;
errors += etext != _etext;
errors += edata != _edata;
errors += end != _end;
return errors;
}

View File

@ -1,13 +0,0 @@
#include <stdio.h>
int etext(void) { return 1; }
int edata(void) { return 2; }
int end(void) { return 3; }
int main(void)
{
int errors = etext() + edata() + end() != 6;
printf("user definitions errors: %d\n", errors);
return errors;
}

View File

@ -1,11 +1,153 @@
#include <stdio.h> int printf(const char *, ...);
#define assert(x) ((x) \
? (printf(" 1 (%s)\n",#x),1) \
: (printf("%s:%d: error: (%s)\n",__FILE__,__LINE__,#x),0))
#define O 100
#define SO 200
/* ------------------------------------------------------ */
#if TEST-O == 1
extern char etext[], _etext[];
extern char edata[], _edata[];
extern char end[], _end[];
int check_linker_symbols(void)
{
int errors = 0;
errors += !assert(etext == _etext);
errors += !assert(edata == _edata);
errors += !assert(end == _end);
return errors;
}
#elif TEST-MAIN == 1
int check_linker_symbols(void); int check_linker_symbols(void);
int main(void)
{
printf("linker symbols test\n");
assert(check_linker_symbols() == 0);
}
/* ------------------------------------------------------ */
#elif TEST-SO == 2
extern char etext[], edata[], end[];
void get_dso_end(char **p)
{
p[0] = etext;
p[1] = edata;
p[2] = end;
}
#elif TEST-MAIN == 2
extern char _etext[], _edata[], _end[];
extern void get_dso_end(char **);
static int fill_data = 123;
static int fill_bss;
int main(void) int main(void)
{ {
int errors = check_linker_symbols(); char *p[3];
printf("DSO linker symbols test\n");
printf("linker symbols errors: %d\n", errors); get_dso_end(p);
return errors; assert(p[0] == _etext) || printf("-- %p %p\n", p[0], _etext);
assert(p[1] == _edata) || printf("-- %p %p\n", p[1], _edata);
assert(p[2] == _end) || printf("-- %p %p\n", p[2], _end);
} }
/* ------------------------------------------------------ */
#elif TEST-MAIN == 3
int etext(void) { return 1; }
int edata(void) { return 2; }
int end(void) { return 3; }
int main(void)
{
printf("user definition test\n");
assert(etext() + edata() + end() == 6);
}
/* ------------------------------------------------------ */
#elif TEST-SO == 4
unsigned char copy_reloc_data[257];
#elif TEST-MAIN == 4
extern unsigned char copy_reloc_data[257];
extern char end[], _end[];
int main(void)
{
unsigned long data_end;
printf("end copy relocation test\n");
copy_reloc_data[256] = 42;
data_end = (unsigned long)(copy_reloc_data + sizeof copy_reloc_data);
assert(end == _end);
assert((unsigned long)end >= data_end);
assert(copy_reloc_data[256] == 42);
}
/* ------------------------------------------------------ */
#elif TEST-O == 5
void __attribute__((section(".text.linker_test"), noinline, used))
linker_test_text(void)
{
}
__attribute__((section(".bss.linker_test"), used))
unsigned char linker_test_bss[257];
__attribute__((section(".data.linker_test"), used))
unsigned char linker_test_data[23] = { 1 };
#elif TEST-MAIN == 5
extern void linker_test_text(void);
extern unsigned char linker_test_data[23];
extern unsigned char linker_test_bss[257];
extern char etext[], _etext[];
extern char edata[], _edata[];
extern char end[], _end[];
int main(void)
{
char* text_addr = (void*)linker_test_text;
char* data_end = linker_test_data + sizeof linker_test_data;
char* bss_end = linker_test_bss + sizeof linker_test_bss;
printf("linker boundary test\n");
linker_test_data[22] = 42;
linker_test_bss[256] = 42;
assert(linker_test_bss > linker_test_data);
assert(etext == _etext);
assert(edata == _edata);
assert(end == _end);
assert(etext >= text_addr);
assert(edata >= data_end);
assert(end >= bss_end);
assert(linker_test_data[22] == 42);
assert(linker_test_bss[256] == 42);
}
/* ------------------------------------------------------ */
#elif TEST-SO == 6
int end;
int eight = 8;
int nine;
int dso_add() { return end + eight + nine; }
#elif TEST-MAIN == 6
int end = 7;
int eight;
int nine = 9;
int dso_add();
int main()
{
int sum;
printf("DSO initialized data test\n");
sum = dso_add();
assert(end == 7);
assert(eight == 8);
assert(nine == 9);
assert(sum == 24) || printf("-- %d + %d + %d = %d\n", end, eight, nine, sum);
}
/* ------------------------------------------------------ */
#endif

View File

@ -1,2 +1,42 @@
linker symbols errors: 0
user definitions errors: 0 --- TEST 1 ---
linker symbols test
1 (etext == _etext)
1 (edata == _edata)
1 (end == _end)
1 (check_linker_symbols() == 0)
--- TEST 2 ---
DSO linker symbols test
1 (p[0] == _etext)
1 (p[1] == _edata)
1 (p[2] == _end)
--- TEST 3 ---
user definition test
1 (etext() + edata() + end() == 6)
--- TEST 4 ---
end copy relocation test
1 (end == _end)
1 ((unsigned long)end >= data_end)
1 (copy_reloc_data[256] == 42)
--- TEST 5 ---
linker boundary test
1 (linker_test_bss > linker_test_data)
1 (etext == _etext)
1 (edata == _edata)
1 (end == _end)
1 (etext >= text_addr)
1 (edata >= data_end)
1 (end >= bss_end)
1 (linker_test_data[22] == 42)
1 (linker_test_bss[256] == 42)
--- TEST 6 ---
DSO initialized data test
1 (end == 7)
1 (eight == 8)
1 (nine == 9)
1 (sum == 24)

View File

@ -1 +0,0 @@
unsigned char copy_reloc_data[257];

View File

@ -1,18 +0,0 @@
#include <stdio.h>
extern unsigned char copy_reloc_data[257];
int end(void) { return 42; }
int _end(void) { return 24; }
int main(void)
{
int errors;
copy_reloc_data[256] = 42;
errors = end() != 42;
errors += _end() != 24;
errors += copy_reloc_data[256] != 42;
printf("user end copy relocation errors: %d\n", errors);
return errors;
}

View File

@ -1,18 +0,0 @@
#include <stdio.h>
extern unsigned char copy_reloc_data[257];
extern char end[], _end[];
int main(void)
{
unsigned long data_end;
int errors = 0;
copy_reloc_data[256] = 42;
data_end = (unsigned long)(copy_reloc_data + sizeof copy_reloc_data);
errors += end != _end;
errors += (unsigned long)end < data_end;
errors += copy_reloc_data[256] != 42;
printf("end copy relocation errors: %d\n", errors);
return errors;
}

View File

@ -1,2 +0,0 @@
end copy relocation errors: 0
user end copy relocation errors: 0

View File

@ -1,14 +0,0 @@
void __attribute__((section(".text.linker_test"), noinline, used))
linker_test_text(void)
{
}
unsigned char linker_test_data[23]
__attribute__((section(".data.linker_test"), used)) = { 1 };
unsigned char linker_test_bss[257]
__attribute__((section(".bss.linker_test"), used));
/* set_global_sym replaces BSS/common definitions of these symbols. */
unsigned char _etext;
unsigned char _edata;

View File

@ -1,31 +0,0 @@
#include <stdio.h>
extern void linker_test_text(void);
extern unsigned char linker_test_data[23];
extern unsigned char linker_test_bss[257];
extern char etext[], _etext[];
extern char edata[], _edata[];
extern char end[], _end[];
int main(void)
{
unsigned long text_addr = (unsigned long)linker_test_text;
unsigned long data_end = (unsigned long)(linker_test_data
+ sizeof linker_test_data);
unsigned long bss_end = (unsigned long)(linker_test_bss
+ sizeof linker_test_bss);
int errors = 0;
linker_test_data[22] = 42;
linker_test_bss[256] = 42;
errors += etext != _etext;
errors += edata != _edata;
errors += end != _end;
errors += (unsigned long)etext <= text_addr;
errors += (unsigned long)edata < data_end;
errors += (unsigned long)end < bss_end;
errors += linker_test_data[22] != 42;
errors += linker_test_bss[256] != 42;
printf("linker boundary errors: %d\n", errors);
return errors;
}

View File

@ -1 +0,0 @@
linker boundary errors: 0

View File

@ -1,6 +0,0 @@
char end = 7;
char *get_dso_end(void)
{
return &end;
}

View File

@ -1,12 +0,0 @@
#include <stdio.h>
extern char _end[];
extern char *get_dso_end(void);
int main(void)
{
int errors = get_dso_end() != _end;
printf("DSO linker symbol errors: %d\n", errors);
return errors;
}

View File

@ -1 +0,0 @@
DSO linker symbol errors: 0

View File

@ -1,17 +0,0 @@
#include <stdint.h>
#include <stdio.h>
static unsigned short data[844];
static uint64_t address_for_index(int index)
{
return (uint64_t)(uintptr_t)data - 845186ULL
+ (uint64_t)index * 2;
}
int main(void)
{
printf("%d\n", address_for_index(422593)
== (uint64_t)(uintptr_t)data);
return 0;
}

View File

@ -1 +0,0 @@
1

View File

@ -35,7 +35,7 @@ ifeq ($(CONFIG_bcheck),no)
endif endif
ifeq ($(CONFIG_dll),no) ifeq ($(CONFIG_dll),no)
SKIP += 113_btdll.test # no shared lib support yet SKIP += 113_btdll.test # no shared lib support yet
SKIP += 149_end_copy_reloc.test 151_dso_linker_symbol.test SKIP += 148_linker_symbols.test
endif endif
ifeq (-$(findstring gcc,$(CC))-,--) ifeq (-$(findstring gcc,$(CC))-,--)
SKIP += $(patsubst %.expect,%.test,$(GEN-ALWAYS)) SKIP += $(patsubst %.expect,%.test,$(GEN-ALWAYS))
@ -47,8 +47,7 @@ ifeq (-$(CONFIG_WIN32)-,-yes-)
SKIP += 106_versym.test # No pthread support SKIP += 106_versym.test # No pthread support
SKIP += 114_bound_signal.test # No pthread support SKIP += 114_bound_signal.test # No pthread support
SKIP += 117_builtins.test # win32 port doesn't define __builtins SKIP += 117_builtins.test # win32 port doesn't define __builtins
SKIP += 148_linker_symbols.test 149_end_copy_reloc.test # ELF linker tests SKIP += 148_linker_symbols.test # ELF linker tests
SKIP += 150_linker_boundaries.test 151_dso_linker_symbol.test
endif endif
ifneq (,$(filter OpenBSD FreeBSD NetBSD DragonFly,$(TARGETOS))) ifneq (,$(filter OpenBSD FreeBSD NetBSD DragonFly,$(TARGETOS)))
SKIP += 106_versym.test # no pthread_condattr_setpshared SKIP += 106_versym.test # no pthread_condattr_setpshared
@ -58,11 +57,10 @@ ifneq (,$(filter OpenBSD FreeBSD NetBSD DragonFly,$(TARGETOS)))
endif endif
ifeq ($(CONFIG_OSX),yes) ifeq ($(CONFIG_OSX),yes)
SKIP += 144_tls.test 146_tls_extern.test # TLS runtime not supported on Mach-O SKIP += 144_tls.test 146_tls_extern.test # TLS runtime not supported on Mach-O
SKIP += 148_linker_symbols.test 149_end_copy_reloc.test # ELF linker tests SKIP += 148_linker_symbols.test # ELF linker tests
SKIP += 150_linker_boundaries.test 151_dso_linker_symbol.test
endif endif
ifeq ($(CONFIG_pie),yes) ifeq ($(CONFIG_pie),yes)
SKIP += 149_end_copy_reloc.test # PIE executables do not use copy relocations SKIP += 148_linker_symbols.test # PIE executables do not use copy relocations
endif endif
ifeq (-$(ARCH)-,-arm-) ifeq (-$(ARCH)-,-arm-)
SKIP += 144_tls.test 146_tls_extern.test # doesn't work currently SKIP += 144_tls.test 146_tls_extern.test # doesn't work currently
@ -124,62 +122,6 @@ GEN-ALWAYS =
# constructor/destructor # constructor/destructor
108_constructor.test: NORUN = true 108_constructor.test: NORUN = true
# TLS needs executable and pthread, not -run
144_tls.test: FLAGS += -pthread
144_tls.test: NORUN = true
# Verify that undefined TLS symbols in TCC objects work with the system linker.
146_tls_extern.test: NORUN = true
146_tls_extern.test: T1 = \
$(TCC) -c $1 -o 146_tls_extern-main.o && \
$(TCC) -c $(subst 146,146+,$1) -o 146_tls_extern-def.o && \
$(CC) -no-pie -Wl,-z,noexecstack 146_tls_extern-main.o \
146_tls_extern-def.o -o $(basename $@).exe && \
./$(basename $@).exe
ifeq (-$(CONFIG_WIN32)-,-yes-)
146_tls_extern.test : CC=$(TCC)
endif
# Check optional ELF linker symbols, including references from an archive.
148_linker_symbols.test: NORUN = true
148_linker_symbols.test: T1 = ( \
$(TCC) -c $(subst 148,148+,$1) -o 148_linker_symbols.o && \
$(AR) rcs 148_linker_symbols.a 148_linker_symbols.o && \
$(TCC) $1 148_linker_symbols.a -o $(basename $@).exe && \
./$(basename $@).exe && \
$(TCC) $(subst 148,148+override,$1) -o 148_linker_symbols-override.exe && \
./148_linker_symbols-override.exe \
)
# Copy relocations must be included in the final end symbols.
149_end_copy_reloc.test: NORUN = true
149_end_copy_reloc.test: T1 = ( \
$(TCC) -shared $(subst 149,149+,$1) -o 149_end_copy_reloc$(DLLSUF) && \
$(TCC) $1 ./149_end_copy_reloc$(DLLSUF) -Wl,-rpath=. \
-o $(basename $@).exe && \
./$(basename $@).exe && \
$(TCC) $(subst 149,149+override,$1) ./149_end_copy_reloc$(DLLSUF) \
-Wl,-rpath=. -o 149_end_copy_reloc-override.exe && \
./149_end_copy_reloc-override.exe \
)
# Boundary symbols must cover separately named sections from external objects.
150_linker_boundaries.test: NORUN = true
150_linker_boundaries.test: T1 = ( \
$(CC) -fcommon -fno-pic -fno-pie -c $(subst 150,150+,$1) \
-o 150_linker_boundaries.o && \
$(TCC) $1 150_linker_boundaries.o -o $(basename $@).exe && \
./$(basename $@).exe \
)
# A linker symbol needed only by a DSO must be provided by the executable.
151_dso_linker_symbol.test: NORUN = true
151_dso_linker_symbol.test: T1 = ( \
$(TCC) -shared $(subst 151,151+,$1) -o 151_dso_linker_symbol$(DLLSUF) && \
$(TCC) $1 ./151_dso_linker_symbol$(DLLSUF) -Wl,-rpath=. \
-o $(basename $@).exe && \
./$(basename $@).exe \
)
112_backtrace.test: FLAGS += -dt -b 112_backtrace.test: FLAGS += -dt -b
112_backtrace.test 113_btdll.test 126_bound_global.test: FILTER += \ 112_backtrace.test 113_btdll.test 126_bound_global.test: FILTER += \
-e 's;[0-9A-Fa-fx]\{5,\};........;g' \ -e 's;[0-9A-Fa-fx]\{5,\};........;g' \
@ -189,9 +131,8 @@ endif
113_btdll.test: T1 = \ 113_btdll.test: T1 = \
$(TCC) -bt $1 -shared -D DLL=1 -o a1$(DLLSUF) && \ $(TCC) -bt $1 -shared -D DLL=1 -o a1$(DLLSUF) && \
$(TCC) -bt $1 -shared -D DLL=2 -o a2$(DLLSUF) && \ $(TCC) -bt $1 -shared -D DLL=2 -o a2$(DLLSUF) && \
$(TCC) -bt $1 a1$(DLLSUF) a2$(DLLSUF) -Wl,-rpath=. -o $(basename $@).exe && \ $(TCC) -bt $1 a1$(DLLSUF) a2$(DLLSUF) -Wl,-rpath=. -o $*.exe && \
./$(basename $@).exe ./$*.exe
114_bound_signal.test: FLAGS += -b 114_bound_signal.test: FLAGS += -b
114_bound_signal.test: NORUN = true # tcc -run does not support fork and -b and SELINUX 114_bound_signal.test: NORUN = true # tcc -run does not support fork and -b and SELINUX
@ -211,6 +152,32 @@ endif
140_arm64_extasm.test: GEN = $(GEN-TCC) 140_arm64_extasm.test: GEN = $(GEN-TCC)
141_riscv_asm.test: GEN = $(GEN-TCC) 141_riscv_asm.test: GEN = $(GEN-TCC)
# TLS needs executable and pthread, not -run
144_tls.test: FLAGS += -pthread
144_tls.test: NORUN = true
# Verify that undefined TLS symbols in TCC objects work with the system linker.
# (note: '$*' is '146_tls_extern' here)
146_tls_extern.test: T1 = \
$(TCC) -c $1 -o $*-main.o && \
$(TCC) -c $1 -DDEFS -o $*-defs.o && \
$(CC) -no-pie -Wl,-z,noexecstack $*-main.o $*-defs.o -o $*.exe && \
./$*.exe
ifeq (-$(CONFIG_WIN32)-,-yes-)
146_tls_extern.test : CC=$(TCC)
endif
# Check ELF linker symbols
148_linker_symbols.test: T1 = \
for T in 1 2 3 4 5 6; do \
echo; echo "--- TEST $$T ---"; F=$*-$$T; \
$(TCC) $1 -DTEST=$$T+100 -c -fcommon -o $$F.o && \
$(TCC) $1 -DTEST=$$T+200 -shared -o $$F.so -fPIC && \
$(TCC) $1 -DTEST=$$T -Wl,-rpath=. -o $$F.exe $$F.o $$F.so -no-pie && \
./$$F.exe; \
done
148_linker_symbols.test: GEN=$(T1)
# Filter source directory in warnings/errors (out-of-tree builds) # Filter source directory in warnings/errors (out-of-tree builds)
FILTER = 2>&1 | sed -e 's,$(SRC)/,,g' FILTER = 2>&1 | sed -e 's,$(SRC)/,,g'
@ -222,7 +189,7 @@ all test tests2.all: $(filter-out $(SKIP),$(TESTS))
@$(call T1,$<) $(T3) @$(call T1,$<) $(T3)
T1 = $(TCC) $(FLAGS) $(T2) $(ARGS) T1 = $(TCC) $(FLAGS) $(T2) $(ARGS)
T2 = $(if $(NORUN),$1 -o $(basename $@).exe && ./$(basename $@).exe,-run $1) T2 = $(if $(NORUN),$1 -o $*.exe && ./$*.exe,-run $1)
T3 = $(FILTER) >$*.output 2>&1 || true \ T3 = $(FILTER) >$*.output 2>&1 || true \
&& diff -Nbu $(filter %.expect,$^) $*.output \ && diff -Nbu $(filter %.expect,$^) $*.output \
&& rm -f $*.output $(filter $*.expect,$(GEN-ALWAYS)) && rm -f $*.output $(filter $*.expect,$(GEN-ALWAYS))
@ -250,9 +217,9 @@ F2 = $1 UPDATE="$(patsubst %.test,%.expect,$1)"
# using TCC for .expect if -dt in FLAGS # using TCC for .expect if -dt in FLAGS
GEN = $(if $(filter -dt -bt -b,$(FLAGS)),$(GEN-TCC),$(GEN-CC)) GEN = $(if $(filter -dt -bt -b,$(FLAGS)),$(GEN-TCC),$(GEN-CC))
GEN-CC = $(CC) -w -std=gnu99 $(FLAGS) $1 -o $(basename $@).exe && ./$(basename $@).exe $(ARGS) GEN-CC = $(CC) -w -std=gnu99 $(FLAGS) $1 -o $*.exe && ./$*.exe $(ARGS)
GEN-TCC = $(T1) GEN-TCC = $(T1)
GEN-MSC = $(MS-CC) $1 && ./$(basename $@).exe GEN-MSC = $(MS-CC) $1 && ./$*.exe
MS-CC = cl MS-CC = cl
# tell make not to delete # tell make not to delete