Skip to content

Commit e151306

Browse files
authored
Merge pull request #610 from dpaoliello/fixtrace
Windows AArch64: Break out of tracing when no longer making progress
2 parents adc9f5c + 3b96ddb commit e151306

File tree

1 file changed

+27
-7
lines changed

1 file changed

+27
-7
lines changed

src/backtrace/dbghelp64.rs

+27-7
Original file line numberDiff line numberDiff line change
@@ -86,46 +86,66 @@ impl MyContext {
8686
pub unsafe fn trace(cb: &mut dyn FnMut(&super::Frame) -> bool) {
8787
use core::ptr;
8888

89+
// Capture the initial context to start walking from.
8990
let mut context = core::mem::zeroed::<MyContext>();
9091
RtlCaptureContext(&mut context.0);
9192

92-
// Call `RtlVirtualUnwind` to find the previous stack frame, walking until we hit ip = 0.
93-
while context.ip() != 0 {
93+
loop {
94+
let ip = context.ip();
95+
9496
// The base address of the module containing the function will be stored here
9597
// when RtlLookupFunctionEntry returns successfully.
9698
let mut base = 0;
97-
98-
let fn_entry = RtlLookupFunctionEntry(context.ip(), &mut base, ptr::null_mut());
99+
let fn_entry = RtlLookupFunctionEntry(ip, &mut base, ptr::null_mut());
99100
if fn_entry.is_null() {
101+
// No function entry could be found - this may indicate a corrupt
102+
// stack or that a binary was unloaded (amongst other issues). Stop
103+
// walking and don't call the callback as we can't be confident in
104+
// this frame or the rest of the stack.
100105
break;
101106
}
102107

103108
let frame = super::Frame {
104109
inner: Frame {
105110
base_address: base as *mut c_void,
106-
ip: context.ip() as *mut c_void,
111+
ip: ip as *mut c_void,
107112
sp: context.sp() as *mut c_void,
108113
#[cfg(not(target_env = "gnu"))]
109114
inline_context: None,
110115
},
111116
};
112117

118+
// We've loaded all the info about the current frame, so now call the
119+
// callback.
113120
if !cb(&frame) {
121+
// Callback told us to stop, so we're done.
114122
break;
115123
}
116124

125+
// Unwind to the next frame.
126+
let previous_ip = ip;
127+
let previous_sp = context.sp();
117128
let mut handler_data = 0usize;
118129
let mut establisher_frame = 0;
119-
120130
RtlVirtualUnwind(
121131
0,
122132
base,
123-
context.ip(),
133+
ip,
124134
fn_entry,
125135
&mut context.0,
126136
ptr::addr_of_mut!(handler_data).cast::<PVOID>(),
127137
&mut establisher_frame,
128138
ptr::null_mut(),
129139
);
140+
141+
// RtlVirtualUnwind indicates the end of the stack in two different ways:
142+
// * On x64, it sets the instruction pointer to 0.
143+
// * On ARM64, it leaves the context unchanged (easiest way to check is
144+
// to see if the instruction and stack pointers are the same).
145+
// If we detect either of these, then unwinding is completed.
146+
let ip = context.ip();
147+
if ip == 0 || (ip == previous_ip && context.sp() == previous_sp) {
148+
break;
149+
}
130150
}
131151
}

0 commit comments

Comments
 (0)