aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile16
-rw-r--r--arm-link.c204
-rw-r--r--arm64-link.c104
-rw-r--r--c67-link.c35
-rw-r--r--i386-link.c90
-rw-r--r--libtcc.c5
-rw-r--r--tcc.h5
-rw-r--r--tccelf.c484
-rw-r--r--x86_64-link.c99
9 files changed, 552 insertions, 490 deletions
diff --git a/Makefile b/Makefile
index 10e0cd6..51fc08e 100644
--- a/Makefile
+++ b/Makefile
@@ -80,14 +80,14 @@ C67_CROSS = c67-tcc$(EXESUF)
CORE_FILES = tcc.c libtcc.c tccpp.c tccgen.c tccelf.c tccasm.c tccrun.c
CORE_FILES += tcc.h config.h libtcc.h tcctok.h
-I386_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h i386-tok.h
-WIN32_FILES = $(CORE_FILES) i386-gen.c i386-asm.c i386-asm.h i386-tok.h tccpe.c
-WIN64_FILES = $(CORE_FILES) x86_64-gen.c i386-asm.c x86_64-asm.h tccpe.c
-WINCE_FILES = $(CORE_FILES) arm-gen.c tccpe.c
-X86_64_FILES = $(CORE_FILES) x86_64-gen.c i386-asm.c x86_64-asm.h
-ARM_FILES = $(CORE_FILES) arm-gen.c
-ARM64_FILES = $(CORE_FILES) arm64-gen.c
-C67_FILES = $(CORE_FILES) c67-gen.c tcccoff.c
+I386_FILES = $(CORE_FILES) i386-gen.c i386-link.c i386-asm.c i386-asm.h i386-tok.h
+WIN32_FILES = $(CORE_FILES) i386-gen.c i386-link.c i386-asm.c i386-asm.h i386-tok.h tccpe.c
+WIN64_FILES = $(CORE_FILES) x86_64-gen.c x86_64-link.c i386-asm.c x86_64-asm.h tccpe.c
+WINCE_FILES = $(CORE_FILES) arm-gen.c arm-link.c tccpe.c
+X86_64_FILES = $(CORE_FILES) x86_64-gen.c x86_64-link.c i386-asm.c x86_64-asm.h
+ARM_FILES = $(CORE_FILES) arm-gen.c arm-link.c
+ARM64_FILES = $(CORE_FILES) arm64-gen.c arm64-link.c
+C67_FILES = $(CORE_FILES) c67-gen.c c67-link.c tcccoff.c
ifdef CONFIG_WIN32
PROGS+=tiny_impdef$(EXESUF) tiny_libmaker$(EXESUF) $(LIBTCC)
diff --git a/arm-link.c b/arm-link.c
new file mode 100644
index 0000000..00332e0
--- /dev/null
+++ b/arm-link.c
@@ -0,0 +1,204 @@
+#include "tcc.h"
+#define HAVE_SECTION_RELOC
+
+void relocate_init(Section *sr) {}
+
+void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
+{
+ ElfW(Sym) *sym;
+ int sym_index;
+
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+
+ switch(type) {
+ case R_ARM_PC24:
+ case R_ARM_CALL:
+ case R_ARM_JUMP24:
+ case R_ARM_PLT32:
+ {
+ int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko;
+ x = (*(int *) ptr) & 0xffffff;
+ if (sym->st_shndx == SHN_UNDEF ||
+ s1->output_type == TCC_OUTPUT_MEMORY)
+ val = s1->plt->sh_addr;
+#ifdef DEBUG_RELOC
+ printf ("reloc %d: x=0x%x val=0x%x ", type, x, val);
+#endif
+ (*(int *)ptr) &= 0xff000000;
+ if (x & 0x800000)
+ x -= 0x1000000;
+ x <<= 2;
+ blx_avail = (TCC_ARM_VERSION >= 5);
+ is_thumb = val & 1;
+ is_bl = (*(unsigned *) ptr) >> 24 == 0xeb;
+ is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl));
+ x += val - addr;
+#ifdef DEBUG_RELOC
+ printf (" newx=0x%x name=%s\n", x,
+ (char *) symtab_section->link->data + sym->st_name);
+#endif
+ h = x & 2;
+ th_ko = (x & 3) && (!blx_avail || !is_call);
+ if (th_ko || x >= 0x2000000 || x < -0x2000000)
+ tcc_error("can't relocate value at %x,%d",addr, type);
+ x >>= 2;
+ x &= 0xffffff;
+ /* Only reached if blx is avail and it is a call */
+ if (is_thumb) {
+ x |= h << 24;
+ (*(int *)ptr) = 0xfa << 24; /* bl -> blx */
+ }
+ (*(int *) ptr) |= x;
+ }
+ return;
+ /* Since these relocations only concern Thumb-2 and blx instruction was
+ introduced before Thumb-2, we can assume blx is available and not
+ guard its use */
+ case R_ARM_THM_PC22:
+ case R_ARM_THM_JUMP24:
+ {
+ int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
+ int to_thumb, is_call, to_plt, blx_bit = 1 << 12;
+ Section *plt;
+
+ /* weak reference */
+ if (sym->st_shndx == SHN_UNDEF &&
+ ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
+ return;
+
+ /* Get initial offset */
+ hi = (*(uint16_t *)ptr);
+ lo = (*(uint16_t *)(ptr+2));
+ s = (hi >> 10) & 1;
+ j1 = (lo >> 13) & 1;
+ j2 = (lo >> 11) & 1;
+ i1 = (j1 ^ s) ^ 1;
+ i2 = (j2 ^ s) ^ 1;
+ imm10 = hi & 0x3ff;
+ imm11 = lo & 0x7ff;
+ x = (s << 24) | (i1 << 23) | (i2 << 22) |
+ (imm10 << 12) | (imm11 << 1);
+ if (x & 0x01000000)
+ x -= 0x02000000;
+
+ /* 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_PC22);
+
+ /* 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 */
+ x = (x + 3) & -4; /* Compute offset from aligned PC */
+ }
+
+ /* Check that relocation is possible
+ * 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) or a jump to PLT */
+ if (!to_thumb || x >= 0x1000000 || x < -0x1000000)
+ if (to_thumb || (val & 2) || (!is_call && !to_plt))
+ tcc_error("can't relocate value at %x,%d",addr, type);
+
+ /* Compute and store final offset */
+ s = (x >> 24) & 1;
+ i1 = (x >> 23) & 1;
+ i2 = (x >> 22) & 1;
+ j1 = s ^ (i1 ^ 1);
+ j2 = s ^ (i2 ^ 1);
+ imm10 = (x >> 12) & 0x3ff;
+ imm11 = (x >> 1) & 0x7ff;
+ (*(uint16_t *)ptr) = (uint16_t) ((hi & 0xf800) |
+ (s << 10) | imm10);
+ (*(uint16_t *)(ptr+2)) = (uint16_t) ((lo & 0xc000) |
+ (j1 << 13) | blx_bit | (j2 << 11) |
+ imm11);
+ }
+ return;
+ case R_ARM_MOVT_ABS:
+ case R_ARM_MOVW_ABS_NC:
+ {
+ int x, imm4, imm12;
+ if (type == R_ARM_MOVT_ABS)
+ val >>= 16;
+ imm12 = val & 0xfff;
+ imm4 = (val >> 12) & 0xf;
+ x = (imm4 << 16) | imm12;
+ if (type == R_ARM_THM_MOVT_ABS)
+ *(int *)ptr |= x;
+ else
+ *(int *)ptr += x;
+ }
+ return;
+ case R_ARM_THM_MOVT_ABS:
+ case R_ARM_THM_MOVW_ABS_NC:
+ {
+ int x, i, imm4, imm3, imm8;
+ if (type == R_ARM_THM_MOVT_ABS)
+ val >>= 16;
+ imm8 = val & 0xff;
+ imm3 = (val >> 8) & 0x7;
+ i = (val >> 11) & 1;
+ imm4 = (val >> 12) & 0xf;
+ x = (imm3 << 28) | (imm8 << 16) | (i << 10) | imm4;
+ if (type == R_ARM_THM_MOVT_ABS)
+ *(int *)ptr |= x;
+ else
+ *(int *)ptr += x;
+ }
+ return;
+ case R_ARM_PREL31:
+ {
+ int x;
+ x = (*(int *)ptr) & 0x7fffffff;
+ (*(int *)ptr) &= 0x80000000;
+ x = (x * 2) / 2;
+ x += val - addr;
+ if((x^(x>>1))&0x40000000)
+ tcc_error("can't relocate value at %x,%d",addr, type);
+ (*(int *)ptr) |= x & 0x7fffffff;
+ }
+ case R_ARM_ABS32:
+ *(int *)ptr += val;
+ return;
+ case R_ARM_REL32:
+ *(int *)ptr += val - addr;
+ return;
+ case R_ARM_GOTPC:
+ *(int *)ptr += s1->got->sh_addr - addr;
+ return;
+ case R_ARM_GOTOFF:
+ *(int *)ptr += val - s1->got->sh_addr;
+ return;
+ case R_ARM_GOT32:
+ /* we load the got offset */
+ *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
+ return;
+ case R_ARM_COPY:
+ return;
+ case R_ARM_V4BX:
+ /* trade Thumb support for ARMv4 support */
+ if ((0x0ffffff0 & *(int*)ptr) == 0x012FFF10)
+ *(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */
+ return;
+ case R_ARM_GLOB_DAT:
+ case R_ARM_JUMP_SLOT:
+ *(addr_t *)ptr = val;
+ return;
+ case R_ARM_NONE:
+ /* Nothing to do. Normally used to indicate a dependency
+ on a certain symbol (like for exception handling under EABI). */
+ return;
+ default:
+ fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
+ return;
+ }
+}
diff --git a/arm64-link.c b/arm64-link.c
new file mode 100644
index 0000000..0364ba0
--- /dev/null
+++ b/arm64-link.c
@@ -0,0 +1,104 @@
+#include "tcc.h"
+#define HAVE_SECTION_RELOC
+
+void relocate_init(Section *sr) {}
+
+void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
+{
+ ElfW(Sym) *sym;
+ int sym_index;
+
+ sym_index = ELFW(R_SYM)(rel->r_info);
+ sym = &((ElfW(Sym) *)symtab_section->data)[sym_index];
+
+ switch(type) {
+ case R_AARCH64_ABS64:
+ write64le(ptr, val);
+ return;
+ case R_AARCH64_ABS32:
+ write32le(ptr, val);
+ return;
+ case R_AARCH64_MOVW_UABS_G0_NC:
+ write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
+ (val & 0xffff) << 5));
+ return;
+ case R_AARCH64_MOVW_UABS_G1_NC:
+ write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
+ (val >> 16 & 0xffff) << 5));
+ return;
+ case R_AARCH64_MOVW_UABS_G2_NC:
+ write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
+ (val >> 32 & 0xffff) << 5));
+ return;
+ case R_AARCH64_MOVW_UABS_G3:
+ write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
+ (val >> 48 & 0xffff) << 5));
+ return;
+ case R_AARCH64_ADR_PREL_PG_HI21: {
+ uint64_t off = (val >> 12) - (addr >> 12);
+ if ((off + ((uint64_t)1 << 20)) >> 21)
+ tcc_error("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
+ write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
+ (off & 0x1ffffc) << 3 | (off & 3) << 29));
+ return;
+ }
+ case R_AARCH64_ADD_ABS_LO12_NC:
+ write32le(ptr, ((read32le(ptr) & 0xffc003ff) |
+ (val & 0xfff) << 10));
+ return;
+ case R_AARCH64_JUMP26:
+ case R_AARCH64_CALL26:
+ /* This check must match the one in build_got_entries, testing
+ if we really need a PLT slot. */
+ if (sym->st_shndx == SHN_UNDEF ||
+ s1->output_type == TCC_OUTPUT_MEMORY)
+ /* We've put the PLT slot offset into r_addend when generating
+ it, and that's what we must use as relocation value (adjusted
+ by section offset of course). */
+ val = s1->plt->sh_addr + rel->r_addend;
+#ifdef DEBUG_RELOC
+ printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
+ (char *) symtab_section->link->data + sym->st_name);
+#endif
+ if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc)
+ {
+ tcc_error("R_AARCH64_(JUMP|CALL)26 relocation failed (val=%lx, addr=%lx)", addr, val);
+ }
+ write32le(ptr, (0x14000000 |
+ (uint32_t)(type == R_AARCH64_CALL26) << 31 |
+ ((val - addr) >> 2 & 0x3ffffff)));
+ return;
+ case R_AARCH64_ADR_GOT_PAGE: {
+ uint64_t off =
+ (((s1->got->sh_addr +
+ s1->sym_attrs[sym_index].got_offset) >> 12) - (addr >> 12));
+ if ((off + ((uint64_t)1 << 20)) >> 21)
+ tcc_error("R_AARCH64_ADR_GOT_PAGE relocation failed");
+ write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
+ (off & 0x1ffffc) << 3 | (off & 3) << 29));
+ return;
+ }
+ case R_AARCH64_LD64_GOT_LO12_NC:
+ write32le(ptr,
+ ((read32le(ptr) & 0xfff803ff) |
+ ((s1->got->sh_addr +
+ s1->sym_attrs[sym_index].got_offset) & 0xff8) << 7));
+ return;
+ case R_AARCH64_COPY:
+ return;
+ case R_AARCH64_GLOB_DAT:
+ case R_AARCH64_JUMP_SLOT:
+ /* They don't need addend */
+#ifdef DEBUG_RELOC
+ printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr,
+ val - rel->r_addend,
+ (char *) symtab_section->link->data + sym->st_name);
+#endif
+ write64le(ptr, val - rel->r_addend);
+ return;
+ default:
+ fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
+ return;
+ }
+}
diff --git a/c67-link.c b/c67-link.c
new file mode 100644
index 0000000..d8b0f7e
--- /dev/null
+++ b/c67-link.c
@@ -0,0 +1,35 @@
+#include "tcc.h"
+#define HAVE_SECTION_RELOC
+
+void relocate_init(Section *sr) {}
+
+void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
+{
+ switch(type) {
+ case R_C60_32:
+ *(int *)ptr += val;
+ break;
+ case R_C60LO16:
+ {
+ uint32_t orig;
+
+ /* put the low 16 bits of the absolute address add to what is
+ already there */
+ orig = ((*(int *)(ptr )) >> 7) & 0xffff;
+ orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16;
+
+ /* patch both at once - assumes always in pairs Low - High */
+ *(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) |
+ (((val+orig) & 0xffff) << 7);
+ *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) |
+ ((((val+orig)>>16) & 0xffff) << 7);
+ }
+ break;
+ case R_C60HI16:
+ break;
+ default:
+ fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
+ type, (unsigned) addr, ptr, (unsigned) val);
+ break;
+ }
+}
diff --git a/i386-link.c b/i386-link.c
new file mode 100644
index 0000000..b33882f
--- /dev/null
+++ b/i386-link.c
@@ -0,0 +1,90 @@
+#include "tcc.h"
+#define HAVE_SECTION_RELOC
+
+static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
+
+void relocate_init(Section *sr)
+{
+ qrel = (ElfW_Rel *) sr->data;
+}
+
+void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
+{
+ int sym_index, esym_index;
+
+ sym_index = ELFW(R_SYM)(rel->r_info);
+
+ switch (type) {
+ case R_386_32:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ qrel->r_offset = rel->r_offset;
+ if (esym_index) {
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
+ qrel++;
+ return;
+ } else {
+ qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
+ qrel++;
+ }
+ }
+ add32le(ptr, val);
+ return;
+ case R_386_PC32:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ /* DLL relocation */
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ if (esym_index) {
+ qrel->r_offset = rel->r_offset;
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
+ qrel++;
+ return;
+ }
+ }
+ add32le(ptr, val - addr);
+ return;
+ case R_386_PLT32:
+ add32le(ptr, val - addr);
+ return;
+ case R_386_GLOB_DAT:
+ case R_386_JMP_SLOT:
+ write32le(ptr, val);
+ return;
+ case R_386_GOTPC:
+ add32le(ptr, s1->got->sh_addr - addr);
+ return;
+ case R_386_GOTOFF:
+ add32le(ptr, val - s1->got->sh_addr);
+ return;
+ case R_386_GOT32:
+ case R_386_GOT32X:
+ /* we load the got offset */
+ add32le(ptr, s1->sym_attrs[sym_index].got_offset);
+ return;
+ case R_386_16:
+ if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
+ output_file:
+ tcc_error("can only produce 16-bit binary files");
+ }
+ write16le(ptr, read16le(ptr) + val);
+ return;
+ case R_386_PC16:
+ if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
+ goto output_file;
+ write16le(ptr, read16le(ptr) + val - addr);
+ return;
+ case R_386_RELATIVE:
+ /* do nothing */
+ return;
+ case R_386_COPY:
+ /* This reloction must copy initialized data from the library
+ to the program .bss segment. Currently made like for ARM
+ (to remove noise of defaukt case). Is this true?
+ */
+ return;
+ default:
+ fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
+ type, (unsigned)addr, ptr, (unsigned)val);
+ return;
+ }
+}
diff --git a/libtcc.c b/libtcc.c
index 2f9c731..aff7dcc 100644
--- a/libtcc.c
+++ b/libtcc.c
@@ -41,18 +41,23 @@ ST_DATA struct TCCState *tcc_state;
#include "tccrun.c"
#ifdef TCC_TARGET_I386
#include "i386-gen.c"
+#include "i386-link.c"
#endif
#ifdef TCC_TARGET_ARM
#include "arm-gen.c"
+#include "arm-link.c"
#endif
#ifdef TCC_TARGET_ARM64
#include "arm64-gen.c"
+#include "arm64-link.c"
#endif
#ifdef TCC_TARGET_C67
#include "c67-gen.c"
+#include "c67-link.c"
#endif
#ifdef TCC_TARGET_X86_64
#include "x86_64-gen.c"
+#include "x86_64-link.c"
#endif
#ifdef CONFIG_TCC_ASM
#include "tccasm.c"
diff --git a/tcc.h b/tcc.h
index c7d6dd5..147cbac 100644
--- a/tcc.h
+++ b/tcc.h
@@ -1485,6 +1485,11 @@ 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 e9fa229..8147c2c 100644
--- a/tccelf.c
+++ b/tccelf.c
@@ -694,11 +694,8 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
int type, sym_index;
unsigned char *ptr;
addr_t val, addr;
-#if defined TCC_TARGET_I386 || defined TCC_TARGET_X86_64
- ElfW_Rel *qrel = (ElfW_Rel *) sr->data; /* ptr to next reloc entry reused */
- int esym_index;
-#endif
+ relocate_init(sr);
for_each_elem(sr, 0, rel, ElfW_Rel) {
ptr = s->data + rel->r_offset;
@@ -711,484 +708,7 @@ ST_FUNC void relocate_section(TCCState *s1, Section *s)
type = ELFW(R_TYPE)(rel->r_info);
addr = s->sh_addr + rel->r_offset;
- /* CPU specific */
- switch(type) {
-#if defined(TCC_TARGET_I386)
- case R_386_32:
- if (s1->output_type == TCC_OUTPUT_DLL) {
- esym_index = s1->symtab_to_dynsym[sym_index];
- qrel->r_offset = rel->r_offset;
- if (esym_index) {
- qrel->r_info = ELFW(R_INFO)(esym_index, R_386_32);
- qrel++;
- break;
- } else {
- qrel->r_info = ELFW(R_INFO)(0, R_386_RELATIVE);
- qrel++;
- }
- }
- add32le(ptr, val);
- break;
- case R_386_PC32:
- if (s1->output_type == TCC_OUTPUT_DLL) {
- /* DLL relocation */
- esym_index = s1->symtab_to_dynsym[sym_index];
- if (esym_index) {
- qrel->r_offset = rel->r_offset;
- qrel->r_info = ELFW(R_INFO)(esym_index, R_386_PC32);
- qrel++;
- break;
- }
- }
- add32le(ptr, val - addr);
- break;
- case R_386_PLT32:
- add32le(ptr, val - addr);
- break;
- case R_386_GLOB_DAT:
- case R_386_JMP_SLOT:
- write32le(ptr, val);
- break;
- case R_386_GOTPC:
- add32le(ptr, s1->got->sh_addr - addr);
- break;
- case R_386_GOTOFF:
- add32le(ptr, val - s1->got->sh_addr);
- break;
- case R_386_GOT32:
- case R_386_GOT32X:
- /* we load the got offset */
- add32le(ptr, s1->sym_attrs[sym_index].got_offset);
- break;
- case R_386_16:
- if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY) {
- output_file:
- tcc_error("can only produce 16-bit binary files");
- }
- write16le(ptr, read16le(ptr) + val);
- break;
- case R_386_PC16:
- if (s1->output_format != TCC_OUTPUT_FORMAT_BINARY)
- goto output_file;
- write16le(ptr, read16le(ptr) + val - addr);
- break;
-
-#ifdef TCC_TARGET_PE
- case R_386_RELATIVE: /* handled in pe_relocate_rva() */
- break;
-#endif
- case R_386_COPY:
- /* This reloction must copy initialized data from the library
- to the program .bss segment. Currently made like for ARM
- (to remove noise of defaukt case). Is this true?
- */
- break;
-
- default:
- fprintf(stderr,"FIXME: handle reloc type %d at %x [%p] to %x\n",
- type, (unsigned)addr, ptr, (unsigned)val);
- break;
-#elif defined(TCC_TARGET_ARM)
- case R_ARM_PC24:
- case R_ARM_CALL:
- case R_ARM_JUMP24:
- case R_ARM_PLT32:
- {
- int x, is_thumb, is_call, h, blx_avail, is_bl, th_ko;
- x = (*(int *) ptr) & 0xffffff;
- if (sym->st_shndx == SHN_UNDEF
- || s1->output_type == TCC_OUTPUT_MEMORY)
- val = s1->plt->sh_addr;
-#ifdef DEBUG_RELOC
- printf ("reloc %d: x=0x%x val=0x%x ", type, x, val);
-#endif
- (*(int *)ptr) &= 0xff000000;
- if (x & 0x800000)
- x -= 0x1000000;
- x <<= 2;
- blx_avail = (TCC_ARM_VERSION >= 5);
- is_thumb = val & 1;
- is_bl = (*(unsigned *) ptr) >> 24 == 0xeb;
- is_call = (type == R_ARM_CALL || (type == R_ARM_PC24 && is_bl));
- x += val - addr;
-#ifdef DEBUG_RELOC
- printf (" newx=0x%x name=%s\n", x,
- (char *) symtab_section->link->data + sym->st_name);
-#endif
- h = x & 2;
- th_ko = (x & 3) && (!blx_avail || !is_call);
- if (th_ko || x >= 0x2000000 || x < -0x2000000)
- tcc_error("can't relocate value at %x,%d",addr, type);
- x >>= 2;
- x &= 0xffffff;
- /* Only reached if blx is avail and it is a call */
- if (is_thumb) {
- x |= h << 24;
- (*(int *)ptr) = 0xfa << 24; /* bl -> blx */
- }
- (*(int *) ptr) |= x;
- }
- break;
- /* Since these relocations only concern Thumb-2 and blx instruction was
- introduced before Thumb-2, we can assume blx is available and not
- guard its use */
- case R_ARM_THM_PC22:
- case R_ARM_THM_JUMP24:
- {
- int x, hi, lo, s, j1, j2, i1, i2, imm10, imm11;
- int to_thumb, is_call, to_plt, blx_bit = 1 << 12;
- Section *plt;
-
- /* weak reference */
- if (sym->st_shndx == SHN_UNDEF &&
- ELFW(ST_BIND)(sym->st_info) == STB_WEAK)
- break;
-
- /* Get initial offset */
- hi = (*(uint16_t *)ptr);
- lo = (*(uint16_t *)(ptr+2));
- s = (hi >> 10) & 1;
- j1 = (lo >> 13) & 1;
- j2 = (lo >> 11) & 1;
- i1 = (j1 ^ s) ^ 1;
- i2 = (j2 ^ s) ^ 1;
- imm10 = hi & 0x3ff;
- imm11 = lo & 0x7ff;
- x = (s << 24) | (i1 << 23) | (i2 << 22) |
- (imm10 << 12) | (imm11 << 1);
- if (x & 0x01000000)
- x -= 0x02000000;
-
- /* 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_PC22);
-
- /* 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 */
- x = (x + 3) & -4; /* Compute offset from aligned PC */
- }
-
- /* Check that relocation is possible
- * 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) or a jump to PLT */
- if (!to_thumb || x >= 0x1000000 || x < -0x1000000)
- if (to_thumb || (val & 2) || (!is_call && !to_plt))
- tcc_error("can't relocate value at %x,%d",addr, type);
-
- /* Compute and store final offset */
- s = (x >> 24) & 1;
- i1 = (x >> 23) & 1;
- i2 = (x >> 22) & 1;
- j1 = s ^ (i1 ^ 1);
- j2 = s ^ (i2 ^ 1);
- imm10 = (x >> 12) & 0x3ff;
- imm11 = (x >> 1) & 0x7ff;
- (*(uint16_t *)ptr) = (uint16_t) ((hi & 0xf800) |
- (s << 10) | imm10);
- (*(uint16_t *)(ptr+2)) = (uint16_t) ((lo & 0xc000) |
- (j1 << 13) | blx_bit | (j2 << 11) |
- imm11);
- }
- break;
- case R_ARM_MOVT_ABS:
- case R_ARM_MOVW_ABS_NC:
- {
- int x, imm4, imm12;
- if (type == R_ARM_MOVT_ABS)
- val >>= 16;
- imm12 = val & 0xfff;
- imm4 = (val >> 12) & 0xf;
- x = (imm4 << 16) | imm12;
- if (type == R_ARM_THM_MOVT_ABS)
- *(int *)ptr |= x;
- else
- *(int *)ptr += x;
- }
- break;
- case R_ARM_THM_MOVT_ABS:
- case R_ARM_THM_MOVW_ABS_NC:
- {
- int x, i, imm4, imm3, imm8;
- if (type == R_ARM_THM_MOVT_ABS)
- val >>= 16;
- imm8 = val & 0xff;
- imm3 = (val >> 8) & 0x7;
- i = (val >> 11) & 1;
- imm4 = (val >> 12) & 0xf;
- x = (imm3 << 28) | (imm8 << 16) | (i << 10) | imm4;
- if (type == R_ARM_THM_MOVT_ABS)
- *(int *)ptr |= x;
- else
- *(int *)ptr += x;
- }
- break;
- case R_ARM_PREL31:
- {
- int x;
- x = (*(int *)ptr) & 0x7fffffff;
- (*(int *)ptr) &= 0x80000000;
- x = (x * 2) / 2;
- x += val - addr;
- if((x^(x>>1))&0x40000000)
- tcc_error("can't relocate value at %x,%d",addr, type);
- (*(int *)ptr) |= x & 0x7fffffff;
- }
- case R_ARM_ABS32:
- *(int *)ptr += val;
- break;
- case R_ARM_REL32:
- *(int *)ptr += val - addr;
- break;
- case R_ARM_GOTPC:
- *(int *)ptr += s1->got->sh_addr - addr;
- break;
- case R_ARM_GOTOFF:
- *(int *)ptr += val - s1->got->sh_addr;
- break;
- case R_ARM_GOT32:
- /* we load the got offset */
- *(int *)ptr += s1->sym_attrs[sym_index].got_offset;
- break;
- case R_ARM_COPY:
- break;
- case R_ARM_V4BX:
- /* trade Thumb support for ARMv4 support */
- if ((0x0ffffff0 & *(int*)ptr) == 0x012FFF10)
- *(int*)ptr ^= 0xE12FFF10 ^ 0xE1A0F000; /* BX Rm -> MOV PC, Rm */
- break;
- case R_ARM_GLOB_DAT:
- case R_ARM_JUMP_SLOT:
- *(addr_t *)ptr = val;
- break;
- case R_ARM_NONE:
- /* Nothing to do. Normally used to indicate a dependency
- on a certain symbol (like for exception handling under EABI). */
- break;
-#ifdef TCC_TARGET_PE
- case R_ARM_RELATIVE: /* handled in pe_relocate_rva() */
- break;
-#endif
- default:
- fprintf(stderr,"FIXME: handle reloc type %x at %x [%p] to %x\n",
- type, (unsigned)addr, ptr, (unsigned)val);
- break;
-#elif defined(TCC_TARGET_ARM64)
- case R_AARCH64_ABS64:
- write64le(ptr, val);
- break;
- case R_AARCH64_ABS32:
- write32le(ptr, val);
- break;
- case R_AARCH64_PREL32:
- write32le(ptr, val - addr);
- break;
- case R_AARCH64_MOVW_UABS_G0_NC:
- write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
- (val & 0xffff) << 5));
- break;
- case R_AARCH64_MOVW_UABS_G1_NC:
- write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
- (val >> 16 & 0xffff) << 5));
- break;
- case R_AARCH64_MOVW_UABS_G2_NC:
- write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
- (val >> 32 & 0xffff) << 5));
- break;
- case R_AARCH64_MOVW_UABS_G3:
- write32le(ptr, ((read32le(ptr) & 0xffe0001f) |
- (val >> 48 & 0xffff) << 5));
- break;
- case R_AARCH64_ADR_PREL_PG_HI21: {
- uint64_t off = (val >> 12) - (addr >> 12);
- if ((off + ((uint64_t)1 << 20)) >> 21)
- tcc_error("R_AARCH64_ADR_PREL_PG_HI21 relocation failed");
- write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
- (off & 0x1ffffc) << 3 | (off & 3) << 29));
- break;
- }
- case R_AARCH64_ADD_ABS_LO12_NC:
- write32le(ptr, ((read32le(ptr) & 0xffc003ff) |
- (val & 0xfff) << 10));
- break;
- case R_AARCH64_JUMP26:
- case R_AARCH64_CALL26:
- /* This check must match the one in build_got_entries, testing
- if we really need a PLT slot. */
- if (sym->st_shndx == SHN_UNDEF ||
- s1->output_type == TCC_OUTPUT_MEMORY)
- /* We've put the PLT slot offset into r_addend when generating
- it, and that's what we must use as relocation value (adjusted
- by section offset of course). */
- val = s1->plt->sh_addr + rel->r_addend;
-#ifdef DEBUG_RELOC
- printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr, val,
- (char *) symtab_section->link->data + sym->st_name);
-#endif
- if (((val - addr) + ((uint64_t)1 << 27)) & ~(uint64_t)0xffffffc)
- {
- tcc_error("R_AARCH64_(JUMP|CALL)26 relocation failed (val=%lx, addr=%lx)", addr, val);
- }
- write32le(ptr, (0x14000000 |
- (uint32_t)(type == R_AARCH64_CALL26) << 31 |
- ((val - addr) >> 2 & 0x3ffffff)));
- break;
- case R_AARCH64_ADR_GOT_PAGE: {
- uint64_t off =
- (((s1->got->sh_addr +
- s1->sym_attrs[sym_index].got_offset) >> 12) - (addr >> 12));
- if ((off + ((uint64_t)1 << 20)) >> 21)
- tcc_error("R_AARCH64_ADR_GOT_PAGE relocation failed");
- write32le(ptr, ((read32le(ptr) & 0x9f00001f) |
- (off & 0x1ffffc) << 3 | (off & 3) << 29));
- break;
- }
- case R_AARCH64_LD64_GOT_LO12_NC:
- write32le(ptr,
- ((read32le(ptr) & 0xfff803ff) |
- ((s1->got->sh_addr +
- s1->sym_attrs[sym_index].got_offset) & 0xff8) << 7));
- break;
- case R_AARCH64_COPY:
- break;
- case R_AARCH64_GLOB_DAT:
- case R_AARCH64_JUMP_SLOT:
- /* They don't need addend */
-#ifdef DEBUG_RELOC
- printf ("reloc %d @ 0x%lx: val=0x%lx name=%s\n", type, addr,
- val - rel->r_addend,
- (char *) symtab_section->link->data + sym->st_name);
-#endif
- write64le(ptr, val - rel->r_addend);
- break;
- default:
- 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:
- *(int *)ptr += val;
- break;
- case R_C60LO16:
- {
- uint32_t orig;
-
- /* put the low 16 bits of the absolute address
- add to what is already there */
-
- orig = ((*(int *)(ptr )) >> 7) & 0xffff;
- orig |= (((*(int *)(ptr+4)) >> 7) & 0xffff) << 16;
-
- /* patch both at once - assumes always in pairs Low - High */
-
- *(int *) ptr = (*(int *) ptr & (~(0xffff << 7)) ) | (((val+orig) & 0xffff) << 7);
- *(int *)(ptr+4) = (*(int *)(ptr+4) & (~(0xffff << 7)) ) | ((((val+orig)>>16) & 0xffff) << 7);
- }
- break;
- case R_C60HI16:
- break;
- default:
- 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:
- if (s1->output_type == TCC_OUTPUT_DLL) {
- esym_index = s1->symtab_to_dynsym[sym_index];
- qrel->r_offset = rel->r_offset;
- if (esym_index) {
- qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64);
- qrel->r_addend = rel->r_addend;
- qrel++;
- break;
- } else {
- qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
- qrel->r_addend = read64le(ptr) + val;
- qrel++;
- }
- }
- add64le(ptr, val);
- break;
- case R_X86_64_32:
- case R_X86_64_32S:
- if (s1->output_type == TCC_OUTPUT_DLL) {
- /* XXX: this logic may depend on TCC's codegen
- now TCC uses R_X86_64_32 even for a 64bit pointer */
- qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
- /* Use sign extension! */
- qrel->r_addend = (int)read32le(ptr) + val;
- qrel++;
- }
- add32le(ptr, val);
- break;
-
- case R_X86_64_PC32:
- if (s1->output_type == TCC_OUTPUT_DLL) {
- /* DLL relocation */
- esym_index = s1->symtab_to_dynsym[sym_index];
- if (esym_index) {
- qrel->r_offset = rel->r_offset;
- qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
- /* Use sign extension! */
- qrel->r_addend = (int)read32le(ptr) + rel->r_addend;
- qrel++;
- break;
- }
- }
- goto plt32pc32;
-
- case R_X86_64_PLT32:
- /* We've put the PLT slot offset into r_addend when generating
- it, and that's what we must use as relocation value (adjusted
- by section offset of course). */
- val = s1->plt->sh_addr + rel->r_addend;
- /* fallthrough. */
-
- plt32pc32:
- {
- long long diff;
- diff = (long long)val - addr;
- if (diff < -2147483648LL || diff > 2147483647LL) {
- tcc_error("internal error: relocation failed");
- }
- add32le(ptr, diff);
- }
- break;
- case R_X86_64_GLOB_DAT:
- case R_X86_64_JUMP_SLOT:
- /* They don't need addend */
- write64le(ptr, val - rel->r_addend);
- break;
- case R_X86_64_GOTPCREL:
- case R_X86_64_GOTPCRELX:
- case R_X86_64_REX_GOTPCRELX:
- add32le(ptr, s1->got->sh_addr - addr
- + s1->sym_attrs[sym_index].got_offset - 4);
- break;
- case R_X86_64_GOTTPOFF:
- add32le(ptr, val - s1->got->sh_addr);
- break;
- case R_X86_64_GOT32:
- /* we load the got offset */
- add32le(ptr, s1->sym_attrs[sym_index].got_offset);
- break;
-#ifdef TCC_TARGET_PE
- case R_X86_64_RELATIVE: /* handled in pe_relocate_rva() */
- break;
-#endif
-
-#else
-#error unsupported processor
-#endif
- }
+ relocate(s1, rel, type, ptr, addr, val);
}
/* if the relocation is allocated, we change its symbol table */
if (sr->sh_flags & SHF_ALLOC)
diff --git a/x86_64-link.c b/x86_64-link.c
new file mode 100644
index 0000000..2a9e9b0
--- /dev/null
+++ b/x86_64-link.c
@@ -0,0 +1,99 @@
+#include "tcc.h"
+#define HAVE_SECTION_RELOC
+
+static ElfW_Rel *qrel; /* ptr to next reloc entry reused */
+
+void relocate_init(Section *sr)
+{
+ qrel = (ElfW_Rel *) sr->data;
+}
+
+void relocate(TCCState *s1, ElfW_Rel *rel, int type, char *ptr, addr_t addr, addr_t val)
+{
+ int sym_index, esym_index;
+
+ sym_index = ELFW(R_SYM)(rel->r_info);
+
+ switch (type) {
+ case R_X86_64_64:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ qrel->r_offset = rel->r_offset;
+ if (esym_index) {
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_64);
+ qrel->r_addend = rel->r_addend;
+ qrel++;
+ break;
+ } else {
+ qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
+ qrel->r_addend = read64le(ptr) + val;
+ qrel++;
+ }
+ }
+ add64le(ptr, val);
+ break;
+ case R_X86_64_32:
+ case R_X86_64_32S:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ /* XXX: this logic may depend on TCC's codegen
+ now TCC uses R_X86_64_32 even for a 64bit pointer */
+ qrel->r_info = ELFW(R_INFO)(0, R_X86_64_RELATIVE);
+ /* Use sign extension! */
+ qrel->r_addend = (int)read32le(ptr) + val;
+ qrel++;
+ }
+ add32le(ptr, val);
+ break;
+
+ case R_X86_64_PC32:
+ if (s1->output_type == TCC_OUTPUT_DLL) {
+ /* DLL relocation */
+ esym_index = s1->symtab_to_dynsym[sym_index];
+ if (esym_index) {
+ qrel->r_offset = rel->r_offset;
+ qrel->r_info = ELFW(R_INFO)(esym_index, R_X86_64_PC32);
+ /* Use sign extension! */
+ qrel->r_addend = (int)read32le(ptr) + rel->r_addend;
+ qrel++;
+ break;
+ }
+ }
+ goto plt32pc32;
+
+ case R_X86_64_PLT32:
+ /* We've put the PLT slot offset into r_addend when generating
+ it, and that's what we must use as relocation value (adjusted
+ by section offset of course). */
+ val = s1->plt->sh_addr + rel->r_addend;
+ /* fallthrough. */
+
+ plt32pc32:
+ {
+ long long diff;
+ diff = (long long)val - addr;
+ if (diff < -2147483648LL || diff > 2147483647LL) {
+ tcc_error("internal error: relocation failed");
+ }
+ add32le(ptr, diff);
+ }
+ break;
+ case R_X86_64_GLOB_DAT:
+ case R_X86_64_JUMP_SLOT:
+ /* They don't need addend */
+ write64le(ptr, val - rel->r_addend);
+ break;
+ case R_X86_64_GOTPCREL:
+ case R_X86_64_GOTPCRELX:
+ case R_X86_64_REX_GOTPCRELX:
+ add32le(ptr, s1->got->sh_addr - addr +
+ s1->sym_attrs[sym_index].got_offset - 4);
+ break;
+ case R_X86_64_GOTTPOFF:
+ add32le(ptr, val - s1->got->sh_addr);
+ break;
+ case R_X86_64_GOT32:
+ /* we load the got offset */
+ add32le(ptr, s1->sym_attrs[sym_index].got_offset);
+ break;
+ }
+}