blob: 4b026e55f2a26ff065b3afd0cf2e86d65a66e3d1 [file] [edit]
// Copyright 2018 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/developer/debug/debug_agent/unwind.h"
#include "lib/syslog/cpp/macros.h"
#include "src/developer/debug/debug_agent/arch.h"
#include "src/developer/debug/debug_agent/general_registers.h"
#include "src/developer/debug/debug_agent/module_list.h"
#include "src/developer/debug/debug_agent/process_handle.h"
#include "src/developer/debug/debug_agent/thread_handle.h"
#include "src/developer/debug/ipc/unwinder_support.h"
#include "src/lib/unwinder/arm_ehabi_unwinder.h"
#include "src/lib/unwinder/cfi_unwinder.h"
#include "src/lib/unwinder/fp_unwinder.h"
#include "src/lib/unwinder/platform.h"
#include "src/lib/unwinder/plt_unwinder.h"
#include "src/lib/unwinder/scs_unwinder.h"
#include "src/lib/unwinder/sigreturn_unwinder.h"
#include "src/lib/unwinder/unwind.h"
namespace debug_agent {
namespace {
using debug::RegisterID;
// The general registers include thread-specific information (fsbase/gsbase on x64, and tpidr on
// ARM64). The unwinders don't deal with these registers because unwinding shouldn't affect them.
// This function copies the current platform's thread-specific registers to the stack frame record
// under the assumption that they never change across stack frames.
void AddThreadRegs(const GeneralRegisters& source, debug_ipc::StackFrame* dest) {
const auto& native_regs = source.GetNativeRegisters();
#if defined(__x86_64__)
// Linux defines fs_base and gs_base as "unsigned long long" which requires a cast to be
// unambiguous given the many Register constructor variants.
dest->regs.emplace_back(RegisterID::kX64_fsbase, static_cast<uint64_t>(native_regs.fs_base));
dest->regs.emplace_back(RegisterID::kX64_gsbase, static_cast<uint64_t>(native_regs.gs_base));
#elif defined(__aarch64__)
dest->regs.emplace_back(RegisterID::kARMv8_tpidr, native_regs.tpidr);
#else
#error Write for your platform
#endif
}
} // namespace
unwinder::Error UnwindStack(const ProcessHandle& process, const ModuleList& modules,
const ThreadHandle& thread, const GeneralRegisters& regs,
size_t max_depth, std::vector<debug_ipc::StackFrame>* stack,
std::optional<unwinder::Frame::Trust> forced_unwinder) {
// Prepare arguments for unwinder::Unwind.
#if defined(__Fuchsia__)
unwinder::FuchsiaMemory memory(process.GetNativeHandle().get());
#elif defined(__linux__)
unwinder::LinuxMemory memory(process.GetKoid());
#else
#error Need unwinder memory for this platform.
#endif
std::vector<uint64_t> module_bases;
module_bases.reserve(modules.modules().size());
for (const auto& module : modules.modules()) {
module_bases.push_back(module.base);
}
auto registers = unwinder::FromPlatformRegisters(regs.GetNativeRegisters());
std::vector<unwinder::Frame> frames;
if (forced_unwinder) {
std::vector<unwinder::Module> unwinder_modules;
unwinder_modules.reserve(module_bases.size());
for (uint64_t addr : module_bases) {
unwinder_modules.emplace_back(addr, &memory, unwinder::Module::AddressMode::kProcess);
}
unwinder::ElfModuleCache module_cache(unwinder_modules);
std::unique_ptr<unwinder::UnwinderBase> unwinder;
switch (*forced_unwinder) {
case unwinder::Frame::Trust::kCFI:
unwinder = std::make_unique<unwinder::CfiUnwinder>(module_cache);
break;
case unwinder::Frame::Trust::kFP:
unwinder = std::make_unique<unwinder::FramePointerUnwinder>(module_cache);
break;
case unwinder::Frame::Trust::kSCS:
unwinder = std::make_unique<unwinder::ShadowCallStackUnwinder>(module_cache);
break;
case unwinder::Frame::Trust::kPLT:
unwinder = std::make_unique<unwinder::PltUnwinder>(module_cache);
break;
case unwinder::Frame::Trust::kArmEhAbi:
unwinder = std::make_unique<unwinder::ArmEhAbiUnwinder>(module_cache);
break;
case unwinder::Frame::Trust::kSigReturn:
unwinder = std::make_unique<unwinder::SigReturnUnwinder>(module_cache);
break;
default:
FX_NOTREACHED();
break;
}
FX_CHECK(unwinder);
frames = unwinder->Unwind(&memory, registers, max_depth + 1);
} else {
// Request one more frame for the CFA of the last frame.
frames = unwinder::Unwind(&memory, module_bases, registers, max_depth + 1);
}
// Convert from unwinder::Frame to debug_ipc::StackFrame.
*stack = debug_ipc::ConvertFrames(frames);
if (stack->size() > max_depth) {
stack->resize(max_depth);
}
for (auto& frame : *stack) {
AddThreadRegs(regs, &frame);
}
return frames.back().fatal_error ? frames.back().error : unwinder::Success();
}
} // namespace debug_agent