lazy binding

Currently tcc does not use lazy binding. It puts all relocations in the RELX
section and solve them all at startup.
This was not working on bsd.

tcc.h:
- New RELPLT_SECTION_FMT for plt relocations
- New entry relocplt in struct Section

tccelf.c:
- put_elf_reloca: put R_JMP_SLOT in relocplt section
- build_got_entries*: Use two passes because R_JMP_SLOT and R_GLOB_DAT
                      can not be intermixed on some targets (arm, arm64)
- layout_sections: Calculate correct size relocplt section for DT_ values.
                   Make sure relocplt is last
- fill_dynamic: Add DT_ values when got is filled
                move DT_VERSYM because dynamic linker cannot handle it standone
- Add note section for NetBSD

arm-link.c/arm64-link.c/i386-link.c/riscv64-link.c/x86_64-link.c:
- fill got table with pointer to plt section or symbol value in case
  of TCC_OUTPUT_MEMORY

arm-link.c/arm64-link.c:
- fix offset first plt entry

i386-link.c/x86_64-link.c:
- use correct reloc entry
- use relofs - sizeof (ElfW_Rel) because the reloc is already done

lib/bcheck.c:
- no __libc_freeres on FreeBSD and NetBSD

tests/Makefile:
- Add -fno-stack-protector for OpenBSD

tests/tests2/Makefile:
- disable 106_pthread/114_bound_signal
This commit is contained in:
herman ten brugge 2020-12-18 15:24:32 +01:00
parent e2e62fcb8b
commit 50b4f320dc
10 changed files with 162 additions and 39 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

3
tcc.h
View File

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

107
tccelf.c
View File

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

View File

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

View File

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

View File

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