From f36941bd21fff6a4f02f6ed792b6e733252932ac Mon Sep 17 00:00:00 2001 From: Gichoel Choi Date: Sun, 27 Aug 2023 17:30:48 +0900 Subject: [PATCH 1/3] arch/riscv64: Add basic build support for RISC-V64 This is a commit to ensure a successful build in a RISC-V 64bit environment, no functional behavior is implemented. Tested-by: Seonghee Jin Co-authored-by: Honggyu Kim Signed-off-by: Gichoel Choi --- arch/riscv64/Makefile | 34 +++++++++++++++++++++++ arch/riscv64/cpuinfo.c | 24 ++++++++++++++++ arch/riscv64/mcount-arch.h | 52 +++++++++++++++++++++++++++++++++++ arch/riscv64/mcount-support.c | 27 ++++++++++++++++++ arch/riscv64/mcount.S | 11 ++++++++ arch/riscv64/plthook.S | 11 ++++++++ cmds/info.c | 3 ++ cmds/record.c | 2 +- uftrace.c | 3 ++ utils/arch.h | 4 +++ utils/compiler.h | 23 ++++++++++++++++ 11 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 arch/riscv64/Makefile create mode 100644 arch/riscv64/cpuinfo.c create mode 100644 arch/riscv64/mcount-arch.h create mode 100644 arch/riscv64/mcount-support.c create mode 100644 arch/riscv64/mcount.S create mode 100644 arch/riscv64/plthook.S diff --git a/arch/riscv64/Makefile b/arch/riscv64/Makefile new file mode 100644 index 000000000..18f1cf420 --- /dev/null +++ b/arch/riscv64/Makefile @@ -0,0 +1,34 @@ +LINKFLAGS := -r -z noexecstack + +sdir := $(srcdir)/arch/riscv64 +odir := $(objdir)/arch/riscv64 + +include $(srcdir)/Makefile.include + +ARCH_ENTRY_SRC = $(wildcard $(sdir)/*.S) +ARCH_MCOUNT_SRC = $(wildcard $(sdir)/mcount-*.c) +ARCH_UFTRACE_SRC = $(sdir)/cpuinfo.c + +ARCH_MCOUNT_OBJS = $(patsubst $(sdir)/%.S,$(odir)/%.op,$(ARCH_ENTRY_SRC)) +ARCH_MCOUNT_OBJS += $(patsubst $(sdir)/%.c,$(odir)/%.op,$(ARCH_MCOUNT_SRC)) +ARCH_UFTRACE_OBJS = $(patsubst $(sdir)/%.c,$(odir)/%.o,$(ARCH_UFTRACE_SRC)) + +all: $(odir)/entry.op + +$(odir)/mcount-entry.op: $(ARCH_MCOUNT_OBJS) + $(QUIET_LINK)$(LD) $(LINKFLAGS) -o $@ $^ + +$(odir)/uftrace.o: $(ARCH_UFTRACE_OBJS) + $(QUIET_LINK)$(LD) $(LINKFLAGS) -o $@ $^ + +$(odir)/%.op: $(sdir)/%.S + $(QUIET_ASM)$(CC) $(LIB_CFLAGS) -c -o $@ $< + +$(odir)/%.op: $(sdir)/%.c + $(QUIET_CC_FPIC)$(CC) $(LIB_CFLAGS) -c -o $@ $< + +$(odir)/%.o: $(sdir)/%.c + $(QUIET_CC)$(CC) $(UFTRACE_CFLAGS) -c -o $@ $< + +clean: + $(RM) $(odir)/*.op $(odir)/*.o diff --git a/arch/riscv64/cpuinfo.c b/arch/riscv64/cpuinfo.c new file mode 100644 index 000000000..c6d58b9f5 --- /dev/null +++ b/arch/riscv64/cpuinfo.c @@ -0,0 +1,24 @@ +#include +#include + +int arch_fill_cpuinfo_model(int fd) +{ + char buf[1024]; + FILE *fp; + int ret = -1; + + fp = fopen("/proc/cpuinfo", "r"); + if (fp == NULL) + return -1; + + while (fgets(buf, sizeof(buf), fp) != NULL) { + if (!strncmp(buf, "isa\t\t: rv64", 11)) { + dprintf(fd, "cpuinfo:desc=RISCV64_%s", &buf[12]); + ret = 0; + break; + } + } + + fclose(fp); + return ret; +} diff --git a/arch/riscv64/mcount-arch.h b/arch/riscv64/mcount-arch.h new file mode 100644 index 000000000..990355eda --- /dev/null +++ b/arch/riscv64/mcount-arch.h @@ -0,0 +1,52 @@ +#ifndef MCOUNT_ARCH_H +#define MCOUNT_ARCH_H + +#define mcount_regs mcount_regs + +struct mcount_regs { + unsigned long a0; + unsigned long a1; + unsigned long a2; + unsigned long a3; + unsigned long a4; + unsigned long a5; + unsigned long a6; + unsigned long a7; +}; + +#define ARG1(x) ((x)->a0) +#define ARG2(x) ((x)->a1) +#define ARG3(x) ((x)->a2) +#define ARG4(x) ((x)->a3) +#define ARG5(x) ((x)->a4) +#define ARG6(x) ((x)->a5) +#define ARG7(x) ((x)->a6) +#define ARG8(x) ((x)->a7) + +#define ARCH_MAX_REG_ARGS 8 +#define ARCH_MAX_FLOAT_REGS 8 + +#define HAVE_MCOUNT_ARCH_CONTEXT +struct mcount_arch_context { + double f[ARCH_MAX_FLOAT_REGS]; +}; + +#if defined(__riscv_compressed) +#define NOP_INSN_SIZE 2 +#else +#define NOP_INSN_SIZE 4 +#endif + +/* TODO: not implemented yet (Start) */ +#define ARCH_PLT0_SIZE 0 +#define ARCH_PLTHOOK_ADDR_OFFSET 0 + +struct mcount_disasm_engine; +struct mcount_dynamic_info; +struct mcount_disasm_info; + +int disasm_check_insns(struct mcount_disasm_engine *disasm, struct mcount_dynamic_info *mdi, + struct mcount_disasm_info *info); +/* TODO: not implemented yet (End) */ + +#endif /* MCOUNT_ARCH_H */ diff --git a/arch/riscv64/mcount-support.c b/arch/riscv64/mcount-support.c new file mode 100644 index 000000000..53a872303 --- /dev/null +++ b/arch/riscv64/mcount-support.c @@ -0,0 +1,27 @@ +#include + +#include "libmcount/internal.h" +#include "utils/filter.h" +#include "utils/utils.h" + +/* TODO: not implemented yet (Start) */ +void mcount_arch_get_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + return; +} + +void mcount_arch_get_retval(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + return; +} + +void mcount_save_arch_context(struct mcount_arch_context *ctx) +{ + return; +} + +void mcount_restore_arch_context(struct mcount_arch_context *ctx) +{ + return; +} +/* TODO: not implemented yet (End) */ diff --git a/arch/riscv64/mcount.S b/arch/riscv64/mcount.S new file mode 100644 index 000000000..f0cd4fd39 --- /dev/null +++ b/arch/riscv64/mcount.S @@ -0,0 +1,11 @@ +#include "utils/asm.h" + +.text + +/* TODO: not implemented yet (Start) */ +GLOBAL(_mcount) +END(_mcount) + +ENTRY(mcount_return) +END(mcount_return) +/* TODO: not implemented yet (End) */ diff --git a/arch/riscv64/plthook.S b/arch/riscv64/plthook.S new file mode 100644 index 000000000..d8b7d5da1 --- /dev/null +++ b/arch/riscv64/plthook.S @@ -0,0 +1,11 @@ +#include "utils/asm.h" + +.text + +/* TODO: not implemented yet (Start) */ +ENTRY(plt_hooker) +END(plt_hooker) + +ENTRY(plthook_return) +END(plthook_return) +/* TODO: not implemented yet (End) */ diff --git a/cmds/info.c b/cmds/info.c index 25052d089..1949f9cad 100644 --- a/cmds/info.c +++ b/cmds/info.c @@ -281,6 +281,9 @@ static int read_cpuinfo(void *arg) else if (!strncmp(info->cpudesc, "ARM64", 5)) { handle->arch = UFT_CPU_AARCH64; } + else if (!strncmp(info->cpudesc, "RISCV64", 7)) { + handle->arch = UFT_CPU_RISCV64; + } else if (data_is_lp64(handle)) { handle->arch = UFT_CPU_X86_64; } diff --git a/cmds/record.c b/cmds/record.c index ec40ca179..959042b52 100644 --- a/cmds/record.c +++ b/cmds/record.c @@ -1589,7 +1589,7 @@ static void check_binary(struct uftrace_opts *opts) static char altname[PATH_MAX]; // for opts->exename to be persistent uint16_t e_type; uint16_t e_machine; - uint16_t supported_machines[] = { EM_X86_64, EM_ARM, EM_AARCH64, EM_386 }; + uint16_t supported_machines[] = { EM_X86_64, EM_ARM, EM_AARCH64, EM_386, EM_RISCV }; again: /* if it cannot be found in PATH, then fails inside */ diff --git a/uftrace.c b/uftrace.c index e4d86b11d..b4dc746f6 100644 --- a/uftrace.c +++ b/uftrace.c @@ -1389,7 +1389,10 @@ int main(int argc, char *argv[]) struct uftrace_opts opts = { .mode = UFTRACE_MODE_INVALID, .dirname = UFTRACE_DIR_NAME, +#if !defined(__riscv) + /* FIXME: disable libcall until PLT hooking is implemented in riscv64. */ .libcall = true, +#endif .bufsize = SHMEM_BUFFER_SIZE, .max_stack = OPT_RSTACK_DEFAULT, .port = UFTRACE_RECV_PORT, diff --git a/utils/arch.h b/utils/arch.h index 0813f1a0e..ff1893bfd 100644 --- a/utils/arch.h +++ b/utils/arch.h @@ -12,6 +12,7 @@ enum uftrace_cpu_arch { UFT_CPU_ARM, UFT_CPU_AARCH64, UFT_CPU_I386, + UFT_CPU_RISCV64, }; static inline enum uftrace_cpu_arch host_cpu_arch(void) @@ -24,6 +25,8 @@ static inline enum uftrace_cpu_arch host_cpu_arch(void) return UFT_CPU_AARCH64; #elif defined(__i386__) return UFT_CPU_I386; +#elif defined(__riscv) && __riscv_xlen == 64 + return UFT_CPU_RISCV64; #else return UFT_CPU_NONE; #endif @@ -34,6 +37,7 @@ static inline bool arch_is_lp64(enum uftrace_cpu_arch arch) switch (arch) { case UFT_CPU_X86_64: case UFT_CPU_AARCH64: + case UFT_CPU_RISCV64: return true; default: return false; diff --git a/utils/compiler.h b/utils/compiler.h index 5ffcfc6a9..a4762d860 100644 --- a/utils/compiler.h +++ b/utils/compiler.h @@ -37,6 +37,29 @@ #endif #endif +/* TODO: not implemented yet (Start) + * + * From RISC-V's "The RISC-V Instruction Set Manual, Volume I: User-Level + * ISA, Document Version 20191213", the G Extension consists of "I, M, A, + * F, D, Zicsr, Zifencei". + * + * In RISC-V, two instructions exist for memory barriers: the fence + * instruction, defined in the I Extension, and the fence.i instruction, + * defined in the Zifencei Extension. + * + * So the memory barrier commands we can use are fence, fence.i, and + * we'll have to figure out which one we need later when we implement + * the functions that call those macro functions. + * + */ +#if defined(__riscv) +#define cpu_relax() asm volatile("nop" ::: "memory") +#define full_memory_barrier() asm volatile("nop" ::: "memory") +#define read_memory_barrier() asm volatile("nop" ::: "memory") +#define write_memory_barrier() asm volatile("nop" ::: "memory") +#endif +/* TODO: not implemented yet (End) */ + /* ignore 'restrict' keyword if not supported (before C99) */ #if !defined(__STDC_VERSION__) || __STDC_VERSION__ < 199901L #define restrict From 0266055dd8aaea6bf6da6ab37f19336661560ef9 Mon Sep 17 00:00:00 2001 From: Gichoel Choi Date: Tue, 5 Sep 2023 22:29:41 +0900 Subject: [PATCH 2/3] arch/riscv64: Support mcount function for tracking In order to figure out the address of parent_loc, we need the frame pointer, but compiler optimization such as `-O2` in gcc removes the `fp` so we won't be able know where to change to hijack the return address to `mcount_return`. This problem only happens in gcc, but not in clang. To avoid the problem, `-fno-omit-frame-pointer` must be used when gcc optimization option is used in riscv64. Tested-by: Seonghee Jin Tested-by: Paran Lee Co-authored-by: Honggyu Kim Signed-off-by: Gichoel Choi --- arch/riscv64/mcount.S | 79 +++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 2 deletions(-) diff --git a/arch/riscv64/mcount.S b/arch/riscv64/mcount.S index f0cd4fd39..62cf13c2e 100644 --- a/arch/riscv64/mcount.S +++ b/arch/riscv64/mcount.S @@ -2,10 +2,85 @@ .text -/* TODO: not implemented yet (Start) */ GLOBAL(_mcount) + /* setup frame pointer & return address */ + addi sp, sp, -80 + sd ra, 72(sp) + sd fp, 64(sp) + addi fp, sp, 80 + + /* save arguments */ + sd a7, 56(sp) + sd a6, 48(sp) + sd a5, 40(sp) + sd a4, 32(sp) + sd a3, 24(sp) + sd a2, 16(sp) + sd a1, 8(sp) + sd a0, 0(sp) + + /* parent location */ + ld t1, 64(sp) + addi t1, t1, -8 + mv a0, t1 + + /* child addr */ + mv a1, ra + + /* mcount_args */ + mv a2, sp + + /* call mcount_entry func */ + call mcount_entry + + /* restore argunents */ + ld a0, 0(sp) + ld a1, 8(sp) + ld a2, 16(sp) + ld a3, 24(sp) + ld a4, 32(sp) + ld a5, 40(sp) + ld a6, 48(sp) + ld a7, 56(sp) + + /* restore frame pointer */ + ld fp, 64(sp) + ld ra, 72(sp) + + addi sp, sp, 80 + + ret END(_mcount) ENTRY(mcount_return) + /* setup frame pointer & return address */ + addi sp, sp, -32 + sd ra, 24(sp) + sd fp, 16(sp) + addi fp, sp, 32 + + /* save return values */ + sd a1, 8(sp) + sd a0, 0(sp) + + /* set the first argument of mcount_exit as pointer to return values */ + addi a0, sp, 0 + + /* call mcount_exit func */ + call mcount_exit + + mv t1, a0 + + /* restore return values */ + ld a0, 0(sp) + ld a1, 8(sp) + + /* restore frame pointer */ + ld fp, 16(sp) + ld ra, 24(sp) + + addi sp, sp, 32 + + /* call return address */ + jr t1 END(mcount_return) -/* TODO: not implemented yet (End) */ From bacf5451d1e12cf09b8e36be42d4e659e2e96fbf Mon Sep 17 00:00:00 2001 From: Gichoel Choi Date: Sun, 10 Sep 2023 01:20:37 +0900 Subject: [PATCH 3/3] arch/riscv64: Support argument handling feature This is work to support argument handling on the RISC-V 64-bit architecture. If you are curious about how RISC-V 64bit register numbers map to DWARF register numbers, please refer to the 'RISC-V Run-time ABI Specification' in 'RISC-V ELF psABI'. Tested-by: Seonghee Jin Tested-by: Jungmin Kim Tested-by: SeokMin Kwon Reviewed-by: Honggyu Kim Signed-off-by: Gichoel Choi --- arch/riscv64/mcount-support.c | 204 +++++++++++++++++++++++++++++++++- utils/arch.h | 24 ++++ utils/dwarf.c | 11 ++ utils/regs.c | 110 +++++++++++++++++- 4 files changed, 342 insertions(+), 7 deletions(-) diff --git a/arch/riscv64/mcount-support.c b/arch/riscv64/mcount-support.c index 53a872303..ba8fece48 100644 --- a/arch/riscv64/mcount-support.c +++ b/arch/riscv64/mcount-support.c @@ -4,24 +4,218 @@ #include "utils/filter.h" #include "utils/utils.h" -/* TODO: not implemented yet (Start) */ +static int mcount_get_register_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + struct mcount_regs *regs = ctx->regs; + int reg_idx; + + switch (spec->type) { + case ARG_TYPE_REG: + reg_idx = spec->reg_idx; + break; + case ARG_TYPE_INDEX: + reg_idx = spec->idx; /* for integer arguments */ + break; + case ARG_TYPE_FLOAT: + reg_idx = spec->idx + UFT_RISCV64_REG_FLOAT_BASE; + break; + case ARG_TYPE_STACK: + default: + return -1; + } + + ctx->val.i = 0; + + switch (reg_idx) { + case UFT_RISCV64_REG_A0: + ctx->val.i = ARG1(regs); + break; + case UFT_RISCV64_REG_A1: + ctx->val.i = ARG2(regs); + break; + case UFT_RISCV64_REG_A2: + ctx->val.i = ARG3(regs); + break; + case UFT_RISCV64_REG_A3: + ctx->val.i = ARG4(regs); + break; + case UFT_RISCV64_REG_A4: + ctx->val.i = ARG5(regs); + break; + case UFT_RISCV64_REG_A5: + ctx->val.i = ARG6(regs); + break; + case UFT_RISCV64_REG_A6: + ctx->val.i = ARG7(regs); + break; + case UFT_RISCV64_REG_A7: + ctx->val.i = ARG8(regs); + break; + case UFT_RISCV64_REG_FA0: + asm volatile("fsd fa0, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA1: + asm volatile("fsd fa1, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA2: + asm volatile("fsd fa2, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA3: + asm volatile("fsd fa3, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA4: + asm volatile("fsd fa4, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA5: + asm volatile("fsd fa5, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA6: + asm volatile("fsd fa6, %0\n" : "=m"(ctx->val.v)); + break; + case UFT_RISCV64_REG_FA7: + asm volatile("fsd fa7, %0\n" : "=m"(ctx->val.v)); + break; + default: + return -1; + } + + return 0; +} + +static void mcount_get_stack_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + int offset; + unsigned long *addr = ctx->stack_base; + + switch (spec->type) { + case ARG_TYPE_STACK: + offset = spec->stack_ofs; + break; + case ARG_TYPE_INDEX: + offset = spec->idx - ARCH_MAX_REG_ARGS; + break; + case ARG_TYPE_FLOAT: + offset = (spec->idx - ARCH_MAX_FLOAT_REGS) * 2 - 1; + break; + case ARG_TYPE_REG: + default: + /* should not reach here */ + pr_err_ns("invalid stack access for arguments\n"); + break; + } + + if (offset < 1 || offset > 100) { + pr_dbg("invalid stack offset: %d\n", offset); + mcount_memset4(ctx->val.v, 0, sizeof(ctx->val)); + return; + } + + addr += offset; + + if (check_mem_region(ctx, (unsigned long)addr)) { + /* save long double arguments properly */ + mcount_memcpy4(ctx->val.v, addr, ALIGN(spec->size, 4)); + } + else { + pr_dbg("stack address is not allowed: %p\n", addr); + mcount_memset4(ctx->val.v, 0, sizeof(ctx->val)); + } +} + +static void mcount_get_struct_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) +{ + struct uftrace_arg_spec reg_spec = { + .type = ARG_TYPE_REG, + }; + void *ptr = ctx->val.p; + int i; + + for (i = 0; i < spec->struct_reg_cnt; i++) { + reg_spec.reg_idx = spec->struct_regs[i]; + + mcount_get_register_arg(ctx, ®_spec); + mcount_memcpy4(ptr, ctx->val.v, sizeof(long)); + ptr += sizeof(long); + } + + if (spec->stack_ofs > 0) { + unsigned long *addr = ctx->stack_base + spec->stack_ofs; + + /* + * it cannot call mcount_get_stack_arg() since the struct + * might be bigger than the ctx->val. It directly updates + * the argument buffer (in the ptr). + */ + if (check_mem_region(ctx, (unsigned long)addr)) + mcount_memcpy4(ptr, addr, spec->size); + else { + pr_dbg("stack address is not allowed: %p\n", addr); + mcount_memset4(ptr, 0, spec->size); + } + } + else if (spec->struct_reg_cnt == 0) { + mcount_get_register_arg(ctx, spec); + mcount_memcpy4(ptr, ctx->val.v, sizeof(long)); + } +} + void mcount_arch_get_arg(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) { - return; + if (spec->fmt == ARG_FMT_STRUCT) { + mcount_get_struct_arg(ctx, spec); + return; + } + + if (mcount_get_register_arg(ctx, spec) < 0) + mcount_get_stack_arg(ctx, spec); } void mcount_arch_get_retval(struct mcount_arg_context *ctx, struct uftrace_arg_spec *spec) { - return; + if (spec->fmt == ARG_FMT_STRUCT) + mcount_memcpy4(ctx->val.v, ctx->retval, sizeof(long)); + /* type of return value cannot be FLOAT, so check format instead */ + else if (spec->fmt == ARG_FMT_FLOAT) { + long *float_retval = ctx->retval - 2; + + if (spec->size <= 4) { + asm volatile("flw fa0, %1\n" + "fsw fa0, %0\n" + : "=m"(ctx->val.v) + : "m"(*float_retval)); + } + else { + asm volatile("fld fa0, %1\n" + "fsd fa0, %0\n" + : "=m"(ctx->val.v) + : "m"(*float_retval)); + } + } + else + mcount_memcpy4(ctx->val.v, ctx->retval, spec->size); } void mcount_save_arch_context(struct mcount_arch_context *ctx) { - return; + asm volatile("fsd fa0, %0\n" : "=m"(ctx->f[0])); + asm volatile("fsd fa1, %0\n" : "=m"(ctx->f[1])); + asm volatile("fsd fa2, %0\n" : "=m"(ctx->f[2])); + asm volatile("fsd fa3, %0\n" : "=m"(ctx->f[3])); + asm volatile("fsd fa4, %0\n" : "=m"(ctx->f[4])); + asm volatile("fsd fa5, %0\n" : "=m"(ctx->f[5])); + asm volatile("fsd fa6, %0\n" : "=m"(ctx->f[6])); + asm volatile("fsd fa7, %0\n" : "=m"(ctx->f[7])); } void mcount_restore_arch_context(struct mcount_arch_context *ctx) { - return; + asm volatile("fld fa0, %0\n" ::"m"(ctx->f[0])); + asm volatile("fld fa1, %0\n" ::"m"(ctx->f[1])); + asm volatile("fld fa2, %0\n" ::"m"(ctx->f[2])); + asm volatile("fld fa3, %0\n" ::"m"(ctx->f[3])); + asm volatile("fld fa4, %0\n" ::"m"(ctx->f[4])); + asm volatile("fld fa5, %0\n" ::"m"(ctx->f[5])); + asm volatile("fld fa6, %0\n" ::"m"(ctx->f[6])); + asm volatile("fld fa7, %0\n" ::"m"(ctx->f[7])); } /* TODO: not implemented yet (End) */ diff --git a/utils/arch.h b/utils/arch.h index ff1893bfd..fb5d7eea3 100644 --- a/utils/arch.h +++ b/utils/arch.h @@ -157,6 +157,30 @@ enum uftrace_i386_reg_index { UFT_I386_REG_XMM7, }; +enum uftrace_riscv64_reg_index { + UFT_RISCV64_REG_INT_BASE = 0, + /* integer argument registers */ + UFT_RISCV64_REG_A0, + UFT_RISCV64_REG_A1, + UFT_RISCV64_REG_A2, + UFT_RISCV64_REG_A3, + UFT_RISCV64_REG_A4, + UFT_RISCV64_REG_A5, + UFT_RISCV64_REG_A6, + UFT_RISCV64_REG_A7, + + UFT_RISCV64_REG_FLOAT_BASE = 100, + /* floating-point argument registers */ + UFT_RISCV64_REG_FA0, + UFT_RISCV64_REG_FA1, + UFT_RISCV64_REG_FA2, + UFT_RISCV64_REG_FA3, + UFT_RISCV64_REG_FA4, + UFT_RISCV64_REG_FA5, + UFT_RISCV64_REG_FA6, + UFT_RISCV64_REG_FA7, +}; + int arch_register_number(enum uftrace_cpu_arch arch, char *reg_name); int arch_register_at(enum uftrace_cpu_arch arch, bool integer, int idx); int arch_register_index(enum uftrace_cpu_arch arch, int idx); diff --git a/utils/dwarf.c b/utils/dwarf.c index a4b2c9b94..1a796dd73 100644 --- a/utils/dwarf.c +++ b/utils/dwarf.c @@ -399,6 +399,13 @@ static void setup_arg_data(struct arg_data *ad, const char *name, struct uftrace ad->struct_arg_needs_ptr = true; /* struct return will use 'x8' register */ break; + case UFT_CPU_RISCV64: + ad->reg_max = 8; + ad->fpreg_max = 8; + ad->struct_arg_needs_ptr = true; + ad->struct_return_needs_ptr = true; + ad->struct_uses_fpreg = true; + break; default: /* TODO */ ad->broken = true; @@ -586,6 +593,10 @@ static void setup_param_data(struct param_data *data) case UFT_CPU_AARCH64: data->max_struct_size = 16 * 8; break; + case UFT_CPU_RISCV64: + data->max_struct_size = 16 * 8; + data->use_fpregs = true; + break; default: /* TODO */ break; diff --git a/utils/regs.c b/utils/regs.c index 08c74bb7d..9b3ba7382 100644 --- a/utils/regs.c +++ b/utils/regs.c @@ -131,8 +131,42 @@ static const struct uftrace_reg_table uft_i386_reg_table[] = { #undef X86_REG }; +static const struct uftrace_reg_table uft_riscv64_reg_table[] = { +#define RISCV64_REG(_r) \ + { \ +#_r, UFT_RISCV64_REG_##_r \ + } + + /* integer registers */ + RISCV64_REG(A0), + RISCV64_REG(A1), + RISCV64_REG(A2), + RISCV64_REG(A3), + RISCV64_REG(A4), + RISCV64_REG(A5), + RISCV64_REG(A6), + RISCV64_REG(A7), + + /* floating-point registers */ + RISCV64_REG(FA0), + RISCV64_REG(FA1), + RISCV64_REG(FA2), + RISCV64_REG(FA3), + RISCV64_REG(FA4), + RISCV64_REG(FA5), + RISCV64_REG(FA6), + RISCV64_REG(FA7), + +#undef RISCV64_REG +}; + static const struct uftrace_reg_table *arch_reg_tables[] = { - NULL, uft_x86_64_reg_table, uft_arm_reg_table, uft_aarch64_reg_table, uft_i386_reg_table, + NULL, + uft_x86_64_reg_table, + uft_arm_reg_table, + uft_aarch64_reg_table, + uft_i386_reg_table, + uft_riscv64_reg_table, }; static const size_t arch_reg_sizes[] = { @@ -141,11 +175,12 @@ static const size_t arch_reg_sizes[] = { ARRAY_SIZE(uft_arm_reg_table), ARRAY_SIZE(uft_aarch64_reg_table), ARRAY_SIZE(uft_i386_reg_table), + ARRAY_SIZE(uft_riscv64_reg_table), }; /* number of integer registers */ static const int arch_reg_int_sizes[] = { - 0, 6, 4, 8, 2, + 0, 6, 4, 8, 2, 8, }; /* returns uftrace register number for the architecture */ @@ -412,12 +447,82 @@ static const struct uftrace_reg_table uft_aarch64_dwarf_table[] = { static const struct uftrace_reg_table uft_i386_dwarf_table[] = {}; +#define RISCV64_REG_FP_BASE 32 +static const struct uftrace_reg_table uft_riscv64_dwarf_table[] = { + /* support registers used for arguments */ + { + "a0", + DW_OP_reg10, + }, + { + "a1", + DW_OP_reg11, + }, + { + "a2", + DW_OP_reg12, + }, + { + "a3", + DW_OP_reg13, + }, + { + "a4", + DW_OP_reg14, + }, + { + "a5", + DW_OP_reg15, + }, + { + "a6", + DW_OP_reg16, + }, + { + "a7", + DW_OP_reg17, + }, + { + "fa0", + RISCV64_REG_FP_BASE + 0, + }, + { + "fa1", + RISCV64_REG_FP_BASE + 1, + }, + { + "fa2", + RISCV64_REG_FP_BASE + 2, + }, + { + "fa3", + RISCV64_REG_FP_BASE + 3, + }, + { + "fa4", + RISCV64_REG_FP_BASE + 4, + }, + { + "fa5", + RISCV64_REG_FP_BASE + 5, + }, + { + "fa6", + RISCV64_REG_FP_BASE + 6, + }, + { + "fa7", + RISCV64_REG_FP_BASE + 7, + }, +}; + static const struct uftrace_reg_table *arch_dwarf_tables[] = { NULL, uft_x86_64_dwarf_table, uft_arm_dwarf_table, uft_aarch64_dwarf_table, uft_i386_dwarf_table, + uft_riscv64_dwarf_table, }; static const size_t arch_dwarf_sizes[] = { @@ -426,6 +531,7 @@ static const size_t arch_dwarf_sizes[] = { ARRAY_SIZE(uft_arm_dwarf_table), ARRAY_SIZE(uft_aarch64_dwarf_table), ARRAY_SIZE(uft_i386_dwarf_table), + ARRAY_SIZE(uft_riscv64_dwarf_table), }; const char *arch_register_dwarf_name(enum uftrace_cpu_arch arch, int dwarf_reg)