@@ -86,46 +86,66 @@ impl MyContext {
8686pub 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