]> git.proxmox.com Git - rustc.git/blob - vendor/backtrace/src/symbolize/libbacktrace.rs
New upstream version 1.42.0+dfsg0+pve1
[rustc.git] / vendor / backtrace / src / symbolize / libbacktrace.rs
1 // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Symbolication strategy using the DWARF-parsing code in libbacktrace.
12 //!
13 //! The libbacktrace C library, typically distributed with gcc, supports not
14 //! only generating a backtrace (which we don't actually use) but also
15 //! symbolicating the backtrace and handling dwarf debug information about
16 //! things like inlined frames and whatnot.
17 //!
18 //! This is relatively complicated due to lots of various concerns here, but the
19 //! basic idea is:
20 //!
21 //! * First we call `backtrace_syminfo`. This gets symbol information from the
22 //! dynamic symbol table if we can.
23 //! * Next we call `backtrace_pcinfo`. This will parse debuginfo tables if
24 //! they're available and allow us to recover information about inline frames,
25 //! filenames, line numbers, etc.
26 //!
27 //! There's lots of trickery about getting the dwarf tables into libbacktrace,
28 //! but hopefully it's not the end of the world and is clear enough when reading
29 //! below.
30 //!
31 //! This is the default symbolication strategy for non-MSVC and non-OSX
32 //! platforms. In libstd though this is the default strategy for OSX.
33
34 #![allow(bad_style)]
35
36 extern crate backtrace_sys as bt;
37
38 use core::{ptr, slice};
39 use libc::{self, c_char, c_int, c_void, uintptr_t};
40
41 use crate::symbolize::{ResolveWhat, SymbolName};
42 use crate::symbolize::dladdr;
43 use crate::types::BytesOrWideString;
44
45 pub enum Symbol<'a> {
46 Syminfo {
47 pc: uintptr_t,
48 symname: *const c_char,
49 },
50 Pcinfo {
51 pc: uintptr_t,
52 filename: *const c_char,
53 lineno: c_int,
54 function: *const c_char,
55 symname: *const c_char,
56 },
57 Dladdr(dladdr::Symbol<'a>),
58 }
59
60 impl Symbol<'_> {
61 pub fn name(&self) -> Option<SymbolName> {
62 let symbol = |ptr: *const c_char| {
63 unsafe {
64 if ptr.is_null() {
65 None
66 } else {
67 let len = libc::strlen(ptr);
68 Some(SymbolName::new(slice::from_raw_parts(
69 ptr as *const u8,
70 len,
71 )))
72 }
73 }
74 };
75 match *self {
76 Symbol::Syminfo { symname, .. } => symbol(symname),
77 Symbol::Pcinfo { function, symname, .. } => {
78 // If possible prefer the `function` name which comes from
79 // debuginfo and can typically be more accurate for inline
80 // frames for example. If that's not present though fall back to
81 // the symbol table name specified in `symname`.
82 //
83 // Note that sometimes `function` can feel somewhat less
84 // accurate, for example being listed as `try<i32,closure>`
85 // isntead of `std::panicking::try::do_call`. It's not really
86 // clear why, but overall the `function` name seems more accurate.
87 if let Some(sym) = symbol(function) {
88 return Some(sym)
89 }
90 symbol(symname)
91 }
92 Symbol::Dladdr(ref s) => s.name(),
93 }
94 }
95
96 pub fn addr(&self) -> Option<*mut c_void> {
97 let pc = match *self {
98 Symbol::Syminfo { pc, .. } => pc,
99 Symbol::Pcinfo { pc, .. } => pc,
100 Symbol::Dladdr(ref s) => return s.addr(),
101 };
102 if pc == 0 {
103 None
104 } else {
105 Some(pc as *mut _)
106 }
107 }
108
109 fn filename_bytes(&self) -> Option<&[u8]> {
110 match *self {
111 Symbol::Syminfo { .. } => None,
112 Symbol::Pcinfo { filename, .. } => {
113 let ptr = filename as *const u8;
114 unsafe {
115 let len = libc::strlen(filename);
116 Some(slice::from_raw_parts(ptr, len))
117 }
118 }
119 Symbol::Dladdr(_) => None,
120 }
121 }
122
123 pub fn filename_raw(&self) -> Option<BytesOrWideString> {
124 self.filename_bytes().map(BytesOrWideString::Bytes)
125 }
126
127 #[cfg(feature = "std")]
128 pub fn filename(&self) -> Option<&::std::path::Path> {
129 use std::path::Path;
130
131 #[cfg(unix)]
132 fn bytes2path(bytes: &[u8]) -> Option<&Path> {
133 use std::ffi::OsStr;
134 use std::os::unix::prelude::*;
135 Some(Path::new(OsStr::from_bytes(bytes)))
136 }
137
138 #[cfg(windows)]
139 fn bytes2path(bytes: &[u8]) -> Option<&Path> {
140 use std::str;
141 str::from_utf8(bytes).ok().map(Path::new)
142 }
143
144 self.filename_bytes().and_then(bytes2path)
145 }
146
147 pub fn lineno(&self) -> Option<u32> {
148 match *self {
149 Symbol::Syminfo { .. } => None,
150 Symbol::Pcinfo { lineno, .. } => Some(lineno as u32),
151 Symbol::Dladdr(ref s) => s.lineno(),
152 }
153 }
154 }
155
156 extern "C" fn error_cb(_data: *mut c_void, _msg: *const c_char, _errnum: c_int) {
157 // do nothing for now
158 }
159
160 /// Type of the `data` pointer passed into `syminfo_cb`
161 struct SyminfoState<'a> {
162 cb: &'a mut (FnMut(&super::Symbol) + 'a),
163 pc: usize,
164 }
165
166 extern "C" fn syminfo_cb(
167 data: *mut c_void,
168 pc: uintptr_t,
169 symname: *const c_char,
170 _symval: uintptr_t,
171 _symsize: uintptr_t,
172 ) {
173 let mut bomb = crate::Bomb { enabled: true };
174
175 // Once this callback is invoked from `backtrace_syminfo` when we start
176 // resolving we go further to call `backtrace_pcinfo`. The
177 // `backtrace_pcinfo` function will consult debug information and attemp tto
178 // do things like recover file/line information as well as inlined frames.
179 // Note though that `backtrace_pcinfo` can fail or not do much if there's
180 // not debug info, so if that happens we're sure to call the callback with
181 // at least one symbol from the `syminfo_cb`.
182 unsafe {
183 let syminfo_state = &mut *(data as *mut SyminfoState);
184 let mut pcinfo_state = PcinfoState {
185 symname,
186 called: false,
187 cb: syminfo_state.cb,
188 };
189 bt::backtrace_pcinfo(
190 init_state(),
191 syminfo_state.pc as uintptr_t,
192 pcinfo_cb,
193 error_cb,
194 &mut pcinfo_state as *mut _ as *mut _,
195 );
196 if !pcinfo_state.called {
197 (pcinfo_state.cb)(&super::Symbol {
198 inner: Symbol::Syminfo {
199 pc: pc,
200 symname: symname,
201 },
202 });
203 }
204 }
205
206 bomb.enabled = false;
207 }
208
209 /// Type of the `data` pointer passed into `pcinfo_cb`
210 struct PcinfoState<'a> {
211 cb: &'a mut (FnMut(&super::Symbol) + 'a),
212 symname: *const c_char,
213 called: bool,
214 }
215
216 extern "C" fn pcinfo_cb(
217 data: *mut c_void,
218 pc: uintptr_t,
219 filename: *const c_char,
220 lineno: c_int,
221 function: *const c_char,
222 ) -> c_int {
223 if filename.is_null() || function.is_null() {
224 return -1;
225 }
226 let mut bomb = crate::Bomb { enabled: true };
227
228 unsafe {
229 let state = &mut *(data as *mut PcinfoState);
230 state.called = true;
231 (state.cb)(&super::Symbol {
232 inner: Symbol::Pcinfo {
233 pc: pc,
234 filename: filename,
235 lineno: lineno,
236 symname: state.symname,
237 function,
238 },
239 });
240 }
241
242 bomb.enabled = false;
243 return 0;
244 }
245
246 // The libbacktrace API supports creating a state, but it does not
247 // support destroying a state. I personally take this to mean that a
248 // state is meant to be created and then live forever.
249 //
250 // I would love to register an at_exit() handler which cleans up this
251 // state, but libbacktrace provides no way to do so.
252 //
253 // With these constraints, this function has a statically cached state
254 // that is calculated the first time this is requested. Remember that
255 // backtracing all happens serially (one global lock).
256 //
257 // Note the lack of synchronization here is due to the requirement that
258 // `resolve` is externally synchronized.
259 unsafe fn init_state() -> *mut bt::backtrace_state {
260 static mut STATE: *mut bt::backtrace_state = 0 as *mut _;
261
262 if !STATE.is_null() {
263 return STATE;
264 }
265
266 STATE = bt::backtrace_create_state(
267 load_filename(),
268 // Don't exercise threadsafe capabilities of libbacktrace since
269 // we're always calling it in a synchronized fashion.
270 0,
271 error_cb,
272 ptr::null_mut(), // no extra data
273 );
274
275 return STATE;
276
277 // Note that for libbacktrace to operate at all it needs to find the DWARF
278 // debug info for the current executable. It typically does that via a
279 // number of mechanisms including, but not limited to:
280 //
281 // * /proc/self/exe on supported platforms
282 // * The filename passed in explicitly when creating state
283 //
284 // The libbacktrace library is a big wad of C code. This naturally means
285 // it's got memory safety vulnerabilities, especially when handling
286 // malformed debuginfo. Libstd has run into plenty of these historically.
287 //
288 // If /proc/self/exe is used then we can typically ignore these as we
289 // assume that libbacktrace is "mostly correct" and otherwise doesn't do
290 // weird things with "attempted to be correct" dwarf debug info.
291 //
292 // If we pass in a filename, however, then it's possible on some platforms
293 // (like BSDs) where a malicious actor can cause an arbitrary file to be
294 // placed at that location. This means that if we tell libbacktrace about a
295 // filename it may be using an arbitrary file, possibly causing segfaults.
296 // If we don't tell libbacktrace anything though then it won't do anything
297 // on platforms that don't support paths like /proc/self/exe!
298 //
299 // Given all that we try as hard as possible to *not* pass in a filename,
300 // but we must on platforms that don't support /proc/self/exe at all.
301 cfg_if::cfg_if! {
302 if #[cfg(any(target_os = "macos", target_os = "ios"))] {
303 // Note that ideally we'd use `std::env::current_exe`, but we can't
304 // require `std` here.
305 //
306 // Use `_NSGetExecutablePath` to load the current executable path
307 // into a static area (which if it's too small just give up).
308 //
309 // Note that we're seriously trusting libbacktrace here to not die
310 // on corrupt executables, but it surely does...
311 unsafe fn load_filename() -> *const libc::c_char {
312 const N: usize = 256;
313 static mut BUF: [u8; N] = [0; N];
314 extern {
315 fn _NSGetExecutablePath(
316 buf: *mut libc::c_char,
317 bufsize: *mut u32,
318 ) -> libc::c_int;
319 }
320 let mut sz: u32 = BUF.len() as u32;
321 let ptr = BUF.as_mut_ptr() as *mut libc::c_char;
322 if _NSGetExecutablePath(ptr, &mut sz) == 0 {
323 ptr
324 } else {
325 ptr::null()
326 }
327 }
328 } else if #[cfg(windows)] {
329 use crate::windows::*;
330
331 // Windows has a mode of opening files where after it's opened it
332 // can't be deleted. That's in general what we want here because we
333 // want to ensure that our executable isn't changing out from under
334 // us after we hand it off to libbacktrace, hopefully mitigating the
335 // ability to pass in arbitrary data into libbacktrace (which may be
336 // mishandled).
337 //
338 // Given that we do a bit of a dance here to attempt to get a sort
339 // of lock on our own image:
340 //
341 // * Get a handle to the current process, load its filename.
342 // * Open a file to that filename with the right flags.
343 // * Reload the current process's filename, making sure it's the same
344 //
345 // If that all passes we in theory have indeed opened our process's
346 // file and we're guaranteed it won't change. FWIW a bunch of this
347 // is copied from libstd historically, so this is my best
348 // interpretation of what was happening.
349 unsafe fn load_filename() -> *const libc::c_char {
350 load_filename_opt().unwrap_or(ptr::null())
351 }
352
353 unsafe fn load_filename_opt() -> Result<*const libc::c_char, ()> {
354 const N: usize = 256;
355 // This lives in static memory so we can return it..
356 static mut BUF: [i8; N] = [0; N];
357 // ... and this lives on the stack since it's temporary
358 let mut stack_buf = [0; N];
359 let name1 = query_full_name(&mut BUF)?;
360
361 let handle = CreateFileA(
362 name1.as_ptr(),
363 GENERIC_READ,
364 FILE_SHARE_READ | FILE_SHARE_WRITE,
365 ptr::null_mut(),
366 OPEN_EXISTING,
367 0,
368 ptr::null_mut(),
369 );
370 if handle.is_null() {
371 return Err(());
372 }
373
374 let name2 = query_full_name(&mut stack_buf)?;
375 if name1 != name2 {
376 CloseHandle(handle);
377 return Err(())
378 }
379 // intentionally leak `handle` here because having that open
380 // should preserve our lock on this file name.
381 Ok(name1.as_ptr())
382 }
383
384 unsafe fn query_full_name(buf: &mut [i8]) -> Result<&[i8], ()> {
385 let p1 = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, GetCurrentProcessId());
386 let mut len = buf.len() as u32;
387 let rc = QueryFullProcessImageNameA(p1, 0, buf.as_mut_ptr(), &mut len);
388 CloseHandle(p1);
389
390 // We want to return a slice that is nul-terminated, so if
391 // everything was filled in and it equals the total length
392 // then equate that to failure.
393 //
394 // Otherwise when returning success make sure the nul byte is
395 // included in the slice.
396 if rc == 0 || len == buf.len() as u32 {
397 Err(())
398 } else {
399 assert_eq!(buf[len as usize], 0);
400 Ok(&buf[..(len + 1) as usize])
401 }
402 }
403 } else if #[cfg(target_os = "vxworks")] {
404 unsafe fn load_filename() -> *const libc::c_char {
405 use libc;
406 use core::mem;
407
408 const N: usize = libc::VX_RTP_NAME_LENGTH as usize + 1;
409 static mut BUF: [libc::c_char; N] = [0; N];
410
411 let mut rtp_desc : libc::RTP_DESC = mem::zeroed();
412 if (libc::rtpInfoGet(0, &mut rtp_desc as *mut libc::RTP_DESC) == 0) {
413 BUF.copy_from_slice(&rtp_desc.pathName);
414 BUF.as_ptr()
415 } else {
416 ptr::null()
417 }
418 }
419 } else {
420 unsafe fn load_filename() -> *const libc::c_char {
421 ptr::null()
422 }
423 }
424 }
425 }
426
427 pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
428 let symaddr = what.address_or_ip() as usize;
429
430 // backtrace errors are currently swept under the rug
431 let state = init_state();
432 if state.is_null() {
433 return dladdr_fallback(what.address_or_ip(), cb);
434 }
435
436 // Call the `backtrace_syminfo` API first. This is (from reading the code)
437 // guaranteed to call `syminfo_cb` exactly once (or fail with an error
438 // presumably). We then handle more within the `syminfo_cb`.
439 //
440 // Note that we do this since `syminfo` will consult the symbol table,
441 // finding symbol names even if there's no debug information in the binary.
442 let mut called = false;
443 {
444 let mut syminfo_state = SyminfoState {
445 pc: symaddr,
446 cb: &mut |sym| {
447 called = true;
448 cb(sym);
449 },
450 };
451 bt::backtrace_syminfo(
452 state,
453 symaddr as uintptr_t,
454 syminfo_cb,
455 error_cb,
456 &mut syminfo_state as *mut _ as *mut _,
457 );
458 }
459
460 if !called {
461 dladdr_fallback(what.address_or_ip(), cb);
462 }
463 }
464
465 unsafe fn dladdr_fallback(addr: *mut c_void, cb: &mut FnMut(&super::Symbol)) {
466 dladdr::resolve(addr, &mut |sym| {
467 cb(&super::Symbol {
468 inner: Symbol::Dladdr(sym),
469 })
470 });
471 }