1#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
5mod libunwind_x86_64;
6
7#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
8mod libunwind_aarch64;
9
10#[cfg(all(target_os = "linux", target_arch = "aarch64"))]
11pub use libunwind_aarch64::*;
12#[cfg(all(target_os = "linux", target_arch = "x86_64"))]
13pub use libunwind_x86_64::*;
14
15#[cfg(all(test, target_os = "linux"))]
16mod remote_tests {
17 use super::*;
18
19 #[test]
22 #[cfg_attr(miri, ignore)]
23 fn test_remote_unwind_child() {
24 unsafe {
25 let child_pid = libc::fork();
26 assert!(child_pid >= 0, "fork failed");
27
28 if child_pid == 0 {
29 libc::ptrace(
30 libc::PTRACE_TRACEME,
31 0,
32 std::ptr::null_mut::<libc::c_void>(),
33 std::ptr::null_mut::<libc::c_void>(),
34 );
35 libc::raise(libc::SIGSTOP);
36 libc::_exit(libc::EXIT_SUCCESS);
37 }
38
39 let mut status: libc::c_int = 0;
40 libc::waitpid(child_pid, &mut status, libc::WUNTRACED);
41 assert!(libc::WIFSTOPPED(status), "child did not stop");
42
43 let addr_space =
44 unw_create_addr_space(std::ptr::addr_of!(_UPT_accessors) as *mut UnwAccessors, 0);
45 let upt_info = _UPT_create(child_pid);
46 let mut cursor: UnwCursor = std::mem::zeroed();
47 let ret = unw_init_remote(&mut cursor, addr_space, upt_info);
48 assert_eq!(ret, 0, "unw_init_remote failed");
49
50 let mut frames = 0usize;
51 while frames <= 256 {
52 if unw_step_remote(&mut cursor) <= 0 {
53 break;
54 }
55 frames += 1;
56
57 let mut ip: UnwWord = 0;
58 unw_get_reg_remote(&mut cursor, UNW_REG_IP, &mut ip);
59 let mut name: [libc::c_char; 256] = [0; 256];
60 let mut offset: UnwWord = 0;
61 let sym =
62 if unw_get_proc_name_remote(&mut cursor, name.as_mut_ptr(), 256, &mut offset)
63 == 0
64 {
65 std::ffi::CStr::from_ptr(name.as_ptr())
66 .to_string_lossy()
67 .into_owned()
68 } else {
69 "<unknown>".to_owned()
70 };
71 println!(" frame {frames:3}: ip=0x{ip:016x} {sym}+0x{offset:x}");
72 }
73 assert!(frames > 0, "expected at least one remote frame");
74
75 _UPT_destroy(upt_info);
76 unw_destroy_addr_space(addr_space);
77 libc::kill(child_pid, libc::SIGKILL);
78 libc::waitpid(child_pid, std::ptr::null_mut(), 0);
79 }
80 }
81
82 #[test]
95 #[cfg_attr(miri, ignore)]
96 fn test_remote_child_ptrace_unwind() {
97 unsafe {
98 let parent_tid = libc::syscall(libc::SYS_gettid) as libc::pid_t;
103
104 let mut pipe_fds: [libc::c_int; 2] = [0; 2];
105 assert_eq!(libc::pipe(pipe_fds.as_mut_ptr()), 0);
106 let [pipe_r, pipe_w] = pipe_fds;
107
108 let child_pid = libc::fork();
109 assert!(child_pid >= 0, "fork failed");
110
111 if child_pid == 0 {
112 libc::close(pipe_w);
113
114 let mut byte = 0u8;
116 libc::read(pipe_r, &mut byte as *mut u8 as *mut libc::c_void, 1);
117 libc::close(pipe_r);
118
119 let ret = libc::ptrace(
122 libc::PTRACE_ATTACH,
123 parent_tid,
124 std::ptr::null_mut::<libc::c_void>(),
125 std::ptr::null_mut::<libc::c_void>(),
126 );
127 if ret != 0 {
128 libc::_exit(1);
129 }
130 let mut status: libc::c_int = 0;
131 libc::waitpid(parent_tid, &mut status, libc::__WALL);
132 if !libc::WIFSTOPPED(status) {
133 libc::ptrace(
134 libc::PTRACE_DETACH,
135 parent_tid,
136 std::ptr::null_mut::<libc::c_void>(),
137 std::ptr::null_mut::<libc::c_void>(),
138 );
139 libc::_exit(1);
140 }
141
142 let addr_space = unw_create_addr_space(
144 std::ptr::addr_of!(_UPT_accessors) as *mut UnwAccessors,
145 0,
146 );
147 let upt_info = _UPT_create(parent_tid);
148 let mut cursor: UnwCursor = std::mem::zeroed();
149 let ret = unw_init_remote(&mut cursor, addr_space, upt_info);
150
151 let mut frames = 0usize;
152 if ret == 0 {
153 while frames <= 256 {
154 if unw_step_remote(&mut cursor) <= 0 {
155 break;
156 }
157 frames += 1;
158
159 let mut ip: UnwWord = 0;
160 unw_get_reg_remote(&mut cursor, UNW_REG_IP, &mut ip);
161 let mut name: [libc::c_char; 256] = [0; 256];
162 let mut offset: UnwWord = 0;
163 let sym = if unw_get_proc_name_remote(
164 &mut cursor,
165 name.as_mut_ptr(),
166 256,
167 &mut offset,
168 ) == 0
169 {
170 std::ffi::CStr::from_ptr(name.as_ptr())
171 .to_string_lossy()
172 .into_owned()
173 } else {
174 "<unknown>".to_owned()
175 };
176 eprintln!(" frame {frames:3}: ip=0x{ip:016x} {sym}+0x{offset:x}");
178 }
179 }
180 assert!(frames > 0, "Expected at least one remote frame");
181
182 _UPT_destroy(upt_info);
183 unw_destroy_addr_space(addr_space);
184 libc::ptrace(
185 libc::PTRACE_DETACH,
186 parent_tid,
187 std::ptr::null_mut::<libc::c_void>(),
188 std::ptr::null_mut::<libc::c_void>(),
189 );
190 libc::_exit(if frames > 0 { 0 } else { 1 });
191 }
192
193 libc::close(pipe_r);
195 libc::prctl(libc::PR_SET_PTRACER, child_pid as libc::c_ulong, 0, 0, 0);
196 libc::write(pipe_w, b"g".as_ptr() as *const libc::c_void, 1);
197 libc::close(pipe_w);
198
199 let mut status: libc::c_int = 0;
201 libc::waitpid(child_pid, &mut status, 0);
202 assert!(
203 libc::WIFEXITED(status) && libc::WEXITSTATUS(status) == 0,
204 "child failed: status={status}"
205 );
206 }
207 }
208}
209
210#[cfg(all(test, target_os = "linux"))]
211mod tests {
212 use super::*;
213
214 #[test]
215 #[cfg_attr(miri, ignore)] fn test_basic_unwind() {
217 unsafe {
218 let mut context: UnwContext = std::mem::zeroed();
219 let mut cursor: UnwCursor = std::mem::zeroed();
220
221 let ret = getcontext(&mut context);
222 assert_eq!(ret, 0, "getcontext failed");
223
224 let ret = unw_init_local2(&mut cursor, &mut context, 0);
226 assert_eq!(ret, 0, "unw_init_local2 failed");
227
228 let mut frames = 0;
230 loop {
231 let ret = unw_step(&mut cursor);
232 if ret <= 0 {
233 break;
234 }
235 frames += 1;
236
237 if frames > 100 {
239 break;
240 }
241 }
242
243 assert!(frames > 0, "Expected at least one stack frame");
245 }
246 }
247
248 #[test]
249 #[cfg_attr(miri, ignore)] fn test_get_register() {
251 unsafe {
252 let mut context: UnwContext = std::mem::zeroed();
253 let mut cursor: UnwCursor = std::mem::zeroed();
254
255 assert_eq!(getcontext(&mut context), 0);
256 assert_eq!(unw_init_local2(&mut cursor, &mut context, 0), 0);
257
258 let mut ip: UnwWord = 0;
260 let ret = unw_get_reg(&mut cursor, UNW_REG_IP, &mut ip);
261 assert_eq!(ret, 0, "Failed to get IP register");
262 assert_ne!(ip, 0, "IP should not be zero");
263
264 let mut sp: UnwWord = 0;
266 let ret = unw_get_reg(&mut cursor, UNW_REG_SP, &mut sp);
267 assert_eq!(ret, 0, "Failed to get SP register");
268 assert_ne!(sp, 0, "SP should not be zero");
269 }
270 }
271
272 #[test]
273 #[cfg_attr(miri, ignore)] fn test_backtrace2() {
275 unsafe {
276 let mut context: UnwContext = std::mem::zeroed();
277 assert_eq!(getcontext(&mut context), 0);
278
279 let mut frames: [*mut ::std::os::raw::c_void; 100] = [std::ptr::null_mut(); 100];
281 let ret = unw_backtrace2(frames.as_mut_ptr(), 100, &mut context, 0);
282
283 assert!(ret >= 0, "unw_backtrace2 failed with error: {}", ret);
285
286 let frame_count = ret as usize;
287 assert!(frame_count > 0, "Expected at least one frame");
288
289 for (i, &frame) in frames.iter().enumerate().take(frame_count) {
291 let frame_ptr = frame as usize;
292 println!("Frame {}: 0x{:016x}", i, frame_ptr);
293 }
294 }
295 }
296
297 #[test]
298 #[cfg_attr(miri, ignore)] fn test_get_proc_name() {
300 unsafe {
301 let mut context: UnwContext = std::mem::zeroed();
302 let mut cursor: UnwCursor = std::mem::zeroed();
303
304 assert_eq!(getcontext(&mut context), 0);
305 assert_eq!(
306 unw_init_local2(&mut cursor, &mut context, UNW_INIT_LOCAL_ONLY_IP),
307 0
308 );
309
310 let mut name: [libc::c_char; 100] = [0; 100];
311 let ret = unw_get_proc_name(&mut cursor, name.as_mut_ptr(), 100, std::ptr::null_mut());
312 assert_eq!(ret, 0, "unw_get_proc_name failed");
313 let fn_name = std::ffi::CStr::from_ptr(name.as_ptr()).to_string_lossy();
314 assert!(!fn_name.is_empty(), "Name should not be empty");
315 assert!(
318 fn_name.contains("test_get_proc_name"),
319 "Name should contain 'test_get_proc_name'"
320 );
321 }
322 }
323}