diff --git a/tcc.h b/tcc.h index 7b4c2371..15333b3d 100644 --- a/tcc.h +++ b/tcc.h @@ -722,6 +722,7 @@ struct sym_attr { unsigned plt_offset; int plt_sym; int dyn_index; + unsigned char linker_sym:1; #ifdef TCC_TARGET_ARM unsigned char plt_thumb_stub:1; #endif diff --git a/tccelf.c b/tccelf.c index 7e9758af..4837936d 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1560,6 +1560,90 @@ 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); } +#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 + || ((ElfW(Sym) *)symtab_section->data)[sym_index].st_shndx != SHN_UNDEF) + 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) { Section *s; @@ -1884,9 +1968,19 @@ static void tcc_add_linker_symbols(TCCState *s1) int i; Section *s; +#ifndef ELF_OBJ_ONLY + set_linker_sym(s1, "_etext", text_section, -1); + set_linker_sym(s1, "_edata", data_section, -1); + 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 set_global_sym(s1, "__executable_start", NULL, ELF_START_ADDR); #endif @@ -3081,6 +3175,7 @@ static int elf_output_file(TCCState *s1, const char *filename) sec_order = tcc_malloc(sizeof(int) * 2 * s1->nb_sections); /* compute section to program header mapping */ layout_sections(s1, sec_order, &dyninf); + finalize_linker_symbols(s1, sec_order); if (dynamic) { /* put in GOT the dynamic section address and relocate PLT */ diff --git a/tests/tests2/148+_linker_symbols.c b/tests/tests2/148+_linker_symbols.c new file mode 100644 index 00000000..bf82d31e --- /dev/null +++ b/tests/tests2/148+_linker_symbols.c @@ -0,0 +1,13 @@ +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; +} diff --git a/tests/tests2/148+override_linker_symbols.c b/tests/tests2/148+override_linker_symbols.c new file mode 100644 index 00000000..cd96e74f --- /dev/null +++ b/tests/tests2/148+override_linker_symbols.c @@ -0,0 +1,13 @@ +#include + +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; +} diff --git a/tests/tests2/148_linker_symbols.c b/tests/tests2/148_linker_symbols.c new file mode 100644 index 00000000..0788f8db --- /dev/null +++ b/tests/tests2/148_linker_symbols.c @@ -0,0 +1,11 @@ +#include + +int check_linker_symbols(void); + +int main(void) +{ + int errors = check_linker_symbols(); + + printf("linker symbols errors: %d\n", errors); + return errors; +} diff --git a/tests/tests2/148_linker_symbols.expect b/tests/tests2/148_linker_symbols.expect new file mode 100644 index 00000000..ef7716df --- /dev/null +++ b/tests/tests2/148_linker_symbols.expect @@ -0,0 +1,2 @@ +linker symbols errors: 0 +user definitions errors: 0 diff --git a/tests/tests2/149+_end_copy_reloc.c b/tests/tests2/149+_end_copy_reloc.c new file mode 100644 index 00000000..9fd6c196 --- /dev/null +++ b/tests/tests2/149+_end_copy_reloc.c @@ -0,0 +1 @@ +unsigned char copy_reloc_data[257]; diff --git a/tests/tests2/149+override_end_copy_reloc.c b/tests/tests2/149+override_end_copy_reloc.c new file mode 100644 index 00000000..52560b1f --- /dev/null +++ b/tests/tests2/149+override_end_copy_reloc.c @@ -0,0 +1,18 @@ +#include + +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; +} diff --git a/tests/tests2/149_end_copy_reloc.c b/tests/tests2/149_end_copy_reloc.c new file mode 100644 index 00000000..4981b8ab --- /dev/null +++ b/tests/tests2/149_end_copy_reloc.c @@ -0,0 +1,18 @@ +#include + +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; +} diff --git a/tests/tests2/149_end_copy_reloc.expect b/tests/tests2/149_end_copy_reloc.expect new file mode 100644 index 00000000..675c2783 --- /dev/null +++ b/tests/tests2/149_end_copy_reloc.expect @@ -0,0 +1,2 @@ +end copy relocation errors: 0 +user end copy relocation errors: 0 diff --git a/tests/tests2/150+_linker_boundaries.c b/tests/tests2/150+_linker_boundaries.c new file mode 100644 index 00000000..1f6beacc --- /dev/null +++ b/tests/tests2/150+_linker_boundaries.c @@ -0,0 +1,14 @@ +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; diff --git a/tests/tests2/150_linker_boundaries.c b/tests/tests2/150_linker_boundaries.c new file mode 100644 index 00000000..f482f278 --- /dev/null +++ b/tests/tests2/150_linker_boundaries.c @@ -0,0 +1,31 @@ +#include + +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; +} diff --git a/tests/tests2/150_linker_boundaries.expect b/tests/tests2/150_linker_boundaries.expect new file mode 100644 index 00000000..feb3f6cc --- /dev/null +++ b/tests/tests2/150_linker_boundaries.expect @@ -0,0 +1 @@ +linker boundary errors: 0 diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 3e18000c..1c094d42 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -38,12 +38,13 @@ ifeq ($(CONFIG_bcheck),no) endif ifeq ($(CONFIG_dll),no) SKIP += 113_btdll.test 147_def_library.test # no shared lib support yet + SKIP += 149_end_copy_reloc.test endif ifeq (-$(findstring gcc,$(CC))-,--) SKIP += $(patsubst %.expect,%.test,$(GEN-ALWAYS)) endif ifeq ($(CC_NAME),tcc) - SKIP += 146_tls_extern.test # needs an external system linker + SKIP += 146_tls_extern.test 150_linker_boundaries.test # needs external tools endif ifeq (-$(CONFIG_WIN32)-$(CONFIG_i386)$(CONFIG_arm)-,--yes-) SKIP += 95_bitfields%.test # type_align is different on 32bit-non-windows @@ -54,6 +55,8 @@ ifeq (-$(CONFIG_WIN32)-,-yes-) SKIP += 117_builtins.test # win32 port doesn't define __builtins SKIP += 124_atomic_counter.test # No pthread support SKIP += 144_tls.test 146_tls_extern.test # TLS runtime not supported + SKIP += 148_linker_symbols.test 149_end_copy_reloc.test # ELF linker tests + SKIP += 150_linker_boundaries.test endif ifneq (,$(filter OpenBSD FreeBSD NetBSD,$(TARGETOS))) SKIP += 106_versym.test # no pthread_condattr_setpshared @@ -66,6 +69,11 @@ ifeq ($(TARGETOS),DragonFly) endif ifeq ($(CONFIG_OSX),yes) 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 += 150_linker_boundaries.test +endif +ifeq ($(CONFIG_pie),yes) + SKIP += 149_end_copy_reloc.test # PIE executables do not use copy relocations endif ifeq (-$(ARCH)-,-arm-) SKIP += 144_tls.test 146_tls_extern.test # doesn't work currently @@ -143,6 +151,38 @@ GEN-ALWAYS = 146_tls_extern-def.o -o $(basename $@).exe && \ ./$(basename $@).exe +# 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) -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 \ + ) + 112_backtrace.test: FLAGS += -dt -b 112_backtrace.test 113_btdll.test 126_bound_global.test: FILTER += \ -e 's;[0-9A-Fa-fx]\{5,\};........;g' \ @@ -233,4 +273,4 @@ $(sort $(GEN-ALWAYS) $(UPDATE)) : force force: clean : - rm -f fred.txt *.output *.o *.exe *.dll *.so *.def $(GEN-ALWAYS) + rm -f fred.txt *.output *.o *.a *.exe *.dll *.so *.def $(GEN-ALWAYS)