]> git.proxmox.com Git - rustc.git/blob - src/libstd/sys_common/gnu/libbacktrace.rs
New upstream version 1.36.0+dfsg1
[rustc.git] / src / libstd / sys_common / gnu / libbacktrace.rs
1 use backtrace_sys::backtrace_state;
2
3 use crate::ffi::CStr;
4 use crate::io;
5 use crate::mem;
6 use crate::ptr;
7 use crate::sys::backtrace::BacktraceContext;
8 use crate::sys_common::backtrace::Frame;
9
10 pub fn foreach_symbol_fileline<F>(frame: Frame,
11 mut f: F,
12 _: &BacktraceContext) -> io::Result<bool>
13 where F: FnMut(&[u8], u32) -> io::Result<()>
14 {
15 // pcinfo may return an arbitrary number of file:line pairs,
16 // in the order of stack trace (i.e., inlined calls first).
17 // in order to avoid allocation, we stack-allocate a fixed size of entries.
18 const FILELINE_SIZE: usize = 32;
19 let mut fileline_buf = [(ptr::null(), !0); FILELINE_SIZE];
20 let ret;
21 let fileline_count = {
22 let state = unsafe { init_state() };
23 if state.is_null() {
24 return Err(io::Error::new(
25 io::ErrorKind::Other,
26 "failed to allocate libbacktrace state")
27 )
28 }
29 let mut fileline_win: &mut [FileLine] = &mut fileline_buf;
30 let fileline_addr = &mut fileline_win as *mut &mut [FileLine];
31 ret = unsafe {
32 backtrace_sys::backtrace_pcinfo(
33 state,
34 frame.exact_position as libc::uintptr_t,
35 pcinfo_cb,
36 error_cb,
37 fileline_addr as *mut libc::c_void,
38 )
39 };
40 FILELINE_SIZE - fileline_win.len()
41 };
42 if ret == 0 {
43 for &(file, line) in &fileline_buf[..fileline_count] {
44 if file.is_null() { continue; } // just to be sure
45 let file = unsafe { CStr::from_ptr(file).to_bytes() };
46 f(file, line)?;
47 }
48 Ok(fileline_count == FILELINE_SIZE)
49 } else {
50 Ok(false)
51 }
52 }
53
54 /// Converts a pointer to symbol to its string value.
55 pub fn resolve_symname<F>(frame: Frame,
56 callback: F,
57 _: &BacktraceContext) -> io::Result<()>
58 where F: FnOnce(Option<&str>) -> io::Result<()>
59 {
60 let symname = {
61 let state = unsafe { init_state() };
62 if state.is_null() {
63 return Err(io::Error::new(
64 io::ErrorKind::Other,
65 "failed to allocate libbacktrace state")
66 )
67 }
68 let mut data: *const libc::c_char = ptr::null();
69 let data_addr = &mut data as *mut *const libc::c_char;
70 let ret = unsafe {
71 backtrace_sys::backtrace_syminfo(
72 state,
73 frame.symbol_addr as libc::uintptr_t,
74 syminfo_cb,
75 error_cb,
76 data_addr as *mut libc::c_void,
77 )
78 };
79 if ret == 0 || data.is_null() {
80 None
81 } else {
82 unsafe {
83 CStr::from_ptr(data).to_str().ok()
84 }
85 }
86 };
87 callback(symname)
88 }
89
90 ////////////////////////////////////////////////////////////////////////
91 // helper callbacks
92 ////////////////////////////////////////////////////////////////////////
93
94 type FileLine = (*const libc::c_char, u32);
95
96 extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char,
97 _errnum: libc::c_int) {
98 // do nothing for now
99 }
100 extern fn syminfo_cb(data: *mut libc::c_void,
101 _pc: libc::uintptr_t,
102 symname: *const libc::c_char,
103 _symval: libc::uintptr_t,
104 _symsize: libc::uintptr_t) {
105 let slot = data as *mut *const libc::c_char;
106 unsafe { *slot = symname; }
107 }
108 extern fn pcinfo_cb(data: *mut libc::c_void,
109 _pc: libc::uintptr_t,
110 filename: *const libc::c_char,
111 lineno: libc::c_int,
112 _function: *const libc::c_char) -> libc::c_int {
113 if !filename.is_null() {
114 let slot = data as *mut &mut [FileLine];
115 let buffer = unsafe {ptr::read(slot)};
116
117 // if the buffer is not full, add file:line to the buffer
118 // and adjust the buffer for next possible calls to pcinfo_cb.
119 if !buffer.is_empty() {
120 buffer[0] = (filename, lineno as u32);
121 unsafe { ptr::write(slot, &mut buffer[1..]); }
122 }
123 }
124
125 0
126 }
127
128 // The libbacktrace API supports creating a state, but it does not
129 // support destroying a state. I personally take this to mean that a
130 // state is meant to be created and then live forever.
131 //
132 // I would love to register an at_exit() handler which cleans up this
133 // state, but libbacktrace provides no way to do so.
134 //
135 // With these constraints, this function has a statically cached state
136 // that is calculated the first time this is requested. Remember that
137 // backtracing all happens serially (one global lock).
138 //
139 // Things don't work so well on not-Linux since libbacktrace can't track
140 // down that executable this is. We at one point used env::current_exe but
141 // it turns out that there are some serious security issues with that
142 // approach.
143 //
144 // Specifically, on certain platforms like BSDs, a malicious actor can cause
145 // an arbitrary file to be placed at the path returned by current_exe.
146 // libbacktrace does not behave defensively in the presence of ill-formed
147 // DWARF information, and has been demonstrated to segfault in at least one
148 // case. There is no evidence at the moment to suggest that a more carefully
149 // constructed file can't cause arbitrary code execution. As a result of all
150 // of this, we don't hint libbacktrace with the path to the current process.
151 unsafe fn init_state() -> *mut backtrace_state {
152 static mut STATE: *mut backtrace_state = ptr::null_mut();
153 if !STATE.is_null() { return STATE }
154
155 let filename = match crate::sys::backtrace::gnu::get_executable_filename() {
156 Ok((filename, file)) => {
157 // filename is purposely leaked here since libbacktrace requires
158 // it to stay allocated permanently, file is also leaked so that
159 // the file stays locked
160 let filename_ptr = filename.as_ptr();
161 mem::forget(filename);
162 mem::forget(file);
163 filename_ptr
164 },
165 Err(_) => ptr::null(),
166 };
167
168 STATE = backtrace_sys::backtrace_create_state(
169 filename,
170 0,
171 error_cb,
172 ptr::null_mut(),
173 );
174 STATE
175 }