mirror of
https://github.com/frida/tinycc
synced 2024-12-24 05:56:49 +03:00
tccelf: some linker cleanup
- generate and use SYM@PLT for plt addresses - get rid of patch_dynsym_undef hack (no idea what it did on FreeBSD) - use sym_attrs instead of symtab_to_dynsym - special case for function pointers into .so on i386 - libtcc_test: test tcc_add_symbol with data object - move target specicic code to *-link.c files - add R_XXX_RELATIVE (needed for PE)
This commit is contained in:
parent
fe6453f8f0
commit
ca92bfc3c6
65
arm-link.c
65
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)
|
||||
|
70
arm64-link.c
70
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)
|
||||
|
28
c67-link.c
28
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)
|
||||
|
75
i386-link.c
75
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);
|
||||
|
1
libtcc.c
1
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);
|
||||
}
|
||||
|
29
tcc.h
29
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
|
||||
|
555
tccelf.c
555
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;
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,12 @@ int add(int a, int b)
|
||||
return a + b;
|
||||
}
|
||||
|
||||
const char hello[] = "Hello World!";
|
||||
|
||||
char my_program[] =
|
||||
"#include <tcclib.h>\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)
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user