summaryrefslogtreecommitdiff
path: root/tccelf.c
diff options
context:
space:
mode:
Diffstat (limited to 'tccelf.c')
-rw-r--r--tccelf.c220
1 files changed, 123 insertions, 97 deletions
diff --git a/tccelf.c b/tccelf.c
index e5bb1cb..500ea18 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -84,7 +84,7 @@ static void rebuild_hash(Section *s, unsigned int nb_buckets)
}
/* return the symbol number */
-ST_FUNC int put_elf_sym(Section *s, uplong value, unsigned long size,
+ST_FUNC int put_elf_sym(Section *s, addr_t value, unsigned long size,
int info, int other, int shndx, const char *name)
{
int name_offset, sym_index;
@@ -157,7 +157,7 @@ ST_FUNC int find_elf_sym(Section *s, const char *name)
}
/* return elf symbol value, signal error if 'err' is nonzero */
-static void *get_elf_sym_addr(TCCState *s, const char *name, int err)
+ST_FUNC addr_t get_elf_sym_addr(TCCState *s, const char *name, int err)
{
int sym_index;
ElfW(Sym) *sym;
@@ -167,26 +167,28 @@ static void *get_elf_sym_addr(TCCState *s, const char *name, int err)
if (!sym_index || sym->st_shndx == SHN_UNDEF) {
if (err)
tcc_error("%s not defined", name);
- return NULL;
+ return 0;
}
- return (void*)(uplong)sym->st_value;
+ return sym->st_value;
}
/* return elf symbol value */
LIBTCCAPI void *tcc_get_symbol(TCCState *s, const char *name)
{
- return get_elf_sym_addr(s, name, 0);
+ return (void*)(uintptr_t)get_elf_sym_addr(s, name, 0);
}
+#ifdef TCC_IS_NATIVE
/* return elf symbol value or error */
-ST_FUNC void *tcc_get_symbol_err(TCCState *s, const char *name)
+ST_FUNC void* tcc_get_symbol_err(TCCState *s, const char *name)
{
- return get_elf_sym_addr(s, name, 1);
+ return (void*)get_elf_sym_addr(s, name, 1);
}
+#endif
/* add an elf symbol : check if it is already defined and patch
it. Return symbol index. NOTE that sh_num can be SHN_UNDEF. */
-ST_FUNC int add_elf_sym(Section *s, uplong value, unsigned long size,
+ST_FUNC int add_elf_sym(Section *s, addr_t value, unsigned long size,
int info, int other, int sh_num, const char *name)
{
ElfW(Sym) *esym;
@@ -238,7 +240,7 @@ ST_FUNC int add_elf_sym(Section *s, uplong value, unsigned long size,
} else if (s == tcc_state->dynsymtab_section) {
/* we accept that two DLL define the same symbol */
} else {
-#if 1
+#if 0
printf("new_bind=%x new_shndx=%x new_vis=%x old_bind=%x old_shndx=%x old_vis=%x\n",
sym_bind, sh_num, new_vis, esym_bind, esym->st_shndx, esym_vis);
#endif
@@ -431,12 +433,12 @@ ST_FUNC void relocate_syms(TCCState *s1, int do_resolve)
if (sh_num == SHN_UNDEF) {
name = strtab_section->data + sym->st_name;
if (do_resolve) {
-#if !defined TCC_TARGET_PE || !defined _WIN32
+#if defined TCC_IS_NATIVE && !defined _WIN32
void *addr;
name = symtab_section->link->data + sym->st_name;
addr = resolve_sym(s1, name);
if (addr) {
- sym->st_value = (uplong)addr;
+ sym->st_value = (addr_t)addr;
goto found;
}
#endif
@@ -469,10 +471,10 @@ ST_FUNC void relocate_syms(TCCState *s1, int do_resolve)
}
}
-#ifndef TCC_TARGET_PE
+#ifdef TCC_HAS_RUNTIME_PLTGOT
#ifdef TCC_TARGET_X86_64
#define JMP_TABLE_ENTRY_SIZE 14
-static uplong add_jmp_table(TCCState *s1, uplong val)
+static addr_t add_jmp_table(TCCState *s1, addr_t val)
{
char *p = s1->runtime_plt_and_got + s1->runtime_plt_and_got_offset;
s1->runtime_plt_and_got_offset += JMP_TABLE_ENTRY_SIZE;
@@ -480,30 +482,30 @@ static uplong add_jmp_table(TCCState *s1, uplong val)
p[0] = 0xff;
p[1] = 0x25;
*(int *)(p + 2) = 0;
- *(uplong *)(p + 6) = val;
- return (uplong)p;
+ *(addr_t *)(p + 6) = val;
+ return (addr_t)p;
}
-static uplong add_got_table(TCCState *s1, uplong val)
+static addr_t add_got_table(TCCState *s1, addr_t val)
{
- uplong *p = (uplong *)(s1->runtime_plt_and_got + s1->runtime_plt_and_got_offset);
- s1->runtime_plt_and_got_offset += sizeof(uplong);
+ addr_t *p = (addr_t *)(s1->runtime_plt_and_got + s1->runtime_plt_and_got_offset);
+ s1->runtime_plt_and_got_offset += sizeof(addr_t);
*p = val;
- return (uplong)p;
+ return (addr_t)p;
}
#elif defined TCC_TARGET_ARM
#define JMP_TABLE_ENTRY_SIZE 8
-static uplong add_jmp_table(TCCState *s1, int val)
+static addr_t add_jmp_table(TCCState *s1, int val)
{
uint32_t *p = (uint32_t *)(s1->runtime_plt_and_got + s1->runtime_plt_and_got_offset);
s1->runtime_plt_and_got_offset += JMP_TABLE_ENTRY_SIZE;
/* ldr pc, [pc, #-4] */
p[0] = 0xE51FF004;
p[1] = val;
- return (uplong)p;
+ return (addr_t)p;
}
#endif
-#endif
+#endif /* def TCC_HAS_RUNTIME_PLTGOT */
/* relocate a given section (CPU dependent) */
ST_FUNC void relocate_section(TCCState *s1, Section *s)
@@ -513,7 +515,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
ElfW(Sym) *sym;
int type, sym_index;
unsigned char *ptr;
- uplong val, addr;
+ addr_t val, addr;
#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
int esym_index;
#endif
@@ -581,7 +583,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break;
case R_386_GOT32:
/* we load the got offset */
- *(int *)ptr += s1->got_offsets[sym_index];
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break;
case R_386_16:
if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
@@ -612,13 +614,16 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
is_call = (type == R_ARM_CALL);
x += val - addr;
h = x & 2;
-#ifndef TCC_TARGET_PE
- if ((x & 3) || x >= 0x4000000 || x < -0x4000000)
- if (!(x & 3) || !blx_avail || !is_call)
- if (s1->output_type == TCC_OUTPUT_MEMORY)
+#ifdef TCC_HAS_RUNTIME_PLTGOT
+ if (s1->output_type == TCC_OUTPUT_MEMORY) {
+ if ((x & 3) || x >= 0x2000000 || x < -0x2000000)
+ if (!(x & 3) || !blx_avail || !is_call) {
x += add_jmp_table(s1, val) - val; /* add veneer */
+ is_thumb = 0; /* Veneer uses ARM instructions */
+ }
+ }
#endif
- if ((x & 3) || x >= 0x4000000 || x < -0x4000000)
+ if ((x & 3) || x >= 0x2000000 || x < -0x2000000)
if (!(x & 3) || !blx_avail || !is_call)
tcc_error("can't relocate value at %x",addr);
x >>= 2;
@@ -638,7 +643,8 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
case R_ARM_THM_JUMP24:
{
int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
- int to_thumb, is_call, blx_bit = 1 << 12;
+ int to_thumb, is_call, to_plt, blx_bit = 1 << 12;
+ Section *plt;
/* weak reference */
if (sym->st_shndx == SHN_UNDEF &&
@@ -662,9 +668,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
/* Relocation infos */
to_thumb = val & 1;
+ plt = s1->plt;
+ to_plt = (val >= plt->sh_addr) &&
+ (val < plt->sh_addr + plt->data_offset);
is_call = (type == R_ARM_THM_CALL);
/* Compute final offset */
+ if (to_plt && !is_call) /* Point to 1st instr of Thumb stub */
+ x -= 4;
x += val - addr;
if (!to_thumb && is_call) {
blx_bit = 0; /* bl -> blx */
@@ -675,9 +686,9 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
* offset must not be out of range
* if target is to be entered in arm mode:
- bit 1 must not set
- - instruction must be a call (bl) */
+ - instruction must be a call (bl) or a jump to PLT */
if (!to_thumb || x >= 0x1000000 || x < -0x1000000)
- if (to_thumb || (val & 2) || !is_call)
+ if (to_thumb || (val & 2) || (!is_call && !to_plt))
tcc_error("can't relocate value at %x",addr);
/* Compute and store final offset */
@@ -752,7 +763,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
break;
case R_ARM_GOT_BREL:
/* we load the got offset */
- *(int *)ptr += s1->got_offsets[sym_index];
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break;
case R_ARM_COPY:
break;
@@ -762,8 +773,8 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
*(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */
break;
default:
- fprintf(stderr,"FIXME: handle reloc type %x at %x [%.8x] to %x\n",
- type, (unsigned)addr, (unsigned)(uplong)ptr, (unsigned)val);
+ fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
break;
#elif defined(TCC_TARGET_C67)
case R_C60_32:
@@ -788,8 +799,8 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
case R_C60HI16:
break;
default:
- fprintf(stderr,"FIXME: handle reloc type %x at %x [%.8x] to %x\n",
- type, (unsigned)addr, (unsigned)(uplong)ptr, (unsigned)val);
+ fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
break;
#elif defined(TCC_TARGET_X86_64)
case R_X86_64_64:
@@ -829,7 +840,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
long long diff;
diff = (long long)val - addr;
if (diff <= -2147483647 || diff > 2147483647) {
-#ifndef TCC_TARGET_PE
+#ifdef TCC_HAS_RUNTIME_PLTGOT
/* XXX: naive support for over 32bit jump */
if (s1->output_type == TCC_OUTPUT_MEMORY) {
val = (add_jmp_table(s1, val - rel->r_addend) +
@@ -850,7 +861,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
*(int *)ptr = val - rel->r_addend;
break;
case R_X86_64_GOTPCREL:
-#ifndef TCC_TARGET_PE
+#ifdef TCC_HAS_RUNTIME_PLTGOT
if (s1->output_type == TCC_OUTPUT_MEMORY) {
val = add_got_table(s1, val - rel->r_addend) + rel->r_addend;
*(int *)ptr += val - addr;
@@ -858,14 +869,14 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
}
#endif
*(int *)ptr += (s1->got->sh_addr - addr +
- s1->got_offsets[sym_index] - 4);
+ s1->sym_attrs[sym_index].got_offset - 4);
break;
case R_X86_64_GOTTPOFF:
*(int *)ptr += val - s1->got->sh_addr;
break;
case R_X86_64_GOT32:
/* we load the got offset */
- *(int *)ptr += s1->got_offsets[sym_index];
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
break;
#else
#error unsupported processor
@@ -935,23 +946,23 @@ static int prepare_dynamic_rel(TCCState *s1, Section *sr)
return count;
}
-static void put_got_offset(TCCState *s1, int index, unsigned long val)
+static struct sym_attr *alloc_sym_attr(TCCState *s1, int index)
{
int n;
- unsigned long *tab;
+ struct sym_attr *tab;
- if (index >= s1->nb_got_offsets) {
+ if (index >= s1->nb_sym_attrs) {
/* find immediately bigger power of 2 and reallocate array */
n = 1;
while (index >= n)
n *= 2;
- tab = tcc_realloc(s1->got_offsets, n * sizeof(unsigned long));
- s1->got_offsets = tab;
- memset(s1->got_offsets + s1->nb_got_offsets, 0,
- (n - s1->nb_got_offsets) * sizeof(unsigned long));
- s1->nb_got_offsets = n;
+ tab = tcc_realloc(s1->sym_attrs, n * sizeof(*s1->sym_attrs));
+ s1->sym_attrs = tab;
+ memset(s1->sym_attrs + s1->nb_sym_attrs, 0,
+ (n - s1->nb_sym_attrs) * sizeof(*s1->sym_attrs));
+ s1->nb_sym_attrs = n;
}
- s1->got_offsets[index] = val;
+ return &s1->sym_attrs[index];
}
/* XXX: suppress that */
@@ -1015,11 +1026,11 @@ static void put_got_entry(TCCState *s1,
build_got(s1);
/* if a got entry already exists for that symbol, no need to add one */
- if (sym_index < s1->nb_got_offsets &&
- s1->got_offsets[sym_index] != 0)
+ if (sym_index < s1->nb_sym_attrs &&
+ s1->sym_attrs[sym_index].got_offset)
return;
-
- put_got_offset(s1, sym_index, s1->got->data_offset);
+
+ alloc_sym_attr(s1, sym_index)->got_offset = s1->got->data_offset;
if (s1->dynsym) {
sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
@@ -1096,10 +1107,16 @@ static void put_got_entry(TCCState *s1,
put32(p + 12, 0xe5bef008);
}
- p = section_ptr_add(plt, 16);
- put32(p , 0xe59fc004);
- put32(p+4, 0xe08fc00c);
- put32(p+8, 0xe59cf000);
+ if (s1->sym_attrs[sym_index].plt_thumb_stub) {
+ p = section_ptr_add(plt, 20);
+ put32(p , 0x4778); // bx pc
+ put32(p+2, 0x46c0); // nop
+ p += 4;
+ } else
+ p = section_ptr_add(plt, 16);
+ put32(p , 0xe59fc004); // ldr ip, [pc, #4] // offset in GOT
+ put32(p+4, 0xe08fc00c); // add ip, pc, ip // absolute address or offset
+ put32(p+8, 0xe59cf000); // ldr pc, [ip] // load absolute address or load offset
put32(p+12, s1->got->data_offset);
/* the symbol is modified so that it will be relocated to
@@ -1261,7 +1278,7 @@ ST_FUNC Section *new_symtab(TCCState *s1,
}
/* put dynamic tag */
-static void put_dt(Section *dynamic, int dt, uplong val)
+static void put_dt(Section *dynamic, int dt, addr_t val)
{
ElfW(Dyn) *dyn;
dyn = section_ptr_add(dynamic, sizeof(ElfW(Dyn)));
@@ -1297,13 +1314,6 @@ static void add_init_array_defines(TCCState *s1, const char *section_name)
s->sh_num, sym_end);
}
-static int tcc_add_support(TCCState *s1, const char *filename)
-{
- char buf[1024];
- snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
- return tcc_add_file(s1, buf);
-}
-
ST_FUNC void tcc_add_bcheck(TCCState *s1)
{
#ifdef CONFIG_TCC_BCHECK
@@ -1321,10 +1331,6 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1)
add_elf_sym(symtab_section, 0, 0,
ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0,
bounds_section->sh_num, "__bounds_start");
- /* add bound check code */
-#ifndef TCC_TARGET_PE
- tcc_add_support(s1, "bcheck.o");
-#endif
#ifdef TCC_TARGET_I386
if (s1->output_type != TCC_OUTPUT_MEMORY) {
/* add 'call __bound_init()' in .init section */
@@ -1340,6 +1346,13 @@ ST_FUNC void tcc_add_bcheck(TCCState *s1)
#endif
}
+static inline int tcc_add_support(TCCState *s1, const char *filename)
+{
+ char buf[1024];
+ snprintf(buf, sizeof(buf), "%s/%s", s1->tcc_lib_path, filename);
+ return tcc_add_file(s1, buf);
+}
+
/* add tcc runtime libraries */
ST_FUNC void tcc_add_runtime(TCCState *s1)
{
@@ -1483,9 +1496,9 @@ ST_FUNC void fill_got_entry(TCCState *s1, ElfW_Rel *rel)
ElfW(Sym) *sym = &((ElfW(Sym) *) symtab_section->data)[sym_index];
unsigned long offset;
- if (sym_index >= s1->nb_got_offsets)
+ if (sym_index >= s1->nb_sym_attrs)
return;
- offset = s1->got_offsets[sym_index];
+ 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 */
@@ -1531,7 +1544,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
int *section_order;
int shnum, i, phnum, file_offset, offset, size, j, sh_order_index, k;
long long tmp;
- uplong addr;
+ addr_t addr;
Section *strsec, *s;
ElfW(Shdr) shdr, *sh;
ElfW(Phdr) *phdr, *ph;
@@ -1539,9 +1552,9 @@ static int elf_output_file(TCCState *s1, const char *filename)
unsigned long saved_dynamic_data_offset;
ElfW(Sym) *sym;
int type, file_type;
- uplong rel_addr, rel_size;
+ addr_t rel_addr, rel_size;
#if defined(__FreeBSD__) || defined(__FreeBSD_kernel__)
- uplong bss_addr, bss_size;
+ addr_t bss_addr, bss_size;
#endif
file_type = s1->output_type;
@@ -1618,8 +1631,16 @@ static int elf_output_file(TCCState *s1, const char *filename)
esym = &((ElfW(Sym) *)s1->dynsymtab_section->data)[sym_index];
type = ELFW(ST_TYPE)(esym->st_info);
if ((type == STT_FUNC) || (type == STT_GNU_IFUNC)) {
+ /* Indirect functions shall have STT_FUNC type
+ * in executable dynsym section. Indeed, a dlsym
+ * call following a lazy resolution would pick
+ * the symbol value from the executable dynsym
+ * entry which would contain the address of the
+ * function wanted by the caller of dlsym
+ * instead of the address of the function that
+ * would return that address */
put_got_entry(s1, R_JMP_SLOT, esym->st_size,
- ELFW(ST_INFO)(STB_GLOBAL,type),
+ ELFW(ST_INFO)(STB_GLOBAL,STT_FUNC),
sym - (ElfW(Sym) *)symtab_section->data);
} else if (type == STT_OBJECT) {
unsigned long offset;
@@ -1719,8 +1740,9 @@ static int elf_output_file(TCCState *s1, const char *filename)
if ((ELFW(ST_TYPE)(sym->st_info) == STT_FUNC ||
ELFW(ST_TYPE)(sym->st_info) == STT_GNU_IFUNC)
&& sym->st_shndx == SHN_UNDEF) {
+ int visibility = ELFW(ST_BIND)(sym->st_info);
put_got_entry(s1, R_JMP_SLOT, sym->st_size,
- sym->st_info,
+ ELFW(ST_INFO)(visibility,STT_FUNC),
sym - (ElfW(Sym) *)symtab_section->data);
}
else if (ELFW(ST_TYPE)(sym->st_info) == STT_OBJECT) {
@@ -2057,8 +2079,10 @@ static int elf_output_file(TCCState *s1, const char *filename)
#elif defined(TCC_TARGET_ARM)
int x;
x=s1->got->sh_addr - s1->plt->sh_addr - 12;
- p +=16;
+ p += 16;
while (p < p_end) {
+ if (get32(p) == 0x46c04778) /* PLT Thumb stub present */
+ p += 4;
put32(p + 12, x + get32(p + 12) + s1->plt->data - p);
p += 16;
}
@@ -2150,7 +2174,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
/* XXX: ignore sections with allocated relocations ? */
for(i = 1; i < s1->nb_sections; i++) {
s = s1->sections[i];
- if (s->reloc && s != s1->got && (s->sh_flags & SHF_ALLOC)) //gr
+ if (s->reloc && s != s1->got)
relocate_section(s1, s);
}
@@ -2166,7 +2190,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
/* get entry point address */
if (file_type == TCC_OUTPUT_EXE)
- ehdr.e_entry = (uplong)tcc_get_symbol_err(s1, "_start");
+ ehdr.e_entry = get_elf_sym_addr(s1, "_start", 1);
else
ehdr.e_entry = text_section->sh_addr; /* XXX: is it correct ? */
}
@@ -2292,7 +2316,7 @@ static int elf_output_file(TCCState *s1, const char *filename)
tcc_free(s1->symtab_to_dynsym);
tcc_free(section_order);
tcc_free(phdr);
- tcc_free(s1->got_offsets);
+ tcc_free(s1->sym_attrs);
return ret;
}
@@ -2587,6 +2611,18 @@ ST_FUNC int tcc_load_object_file(TCCState *s1,
rel->r_info = ELFW(R_INFO)(sym_index, type);
/* offset the relocation offset */
rel->r_offset += offseti;
+#ifdef TCC_TARGET_ARM
+ /* Jumps and branches from a Thumb code to a PLT entry need
+ special handling since PLT entries are ARM code.
+ Unconditional bl instructions referencing PLT entries are
+ handled by converting these instructions into blx
+ instructions. Other case of instructions referencing a PLT
+ entry require to add a Thumb stub before the PLT entry to
+ switch to ARM mode. We set bit 0 of the got offset of a
+ symbol to indicate such a case. */
+ if (type == R_ARM_THM_JUMP24)
+ alloc_sym_attr(s1, sym_index)->plt_thumb_stub = 1;
+#endif
}
break;
default:
@@ -2955,20 +2991,6 @@ static int ld_next(TCCState *s1, char *name, int name_size)
return c;
}
-/*
- * Extract the file name from the library name
- *
- * /!\ No test on filename capacity, be careful
- */
-static void libname_to_filename(TCCState *s1, const char libname[], char filename[])
-{
- if (!s1->static_link) {
- sprintf(filename, "lib%s.so", libname);
- } else {
- sprintf(filename, "lib%s.a", libname);
- }
-}
-
static int ld_add_file(TCCState *s1, const char filename[])
{
int ret;
@@ -3015,8 +3037,12 @@ static int ld_add_file_list(TCCState *s1, const char *cmd, int as_needed)
ret = -1;
goto lib_parse_error;
}
- strcpy(libname, &filename[1]);
- libname_to_filename(s1, libname, filename);
+ pstrcpy(libname, sizeof libname, &filename[1]);
+ if (s1->static_link) {
+ snprintf(filename, sizeof filename, "lib%s.a", libname);
+ } else {
+ snprintf(filename, sizeof filename, "lib%s.so", libname);
+ }
} else if (t != LD_TOK_NAME) {
tcc_error_noabort("filename expected");
ret = -1;