aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/processor/stackwalker_arm64.cc39
-rw-r--r--src/processor/stackwalker_arm64.h7
2 files changed, 46 insertions, 0 deletions
diff --git a/src/processor/stackwalker_arm64.cc b/src/processor/stackwalker_arm64.cc
index 47e245ed..5bfd2636 100644
--- a/src/processor/stackwalker_arm64.cc
+++ b/src/processor/stackwalker_arm64.cc
@@ -208,6 +208,9 @@ StackFrameARM64* StackwalkerARM64::GetCallerByStackScan(
StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
const vector<StackFrame*> &frames) {
StackFrameARM64* last_frame = static_cast<StackFrameARM64*>(frames.back());
+ if (!(last_frame->context_validity & StackFrameARM64::CONTEXT_VALID_LR)) {
+ CorrectRegLRByFramePointer(frames, last_frame);
+ }
uint64_t last_fp = last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP];
@@ -248,6 +251,42 @@ StackFrameARM64* StackwalkerARM64::GetCallerByFramePointer(
return frame;
}
+void StackwalkerARM64::CorrectRegLRByFramePointer(
+ const vector<StackFrame*>& frames,
+ StackFrameARM64* last_frame) {
+ // Need at least two frames to correct and
+ // register $FP should always be greater than register $SP.
+ if (frames.size() < 2 || !last_frame ||
+ last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] <=
+ last_frame->context.iregs[MD_CONTEXT_ARM64_REG_SP])
+ return;
+
+ StackFrameARM64* last_last_frame =
+ static_cast<StackFrameARM64*>(*(frames.end() - 2));
+ uint64_t last_last_fp =
+ last_last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP];
+
+ uint64_t last_fp = 0;
+ if (last_last_fp && !memory_->GetMemoryAtAddress(last_last_fp, &last_fp)) {
+ BPLOG(ERROR) << "Unable to read last_fp from last_last_fp: 0x"
+ << std::hex << last_last_fp;
+ return;
+ }
+ // Give up if STACK CFI doesn't agree with frame pointer.
+ if (last_frame->context.iregs[MD_CONTEXT_ARM64_REG_FP] != last_fp)
+ return;
+
+ uint64_t last_lr = 0;
+ if (last_last_fp && !memory_->GetMemoryAtAddress(last_last_fp + 8, &last_lr)) {
+ BPLOG(ERROR) << "Unable to read last_lr from (last_last_fp + 8): 0x"
+ << std::hex << (last_last_fp + 8);
+ return;
+ }
+ last_lr = PtrauthStrip(last_lr);
+
+ last_frame->context.iregs[MD_CONTEXT_ARM64_REG_LR] = last_lr;
+}
+
StackFrame* StackwalkerARM64::GetCallerFrame(const CallStack* stack,
bool stack_scan_allowed) {
if (!memory_ || !stack) {
diff --git a/src/processor/stackwalker_arm64.h b/src/processor/stackwalker_arm64.h
index 241383ea..39735c67 100644
--- a/src/processor/stackwalker_arm64.h
+++ b/src/processor/stackwalker_arm64.h
@@ -90,6 +90,13 @@ class StackwalkerARM64 : public Stackwalker {
// of the returned frame. Return NULL on failure.
StackFrameARM64* GetCallerByStackScan(const vector<StackFrame*> &frames);
+ // GetCallerByFramePointer() depends on the previous frame having recovered
+ // x30($LR) which may not have been done when using CFI.
+ // This function recovers $LR in the previous frame by using the frame-pointer
+ // two frames back to read it from the stack.
+ void CorrectRegLRByFramePointer(const vector<StackFrame*>& frames,
+ StackFrameARM64* last_frame);
+
// Stores the CPU context corresponding to the youngest stack frame, to
// be returned by GetContextFrame.
const MDRawContextARM64* context_;