135 lines
4.9 KiB
C++
135 lines
4.9 KiB
C++
/*
|
|
* Copyright (C) 2020 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "gwp_asan/common.h"
|
|
#include "gwp_asan/optional/backtrace.h"
|
|
#include "gwp_asan/optional/segv_handler.h"
|
|
|
|
#include <unwindstack/LocalUnwinder.h>
|
|
#include <unwindstack/Unwinder.h>
|
|
|
|
namespace gwp_asan {
|
|
namespace options {
|
|
|
|
// In reality, on Android, we use two separate unwinders. GWP-ASan internally
|
|
// uses a fast, frame-pointer unwinder for allocation/deallocation stack traces
|
|
// (android_unsafe_frame_pointer_chase, provided by bionic libc). When a process
|
|
// crashes, debuggerd unwinds the access trace using libunwindstack, which is a
|
|
// slow CFI-based unwinder. We don't split the unwinders in the test harness,
|
|
// and frame-pointer unwinding doesn't work properly though a signal handler, so
|
|
// we opt to use libunwindstack in this test. This has the effect that we get
|
|
// potentially more detailed stack frames in the allocation/deallocation traces
|
|
// (as we don't use the production unwinder), but that's fine for test-only.
|
|
size_t BacktraceUnwindstack(uintptr_t *TraceBuffer, size_t Size) {
|
|
unwindstack::LocalUnwinder unwinder;
|
|
if (!unwinder.Init()) {
|
|
return 0;
|
|
}
|
|
std::vector<unwindstack::LocalFrameData> frames;
|
|
if (!unwinder.Unwind(&frames, Size)) {
|
|
return 0;
|
|
}
|
|
for (const auto& frame : frames) {
|
|
*TraceBuffer = frame.pc;
|
|
TraceBuffer++;
|
|
}
|
|
return frames.size();
|
|
}
|
|
|
|
Backtrace_t getBacktraceFunction() {
|
|
return BacktraceUnwindstack;
|
|
}
|
|
|
|
// 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) {
|
|
unwindstack::FrameData frame;
|
|
|
|
unwindstack::Maps* maps = unwinder->GetMaps();
|
|
unwindstack::MapInfo* map_info = maps->Find(pc);
|
|
if (!map_info) {
|
|
frame.rel_pc = pc;
|
|
return frame;
|
|
}
|
|
|
|
unwindstack::Elf* elf =
|
|
map_info->GetElf(unwinder->GetProcessMemory(), unwindstack::Regs::CurrentArch());
|
|
|
|
uint64_t relative_pc = elf->GetRelPc(pc, map_info);
|
|
|
|
// Create registers just to get PC adjustment. Doesn't matter what they point
|
|
// to.
|
|
unwindstack::Regs* regs = unwindstack::Regs::CreateFromLocal();
|
|
uint64_t pc_adjustment = regs->GetPcAdjustment(relative_pc, elf);
|
|
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;
|
|
}
|
|
|
|
// This function is a good mimic as to what's happening in the out-of-process
|
|
// tombstone daemon (see debuggerd for more information). In our case, we want
|
|
// to provide symbolized backtraces during ***testing only*** here. This
|
|
// function called from a signal handler, and is extraordinarily not
|
|
// signal-safe, but works for our purposes.
|
|
void PrintBacktraceUnwindstack(uintptr_t *TraceBuffer, size_t TraceLength,
|
|
crash_handler::Printf_t Print) {
|
|
unwindstack::UnwinderFromPid unwinder(
|
|
AllocationMetadata::kMaxTraceLengthToCollect, getpid());
|
|
unwinder.Init(unwindstack::Regs::CurrentArch());
|
|
unwinder.SetRegs(unwindstack::Regs::CreateFromLocal());
|
|
|
|
for (size_t i = 0; i < TraceLength; ++i) {
|
|
unwindstack::FrameData frame_data = BuildFrame(&unwinder, TraceBuffer[i]);
|
|
frame_data.num = i;
|
|
Print(" %s\n", unwinder.FormatFrame(frame_data).c_str());
|
|
}
|
|
}
|
|
|
|
crash_handler::PrintBacktrace_t getPrintBacktraceFunction() {
|
|
return PrintBacktraceUnwindstack;
|
|
}
|
|
} // namespace options
|
|
} // namespace gwp_asan
|