@@ -86,46 +86,66 @@ impl MyContext {
86
86
pub unsafe fn trace ( cb : & mut dyn FnMut ( & super :: Frame ) -> bool ) {
87
87
use core:: ptr;
88
88
89
+ // Capture the initial context to start walking from.
89
90
let mut context = core:: mem:: zeroed :: < MyContext > ( ) ;
90
91
RtlCaptureContext ( & mut context. 0 ) ;
91
92
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
+
94
96
// The base address of the module containing the function will be stored here
95
97
// when RtlLookupFunctionEntry returns successfully.
96
98
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 ( ) ) ;
99
100
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.
100
105
break ;
101
106
}
102
107
103
108
let frame = super :: Frame {
104
109
inner : Frame {
105
110
base_address : base as * mut c_void ,
106
- ip : context . ip ( ) as * mut c_void ,
111
+ ip : ip as * mut c_void ,
107
112
sp : context. sp ( ) as * mut c_void ,
108
113
#[ cfg( not( target_env = "gnu" ) ) ]
109
114
inline_context : None ,
110
115
} ,
111
116
} ;
112
117
118
+ // We've loaded all the info about the current frame, so now call the
119
+ // callback.
113
120
if !cb ( & frame) {
121
+ // Callback told us to stop, so we're done.
114
122
break ;
115
123
}
116
124
125
+ // Unwind to the next frame.
126
+ let previous_ip = ip;
127
+ let previous_sp = context. sp ( ) ;
117
128
let mut handler_data = 0usize ;
118
129
let mut establisher_frame = 0 ;
119
-
120
130
RtlVirtualUnwind (
121
131
0 ,
122
132
base,
123
- context . ip ( ) ,
133
+ ip ,
124
134
fn_entry,
125
135
& mut context. 0 ,
126
136
ptr:: addr_of_mut!( handler_data) . cast :: < PVOID > ( ) ,
127
137
& mut establisher_frame,
128
138
ptr:: null_mut ( ) ,
129
139
) ;
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
+ }
130
150
}
131
151
}
0 commit comments