diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3cf3dfb --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,43 @@ +name: Alpine Test CI + +on: + push: + branches: + - "tmp-host-riscv64" + +jobs: + build: + runs-on: ubuntu-latest + # defaults: + # run: + # shell: alpine.sh {0} + steps: + - name: Update & Upgrade + run: sudo apt update && sudo apt upgrade --yes + - name: Checkout repository + uses: actions/checkout@v4 + with: + submodules: 'true' + - name: Setup Alpine Linux v3.21 for riscv64 + uses: jirutka/setup-alpine@v1 + with: + arch: riscv64 + branch: v3.21 + + - name: Run script inside Alpine chroot with riscv64 emulation + run: uname -m + shell: alpine.sh {0} + - name: Install dependencies + run: | + apk add build-base gcc g++ cmake meson llvm18-dev clang18-dev lld18-dev pkgconf libressl-dev + ln -s /usr/bin/clang-18 /usr/bin/clang + shell: alpine.sh --root {0} + - name: Build + run: | + meson setup build/ -Dbuildtype=release + ninja -C build + ninja -C build test + cat build/meson-logs/meson-log.txt + shell: alpine.sh {0} + + diff --git a/.test/gdb/.gdbinit b/.test/gdb/.gdbinit new file mode 100644 index 0000000..6fe123b --- /dev/null +++ b/.test/gdb/.gdbinit @@ -0,0 +1,19 @@ +set history save +set breakpoint pending on +b connection.cc:197 +run +del +n +set follow-exec-mode new +# b dispatch.c:55 +# # b dispatch.c:77 +# c +# # s +# # finish +# # b rtld.c:296 +# # del 2 +b dispatch.c:128 +c +c +c + diff --git a/client/dispatch.c b/client/dispatch.c index 68164ee..548c327 100644 --- a/client/dispatch.c +++ b/client/dispatch.c @@ -14,6 +14,12 @@ // Prototype to make compilers happy. This is used in the assembly HHVM // dispatcher on x86-64 below. + +// #define DISPATCH_DEBUG + +#if defined DISPATCH_DEBUG +static uint64_t hunk_count = 0; +#endif uintptr_t resolve_func(struct CpuState*, uintptr_t, struct RtldPatchData*); static void @@ -119,6 +125,10 @@ inline void dispatch_cdecl(uint64_t* cpu_regs) { uintptr_t addr = cpu_regs[0]; uintptr_t hash = QUICK_TLB_HASH(addr); +#if defined DISPATCH_DEBUG + printf("hunk: %u\n", hunk_count); + hunk_count++; +#endif uintptr_t func = cpu_state->quick_tlb[hash][1]; if (UNLIKELY(cpu_state->quick_tlb[hash][0] != addr)) func = resolve_func(cpu_state, addr, NULL); diff --git a/client/emulate.c b/client/emulate.c index 60cef21..49ab6f4 100644 --- a/client/emulate.c +++ b/client/emulate.c @@ -248,6 +248,9 @@ handle_clone(struct State* state, struct clone_args* uargs, size_t usize) { // clone(flags, stack, parent_tid, tls, child_tid) ssize_t res = syscall(__NR_clone, flags, 0, args.parent_tid, args.tls, args.child_tid, 0); +#elif defined(__riscv) + ssize_t res = syscall(__NR_clone, flags, 0, args.parent_tid, args.tls, + args.child_tid, 0); #else #error "clone not implemented for target" #endif @@ -448,15 +451,20 @@ emulate_syscall(uint64_t* cpu_regs) { case 230: nr = __NR_clock_nanosleep; goto native; case 257: goto common_openat; case 260: nr = __NR_fchownat; goto native; + case 263: nr = __NR_unlinkat; goto native; + case 267: nr = __NR_readlinkat; goto native; case 268: nr = __NR_fchmodat; goto native; + case 269: nr = __NR_faccessat; goto native; case 270: nr = __NR_pselect6; goto native; case 271: nr = __NR_ppoll; goto native; case 273: nr = __NR_set_robust_list; goto native; case 274: nr = __NR_get_robust_list; goto native; + case 280: nr = __NR_utimensat; goto native; case 292: nr = __NR_dup3; goto native; case 293: nr = __NR_pipe2; goto native; case 302: nr = __NR_prlimit64; goto native; case 318: nr = __NR_getrandom; goto native; + case 439: nr = __NR_faccessat2; goto native; // Some are too old to work on newer platforms, but have replacements. case 2: // open @@ -474,7 +482,11 @@ emulate_syscall(uint64_t* cpu_regs) { res = syscall(__NR_pipe2, arg0, 0, 0, 0, 0, 0); break; case 82: // rename +#ifdef __riscv + res = syscall(__NR_renameat2, AT_FDCWD, arg0, AT_FDCWD, arg1, 0, 0); +#else res = syscall(__NR_renameat, AT_FDCWD, arg0, AT_FDCWD, arg1, 0, 0); +#endif break; case 83: // mkdir res = syscall(__NR_mkdirat, AT_FDCWD, arg0, arg1, 0, 0, 0); @@ -751,7 +763,18 @@ emulate_syscall_generic(struct CpuState* cpu_state, uint64_t* resp, uint64_t nr, } case 29: nr = __NR_ioctl; goto native; // TODO: catch dangerous commands case 35: nr = __NR_unlinkat; goto native; - case 38: nr = __NR_renameat; goto native; + case 38: +#ifdef __riscv +#ifdef SYS_renameat + nr = SYS_renameat; +#else + nr = __NR_renameat2; + arg5 = 0; +#endif +#else + nr = __NR_renameat; +#endif + goto native; case 46: nr = __NR_ftruncate; goto native; case 48: nr = __NR_faccessat; goto native; case 49: nr = __NR_chdir; goto native; diff --git a/client/main.c b/client/main.c index aab33fc..10ef38c 100644 --- a/client/main.c +++ b/client/main.c @@ -54,6 +54,8 @@ int main(int argc, char** argv) { state.tsc.tsc_stack_alignment = 8; #elif defined(__aarch64__) state.tsc.tsc_host_arch = EM_AARCH64; +#elif defined(__riscv) + state.tsc.tsc_host_arch = EM_RISCV; #else #error "Unsupported architecture!" #endif diff --git a/client/memory.c b/client/memory.c index 0c4eb81..3723b22 100644 --- a/client/memory.c +++ b/client/memory.c @@ -112,6 +112,8 @@ mem_write_code(void* dst, const void* src, size_t size) { __asm__ volatile("dsb ish"); } __asm__ volatile("isb"); +#elif defined(__riscv) + syscall(__NR_riscv_flush_icache, (uintptr_t)dst, (uintptr_t)((uintptr_t)dst + size), 0, 0, 0, 0); #else #error "Implement ICache flush for unknown target" #endif diff --git a/client/meson.build b/client/meson.build index 910d871..f299c49 100644 --- a/client/meson.build +++ b/client/meson.build @@ -18,13 +18,17 @@ endif client_c_args = ['-D_GNU_SOURCE', '-nostdlib', '-fno-builtin', '-fno-stack-protector', '-fomit-frame-pointer', '-fPIC'] -client_link_args = ['-nostdlib', '-nostartfiles', '-lgcc'] +client_link_args = ['-nostdlib', '-nostartfiles', '-lgcc', '-latomic'] cc = meson.get_compiler('c') -if cc.has_argument('-static-pie') - client_link_args += ['-static-pie'] +if host_machine.cpu_family() == 'riscv64' and cc.get_id() == 'gcc' and cc.version() == '12' + client_link_args += ['-static-pie', '-Wl,-static', '-Wl,-pie', '-Wl,--no-dynamic-linker', '-Wl,-z,text'] else - client_link_args += ['-Wl,-static', '-Wl,-pie', '-Wl,--no-dynamic-linker', '-Wl,-z,text'] + if cc.has_argument('-static-pie') + client_link_args += ['-static-pie'] + else + client_link_args += ['-Wl,-static', '-Wl,-pie', '-Wl,--no-dynamic-linker', '-Wl,-z,text'] + endif endif if host_machine.cpu_family() == 'x86_64' @@ -45,3 +49,6 @@ client = executable('instrew-client', sources, override_options: ['b_sanitize=none']) test('mathlib', executable('test_mathlib', 'math.c', c_args: ['-DTEST', '-fno-builtin']), protocol: 'tap') +# test('rtldlib', executable('test_rtldlib', +# 'rtld.c', include_directories: include_directories('.'), +# c_args: client_c_args + '-DTEST', link_args: client_link_args), protocol: 'tap') diff --git a/client/minilibc.c b/client/minilibc.c index 87f1738..9835ff7 100644 --- a/client/minilibc.c +++ b/client/minilibc.c @@ -282,6 +282,154 @@ get_thread_area(void) { return tp; } +#elif defined(__riscv) +#define R_RELATIVE R_RISCV_RELATIVE + +//TODO: maybe put this def to common? +#ifndef SA_RESTORER +#define SA_RESTORER 0x04000000 +#endif + +ASM_BLOCK( + .text; + .type _start, %function; + .weak _DYNAMIC; + .hidden _DYNAMIC; + .globl _start; +_start: + mv fp, x0; + mv a0, sp; + lla a1, _DYNAMIC; + andi sp, sp, 0xfffffffffffffff0; + jal __start_main; +); + +ASM_BLOCK( + .global __clone; + .type __clone, %function; +__clone: + // Save func and arg to stack + addi a1, a1, -16; + sd a0, 0(a1); + sd a3, 8(a1); + + // Call SYS_clone + mv a0, a2; + mv a2, a4; + mv a3, a5; + mv a4, a6; + li a7, 220; // SYS_clone + ecall; + + beqz a0, 1f; + // Parent + ret; + + // Child +1: ld a1, 0(sp); + ld a0, 8(sp); + jalr a1; + + // Exit + li a7, 93; // SYS_exit + ecall; +); + +ASM_BLOCK( + .globl __restore; + .type __restore, %function; +__restore: + li a0, __NR_rt_sigreturn; + ecall; +); + +static size_t syscall0(int n) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0"); + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7) : "memory"); + return a0; +} + +static size_t syscall1(int n, size_t a) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0") = a; + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7), "0"(a0) : "memory"); + return a0; +} + +static size_t syscall2(int n, size_t a, size_t b) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0") = a; + register size_t a1 __asm__("a1") = b; + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7), "0"(a0), "r"(a1) : "memory"); + return a0; +} + +static size_t syscall3(int n, size_t a, size_t b, size_t c) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0") = a; + register size_t a1 __asm__("a1") = b; + register size_t a2 __asm__("a2") = c; + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7), "0"(a0), "r"(a1), "r"(a2) : "memory"); + return a0; +} + +static size_t syscall4(int n, size_t a, size_t b, size_t c, size_t d) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0") = a; + register size_t a1 __asm__("a1") = b; + register size_t a2 __asm__("a2") = c; + register size_t a3 __asm__("a3") = d; + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7), "0"(a0), "r"(a1), "r"(a2), "r"(a3) : "memory"); + return a0; +} + +static size_t syscall5(int n, size_t a, size_t b, size_t c, size_t d, size_t e) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0") = a; + register size_t a1 __asm__("a1") = b; + register size_t a2 __asm__("a2") = c; + register size_t a3 __asm__("a3") = d; + register size_t a4 __asm__("a4") = e; + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7), "0"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4) : "memory"); + return a0; +} + +static size_t syscall6(int n, size_t a, size_t b, size_t c, size_t d, size_t e, size_t f) +{ + register size_t a7 __asm__("a7") = n; + register size_t a0 __asm__("a0") = a; + register size_t a1 __asm__("a1") = b; + register size_t a2 __asm__("a2") = c; + register size_t a3 __asm__("a3") = d; + register size_t a4 __asm__("a4") = e; + register size_t a5 __asm__("a5") = f; + __asm__ volatile ("ecall\n\t" : "+r"(a0) : "r"(a7), "0"(a0), "r"(a1), "r"(a2), "r"(a3), "r"(a4), "r"(a5) : "memory"); + return a0; +} + +int +set_thread_area(void* tp) { + __asm__ volatile ("mv %0, a0\n\t" + "li a0, 0\r\n" :: "r"(tp) : "memory"); + return 0; +} + +void* get_thread_area(void) { +#ifdef SYS_set_thread_area + void* tp; + syscall1(SYS_set_thread_area, tp); +#else + return (void*)-ENOSYS; +#endif +} + #else #error #endif @@ -740,7 +888,9 @@ sigaction(int num, const struct sigaction* restrict act, if (act) { kact = *act; kact.sa_flags |= SA_RESTORER; - kact.sa_restorer = __restore; +#ifndef __riscv + kact.sa_restorer = __restore; +#endif act = &kact; } return syscall4(__NR_rt_sigaction, num, (uintptr_t) act, (uintptr_t) oact, @@ -774,7 +924,9 @@ size_t getpagesize(void) { } // May be overriden by an architecture-specific implementation. +#ifndef __riscv __attribute__((weak)) GNU_FORCE_EXTERN +#endif void* memset(void* s, int c, size_t n) { unsigned char* sptr = s; for (; n > 0; n--, sptr++) diff --git a/client/plt.inc b/client/plt.inc index 35e1d44..cbc01d4 100644 --- a/client/plt.inc +++ b/client/plt.inc @@ -6,6 +6,24 @@ PLT_ENTRY("__divti3", __divti3) // libgcc PLT_ENTRY("__udivti3", __udivti3) // libgcc PLT_ENTRY("__modti3", __modti3) // libgcc PLT_ENTRY("__umodti3", __umodti3) // libgcc +#if defined(__riscv) +PLT_ENTRY("__muldi3", __muldi3) // libgcc +PLT_ENTRY("__multi3", __multi3) // libgcc +PLT_ENTRY("__umoddi3", __umoddi3) // libgcc +PLT_ENTRY("__udivdi3", __udivdi3) // libgcc +PLT_ENTRY("__divdi3", __divdi3) // libgcc +PLT_ENTRY("__moddi3", __moddi3) // libgcc +PLT_ENTRY("__atomic_fetch_add_4", __atomic_fetch_add_4) +PLT_ENTRY("__atomic_fetch_add_8", __atomic_fetch_add_8) +PLT_ENTRY("__atomic_fetch_sub_4", __atomic_fetch_sub_4) +PLT_ENTRY("__atomic_fetch_sub_8", __atomic_fetch_sub_8) +PLT_ENTRY("__atomic_fetch_or_4", __atomic_fetch_or_4) +PLT_ENTRY("__atomic_fetch_or_8", __atomic_fetch_or_8) +PLT_ENTRY("__atomic_exchange_4", __atomic_exchange_4) +PLT_ENTRY("__atomic_exchange_8", __atomic_exchange_8) +PLT_ENTRY("__atomic_compare_exchange_4", __atomic_compare_exchange_4) +PLT_ENTRY("__atomic_compare_exchange_8", __atomic_compare_exchange_8) +#endif //defined(__riscv) PLT_ENTRY("floorf", floorf) // math.c PLT_ENTRY("floor", floor) // math.c PLT_ENTRY("ceilf", ceilf) // math.c diff --git a/client/rtld.c b/client/rtld.c index 8429bd3..522e4da 100644 --- a/client/rtld.c +++ b/client/rtld.c @@ -21,6 +21,10 @@ #define EM_CURRENT EM_X86_64 #elif defined(__aarch64__) #define EM_CURRENT EM_AARCH64 +#elif defined(__riscv) +#define EM_CURRENT EM_RISCV +#else +#error #endif #define elf_check_arch(x) ((x)->e_machine == EM_CURRENT) @@ -28,6 +32,8 @@ ((val) >= -(1ll << (bits-1)) && (val) < (1ll << (bits-1))-1) #define CHECK_UNSIGNED_BITS(val,bits) ((val) < (1ull << (bits))-1) +// #define RTLD_DEBUG + static bool rtld_elf_signed_range(int64_t val, unsigned bits, const char* relinfo) { if (!CHECK_SIGNED_BITS(val, bits)) { @@ -86,6 +92,8 @@ static const struct PltEntry plt_entries[] = { #define PLT_FUNC_SIZE 8 #elif defined(__aarch64__) #define PLT_FUNC_SIZE 8 +#elif defined(__riscv) +#define PLT_FUNC_SIZE 16 #else #error "currently unsupported architecture" #endif @@ -117,6 +125,11 @@ plt_create(const struct DispatcherInfo* disp_info, void** out_plt) { #elif defined(__aarch64__) *((uint32_t*) code_ptr+0) = 0x58000011 | (offset << 3); // ldr x17, [pc+off] *((uint32_t*) code_ptr+1) = 0xd61f0220; // br x17 +#elif defined(__riscv) + *((uint32_t*) code_ptr+0) = 0x00000297 | ((offset + 0x800) & 0xfffff000); // auipc, t0, offset[31:12] + *((uint32_t*) code_ptr+1) = 0x0002b283 | (offset << 20); // ld t0, offset[11:0](t0) + *((uint32_t*) code_ptr+2) = 0x00028067; // jalr, x0, 0(t0) + *((uint32_t*) code_ptr+3) = 0x00000013; // nop #else #error #endif // defined(__x86_64__) @@ -162,6 +175,7 @@ rtld_patch_create_stub(Rtld* rtld, const struct RtldPatchData* patch_data, if (!rtld_elf_signed_range(jmptgtdiff - 4, 28, "R_AARCH64_JUMP26")) return -EINVAL; rtld_blend(stcode + 4, 0x03ffffff, (jmptgtdiff - 4) >> 2); +#elif defined(__riscv) #else #error "missing patch stub" #endif @@ -341,12 +355,21 @@ rtld_elf_add_stub(uintptr_t sym, uintptr_t* out_stub) { } #endif +struct RtldPendingReloc { + struct RtldPatchData patch_data; + int64_t sym; +}; + static int -rtld_reloc_at(const struct RtldPatchData* patch_data, void* tgt, void* sym) { +rtld_reloc_at(struct RtldPatchData* patch_data, void* tgt, void* sym, struct RtldPendingReloc **pending_relocs, uint64_t *pending_relocs_length) { uint64_t syma = (uintptr_t) sym + patch_data->addend; uint64_t pc = patch_data->patch_addr; int64_t prel_syma = syma - (int64_t) pc; +#if defined RTLD_DEBUG + printf("rel: %u, tgt: %p, sym: %p, addend: %p, syma: %p, pc: %p, prel_syma: %p\n", patch_data->rel_type, *(uint64_t*)tgt,(uintptr_t)sym, patch_data->addend, syma, pc, prel_syma); +#endif + switch (patch_data->rel_type) { #if defined(__x86_64__) case R_X86_64_PC64: @@ -437,15 +460,98 @@ rtld_reloc_at(const struct RtldPatchData* patch_data, void* tgt, void* sym) { case R_AARCH64_MOVW_UABS_G3: rtld_blend(tgt, 0xffff << 5, syma >> 48 << 5); break; +#elif defined(__riscv) + case R_RISCV_32_PCREL: + if (!rtld_elf_signed_range(prel_syma, 32, "R_RISCV_32_PCREL")) + return -EINVAL; + rtld_blend(tgt, 0xffffffff, prel_syma); + break; + case R_RISCV_ADD32: + *((uint32_t*)tgt) += (uint32_t) syma; + break; + case R_RISCV_SUB32: + *((uint32_t*)tgt) -= (uint32_t) syma; + break; + case R_RISCV_CALL_PLT: + if (!rtld_elf_signed_range(prel_syma, 32, "R_RISCV_CALL_PLT")) + return -EINVAL; + rtld_blend(tgt + 4, 0xfff00000 , prel_syma << 20); + rtld_blend(tgt, 0xfffff000, prel_syma + 0x800); + break; + case R_RISCV_SET6: + rtld_blend(tgt, 0x3f, syma); + break; + case R_RISCV_SUB6: +// uint8_t *res = *((uint8_t*)tgt); +// *res = (res - ((uint8_t)syma & 0x3f)) & 0x3f; + *((uint8_t*)tgt) = (*(uint8_t*)tgt - ((uint8_t)syma & 0x3f)) & 0x3f; + break; + case R_RISCV_SET8: + rtld_blend(tgt, 0xff, syma); + break; + case R_RISCV_SUB8: + *((uint8_t*)tgt) -= (uint8_t)syma; + break; + case R_RISCV_SET16: + rtld_blend(tgt, 0xffff, syma); + break; + case R_RISCV_SUB16: + *((uint16_t*)tgt) -= (uint16_t)syma; + break; + case R_RISCV_PCREL_HI20: + if (!rtld_elf_signed_range(prel_syma, 32, "R_RISCV_PCREL_HI20")) + return -EINVAL; + rtld_blend(tgt, 0xfffff000, (prel_syma & 0x800) & 0xfffff000); + + struct RtldPendingReloc *pcrel_hi20_reloc = mem_alloc_data(sizeof(struct RtldPendingReloc), getpagesize()); + pcrel_hi20_reloc->patch_data = *patch_data; + pcrel_hi20_reloc->sym = (int64_t)prel_syma; + + pending_relocs[0] = pcrel_hi20_reloc; + (*pending_relocs_length)++; + break; + case R_RISCV_PCREL_LO12_I: + if (!rtld_elf_signed_range(prel_syma, 32, "R_RISCV_PCREL_LO12_I")) + return -EINVAL; + + if (pending_relocs && pending_relocs_length) { +#if defined RTLD_DEBUG + printf("pending_relocs != 0, pending_relocs_length != 0\n"); +#endif + for (int i = *pending_relocs_length; i >= 0; i--) { + if (!pending_relocs[i]) + continue; +#if defined RTLD_DEBUG + printf("pending_relocs[i] != 0, rel_type = %u\n", pending_relocs[i]->patch_data.rel_type); +#endif + if (pending_relocs[i]->patch_data.rel_type == R_RISCV_PCREL_HI20 && pending_relocs[i]->patch_data.patch_addr == (uintptr_t)sym) { +#if defined RTLD_DEBUG + printf("pending_relocs == R_RISCV_PCREL_HI20\n"); +#endif + prel_syma = pending_relocs[i]->sym; + } + } + } else { +#if defined RTLD_DEBUG + printf("!pending_relocs\n"); +#endif + } + rtld_blend(tgt, 0xfff00000, (prel_syma & 0xfff) << 20 ); + break; #endif default: dprintf(2, "unhandled relocation %u\n", patch_data->rel_type); return -EINVAL; } +#if defined RTLD_DEBUG + printf("tgt after: %p\n", *(uint64_t*)tgt); +#endif return 0; } + + static int rtld_elf_process_rela(RtldElf* re, int rela_idx) { if (rela_idx == 0 || rela_idx >= re->re_ehdr->e_shnum) @@ -469,6 +575,13 @@ rtld_elf_process_rela(RtldElf* re, int rela_idx) { unsigned symtab_idx = rela_shdr->sh_link; + size_t pending_relocs_count = rela_shdr->sh_size / sizeof(Elf64_Rela); +#if defined RTLD_DEBUG + printf("rela_shdr->sh_size: %u\n", pending_relocs_count); +#endif + struct RtldPendingReloc **pending_relocs = mem_alloc_data(pending_relocs_count * sizeof(struct RtldPendingReloc*), getpagesize()); + uint64_t pending_relocs_length = 0; + for (; elf_rela != elf_rela_end; ++elf_rela) { // TODO: ensure that size doesn't overflow if (elf_rela->r_offset >= tgt_shdr->sh_size) @@ -487,7 +600,8 @@ rtld_elf_process_rela(RtldElf* re, int rela_idx) { return -EINVAL; uint8_t* tgt = sec_write_addr + elf_rela->r_offset; - int retval = rtld_reloc_at(&reloc_patch, tgt, (void*) sym); + + int retval = rtld_reloc_at(&reloc_patch, tgt, (void*) sym, pending_relocs, &pending_relocs_length); if (retval < 0) return retval; } @@ -658,8 +772,8 @@ int rtld_add_object(Rtld* r, void* obj_base, size_t obj_size, uint64_t skew) { size_t totalign = 1; for (i = 0, elf_shnt = re.re_shdr; i < re.re_ehdr->e_shnum; i++, elf_shnt++) { // We don't support more flags - if (elf_shnt->sh_flags & ~(SHF_ALLOC|SHF_EXECINSTR|SHF_MERGE|SHF_STRINGS|SHF_INFO_LINK)) { - dprintf(2, "unsupported section flags\n"); + if (elf_shnt->sh_flags & ~(SHF_ALLOC|SHF_EXECINSTR|SHF_MERGE|SHF_STRINGS|SHF_INFO_LINK|SHF_WRITE)) { + dprintf(2, "unsupported section flags: %p\n", elf_shnt->sh_flags); return -EINVAL; } if (elf_shnt->sh_flags & SHF_ALLOC) { @@ -796,6 +910,6 @@ rtld_patch(struct RtldPatchData* patch_data, void* sym) { if (patch_data->rel_size > sizeof reloc_buf) return; memcpy(reloc_buf, (void*) patch_data->patch_addr, patch_data->rel_size); - (void) rtld_reloc_at(patch_data, reloc_buf, sym); + (void) rtld_reloc_at(patch_data, reloc_buf, sym, NULL, NULL); mem_write_code((void*) patch_data->patch_addr, reloc_buf, patch_data->rel_size); } diff --git a/server/codegenerator.cc b/server/codegenerator.cc index 797fb11..c695985 100644 --- a/server/codegenerator.cc +++ b/server/codegenerator.cc @@ -68,6 +68,10 @@ class CodeGenerator::impl { // The AArch64 target doesn't support the medium code model. cm = pic ? llvm::CodeModel::Large : llvm::CodeModel::Small; break; + case EM_RISCV: + triple = "riscv64-unknown-linux-gnu"; + cm = pic ? llvm::CodeModel::Medium : llvm::CodeModel::Small; + break; default: std::cerr << "unknown host architecture" << std::endl; abort(); diff --git a/subprojects/rellume b/subprojects/rellume index 3e84ecb..47122ae 160000 --- a/subprojects/rellume +++ b/subprojects/rellume @@ -1 +1 @@ -Subproject commit 3e84ecbca930650131068c95ba34e8bfb08cb3e8 +Subproject commit 47122ae5e9f3b5a4caf25768afae661bfee20b99 diff --git a/test/meson.build b/test/meson.build index 9fc9025..29a7672 100644 --- a/test/meson.build +++ b/test/meson.build @@ -35,7 +35,7 @@ foreach arch : ['aarch64', 'riscv64', 'x86_64'] depfile: name + '.d', command: testcc + ['-MD', '-MF', '@DEPFILE@', '-o', '@OUTPUT@', '@INPUT@'] + case.get('compile_args', [])) test(name, instrew, suite: [arch], - args: case.get('instrew_args', []) + [exec] + case.get('args', []), + args: case.get('instrew_args', []) + ['--stub=' + client.path()] + [exec] + case.get('args', []), should_fail: case.get('should_fail', false)) endforeach endforeach diff --git a/test/riscv64/fact_rv64.S b/test/riscv64/fact_rv64.S new file mode 100644 index 0000000..c8e8444 --- /dev/null +++ b/test/riscv64/fact_rv64.S @@ -0,0 +1,27 @@ +# +# a0-a2 - parameters to linux function services +# a7 - linux function number +# + +# Provide program starting address to linker +.global _start +_start: li a0, 5 + call fac + call print_int + addi a0, x0, 0 # return 0 + #addi a7, x0, 93 # Service command code 93 terminates + li a7, 93 # Service command code 93 terminates + ecall # Call linux to terminate the program + +fac: # a0 is argument and return value + li a1, 1 # 1 + ble a0, a1, .fac_exit + mv a5, a0 + li a0, 1 +.fac_loop: + mv a4, a5 + addi a5, a5, -1 + mul a0, a4, a0 + bne a5, a1, .fac_loop +.fac_exit: + ret diff --git a/test/riscv64/fib_nolc.c b/test/riscv64/fib_nolc.c new file mode 100644 index 0000000..37f30cc --- /dev/null +++ b/test/riscv64/fib_nolc.c @@ -0,0 +1,17 @@ +int rec(int n) { + if (n <= 0) + return 1; + return n + rec(n - 1); +} + +__attribute__((force_align_arg_pointer)) +void _start() { + int n = 5; + int res = rec(n); + asm("lb a0, %0\n" + "li a7, 93\n" + "ecall\n" :: "m"(res) + ); + __builtin_unreachable(); +} + diff --git a/test/riscv64/meson.build b/test/riscv64/meson.build index 336a994..ad0753e 100644 --- a/test/riscv64/meson.build +++ b/test/riscv64/meson.build @@ -3,4 +3,8 @@ triple = 'riscv64-linux-gnu' cases = [ {'name': 'exit', 'src': files('exit.S')}, {'name': 'lrsc-loop', 'src': files('lrsc-loop.S')}, + + {'name': 'fib_nolc', 'src': files('fib_nolc.c'), 'should_fail' : true}, + {'name': 'rec_nolibc', 'src': files('rec_nolibc.c'), 'should_fail' : true}, + {'name': 'fact_rv64', 'src': files('fact_rv64.S', 'rv64_runtime.S')}, ] diff --git a/test/riscv64/rec_nolibc.c b/test/riscv64/rec_nolibc.c new file mode 100644 index 0000000..5056a7e --- /dev/null +++ b/test/riscv64/rec_nolibc.c @@ -0,0 +1,17 @@ +int rec(int n) { + if (n <= 0) + return 1; + return n * rec(n - 1); +} + +__attribute__((force_align_arg_pointer)) +void _start() { + int n = 5; + int res = rec(n); + asm("lb a0, %0\n" + "li a7, 93\n" + "ecall\n" :: "m"(res) + ); + __builtin_unreachable(); +} + diff --git a/test/riscv64/rv64_runtime.S b/test/riscv64/rv64_runtime.S new file mode 100644 index 0000000..294f491 --- /dev/null +++ b/test/riscv64/rv64_runtime.S @@ -0,0 +1,129 @@ +.section .data +str2: .string "01234567012345670123456701234567\n" # 32+1 bytes +#str2: .string "01234567\n" # 32+1 bytes +NOT_IMPLEMENTED_STR: .string "Not implemented\n\0" +.equ BUFSIZE,32 +.globl str2 +.text + +.global not_implemented +not_implemented: + li a7, 64 # write on RISCV linux + li a0, 1 # stdout + la a1, NOT_IMPLEMENTED_STR + li a2, 16 + ecall + li a0, 1 + li a7, 93 + ecall + ret + +.global memset +memset: # a0 is addr, a1 is byte, a2 is length +memset_loop: + beq a2, zero, memset_fin + sb a1, (a0) + addi a0, a0, 1 + addi a2, a2, -1 + j memset_loop +memset_fin: + ret + +.global strlen +strlen: # a0 is a string, a0 is a result + li t0, 0 +strlen_loop: + ld t1, (a0) + bne zero, t1, strlen_fin + addi a0, a0, 1 + addi t0, t0, 1 + j strlen_loop +strlen_fin: + mv a0, t0 + ret + +.global memcpy +memcpy: # a0 dest, a1 src, a2 size. No intersection +memcpy_loop: + beq a2, zero, memcpy_fin + lb t0, (a1) + sb t0, (a0) + addi a0, a0, 1 + addi a1, a1, 1 + addi a2, a2, -1 + j memcpy_loop +memcpy_fin: + ret + +.global myitoa +myitoa: + # t2 is an arg, a0 is current pos, a1 is output len + li t1, 10 # t1 is basis + mv t2, a0 # argument + mv a1, zero # output length + la a0, str2 + addi a0, a0, BUFSIZE # 32 is preallocated space +myitoa_loop: + # + remu t4, t2, t1 + addi t4, t4, 0x30 # t4 stores one more char + addi a1, a1, 1 + addi a0, a0, -1 + sb t4, (a0) + divu t2, t2, t1 + bne t2, zero, myitoa_loop + ret + +.global trace_variable +trace_variable: + addi sp, sp, -8*4 + sd ra, 24(sp) # save ra beacuse we do calls IMPORTANT + sd a0, 16(sp) # a0 is varname + sd a1, 8(sp) # a1 is varname length + sd a2, (sp) # a2 is a value + # TODO: varname length + + la a0, str2 + li a1, 0x20 # space + li a2, BUFSIZE + call memset + + ld a0, (sp) + call myitoa + + la a0, str2 + ld a1, 16(sp) + ld a2, 8(sp) + call memcpy + + li a7, 64 # write on RISCV linux + li a0, 1 + la a1, str2 + li a2, BUFSIZE+1 + ecall + ld ra, 24(sp) + addi sp, sp, 8*4 + ret + +.global print_int +print_int: + addi sp, sp, -16 + sd a0, 8(sp) + sd ra, (sp) + la a0, str2 + li a1, 0x20 # space + li a2, BUFSIZE + call memset + + ld a0, 8(sp) + call myitoa + + li a7, 64 # write on RISCV linux + li a0, 1 + la a1, str2 + li a2, BUFSIZE+1 + ecall + + ld ra, (sp) + addi sp, sp, 16 + ret