diff options
author | Peter Maydell <peter.maydell@linaro.org> | 2020-11-17 15:56:34 +0000 |
---|---|---|
committer | Peter Maydell <peter.maydell@linaro.org> | 2020-11-23 11:04:51 +0000 |
commit | acebed948c4f2f3be89ba91f4cba1905a8b66318 (patch) | |
tree | 6f194d1c721ee2459964582a74d0a2d69a76b278 | |
parent | 6951595183e5481931f03284d5cc6a481fe7c713 (diff) |
linux-user/arm: Deliver SIGTRAP for UDF patterns used as breakpoints
The Linux kernel doesn't use the official bkpt insn for breakpoints;
instead it uses three instructions in the guaranteed-to-UNDEF space,
and generates SIGTRAP for these rather than the SIGILL that most
UNDEF insns generate:
https://elixir.bootlin.com/linux/v5.9.8/source/arch/arm/kernel/ptrace.c#L197
Make QEMU treat these insns specially too. The main benefit of this
is that if you're running a debugger on a guest program that runs
into a GCC __builtin_trap() or LLVM "trap because execution should
never reach here" then you'll get the expected signal rather than a
SIGILL.
Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
Reviewed-by: Richard Henderson <richard.henderson@linaro.org>
Message-id: 20201117155634.6924-1-peter.maydell@linaro.org
-rw-r--r-- | linux-user/arm/cpu_loop.c | 28 |
1 files changed, 28 insertions, 0 deletions
diff --git a/linux-user/arm/cpu_loop.c b/linux-user/arm/cpu_loop.c index 13629ee1f6..3d272b56ef 100644 --- a/linux-user/arm/cpu_loop.c +++ b/linux-user/arm/cpu_loop.c @@ -205,6 +205,24 @@ do_kernel_trap(CPUARMState *env) return 0; } +static bool insn_is_linux_bkpt(uint32_t opcode, bool is_thumb) +{ + /* + * Return true if this insn is one of the three magic UDF insns + * which the kernel treats as breakpoint insns. + */ + if (!is_thumb) { + return (opcode & 0x0fffffff) == 0x07f001f0; + } else { + /* + * Note that we get the two halves of the 32-bit T32 insn + * in the opposite order to the value the kernel uses in + * its undef_hook struct. + */ + return ((opcode & 0xffff) == 0xde01) || (opcode == 0xa000f7f0); + } +} + void cpu_loop(CPUARMState *env) { CPUState *cs = env_cpu(env); @@ -234,6 +252,16 @@ void cpu_loop(CPUARMState *env) /* FIXME - what to do if get_user() fails? */ get_user_code_u32(opcode, env->regs[15], env); + /* + * The Linux kernel treats some UDF patterns specially + * to use as breakpoints (instead of the architectural + * bkpt insn). These should trigger a SIGTRAP rather + * than SIGILL. + */ + if (insn_is_linux_bkpt(opcode, env->thumb)) { + goto excp_debug; + } + rc = EmulateAll(opcode, &ts->fpa, env); if (rc == 0) { /* illegal instruction */ info.si_signo = TARGET_SIGILL; |