Move PC-only unwind frame generation to libunwindstack.

GWP-ASan uses frame-pointer based unwinding internally on
allocation/deallocation to collect stack traces that are used when
crashes are reported.

This should be generic, so pull it out into libunwindstack so it can be
used by MTE as well.

Bug: 152412331
Test: atest debuggerd_test
Change-Id: I27b32263aac63446f5fe398af108676b70cd3971
This commit is contained in:
Mitch Phillips 2020-03-25 10:50:25 -07:00
parent 1e413c8a9d
commit b9c072c551
3 changed files with 61 additions and 56 deletions

View File

@ -157,60 +157,6 @@ void GwpAsanCrashData::DumpCause(log_t* log) const {
error_string_, diff, byte_suffix, location_str, alloc_size, alloc_address);
}
// Build a frame for symbolization using the maps from the provided unwinder.
// The constructed frame contains just enough information to be used to
// symbolize a GWP-ASan stack trace.
static unwindstack::FrameData BuildFrame(unwindstack::Unwinder* unwinder, uintptr_t pc,
size_t frame_num) {
unwindstack::FrameData frame;
frame.num = frame_num;
unwindstack::Maps* maps = unwinder->GetMaps();
unwindstack::MapInfo* map_info = maps->Find(pc);
if (!map_info) {
frame.rel_pc = pc;
return frame;
}
unwindstack::ArchEnum arch = unwindstack::Regs::CurrentArch();
unwindstack::Elf* elf = map_info->GetElf(unwinder->GetProcessMemory(), arch);
uint64_t relative_pc = elf->GetRelPc(pc, map_info);
uint64_t pc_adjustment = unwindstack::GetPcAdjustment(relative_pc, elf, arch);
relative_pc -= pc_adjustment;
// The debug PC may be different if the PC comes from the JIT.
uint64_t debug_pc = relative_pc;
// If we don't have a valid ELF file, check the JIT.
if (!elf->valid()) {
unwindstack::JitDebug jit_debug(unwinder->GetProcessMemory());
uint64_t jit_pc = pc - pc_adjustment;
unwindstack::Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
if (jit_elf != nullptr) {
debug_pc = jit_pc;
elf = jit_elf;
}
}
// Copy all the things we need into the frame for symbolization.
frame.rel_pc = relative_pc;
frame.pc = pc - pc_adjustment;
frame.map_name = map_info->name;
frame.map_elf_start_offset = map_info->elf_start_offset;
frame.map_exact_offset = map_info->offset;
frame.map_start = map_info->start;
frame.map_end = map_info->end;
frame.map_flags = map_info->flags;
frame.map_load_bias = elf->GetLoadBias();
if (!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
frame.function_name = "";
frame.function_offset = 0;
}
return frame;
}
constexpr size_t kMaxTraceLength = gwp_asan::AllocationMetadata::kMaxTraceLengthToCollect;
bool GwpAsanCrashData::HasDeallocationTrace() const {
@ -237,7 +183,8 @@ void GwpAsanCrashData::DumpDeallocationTrace(log_t* log, unwindstack::Unwinder*
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < num_frames; ++i) {
unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]);
frame_data.num = i;
_LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
}
}
@ -263,7 +210,8 @@ void GwpAsanCrashData::DumpAllocationTrace(log_t* log, unwindstack::Unwinder* un
unwinder->SetDisplayBuildID(true);
for (size_t i = 0; i < num_frames; ++i) {
unwindstack::FrameData frame_data = BuildFrame(unwinder, frames.get()[i], i);
unwindstack::FrameData frame_data = unwinder->BuildFrameFromPcOnly(frames.get()[i]);
frame_data.num = i;
_LOG(log, logtype::BACKTRACE, " %s\n", unwinder->FormatFrame(frame_data).c_str());
}
}

View File

@ -395,4 +395,54 @@ bool UnwinderFromPid::Init(ArchEnum arch) {
return true;
}
FrameData Unwinder::BuildFrameFromPcOnly(uint64_t pc) {
FrameData frame;
Maps* maps = GetMaps();
MapInfo* map_info = maps->Find(pc);
if (!map_info) {
frame.rel_pc = pc;
return frame;
}
ArchEnum arch = Regs::CurrentArch();
Elf* elf = map_info->GetElf(GetProcessMemory(), arch);
uint64_t relative_pc = elf->GetRelPc(pc, map_info);
uint64_t pc_adjustment = GetPcAdjustment(relative_pc, elf, arch);
relative_pc -= pc_adjustment;
// The debug PC may be different if the PC comes from the JIT.
uint64_t debug_pc = relative_pc;
// If we don't have a valid ELF file, check the JIT.
if (!elf->valid()) {
JitDebug jit_debug(GetProcessMemory());
uint64_t jit_pc = pc - pc_adjustment;
Elf* jit_elf = jit_debug.GetElf(maps, jit_pc);
if (jit_elf != nullptr) {
debug_pc = jit_pc;
elf = jit_elf;
}
}
// Copy all the things we need into the frame for symbolization.
frame.rel_pc = relative_pc;
frame.pc = pc - pc_adjustment;
frame.map_name = map_info->name;
frame.map_elf_start_offset = map_info->elf_start_offset;
frame.map_exact_offset = map_info->offset;
frame.map_start = map_info->start;
frame.map_end = map_info->end;
frame.map_flags = map_info->flags;
frame.map_load_bias = elf->GetLoadBias();
if (!resolve_names_ ||
!elf->GetFunctionName(relative_pc, &frame.function_name, &frame.function_offset)) {
frame.function_name = "";
frame.function_offset = 0;
}
return frame;
}
} // namespace unwindstack

View File

@ -114,6 +114,13 @@ class Unwinder {
ErrorCode LastErrorCode() { return last_error_.code; }
uint64_t LastErrorAddress() { return last_error_.address; }
// Builds a frame for symbolization using the maps from this unwinder. The
// constructed frame contains just enough information to be used to symbolize
// frames collected by frame-pointer unwinding that's done outside of
// libunwindstack. This is used by tombstoned to symbolize frame pointer-based
// stack traces that are collected by tools such as GWP-ASan and MTE.
FrameData BuildFrameFromPcOnly(uint64_t pc);
protected:
Unwinder(size_t max_frames) : max_frames_(max_frames) { frames_.reserve(max_frames); }