diff --git a/Makefile b/Makefile index 10e0cd62..51fc08e2 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 00000000..00332e08 --- /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 00000000..0364ba01 --- /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 00000000..d8b0f7e8 --- /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 00000000..b33882fa --- /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 2f9c7316..aff7dcc7 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 c7d6dd5f..147cbac1 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 e9fa2295..8147c2ce 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 00000000..2a9e9b03 --- /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; + } +}