1 use backtrace_sys
::backtrace_state
;
7 use crate::sys
::backtrace
::BacktraceContext
;
8 use crate::sys_common
::backtrace
::Frame
;
10 pub fn foreach_symbol_fileline
<F
>(frame
: Frame
,
12 _
: &BacktraceContext
) -> io
::Result
<bool
>
13 where F
: FnMut(&[u8], u32) -> io
::Result
<()>
15 // pcinfo may return an arbitrary number of file:line pairs,
16 // in the order of stack trace (i.e., inlined calls first).
17 // in order to avoid allocation, we stack-allocate a fixed size of entries.
18 const FILELINE_SIZE
: usize = 32;
19 let mut fileline_buf
= [(ptr
::null(), !0); FILELINE_SIZE
];
21 let fileline_count
= {
22 let state
= unsafe { init_state() }
;
24 return Err(io
::Error
::new(
26 "failed to allocate libbacktrace state")
29 let mut fileline_win
: &mut [FileLine
] = &mut fileline_buf
;
30 let fileline_addr
= &mut fileline_win
as *mut &mut [FileLine
];
32 backtrace_sys
::backtrace_pcinfo(
34 frame
.exact_position
as libc
::uintptr_t
,
37 fileline_addr
as *mut libc
::c_void
,
40 FILELINE_SIZE
- fileline_win
.len()
43 for &(file
, line
) in &fileline_buf
[..fileline_count
] {
44 if file
.is_null() { continue; }
// just to be sure
45 let file
= unsafe { CStr::from_ptr(file).to_bytes() }
;
48 Ok(fileline_count
== FILELINE_SIZE
)
54 /// Converts a pointer to symbol to its string value.
55 pub fn resolve_symname
<F
>(frame
: Frame
,
57 _
: &BacktraceContext
) -> io
::Result
<()>
58 where F
: FnOnce(Option
<&str>) -> io
::Result
<()>
61 let state
= unsafe { init_state() }
;
63 return Err(io
::Error
::new(
65 "failed to allocate libbacktrace state")
68 let mut data
: *const libc
::c_char
= ptr
::null();
69 let data_addr
= &mut data
as *mut *const libc
::c_char
;
71 backtrace_sys
::backtrace_syminfo(
73 frame
.symbol_addr
as libc
::uintptr_t
,
76 data_addr
as *mut libc
::c_void
,
79 if ret
== 0 || data
.is_null() {
83 CStr
::from_ptr(data
).to_str().ok()
90 ////////////////////////////////////////////////////////////////////////
92 ////////////////////////////////////////////////////////////////////////
94 type FileLine
= (*const libc
::c_char
, u32);
96 extern fn error_cb(_data
: *mut libc
::c_void
, _msg
: *const libc
::c_char
,
97 _errnum
: libc
::c_int
) {
100 extern fn syminfo_cb(data
: *mut libc
::c_void
,
101 _pc
: libc
::uintptr_t
,
102 symname
: *const libc
::c_char
,
103 _symval
: libc
::uintptr_t
,
104 _symsize
: libc
::uintptr_t
) {
105 let slot
= data
as *mut *const libc
::c_char
;
106 unsafe { *slot = symname; }
108 extern fn pcinfo_cb(data
: *mut libc
::c_void
,
109 _pc
: libc
::uintptr_t
,
110 filename
: *const libc
::c_char
,
112 _function
: *const libc
::c_char
) -> libc
::c_int
{
113 if !filename
.is_null() {
114 let slot
= data
as *mut &mut [FileLine
];
115 let buffer
= unsafe {ptr::read(slot)}
;
117 // if the buffer is not full, add file:line to the buffer
118 // and adjust the buffer for next possible calls to pcinfo_cb.
119 if !buffer
.is_empty() {
120 buffer
[0] = (filename
, lineno
as u32);
121 unsafe { ptr::write(slot, &mut buffer[1..]); }
128 // The libbacktrace API supports creating a state, but it does not
129 // support destroying a state. I personally take this to mean that a
130 // state is meant to be created and then live forever.
132 // I would love to register an at_exit() handler which cleans up this
133 // state, but libbacktrace provides no way to do so.
135 // With these constraints, this function has a statically cached state
136 // that is calculated the first time this is requested. Remember that
137 // backtracing all happens serially (one global lock).
139 // Things don't work so well on not-Linux since libbacktrace can't track
140 // down that executable this is. We at one point used env::current_exe but
141 // it turns out that there are some serious security issues with that
144 // Specifically, on certain platforms like BSDs, a malicious actor can cause
145 // an arbitrary file to be placed at the path returned by current_exe.
146 // libbacktrace does not behave defensively in the presence of ill-formed
147 // DWARF information, and has been demonstrated to segfault in at least one
148 // case. There is no evidence at the moment to suggest that a more carefully
149 // constructed file can't cause arbitrary code execution. As a result of all
150 // of this, we don't hint libbacktrace with the path to the current process.
151 unsafe fn init_state() -> *mut backtrace_state
{
152 static mut STATE
: *mut backtrace_state
= ptr
::null_mut();
153 if !STATE
.is_null() { return STATE }
155 let filename
= match crate::sys
::backtrace
::gnu
::get_executable_filename() {
156 Ok((filename
, file
)) => {
157 // filename is purposely leaked here since libbacktrace requires
158 // it to stay allocated permanently, file is also leaked so that
159 // the file stays locked
160 let filename_ptr
= filename
.as_ptr();
161 mem
::forget(filename
);
165 Err(_
) => ptr
::null(),
168 STATE
= backtrace_sys
::backtrace_create_state(