Add a new unwind method on error.
If a function crashes by jumping into unexecutable code, the old method could not unwind through that. Add a fallback method to set the pc from the default return address location. In addition, add a new finished check for steps. This will provide a method to indicate that this step is the last step. This prevents cases where the fallback method might be triggered incorrectly. Update the libbacktrace code to unwind using the new methodology. Update the unwind tool to use the new unwind methodology. Add a new option to crasher that calls through a null function. Create a new object, Unwinder, that encapsulates the a basic unwind. For now, libbacktrace will still use the custom code. Added new unit tests to cover the new cases. Also add a test that crashes calling a nullptr as a function, and then has call frames in the signal stack. Bug: 65842173 Test: Pass all unit tests, verify crasher dumps properly. Change-Id: Ia18430ab107e9f7bdf0e14a9b74710b1280bd7f4
This commit is contained in:
parent
0bb8dcb23e
commit
b9de87f7ed
|
@ -114,6 +114,11 @@ noinline int do_action_on_thread(const char* arg) {
|
|||
return reinterpret_cast<uintptr_t>(result);
|
||||
}
|
||||
|
||||
noinline int crash_null() {
|
||||
int (*null_func)() = nullptr;
|
||||
return null_func();
|
||||
}
|
||||
|
||||
noinline int crash3(int a) {
|
||||
*reinterpret_cast<int*>(0xdead) = a;
|
||||
return a*4;
|
||||
|
@ -169,6 +174,7 @@ static int usage() {
|
|||
fprintf(stderr, " nostack crash with a NULL stack pointer\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " heap-usage cause a libc abort by abusing a heap function\n");
|
||||
fprintf(stderr, " call-null cause a crash by calling through a nullptr\n");
|
||||
fprintf(stderr, " leak leak memory until we get OOM-killed\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, " abort call abort()\n");
|
||||
|
@ -239,6 +245,8 @@ noinline int do_action(const char* arg) {
|
|||
crashnostack();
|
||||
} else if (!strcasecmp(arg, "exit")) {
|
||||
exit(1);
|
||||
} else if (!strcasecmp(arg, "call-null")) {
|
||||
return crash_null();
|
||||
} else if (!strcasecmp(arg, "crash") || !strcmp(arg, "SIGSEGV")) {
|
||||
return crash(42);
|
||||
} else if (!strcasecmp(arg, "abort")) {
|
||||
|
|
|
@ -68,6 +68,32 @@ static bool IsUnwindLibrary(const std::string& map_name) {
|
|||
return library == "libunwindstack.so" || library == "libbacktrace.so";
|
||||
}
|
||||
|
||||
static void SetFrameInfo(unwindstack::Regs* regs, unwindstack::MapInfo* map_info,
|
||||
uint64_t adjusted_rel_pc, backtrace_frame_data_t* frame) {
|
||||
// This will point to the adjusted absolute pc. regs->pc() is
|
||||
// unaltered.
|
||||
frame->pc = map_info->start + adjusted_rel_pc;
|
||||
frame->sp = regs->sp();
|
||||
frame->rel_pc = adjusted_rel_pc;
|
||||
frame->stack_size = 0;
|
||||
|
||||
frame->map.start = map_info->start;
|
||||
frame->map.end = map_info->end;
|
||||
frame->map.offset = map_info->offset;
|
||||
frame->map.flags = map_info->flags;
|
||||
frame->map.name = map_info->name;
|
||||
|
||||
unwindstack::Elf* elf = map_info->elf;
|
||||
frame->map.load_bias = elf->GetLoadBias();
|
||||
uint64_t func_offset = 0;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
|
||||
frame->func_name = demangle(frame->func_name.c_str());
|
||||
} else {
|
||||
frame->func_name = "";
|
||||
}
|
||||
frame->func_offset = func_offset;
|
||||
}
|
||||
|
||||
static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
||||
std::vector<backtrace_frame_data_t>* frames, size_t num_ignore_frames) {
|
||||
UnwindStackMap* stack_map = reinterpret_cast<UnwindStackMap*>(back_map);
|
||||
|
@ -75,70 +101,96 @@ static bool Unwind(unwindstack::Regs* regs, BacktraceMap* back_map,
|
|||
bool adjust_rel_pc = false;
|
||||
size_t num_frames = 0;
|
||||
frames->clear();
|
||||
bool return_address_attempted = false;
|
||||
auto process_memory = stack_map->process_memory();
|
||||
while (num_frames < MAX_BACKTRACE_FRAMES) {
|
||||
if (regs->pc() == 0) {
|
||||
break;
|
||||
}
|
||||
unwindstack::MapInfo* map_info = maps->Find(regs->pc());
|
||||
bool stepped;
|
||||
bool in_device_map = false;
|
||||
if (map_info == nullptr) {
|
||||
break;
|
||||
}
|
||||
|
||||
unwindstack::Elf* elf = map_info->GetElf(stack_map->process_memory(), true);
|
||||
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
|
||||
|
||||
bool skip_frame = num_frames == 0 && IsUnwindLibrary(map_info->name);
|
||||
if (num_ignore_frames == 0 && !skip_frame) {
|
||||
uint64_t adjusted_rel_pc = rel_pc;
|
||||
if (adjust_rel_pc) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
|
||||
}
|
||||
frames->resize(num_frames + 1);
|
||||
backtrace_frame_data_t* frame = &frames->at(num_frames);
|
||||
frame->num = num_frames;
|
||||
// This will point to the adjusted absolute pc. regs->pc() is
|
||||
// unaltered.
|
||||
frame->pc = map_info->start + adjusted_rel_pc;
|
||||
frame->sp = regs->sp();
|
||||
frame->rel_pc = adjusted_rel_pc;
|
||||
frame->stack_size = 0;
|
||||
|
||||
frame->map.start = map_info->start;
|
||||
frame->map.end = map_info->end;
|
||||
frame->map.offset = map_info->offset;
|
||||
frame->map.load_bias = elf->GetLoadBias();
|
||||
frame->map.flags = map_info->flags;
|
||||
frame->map.name = map_info->name;
|
||||
|
||||
uint64_t func_offset = 0;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &frame->func_name, &func_offset)) {
|
||||
frame->func_name = demangle(frame->func_name.c_str());
|
||||
stepped = false;
|
||||
if (num_ignore_frames == 0) {
|
||||
frames->resize(num_frames + 1);
|
||||
backtrace_frame_data_t* frame = &frames->at(num_frames);
|
||||
frame->pc = regs->pc();
|
||||
frame->sp = regs->sp();
|
||||
frame->rel_pc = frame->pc;
|
||||
num_frames++;
|
||||
} else {
|
||||
frame->func_name = "";
|
||||
num_ignore_frames--;
|
||||
}
|
||||
frame->func_offset = func_offset;
|
||||
if (num_frames > 0) {
|
||||
// Set the stack size for the previous frame.
|
||||
backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
|
||||
prev->stack_size = frame->sp - prev->sp;
|
||||
} else {
|
||||
unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
|
||||
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
|
||||
|
||||
if (frames->size() != 0 || !IsUnwindLibrary(map_info->name)) {
|
||||
if (num_ignore_frames == 0) {
|
||||
uint64_t adjusted_rel_pc = rel_pc;
|
||||
if (adjust_rel_pc) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
|
||||
}
|
||||
|
||||
frames->resize(num_frames + 1);
|
||||
backtrace_frame_data_t* frame = &frames->at(num_frames);
|
||||
frame->num = num_frames;
|
||||
SetFrameInfo(regs, map_info, adjusted_rel_pc, frame);
|
||||
|
||||
if (num_frames > 0) {
|
||||
// Set the stack size for the previous frame.
|
||||
backtrace_frame_data_t* prev = &frames->at(num_frames - 1);
|
||||
prev->stack_size = frame->sp - prev->sp;
|
||||
}
|
||||
num_frames++;
|
||||
} else {
|
||||
num_ignore_frames--;
|
||||
}
|
||||
}
|
||||
|
||||
if (map_info->flags & PROT_DEVICE_MAP) {
|
||||
// Do not stop here, fall through in case we are
|
||||
// in the speculative unwind path and need to remove
|
||||
// some of the speculative frames.
|
||||
stepped = false;
|
||||
in_device_map = true;
|
||||
} else {
|
||||
unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
|
||||
if (sp_info->flags & PROT_DEVICE_MAP) {
|
||||
// Do not stop here, fall through in case we are
|
||||
// in the speculative unwind path and need to remove
|
||||
// some of the speculative frames.
|
||||
stepped = false;
|
||||
in_device_map = true;
|
||||
} else {
|
||||
bool finished;
|
||||
stepped = elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
|
||||
if (stepped && finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
num_frames++;
|
||||
} else if (!skip_frame && num_ignore_frames > 0) {
|
||||
num_ignore_frames--;
|
||||
}
|
||||
adjust_rel_pc = true;
|
||||
|
||||
// Do not unwind through a device map.
|
||||
if (map_info->flags & PROT_DEVICE_MAP) {
|
||||
break;
|
||||
}
|
||||
unwindstack::MapInfo* sp_info = maps->Find(regs->sp());
|
||||
if (sp_info->flags & PROT_DEVICE_MAP) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!elf->Step(rel_pc + map_info->elf_offset, regs, stack_map->process_memory().get())) {
|
||||
break;
|
||||
if (!stepped) {
|
||||
if (return_address_attempted) {
|
||||
// Remove the speculative frame.
|
||||
if (frames->size() > 0) {
|
||||
frames->pop_back();
|
||||
}
|
||||
break;
|
||||
} else if (in_device_map) {
|
||||
// Do not attempt any other unwinding, pc or sp is in a device
|
||||
// map.
|
||||
break;
|
||||
} else {
|
||||
// Stepping didn't work, try this secondary method.
|
||||
if (!regs->SetPcFromReturnAddress(process_memory.get())) {
|
||||
break;
|
||||
}
|
||||
return_address_attempted = true;
|
||||
}
|
||||
} else {
|
||||
return_address_attempted = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,6 +82,14 @@ struct dump_thread_t {
|
|||
int32_t done;
|
||||
};
|
||||
|
||||
typedef Backtrace* (*create_func_t)(pid_t, pid_t, BacktraceMap*);
|
||||
typedef BacktraceMap* (*map_create_func_t)(pid_t, bool);
|
||||
|
||||
static void VerifyLevelDump(Backtrace* backtrace, create_func_t create_func = nullptr,
|
||||
map_create_func_t map_func = nullptr);
|
||||
static void VerifyMaxDump(Backtrace* backtrace, create_func_t create_func = nullptr,
|
||||
map_create_func_t map_func = nullptr);
|
||||
|
||||
static uint64_t NanoTime() {
|
||||
struct timespec t = { 0, 0 };
|
||||
clock_gettime(CLOCK_MONOTONIC, &t);
|
||||
|
@ -147,7 +155,7 @@ static bool ReadyLevelBacktrace(Backtrace* backtrace) {
|
|||
return found;
|
||||
}
|
||||
|
||||
static void VerifyLevelDump(Backtrace* backtrace) {
|
||||
static void VerifyLevelDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
|
||||
ASSERT_GT(backtrace->NumFrames(), static_cast<size_t>(0))
|
||||
<< DumpFrames(backtrace);
|
||||
ASSERT_LT(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
|
||||
|
@ -189,7 +197,7 @@ static bool ReadyMaxBacktrace(Backtrace* backtrace) {
|
|||
return (backtrace->NumFrames() == MAX_BACKTRACE_FRAMES);
|
||||
}
|
||||
|
||||
static void VerifyMaxDump(Backtrace* backtrace) {
|
||||
static void VerifyMaxDump(Backtrace* backtrace, create_func_t, map_create_func_t) {
|
||||
ASSERT_EQ(backtrace->NumFrames(), static_cast<size_t>(MAX_BACKTRACE_FRAMES))
|
||||
<< DumpFrames(backtrace);
|
||||
// Verify that the last frame is our recursive call.
|
||||
|
@ -251,10 +259,14 @@ TEST(libbacktrace, local_trace) {
|
|||
|
||||
static void VerifyIgnoreFrames(Backtrace* bt_all, Backtrace* bt_ign1, Backtrace* bt_ign2,
|
||||
const char* cur_proc) {
|
||||
EXPECT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1)
|
||||
<< "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 1 backtrace:\n" << DumpFrames(bt_ign1);
|
||||
EXPECT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2)
|
||||
<< "All backtrace:\n" << DumpFrames(bt_all) << "Ignore 2 backtrace:\n" << DumpFrames(bt_ign2);
|
||||
ASSERT_EQ(bt_all->NumFrames(), bt_ign1->NumFrames() + 1) << "All backtrace:\n"
|
||||
<< DumpFrames(bt_all)
|
||||
<< "Ignore 1 backtrace:\n"
|
||||
<< DumpFrames(bt_ign1);
|
||||
ASSERT_EQ(bt_all->NumFrames(), bt_ign2->NumFrames() + 2) << "All backtrace:\n"
|
||||
<< DumpFrames(bt_all)
|
||||
<< "Ignore 2 backtrace:\n"
|
||||
<< DumpFrames(bt_ign2);
|
||||
|
||||
// Check all of the frames are the same > the current frame.
|
||||
bool check = (cur_proc == nullptr);
|
||||
|
@ -305,9 +317,8 @@ TEST(libbacktrace, local_max_trace) {
|
|||
}
|
||||
|
||||
static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
|
||||
void (*VerifyFunc)(Backtrace*),
|
||||
Backtrace* (*back_func)(pid_t, pid_t, BacktraceMap*),
|
||||
BacktraceMap* (*map_func)(pid_t, bool)) {
|
||||
void (*VerifyFunc)(Backtrace*, create_func_t, map_create_func_t),
|
||||
create_func_t create_func, map_create_func_t map_create_func) {
|
||||
pid_t ptrace_tid;
|
||||
if (tid < 0) {
|
||||
ptrace_tid = pid;
|
||||
|
@ -324,13 +335,13 @@ static void VerifyProcTest(pid_t pid, pid_t tid, bool (*ReadyFunc)(Backtrace*),
|
|||
WaitForStop(ptrace_tid);
|
||||
|
||||
std::unique_ptr<BacktraceMap> map;
|
||||
map.reset(map_func(pid, false));
|
||||
std::unique_ptr<Backtrace> backtrace(back_func(pid, tid, map.get()));
|
||||
map.reset(map_create_func(pid, false));
|
||||
std::unique_ptr<Backtrace> backtrace(create_func(pid, tid, map.get()));
|
||||
ASSERT_TRUE(backtrace.get() != nullptr);
|
||||
ASSERT_TRUE(backtrace->Unwind(0));
|
||||
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, backtrace->GetError());
|
||||
if (ReadyFunc(backtrace.get())) {
|
||||
VerifyFunc(backtrace.get());
|
||||
VerifyFunc(backtrace.get(), create_func, map_create_func);
|
||||
verified = true;
|
||||
} else {
|
||||
last_dump = DumpFrames(backtrace.get());
|
||||
|
@ -399,13 +410,15 @@ TEST(libbacktrace, ptrace_max_trace_new) {
|
|||
ASSERT_EQ(waitpid(pid, &status, 0), pid);
|
||||
}
|
||||
|
||||
static void VerifyProcessIgnoreFrames(Backtrace* bt_all) {
|
||||
std::unique_ptr<Backtrace> ign1(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
|
||||
static void VerifyProcessIgnoreFrames(Backtrace* bt_all, create_func_t create_func,
|
||||
map_create_func_t map_create_func) {
|
||||
std::unique_ptr<BacktraceMap> map(map_create_func(bt_all->Pid(), false));
|
||||
std::unique_ptr<Backtrace> ign1(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
|
||||
ASSERT_TRUE(ign1.get() != nullptr);
|
||||
ASSERT_TRUE(ign1->Unwind(1));
|
||||
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign1->GetError());
|
||||
|
||||
std::unique_ptr<Backtrace> ign2(Backtrace::Create(bt_all->Pid(), BACKTRACE_CURRENT_THREAD));
|
||||
std::unique_ptr<Backtrace> ign2(create_func(bt_all->Pid(), BACKTRACE_CURRENT_THREAD, map.get()));
|
||||
ASSERT_TRUE(ign2.get() != nullptr);
|
||||
ASSERT_TRUE(ign2->Unwind(2));
|
||||
ASSERT_EQ(BACKTRACE_UNWIND_NO_ERROR, ign2->GetError());
|
||||
|
@ -1702,9 +1715,8 @@ static void SetValueAndLoop(void* data) {
|
|||
;
|
||||
}
|
||||
|
||||
static void UnwindThroughSignal(bool use_action,
|
||||
Backtrace* (*back_func)(pid_t, pid_t, BacktraceMap*),
|
||||
BacktraceMap* (*map_func)(pid_t, bool)) {
|
||||
static void UnwindThroughSignal(bool use_action, create_func_t create_func,
|
||||
map_create_func_t map_create_func) {
|
||||
volatile int value = 0;
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
|
@ -1730,8 +1742,8 @@ static void UnwindThroughSignal(bool use_action,
|
|||
|
||||
WaitForStop(pid);
|
||||
|
||||
std::unique_ptr<BacktraceMap> map(map_func(pid, false));
|
||||
std::unique_ptr<Backtrace> backtrace(back_func(pid, pid, map.get()));
|
||||
std::unique_ptr<BacktraceMap> map(map_create_func(pid, false));
|
||||
std::unique_ptr<Backtrace> backtrace(create_func(pid, pid, map.get()));
|
||||
|
||||
size_t bytes_read = backtrace->Read(reinterpret_cast<uintptr_t>(const_cast<int*>(&value)),
|
||||
reinterpret_cast<uint8_t*>(&read_value), sizeof(read_value));
|
||||
|
@ -1758,9 +1770,9 @@ static void UnwindThroughSignal(bool use_action,
|
|||
|
||||
WaitForStop(pid);
|
||||
|
||||
map.reset(map_func(pid, false));
|
||||
map.reset(map_create_func(pid, false));
|
||||
ASSERT_TRUE(map.get() != nullptr);
|
||||
backtrace.reset(back_func(pid, pid, map.get()));
|
||||
backtrace.reset(create_func(pid, pid, map.get()));
|
||||
ASSERT_TRUE(backtrace->Unwind(0));
|
||||
bool found = false;
|
||||
for (frame_iter = backtrace->begin(); frame_iter != backtrace->end(); ++frame_iter) {
|
||||
|
|
|
@ -61,6 +61,7 @@ cc_library {
|
|||
"Maps.cpp",
|
||||
"Memory.cpp",
|
||||
"Regs.cpp",
|
||||
"Unwinder.cpp",
|
||||
"Symbols.cpp",
|
||||
],
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ const DwarfFde* DwarfSection::GetFdeFromPc(uint64_t pc) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
|
||||
bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
last_error_ = DWARF_ERROR_NONE;
|
||||
const DwarfFde* fde = GetFdeFromPc(pc);
|
||||
if (fde == nullptr || fde->cie == nullptr) {
|
||||
|
@ -62,7 +62,7 @@ bool DwarfSection::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
|
|||
}
|
||||
|
||||
// Now eval the actual registers.
|
||||
return Eval(fde->cie, process_memory, loc_regs, regs);
|
||||
return Eval(fde->cie, process_memory, loc_regs, regs, finished);
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
|
@ -92,7 +92,8 @@ bool DwarfSectionImpl<AddressType>::EvalExpression(const DwarfLocation& loc, uin
|
|||
|
||||
template <typename AddressType>
|
||||
bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_memory,
|
||||
const dwarf_loc_regs_t& loc_regs, Regs* regs) {
|
||||
const dwarf_loc_regs_t& loc_regs, Regs* regs,
|
||||
bool* finished) {
|
||||
RegsImpl<AddressType>* cur_regs = reinterpret_cast<RegsImpl<AddressType>*>(regs);
|
||||
if (cie->return_address_register >= cur_regs->total_regs()) {
|
||||
last_error_ = DWARF_ERROR_ILLEGAL_VALUE;
|
||||
|
@ -224,12 +225,14 @@ bool DwarfSectionImpl<AddressType>::Eval(const DwarfCie* cie, Memory* regular_me
|
|||
// Find the return address location.
|
||||
if (return_address_undefined) {
|
||||
cur_regs->set_pc(0);
|
||||
*finished = true;
|
||||
} else {
|
||||
cur_regs->set_pc((*cur_regs)[cie->return_address_register]);
|
||||
*finished = false;
|
||||
}
|
||||
cur_regs->set_sp(cfa);
|
||||
// Stop if the cfa and pc are the same.
|
||||
return prev_cfa != cfa || prev_pc != cur_regs->pc();
|
||||
// Return false if the unwind is not finished or the cfa and pc didn't change.
|
||||
return *finished || prev_cfa != cfa || prev_pc != cur_regs->pc();
|
||||
}
|
||||
|
||||
template <typename AddressType>
|
||||
|
|
|
@ -95,11 +95,17 @@ bool Elf::GetFunctionName(uint64_t addr, std::string* name, uint64_t* func_offse
|
|||
gnu_debugdata_interface_->GetFunctionName(addr, name, func_offset)));
|
||||
}
|
||||
|
||||
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory) {
|
||||
return valid_ && (regs->StepIfSignalHandler(rel_pc, this, process_memory) ||
|
||||
interface_->Step(rel_pc, regs, process_memory) ||
|
||||
(gnu_debugdata_interface_ &&
|
||||
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory)));
|
||||
bool Elf::Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
if (!valid_) {
|
||||
return false;
|
||||
}
|
||||
if (regs->StepIfSignalHandler(rel_pc, this, process_memory)) {
|
||||
*finished = false;
|
||||
return true;
|
||||
}
|
||||
return interface_->Step(rel_pc, regs, process_memory, finished) ||
|
||||
(gnu_debugdata_interface_ &&
|
||||
gnu_debugdata_interface_->Step(rel_pc, regs, process_memory, finished));
|
||||
}
|
||||
|
||||
uint64_t Elf::GetLoadBias() {
|
||||
|
|
|
@ -348,7 +348,7 @@ bool ElfInterface::GetFunctionNameWithTemplate(uint64_t addr, std::string* name,
|
|||
return false;
|
||||
}
|
||||
|
||||
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
|
||||
bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
// Need to subtract off the load_bias to get the correct pc.
|
||||
if (pc < load_bias_) {
|
||||
return false;
|
||||
|
@ -357,16 +357,15 @@ bool ElfInterface::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
|
|||
|
||||
// Try the eh_frame first.
|
||||
DwarfSection* eh_frame = eh_frame_.get();
|
||||
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory)) {
|
||||
if (eh_frame != nullptr && eh_frame->Step(pc, regs, process_memory, finished)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try the debug_frame next.
|
||||
DwarfSection* debug_frame = debug_frame_.get();
|
||||
if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory)) {
|
||||
if (debug_frame != nullptr && debug_frame->Step(pc, regs, process_memory, finished)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -99,22 +99,25 @@ bool ElfInterfaceArm::HandleType(uint64_t offset, uint32_t type) {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory) {
|
||||
bool ElfInterfaceArm::Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
// Dwarf unwind information is precise about whether a pc is covered or not,
|
||||
// but arm unwind information only has ranges of pc. In order to avoid
|
||||
// incorrectly doing a bad unwind using arm unwind information for a
|
||||
// different function, always try and unwind with the dwarf information first.
|
||||
return ElfInterface32::Step(pc, regs, process_memory) || StepExidx(pc, regs, process_memory);
|
||||
return ElfInterface32::Step(pc, regs, process_memory, finished) ||
|
||||
StepExidx(pc, regs, process_memory, finished);
|
||||
}
|
||||
|
||||
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory) {
|
||||
bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) {
|
||||
RegsArm* regs_arm = reinterpret_cast<RegsArm*>(regs);
|
||||
uint64_t entry_offset;
|
||||
if (!FindEntry(pc, &entry_offset)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ArmExidx arm(regs_arm, memory_, process_memory);
|
||||
arm.set_cfa(regs_arm->sp());
|
||||
bool return_value = false;
|
||||
if (arm.ExtractEntryData(entry_offset) && arm.Eval()) {
|
||||
// If the pc was not set, then use the LR registers for the PC.
|
||||
if (!arm.pc_set()) {
|
||||
|
@ -125,9 +128,15 @@ bool ElfInterfaceArm::StepExidx(uint64_t pc, Regs* regs, Memory* process_memory)
|
|||
}
|
||||
regs_arm->set_sp(arm.cfa());
|
||||
(*regs_arm)[ARM_REG_SP] = regs_arm->sp();
|
||||
*finished = false;
|
||||
return_value = true;
|
||||
}
|
||||
|
||||
if (arm.status() == ARM_STATUS_NO_UNWIND) {
|
||||
*finished = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return return_value;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -70,9 +70,9 @@ class ElfInterfaceArm : public ElfInterface32 {
|
|||
|
||||
bool HandleType(uint64_t offset, uint32_t type) override;
|
||||
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory) override;
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished) override;
|
||||
|
||||
bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory);
|
||||
bool StepExidx(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
|
||||
uint64_t start_offset() { return start_offset_; }
|
||||
|
||||
|
|
|
@ -91,6 +91,15 @@ void RegsArm::SetFromRaw() {
|
|||
set_sp(regs_[ARM_REG_SP]);
|
||||
}
|
||||
|
||||
bool RegsArm::SetPcFromReturnAddress(Memory*) {
|
||||
if (pc() == regs_[ARM_REG_LR]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_pc(regs_[ARM_REG_LR]);
|
||||
return true;
|
||||
}
|
||||
|
||||
RegsArm64::RegsArm64()
|
||||
: RegsImpl<uint64_t>(ARM64_REG_LAST, ARM64_REG_SP, Location(LOCATION_REGISTER, ARM64_REG_LR)) {}
|
||||
|
||||
|
@ -114,6 +123,15 @@ void RegsArm64::SetFromRaw() {
|
|||
set_sp(regs_[ARM64_REG_SP]);
|
||||
}
|
||||
|
||||
bool RegsArm64::SetPcFromReturnAddress(Memory*) {
|
||||
if (pc() == regs_[ARM64_REG_LR]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_pc(regs_[ARM64_REG_LR]);
|
||||
return true;
|
||||
}
|
||||
|
||||
RegsX86::RegsX86()
|
||||
: RegsImpl<uint32_t>(X86_REG_LAST, X86_REG_SP, Location(LOCATION_SP_OFFSET, -4)) {}
|
||||
|
||||
|
@ -137,6 +155,17 @@ void RegsX86::SetFromRaw() {
|
|||
set_sp(regs_[X86_REG_SP]);
|
||||
}
|
||||
|
||||
bool RegsX86::SetPcFromReturnAddress(Memory* process_memory) {
|
||||
// Attempt to get the return address from the top of the stack.
|
||||
uint32_t new_pc;
|
||||
if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_pc(new_pc);
|
||||
return true;
|
||||
}
|
||||
|
||||
RegsX86_64::RegsX86_64()
|
||||
: RegsImpl<uint64_t>(X86_64_REG_LAST, X86_64_REG_SP, Location(LOCATION_SP_OFFSET, -8)) {}
|
||||
|
||||
|
@ -161,6 +190,17 @@ void RegsX86_64::SetFromRaw() {
|
|||
set_sp(regs_[X86_64_REG_SP]);
|
||||
}
|
||||
|
||||
bool RegsX86_64::SetPcFromReturnAddress(Memory* process_memory) {
|
||||
// Attempt to get the return address from the top of the stack.
|
||||
uint64_t new_pc;
|
||||
if (!process_memory->Read(sp_, &new_pc, sizeof(new_pc)) || new_pc == pc()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
set_pc(new_pc);
|
||||
return true;
|
||||
}
|
||||
|
||||
static Regs* ReadArm(void* remote_data) {
|
||||
arm_user_regs* user = reinterpret_cast<arm_user_regs*>(remote_data);
|
||||
|
||||
|
|
|
@ -0,0 +1,145 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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 <elf.h>
|
||||
#include <inttypes.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
void Unwinder::FillInFrame(MapInfo* map_info, uint64_t* rel_pc) {
|
||||
size_t frame_num = frames_.size();
|
||||
frames_.resize(frame_num + 1);
|
||||
FrameData* frame = &frames_.at(frame_num);
|
||||
frame->num = frame_num;
|
||||
frame->pc = regs_->pc();
|
||||
frame->sp = regs_->sp();
|
||||
frame->rel_pc = frame->pc;
|
||||
|
||||
if (map_info == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
Elf* elf = map_info->GetElf(process_memory_, true);
|
||||
*rel_pc = elf->GetRelPc(regs_->pc(), map_info);
|
||||
if (frame_num != 0) {
|
||||
// Don't adjust the first frame pc.
|
||||
frame->rel_pc = regs_->GetAdjustedPc(*rel_pc, elf);
|
||||
|
||||
// Adjust the original pc.
|
||||
frame->pc -= *rel_pc - frame->rel_pc;
|
||||
} else {
|
||||
frame->rel_pc = *rel_pc;
|
||||
}
|
||||
|
||||
frame->map_name = map_info->name;
|
||||
frame->map_offset = map_info->elf_offset;
|
||||
frame->map_start = map_info->start;
|
||||
frame->map_end = map_info->end;
|
||||
|
||||
if (!elf->GetFunctionName(frame->rel_pc, &frame->function_name, &frame->function_offset)) {
|
||||
frame->function_name = "";
|
||||
frame->function_offset = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Unwinder::Unwind() {
|
||||
frames_.clear();
|
||||
|
||||
bool return_address_attempt = false;
|
||||
for (; frames_.size() < max_frames_;) {
|
||||
MapInfo* map_info = maps_->Find(regs_->pc());
|
||||
|
||||
uint64_t rel_pc;
|
||||
FillInFrame(map_info, &rel_pc);
|
||||
|
||||
bool stepped;
|
||||
if (map_info == nullptr) {
|
||||
stepped = false;
|
||||
} else {
|
||||
bool finished;
|
||||
stepped = map_info->elf->Step(rel_pc + map_info->elf_offset, regs_, process_memory_.get(),
|
||||
&finished);
|
||||
if (stepped && finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!stepped) {
|
||||
if (return_address_attempt) {
|
||||
// Remove the speculative frame.
|
||||
frames_.pop_back();
|
||||
break;
|
||||
} else {
|
||||
// Steping didn't work, try this secondary method.
|
||||
if (!regs_->SetPcFromReturnAddress(process_memory_.get())) {
|
||||
break;
|
||||
}
|
||||
return_address_attempt = true;
|
||||
}
|
||||
} else {
|
||||
return_address_attempt = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string Unwinder::FormatFrame(size_t frame_num) {
|
||||
if (frame_num >= frames_.size()) {
|
||||
return "";
|
||||
}
|
||||
return FormatFrame(frames_[frame_num],
|
||||
regs_->MachineType() == EM_ARM || regs_->MachineType() == EM_386);
|
||||
}
|
||||
|
||||
std::string Unwinder::FormatFrame(const FrameData& frame, bool bits32) {
|
||||
std::string data;
|
||||
|
||||
if (bits32) {
|
||||
data += android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame.num, frame.rel_pc);
|
||||
} else {
|
||||
data += android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame.num, frame.rel_pc);
|
||||
}
|
||||
|
||||
if (frame.map_offset != 0) {
|
||||
data += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", frame.map_offset);
|
||||
}
|
||||
|
||||
if (frame.map_start == frame.map_end) {
|
||||
// No valid map associated with this frame.
|
||||
data += " <unknown>";
|
||||
} else if (!frame.map_name.empty()) {
|
||||
data += " " + frame.map_name;
|
||||
} else {
|
||||
data += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", frame.map_start);
|
||||
}
|
||||
if (!frame.function_name.empty()) {
|
||||
data += " (" + frame.function_name;
|
||||
if (frame.function_offset != 0) {
|
||||
data += android::base::StringPrintf("+%" PRId64, frame.function_offset);
|
||||
}
|
||||
data += ')';
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
|
@ -76,7 +76,7 @@ class DwarfSection {
|
|||
|
||||
virtual bool Init(uint64_t offset, uint64_t size) = 0;
|
||||
|
||||
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*) = 0;
|
||||
virtual bool Eval(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*) = 0;
|
||||
|
||||
virtual bool GetFdeOffsetFromPc(uint64_t pc, uint64_t* fde_offset) = 0;
|
||||
|
||||
|
@ -100,7 +100,7 @@ class DwarfSection {
|
|||
|
||||
virtual uint64_t AdjustPcFromFde(uint64_t pc) = 0;
|
||||
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory);
|
||||
bool Step(uint64_t pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
|
||||
protected:
|
||||
DwarfMemory memory_;
|
||||
|
@ -119,7 +119,7 @@ class DwarfSectionImpl : public DwarfSection {
|
|||
virtual ~DwarfSectionImpl() = default;
|
||||
|
||||
bool Eval(const DwarfCie* cie, Memory* regular_memory, const dwarf_loc_regs_t& loc_regs,
|
||||
Regs* regs) override;
|
||||
Regs* regs, bool* finished) override;
|
||||
|
||||
const DwarfCie* GetCie(uint64_t offset);
|
||||
bool FillInCie(DwarfCie* cie);
|
||||
|
|
|
@ -50,7 +50,7 @@ class Elf {
|
|||
|
||||
uint64_t GetRelPc(uint64_t pc, const MapInfo* map_info);
|
||||
|
||||
bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
|
||||
bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
|
||||
ElfInterface* CreateInterfaceFromMemory(Memory* memory);
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class ElfInterface {
|
|||
|
||||
virtual bool GetFunctionName(uint64_t addr, std::string* name, uint64_t* offset) = 0;
|
||||
|
||||
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory);
|
||||
virtual bool Step(uint64_t rel_pc, Regs* regs, Memory* process_memory, bool* finished);
|
||||
|
||||
Memory* CreateGnuDebugdataMemory();
|
||||
|
||||
|
|
|
@ -63,6 +63,8 @@ class Regs {
|
|||
|
||||
virtual void SetFromRaw() = 0;
|
||||
|
||||
virtual bool SetPcFromReturnAddress(Memory* process_memory) = 0;
|
||||
|
||||
uint16_t sp_reg() { return sp_reg_; }
|
||||
uint16_t total_regs() { return total_regs_; }
|
||||
|
||||
|
@ -113,6 +115,8 @@ class RegsArm : public RegsImpl<uint32_t> {
|
|||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool SetPcFromReturnAddress(Memory* process_memory) override;
|
||||
|
||||
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
|
||||
};
|
||||
|
||||
|
@ -127,6 +131,8 @@ class RegsArm64 : public RegsImpl<uint64_t> {
|
|||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool SetPcFromReturnAddress(Memory* process_memory) override;
|
||||
|
||||
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
|
||||
};
|
||||
|
||||
|
@ -141,6 +147,8 @@ class RegsX86 : public RegsImpl<uint32_t> {
|
|||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool SetPcFromReturnAddress(Memory* process_memory) override;
|
||||
|
||||
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
|
||||
|
||||
void SetFromUcontext(x86_ucontext_t* ucontext);
|
||||
|
@ -157,6 +165,8 @@ class RegsX86_64 : public RegsImpl<uint64_t> {
|
|||
|
||||
void SetFromRaw() override;
|
||||
|
||||
bool SetPcFromReturnAddress(Memory* process_memory) override;
|
||||
|
||||
bool StepIfSignalHandler(uint64_t rel_pc, Elf* elf, Memory* process_memory) override;
|
||||
|
||||
void SetFromUcontext(x86_64_ucontext_t* ucontext);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (C) 2017 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.
|
||||
*/
|
||||
|
||||
#ifndef _LIBUNWINDSTACK_UNWINDER_H
|
||||
#define _LIBUNWINDSTACK_UNWINDER_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
|
||||
namespace unwindstack {
|
||||
|
||||
struct FrameData {
|
||||
size_t num;
|
||||
|
||||
uint64_t rel_pc;
|
||||
uint64_t pc;
|
||||
uint64_t sp;
|
||||
|
||||
std::string function_name;
|
||||
uint64_t function_offset;
|
||||
|
||||
std::string map_name;
|
||||
uint64_t map_offset;
|
||||
uint64_t map_start;
|
||||
uint64_t map_end;
|
||||
};
|
||||
|
||||
class Unwinder {
|
||||
public:
|
||||
Unwinder(size_t max_frames, Maps* maps, Regs* regs, std::shared_ptr<Memory> process_memory)
|
||||
: max_frames_(max_frames), maps_(maps), regs_(regs), process_memory_(process_memory) {
|
||||
frames_.reserve(max_frames);
|
||||
}
|
||||
~Unwinder() = default;
|
||||
|
||||
void Unwind();
|
||||
|
||||
size_t NumFrames() { return frames_.size(); }
|
||||
|
||||
const std::vector<FrameData>& frames() { return frames_; }
|
||||
|
||||
std::string FormatFrame(size_t frame_num);
|
||||
static std::string FormatFrame(const FrameData& frame, bool bits32);
|
||||
|
||||
private:
|
||||
void FillInFrame(MapInfo* map_info, uint64_t* rel_pc);
|
||||
|
||||
size_t max_frames_;
|
||||
Maps* maps_;
|
||||
Regs* regs_;
|
||||
std::vector<FrameData> frames_;
|
||||
std::shared_ptr<Memory> process_memory_;
|
||||
};
|
||||
|
||||
} // namespace unwindstack
|
||||
|
||||
#endif // _LIBUNWINDSTACK_UNWINDER_H
|
|
@ -98,7 +98,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_eval_fail) {
|
|||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_MEMORY_INVALID, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -113,7 +114,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_no_stack) {
|
|||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x96, 0x96, 0x96});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -130,7 +132,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr) {
|
|||
TypeParam cfa_value = 0x12345;
|
||||
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x12345U, regs.sp());
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
}
|
||||
|
@ -146,7 +150,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_val_expr) {
|
|||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
ASSERT_FALSE(finished);
|
||||
EXPECT_EQ(0x80000000U, regs.sp());
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
}
|
||||
|
@ -162,7 +168,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_expr_is_register) {
|
|||
regs[9] = 0x3000;
|
||||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x50, 0x96, 0x96});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x2, 0x5000}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_NOT_IMPLEMENTED, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -171,7 +178,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_bad_regs) {
|
|||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -180,7 +188,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_no_cfa) {
|
|||
RegsFake<TypeParam> regs(10, 9);
|
||||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_CFA_NOT_DEFINED, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -190,25 +199,26 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_bad) {
|
|||
dwarf_loc_regs_t loc_regs;
|
||||
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {20, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearError();
|
||||
loc_regs.erase(CFA_REG);
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_INVALID, {0, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearError();
|
||||
loc_regs.erase(CFA_REG);
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_OFFSET, {0, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
|
||||
this->section_->TestClearError();
|
||||
loc_regs.erase(CFA_REG);
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -222,7 +232,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_prev) {
|
|||
regs[5] = 0x20;
|
||||
regs[9] = 0x3000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {9, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x2000U, regs.sp());
|
||||
}
|
||||
|
@ -238,7 +250,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_cfa_register_from_value) {
|
|||
regs[6] = 0x4000;
|
||||
regs[9] = 0x3000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {6, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x4000U, regs.sp());
|
||||
}
|
||||
|
@ -254,7 +268,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_double_indirection) {
|
|||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {3, 0}};
|
||||
loc_regs[9] = DwarfLocation{DWARF_LOCATION_REGISTER, {1, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_STATE, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -268,7 +283,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_invalid_register) {
|
|||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_REGISTER, {10, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(DWARF_ERROR_ILLEGAL_VALUE, this->section_->last_error());
|
||||
}
|
||||
|
||||
|
@ -292,7 +308,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_different_reg_locations) {
|
|||
loc_regs[1] = DwarfLocation{DWARF_LOCATION_VAL_OFFSET, {0x100, 0}};
|
||||
loc_regs[2] = DwarfLocation{DWARF_LOCATION_OFFSET, {0x50, 0}};
|
||||
loc_regs[3] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x10U, regs.pc());
|
||||
EXPECT_EQ(0x2100U, regs.sp());
|
||||
EXPECT_EQ(0x2200U, regs[1]);
|
||||
|
@ -315,7 +333,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address_undefined) {
|
|||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_UNDEFINED, {0, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_TRUE(finished);
|
||||
EXPECT_EQ(0U, regs.pc());
|
||||
EXPECT_EQ(0x10U, regs.sp());
|
||||
}
|
||||
|
@ -330,7 +350,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_return_address) {
|
|||
regs[5] = 0x20;
|
||||
regs[8] = 0x10;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x10U, regs.sp());
|
||||
}
|
||||
|
@ -347,7 +369,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_ignore_large_reg_loc) {
|
|||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
// This should not result in any errors.
|
||||
loc_regs[20] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x20U, regs.pc());
|
||||
EXPECT_EQ(0x10U, regs.sp());
|
||||
}
|
||||
|
@ -365,7 +389,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_expr) {
|
|||
this->memory_.SetMemory(0x80000000, &cfa_value, sizeof(cfa_value));
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x3000U, regs.sp());
|
||||
EXPECT_EQ(0x12345U, regs.pc());
|
||||
}
|
||||
|
@ -381,7 +407,9 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_reg_val_expr) {
|
|||
this->memory_.SetMemory(0x5000, std::vector<uint8_t>{0x0c, 0x00, 0x00, 0x00, 0x80});
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
loc_regs[5] = DwarfLocation{DWARF_LOCATION_VAL_EXPRESSION, {0x4, 0x5000}};
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_TRUE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_FALSE(finished);
|
||||
EXPECT_EQ(0x3000U, regs.sp());
|
||||
EXPECT_EQ(0x80000000U, regs.pc());
|
||||
}
|
||||
|
@ -396,7 +424,8 @@ TYPED_TEST_P(DwarfSectionImplTest, Eval_same_cfa_same_pc) {
|
|||
regs[5] = 0x100;
|
||||
regs[8] = 0x2000;
|
||||
loc_regs[CFA_REG] = DwarfLocation{DWARF_LOCATION_REGISTER, {8, 0}};
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s));
|
||||
bool finished;
|
||||
ASSERT_FALSE(this->section_->Eval(&cie, &this->memory_, loc_regs, ®s, &finished));
|
||||
EXPECT_EQ(0x2000U, regs.sp());
|
||||
EXPECT_EQ(0x100U, regs.pc());
|
||||
}
|
||||
|
|
|
@ -32,7 +32,7 @@ class MockDwarfSection : public DwarfSection {
|
|||
|
||||
MOCK_METHOD4(Log, bool(uint8_t, uint64_t, uint64_t, const DwarfFde*));
|
||||
|
||||
MOCK_METHOD4(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*));
|
||||
MOCK_METHOD5(Eval, bool(const DwarfCie*, Memory*, const dwarf_loc_regs_t&, Regs*, bool*));
|
||||
|
||||
MOCK_METHOD3(GetCfaLocationInfo, bool(uint64_t, const DwarfFde*, dwarf_loc_regs_t*));
|
||||
|
||||
|
@ -104,7 +104,8 @@ TEST_F(DwarfSectionTest, Step_fail_fde) {
|
|||
EXPECT_CALL(mock_section, GetFdeOffsetFromPc(0x1000, ::testing::_))
|
||||
.WillOnce(::testing::Return(false));
|
||||
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
|
||||
bool finished;
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_fail_cie_null) {
|
||||
|
@ -118,7 +119,8 @@ TEST_F(DwarfSectionTest, Step_fail_cie_null) {
|
|||
.WillOnce(::testing::Return(true));
|
||||
EXPECT_CALL(mock_section, GetFdeFromOffset(::testing::_)).WillOnce(::testing::Return(&fde));
|
||||
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
|
||||
bool finished;
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
|
||||
|
@ -136,7 +138,8 @@ TEST_F(DwarfSectionTest, Step_fail_cfa_location) {
|
|||
EXPECT_CALL(mock_section, GetCfaLocationInfo(0x1000, &fde, ::testing::_))
|
||||
.WillOnce(::testing::Return(false));
|
||||
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr));
|
||||
bool finished;
|
||||
ASSERT_FALSE(mock_section.Step(0x1000, nullptr, nullptr, &finished));
|
||||
}
|
||||
|
||||
TEST_F(DwarfSectionTest, Step_pass) {
|
||||
|
@ -155,10 +158,11 @@ TEST_F(DwarfSectionTest, Step_pass) {
|
|||
.WillOnce(::testing::Return(true));
|
||||
|
||||
MemoryFake process;
|
||||
EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr))
|
||||
EXPECT_CALL(mock_section, Eval(&cie, &process, ::testing::_, nullptr, ::testing::_))
|
||||
.WillOnce(::testing::Return(true));
|
||||
|
||||
ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process));
|
||||
bool finished;
|
||||
ASSERT_TRUE(mock_section.Step(0x1000, nullptr, &process, &finished));
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -322,7 +322,8 @@ TEST_F(ElfInterfaceArmTest, StepExidx) {
|
|||
ElfInterfaceArm interface(&memory_);
|
||||
|
||||
// FindEntry fails.
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr));
|
||||
bool finished;
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, nullptr, nullptr, &finished));
|
||||
|
||||
// ExtractEntry should fail.
|
||||
interface.set_start_offset(0x1000);
|
||||
|
@ -335,15 +336,16 @@ TEST_F(ElfInterfaceArmTest, StepExidx) {
|
|||
regs[ARM_REG_LR] = 0x20000;
|
||||
regs.set_sp(regs[ARM_REG_SP]);
|
||||
regs.set_pc(0x1234);
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_));
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
|
||||
// Eval should fail.
|
||||
memory_.SetData32(0x1004, 0x81000000);
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_));
|
||||
ASSERT_FALSE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
|
||||
// Everything should pass.
|
||||
memory_.SetData32(0x1004, 0x80b0b0b0);
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_));
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_FALSE(finished);
|
||||
ASSERT_EQ(0x1000U, regs.sp());
|
||||
ASSERT_EQ(0x1000U, regs[ARM_REG_SP]);
|
||||
ASSERT_EQ(0x20000U, regs.pc());
|
||||
|
@ -367,11 +369,57 @@ TEST_F(ElfInterfaceArmTest, StepExidx_pc_set) {
|
|||
regs.set_pc(0x1234);
|
||||
|
||||
// Everything should pass.
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_));
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_FALSE(finished);
|
||||
ASSERT_EQ(0x10004U, regs.sp());
|
||||
ASSERT_EQ(0x10004U, regs[ARM_REG_SP]);
|
||||
ASSERT_EQ(0x10U, regs.pc());
|
||||
ASSERT_EQ(0x10U, regs[ARM_REG_PC]);
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceArmTest, StepExidx_cant_unwind) {
|
||||
ElfInterfaceArm interface(&memory_);
|
||||
|
||||
interface.set_start_offset(0x1000);
|
||||
interface.set_total_entries(1);
|
||||
memory_.SetData32(0x1000, 0x6000);
|
||||
memory_.SetData32(0x1004, 1);
|
||||
|
||||
RegsArm regs;
|
||||
regs[ARM_REG_SP] = 0x10000;
|
||||
regs[ARM_REG_LR] = 0x20000;
|
||||
regs.set_sp(regs[ARM_REG_SP]);
|
||||
regs.set_pc(0x1234);
|
||||
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(finished);
|
||||
ASSERT_EQ(0x10000U, regs.sp());
|
||||
ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
|
||||
ASSERT_EQ(0x1234U, regs.pc());
|
||||
}
|
||||
|
||||
TEST_F(ElfInterfaceArmTest, StepExidx_refuse_unwind) {
|
||||
ElfInterfaceArm interface(&memory_);
|
||||
|
||||
interface.set_start_offset(0x1000);
|
||||
interface.set_total_entries(1);
|
||||
memory_.SetData32(0x1000, 0x6000);
|
||||
memory_.SetData32(0x1004, 0x808000b0);
|
||||
|
||||
RegsArm regs;
|
||||
regs[ARM_REG_SP] = 0x10000;
|
||||
regs[ARM_REG_LR] = 0x20000;
|
||||
regs.set_sp(regs[ARM_REG_SP]);
|
||||
regs.set_pc(0x1234);
|
||||
|
||||
bool finished;
|
||||
ASSERT_TRUE(interface.StepExidx(0x7000, ®s, &process_memory_, &finished));
|
||||
ASSERT_TRUE(finished);
|
||||
ASSERT_EQ(0x10000U, regs.sp());
|
||||
ASSERT_EQ(0x10000U, regs[ARM_REG_SP]);
|
||||
ASSERT_EQ(0x1234U, regs.pc());
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -129,7 +129,8 @@ TEST_F(ElfTest, elf_invalid) {
|
|||
uint64_t func_offset;
|
||||
ASSERT_FALSE(elf.GetFunctionName(0, &name, &func_offset));
|
||||
|
||||
ASSERT_FALSE(elf.Step(0, nullptr, nullptr));
|
||||
bool finished;
|
||||
ASSERT_FALSE(elf.Step(0, nullptr, nullptr, &finished));
|
||||
}
|
||||
|
||||
TEST_F(ElfTest, elf32_invalid_machine) {
|
||||
|
|
|
@ -35,6 +35,7 @@ class RegsFake : public RegsImpl<TypeParam> {
|
|||
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
void SetFromRaw() override {}
|
||||
bool SetPcFromReturnAddress(Memory*) override { return false; }
|
||||
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
|
||||
bool GetReturnAddressFromDefault(Memory*, uint64_t*) { return false; }
|
||||
};
|
||||
|
|
|
@ -46,7 +46,7 @@ class ElfInterfaceFake : public ElfInterface {
|
|||
void InitHeaders() override {}
|
||||
bool GetSoname(std::string*) override { return false; }
|
||||
bool GetFunctionName(uint64_t, std::string*, uint64_t*) override { return false; }
|
||||
bool Step(uint64_t, Regs*, Memory*) override { return false; }
|
||||
bool Step(uint64_t, Regs*, Memory*, bool*) override { return false; }
|
||||
};
|
||||
|
||||
template <typename TypeParam>
|
||||
|
@ -62,6 +62,7 @@ class RegsTestImpl : public RegsImpl<TypeParam> {
|
|||
|
||||
uint64_t GetAdjustedPc(uint64_t, Elf*) override { return 0; }
|
||||
void SetFromRaw() override {}
|
||||
bool SetPcFromReturnAddress(Memory*) override { return false; }
|
||||
bool StepIfSignalHandler(uint64_t, Elf*, Memory*) override { return false; }
|
||||
};
|
||||
|
||||
|
|
|
@ -31,12 +31,13 @@
|
|||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Maps.h>
|
||||
#include <unwindstack/Memory.h>
|
||||
#include <unwindstack/Regs.h>
|
||||
#include <unwindstack/RegsGetLocal.h>
|
||||
#include <unwindstack/Unwinder.h>
|
||||
|
||||
#include "TestUtils.h"
|
||||
|
||||
|
@ -56,11 +57,11 @@ static void ResetGlobals() {
|
|||
g_ucontext = 0;
|
||||
}
|
||||
|
||||
static std::vector<const char*> kFunctionOrder{"InnerFunction", "MiddleFunction", "OuterFunction"};
|
||||
static std::vector<const char*> kFunctionOrder{"OuterFunction", "MiddleFunction", "InnerFunction"};
|
||||
|
||||
static std::vector<const char*> kFunctionSignalOrder{"SignalInnerFunction", "SignalMiddleFunction",
|
||||
"SignalOuterFunction", "InnerFunction",
|
||||
"MiddleFunction", "OuterFunction"};
|
||||
static std::vector<const char*> kFunctionSignalOrder{"OuterFunction", "MiddleFunction",
|
||||
"InnerFunction", "SignalOuterFunction",
|
||||
"SignalMiddleFunction", "SignalInnerFunction"};
|
||||
|
||||
static void SignalHandler(int, siginfo_t*, void* sigcontext) {
|
||||
g_ucontext = reinterpret_cast<uintptr_t>(sigcontext);
|
||||
|
@ -86,62 +87,44 @@ static void SignalCallerHandler(int, siginfo_t*, void*) {
|
|||
SignalOuterFunction();
|
||||
}
|
||||
|
||||
static std::string ErrorMsg(const std::vector<const char*>& function_names, size_t index,
|
||||
std::stringstream& unwind_stream) {
|
||||
static std::string ErrorMsg(const std::vector<const char*>& function_names, Unwinder& unwinder) {
|
||||
std::string unwind;
|
||||
for (size_t i = 0; i < unwinder.NumFrames(); i++) {
|
||||
unwind += unwinder.FormatFrame(i) + '\n';
|
||||
}
|
||||
|
||||
return std::string(
|
||||
"Unwind completed without finding all frames\n"
|
||||
" Looking for function: ") +
|
||||
function_names[index] + "\n" + "Unwind data:\n" + unwind_stream.str();
|
||||
function_names.front() + "\n" + "Unwind data:\n" + unwind;
|
||||
}
|
||||
|
||||
static void VerifyUnwind(pid_t pid, Maps* maps, Regs* regs,
|
||||
std::vector<const char*>& function_names) {
|
||||
size_t function_name_index = 0;
|
||||
std::vector<const char*> expected_function_names) {
|
||||
auto process_memory(Memory::CreateProcessMemory(pid));
|
||||
|
||||
auto process_memory = Memory::CreateProcessMemory(pid);
|
||||
std::stringstream unwind_stream;
|
||||
unwind_stream << std::hex;
|
||||
for (size_t frame_num = 0; frame_num < 64; frame_num++) {
|
||||
ASSERT_NE(0U, regs->pc()) << ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
MapInfo* map_info = maps->Find(regs->pc());
|
||||
ASSERT_TRUE(map_info != nullptr) << ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
Unwinder unwinder(512, maps, regs, process_memory);
|
||||
unwinder.Unwind();
|
||||
|
||||
Elf* elf = map_info->GetElf(process_memory, true);
|
||||
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
|
||||
uint64_t adjusted_rel_pc = rel_pc;
|
||||
if (frame_num != 0) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
|
||||
}
|
||||
unwind_stream << " PC: 0x" << regs->pc() << " Rel: 0x" << adjusted_rel_pc;
|
||||
unwind_stream << " Map: ";
|
||||
if (!map_info->name.empty()) {
|
||||
unwind_stream << map_info->name;
|
||||
} else {
|
||||
unwind_stream << " anonymous";
|
||||
}
|
||||
unwind_stream << "<" << map_info->start << "-" << map_info->end << ">";
|
||||
|
||||
std::string name;
|
||||
uint64_t func_offset;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
|
||||
if (name == function_names[function_name_index]) {
|
||||
if (++function_name_index == function_names.size()) {
|
||||
return;
|
||||
}
|
||||
std::string expected_function = expected_function_names.back();
|
||||
expected_function_names.pop_back();
|
||||
for (auto& frame : unwinder.frames()) {
|
||||
if (frame.function_name == expected_function) {
|
||||
if (expected_function_names.empty()) {
|
||||
break;
|
||||
}
|
||||
unwind_stream << " " << name;
|
||||
expected_function = expected_function_names.back();
|
||||
expected_function_names.pop_back();
|
||||
}
|
||||
unwind_stream << "\n";
|
||||
ASSERT_TRUE(elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get()))
|
||||
<< ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
}
|
||||
ASSERT_TRUE(false) << ErrorMsg(function_names, function_name_index, unwind_stream);
|
||||
|
||||
ASSERT_TRUE(expected_function_names.empty()) << ErrorMsg(expected_function_names, unwinder);
|
||||
}
|
||||
|
||||
// This test assumes that this code is compiled with optimizations turned
|
||||
// off. If this doesn't happen, then all of the calls will be optimized
|
||||
// away.
|
||||
extern "C" void InnerFunction(bool local) {
|
||||
extern "C" void InnerFunction(bool local, bool trigger_invalid_call) {
|
||||
if (local) {
|
||||
LocalMaps maps;
|
||||
ASSERT_TRUE(maps.Parse());
|
||||
|
@ -152,17 +135,21 @@ extern "C" void InnerFunction(bool local) {
|
|||
} else {
|
||||
g_ready_for_remote = true;
|
||||
g_ready = true;
|
||||
if (trigger_invalid_call) {
|
||||
void (*crash_func)() = nullptr;
|
||||
crash_func();
|
||||
}
|
||||
while (!g_finish.load()) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void MiddleFunction(bool local) {
|
||||
InnerFunction(local);
|
||||
extern "C" void MiddleFunction(bool local, bool trigger_invalid_call) {
|
||||
InnerFunction(local, trigger_invalid_call);
|
||||
}
|
||||
|
||||
extern "C" void OuterFunction(bool local) {
|
||||
MiddleFunction(local);
|
||||
extern "C" void OuterFunction(bool local, bool trigger_invalid_call) {
|
||||
MiddleFunction(local, trigger_invalid_call);
|
||||
}
|
||||
|
||||
class UnwindTest : public ::testing::Test {
|
||||
|
@ -171,7 +158,7 @@ class UnwindTest : public ::testing::Test {
|
|||
};
|
||||
|
||||
TEST_F(UnwindTest, local) {
|
||||
OuterFunction(true);
|
||||
OuterFunction(true, false);
|
||||
}
|
||||
|
||||
void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* completed) {
|
||||
|
@ -206,7 +193,7 @@ void WaitForRemote(pid_t pid, uint64_t addr, bool leave_attached, bool* complete
|
|||
TEST_F(UnwindTest, remote) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
OuterFunction(false);
|
||||
OuterFunction(false, false);
|
||||
exit(0);
|
||||
}
|
||||
ASSERT_NE(-1, pid);
|
||||
|
@ -231,7 +218,7 @@ TEST_F(UnwindTest, from_context) {
|
|||
std::atomic_int tid(0);
|
||||
std::thread thread([&]() {
|
||||
tid = syscall(__NR_gettid);
|
||||
OuterFunction(false);
|
||||
OuterFunction(false, false);
|
||||
});
|
||||
|
||||
struct sigaction act, oldact;
|
||||
|
@ -273,25 +260,27 @@ TEST_F(UnwindTest, from_context) {
|
|||
thread.join();
|
||||
}
|
||||
|
||||
static void RemoteThroughSignal(unsigned int sa_flags) {
|
||||
static void RemoteThroughSignal(int signal, unsigned int sa_flags) {
|
||||
pid_t pid;
|
||||
if ((pid = fork()) == 0) {
|
||||
struct sigaction act, oldact;
|
||||
memset(&act, 0, sizeof(act));
|
||||
act.sa_sigaction = SignalCallerHandler;
|
||||
act.sa_flags = SA_RESTART | SA_ONSTACK | sa_flags;
|
||||
ASSERT_EQ(0, sigaction(SIGUSR1, &act, &oldact));
|
||||
ASSERT_EQ(0, sigaction(signal, &act, &oldact));
|
||||
|
||||
OuterFunction(false);
|
||||
OuterFunction(false, signal == SIGSEGV);
|
||||
exit(0);
|
||||
}
|
||||
ASSERT_NE(-1, pid);
|
||||
TestScopedPidReaper reap(pid);
|
||||
|
||||
bool completed;
|
||||
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
|
||||
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
|
||||
ASSERT_EQ(0, kill(pid, SIGUSR1));
|
||||
if (signal != SIGSEGV) {
|
||||
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_ready_for_remote), false, &completed);
|
||||
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be ready.";
|
||||
ASSERT_EQ(0, kill(pid, SIGUSR1));
|
||||
}
|
||||
WaitForRemote(pid, reinterpret_cast<uint64_t>(&g_signal_ready_for_remote), true, &completed);
|
||||
ASSERT_TRUE(completed) << "Timed out waiting for remote process to be in signal handler.";
|
||||
|
||||
|
@ -307,11 +296,19 @@ static void RemoteThroughSignal(unsigned int sa_flags) {
|
|||
}
|
||||
|
||||
TEST_F(UnwindTest, remote_through_signal) {
|
||||
RemoteThroughSignal(0);
|
||||
RemoteThroughSignal(SIGUSR1, 0);
|
||||
}
|
||||
|
||||
TEST_F(UnwindTest, remote_through_signal_sa_siginfo) {
|
||||
RemoteThroughSignal(SA_SIGINFO);
|
||||
RemoteThroughSignal(SIGUSR1, SA_SIGINFO);
|
||||
}
|
||||
|
||||
TEST_F(UnwindTest, remote_through_signal_with_invalid_func) {
|
||||
RemoteThroughSignal(SIGSEGV, 0);
|
||||
}
|
||||
|
||||
TEST_F(UnwindTest, remote_through_signal_sa_siginfo_with_invalid_func) {
|
||||
RemoteThroughSignal(SIGSEGV, SA_SIGINFO);
|
||||
}
|
||||
|
||||
} // namespace unwindstack
|
||||
|
|
|
@ -26,7 +26,11 @@
|
|||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <android-base/stringprintf.h>
|
||||
|
||||
#include <unwindstack/Elf.h>
|
||||
#include <unwindstack/MapInfo.h>
|
||||
|
@ -55,6 +59,62 @@ static bool Detach(pid_t pid) {
|
|||
return ptrace(PTRACE_DETACH, pid, 0, 0) == 0;
|
||||
}
|
||||
|
||||
std::string GetFrameInfo(size_t frame_num, unwindstack::Regs* regs,
|
||||
const std::shared_ptr<unwindstack::Memory>& process_memory,
|
||||
unwindstack::MapInfo* map_info, uint64_t* rel_pc) {
|
||||
bool bits32;
|
||||
switch (regs->MachineType()) {
|
||||
case EM_ARM:
|
||||
case EM_386:
|
||||
bits32 = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
bits32 = false;
|
||||
}
|
||||
|
||||
if (map_info == nullptr) {
|
||||
if (bits32) {
|
||||
return android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame_num, regs->pc());
|
||||
} else {
|
||||
return android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame_num, regs->pc());
|
||||
}
|
||||
}
|
||||
|
||||
unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
|
||||
*rel_pc = elf->GetRelPc(regs->pc(), map_info);
|
||||
uint64_t adjusted_rel_pc = *rel_pc;
|
||||
// Don't need to adjust the first frame pc.
|
||||
if (frame_num != 0) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(*rel_pc, elf);
|
||||
}
|
||||
|
||||
std::string line;
|
||||
if (bits32) {
|
||||
line = android::base::StringPrintf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
|
||||
} else {
|
||||
line = android::base::StringPrintf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
|
||||
}
|
||||
if (!map_info->name.empty()) {
|
||||
line += " " + map_info->name;
|
||||
if (map_info->elf_offset != 0) {
|
||||
line += android::base::StringPrintf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
|
||||
}
|
||||
} else {
|
||||
line += android::base::StringPrintf(" <anonymous:%" PRIx64 ">", map_info->offset);
|
||||
}
|
||||
uint64_t func_offset;
|
||||
std::string func_name;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &func_name, &func_offset)) {
|
||||
line += " (" + func_name;
|
||||
if (func_offset != 0) {
|
||||
line += android::base::StringPrintf("+%" PRId64, func_offset);
|
||||
}
|
||||
line += ')';
|
||||
}
|
||||
return line;
|
||||
}
|
||||
|
||||
void DoUnwind(pid_t pid) {
|
||||
unwindstack::RemoteMaps remote_maps(pid);
|
||||
if (!remote_maps.Parse()) {
|
||||
|
@ -68,7 +128,6 @@ void DoUnwind(pid_t pid) {
|
|||
return;
|
||||
}
|
||||
|
||||
bool bits32 = true;
|
||||
printf("ABI: ");
|
||||
switch (regs->MachineType()) {
|
||||
case EM_ARM:
|
||||
|
@ -79,11 +138,9 @@ void DoUnwind(pid_t pid) {
|
|||
break;
|
||||
case EM_AARCH64:
|
||||
printf("arm64");
|
||||
bits32 = false;
|
||||
break;
|
||||
case EM_X86_64:
|
||||
printf("x86_64");
|
||||
bits32 = false;
|
||||
break;
|
||||
default:
|
||||
printf("unknown\n");
|
||||
|
@ -92,52 +149,48 @@ void DoUnwind(pid_t pid) {
|
|||
printf("\n");
|
||||
|
||||
auto process_memory = unwindstack::Memory::CreateProcessMemory(pid);
|
||||
bool return_address_attempt = false;
|
||||
std::vector<std::string> frames;
|
||||
for (size_t frame_num = 0; frame_num < 64; frame_num++) {
|
||||
if (regs->pc() == 0) {
|
||||
break;
|
||||
}
|
||||
unwindstack::MapInfo* map_info = remote_maps.Find(regs->pc());
|
||||
uint64_t rel_pc;
|
||||
frames.push_back(GetFrameInfo(frame_num, regs, process_memory, map_info, &rel_pc));
|
||||
bool stepped;
|
||||
if (map_info == nullptr) {
|
||||
printf("Failed to find map data for the pc\n");
|
||||
break;
|
||||
}
|
||||
|
||||
unwindstack::Elf* elf = map_info->GetElf(process_memory, true);
|
||||
|
||||
uint64_t rel_pc = elf->GetRelPc(regs->pc(), map_info);
|
||||
uint64_t adjusted_rel_pc = rel_pc;
|
||||
// Don't need to adjust the first frame pc.
|
||||
if (frame_num != 0) {
|
||||
adjusted_rel_pc = regs->GetAdjustedPc(rel_pc, elf);
|
||||
}
|
||||
|
||||
std::string name;
|
||||
if (bits32) {
|
||||
printf(" #%02zu pc %08" PRIx64, frame_num, adjusted_rel_pc);
|
||||
stepped = false;
|
||||
} else {
|
||||
printf(" #%02zu pc %016" PRIx64, frame_num, adjusted_rel_pc);
|
||||
bool finished;
|
||||
stepped =
|
||||
map_info->elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get(), &finished);
|
||||
if (stepped && finished) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!map_info->name.empty()) {
|
||||
printf(" %s", map_info->name.c_str());
|
||||
if (map_info->elf_offset != 0) {
|
||||
printf(" (offset 0x%" PRIx64 ")", map_info->elf_offset);
|
||||
if (!stepped) {
|
||||
if (return_address_attempt) {
|
||||
// We tried the return address and it didn't work, remove the last
|
||||
// two frames. If this bad frame is the only frame, only remove
|
||||
// the last frame.
|
||||
frames.pop_back();
|
||||
if (frame_num != 1) {
|
||||
frames.pop_back();
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
// Steping didn't work, try this secondary method.
|
||||
if (!regs->SetPcFromReturnAddress(process_memory.get())) {
|
||||
break;
|
||||
}
|
||||
return_address_attempt = true;
|
||||
}
|
||||
} else {
|
||||
printf(" <anonymous:%" PRIx64 ">", map_info->offset);
|
||||
return_address_attempt = false;
|
||||
}
|
||||
uint64_t func_offset;
|
||||
if (elf->GetFunctionName(adjusted_rel_pc, &name, &func_offset)) {
|
||||
printf(" (%s", name.c_str());
|
||||
if (func_offset != 0) {
|
||||
printf("+%" PRId64, func_offset);
|
||||
}
|
||||
printf(")");
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (!elf->Step(rel_pc + map_info->elf_offset, regs, process_memory.get())) {
|
||||
break;
|
||||
}
|
||||
// Print the frames.
|
||||
for (auto& frame : frames) {
|
||||
printf("%s\n", frame.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue