arm: Allow the first function to use linked register as return pc

For a crash at the function entry with corrupted PC, the caller's PC
could be lying in the link register. Using the PC from link register
would be more effective than blindly scanning the stack immediately.

Change-Id: I51673b7298e70faeeab2bfa97075e3c4793f94bc
Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/2678992
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Sim Sun 2021-02-06 04:27:41 -08:00 committed by Mike Frysinger
parent 2d3e030fdb
commit f2b3ab5e0a
3 changed files with 33 additions and 1 deletions

View file

@ -232,7 +232,7 @@ TEST_F(MicrodumpProcessorTest, TestProcessMultiple) {
ASSERT_EQ("arm", state.system_info()->cpu); ASSERT_EQ("arm", state.system_info()->cpu);
ASSERT_EQ("lge/p1_tmo_us/p1:6.0/MRA58K/1603210524c8d:user/release-keys", ASSERT_EQ("lge/p1_tmo_us/p1:6.0/MRA58K/1603210524c8d:user/release-keys",
state.system_info()->os_version); state.system_info()->os_version);
ASSERT_EQ(5U, state.threads()->at(0)->frames()->size()); ASSERT_EQ(6U, state.threads()->at(0)->frames()->size());
} }
TEST_F(MicrodumpProcessorTest, TestProcessMips) { TEST_F(MicrodumpProcessorTest, TestProcessMips) {

View file

@ -238,6 +238,27 @@ StackFrameARM* StackwalkerARM::GetCallerByFramePointer(
return frame; return frame;
} }
StackFrameARM* StackwalkerARM::GetCallerByLinkRegister(
const vector<StackFrame*>& frames) {
StackFrameARM* last_frame = static_cast<StackFrameARM*>(frames.back());
uint32_t last_lr = last_frame->context.iregs[MD_CONTEXT_ARM_REG_LR];
if (!(last_frame->context_validity & StackFrameARM::CONTEXT_VALID_LR) ||
!InstructionAddressSeemsValid(last_lr)) {
return NULL;
}
StackFrameARM* frame = new StackFrameARM();
frame->trust = StackFrame::FRAME_TRUST_SCAN;
frame->context = last_frame->context;
frame->context.iregs[MD_CONTEXT_ARM_REG_PC] = last_lr;
frame->context_validity =
context_frame_validity_ & (~StackFrameARM::CONTEXT_VALID_LR);
return frame;
}
StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack, StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack,
bool stack_scan_allowed) { bool stack_scan_allowed) {
if (!memory_ || !stack) { if (!memory_ || !stack) {
@ -264,6 +285,12 @@ StackFrame* StackwalkerARM::GetCallerFrame(const CallStack* stack,
if (fp_register_ >= 0 && !frame.get()) if (fp_register_ >= 0 && !frame.get())
frame.reset(GetCallerByFramePointer(frames)); frame.reset(GetCallerByFramePointer(frames));
// For the first frame, return address may still be in the LR register at
// entry. Prefer to use LR register than scanning stack if LR register value
// points to a function range.
if (frames.size() == 1 && !frame.get())
frame.reset(GetCallerByLinkRegister(frames));
// If everuthing failed, fall back to stack scanning. // If everuthing failed, fall back to stack scanning.
if (stack_scan_allowed && !frame.get()) if (stack_scan_allowed && !frame.get())
frame.reset(GetCallerByStackScan(frames)); frame.reset(GetCallerByStackScan(frames));

View file

@ -82,6 +82,11 @@ class StackwalkerARM : public Stackwalker {
// Return NULL on failure. // Return NULL on failure.
StackFrameARM* GetCallerByFramePointer(const vector<StackFrame*>& frames); StackFrameARM* GetCallerByFramePointer(const vector<StackFrame*>& frames);
// Use the link register if it seems to be a valid function adderss.
// The caller takes ownership of the returned frame. Return NULL on failure.
// This is useful when PC register is corrupted.
StackFrameARM* GetCallerByLinkRegister(const vector<StackFrame*>& frames);
// Scan the stack for plausible return addresses. The caller takes ownership // Scan the stack for plausible return addresses. The caller takes ownership
// of the returned frame. Return NULL on failure. // of the returned frame. Return NULL on failure.
StackFrameARM* GetCallerByStackScan(const vector<StackFrame*>& frames); StackFrameARM* GetCallerByStackScan(const vector<StackFrame*>& frames);