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.
14 use sys_common
::backtrace
::{output, output_fileline}
;
16 pub fn print(w
: &mut Write
, idx
: isize, addr
: *mut libc
::c_void
,
17 symaddr
: *mut libc
::c_void
) -> io
::Result
<()> {
21 ////////////////////////////////////////////////////////////////////////
23 ////////////////////////////////////////////////////////////////////////
24 type backtrace_syminfo_callback
=
25 extern "C" fn(data
: *mut libc
::c_void
,
27 symname
: *const libc
::c_char
,
28 symval
: libc
::uintptr_t
,
29 symsize
: libc
::uintptr_t
);
30 type backtrace_full_callback
=
31 extern "C" fn(data
: *mut libc
::c_void
,
33 filename
: *const libc
::c_char
,
35 function
: *const libc
::c_char
) -> libc
::c_int
;
36 type backtrace_error_callback
=
37 extern "C" fn(data
: *mut libc
::c_void
,
38 msg
: *const libc
::c_char
,
40 enum backtrace_state {}
41 #[link(name = "backtrace", kind = "static")]
42 #[cfg(all(not(test), not(cargobuild)))]
46 fn backtrace_create_state(filename
: *const libc
::c_char
,
47 threaded
: libc
::c_int
,
48 error
: backtrace_error_callback
,
49 data
: *mut libc
::c_void
)
50 -> *mut backtrace_state
;
51 fn backtrace_syminfo(state
: *mut backtrace_state
,
52 addr
: libc
::uintptr_t
,
53 cb
: backtrace_syminfo_callback
,
54 error
: backtrace_error_callback
,
55 data
: *mut libc
::c_void
) -> libc
::c_int
;
56 fn backtrace_pcinfo(state
: *mut backtrace_state
,
57 addr
: libc
::uintptr_t
,
58 cb
: backtrace_full_callback
,
59 error
: backtrace_error_callback
,
60 data
: *mut libc
::c_void
) -> libc
::c_int
;
63 ////////////////////////////////////////////////////////////////////////
65 ////////////////////////////////////////////////////////////////////////
67 type FileLine
= (*const libc
::c_char
, libc
::c_int
);
69 extern fn error_cb(_data
: *mut libc
::c_void
, _msg
: *const libc
::c_char
,
70 _errnum
: libc
::c_int
) {
73 extern fn syminfo_cb(data
: *mut libc
::c_void
,
75 symname
: *const libc
::c_char
,
76 _symval
: libc
::uintptr_t
,
77 _symsize
: libc
::uintptr_t
) {
78 let slot
= data
as *mut *const libc
::c_char
;
79 unsafe { *slot = symname; }
81 extern fn pcinfo_cb(data
: *mut libc
::c_void
,
83 filename
: *const libc
::c_char
,
85 _function
: *const libc
::c_char
) -> libc
::c_int
{
86 if !filename
.is_null() {
87 let slot
= data
as *mut &mut [FileLine
];
88 let buffer
= unsafe {ptr::read(slot)}
;
90 // if the buffer is not full, add file:line to the buffer
91 // and adjust the buffer for next possible calls to pcinfo_cb.
92 if !buffer
.is_empty() {
93 buffer
[0] = (filename
, lineno
);
94 unsafe { ptr::write(slot, &mut buffer[1..]); }
101 // The libbacktrace API supports creating a state, but it does not
102 // support destroying a state. I personally take this to mean that a
103 // state is meant to be created and then live forever.
105 // I would love to register an at_exit() handler which cleans up this
106 // state, but libbacktrace provides no way to do so.
108 // With these constraints, this function has a statically cached state
109 // that is calculated the first time this is requested. Remember that
110 // backtracing all happens serially (one global lock).
112 // Things don't work so well on not-Linux since libbacktrace can't track
113 // down that executable this is. We at one point used env::current_exe but
114 // it turns out that there are some serious security issues with that
117 // Specifically, on certain platforms like BSDs, a malicious actor can cause
118 // an arbitrary file to be placed at the path returned by current_exe.
119 // libbacktrace does not behave defensively in the presence of ill-formed
120 // DWARF information, and has been demonstrated to segfault in at least one
121 // case. There is no evidence at the moment to suggest that a more carefully
122 // constructed file can't cause arbitrary code execution. As a result of all
123 // of this, we don't hint libbacktrace with the path to the current process.
124 unsafe fn init_state() -> *mut backtrace_state
{
125 static mut STATE
: *mut backtrace_state
= ptr
::null_mut();
126 if !STATE
.is_null() { return STATE }
127 STATE
= backtrace_create_state(ptr
::null(), 0, error_cb
,
132 ////////////////////////////////////////////////////////////////////////
134 ////////////////////////////////////////////////////////////////////////
136 // backtrace errors are currently swept under the rug, only I/O
137 // errors are reported
138 let state
= unsafe { init_state() }
;
140 return output(w
, idx
, addr
, None
)
142 let mut data
= ptr
::null();
143 let data_addr
= &mut data
as *mut *const libc
::c_char
;
145 backtrace_syminfo(state
, symaddr
as libc
::uintptr_t
,
146 syminfo_cb
, error_cb
,
147 data_addr
as *mut libc
::c_void
)
149 if ret
== 0 || data
.is_null() {
150 output(w
, idx
, addr
, None
)?
;
152 output(w
, idx
, addr
, Some(unsafe { CStr::from_ptr(data).to_bytes() }
))?
;
155 // pcinfo may return an arbitrary number of file:line pairs,
156 // in the order of stack trace (i.e. inlined calls first).
157 // in order to avoid allocation, we stack-allocate a fixed size of entries.
158 const FILELINE_SIZE
: usize = 32;
159 let mut fileline_buf
= [(ptr
::null(), -1); FILELINE_SIZE
];
163 let mut fileline_win
: &mut [FileLine
] = &mut fileline_buf
;
164 let fileline_addr
= &mut fileline_win
as *mut &mut [FileLine
];
166 backtrace_pcinfo(state
, addr
as libc
::uintptr_t
,
168 fileline_addr
as *mut libc
::c_void
)
170 fileline_count
= FILELINE_SIZE
- fileline_win
.len();
173 for (i
, &(file
, line
)) in fileline_buf
[..fileline_count
].iter().enumerate() {
174 if file
.is_null() { continue; }
// just to be sure
175 let file
= unsafe { CStr::from_ptr(file).to_bytes() }
;
176 output_fileline(w
, file
, line
, i
== FILELINE_SIZE
- 1)?
;