]> git.proxmox.com Git - rustc.git/blame - src/vendor/backtrace/src/symbolize/libbacktrace.rs
New upstream version 1.20.0+dfsg1
[rustc.git] / src / vendor / backtrace / src / symbolize / libbacktrace.rs
CommitLineData
7cac9316
XL
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#![allow(bad_style)]
12
13extern crate backtrace_sys as bt;
14
15use libc::uintptr_t;
16use std::ffi::{CStr, OsStr};
17use std::os::raw::{c_void, c_char, c_int};
18use std::os::unix::prelude::*;
19use std::path::Path;
20use std::ptr;
21use std::sync::{ONCE_INIT, Once};
22
23use SymbolName;
24
7cac9316
XL
25pub enum Symbol {
26 Syminfo {
27 pc: uintptr_t,
28 symname: *const c_char,
29 },
30 Pcinfo {
31 pc: uintptr_t,
32 filename: *const c_char,
33 lineno: c_int,
34 function: *const c_char,
35 },
36}
37
38impl Symbol {
39 pub fn name(&self) -> Option<SymbolName> {
40 let ptr = match *self {
41 Symbol::Syminfo { symname, .. } => symname,
42 Symbol::Pcinfo { function, .. } => function,
43 };
44 if ptr.is_null() {
45 None
46 } else {
47 Some(SymbolName::new(unsafe { CStr::from_ptr(ptr).to_bytes() }))
48 }
49 }
50
51 pub fn addr(&self) -> Option<*mut c_void> {
52 let pc = match *self {
53 Symbol::Syminfo { pc, .. } => pc,
54 Symbol::Pcinfo { pc, .. } => pc,
55 };
56 if pc == 0 {None} else {Some(pc as *mut _)}
57 }
58
59 pub fn filename(&self) -> Option<&Path> {
60 match *self {
61 Symbol::Syminfo { .. } => None,
62 Symbol::Pcinfo { filename, .. } => {
63 Some(Path::new(OsStr::from_bytes(unsafe {
64 CStr::from_ptr(filename).to_bytes()
65 })))
66 }
67 }
68 }
69
70 pub fn lineno(&self) -> Option<u32> {
71 match *self {
72 Symbol::Syminfo { .. } => None,
73 Symbol::Pcinfo { lineno, .. } => Some(lineno as u32),
74 }
75 }
76}
77
78extern fn error_cb(_data: *mut c_void, _msg: *const c_char,
79 _errnum: c_int) {
80 // do nothing for now
81}
82
83extern fn syminfo_cb(data: *mut c_void,
84 pc: uintptr_t,
85 symname: *const c_char,
86 _symval: uintptr_t,
87 _symsize: uintptr_t) {
88 unsafe {
89 call(data, &super::Symbol {
90 inner: Symbol::Syminfo {
91 pc: pc,
92 symname: symname,
93 },
94 });
95 }
96}
97
98extern fn pcinfo_cb(data: *mut c_void,
99 pc: uintptr_t,
100 filename: *const c_char,
101 lineno: c_int,
102 function: *const c_char) -> c_int {
103 unsafe {
104 if filename.is_null() || function.is_null() {
105 return -1
106 }
107 call(data, &super::Symbol {
108 inner: Symbol::Pcinfo {
109 pc: pc,
110 filename: filename,
111 lineno: lineno,
112 function: function,
113 },
114 });
115 return 0
116 }
117}
118
119unsafe fn call(data: *mut c_void, sym: &super::Symbol) {
120 let cb = data as *mut &mut FnMut(&super::Symbol);
121 let mut bomb = ::Bomb { enabled: true };
122 (*cb)(sym);
123 bomb.enabled = false;
124}
125
126// The libbacktrace API supports creating a state, but it does not
127// support destroying a state. I personally take this to mean that a
128// state is meant to be created and then live forever.
129//
130// I would love to register an at_exit() handler which cleans up this
131// state, but libbacktrace provides no way to do so.
132//
133// With these constraints, this function has a statically cached state
134// that is calculated the first time this is requested. Remember that
135// backtracing all happens serially (one global lock).
136//
137// Things don't work so well on not-Linux since libbacktrace can't track down
138// that executable this is. We at one point used env::current_exe but it turns
139// out that there are some serious security issues with that approach.
140//
141// Specifically, on certain platforms like BSDs, a malicious actor can cause an
142// arbitrary file to be placed at the path returned by current_exe. libbacktrace
143// does not behave defensively in the presence of ill-formed DWARF information,
144// and has been demonstrated to segfault in at least one case. There is no
145// evidence at the moment to suggest that a more carefully constructed file
146// can't cause arbitrary code execution. As a result of all of this, we don't
147// hint libbacktrace with the path to the current process.
148unsafe fn init_state() -> *mut bt::backtrace_state {
149 static mut STATE: *mut bt::backtrace_state = 0 as *mut _;
150 static INIT: Once = ONCE_INIT;
151 INIT.call_once(|| {
152 // Our libbacktrace may not have multithreading support, so
153 // set `threaded = 0` and synchronize ourselves.
154 STATE = bt::backtrace_create_state(ptr::null(), 0, error_cb,
155 ptr::null_mut());
156 });
157
158 STATE
159}
160
161pub fn resolve(symaddr: *mut c_void, mut cb: &mut FnMut(&super::Symbol)) {
162 let _guard = ::lock::lock();
163
164 // backtrace errors are currently swept under the rug
165 unsafe {
166 let state = init_state();
167 if state.is_null() {
168 return
169 }
170
171 let ret = bt::backtrace_pcinfo(state, symaddr as uintptr_t,
172 pcinfo_cb, error_cb,
173 &mut cb as *mut _ as *mut _);
174 if ret != 0 {
175 bt::backtrace_syminfo(state, symaddr as uintptr_t,
176 syminfo_cb, error_cb,
177 &mut cb as *mut _ as *mut _);
178 }
179 }
180}