Support for leaf functions which don't touch any callee-saved registers
for Windows x64 stacks. According to https://reviews.llvm.org/D2474, LLVM does't generate unwind info for leaf function which doesn't touch any callee-saved registers. According to MSDN, leaf functions can be unwound simply by simulating a return. Change-Id: Ic0503e2aca90b0ba5799133ea8439f1b5f2eefda Reviewed-on: https://chromium-review.googlesource.com/c/breakpad/breakpad/+/3489332 Reviewed-by: Mark Mentovai <mark@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
parent
88f5fc451e
commit
622a582fa6
3 changed files with 53 additions and 4 deletions
|
@ -50,9 +50,12 @@ struct StackFrame {
|
||||||
FRAME_TRUST_CFI_SCAN, // Found while scanning stack using call frame info
|
FRAME_TRUST_CFI_SCAN, // Found while scanning stack using call frame info
|
||||||
FRAME_TRUST_FP, // Derived from frame pointer
|
FRAME_TRUST_FP, // Derived from frame pointer
|
||||||
FRAME_TRUST_CFI, // Derived from call frame info
|
FRAME_TRUST_CFI, // Derived from call frame info
|
||||||
FRAME_TRUST_PREWALKED, // Explicitly provided by some external stack walker.
|
// Explicitly provided by some external stack walker.
|
||||||
|
FRAME_TRUST_PREWALKED,
|
||||||
FRAME_TRUST_CONTEXT, // Given as instruction pointer in a context
|
FRAME_TRUST_CONTEXT, // Given as instruction pointer in a context
|
||||||
FRAME_TRUST_INLINE // Found by inline records in symbol files.
|
FRAME_TRUST_INLINE, // Found by inline records in symbol files.
|
||||||
|
// Derived from leaf function by simulating a return.
|
||||||
|
FRAME_TRUST_LEAF,
|
||||||
};
|
};
|
||||||
|
|
||||||
StackFrame()
|
StackFrame()
|
||||||
|
@ -85,7 +88,9 @@ struct StackFrame {
|
||||||
return "stack scanning";
|
return "stack scanning";
|
||||||
case StackFrame::FRAME_TRUST_INLINE:
|
case StackFrame::FRAME_TRUST_INLINE:
|
||||||
return "inline record";
|
return "inline record";
|
||||||
default:
|
case StackFrame::FRAME_TRUST_LEAF:
|
||||||
|
return "simulating a return from leaf function";
|
||||||
|
default:
|
||||||
return "unknown";
|
return "unknown";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,6 +221,35 @@ StackFrameAMD64* StackwalkerAMD64::GetCallerByFramePointerRecovery(
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
StackFrameAMD64* StackwalkerAMD64::GetCallerBySimulatingReturn(
|
||||||
|
const vector<StackFrame*>& frames) {
|
||||||
|
assert(frames.size() == 1);
|
||||||
|
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||||
|
uint64_t last_rsp = last_frame->context.rsp;
|
||||||
|
uint64_t caller_rip_address, caller_rip;
|
||||||
|
int searchwords = 1;
|
||||||
|
if (!ScanForReturnAddress(last_rsp, &caller_rip_address, &caller_rip,
|
||||||
|
searchwords)) {
|
||||||
|
// No plausible return address at the top of the stack. Unable to simulate
|
||||||
|
// a return.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a new stack frame (ownership will be transferred to the caller)
|
||||||
|
// and fill it in.
|
||||||
|
StackFrameAMD64* frame = new StackFrameAMD64();
|
||||||
|
|
||||||
|
frame->trust = StackFrame::FRAME_TRUST_LEAF;
|
||||||
|
frame->context = last_frame->context;
|
||||||
|
frame->context.rip = caller_rip;
|
||||||
|
// The caller's %rsp is directly underneath the return address pushed by
|
||||||
|
// the call.
|
||||||
|
frame->context.rsp = caller_rip_address + 8;
|
||||||
|
frame->context_validity = last_frame->context_validity;
|
||||||
|
|
||||||
|
return frame;
|
||||||
|
}
|
||||||
|
|
||||||
StackFrameAMD64* StackwalkerAMD64::GetCallerByStackScan(
|
StackFrameAMD64* StackwalkerAMD64::GetCallerByStackScan(
|
||||||
const vector<StackFrame*>& frames) {
|
const vector<StackFrame*>& frames) {
|
||||||
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||||
|
@ -282,12 +311,22 @@ StackFrame* StackwalkerAMD64::GetCallerFrame(const CallStack* stack,
|
||||||
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
StackFrameAMD64* last_frame = static_cast<StackFrameAMD64*>(frames.back());
|
||||||
scoped_ptr<StackFrameAMD64> new_frame;
|
scoped_ptr<StackFrameAMD64> new_frame;
|
||||||
|
|
||||||
// If we have DWARF CFI information, use it.
|
// If we have CFI information, use it.
|
||||||
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
scoped_ptr<CFIFrameInfo> cfi_frame_info(
|
||||||
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
frame_symbolizer_->FindCFIFrameInfo(last_frame));
|
||||||
if (cfi_frame_info.get())
|
if (cfi_frame_info.get())
|
||||||
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
new_frame.reset(GetCallerByCFIFrameInfo(frames, cfi_frame_info.get()));
|
||||||
|
|
||||||
|
// If CFI was not available and this is a Windows x64 stack, check whether
|
||||||
|
// this is a leaf function which doesn't touch any callee-saved registers.
|
||||||
|
// According to https://reviews.llvm.org/D24748, LLVM doesn't generate unwind
|
||||||
|
// info for such functions. According to MSDN, leaf functions can be unwound
|
||||||
|
// simply by simulating a return.
|
||||||
|
if (!new_frame.get() && stack->frames()->size() == 1 &&
|
||||||
|
system_info_->os_short == "windows") {
|
||||||
|
new_frame.reset(GetCallerBySimulatingReturn(frames));
|
||||||
|
}
|
||||||
|
|
||||||
// If CFI was not available or failed, try using frame pointer recovery.
|
// If CFI was not available or failed, try using frame pointer recovery.
|
||||||
// Never try to use frame pointer unwinding on Windows x64 stack. MSVC never
|
// Never try to use frame pointer unwinding on Windows x64 stack. MSVC never
|
||||||
// generates code that works with frame pointer chasing, and LLVM does the
|
// generates code that works with frame pointer chasing, and LLVM does the
|
||||||
|
|
|
@ -90,6 +90,11 @@ class StackwalkerAMD64 : public Stackwalker {
|
||||||
// of the returned frame. Return NULL on failure.
|
// of the returned frame. Return NULL on failure.
|
||||||
StackFrameAMD64* GetCallerByStackScan(const vector<StackFrame*>& frames);
|
StackFrameAMD64* GetCallerByStackScan(const vector<StackFrame*>& frames);
|
||||||
|
|
||||||
|
// Trying to simulate a return. The caller takes ownership of the returned
|
||||||
|
// frame. Return NULL on failure.
|
||||||
|
StackFrameAMD64* GetCallerBySimulatingReturn(
|
||||||
|
const vector<StackFrame*>& frames);
|
||||||
|
|
||||||
// Stores the CPU context corresponding to the innermost stack frame to
|
// Stores the CPU context corresponding to the innermost stack frame to
|
||||||
// be returned by GetContextFrame.
|
// be returned by GetContextFrame.
|
||||||
const MDRawContextAMD64* context_;
|
const MDRawContextAMD64* context_;
|
||||||
|
|
Loading…
Reference in a new issue