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, ...@@ -38,10 +38,16 @@ UnwindResult ChromeUnwinderAndroid::TryUnwind(RegisterContext* thread_context,
uintptr_t func_addr = pc - module->GetBaseAddress(); uintptr_t func_addr = pc - module->GetBaseAddress();
auto entry = cfi_table_->FindEntryForAddress(func_addr); auto entry = cfi_table_->FindEntryForAddress(func_addr);
if (!entry) if (entry) {
return UnwindResult::ABORTED; if (!Step(thread_context, stack_top, *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; return UnwindResult::ABORTED;
}
stack->emplace_back(RegisterContextInstructionPointer(thread_context), stack->emplace_back(RegisterContextInstructionPointer(thread_context),
module_cache->GetModuleForAddress( module_cache->GetModuleForAddress(
RegisterContextInstructionPointer(thread_context))); RegisterContextInstructionPointer(thread_context)));
...@@ -55,35 +61,45 @@ bool ChromeUnwinderAndroid::Step(RegisterContext* thread_context, ...@@ -55,35 +61,45 @@ bool ChromeUnwinderAndroid::Step(RegisterContext* thread_context,
const ArmCFITable::FrameEntry& entry) { const ArmCFITable::FrameEntry& entry) {
CHECK_NE(RegisterContextStackPointer(thread_context), 0U); CHECK_NE(RegisterContextStackPointer(thread_context), 0U);
CHECK_LE(RegisterContextStackPointer(thread_context), stack_top); CHECK_LE(RegisterContextStackPointer(thread_context), stack_top);
if (entry.cfa_offset == 0) { if (entry.cfa_offset == 0)
uintptr_t pc = RegisterContextInstructionPointer(thread_context); return StepUsingLrRegister(thread_context, stack_top);
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.ra_offset > entry.cfa_offset) // The rules for unwinding using the CFI information are:
return false; // SP_prev = SP_cur + cfa_offset and
// PC_prev = * (SP_prev - ra_offset).
// Underflow is prevented because |ra_offset| <= |cfa_offset|. auto new_sp =
uintptr_t ip_address = (new_sp - CheckedNumeric<uint16_t>(entry.ra_offset)) CheckedNumeric<uintptr_t>(RegisterContextStackPointer(thread_context)) +
.ValueOrDie<uintptr_t>(); CheckedNumeric<uint16_t>(entry.cfa_offset);
RegisterContextInstructionPointer(thread_context) = if (!new_sp.AssignIfValid(&RegisterContextStackPointer(thread_context)) ||
*reinterpret_cast<uintptr_t*>(ip_address); 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; return true;
} }
......
...@@ -41,6 +41,9 @@ class BASE_EXPORT ChromeUnwinderAndroid : public Unwinder { ...@@ -41,6 +41,9 @@ class BASE_EXPORT ChromeUnwinderAndroid : public Unwinder {
static bool Step(RegisterContext* thread_context, static bool Step(RegisterContext* thread_context,
uintptr_t stack_top, uintptr_t stack_top,
const ArmCFITable::FrameEntry& entry); 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 ArmCFITable* cfi_table_;
const uintptr_t chrome_module_base_address_; const uintptr_t chrome_module_base_address_;
......
...@@ -315,10 +315,13 @@ TEST(ChromeUnwinderAndroidTest, TryUnwindNoData) { ...@@ -315,10 +315,13 @@ TEST(ChromeUnwinderAndroidTest, TryUnwindNoData) {
reinterpret_cast<uintptr_t>(stack_buffer.data()); reinterpret_cast<uintptr_t>(stack_buffer.data());
context.arm_lr = 0x12AA; 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, EXPECT_EQ(UnwindResult::ABORTED,
unwinder.TryUnwind(&context, stack_top, &module_cache, &stack)); 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 } // 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