Skip to content

Commit

Permalink
i#3544 RV64: Refine fault translation and signal handling
Browse files Browse the repository at this point in the history
Refine fault translation and signal handling, part of the linux.signalXXXX tests works on real hardware; however, they do not work on QEMU, so we can't enable them on CI.

Issue: #3544
  • Loading branch information
ksco committed Nov 17, 2023
1 parent 365b1a1 commit 60f56c9
Show file tree
Hide file tree
Showing 7 changed files with 123 additions and 28 deletions.
2 changes: 1 addition & 1 deletion core/arch/arch.h
Original file line number Diff line number Diff line change
Expand Up @@ -1569,7 +1569,7 @@ translate_x86_to_x64(dcontext_t *dcontext, instrlist_t *ilist,
DR_PARAM_INOUT instr_t **instr);
#endif

#ifdef AARCHXX
#if defined( AARCHXX) || defined(RISCV64)
bool
instr_is_ldstex_mangling(dcontext_t *dcontext, instr_t *inst);
#endif
Expand Down
41 changes: 41 additions & 0 deletions core/arch/riscv64/mangle.c
Original file line number Diff line number Diff line change
Expand Up @@ -648,6 +648,47 @@ mangle_special_registers(dcontext_t *dcontext, instrlist_t *ilist, instr_t *inst
return next_instr;
}

/***************************************************************************
* LR/SC sequence mangling.
*/

bool
instr_is_ldstex_mangling(dcontext_t *dcontext, instr_t *instr)
{
/* This should be kept in sync with mangle_exclusive_monitor_op(). */
if (!instr_is_our_mangling(instr))
return false;

opnd_t memop = opnd_create_null();
if (instr_get_opcode(instr) == OP_sd)
memop = instr_get_src(instr, 0);
else if (instr_get_opcode(instr) == OP_ld)
memop = instr_get_dst(instr, 0);
if (opnd_is_base_disp(memop)) {
ASSERT(opnd_get_index(memop) == DR_REG_NULL && opnd_get_scale(memop) == 0);
uint offs = opnd_get_disp(memop);
if (opnd_get_base(memop) == dr_reg_stolen && offs >= TLS_LRSC_ADDR_SLOT &&
offs <= TLS_LRSC_SIZE_SLOT)
return true;
}

ptr_int_t val;
if (instr_get_opcode(instr) == OP_fence || instr_get_opcode(instr) == OP_bne ||
instr_get_opcode(instr) == OP_bne ||
/* Check for sc.w/d+bne+jal pattern. */
(instr_get_opcode(instr) == OP_jal && instr_get_prev(instr) != NULL &&
instr_get_opcode(instr_get_prev(instr)) == OP_bne &&
instr_get_prev(instr_get_prev(instr)) != NULL &&
instr_is_exclusive_store(instr_get_prev(instr_get_prev(instr)))) ||
instr_is_exclusive_load(instr) || instr_is_exclusive_store(instr) ||
(instr_is_mov_constant(instr, &val) &&
(val == 1 /* cas fail */ || val == -1 /* reservation invalidation */ ||
val == 4 /* lr.w/sc.w size */ || val == 8 /* lr.d/sc.d size */)))
return true;

return false;
}

static instr_t *
mangle_exclusive_load(dcontext_t *dcontext, instrlist_t *ilist, instr_t *instr,
instr_t *next_instr)
Expand Down
2 changes: 1 addition & 1 deletion core/arch/x86_code.c
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ new_thread_setup(priv_mcontext_t *mc)
ASSERT(rc != -1); /* this better be a new thread */
dcontext = get_thread_private_dcontext();
ASSERT(dcontext != NULL);
# ifdef AARCHXX
# if defined(AARCHXX) || defined(RISCV64)
set_app_lib_tls_base_from_clone_record(dcontext, crec);
# endif
# ifdef ARM
Expand Down
27 changes: 17 additions & 10 deletions core/translate.c
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,8 @@ instr_is_inline_syscall_jmp(dcontext_t *dcontext, instr_t *inst)
/* A32 uses a regular jump */
instr_get_opcode(inst) == OP_b) &&
opnd_is_instr(instr_get_target(inst)));
# elif defined(RISCV64)
return (instr_get_opcode(inst) == OP_jal && opnd_is_instr(instr_get_target(inst)));
# else
ASSERT_NOT_IMPLEMENTED(false);
return false;
Expand Down Expand Up @@ -304,14 +306,14 @@ translate_walk_track_pre_instr(dcontext_t *tdcontext, instr_t *inst,
walk->unsupported_mangle = false;
walk->xsp_adjust = 0;
for (reg_id_t r = 0; r < REG_SPILL_NUM; r++) {
#ifndef AARCHXX
#ifdef X86
/* we should have seen a restore for every spill, unless at
* fragment-ending jump to ibl, which shouldn't come here
*/
ASSERT(walk->reg_spill_offs[r] == UINT_MAX);
walk->reg_spill_offs[r] = UINT_MAX; /* be paranoid */
#else
/* On AArchXX we do spill registers across app instrs and mangle
/* On AArchXX/RISCV64 we do spill registers across app instrs and mangle
* regions, though right now only the following routines do this:
* - mangle_stolen_reg()
* - mangle_gpr_list_read()
Expand Down Expand Up @@ -399,9 +401,6 @@ translate_walk_track_post_instr(dcontext_t *tdcontext, instr_t *inst,
* comment above for post-mangling traces), and so for local
* spills like rip-rel and ind branches this is fine.
*/
#if defined(RISCV64)
ASSERT_NOT_IMPLEMENTED(false);
#endif
if (instr_is_cti(inst) &&
#ifdef X86
/* Do not reset for a trace-cmp jecxz or jmp (32-bit) or
Expand All @@ -420,10 +419,7 @@ translate_walk_track_post_instr(dcontext_t *tdcontext, instr_t *inst,
(!opnd_is_pc(instr_get_target(inst)) ||
(opnd_get_pc(instr_get_target(inst)) >= walk->start_cache &&
opnd_get_pc(instr_get_target(inst)) < walk->end_cache))))
#elif defined(RISCV64)
/* FIXME i#3544: Not implemented */
false
#else
#elif defined(AARCHXX)
/* Do not reset for cbnz/bne in ldstex mangling, nor for the b after strex. */
!(instr_get_opcode(inst) == OP_cbnz ||
(instr_get_opcode(inst) == OP_b &&
Expand All @@ -432,6 +428,17 @@ translate_walk_track_post_instr(dcontext_t *tdcontext, instr_t *inst,
(instr_get_opcode(inst) == OP_b &&
(instr_get_prev(inst) != NULL &&
instr_is_exclusive_store(instr_get_prev(inst)))))
#elif defined(RISCV64)
/* Do not reset for bne in LR/SC mangling, nor for the jal after SC.
* This should be kept in sync with mangle_exclusive_monitor_op().
*/
!(instr_get_opcode(inst) == OP_bne ||
(instr_get_opcode(inst) == OP_jal && instr_get_prev(inst) != NULL &&
instr_get_opcode(instr_get_prev(inst)) == OP_bne &&
instr_get_prev(instr_get_prev(inst)) != NULL &&
instr_is_exclusive_store(instr_get_prev(instr_get_prev(inst)))))
#else
# error Unsupported architecture
#endif
) {
/* FIXME i#1551: add ARM version of the series of trace cti checks above */
Expand Down Expand Up @@ -547,7 +554,7 @@ translate_walk_track_post_instr(dcontext_t *tdcontext, instr_t *inst,
/* nothing to do */
}
#endif
#ifdef AARCHXX
#if defined(AARCHXX) || defined(RISCV64)
else if (instr_is_ldstex_mangling(tdcontext, inst)) {
/* nothing to do */
}
Expand Down
43 changes: 28 additions & 15 deletions core/unix/signal.c
Original file line number Diff line number Diff line change
Expand Up @@ -2826,7 +2826,6 @@ sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full,
if (TEST(DR_MC_INTEGER, flags)) {
mc->ra = sc->SC_FIELD(sc_regs.ra);
mc->gp = sc->SC_FIELD(sc_regs.gp);
mc->tp = sc->SC_FIELD(sc_regs.tp);
mc->t0 = sc->SC_FIELD(sc_regs.t0);
mc->t1 = sc->SC_FIELD(sc_regs.t1);
mc->t2 = sc->SC_FIELD(sc_regs.t2);
Expand Down Expand Up @@ -2921,6 +2920,13 @@ sigcontext_to_mcontext(priv_mcontext_t *mc, sig_full_cxt_t *sc_full,
* thread_get_mcontext() sets the dcontext isa_mode from cpsr, and thread_set_mcontext()
* sets it.
*/
/* XXX: on RISC-V, the tp register is a general-purpose register, but as its name
* suggests, it is conventionally used as the thread register.
* We save DR's tls by keeping tp's value unchanged, regardless in DR or in application.
* Therefore, when converting between sigcontext and mcontext, we should keep tp
* unchanged, otherwise for example when we sigreturn to app tp will change, causing an
* error.
*/
void
mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc,
dr_mcontext_flags_t flags)
Expand Down Expand Up @@ -2956,7 +2962,6 @@ mcontext_to_sigcontext(sig_full_cxt_t *sc_full, priv_mcontext_t *mc,
if (TEST(DR_MC_INTEGER, flags)) {
sc->SC_FIELD(sc_regs.ra) = mc->ra;
sc->SC_FIELD(sc_regs.gp) = mc->gp;
sc->SC_FIELD(sc_regs.tp) = mc->tp;
sc->SC_FIELD(sc_regs.t0) = mc->t0;
sc->SC_FIELD(sc_regs.t1) = mc->t1;
sc->SC_FIELD(sc_regs.t2) = mc->t2;
Expand Down Expand Up @@ -3051,20 +3056,22 @@ mcontext_to_ucontext(kernel_ucontext_t *uc, priv_mcontext_t *mc)
mcontext_to_sigcontext(&sc_full, mc, DR_MC_ALL);
}

#ifdef AARCHXX
#if defined(AARCHXX) || defined(RISCV64)
static void
set_sigcxt_stolen_reg(sigcontext_t *sc, reg_t val)
{
*(&sc->SC_R0 + (dr_reg_stolen - DR_REG_R0)) = val;
*(&sc->IF_AARCHXX_ELSE(SC_R0, SC_A0) +
(dr_reg_stolen - IF_AARCHXX_ELSE(DR_REG_R0, DR_REG_A0))) = val;
}

static reg_t
get_sigcxt_stolen_reg(sigcontext_t *sc)
{
return *(&sc->SC_R0 + (dr_reg_stolen - DR_REG_R0));
return *(&sc->IF_AARCHXX_ELSE(SC_R0, SC_A0) +
(dr_reg_stolen - IF_AARCHXX_ELSE(DR_REG_R0, DR_REG_A0)));
}

# ifndef AARCH64
# ifdef ARM
static dr_isa_mode_t
get_pc_mode_from_cpsr(sigcontext_t *sc)
{
Expand Down Expand Up @@ -3251,7 +3258,11 @@ thread_set_self_context(void *cxt)
#elif defined(ARM)
asm("ldr " ASM_XSP ", %0" : : "m"(xsp_for_sigreturn));
asm("b dynamorio_sigreturn");
#endif /* X86/AARCH64/ARM */
#elif defined(RISCV)
ASSERT_NOT_TESTED();
asm("addi " ASM_XSP ", %0, 0" : : "r"(xsp_for_sigreturn));
asm("j dynamorio_sigreturn");
#endif /* X86/AARCH64/ARM/RISCV64 */
ASSERT_NOT_REACHED();
}

Expand Down Expand Up @@ -3370,7 +3381,6 @@ sig_has_restorer(thread_sig_info_t *info, int sig)
# elif defined(RISCV64)
static const byte SIGRET_NONRT[8] = { 0 }; /* unused */
static const byte SIGRET_RT[8] = { 0 }; /* unused */
;
# endif
byte buf[MAX(sizeof(SIGRET_NONRT), sizeof(SIGRET_RT))] = { 0 };
if (d_r_safe_read(info->sighand->action[sig]->restorer, sizeof(buf), buf) &&
Expand Down Expand Up @@ -4626,7 +4636,7 @@ adjust_syscall_for_restart(dcontext_t *dcontext, thread_sig_info_t *info, int si
} else {
ASSERT_NOT_REACHED(); /* Inlined syscalls no longer come here. */
}
#ifdef AARCHXX
#if defined(AARCHXX) || defined(RISCV64)
/* dr_reg_stolen is holding DR's TLS on receiving a signal,
* so we need to put the app's reg value into the ucontext instead.
* The translation process normally does this for us, but here we're doing
Expand Down Expand Up @@ -4734,7 +4744,8 @@ find_next_fragment_from_gencode(dcontext_t *dcontext, sigcontext_t *sc)
if (f == NULL && sc->SC_XCX != 0)
f = fragment_lookup(dcontext, (app_pc)sc->SC_XCX);
#elif defined(RISCV64)
/* FIXME i#3544: Not implemented */
/* FIXME i#3544: Not implemented */
ASSERT_NOT_IMPLEMENTED(false);
#else
# error Unsupported arch.
#endif
Expand Down Expand Up @@ -6292,7 +6303,9 @@ execute_handler_from_dispatch(dcontext_t *dcontext, int sig)
dump_sigcontext(dcontext, sc);
LOG(THREAD, LOG_ASYNCH, 3, "\n");
}
IF_AARCHXX(ASSERT(get_sigcxt_stolen_reg(sc) != (reg_t)*get_dr_tls_base_addr()));
# if defined(AARCHXX) || defined(RISCV64)
ASSERT(get_sigcxt_stolen_reg(sc) != (reg_t)*get_dr_tls_base_addr());
# endif
#endif
/* FIXME: other state? debug regs?
* if no syscall allowed between main_ (when frame created) and
Expand Down Expand Up @@ -7370,26 +7383,26 @@ handle_sigreturn(dcontext_t *dcontext, void *ucxt_param, int style)
* look like whatever would happen to the app...
*/
ASSERT((app_pc)sc->SC_XIP != next_pc);
# if defined(AARCHXX)
# if defined(AARCHXX) || defined(RISCV64)
ASSERT(get_sigcxt_stolen_reg(sc) != (reg_t)*get_dr_tls_base_addr());
/* We're called from DR and are not yet in the cache, so we want to set the
* mcontext slot, not the TLS slot, to set the stolen reg value.
*/
set_stolen_reg_val(get_mcontext(dcontext), get_sigcxt_stolen_reg(sc));
/* The linkstub expects DR's TLS to be in the actual register. */
set_sigcxt_stolen_reg(sc, (reg_t)*get_dr_tls_base_addr());
# ifdef AARCH64
# if defined(AARCH64)
/* On entry to the do_syscall gencode, we save X1 into TLS_REG1_SLOT.
* Then the sigreturn would redirect the flow to the fcache_return gencode.
* In fcache_return it recovers the values of x0 and x1 from TLS_SLOT 0 and 1.
*/
get_mcontext(dcontext)->r1 = sc->SC_FIELD_AARCH64(1);
# elif defined(RISCV64)
get_mcontext(dcontext)->a1 = sc->SC_FIELD(sc_regs.a1);
# else
/* We're going to our fcache_return gencode which uses DEFAULT_ISA_MODE */
set_pc_mode_in_cpsr(sc, DEFAULT_ISA_MODE);
# endif
# elif defined(RISCV64)
ASSERT_NOT_IMPLEMENTED(false);
# endif
#endif

Expand Down
34 changes: 33 additions & 1 deletion core/unix/signal_linux_riscv64.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,39 @@ save_fpstate(dcontext_t *dcontext, sigframe_rt_t *frame)
void
dump_sigcontext(dcontext_t *dcontext, sigcontext_t *sc)
{
LOG(THREAD, LOG_ASYNCH, 1, "FIXME i#3544: NYI on RISCV64");

LOG(THREAD, LOG_ASYNCH, 1, "\tpc = " PFX "\n", sc->sc_regs.pc);
LOG(THREAD, LOG_ASYNCH, 1, "\tra = " PFX "\n", sc->sc_regs.ra);
LOG(THREAD, LOG_ASYNCH, 1, "\tsp = " PFX "\n", sc->sc_regs.sp);
LOG(THREAD, LOG_ASYNCH, 1, "\tgp = " PFX "\n", sc->sc_regs.gp);
LOG(THREAD, LOG_ASYNCH, 1, "\ttp = " PFX "\n", sc->sc_regs.tp);
LOG(THREAD, LOG_ASYNCH, 1, "\tt0 = " PFX "\n", sc->sc_regs.t0);
LOG(THREAD, LOG_ASYNCH, 1, "\tt1 = " PFX "\n", sc->sc_regs.t1);
LOG(THREAD, LOG_ASYNCH, 1, "\tt2 = " PFX "\n", sc->sc_regs.t2);
LOG(THREAD, LOG_ASYNCH, 1, "\ts0 = " PFX "\n", sc->sc_regs.s0);
LOG(THREAD, LOG_ASYNCH, 1, "\ts1 = " PFX "\n", sc->sc_regs.s1);
LOG(THREAD, LOG_ASYNCH, 1, "\ta0 = " PFX "\n", sc->sc_regs.a0);
LOG(THREAD, LOG_ASYNCH, 1, "\ta1 = " PFX "\n", sc->sc_regs.a1);
LOG(THREAD, LOG_ASYNCH, 1, "\ta2 = " PFX "\n", sc->sc_regs.a2);
LOG(THREAD, LOG_ASYNCH, 1, "\ta3 = " PFX "\n", sc->sc_regs.a3);
LOG(THREAD, LOG_ASYNCH, 1, "\ta4 = " PFX "\n", sc->sc_regs.a4);
LOG(THREAD, LOG_ASYNCH, 1, "\ta5 = " PFX "\n", sc->sc_regs.a5);
LOG(THREAD, LOG_ASYNCH, 1, "\ta6 = " PFX "\n", sc->sc_regs.a6);
LOG(THREAD, LOG_ASYNCH, 1, "\ta7 = " PFX "\n", sc->sc_regs.a7);
LOG(THREAD, LOG_ASYNCH, 1, "\ts2 = " PFX "\n", sc->sc_regs.s2);
LOG(THREAD, LOG_ASYNCH, 1, "\ts3 = " PFX "\n", sc->sc_regs.s3);
LOG(THREAD, LOG_ASYNCH, 1, "\ts4 = " PFX "\n", sc->sc_regs.s4);
LOG(THREAD, LOG_ASYNCH, 1, "\ts5 = " PFX "\n", sc->sc_regs.s5);
LOG(THREAD, LOG_ASYNCH, 1, "\ts6 = " PFX "\n", sc->sc_regs.s6);
LOG(THREAD, LOG_ASYNCH, 1, "\ts7 = " PFX "\n", sc->sc_regs.s7);
LOG(THREAD, LOG_ASYNCH, 1, "\ts8 = " PFX "\n", sc->sc_regs.s8);
LOG(THREAD, LOG_ASYNCH, 1, "\ts9 = " PFX "\n", sc->sc_regs.s9);
LOG(THREAD, LOG_ASYNCH, 1, "\ts10 = " PFX "\n", sc->sc_regs.s10);
LOG(THREAD, LOG_ASYNCH, 1, "\ts11 = " PFX "\n", sc->sc_regs.s11);
LOG(THREAD, LOG_ASYNCH, 1, "\tt3 = " PFX "\n", sc->sc_regs.t3);
LOG(THREAD, LOG_ASYNCH, 1, "\tt4 = " PFX "\n", sc->sc_regs.t4);
LOG(THREAD, LOG_ASYNCH, 1, "\tt5 = " PFX "\n", sc->sc_regs.t5);
LOG(THREAD, LOG_ASYNCH, 1, "\tt6 = " PFX "\n", sc->sc_regs.t6);
}
#endif /* DEBUG */

Expand Down
2 changes: 2 additions & 0 deletions suite/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5870,6 +5870,7 @@ if (RISCV64)
code_api|client.stack-overflow
code_api|client.truncate
code_api|common.broadfun
code_api|common.fib
code_api|libutil.drconfig_test
code_api|libutil.frontend_test
code_api|linux.exit
Expand All @@ -5880,6 +5881,7 @@ if (RISCV64)
code_api|linux.signalfd
code_api|pthreads.pthreads
code_api|pthreads.pthreads_exit
code_api|pthreads.pthreads_exit
code_api|security-linux.trampoline
no_code_api,no_intercept_all_signals|linux.sigaction
PROPERTIES LABELS RUNS_ON_QEMU)
Expand Down

0 comments on commit 60f56c9

Please sign in to comment.