diff --git a/arm-link.c b/arm-link.c index a92342a..5e9c698 100644 --- a/arm-link.c +++ b/arm-link.c @@ -14,8 +14,8 @@ #define ELF_START_ADDR 0x00008000 #define ELF_PAGE_SIZE 0x1000 -#define HAVE_SECTION_RELOC #define PCRELATIVE_DLLPLT 1 +#define RELOCATE_DLLPLT 0 enum float_abi { ARM_SOFTFP_FLOAT, @@ -101,6 +101,69 @@ int gotplt_entry_type (int reloc_type) return -1; } +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr) +{ + Section *plt = s1->plt; + uint8_t *p; + unsigned plt_offset; + + /* when building a DLL, GOT entry accesses must be done relative to + start of GOT (see x86_64 examble above) */ + if (s1->output_type == TCC_OUTPUT_DLL) + tcc_error("DLLs unimplemented!"); + + /* empty PLT: create PLT0 entry that push address of call site and + jump to ld.so resolution routine (GOT + 8) */ + if (plt->data_offset == 0) { + p = section_ptr_add(plt, 20); + write32le(p, 0xe52de004); /* push {lr} */ + write32le(p+4, 0xe59fe004); /* ldr lr, [pc, #4] */ + write32le(p+8, 0xe08fe00e); /* add lr, pc, lr */ + write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */ + /* p+16 is set in relocate_plt */ + } + plt_offset = plt->data_offset; + + if (attr->plt_thumb_stub) { + p = section_ptr_add(plt, 4); + write32le(p, 0x4778); /* bx pc */ + write32le(p+2, 0x46c0); /* nop */ + } + p = section_ptr_add(plt, 16); + /* Jump to GOT entry where ld.so initially put address of PLT0 */ + write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] */ + write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */ + write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */ + /* p + 12 contains offset to GOT entry once patched by relocate_plt */ + write32le(p+12, got_offset); + return plt_offset; +} + +/* relocate the PLT: compute addresses and offsets in the PLT now that final + address for PLT and GOT are known (see fill_program_header) */ +ST_FUNC void relocate_plt(TCCState *s1) +{ + uint8_t *p, *p_end; + + if (!s1->plt) + return; + + p = s1->plt->data; + p_end = p + s1->plt->data_offset; + + if (p < p_end) { + int x = s1->got->sh_addr - s1->plt->sh_addr - 12; + write32le(s1->plt->data + 16, x - 16); + p += 20; + while (p < p_end) { + if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */ + p += 4; + add32le(p + 12, x + s1->plt->data - p); + p += 16; + } + } +} + void relocate_init(Section *sr) {} void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val) diff --git a/arm64-link.c b/arm64-link.c index 0833746..e8f810d 100644 --- a/arm64-link.c +++ b/arm64-link.c @@ -13,8 +13,8 @@ #define ELF_START_ADDR 0x00400000 #define ELF_PAGE_SIZE 0x1000 -#define HAVE_SECTION_RELOC #define PCRELATIVE_DLLPLT 1 +#define RELOCATE_DLLPLT 1 #else /* !TARGET_DEFS_ONLY */ @@ -81,6 +81,74 @@ int gotplt_entry_type (int reloc_type) return -1; } +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr) +{ + Section *plt = s1->plt; + uint8_t *p; + unsigned plt_offset; + + if (s1->output_type == TCC_OUTPUT_DLL) + tcc_error("DLLs unimplemented!"); + + if (plt->data_offset == 0) { + section_ptr_add(plt, 32); + } + plt_offset = plt->data_offset; + + p = section_ptr_add(plt, 16); + write32le(p, got_offset); + write32le(p + 4, (uint64_t) got_offset >> 32); + return plt_offset; +} + +/* relocate the PLT: compute addresses and offsets in the PLT now that final + address for PLT and GOT are known (see fill_program_header) */ +ST_FUNC void relocate_plt(TCCState *s1) +{ + uint8_t *p, *p_end; + + if (!s1->plt) + return; + + p = s1->plt->data; + p_end = p + s1->plt->data_offset; + + if (p < p_end) { + uint64_t plt = s1->plt->sh_addr; + uint64_t got = s1->got->sh_addr; + 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)", off, got, plt); + write32le(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]! + write32le(p + 4, (0x90000010 | // adrp x16,... + (off & 0x1ffffc) << 3 | (off & 3) << 29)); + write32le(p + 8, (0xf9400211 | // ldr x17,[x16,#...] + (got & 0xff8) << 7)); + write32le(p + 12, (0x91000210 | // add x16,x16,#... + (got & 0xfff) << 10)); + write32le(p + 16, 0xd61f0220); // br x17 + write32le(p + 20, 0xd503201f); // nop + write32le(p + 24, 0xd503201f); // nop + write32le(p + 28, 0xd503201f); // nop + p += 32; + while (p < p_end) { + uint64_t pc = plt + (p - s1->plt->data); + uint64_t addr = got + read64le(p); + uint64_t off = (addr >> 12) - (pc >> 12); + if ((off + ((uint32_t)1 << 20)) >> 21) + tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc); + write32le(p, (0x90000010 | // adrp x16,... + (off & 0x1ffffc) << 3 | (off & 3) << 29)); + write32le(p + 4, (0xf9400211 | // ldr x17,[x16,#...] + (addr & 0xff8) << 7)); + write32le(p + 8, (0x91000210 | // add x16,x16,#... + (addr & 0xfff) << 10)); + write32le(p + 12, 0xd61f0220); // br x17 + p += 16; + } + } +} + void relocate_init(Section *sr) {} void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val) diff --git a/c67-link.c b/c67-link.c index f3220cb..ce45269 100644 --- a/c67-link.c +++ b/c67-link.c @@ -14,8 +14,8 @@ #define ELF_START_ADDR 0x00000400 #define ELF_PAGE_SIZE 0x1000 -#define HAVE_SECTION_RELOC #define PCRELATIVE_DLLPLT 0 +#define RELOCATE_DLLPLT 0 #else /* !TARGET_DEFS_ONLY */ @@ -68,6 +68,32 @@ int gotplt_entry_type (int reloc_type) return -1; } +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr) +{ + tcc_error("C67 got not implemented"); + return 0; +} + +/* relocate the PLT: compute addresses and offsets in the PLT now that final + address for PLT and GOT are known (see fill_program_header) */ +ST_FUNC void relocate_plt(TCCState *s1) +{ + uint8_t *p, *p_end; + + if (!s1->plt) + return; + + p = s1->plt->data; + p_end = p + s1->plt->data_offset; + + if (p < p_end) { + /* XXX: TODO */ + while (p < p_end) { + /* XXX: TODO */ + } + } +} + void relocate_init(Section *sr) {} void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val) diff --git a/i386-link.c b/i386-link.c index 5a0511c..c098172 100644 --- a/i386-link.c +++ b/i386-link.c @@ -14,8 +14,8 @@ #define ELF_START_ADDR 0x08048000 #define ELF_PAGE_SIZE 0x1000 -#define HAVE_SECTION_RELOC #define PCRELATIVE_DLLPLT 0 +#define RELOCATE_DLLPLT 0 #else /* !TARGET_DEFS_ONLY */ @@ -26,6 +26,7 @@ int code_reloc (int reloc_type) { switch (reloc_type) { + case R_386_RELATIVE: case R_386_16: case R_386_32: case R_386_GOTPC: @@ -53,6 +54,7 @@ int code_reloc (int reloc_type) 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: @@ -78,6 +80,73 @@ int gotplt_entry_type (int reloc_type) return -1; } +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr) +{ + Section *plt = s1->plt; + uint8_t *p; + int modrm; + unsigned plt_offset, relofs; + + /* on i386 if we build a DLL, we add a %ebx offset */ + if (s1->output_type == TCC_OUTPUT_DLL) + modrm = 0xa3; + else + modrm = 0x25; + + /* empty PLT: create PLT0 entry that pushes the library indentifier + (GOT + PTR_SIZE) and jumps to ld.so resolution routine + (GOT + 2 * PTR_SIZE) */ + if (plt->data_offset == 0) { + p = section_ptr_add(plt, 16); + p[0] = 0xff; /* pushl got + PTR_SIZE */ + p[1] = modrm + 0x10; + write32le(p + 2, PTR_SIZE); + p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ + p[7] = modrm; + write32le(p + 8, PTR_SIZE * 2); + } + plt_offset = plt->data_offset; + + /* 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; + + /* Jump to GOT entry where ld.so initially put the address of ip + 4 */ + p = section_ptr_add(plt, 16); + p[0] = 0xff; /* jmp *(got + x) */ + p[1] = modrm; + write32le(p + 2, got_offset); + p[6] = 0x68; /* push $xxx */ + write32le(p + 7, relofs); + p[11] = 0xe9; /* jmp plt_start */ + write32le(p + 12, -(plt->data_offset)); + return plt_offset; +} + +/* relocate the PLT: compute addresses and offsets in the PLT now that final + address for PLT and GOT are known (see fill_program_header) */ +ST_FUNC void relocate_plt(TCCState *s1) +{ + uint8_t *p, *p_end; + + if (!s1->plt) + return; + + p = s1->plt->data; + p_end = p + s1->plt->data_offset; + + if (p < p_end) { + add32le(p + 2, s1->got->sh_addr); + add32le(p + 8, s1->got->sh_addr); + p += 16; + while (p < p_end) { + add32le(p + 2, s1->got->sh_addr); + p += 16; + } + } +} + static ElfW_Rel *qrel; /* ptr to next reloc entry reused */ void relocate_init(Section *sr) @@ -94,7 +163,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add switch (type) { case R_386_32: if (s1->output_type == TCC_OUTPUT_DLL) { - esym_index = s1->symtab_to_dynsym[sym_index]; + esym_index = s1->sym_attrs[sym_index].dyn_index; qrel->r_offset = rel->r_offset; if (esym_index) { qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32); @@ -110,7 +179,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add case R_386_PC32: if (s1->output_type == TCC_OUTPUT_DLL) { /* DLL relocation */ - esym_index = s1->symtab_to_dynsym[sym_index]; + esym_index = s1->sym_attrs[sym_index].dyn_index; if (esym_index) { qrel->r_offset = rel->r_offset; qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32); diff --git a/libtcc.c b/libtcc.c index aff7dcc..e92a035 100644 --- a/libtcc.c +++ b/libtcc.c @@ -941,7 +941,6 @@ LIBTCCAPI void tcc_delete(TCCState *s1) tcc_run_free(s1); #endif - tcc_free(s1->sym_attrs); tcc_free(s1); tcc_memstats(bench); } diff --git a/tcc.h b/tcc.h index 310134c..47af365 100644 --- a/tcc.h +++ b/tcc.h @@ -271,8 +271,6 @@ # define ElfW_Rel ElfW(Rela) # define SHT_RELX SHT_RELA # define REL_SECTION_FMT ".rela%s" -/* XXX: DLL with PLT would only work with x86-64 for now */ -# define TCC_OUTPUT_DLL_WITH_PLT #else # define ELFCLASSW ELFCLASS32 # define ElfW(type) Elf##32##_##type @@ -577,8 +575,10 @@ typedef struct ASMOperand { /* extra symbol attributes (not in symbol table) */ struct sym_attr { - unsigned long got_offset; - unsigned long plt_offset; + unsigned got_offset; + unsigned plt_offset; + int plt_sym; + int dyn_index; #ifdef TCC_TARGET_ARM unsigned char plt_thumb_stub:1; #endif @@ -633,7 +633,7 @@ struct TCCState { addr_t text_addr; /* address of text section */ int has_text_addr; - unsigned long section_align; /* section alignment */ + unsigned section_align; /* section alignment */ char *init_symbol; /* symbols to call at load-time (not used currently) */ char *fini_symbol; /* symbols to call at unload-time (not used currently) */ @@ -714,8 +714,6 @@ struct TCCState { /* got & plt handling */ Section *got; Section *plt; - /* give the correspondance from symtab indexes to dynsym indexes */ - int *symtab_to_dynsym; /* temporary dynamic symbol sections (for dll loading) */ Section *dynsymtab_section; @@ -1332,9 +1330,6 @@ ST_FUNC void tccelf_new(TCCState *s); ST_FUNC void tccelf_delete(TCCState *s); ST_FUNC void tccelf_stab_new(TCCState *s); -/* return offset of 'ptr' from start of section 'sec' */ -#define OFFSET_FROM_SECTION_START(sec, ptr) ((size_t)ptr - (size_t)sec->data) - ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags); ST_FUNC void section_realloc(Section *sec, unsigned long new_size); ST_FUNC void *section_ptr_add(Section *sec, addr_t size); @@ -1362,17 +1357,16 @@ ST_FUNC void put_stabd(int type, int other, int desc); ST_FUNC void relocate_common_syms(void); ST_FUNC void relocate_syms(TCCState *s1, Section *symtab, int do_resolve); ST_FUNC void relocate_section(TCCState *s1, Section *s); -ST_FUNC void relocate_plt(TCCState *s1); ST_FUNC void tcc_add_linker_symbols(TCCState *s1); ST_FUNC int tcc_object_type(int fd, ElfW(Ehdr) *h); ST_FUNC int tcc_load_object_file(TCCState *s1, int fd, unsigned long file_offset); ST_FUNC int tcc_load_archive(TCCState *s1, int fd); ST_FUNC void tcc_add_bcheck(TCCState *s1); - -ST_FUNC void build_got_entries(TCCState *s1); ST_FUNC void tcc_add_runtime(TCCState *s1); +ST_FUNC void build_got_entries(TCCState *s1); +ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc); ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err); #if defined TCC_IS_NATIVE || defined TCC_TARGET_PE ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name); @@ -1400,6 +1394,10 @@ enum gotplt_entry { ST_FUNC int code_reloc (int reloc_type); ST_FUNC int gotplt_entry_type (int reloc_type); +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr); +ST_FUNC void relocate_init(Section *sr); +ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val); +ST_FUNC void relocate_plt(TCCState *s1); /* ------------ xxx-gen.c ------------ */ @@ -1506,11 +1504,6 @@ ST_FUNC void gen_clear_cache(void); #ifdef TCC_TARGET_C67 #endif -/* ------------ xxx-link.c ------------ */ - -ST_FUNC void relocate_init(Section *sr); -ST_FUNC void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val); - /* ------------ tcccoff.c ------------ */ #ifdef TCC_TARGET_COFF diff --git a/tccelf.c b/tccelf.c index d806f7c..c97d742 100644 --- a/tccelf.c +++ b/tccelf.c @@ -67,6 +67,7 @@ ST_FUNC void tccelf_new(TCCState *s) s->dynsymtab_section = new_symtab(s, ".dynsymtab", SHT_SYMTAB, SHF_PRIVATE, ".dynstrtab", ".dynhashtab", SHF_PRIVATE); + get_sym_attr(s, 0, 1); } #ifdef CONFIG_TCC_BCHECK @@ -123,6 +124,7 @@ ST_FUNC void tccelf_delete(TCCState *s1) #endif /* free loaded dlls array */ dynarray_reset(&s1->loaded_dlls, &s1->nb_loaded_dlls); + tcc_free(s1->sym_attrs); } ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh_flags) @@ -160,6 +162,35 @@ ST_FUNC Section *new_section(TCCState *s1, const char *name, int sh_type, int sh return sec; } +ST_FUNC Section *new_symtab(TCCState *s1, + const char *symtab_name, int sh_type, int sh_flags, + const char *strtab_name, + const char *hash_name, int hash_sh_flags) +{ + Section *symtab, *strtab, *hash; + int *ptr, nb_buckets; + + symtab = new_section(s1, symtab_name, sh_type, sh_flags); + symtab->sh_entsize = sizeof(ElfW(Sym)); + strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags); + put_elf_str(strtab, ""); + symtab->link = strtab; + put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL); + + nb_buckets = 1; + + hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags); + hash->sh_entsize = sizeof(int); + symtab->hash = hash; + hash->link = symtab; + + ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int)); + ptr[0] = nb_buckets; + ptr[1] = 1; + memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int)); + return symtab; +} + /* realloc section and set its content to zero */ ST_FUNC void section_realloc(Section *sec, unsigned long new_size) { @@ -490,7 +521,7 @@ ST_FUNC void put_elf_reloca(Section *symtab, Section *s, unsigned long offset, rel = section_ptr_add(sr, sizeof(ElfW_Rel)); rel->r_offset = offset; rel->r_info = ELFW(R_INFO)(symbol, type); -#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) +#if SHT_RELX == SHT_RELA rel->r_addend = addend; #else if (addend) @@ -542,14 +573,14 @@ ST_FUNC void put_stabd(int type, int other, int desc) put_stabs(NULL, type, other, desc, 0); } -static struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc) +ST_FUNC struct sym_attr *get_sym_attr(TCCState *s1, int index, int alloc) { int n; struct sym_attr *tab; if (index >= s1->nb_sym_attrs) { if (!alloc) - return NULL; + return s1->sym_attrs; /* find immediately bigger power of 2 and reallocate array */ n = 1; while (index >= n) @@ -706,29 +737,19 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s) int type, sym_index; unsigned char *ptr; addr_t tgt, addr; - struct sym_attr *symattr; relocate_init(sr); + for_each_elem(sr, 0, rel, ElfW_Rel) { ptr = s->data + rel->r_offset; - sym_index = ELFW(R_SYM)(rel->r_info); sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; type = ELFW(R_TYPE)(rel->r_info); - symattr = get_sym_attr(s1, sym_index, 0); tgt = sym->st_value; - /* If static relocation to a dynamic symbol, relocate to PLT entry. - Note 1: in tcc -run mode we go through PLT to avoid range issues - Note 2: symbols compiled with libtcc and later added with - tcc_add_symbol are not dynamic and thus have symattr NULL */ - if (gotplt_entry_type(type) != NO_GOTPLT_ENTRY && - code_reloc(type) && symattr && symattr->plt_offset) - tgt = s1->plt->sh_addr + symattr->plt_offset; -#if defined(TCC_TARGET_ARM64) || defined(TCC_TARGET_X86_64) +#if SHT_RELX == SHT_RELA tgt += rel->r_addend; #endif addr = s->sh_addr + rel->r_offset; - relocate(s1, rel, type, ptr, addr, tgt); } /* if the relocation is allocated, we change its symbol table */ @@ -752,7 +773,7 @@ static void relocate_rel(TCCState *s1, Section *sr) static int prepare_dynamic_rel(TCCState *s1, Section *sr) { ElfW_Rel *rel; - int sym_index, esym_index, type, count; + int sym_index, type, count; count = 0; for_each_elem(sr, 0, rel, ElfW_Rel) { @@ -773,8 +794,7 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr) #elif defined(TCC_TARGET_X86_64) case R_X86_64_PC32: #endif - esym_index = s1->symtab_to_dynsym[sym_index]; - if (esym_index) + if (get_sym_attr(s1, sym_index, 0)->dyn_index) count++; break; default: @@ -791,206 +811,43 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr) static void build_got(TCCState *s1) { - unsigned char *ptr; - /* if no got, then create it */ s1->got = new_section(s1, ".got", SHT_PROGBITS, SHF_ALLOC | SHF_WRITE); s1->got->sh_entsize = 4; set_elf_sym(symtab_section, 0, 4, ELFW(ST_INFO)(STB_GLOBAL, STT_OBJECT), 0, s1->got->sh_num, "_GLOBAL_OFFSET_TABLE_"); - ptr = section_ptr_add(s1->got, 3 * PTR_SIZE); -#if PTR_SIZE == 4 - /* keep space for _DYNAMIC pointer, if present */ - write32le(ptr, 0); - /* two dummy got entries */ - write32le(ptr + 4, 0); - write32le(ptr + 8, 0); -#else - /* keep space for _DYNAMIC pointer, if present */ - write32le(ptr, 0); - write32le(ptr + 4, 0); - /* two dummy got entries */ - write32le(ptr + 8, 0); - write32le(ptr + 12, 0); - write32le(ptr + 16, 0); - write32le(ptr + 20, 0); -#endif + /* keep space for _DYNAMIC pointer and two dummy got entries */ + section_ptr_add(s1->got, 3 * PTR_SIZE); } /* Create a GOT and (for function call) a PLT entry corresponding to a symbol in s1->symtab. When creating the dynamic symbol table entry for the GOT relocation, use 'size' and 'info' for the corresponding symbol metadata. Returns the offset of the GOT or (if any) PLT entry. */ -static unsigned long put_got_entry(TCCState *s1, int dyn_reloc_type, - int reloc_type, unsigned long size, - int info, int sym_index) +static struct sym_attr * put_got_entry(TCCState *s1, int dyn_reloc_type, + int reloc_type, unsigned long size, + int info, int sym_index) { - int index, need_plt_entry = 0; + int need_plt_entry; const char *name; - ElfW(Sym) *sym, *esym; - unsigned long offset; - int *ptr; - size_t got_offset; - struct sym_attr *symattr; + ElfW(Sym) *sym; + struct sym_attr *attr; + unsigned got_offset; + char plt_name[100]; + int len; need_plt_entry = (dyn_reloc_type == R_JMP_SLOT); - - if (!s1->got) - build_got(s1); - - /* create PLT if needed */ - if (need_plt_entry && !s1->plt) { - s1->plt = new_section(s1, ".plt", SHT_PROGBITS, - SHF_ALLOC | SHF_EXECINSTR); - s1->plt->sh_entsize = 4; - } - - /* already a GOT and/or PLT entry, no need to add one */ - if (sym_index < s1->nb_sym_attrs) { - if (need_plt_entry && s1->sym_attrs[sym_index].plt_offset) - return s1->sym_attrs[sym_index].plt_offset; - else if (!need_plt_entry && s1->sym_attrs[sym_index].got_offset) - return s1->sym_attrs[sym_index].got_offset; - } - - symattr = get_sym_attr(s1, sym_index, 1); - - /* create the GOT entry */ - ptr = section_ptr_add(s1->got, PTR_SIZE); - *ptr = 0; - got_offset = OFFSET_FROM_SECTION_START (s1->got, ptr); + attr = get_sym_attr(s1, sym_index, 1); /* In case a function is both called and its address taken 2 GOT entries are created, one for taking the address (GOT) and the other for the PLT - entry (PLTGOT). We don't record the offset of the PLTGOT entry in the - got_offset field since it might overwrite the offset of a GOT entry. - Besides, for PLT entry the static relocation is against the PLT entry - and the dynamic relocation for PLTGOT is created in this function. */ - if (!need_plt_entry) - symattr->got_offset = got_offset; + entry (PLTGOT). */ + if (need_plt_entry ? attr->plt_offset : attr->got_offset) + return attr; - sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; - name = (char *) symtab_section->link->data + sym->st_name; - offset = sym->st_value; - - /* create PLT entry */ - if (need_plt_entry) { -#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64) - Section *plt; - uint8_t *p; - int modrm; - unsigned long relofs; - -#if defined(TCC_OUTPUT_DLL_WITH_PLT) - modrm = 0x25; -#else - /* if we build a DLL, we add a %ebx offset */ - if (s1->output_type == TCC_OUTPUT_DLL) - modrm = 0xa3; - else - modrm = 0x25; -#endif - - plt = s1->plt; - /* empty PLT: create PLT0 entry that pushes the library indentifier - (GOT + PTR_SIZE) and jumps to ld.so resolution routine - (GOT + 2 * PTR_SIZE) */ - if (plt->data_offset == 0) { - p = section_ptr_add(plt, 16); - p[0] = 0xff; /* pushl got + PTR_SIZE */ - p[1] = modrm + 0x10; - write32le(p + 2, PTR_SIZE); - p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ - p[7] = modrm; - write32le(p + 8, PTR_SIZE * 2); - } - - /* 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; - symattr->plt_offset = plt->data_offset; - - /* Jump to GOT entry where ld.so initially put the address of ip + 4 */ - p = section_ptr_add(plt, 16); - p[0] = 0xff; /* jmp *(got + x) */ - p[1] = modrm; - write32le(p + 2, got_offset); - p[6] = 0x68; /* push $xxx */ -#ifdef TCC_TARGET_X86_64 - /* On x86-64, the relocation is referred to by _index_ */ - write32le(p + 7, relofs / sizeof (ElfW_Rel)); -#else - write32le(p + 7, relofs); -#endif - p[11] = 0xe9; /* jmp plt_start */ - write32le(p + 12, -(plt->data_offset)); - - /* If this was an UNDEF symbol set the offset in the dynsymtab to the - PLT slot, so that PC32 relocs to it can be resolved */ - if (sym->st_shndx == SHN_UNDEF) - offset = plt->data_offset - 16; -#elif defined(TCC_TARGET_ARM) - Section *plt; - uint8_t *p; - - /* when building a DLL, GOT entry accesses must be done relative to - start of GOT (see x86_64 examble above) */ - if (s1->output_type == TCC_OUTPUT_DLL) - tcc_error("DLLs unimplemented!"); - - plt = s1->plt; - /* empty PLT: create PLT0 entry that push address of call site and - jump to ld.so resolution routine (GOT + 8) */ - if (plt->data_offset == 0) { - p = section_ptr_add(plt, 20); - write32le(p, 0xe52de004); /* push {lr} */ - write32le(p+4, 0xe59fe004); /* ldr lr, [pc, #4] */ - write32le(p+8, 0xe08fe00e); /* add lr, pc, lr */ - write32le(p+12, 0xe5bef008); /* ldr pc, [lr, #8]! */ - /* p+16 is set in relocate_plt */ - } - - symattr->plt_offset = plt->data_offset; - if (symattr->plt_thumb_stub) { - p = section_ptr_add(plt, 4); - write32le(p, 0x4778); /* bx pc */ - write32le(p+2, 0x46c0); /* nop */ - } - p = section_ptr_add(plt, 16); - /* Jump to GOT entry where ld.so initially put address of PLT0 */ - write32le(p, 0xe59fc004); /* ldr ip, [pc, #4] */ - write32le(p+4, 0xe08fc00c); /* add ip, pc, ip */ - write32le(p+8, 0xe59cf000); /* ldr pc, [ip] */ - /* p + 12 contains offset to GOT entry once patched by relocate_plt */ - write32le(p+12, got_offset); - - /* the symbol is modified so that it will be relocated to the PLT */ - if (sym->st_shndx == SHN_UNDEF) - offset = plt->data_offset - 16; -#elif defined(TCC_TARGET_ARM64) - Section *plt; - uint8_t *p; - - if (s1->output_type == TCC_OUTPUT_DLL) - tcc_error("DLLs unimplemented!"); - - plt = s1->plt; - if (plt->data_offset == 0) - section_ptr_add(plt, 32); - symattr->plt_offset = plt->data_offset; - p = section_ptr_add(plt, 16); - write32le(p, got_offset); - write32le(p + 4, (uint64_t) got_offset >> 32); - - if (sym->st_shndx == SHN_UNDEF) - offset = plt->data_offset - 16; -#elif defined(TCC_TARGET_C67) - tcc_error("C67 got not implemented"); -#else -#error unsupported CPU -#endif - } + /* create the GOT entry */ + got_offset = s1->got->data_offset; + section_ptr_add(s1->got, PTR_SIZE); /* Create the GOT relocation that will insert the address of the object or function of interest in the GOT entry. This is a static relocation for @@ -999,31 +856,44 @@ static unsigned long put_got_entry(TCCState *s1, int dyn_reloc_type, done lazily for GOT entry with *_JUMP_SLOT relocation type (the one associated to a PLT entry) but is currently done at load time for an unknown reason. */ + + sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; + name = (char *) symtab_section->link->data + sym->st_name; + if (s1->dynsym) { - /* create the dynamic symbol table entry that the relocation refers to - in its r_info field to identify the symbol */ - /* XXX This might generate multiple syms for name. */ - index = find_elf_sym (s1->dynsym, name); - if (index) { - esym = (ElfW(Sym) *) s1->dynsym->data + index; - esym->st_value = offset; - - } else if (s1->output_type == TCC_OUTPUT_MEMORY || - ELFW(ST_BIND)(sym->st_info) == STB_WEAK || - gotplt_entry_type(reloc_type) == ALWAYS_GOTPLT_ENTRY) - index = put_elf_sym(s1->dynsym, offset, size, info, 0, - sym->st_shndx, name); - else - tcc_error("Runtime relocation without dynamic symbol: %s", name); - put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type, index); - } else - put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type, + if (0 == attr->dyn_index) + attr->dyn_index = set_elf_sym(s1->dynsym, sym->st_value, size, + info, 0, sym->st_shndx, name); + put_elf_reloc(s1->dynsym, s1->got, got_offset, dyn_reloc_type, + attr->dyn_index); + } else { + put_elf_reloc(symtab_section, s1->got, got_offset, dyn_reloc_type, sym_index); + } - if (need_plt_entry) - return symattr->plt_offset; - else - return symattr->got_offset; + if (need_plt_entry) { + if (!s1->plt) { + s1->plt = new_section(s1, ".plt", SHT_PROGBITS, + SHF_ALLOC | SHF_EXECINSTR); + s1->plt->sh_entsize = 4; + } + + attr->plt_offset = create_plt_entry(s1, got_offset, attr); + + /* create a symbol 'sym@plt' for the PLT jump vector */ + len = strlen(name); + if (len > sizeof plt_name - 5) + len = sizeof plt_name - 5; + memcpy(plt_name, name, len); + strcpy(plt_name + len, "@plt"); + attr->plt_sym = put_elf_sym(s1->symtab, attr->plt_offset, sym->st_size, + ELFW(ST_INFO)(STB_GLOBAL, STT_FUNC), 0, s1->plt->sh_num, plt_name); + + } else { + attr->got_offset = got_offset; + } + + return attr; } /* build GOT and PLT entries */ @@ -1033,6 +903,7 @@ ST_FUNC void build_got_entries(TCCState *s1) ElfW_Rel *rel; ElfW(Sym) *sym; int i, type, gotplt_entry, reloc_type, sym_index; + struct sym_attr *attr; for(i = 1; i < s1->nb_sections; i++) { s = s1->sections[i]; @@ -1047,29 +918,36 @@ ST_FUNC void build_got_entries(TCCState *s1) sym_index = ELFW(R_SYM)(rel->r_info); sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; - if (gotplt_entry == NO_GOTPLT_ENTRY) + 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; + } - /* Proceed with PLT/GOT [entry] creation if any of the following - condition is met: - - it is an undefined reference (dynamic relocation needed) - - symbol is absolute (probably created by tcc_add_symbol and - thus might be too far from application code) - - relocation requires a PLT/GOT (BUILD_GOTPLT_ENTRY or - ALWAYS_GOTPLT_ENTRY). */ - if (sym->st_shndx != SHN_UNDEF && - sym->st_shndx != SHN_ABS && - gotplt_entry == AUTO_GOTPLT_ENTRY) - continue; - - /* Building a dynamic library but target is not capable of PC - relative PLT entries. It can thus only use PLT entries if - it expects one to be used (ALWAYS_GOTPLT_ENTRY). */ - if (sym->st_shndx == SHN_UNDEF && - s1->output_type == TCC_OUTPUT_DLL && - !PCRELATIVE_DLLPLT && - gotplt_entry == AUTO_GOTPLT_ENTRY) - 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 */ + if (gotplt_entry == AUTO_GOTPLT_ENTRY) { + if (sym->st_shndx == SHN_UNDEF) { + if (s1->output_type == TCC_OUTPUT_DLL && ! PCRELATIVE_DLLPLT) + continue; + } else if (!(sym->st_shndx == SHN_ABS && PTR_SIZE == 8)) + continue; + } #ifdef TCC_TARGET_X86_64 if (type == R_X86_64_PLT32 && @@ -1078,6 +956,13 @@ ST_FUNC void build_got_entries(TCCState *s1) continue; } #endif + if (code_reloc(type)) { +#ifdef TCC_TARGET_I386 + jmp_slot: +#endif + reloc_type = R_JMP_SLOT; + } else + reloc_type = R_GLOB_DAT; if (!s1->got) build_got(s1); @@ -1085,45 +970,15 @@ ST_FUNC void build_got_entries(TCCState *s1) if (gotplt_entry == BUILD_GOT_ONLY) continue; - if (code_reloc(type)) - reloc_type = R_JMP_SLOT; - else - reloc_type = R_GLOB_DAT; - put_got_entry(s1, reloc_type, type, sym->st_size, sym->st_info, - sym_index); + attr = put_got_entry(s1, reloc_type, type, sym->st_size, sym->st_info, + sym_index); + + if (reloc_type == R_JMP_SLOT) + rel->r_info = ELFW(R_INFO)(attr->plt_sym, type); } } } -ST_FUNC Section *new_symtab(TCCState *s1, - const char *symtab_name, int sh_type, int sh_flags, - const char *strtab_name, - const char *hash_name, int hash_sh_flags) -{ - Section *symtab, *strtab, *hash; - int *ptr, nb_buckets; - - symtab = new_section(s1, symtab_name, sh_type, sh_flags); - symtab->sh_entsize = sizeof(ElfW(Sym)); - strtab = new_section(s1, strtab_name, SHT_STRTAB, sh_flags); - put_elf_str(strtab, ""); - symtab->link = strtab; - put_elf_sym(symtab, 0, 0, 0, 0, 0, NULL); - - nb_buckets = 1; - - hash = new_section(s1, hash_name, SHT_HASH, hash_sh_flags); - hash->sh_entsize = sizeof(int); - symtab->hash = hash; - hash->link = symtab; - - ptr = section_ptr_add(hash, (2 + nb_buckets + 1) * sizeof(int)); - ptr[0] = nb_buckets; - ptr[1] = 1; - memset(ptr + 2, 0, (nb_buckets + 1) * sizeof(int)); - return symtab; -} - /* put dynamic tag */ static void put_dt(Section *dynamic, int dt, addr_t val) { @@ -1308,52 +1163,26 @@ static void tcc_output_binary(TCCState *s1, FILE *f, #if defined(__FreeBSD__) || defined(__FreeBSD_kernel__) #define HAVE_PHDR 1 #define EXTRA_RELITEMS 14 - -/* move the relocation value from .dynsym to .got */ -static void patch_dynsym_undef(TCCState *s1, Section *s) -{ - uint32_t *gotd = (void *)s1->got->data; - ElfW(Sym) *sym; - - gotd += 3; /* dummy entries in .got */ - /* relocate symbols in .dynsym */ - for_each_elem(s, 1, sym, ElfW(Sym)) { - if (sym->st_shndx == SHN_UNDEF) { - *gotd++ = sym->st_value + 6; /* XXX 6 is magic ? */ - sym->st_value = 0; - } - } -} #else #define HAVE_PHDR 1 #define EXTRA_RELITEMS 9 - -/* zero plt offsets of weak symbols in .dynsym */ -static void patch_dynsym_undef(TCCState *s1, Section *s) -{ - ElfW(Sym) *sym; - - for_each_elem(s, 1, sym, ElfW(Sym)) - if (sym->st_shndx == SHN_UNDEF && ELFW(ST_BIND)(sym->st_info) == STB_WEAK) - sym->st_value = 0; -} #endif ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel) { int sym_index = ELFW(R_SYM) (rel->r_info); ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index]; - unsigned long offset; + struct sym_attr *attr = get_sym_attr(s1, sym_index, 0); + unsigned offset = attr->got_offset; - if (sym_index >= s1->nb_sym_attrs) + if (0 == offset) return; - offset = s1->sym_attrs[sym_index].got_offset; section_reserve(s1->got, offset + PTR_SIZE); #ifdef TCC_TARGET_X86_64 - /* only works for x86-64 */ - write32le(s1->got->data + offset + 4, sym->st_value >> 32); + write64le(s1->got->data + offset, sym->st_value); +#else + write32le(s1->got->data + offset, sym->st_value); #endif - write32le(s1->got->data + offset, sym->st_value & 0xffffffff); } /* Perform relocation to GOT or PLT entries */ @@ -1426,6 +1255,7 @@ static void bind_exe_dynsyms(TCCState *s1) index = put_elf_sym(s1->dynsym, offset, esym->st_size, esym->st_info, 0, bss_section->sh_num, name); + /* Ensure R_COPY works for weak symbol aliases */ if (ELFW(ST_BIND)(esym->st_info) == STB_WEAK) { for_each_elem(s1->dynsymtab_section, 1, dynsym, ElfW(Sym)) { @@ -1440,6 +1270,7 @@ static void bind_exe_dynsyms(TCCState *s1) } } } + put_elf_reloc(s1->dynsym, bss_section, offset, R_COPY, index); offset += esym->st_size; @@ -1497,106 +1328,23 @@ static void bind_libs_dynsyms(TCCState *s1) symbols to be resolved by other shared libraries or by the executable. */ static void export_global_syms(TCCState *s1) { - int nb_syms, dynindex, index; + int dynindex, index; const char *name; ElfW(Sym) *sym; - nb_syms = symtab_section->data_offset / sizeof(ElfW(Sym)); - s1->symtab_to_dynsym = tcc_mallocz(sizeof(int) * nb_syms); for_each_elem(symtab_section, 1, sym, ElfW(Sym)) { if (ELFW(ST_BIND)(sym->st_info) != STB_LOCAL) { name = (char *) symtab_section->link->data + sym->st_name; dynindex = put_elf_sym(s1->dynsym, sym->st_value, sym->st_size, sym->st_info, 0, sym->st_shndx, name); index = sym - (ElfW(Sym) *) symtab_section->data; - s1->symtab_to_dynsym[index] = dynindex; + get_sym_attr(s1, index, 1)->dyn_index = dynindex; } } } -/* relocate the PLT: compute addresses and offsets in the PLT now that final - address for PLT and GOT are known (see fill_program_header) */ -ST_FUNC void relocate_plt(TCCState *s1) -{ - uint8_t *p, *p_end; - - if (!s1->plt) - return; - - p = s1->plt->data; - p_end = p + s1->plt->data_offset; - if (p < p_end) { -#if defined(TCC_TARGET_I386) - add32le(p + 2, s1->got->sh_addr); - add32le(p + 8, s1->got->sh_addr); - p += 16; - while (p < p_end) { - add32le(p + 2, s1->got->sh_addr); - p += 16; - } -#elif defined(TCC_TARGET_X86_64) - int x = s1->got->sh_addr - s1->plt->sh_addr - 6; - add32le(p + 2, x); - add32le(p + 8, x - 6); - p += 16; - while (p < p_end) { - add32le(p + 2, x + s1->plt->data - p); - p += 16; - } -#elif defined(TCC_TARGET_ARM) - int x = s1->got->sh_addr - s1->plt->sh_addr - 12; - write32le(s1->plt->data + 16, x - 16); - p += 20; - while (p < p_end) { - if (read32le(p) == 0x46c04778) /* PLT Thumb stub present */ - p += 4; - add32le(p + 12, x + s1->plt->data - p); - p += 16; - } -#elif defined(TCC_TARGET_ARM64) - uint64_t plt = s1->plt->sh_addr; - uint64_t got = s1->got->sh_addr; - 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)", off, got, plt); - write32le(p, 0xa9bf7bf0); // stp x16,x30,[sp,#-16]! - write32le(p + 4, (0x90000010 | // adrp x16,... - (off & 0x1ffffc) << 3 | (off & 3) << 29)); - write32le(p + 8, (0xf9400211 | // ldr x17,[x16,#...] - (got & 0xff8) << 7)); - write32le(p + 12, (0x91000210 | // add x16,x16,#... - (got & 0xfff) << 10)); - write32le(p + 16, 0xd61f0220); // br x17 - write32le(p + 20, 0xd503201f); // nop - write32le(p + 24, 0xd503201f); // nop - write32le(p + 28, 0xd503201f); // nop - p += 32; - while (p < p_end) { - uint64_t pc = plt + (p - s1->plt->data); - uint64_t addr = got + read64le(p); - uint64_t off = (addr >> 12) - (pc >> 12); - if ((off + ((uint32_t)1 << 20)) >> 21) - tcc_error("Failed relocating PLT (off=0x%lx, addr=0x%lx, pc=0x%lx)", off, addr, pc); - write32le(p, (0x90000010 | // adrp x16,... - (off & 0x1ffffc) << 3 | (off & 3) << 29)); - write32le(p + 4, (0xf9400211 | // ldr x17,[x16,#...] - (addr & 0xff8) << 7)); - write32le(p + 8, (0x91000210 | // add x16,x16,#... - (addr & 0xfff) << 10)); - write32le(p + 12, 0xd61f0220); // br x17 - p += 16; - } -#elif defined(TCC_TARGET_C67) - /* XXX: TODO */ -#else -#error unsupported CPU -#endif - } -} - /* Allocate strings for section names and decide if an unallocated section should be output. - NOTE: the strsec section comes last, so its size is also correct ! */ static void alloc_sec_names(TCCState *s1, int file_type, Section *strsec) { @@ -2024,8 +1772,6 @@ static void tcc_output_elf(TCCState *s1, FILE *f, int phnum, ElfW(Phdr) *phdr, for(i = 1; i < s1->nb_sections; i++) { s = s1->sections[sec_order[i]]; if (s->sh_type != SHT_NOBITS) { - if (s->sh_type == SHT_DYNSYM) - patch_dynsym_undef(s1, s); while (offset < s->sh_offset) { fputc(0, f); offset++; @@ -2251,22 +1997,12 @@ static int elf_output_file(TCCState *s1, const char *filename) /* put in GOT the dynamic section address and relocate PLT */ write32le(s1->got->data, dynamic->sh_addr); if (file_type == TCC_OUTPUT_EXE -#if defined(TCC_OUTPUT_DLL_WITH_PLT) - || file_type == TCC_OUTPUT_DLL -#endif - ) + || (RELOCATE_DLLPLT && file_type == TCC_OUTPUT_DLL)) relocate_plt(s1); /* relocate symbols in .dynsym now that final addresses are known */ for_each_elem(s1->dynsym, 1, sym, ElfW(Sym)) { - if (sym->st_shndx == SHN_UNDEF) { - /* relocate to PLT if symbol corresponds to a PLT entry, - but not if it's a weak symbol */ - if (ELFW(ST_BIND)(sym->st_info) == STB_WEAK) - sym->st_value = 0; - else if (sym->st_value) - sym->st_value += s1->plt->sh_addr; - } else if (sym->st_shndx < SHN_LORESERVE) { + if (sym->st_shndx != SHN_UNDEF && sym->st_shndx < SHN_LORESERVE) { /* do symbol relocation */ sym->st_value += s1->sections[sym->st_shndx]->sh_addr; } @@ -2289,11 +2025,8 @@ static int elf_output_file(TCCState *s1, const char *filename) /* Create the ELF file with name 'filename' */ ret = tcc_write_elf_file(s1, filename, phnum, phdr, file_offset, sec_order); the_end: - tcc_free(s1->symtab_to_dynsym); tcc_free(sec_order); tcc_free(phdr); - tcc_free(s1->sym_attrs); - s1->sym_attrs = NULL; return ret; } diff --git a/tests/libtcc_test.c b/tests/libtcc_test.c index 109e488..be5db61 100644 --- a/tests/libtcc_test.c +++ b/tests/libtcc_test.c @@ -15,9 +15,12 @@ int add(int a, int b) return a + b; } +const char hello[] = "Hello World!"; + char my_program[] = "#include \n" /* include the "Simple libc header for TCC" */ "extern int add(int a, int b);\n" +"extern const char hello[];\n" "int fib(int n)\n" "{\n" " if (n <= 2)\n" @@ -28,7 +31,7 @@ char my_program[] = "\n" "int foo(int n)\n" "{\n" -" printf(\"Hello World!\\n\");\n" +" printf(\"%s\\n\", hello);\n" " printf(\"fib(%d) = %d\\n\", n, fib(n));\n" " printf(\"add(%d, %d) = %d\\n\", n, 2 * n, add(n, 2 * n));\n" " return 0;\n" @@ -65,9 +68,10 @@ int main(int argc, char **argv) if (tcc_compile_string(s, my_program) == -1) return 1; - /* as a test, we add a symbol that the compiled program can use. + /* as a test, we add symbols that the compiled program can use. You may also open a dll with tcc_add_dll() and use symbols from that */ tcc_add_symbol(s, "add", add); + tcc_add_symbol(s, "hello", hello); /* relocate the code */ if (tcc_relocate(s, TCC_RELOCATE_AUTO) < 0) diff --git a/x86_64-link.c b/x86_64-link.c index 92404e4..9ff234b 100644 --- a/x86_64-link.c +++ b/x86_64-link.c @@ -14,8 +14,8 @@ #define ELF_START_ADDR 0x400000 #define ELF_PAGE_SIZE 0x200000 -#define HAVE_SECTION_RELOC #define PCRELATIVE_DLLPLT 1 +#define RELOCATE_DLLPLT 1 #else /* !TARGET_DEFS_ONLY */ @@ -36,6 +36,7 @@ int code_reloc (int reloc_type) case R_X86_64_GOT32: case R_X86_64_GLOB_DAT: case R_X86_64_COPY: + case R_X86_64_RELATIVE: return 0; case R_X86_64_PC32: @@ -57,6 +58,7 @@ int gotplt_entry_type (int reloc_type) case R_X86_64_GLOB_DAT: case R_X86_64_JUMP_SLOT: case R_X86_64_COPY: + case R_X86_64_RELATIVE: return NO_GOTPLT_ENTRY; case R_X86_64_32: @@ -80,6 +82,71 @@ int gotplt_entry_type (int reloc_type) return -1; } +ST_FUNC unsigned create_plt_entry(TCCState *s1, unsigned got_offset, struct sym_attr *attr) +{ + Section *plt = s1->plt; + uint8_t *p; + int modrm; + unsigned plt_offset, relofs; + + modrm = 0x25; + + /* empty PLT: create PLT0 entry that pushes the library indentifier + (GOT + PTR_SIZE) and jumps to ld.so resolution routine + (GOT + 2 * PTR_SIZE) */ + if (plt->data_offset == 0) { + p = section_ptr_add(plt, 16); + p[0] = 0xff; /* pushl got + PTR_SIZE */ + p[1] = modrm + 0x10; + write32le(p + 2, PTR_SIZE); + p[6] = 0xff; /* jmp *(got + PTR_SIZE * 2) */ + p[7] = modrm; + write32le(p + 8, PTR_SIZE * 2); + } + plt_offset = plt->data_offset; + + /* 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; + + /* Jump to GOT entry where ld.so initially put the address of ip + 4 */ + p = section_ptr_add(plt, 16); + p[0] = 0xff; /* jmp *(got + x) */ + p[1] = modrm; + 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)); + p[11] = 0xe9; /* jmp plt_start */ + write32le(p + 12, -(plt->data_offset)); + return plt_offset; +} + +/* relocate the PLT: compute addresses and offsets in the PLT now that final + address for PLT and GOT are known (see fill_program_header) */ +ST_FUNC void relocate_plt(TCCState *s1) +{ + uint8_t *p, *p_end; + + if (!s1->plt) + return; + + p = s1->plt->data; + p_end = p + s1->plt->data_offset; + + if (p < p_end) { + int x = s1->got->sh_addr - s1->plt->sh_addr - 6; + add32le(p + 2, x); + add32le(p + 8, x - 6); + p += 16; + while (p < p_end) { + add32le(p + 2, x + s1->plt->data - p); + p += 16; + } + } +} + static ElfW_Rel *qrel; /* ptr to next reloc entry reused */ void relocate_init(Section *sr) @@ -96,7 +163,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add switch (type) { case R_X86_64_64: if (s1->output_type == TCC_OUTPUT_DLL) { - esym_index = s1->symtab_to_dynsym[sym_index]; + esym_index = s1->sym_attrs[sym_index].dyn_index; qrel->r_offset = rel->r_offset; if (esym_index) { qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64); @@ -127,7 +194,7 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add case R_X86_64_PC32: if (s1->output_type == TCC_OUTPUT_DLL) { /* DLL relocation */ - esym_index = s1->symtab_to_dynsym[sym_index]; + esym_index = s1->sym_attrs[sym_index].dyn_index; if (esym_index) { qrel->r_offset = rel->r_offset; qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32); @@ -170,6 +237,9 @@ void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, add /* we load the got offset */ add32le(ptr, s1->sym_attrs[sym_index].got_offset); break; + case R_X86_64_RELATIVE: + /* do nothing */ + break; } }