diff --git a/arm-link.c b/arm-link.c index 3e37ec8..54d0a10 100644 --- a/arm-link.c +++ b/arm-link.c @@ -166,6 +166,7 @@ ST_FUNC void relocate_plt(TCCState *s1) } void relocate_init(Section *sr) {} +void relocate_fini(Section *sr) {} void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) { diff --git a/arm64-link.c b/arm64-link.c index a46fffc..5e05788 100644 --- a/arm64-link.c +++ b/arm64-link.c @@ -153,6 +153,7 @@ ST_FUNC void relocate_plt(TCCState *s1) } void relocate_init(Section *sr) {} +void relocate_fini(Section *sr) {} void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) { diff --git a/c67-link.c b/c67-link.c index de72e44..48f9467 100644 --- a/c67-link.c +++ b/c67-link.c @@ -96,6 +96,7 @@ ST_FUNC void relocate_plt(TCCState *s1) } void relocate_init(Section *sr) {} +void relocate_fini(Section *sr) {} void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) { diff --git a/i386-link.c b/i386-link.c index e467a4e..8eddaa4 100644 --- a/i386-link.c +++ b/i386-link.c @@ -160,6 +160,10 @@ void relocate_init(Section *sr) qrel = (ElfW_Rel *) sr->data; } +void relocate_fini(Section *sr) +{ +} + void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) { int sym_index, esym_index; diff --git a/riscv64-link.c b/riscv64-link.c index 9e50c5a..5265acb 100644 --- a/riscv64-link.c +++ b/riscv64-link.c @@ -26,8 +26,27 @@ int code_reloc (int reloc_type) { switch (reloc_type) { + + case R_RISCV_BRANCH: + case R_RISCV_CALL: + case R_RISCV_JAL: + return 1; + + case R_RISCV_GOT_HI20: + case R_RISCV_PCREL_HI20: + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + case R_RISCV_32_PCREL: + case R_RISCV_ADD32: + case R_RISCV_SUB32: + case R_RISCV_32: + case R_RISCV_64: + return 0; + + case R_RISCV_CALL_PLT: + return 1; } - tcc_error ("Unknown relocation type: %d", reloc_type); + tcc_error ("Unknown relocation type in code_reloc: %d", reloc_type); return -1; } @@ -37,6 +56,28 @@ int code_reloc (int reloc_type) int gotplt_entry_type (int reloc_type) { switch (reloc_type) { + case R_RISCV_ALIGN: + case R_RISCV_RELAX: + case R_RISCV_RVC_BRANCH: + case R_RISCV_RVC_JUMP: + return NO_GOTPLT_ENTRY; + + case R_RISCV_BRANCH: + case R_RISCV_CALL: + case R_RISCV_PCREL_HI20: + case R_RISCV_PCREL_LO12_I: + case R_RISCV_PCREL_LO12_S: + case R_RISCV_32_PCREL: + case R_RISCV_ADD32: + case R_RISCV_SUB32: + case R_RISCV_32: + case R_RISCV_64: + case R_RISCV_JAL: + return AUTO_GOTPLT_ENTRY; + + case R_RISCV_GOT_HI20: + case R_RISCV_CALL_PLT: + return ALWAYS_GOTPLT_ENTRY; } tcc_error ("Unknown relocation type: %d", reloc_type); @@ -76,28 +117,31 @@ ST_FUNC void relocate_plt(TCCState *s1) 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); + uint64_t off = (got - plt + 0x800) >> 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, 0x0); - write32le(p + 4, 0x0); - write32le(p + 8, 0x0); - write32le(p + 12, 0x0); - write32le(p + 16, 0x0); - write32le(p + 20, 0x0); - write32le(p + 24, 0x0); - write32le(p + 28, 0x0); + write32le(p, 0x397 | (off << 12)); // auipc t2, %pcrel_hi(got) + write32le(p + 4, 0x41c30333); // sub t1, t1, t3 + write32le(p + 8, 0x0003be03 // ld t3, %pcrel_lo(got)(t2) + | (((got - plt) & 0xfff) << 20)); + write32le(p + 12, 0xfd430313); // addi t1, t1, -(32+12) + write32le(p + 16, 0x00038293 // addi t0, t2, %pcrel_lo(got) + | (((got - plt) & 0xfff) << 20)); + write32le(p + 20, 0x00135313); // srli t1, t1, log2(16/PTRSIZE) + write32le(p + 24, 0x0082b283); // ld t0, PTRSIZE(t0) + write32le(p + 28, 0x000e0067); // jr t3 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); + uint64_t off = (addr - pc + 0x800) >> 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, 0x0); - write32le(p + 4, 0x0); - write32le(p + 8, 0x0); - write32le(p + 12, 0x0); + write32le(p, 0xe17 | (off << 12)); // auipc t3, %pcrel_hi(func@got) + write32le(p + 4, 0x000e3e03 // ld t3, %pcrel_lo(func@got)(t3) + | (((addr - pc) & 0xfff) << 20)); + write32le(p + 8, 0x000e0367); // jalr t1, t3 + write32le(p + 12, 0x00000013); // nop p += 16; } } @@ -105,18 +149,154 @@ ST_FUNC void relocate_plt(TCCState *s1) void relocate_init(Section *sr) {} -void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) +void relocate_fini(Section *sr) { +} + +struct pcrel_hi { + addr_t addr, val; +}; +static struct pcrel_hi last_hi; + +void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, + addr_t addr, addr_t val) +{ + uint64_t off64; + uint32_t off32; int sym_index = ELFW(R_SYM)(rel->r_info); #ifdef DEBUG_RELOC ElfW(Sym) *sym = &((ElfW(Sym) *)symtab_section->data)[sym_index]; #endif switch(type) { - default: - fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n", - type, (unsigned)addr, ptr, (unsigned)val); - return; + case R_RISCV_ALIGN: + case R_RISCV_RELAX: + return; + + case R_RISCV_BRANCH: + off64 = val - addr; + if ((off64 + (1 << 12)) & ~(uint64_t)0x1ffe) + tcc_error("R_RISCV_BRANCH relocation failed" + " (val=%lx, addr=%lx)", val, addr); + off32 = off64 >> 1; + write32le(ptr, (read32le(ptr) & ~0xfe000f80) + | ((off32 & 0x800) << 20) + | ((off32 & 0x3f0) << 21) + | ((off32 & 0x00f) << 8) + | ((off32 & 0x400) >> 3)); + return; + case R_RISCV_JAL: + off64 = val - addr; + if ((off64 + (1 << 21)) & ~(((uint64_t)1 << 22) - 2)) + tcc_error("R_RISCV_BRANCH relocation failed" + " (val=%lx, addr=%lx)", val, addr); + off32 = off64; + write32le(ptr, (read32le(ptr) & 0xfff) + | (((off32 >> 12) & 0xff) << 12) + | (((off32 >> 11) & 1) << 20) + | (((off32 >> 1) & 0x3ff) << 21) + | (((off32 >> 20) & 1) << 31)); + return; + case R_RISCV_CALL: + case R_RISCV_CALL_PLT: + write32le(ptr, (read32le(ptr) & 0xfff) + | ((val - addr + 0x800) & ~0xfff)); + write32le(ptr + 4, (read32le(ptr + 4) & 0xfffff) + | (((val - addr) & 0xfff) << 20)); + return; + case R_RISCV_PCREL_HI20: + printf("PCREL_HI20: val=%lx addr=%lx\n", val, addr); + off64 = (int64_t)(val - addr + 0x800) >> 12; + if ((off64 + ((uint64_t)1 << 20)) >> 21) + tcc_error("R_RISCV_PCREL_HI20 relocation failed: off=%lx cond=%lx", + off64, ((int64_t)(off64 + ((uint64_t)1 << 20)) >> 21)); + write32le(ptr, (read32le(ptr) & 0xfff) + | ((off64 & 0xfffff) << 12)); + last_hi.addr = addr; + last_hi.val = val; + return; + case R_RISCV_GOT_HI20: + val = s1->got->sh_addr + get_sym_attr(s1, sym_index, 0)->got_offset; + off64 = (int64_t)(val - addr + 0x800) >> 12; + if ((off64 + ((uint64_t)1 << 20)) >> 21) + tcc_error("R_RISCV_GOT_HI20 relocation failed"); + last_hi.addr = addr; + last_hi.val = val; + write32le(ptr, (read32le(ptr) & 0xfff) + | ((off64 & 0xfffff) << 12)); + return; + case R_RISCV_PCREL_LO12_I: + printf("PCREL_LO12_I: val=%lx addr=%lx\n", val, addr); + if (val != last_hi.addr) + tcc_error("unsupported hi/lo pcrel reloc scheme"); + val = last_hi.val; + addr = last_hi.addr; + write32le(ptr, (read32le(ptr) & 0xfffff) + | (((val - addr) & 0xfff) << 20)); + return; + case R_RISCV_PCREL_LO12_S: + if (val != last_hi.addr) + tcc_error("unsupported hi/lo pcrel reloc scheme"); + val = last_hi.val; + addr = last_hi.addr; + off32 = val - addr; + write32le(ptr, (read32le(ptr) & ~0xfe000f80) + | ((off32 & 0xfe0) << 20) + | ((off32 & 0x01f) << 7)); + return; + + case R_RISCV_RVC_BRANCH: + off64 = (val - addr); + if ((off64 + (1 << 8)) & ~(uint64_t)0x1fe) + tcc_error("R_RISCV_RVC_BRANCH relocation failed" + " (val=%lx, addr=%lx)", val, addr); + off32 = off64; + write16le(ptr, (read16le(ptr) & 0xe383) + | (((off32 >> 5) & 1) << 2) + | (((off32 >> 1) & 3) << 3) + | (((off32 >> 6) & 3) << 5) + | (((off32 >> 3) & 3) << 10) + | (((off32 >> 8) & 1) << 12)); + return; + case R_RISCV_RVC_JUMP: + off64 = (val - addr); + if ((off64 + (1 << 11)) & ~(uint64_t)0xffe) + tcc_error("R_RISCV_RVC_BRANCH relocation failed" + " (val=%lx, addr=%lx)", val, addr); + off32 = off64; + write16le(ptr, (read16le(ptr) & 0xe003) + | (((off32 >> 5) & 1) << 2) + | (((off32 >> 1) & 7) << 3) + | (((off32 >> 7) & 1) << 6) + | (((off32 >> 6) & 1) << 7) + | (((off32 >> 10) & 1) << 8) + | (((off32 >> 8) & 3) << 9) + | (((off32 >> 4) & 1) << 11) + | (((off32 >> 11) & 1) << 12)); + return; + + case R_RISCV_32: + write32le(ptr, val); + return; + case R_RISCV_64: + write64le(ptr, val); + return; + case R_RISCV_ADD32: + write32le(ptr, read32le(ptr) + val); + return; + case R_RISCV_SUB32: + write32le(ptr, read32le(ptr) - val); + return; + + case R_RISCV_32_PCREL: + case R_RISCV_COPY: + /* XXX */ + return; + + default: + fprintf(stderr, "FIXME: handle reloc type %x at %x [%p] to %x\n", + type, (unsigned)addr, ptr, (unsigned)val); + return; } } #endif diff --git a/tcc.h b/tcc.h index fe3f642..4646b23 100644 --- a/tcc.h +++ b/tcc.h @@ -266,6 +266,8 @@ extern long double strtold (const char *__nptr, char **__endptr); # else # define CONFIG_TCC_ELFINTERP "/lib64/ld-linux-x86-64.so.2" # endif +# elif defined(TCC_TARGET_RISCV64) +# define CONFIG_TCC_ELFINTERP "/lib/ld-linux-riscv64-lp64d.so.1" # elif !defined(TCC_ARM_EABI) # if defined(TCC_MUSL) # if defined(TCC_TARGET_I386) diff --git a/tccelf.c b/tccelf.c index 37e2e3d..ae468c0 100644 --- a/tccelf.c +++ b/tccelf.c @@ -1232,6 +1232,13 @@ static void tcc_add_linker_symbols(TCCState *s1) bss_section->data_offset, 0, ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, bss_section->sh_num, "_end"); +#ifdef TCC_TARGET_RISCV64 + /* XXX should be .sdata+0x800, not .data+0x800 */ + set_elf_sym(symtab_section, + 0x800, 0, + ELFW(ST_INFO)(STB_GLOBAL, STT_NOTYPE), 0, + data_section->sh_num, "__global_pointer$"); +#endif #ifndef TCC_TARGET_PE /* horrible new standard ldscript defines */ add_init_array_defines(s1, ".preinit_array"); @@ -2541,6 +2548,9 @@ ST_FUNC int tcc_load_object_file(TCCState *s1, if (!sym_index && !sm->link_once #ifdef TCC_TARGET_ARM && type != R_ARM_V4BX +#elif defined TCC_TARGET_RISCV64 + && type != R_RISCV_ALIGN + && type != R_RISCV_RELAX #endif ) { invalid_reloc: diff --git a/tests/tcctest.c b/tests/tcctest.c index 691c7a2..5a959c2 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -3879,8 +3879,10 @@ void builtin_frame_address_test(void) char *fp0 = __builtin_frame_address(0); printf("str: %s\n", str); +#ifndef __riscv bfa1(str-fp0); #endif +#endif } char via_volatile (char i) diff --git a/x86_64-link.c b/x86_64-link.c index 9210a25..1b361a3 100644 --- a/x86_64-link.c +++ b/x86_64-link.c @@ -170,6 +170,10 @@ void relocate_init(Section *sr) qrel = (ElfW_Rel *) sr->data; } +void relocate_fini(Section *sr) +{ +} + void relocate(TCCState *s1, ElfW_Rel *rel, int type, unsigned char *ptr, addr_t addr, addr_t val) { int sym_index, esym_index;