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 &frames) { StackFrameARM64* last_frame = static_cast(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& 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(*(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 &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& frames, + StackFrameARM64* last_frame); + // Stores the CPU context corresponding to the youngest stack frame, to // be returned by GetContextFrame. const MDRawContextARM64* context_;