diff --git a/i386-link.c b/i386-link.c index c0981727..c232f392 100644 --- a/i386-link.c +++ b/i386-link.c @@ -56,12 +56,17 @@ int gotplt_entry_type (int reloc_type) switch (reloc_type) { case R_386_RELATIVE: case R_386_16: - case R_386_32: case R_386_GLOB_DAT: case R_386_JMP_SLOT: case R_386_COPY: return NO_GOTPLT_ENTRY; + case R_386_32: + /* This relocations shouldn't normally need GOT or PLT + slots if it weren't for simplicity in the code generator. + See our caller for comments. */ + return AUTO_GOTPLT_ENTRY; + case R_386_PC16: case R_386_PC32: return AUTO_GOTPLT_ENTRY; diff --git a/tccelf.c b/tccelf.c index 003797c9..e2ba5a42 100644 --- a/tccelf.c +++ b/tccelf.c @@ -956,32 +956,39 @@ ST_FUNC void build_got_entries(TCCState *s1) sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; if (gotplt_entry == NO_GOTPLT_ENTRY) { -#ifdef TCC_TARGET_I386 - if (type == R_386_32 && sym->st_shndx == SHN_UNDEF) { - /* the i386 generator uses the plt address for function - pointers into .so. This may break pointer equality - but helps to keep it simple */ - char *name = (char *)symtab_section->link->data + sym->st_name; - int index = find_elf_sym(s1->dynsymtab_section, name); - ElfW(Sym) *esym = (ElfW(Sym) *)s1->dynsymtab_section->data + index; - if (index - && (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC - || (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE - && ELFW(ST_TYPE)(sym->st_info) == STT_FUNC))) - goto jmp_slot; - } -#endif continue; } - /* Automatically create PLT/GOT [entry] it is an undefined reference - (resolved at runtime), or the symbol is absolute, probably created - by tcc_add_symbol, and thus on 64-bit targets might be too far - from application code */ + /* Automatically create PLT/GOT [entry] if it is an undefined + reference (resolved at runtime), or the symbol is absolute, + probably created by tcc_add_symbol, and thus on 64-bit + targets might be too far from application code. */ if (gotplt_entry == AUTO_GOTPLT_ENTRY) { if (sym->st_shndx == SHN_UNDEF) { + ElfW(Sym) *esym; + int dynindex; if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT) continue; + /* Relocations for UNDEF symbols would normally need + to be transferred into the executable or shared object. + If that were done AUTO_GOTPLT_ENTRY wouldn't exist. + But TCC doesn't do that (at least for exes), so we + need to resolve all such relocs locally. And that + means PLT slots for functions in DLLs and COPY relocs for + data symbols. COPY relocs were generated in + bind_exe_dynsyms (and the symbol adjusted to be defined), + and for functions we were generated a dynamic symbol + of function type. */ + if (s1->dynsym) { + /* dynsym isn't set for -run :-/ */ + dynindex = get_sym_attr(s1, sym_index, 0)->dyn_index; + esym = (ElfW(Sym) *)s1->dynsym->data + dynindex; + if (dynindex + && (ELFW(ST_TYPE)(esym->st_info) == STT_FUNC + || (ELFW(ST_TYPE)(esym->st_info) == STT_NOTYPE + && ELFW(ST_TYPE)(sym->st_info) == STT_FUNC))) + goto jmp_slot; + } } else if (!(sym->st_shndx == SHN_ABS && PTR_SIZE == 8)) continue; } @@ -994,9 +1001,7 @@ ST_FUNC void build_got_entries(TCCState *s1) } #endif if (code_reloc(type)) { -#ifdef TCC_TARGET_I386 jmp_slot: -#endif reloc_type = R_JMP_SLOT; } else reloc_type = R_GLOB_DAT; @@ -1278,9 +1283,12 @@ static void bind_exe_dynsyms(TCCState *s1) * of the function wanted by the caller of dlsym instead of * the address of the function that would return that * address */ - put_elf_sym(s1->dynsym, 0, esym->st_size, - ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), 0, 0, - name); + int dynindex + = put_elf_sym(s1->dynsym, 0, esym->st_size, + ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC), 0, 0, + name); + int index = sym - (ElfW(Sym) *) symtab_section->data; + get_sym_attr(s1, index, 1)->dyn_index = dynindex; } else if (type == STT_OBJECT) { unsigned long offset; ElfW(Sym) *dynsym; diff --git a/tests/tests2/42_function_pointer.c b/tests/tests2/42_function_pointer.c index 49c331bf..697bd79a 100644 --- a/tests/tests2/42_function_pointer.c +++ b/tests/tests2/42_function_pointer.c @@ -8,9 +8,13 @@ int fred(int p) int (*f)(int) = &fred; +/* To test what this is supposed to test the destination function + (fprint here) must not be called directly anywhere in the test. */ +int (*fprintfptr)(FILE *, const char *, ...) = &fprintf; + int main() { - printf("%d\n", (*f)(24)); + fprintfptr(stdout, "%d\n", (*f)(24)); return 0; } diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 2434215a..166fb2a9 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -34,6 +34,10 @@ ARGS = 31_args.test : ARGS = arg1 arg2 arg3 arg4 arg5 46_grep.test : ARGS = '[^* ]*[:a:d: ]+\:\*-/: $$' $(SRC)/46_grep.c +# And some tests don't test the right thing with -run +NORUN = +42_function_pointer.test : NORUN = true + # Some tests might need different flags 76_dollars_in_identifiers.test : TCCFLAGS += -fdollars-in-identifiers @@ -49,7 +53,12 @@ all test: $(filter-out $(SKIP),$(TESTS)) %.test: %.c %.expect @echo Test: $*... # test -run - @$(TCC) -run $< $(ARGS) $(FILTER) >$*.output 2>&1 || true + @if test -z "$(NORUN)"; then \ + $(TCC) -run $< $(ARGS) $(FILTER) >$*.output 2>&1 || true; \ + else \ + $(TCC) $< -o ./$*.exe $(FILTER) 2>&1 && \ + ./$*.exe $(ARGS) >$*.output 2>&1 || true; \ + fi @diff -Nbu $(SRC)/$*.expect $*.output && rm -f $*.output # automatically generate .expect files with gcc: diff --git a/x86_64-link.c b/x86_64-link.c index 602a8ef0..27cad93f 100644 --- a/x86_64-link.c +++ b/x86_64-link.c @@ -61,6 +61,9 @@ int gotplt_entry_type (int reloc_type) case R_X86_64_RELATIVE: return NO_GOTPLT_ENTRY; + /* The following relocs wouldn't normally need GOT or PLT + slots, but we need them for simplicity in the link + editor part. See our caller for comments. */ case R_X86_64_32: case R_X86_64_32S: case R_X86_64_64: