Commit 608afc55 authored by Etienne Pierre-doray's avatar Etienne Pierre-doray Committed by Commit Bot

[Clank SSM]: Use ChromeUnwinderAndroid::DefaultStep.

Most Chrome unwinding failures can be correctly handled bu applying the
default step (using lr) when no cfi is present at the top of stack.

Change-Id: I0349688dcafa3973f1f77f230660312a4140dc19
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2381730
Commit-Queue: Etienne Pierre-Doray <etiennep@chromium.org>
Reviewed-by: default avatarMike Wittman <wittman@chromium.org>
Cr-Commit-Position: refs/heads/master@{#803496}
parent 418f0d13
......@@ -38,10 +38,16 @@ UnwindResult ChromeUnwinderAndroid::TryUnwind(RegisterContext* thread_context,
uintptr_t func_addr = pc - module->GetBaseAddress();
auto entry = cfi_table_->FindEntryForAddress(func_addr);
if (!entry)
return UnwindResult::ABORTED;
if (!Step(thread_context, stack_top, *entry))
if (entry) {
if (!Step(thread_context, stack_top, *entry))
return UnwindResult::ABORTED;
} else if (stack->size() == 1) {
// Try unwinding by sourcing the return address from the lr register.
if (!StepUsingLrRegister(thread_context, stack_top))
return UnwindResult::ABORTED;
} else {
return UnwindResult::ABORTED;
}
stack->emplace_back(RegisterContextInstructionPointer(thread_context),
module_cache->GetModuleForAddress(
RegisterContextInstructionPointer(thread_context)));
......@@ -55,35 +61,45 @@ bool ChromeUnwinderAndroid::Step(RegisterContext* thread_context,
const ArmCFITable::FrameEntry& entry) {
CHECK_NE(RegisterContextStackPointer(thread_context), 0U);
CHECK_LE(RegisterContextStackPointer(thread_context), stack_top);
if (entry.cfa_offset == 0) {
uintptr_t pc = RegisterContextInstructionPointer(thread_context);
uintptr_t return_address = static_cast<uintptr_t>(thread_context->arm_lr);
if (pc == return_address)
return false;
RegisterContextInstructionPointer(thread_context) = return_address;
} else {
// The rules for unwinding using the CFI information are:
// SP_prev = SP_cur + cfa_offset and
// PC_prev = * (SP_prev - ra_offset).
auto new_sp =
CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
CheckedNumeric<uint16_t>(entry.cfa_offset);
if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context)) ||
RegisterContextStackPointer(thread_context) >= stack_top) {
return false;
}
if (entry.cfa_offset == 0)
return StepUsingLrRegister(thread_context, stack_top);
if (entry.ra_offset > entry.cfa_offset)
return false;
// Underflow is prevented because |ra_offset| <= |cfa_offset|.
uintptr_t ip_address = (new_sp - CheckedNumeric<uint16_t>(entry.ra_offset))
.ValueOrDie<uintptr_t>();
RegisterContextInstructionPointer(thread_context) =
*reinterpret_cast<uintptr_t*>(ip_address);
// The rules for unwinding using the CFI information are:
// SP_prev = SP_cur + cfa_offset and
// PC_prev = * (SP_prev - ra_offset).
auto new_sp =
CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
CheckedNumeric<uint16_t>(entry.cfa_offset);
if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context)) ||
RegisterContextStackPointer(thread_context) >= stack_top) {
return false;
}
if (entry.ra_offset > entry.cfa_offset)
return false;
// Underflow is prevented because |ra_offset| <= |cfa_offset|.
uintptr_t ip_address = (new_sp - CheckedNumeric<uint16_t>(entry.ra_offset))
.ValueOrDie<uintptr_t>();
RegisterContextInstructionPointer(thread_context) =
*reinterpret_cast<uintptr_t*>(ip_address);
return true;
}
// static
bool ChromeUnwinderAndroid::StepUsingLrRegister(RegisterContext* thread_context,
uintptr_t stack_top) {
CHECK_NE(RegisterContextStackPointer(thread_context), 0U);
CHECK_LE(RegisterContextStackPointer(thread_context), stack_top);
uintptr_t pc = RegisterContextInstructionPointer(thread_context);
uintptr_t return_address = static_cast<uintptr_t>(thread_context->arm_lr);
// The step failed if the pc doesn't change.
if (pc == return_address)
return false;
RegisterContextInstructionPointer(thread_context) = return_address;
return true;
}
......
......@@ -41,6 +41,9 @@ class BASE_EXPORT ChromeUnwinderAndroid : public Unwinder {
static bool Step(RegisterContext* thread_context,
uintptr_t stack_top,
const ArmCFITable::FrameEntry& entry);
// Fallback setp that attempts to use lr as return address.
static bool StepUsingLrRegister(RegisterContext* thread_context,
uintptr_t stack_top);
const ArmCFITable* cfi_table_;
const uintptr_t chrome_module_base_address_;
......
......@@ -315,10 +315,13 @@ TEST(ChromeUnwinderAndroidTest, TryUnwindNoData) {
reinterpret_cast<uintptr_t>(stack_buffer.data());
context.arm_lr = 0x12AA;
// Aborted because there's no unwind info for the instruction pointer.
// Unwinding will first use arm_lr as fallback because there's no unwind info
// for the instruction pointer, and then abort.
EXPECT_EQ(UnwindResult::ABORTED,
unwinder.TryUnwind(&context, stack_top, &module_cache, &stack));
EXPECT_EQ(std::vector<Frame>({{0x1200, chrome_module}}), stack);
EXPECT_EQ(
std::vector<Frame>({{0x1200, chrome_module}, {0x12AA, chrome_module}}),
stack);
}
} // namespace base
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment