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.
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.
11 //! Symbolication strategy using the DWARF-parsing code in libbacktrace.
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.
18 //! This is relatively complicated due to lots of various concerns here, but the
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.
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
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.
36 extern crate backtrace_sys
as bt
;
38 use core
::{ptr, slice}
;
39 use libc
::{self, c_char, c_int, c_void, uintptr_t}
;
41 use crate::symbolize
::{ResolveWhat, SymbolName}
;
42 use crate::symbolize
::dladdr
;
43 use crate::types
::BytesOrWideString
;
48 symname
: *const c_char
,
52 filename
: *const c_char
,
54 function
: *const c_char
,
55 symname
: *const c_char
,
57 Dladdr(dladdr
::Symbol
<'a
>),
61 pub fn name(&self) -> Option
<SymbolName
> {
62 let symbol
= |ptr
: *const c_char
| {
67 let len
= libc
::strlen(ptr
);
68 Some(SymbolName
::new(slice
::from_raw_parts(
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`.
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
) {
92 Symbol
::Dladdr(ref s
) => s
.name(),
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(),
109 fn filename_bytes(&self) -> Option
<&[u8]> {
111 Symbol
::Syminfo { .. }
=> None
,
112 Symbol
::Pcinfo { filename, .. }
=> {
113 let ptr
= filename
as *const u8;
115 let len
= libc
::strlen(filename
);
116 Some(slice
::from_raw_parts(ptr
, len
))
119 Symbol
::Dladdr(_
) => None
,
123 pub fn filename_raw(&self) -> Option
<BytesOrWideString
> {
124 self.filename_bytes().map(BytesOrWideString
::Bytes
)
127 #[cfg(feature = "std")]
128 pub fn filename(&self) -> Option
<&::std
::path
::Path
> {
132 fn bytes2path(bytes
: &[u8]) -> Option
<&Path
> {
134 use std
::os
::unix
::prelude
::*;
135 Some(Path
::new(OsStr
::from_bytes(bytes
)))
139 fn bytes2path(bytes
: &[u8]) -> Option
<&Path
> {
141 str::from_utf8(bytes
).ok().map(Path
::new
)
144 self.filename_bytes().and_then(bytes2path
)
147 pub fn lineno(&self) -> Option
<u32> {
149 Symbol
::Syminfo { .. }
=> None
,
150 Symbol
::Pcinfo { lineno, .. }
=> Some(lineno
as u32),
151 Symbol
::Dladdr(ref s
) => s
.lineno(),
156 extern "C" fn error_cb(_data
: *mut c_void
, _msg
: *const c_char
, _errnum
: c_int
) {
157 // do nothing for now
160 /// Type of the `data` pointer passed into `syminfo_cb`
161 struct SyminfoState
<'a
> {
162 cb
: &'a
mut (FnMut(&super::Symbol
) + 'a
),
166 extern "C" fn syminfo_cb(
169 symname
: *const c_char
,
173 let mut bomb
= crate::Bomb { enabled: true }
;
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`.
183 let syminfo_state
= &mut *(data
as *mut SyminfoState
);
184 let mut pcinfo_state
= PcinfoState
{
187 cb
: syminfo_state
.cb
,
189 bt
::backtrace_pcinfo(
191 syminfo_state
.pc
as uintptr_t
,
194 &mut pcinfo_state
as *mut _
as *mut _
,
196 if !pcinfo_state
.called
{
197 (pcinfo_state
.cb
)(&super::Symbol
{
198 inner
: Symbol
::Syminfo
{
206 bomb
.enabled
= false;
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
,
216 extern "C" fn pcinfo_cb(
219 filename
: *const c_char
,
221 function
: *const c_char
,
223 if filename
.is_null() || function
.is_null() {
226 let mut bomb
= crate::Bomb { enabled: true }
;
229 let state
= &mut *(data
as *mut PcinfoState
);
231 (state
.cb
)(&super::Symbol
{
232 inner
: Symbol
::Pcinfo
{
236 symname
: state
.symname
,
242 bomb
.enabled
= false;
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.
250 // I would love to register an at_exit() handler which cleans up this
251 // state, but libbacktrace provides no way to do so.
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).
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 _
;
262 if !STATE
.is_null() {
266 STATE
= bt
::backtrace_create_state(
268 // Don't exercise threadsafe capabilities of libbacktrace since
269 // we're always calling it in a synchronized fashion.
272 ptr
::null_mut(), // no extra data
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:
281 // * /proc/self/exe on supported platforms
282 // * The filename passed in explicitly when creating state
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.
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.
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!
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.
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.
306 // Use `_NSGetExecutablePath` to load the current executable path
307 // into a static area (which if it's too small just give up).
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
];
315 fn _NSGetExecutablePath(
316 buf
: *mut libc
::c_char
,
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 {
328 } else if #[cfg(windows)] {
329 use crate::windows
::*;
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
338 // Given that we do a bit of a dance here to attempt to get a sort
339 // of lock on our own image:
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
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())
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
)?
;
361 let handle
= CreateFileA(
364 FILE_SHARE_READ
| FILE_SHARE_WRITE
,
370 if handle
.is_null() {
374 let name2
= query_full_name(&mut stack_buf
)?
;
379 // intentionally leak `handle` here because having that open
380 // should preserve our lock on this file name.
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
);
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.
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 {
399 assert_eq
!(buf
[len
as usize], 0);
400 Ok(&buf
[..(len
+ 1) as usize])
403 } else if #[cfg(target_os = "vxworks")] {
404 unsafe fn load_filename() -> *const libc
::c_char
{
408 const N
: usize = libc
::VX_RTP_NAME_LENGTH
as usize + 1;
409 static mut BUF
: [libc
::c_char
; N
] = [0; N
];
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
);
420 unsafe fn load_filename() -> *const libc
::c_char
{
427 pub unsafe fn resolve(what
: ResolveWhat
, cb
: &mut FnMut(&super::Symbol
)) {
428 let symaddr
= what
.address_or_ip() as usize;
430 // backtrace errors are currently swept under the rug
431 let state
= init_state();
433 return dladdr_fallback(what
.address_or_ip(), cb
);
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`.
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;
444 let mut syminfo_state
= SyminfoState
{
451 bt
::backtrace_syminfo(
453 symaddr
as uintptr_t
,
456 &mut syminfo_state
as *mut _
as *mut _
,
461 dladdr_fallback(what
.address_or_ip(), cb
);
465 unsafe fn dladdr_fallback(addr
: *mut c_void
, cb
: &mut FnMut(&super::Symbol
)) {
466 dladdr
::resolve(addr
, &mut |sym
| {
468 inner
: Symbol
::Dladdr(sym
),