blob: d64746bff102431fbfb9855692b677ea326b7dfe [file] [log] [blame]
// Copyright 2019 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/lib/debug/backtrace-request-utils.h"
#include <zircon/syscalls.h>
#include "src/lib/debug/backtrace-request.h"
namespace {
uint64_t get_swbreak_argument(const zx_thread_state_general_regs_t* regs) {
#if defined(__x86_64__)
return regs->rax;
#elif defined(__aarch64__)
return regs->r[0];
#elif defined(__riscv)
return regs->a0;
#endif
}
bool have_swbreak_magic(const zx_thread_state_general_regs_t* regs) {
uint64_t arg = get_swbreak_argument(regs);
return arg == BACKTRACE_REQUEST_MAGIC_ALL_THREADS ||
arg == BACKTRACE_REQUEST_MAGIC_CURRENT_THREAD;
}
} // namespace
// TODO: consider disabling this feature for non-development builds.
bool is_backtrace_request(zx_excp_type_t excp_type, const zx_thread_state_general_regs_t* regs) {
return excp_type == ZX_EXCP_SW_BREAKPOINT && regs != nullptr && have_swbreak_magic(regs);
}
bool is_backtrace_request_current_thread(const zx_thread_state_general_regs_t* regs) {
return get_swbreak_argument(regs) == BACKTRACE_REQUEST_MAGIC_CURRENT_THREAD;
}
zx_status_t cleanup_backtrace_request(zx_handle_t thread, zx_thread_state_general_regs_t* regs) {
#if defined(__x86_64__)
// On x86, the pc is left at one past the s/w break insn,
// so there's nothing more we need to do.
return ZX_OK;
#elif defined(__aarch64__)
// Skip past the brk instruction.
regs->pc += 4;
return zx_thread_write_state(thread, ZX_THREAD_STATE_GENERAL_REGS, regs, sizeof(*regs));
#elif defined(__riscv)
// Skip past the EBREAK instruction. This assumes that the instruction was compressed, which is
// a safe assumption because `backtrace-request.h` explicitly uses `C.EBREAK`.
regs->pc += 2;
return zx_thread_write_state(thread, ZX_THREAD_STATE_GENERAL_REGS, regs, sizeof(*regs));
#else
return ZX_ERR_NOT_SUPPORTED;
#endif
}