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.
17 use sys
::backtrace
::BacktraceContext
;
18 use sys_common
::backtrace
::Frame
;
20 pub fn foreach_symbol_fileline
<F
>(frame
: Frame
,
22 _
: &BacktraceContext
) -> io
::Result
<bool
>
23 where F
: FnMut(&[u8], libc
::c_int
) -> io
::Result
<()>
25 // pcinfo may return an arbitrary number of file:line pairs,
26 // in the order of stack trace (i.e. inlined calls first).
27 // in order to avoid allocation, we stack-allocate a fixed size of entries.
28 const FILELINE_SIZE
: usize = 32;
29 let mut fileline_buf
= [(ptr
::null(), -1); FILELINE_SIZE
];
31 let fileline_count
= {
32 let state
= unsafe { init_state() }
;
34 return Err(io
::Error
::new(
36 "failed to allocate libbacktrace state")
39 let mut fileline_win
: &mut [FileLine
] = &mut fileline_buf
;
40 let fileline_addr
= &mut fileline_win
as *mut &mut [FileLine
];
42 backtrace_pcinfo(state
,
43 frame
.exact_position
as libc
::uintptr_t
,
46 fileline_addr
as *mut libc
::c_void
)
48 FILELINE_SIZE
- fileline_win
.len()
51 for &(file
, line
) in &fileline_buf
[..fileline_count
] {
52 if file
.is_null() { continue; }
// just to be sure
53 let file
= unsafe { CStr::from_ptr(file).to_bytes() }
;
56 Ok(fileline_count
== FILELINE_SIZE
)
62 /// Converts a pointer to symbol to its string value.
63 pub fn resolve_symname
<F
>(frame
: Frame
,
65 _
: &BacktraceContext
) -> io
::Result
<()>
66 where F
: FnOnce(Option
<&str>) -> io
::Result
<()>
69 let state
= unsafe { init_state() }
;
71 return Err(io
::Error
::new(
73 "failed to allocate libbacktrace state")
76 let mut data
= ptr
::null();
77 let data_addr
= &mut data
as *mut *const libc
::c_char
;
79 backtrace_syminfo(state
,
80 frame
.symbol_addr
as libc
::uintptr_t
,
83 data_addr
as *mut libc
::c_void
)
85 if ret
== 0 || data
.is_null() {
89 CStr
::from_ptr(data
).to_str().ok()
96 ////////////////////////////////////////////////////////////////////////
98 ////////////////////////////////////////////////////////////////////////
99 type backtrace_syminfo_callback
=
100 extern "C" fn(data
: *mut libc
::c_void
,
102 symname
: *const libc
::c_char
,
103 symval
: libc
::uintptr_t
,
104 symsize
: libc
::uintptr_t
);
105 type backtrace_full_callback
=
106 extern "C" fn(data
: *mut libc
::c_void
,
108 filename
: *const libc
::c_char
,
110 function
: *const libc
::c_char
) -> libc
::c_int
;
111 type backtrace_error_callback
=
112 extern "C" fn(data
: *mut libc
::c_void
,
113 msg
: *const libc
::c_char
,
114 errnum
: libc
::c_int
);
115 enum backtrace_state {}
118 fn backtrace_create_state(filename
: *const libc
::c_char
,
119 threaded
: libc
::c_int
,
120 error
: backtrace_error_callback
,
121 data
: *mut libc
::c_void
)
122 -> *mut backtrace_state
;
123 fn backtrace_syminfo(state
: *mut backtrace_state
,
124 addr
: libc
::uintptr_t
,
125 cb
: backtrace_syminfo_callback
,
126 error
: backtrace_error_callback
,
127 data
: *mut libc
::c_void
) -> libc
::c_int
;
128 fn backtrace_pcinfo(state
: *mut backtrace_state
,
129 addr
: libc
::uintptr_t
,
130 cb
: backtrace_full_callback
,
131 error
: backtrace_error_callback
,
132 data
: *mut libc
::c_void
) -> libc
::c_int
;
135 ////////////////////////////////////////////////////////////////////////
137 ////////////////////////////////////////////////////////////////////////
139 type FileLine
= (*const libc
::c_char
, libc
::c_int
);
141 extern fn error_cb(_data
: *mut libc
::c_void
, _msg
: *const libc
::c_char
,
142 _errnum
: libc
::c_int
) {
143 // do nothing for now
145 extern fn syminfo_cb(data
: *mut libc
::c_void
,
146 _pc
: libc
::uintptr_t
,
147 symname
: *const libc
::c_char
,
148 _symval
: libc
::uintptr_t
,
149 _symsize
: libc
::uintptr_t
) {
150 let slot
= data
as *mut *const libc
::c_char
;
151 unsafe { *slot = symname; }
153 extern fn pcinfo_cb(data
: *mut libc
::c_void
,
154 _pc
: libc
::uintptr_t
,
155 filename
: *const libc
::c_char
,
157 _function
: *const libc
::c_char
) -> libc
::c_int
{
158 if !filename
.is_null() {
159 let slot
= data
as *mut &mut [FileLine
];
160 let buffer
= unsafe {ptr::read(slot)}
;
162 // if the buffer is not full, add file:line to the buffer
163 // and adjust the buffer for next possible calls to pcinfo_cb.
164 if !buffer
.is_empty() {
165 buffer
[0] = (filename
, lineno
);
166 unsafe { ptr::write(slot, &mut buffer[1..]); }
173 // The libbacktrace API supports creating a state, but it does not
174 // support destroying a state. I personally take this to mean that a
175 // state is meant to be created and then live forever.
177 // I would love to register an at_exit() handler which cleans up this
178 // state, but libbacktrace provides no way to do so.
180 // With these constraints, this function has a statically cached state
181 // that is calculated the first time this is requested. Remember that
182 // backtracing all happens serially (one global lock).
184 // Things don't work so well on not-Linux since libbacktrace can't track
185 // down that executable this is. We at one point used env::current_exe but
186 // it turns out that there are some serious security issues with that
189 // Specifically, on certain platforms like BSDs, a malicious actor can cause
190 // an arbitrary file to be placed at the path returned by current_exe.
191 // libbacktrace does not behave defensively in the presence of ill-formed
192 // DWARF information, and has been demonstrated to segfault in at least one
193 // case. There is no evidence at the moment to suggest that a more carefully
194 // constructed file can't cause arbitrary code execution. As a result of all
195 // of this, we don't hint libbacktrace with the path to the current process.
196 unsafe fn init_state() -> *mut backtrace_state
{
197 static mut STATE
: *mut backtrace_state
= ptr
::null_mut();
198 if !STATE
.is_null() { return STATE }
200 let filename
= match ::sys
::backtrace
::gnu
::get_executable_filename() {
201 Ok((filename
, file
)) => {
202 // filename is purposely leaked here since libbacktrace requires
203 // it to stay allocated permanently, file is also leaked so that
204 // the file stays locked
205 let filename_ptr
= filename
.as_ptr();
206 mem
::forget(filename
);
210 Err(_
) => ptr
::null(),
213 STATE
= backtrace_create_state(filename
, 0, error_cb
,