diff --git a/arm-link.c b/arm-link.c index c75eeb70..207fc61a 100644 --- a/arm-link.c +++ b/arm-link.c @@ -145,7 +145,7 @@ ST_FUNC void relocate_plt(TCCState *s1) if (p < p_end) { int x = s1->got->sh_addr - s1->plt->sh_addr - 12; - write32le(s1->plt->data + 16, x - 16); + write32le(s1->plt->data + 16, x - 4); p += 20; while (p < p_end) { unsigned off = x + read32le(p + 4) + (s1->plt->data - p) + 4; @@ -157,6 +157,18 @@ ST_FUNC void relocate_plt(TCCState *s1) p += 12; } } + + if (s1->got->relocplt) { + int mem = s1->output_type == TCC_OUTPUT_MEMORY; + ElfW_Rel *rel; + + p = s1->got->data; + for_each_elem(s1->got->relocplt, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + write32le(p + rel->r_offset, mem ? sym->st_value : s1->plt->sh_addr); + } + } } #endif diff --git a/arm64-link.c b/arm64-link.c index e0d6c6e3..b9de4818 100644 --- a/arm64-link.c +++ b/arm64-link.c @@ -113,7 +113,7 @@ ST_FUNC void relocate_plt(TCCState *s1) if (p < p_end) { uint64_t plt = s1->plt->sh_addr; - uint64_t got = s1->got->sh_addr; + uint64_t got = s1->got->sh_addr + 16; uint64_t off = (got >> 12) - (plt >> 12); if ((off + ((uint32_t)1 << 20)) >> 21) tcc_error("Failed relocating PLT (off=0x%lx, got=0x%lx, plt=0x%lx)", (long)off, (long)got, (long)plt); @@ -129,6 +129,7 @@ ST_FUNC void relocate_plt(TCCState *s1) write32le(p + 24, 0xd503201f); // nop write32le(p + 28, 0xd503201f); // nop p += 32; + got = s1->got->sh_addr; while (p < p_end) { uint64_t pc = plt + (p - s1->plt->data); uint64_t addr = got + read64le(p); @@ -145,6 +146,18 @@ ST_FUNC void relocate_plt(TCCState *s1) p += 16; } } + + if (s1->got->relocplt) { + int mem = s1->output_type == TCC_OUTPUT_MEMORY; + ElfW_Rel *rel; + + p = s1->got->data; + for_each_elem(s1->got->relocplt, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + write64le(p + rel->r_offset, mem ? sym->st_value : s1->plt->sh_addr); + } + } } void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) diff --git a/i386-link.c b/i386-link.c index 04285696..c9414693 100644 --- a/i386-link.c +++ b/i386-link.c @@ -16,7 +16,7 @@ #define ELF_PAGE_SIZE 0x1000 #define PCRELATIVE_DLLPLT 0 -#define RELOCATE_DLLPLT 0 +#define RELOCATE_DLLPLT 1 #else /* !TARGET_DEFS_ONLY */ @@ -121,7 +121,7 @@ ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_ /* The PLT slot refers to the relocation entry it needs via offset. The reloc entry is created below, so its offset is the current data_offset */ - relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0; + relofs = s1->got->relocplt ? s1->got->relocplt->data_offset : 0; /* Jump to GOT entry where ld.so initially put the address of ip + 4 */ p = section_ptr_add(plt, 16); @@ -129,7 +129,7 @@ ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_ p[1] = modrm; write32le(p + 2, got_offset); p[6] = 0x68; /* push $xxx */ - write32le(p + 7, relofs); + write32le(p + 7, relofs - sizeof (ElfW_Rel)); p[11] = 0xe9; /* jmp plt_start */ write32le(p + 12, -(plt->data_offset)); return plt_offset; @@ -147,7 +147,7 @@ ST_FUNC void relocate_plt(TCCState *s1) p = s1->plt->data; p_end = p + s1->plt->data_offset; - if (p < p_end) { + if (s1->output_type != TCC_OUTPUT_DLL && p < p_end) { add32le(p + 2, s1->got->sh_addr); add32le(p + 8, s1->got->sh_addr); p += 16; @@ -156,6 +156,20 @@ ST_FUNC void relocate_plt(TCCState *s1) p += 16; } } + + if (s1->got->relocplt) { + int mem = s1->output_type == TCC_OUTPUT_MEMORY; + ElfW_Rel *rel; + int x = s1->plt->sh_addr + 16 + 6; + + p = s1->got->data; + for_each_elem(s1->got->relocplt, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + write32le(p + rel->r_offset, mem ? sym->st_value : x); + x += 16; + } + } } #endif diff --git a/lib/bcheck.c b/lib/bcheck.c index 114b6e49..a880a216 100644 --- a/lib/bcheck.c +++ b/lib/bcheck.c @@ -1153,7 +1153,8 @@ void __attribute__((destructor)) __bound_exit(void) dprintf(stderr, "%s, %s():\n", __FILE__, __FUNCTION__); if (inited) { -#if !defined(_WIN32) && !defined(__APPLE__) && !defined(__OpenBSD__) && !defined TCC_MUSL +#if !defined(_WIN32) && !defined(__APPLE__) && !defined TCC_MUSL && \ + !defined(__OpenBSD__) && !defined(__FreeBSD__) && !defined(__NetBSD__) if (print_heap) { extern void __libc_freeres (void); __libc_freeres (); diff --git a/riscv64-link.c b/riscv64-link.c index 02c5718a..dbc3ee44 100644 --- a/riscv64-link.c +++ b/riscv64-link.c @@ -153,6 +153,18 @@ ST_FUNC void relocate_plt(TCCState *s1) p += 16; } } + + if (s1->got->relocplt) { + int mem = s1->output_type == TCC_OUTPUT_MEMORY; + ElfW_Rel *rel; + + p = s1->got->data; + for_each_elem(s1->got->relocplt, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + write64le(p + rel->r_offset, mem ? sym->st_value + rel->r_addend : s1->plt->sh_addr); + } + } } void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, diff --git a/tcc.h b/tcc.h index ccd04ac1..a9a822ea 100644 --- a/tcc.h +++ b/tcc.h @@ -397,6 +397,7 @@ extern long double strtold (const char *__nptr, char **__endptr); # define ElfW_Rel ElfW(Rela) # define SHT_RELX SHT_RELA # define REL_SECTION_FMT ".rela%s" +# define RELPLT_SECTION_FMT ".rel.plt" #else # define ELFCLASSW ELFCLASS32 # define ElfW(type) Elf##32##_##type @@ -404,6 +405,7 @@ extern long double strtold (const char *__nptr, char **__endptr); # define ElfW_Rel ElfW(Rel) # define SHT_RELX SHT_REL # define REL_SECTION_FMT ".rel%s" +# define RELPLT_SECTION_FMT ".rel.plt" #endif /* target address type */ #define addr_t ElfW(Addr) @@ -565,6 +567,7 @@ typedef struct Section { struct Section *reloc; /* corresponding section for relocation, if any */ struct Section *hash; /* hash table for symbols */ struct Section *prev; /* previous section on section stack */ + struct Section *relocplt;/* reloc with JMP_SLOTs */ char name[1]; /* section name */ } Section; diff --git a/tccelf.c b/tccelf.c index f4fc2826..1b05b69e 100644 --- a/tccelf.c +++ b/tccelf.c @@ -735,18 +735,25 @@ ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, char buf[256]; Section *sr; ElfW_Rel *rel; + int jmp_slot = type == R_JMP_SLOT; - sr = s->reloc; + sr = jmp_slot ? s->relocplt : s->reloc; if (!sr) { /* if no relocation section, create it */ - snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name); + if (jmp_slot) + snprintf(buf, sizeof(buf), RELPLT_SECTION_FMT); + else + snprintf(buf, sizeof(buf), REL_SECTION_FMT, s->name); /* if the symtab is allocated, then we consider the relocation are also */ sr = new_section(s->s1, buf, SHT_RELX, symtab->sh_flags); sr->sh_entsize = sizeof(ElfW_Rel); sr->link = symtab; sr->sh_info = s->sh_num; - s->reloc = sr; + if (jmp_slot) + s->relocplt = sr; + else + s->reloc = sr; } rel = section_ptr_add(sr, sizeof(ElfW_Rel)); rel->r_offset = offset; @@ -1164,7 +1171,7 @@ static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type, } /* build GOT and PLT entries */ -ST_FUNC void build_got_entries(TCCState *s1) +static void build_got_entries_pass(TCCState *s1, int pass) { Section *s; ElfW_Rel *rel; @@ -1235,6 +1242,8 @@ ST_FUNC void build_got_entries(TCCState *s1) (ELFW(ST_VISIBILITY)(sym->st_other) != STV_DEFAULT || ELFW(ST_BIND)(sym->st_info) == STB_LOCAL || s1->output_type == TCC_OUTPUT_EXE)) { + if (pass == 0) + continue; rel->r_info = ELFW(R_INFO)(sym_index, R_X86_64_PC32); continue; } @@ -1248,6 +1257,11 @@ ST_FUNC void build_got_entries(TCCState *s1) } else reloc_type = R_GLOB_DAT; + + if ((pass == 0 && reloc_type == R_GLOB_DAT) || + (pass == 1 && reloc_type == R_JMP_SLOT)) + continue; + if (!s1->got) build_got(s1); @@ -1261,6 +1275,16 @@ ST_FUNC void build_got_entries(TCCState *s1) } } } + +ST_FUNC void build_got_entries(TCCState *s1) +{ + int i; + + /* Two passes because R_JMP_SLOT should become first. + Some targets (arm, arm64) do not allow mixing R_JMP_SLOT and R_GLOB_DAT. */ + for (i = 0; i < 2; i++) + build_got_entries_pass(s1, i); +} #endif ST_FUNC int set_global_sym(TCCState *s1, const char *name, Section *sec, addr_t offs) @@ -1801,7 +1825,7 @@ struct dyn_inf { unsigned long data_offset; addr_t rel_addr; addr_t rel_size; -#if TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel +#if PTR_SIZE == 4 && (TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel) addr_t bss_addr; addr_t bss_size; #endif @@ -1861,11 +1885,13 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum, /* dynamic relocation table information, for .dynamic section */ dyninf->rel_addr = dyninf->rel_size = 0; -#if TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel +#if PTR_SIZE == 4 && (TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel) dyninf->bss_addr = dyninf->bss_size = 0; #endif for(j = 0; j < (phnum == 6 ? 3 : 2); j++) { + Section *relocplt = s1->got ? s1->got->relocplt : NULL; + ph->p_type = j == 2 ? PT_TLS : PT_LOAD; if (j == 0) ph->p_flags = PF_R | PF_X; @@ -1878,7 +1904,7 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum, info about the layout. We do the following ordering: interp, symbol tables, relocations, progbits, nobits */ /* XXX: do faster and simpler sorting */ - for(k = 0; k < 5; k++) { + for(k = 0; k < 6; k++) { for(i = 1; i < s1->nb_sections; i++) { s = s1->sections[i]; /* compute if section should be included */ @@ -1905,13 +1931,15 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum, if (k != 1) continue; } else if (s->sh_type == SHT_RELX) { - if (k != 2) + if (k != 2 && s != relocplt) + continue; + else if (k != 3 && s == relocplt) continue; } else if (s->sh_type == SHT_NOBITS) { - if (k != 4) + if (k != 5) continue; } else { - if (k != 3) + if (k != 4) continue; } sec_order[sh_order_index++] = i; @@ -1931,8 +1959,8 @@ static int layout_sections(TCCState *s1, ElfW(Phdr) *phdr, int phnum, ph->p_paddr = ph->p_vaddr; } /* update dynamic relocation infos */ - if (s->sh_type == SHT_RELX) { -#if TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel + if (s->sh_type == SHT_RELX && s != relocplt) { +#if PTR_SIZE == 4 && (TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel) if (!strcmp(strsec->data + s->sh_name, ".rel.got")) { dyninf->rel_addr = addr; dyninf->rel_size += s->sh_size; /* XXX only first rel. */ @@ -2079,16 +2107,17 @@ static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf) put_dt(dynamic, DT_RELA, dyninf->rel_addr); put_dt(dynamic, DT_RELASZ, dyninf->rel_size); put_dt(dynamic, DT_RELAENT, sizeof(ElfW_Rel)); -#if TARGETOS_OpenBSD || TARGETOS_FreeBSD || TARGETOS_NetBSD - put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr); - put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size); - put_dt(dynamic, DT_JMPREL, dyninf->rel_addr); - put_dt(dynamic, DT_PLTREL, DT_RELA); - put_dt(dynamic, DT_BIND_NOW, 1); /* Dirty hack */ -#endif + if (s1->got && s1->got->relocplt) { + put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr); + put_dt(dynamic, DT_PLTRELSZ, s1->got->relocplt->data_offset); + put_dt(dynamic, DT_JMPREL, s1->got->relocplt->sh_addr); + put_dt(dynamic, DT_PLTREL, DT_RELA); + } + put_dt(dynamic, DT_RELACOUNT, 0); #else -#if TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel - put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr); +#if PTR_SIZE == 4 && (TARGETOS_FreeBSD || TARGETOS_FreeBSD_kernel) + if (s1->got) + put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr); put_dt(dynamic, DT_PLTRELSZ, dyninf->rel_size); put_dt(dynamic, DT_JMPREL, dyninf->rel_addr); put_dt(dynamic, DT_PLTREL, DT_REL); @@ -2098,11 +2127,18 @@ static void fill_dynamic(TCCState *s1, struct dyn_inf *dyninf) put_dt(dynamic, DT_REL, dyninf->rel_addr); put_dt(dynamic, DT_RELSZ, dyninf->rel_size); put_dt(dynamic, DT_RELENT, sizeof(ElfW_Rel)); + if (s1->got && s1->got->relocplt) { + put_dt(dynamic, DT_PLTGOT, s1->got->sh_addr); + put_dt(dynamic, DT_PLTRELSZ, s1->got->relocplt->data_offset); + put_dt(dynamic, DT_JMPREL, s1->got->relocplt->sh_addr); + put_dt(dynamic, DT_PLTREL, DT_REL); + } + put_dt(dynamic, DT_RELCOUNT, 0); #endif #endif - if (versym_section) + if (versym_section && verneed_section) { + /* The dynamic linker can not handle VERSYM without VERNEED */ put_dt(dynamic, DT_VERSYM, versym_section->sh_addr); - if (verneed_section) { put_dt(dynamic, DT_VERNEED, verneed_section->sh_addr); put_dt(dynamic, DT_VERNEEDNUM, dt_verneednum); } @@ -2406,10 +2442,12 @@ static void create_arm_attribute_section(TCCState *s1) } #endif -#if TARGETOS_OpenBSD -static Section *create_openbsd_note_section(TCCState *s1) +#if TARGETOS_OpenBSD || TARGETOS_NetBSD +static Section *create_bsd_note_section(TCCState *s1, + const char *name, + const char *value) { - Section *s = find_section (s1, ".note.openbsd.ident"); + Section *s = find_section (s1, name); if (s->data_offset == 0) { unsigned char *ptr = section_ptr_add(s, sizeof(ElfW(Nhdr)) + 8 + 4); @@ -2419,7 +2457,7 @@ static Section *create_openbsd_note_section(TCCState *s1) note->n_namesz = 8; note->n_descsz = 4; note->n_type = ELF_NOTE_OS_GNU; - strcpy (ptr + sizeof(ElfW(Nhdr)), "OpenBSD"); + strcpy (ptr + sizeof(ElfW(Nhdr)), value); } return s; } @@ -2441,7 +2479,11 @@ static int elf_output_file(TCCState *s1, const char *filename) #endif #if TARGETOS_OpenBSD if (file_type != TCC_OUTPUT_OBJ) - note = create_openbsd_note_section (s1); + note = create_bsd_note_section (s1, ".note.openbsd.ident", "OpenBSD"); +#endif +#if TARGETOS_NetBSD + if (file_type != TCC_OUTPUT_OBJ) + note = create_bsd_note_section (s1, ".note.netbsd.ident", "NetBSD"); #endif s1->nb_errors = 0; @@ -2814,8 +2856,13 @@ ST_FUNC int tcc_load_object_file(TCCState *s1, sm_table[i].new_section = 1; found: if (sh->sh_type != s->sh_type) { - tcc_error_noabort("invalid section type"); - goto fail; +#if TARGETOS_OpenBSD + if (strcmp (s->name, ".eh_frame") || sh->sh_type != SHT_PROGBITS) +#endif + { + tcc_error_noabort("invalid section type"); + goto fail; + } } /* align start of section */ s->data_offset += -s->data_offset & (sh->sh_addralign - 1); diff --git a/tests/Makefile b/tests/Makefile index 371bf628..8e516bf3 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -61,6 +61,9 @@ endif ifeq ($(CC_NAME),msvc) test.ref abitest : CC = gcc endif +ifeq ($(TARGETOS),OpenBSD) +dlltest: CFLAGS+=-fno-stack-protector +endif RUN_TCC = $(NATIVE_DEFINES) -run $(TOPSRC)/tcc.c $(TCCFLAGS) DISAS = objdump -d diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 751b17fc..49dc6ff6 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -48,6 +48,10 @@ ifeq (-$(CONFIG_WIN32)-,-yes-) SKIP += 106_pthread.test # No pthread support SKIP += 114_bound_signal.test # No pthread support endif +ifneq (,$(filter OpenBSD FreeBSD NetBSD,$(TARGETOS))) + SKIP += 106_pthread.test # no pthread_condattr_setpshared + SKIP += 114_bound_signal.test # libc problem signal/fork +endif # Some tests might need arguments ARGS = diff --git a/x86_64-link.c b/x86_64-link.c index 507ecb78..e072379d 100644 --- a/x86_64-link.c +++ b/x86_64-link.c @@ -131,7 +131,7 @@ ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_ /* The PLT slot refers to the relocation entry it needs via offset. The reloc entry is created below, so its offset is the current data_offset */ - relofs = s1->got->reloc ? s1->got->reloc->data_offset : 0; + relofs = s1->got->relocplt ? s1->got->relocplt->data_offset : 0; /* Jump to GOT entry where ld.so initially put the address of ip + 4 */ p = section_ptr_add(plt, 16); @@ -140,7 +140,7 @@ ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_ write32le(p + 2, got_offset); p[6] = 0x68; /* push $xxx */ /* On x86-64, the relocation is referred to by _index_ */ - write32le(p + 7, relofs / sizeof (ElfW_Rel)); + write32le(p + 7, relofs / sizeof (ElfW_Rel) - 1); p[11] = 0xe9; /* jmp plt_start */ write32le(p + 12, -(plt->data_offset)); return plt_offset; @@ -168,6 +168,20 @@ ST_FUNC void relocate_plt(TCCState *s1) p += 16; } } + + if (s1->got->relocplt) { + int mem = s1->output_type == TCC_OUTPUT_MEMORY; + ElfW_Rel *rel; + int x = s1->plt->sh_addr + 16 + 6; + + p = s1->got->data; + for_each_elem(s1->got->relocplt, 0, rel, ElfW_Rel) { + int sym_index = ELFW(R_SYM)(rel->r_info); + ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; + write64le(p + rel->r_offset, mem ? sym->st_value : x); + x += 16; + } + } } #endif #endif