]>
Commit | Line | Data |
---|---|---|
85aaf69f | 1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT |
1a4d82fc JJ |
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 | /// Backtrace support built on libgcc with some extra OS-specific support | |
12 | /// | |
13 | /// Some methods of getting a backtrace: | |
14 | /// | |
15 | /// * The backtrace() functions on unix. It turns out this doesn't work very | |
16 | /// well for green threads on OSX, and the address to symbol portion of it | |
17 | /// suffers problems that are described below. | |
18 | /// | |
19 | /// * Using libunwind. This is more difficult than it sounds because libunwind | |
20 | /// isn't installed everywhere by default. It's also a bit of a hefty library, | |
21 | /// so possibly not the best option. When testing, libunwind was excellent at | |
22 | /// getting both accurate backtraces and accurate symbols across platforms. | |
23 | /// This route was not chosen in favor of the next option, however. | |
24 | /// | |
bd371182 | 25 | /// * We're already using libgcc_s for exceptions in rust (triggering thread |
1a4d82fc JJ |
26 | /// unwinding and running destructors on the stack), and it turns out that it |
27 | /// conveniently comes with a function that also gives us a backtrace. All of | |
28 | /// these functions look like _Unwind_*, but it's not quite the full | |
29 | /// repertoire of the libunwind API. Due to it already being in use, this was | |
30 | /// the chosen route of getting a backtrace. | |
31 | /// | |
32 | /// After choosing libgcc_s for backtraces, the sad part is that it will only | |
33 | /// give us a stack trace of instruction pointers. Thankfully these instruction | |
34 | /// pointers are accurate (they work for green and native threads), but it's | |
35 | /// then up to us again to figure out how to translate these addresses to | |
36 | /// symbols. As with before, we have a few options. Before, that, a little bit | |
37 | /// of an interlude about symbols. This is my very limited knowledge about | |
38 | /// symbol tables, and this information is likely slightly wrong, but the | |
39 | /// general idea should be correct. | |
40 | /// | |
41 | /// When talking about symbols, it's helpful to know a few things about where | |
42 | /// symbols are located. Some symbols are located in the dynamic symbol table | |
43 | /// of the executable which in theory means that they're available for dynamic | |
44 | /// linking and lookup. Other symbols end up only in the local symbol table of | |
45 | /// the file. This loosely corresponds to pub and priv functions in Rust. | |
46 | /// | |
47 | /// Armed with this knowledge, we know that our solution for address to symbol | |
48 | /// translation will need to consult both the local and dynamic symbol tables. | |
49 | /// With that in mind, here's our options of translating an address to | |
50 | /// a symbol. | |
51 | /// | |
52 | /// * Use dladdr(). The original backtrace()-based idea actually uses dladdr() | |
53 | /// behind the scenes to translate, and this is why backtrace() was not used. | |
54 | /// Conveniently, this method works fantastically on OSX. It appears dladdr() | |
55 | /// uses magic to consult the local symbol table, or we're putting everything | |
56 | /// in the dynamic symbol table anyway. Regardless, for OSX, this is the | |
57 | /// method used for translation. It's provided by the system and easy to do.o | |
58 | /// | |
59 | /// Sadly, all other systems have a dladdr() implementation that does not | |
60 | /// consult the local symbol table. This means that most functions are blank | |
61 | /// because they don't have symbols. This means that we need another solution. | |
62 | /// | |
63 | /// * Use unw_get_proc_name(). This is part of the libunwind api (not the | |
64 | /// libgcc_s version of the libunwind api), but involves taking a dependency | |
65 | /// to libunwind. We may pursue this route in the future if we bundle | |
66 | /// libunwind, but libunwind was unwieldy enough that it was not chosen at | |
67 | /// this time to provide this functionality. | |
68 | /// | |
69 | /// * Shell out to a utility like `readelf`. Crazy though it may sound, it's a | |
70 | /// semi-reasonable solution. The stdlib already knows how to spawn processes, | |
71 | /// so in theory it could invoke readelf, parse the output, and consult the | |
72 | /// local/dynamic symbol tables from there. This ended up not getting chosen | |
73 | /// due to the craziness of the idea plus the advent of the next option. | |
74 | /// | |
75 | /// * Use `libbacktrace`. It turns out that this is a small library bundled in | |
76 | /// the gcc repository which provides backtrace and symbol translation | |
77 | /// functionality. All we really need from it is the backtrace functionality, | |
78 | /// and we only really need this on everything that's not OSX, so this is the | |
79 | /// chosen route for now. | |
80 | /// | |
81 | /// In summary, the current situation uses libgcc_s to get a trace of stack | |
82 | /// pointers, and we use dladdr() or libbacktrace to translate these addresses | |
83 | /// to symbols. This is a bit of a hokey implementation as-is, but it works for | |
84 | /// all unix platforms we support right now, so it at least gets the job done. | |
85 | ||
86 | use prelude::v1::*; | |
c34b1796 | 87 | use io::prelude::*; |
1a4d82fc | 88 | |
85aaf69f | 89 | use ffi::CStr; |
c34b1796 | 90 | use io; |
1a4d82fc JJ |
91 | use libc; |
92 | use mem; | |
93 | use str; | |
62682a34 | 94 | use sync::StaticMutex; |
1a4d82fc JJ |
95 | |
96 | use sys_common::backtrace::*; | |
97 | ||
98 | /// As always - iOS on arm uses SjLj exceptions and | |
99 | /// _Unwind_Backtrace is even not available there. Still, | |
100 | /// backtraces could be extracted using a backtrace function, | |
101 | /// which thanks god is public | |
102 | /// | |
103 | /// As mentioned in a huge comment block above, backtrace doesn't | |
104 | /// play well with green threads, so while it is extremely nice | |
105 | /// and simple to use it should be used only on iOS devices as the | |
106 | /// only viable option. | |
107 | #[cfg(all(target_os = "ios", target_arch = "arm"))] | |
108 | #[inline(never)] | |
c34b1796 | 109 | pub fn write(w: &mut Write) -> io::Result<()> { |
1a4d82fc JJ |
110 | extern { |
111 | fn backtrace(buf: *mut *mut libc::c_void, | |
112 | sz: libc::c_int) -> libc::c_int; | |
113 | } | |
114 | ||
115 | // while it doesn't requires lock for work as everything is | |
116 | // local, it still displays much nicer backtraces when a | |
bd371182 | 117 | // couple of threads panic simultaneously |
62682a34 | 118 | static LOCK: StaticMutex = StaticMutex::new(); |
c34b1796 | 119 | let _g = LOCK.lock(); |
1a4d82fc JJ |
120 | |
121 | try!(writeln!(w, "stack backtrace:")); | |
122 | // 100 lines should be enough | |
c34b1796 | 123 | const SIZE: usize = 100; |
1a4d82fc | 124 | let mut buf: [*mut libc::c_void; SIZE] = unsafe {mem::zeroed()}; |
c34b1796 | 125 | let cnt = unsafe { backtrace(buf.as_mut_ptr(), SIZE as libc::c_int) as usize}; |
1a4d82fc JJ |
126 | |
127 | // skipping the first one as it is write itself | |
c1a9b12d SL |
128 | for i in 1..cnt { |
129 | try!(print(w, i as isize, buf[i], buf[i])) | |
130 | } | |
131 | Ok(()) | |
1a4d82fc JJ |
132 | } |
133 | ||
134 | #[cfg(not(all(target_os = "ios", target_arch = "arm")))] | |
135 | #[inline(never)] // if we know this is a function call, we can skip it when | |
136 | // tracing | |
c34b1796 | 137 | pub fn write(w: &mut Write) -> io::Result<()> { |
1a4d82fc | 138 | struct Context<'a> { |
c34b1796 AL |
139 | idx: isize, |
140 | writer: &'a mut (Write+'a), | |
141 | last_error: Option<io::Error>, | |
1a4d82fc JJ |
142 | } |
143 | ||
144 | // When using libbacktrace, we use some necessary global state, so we | |
145 | // need to prevent more than one thread from entering this block. This | |
146 | // is semi-reasonable in terms of printing anyway, and we know that all | |
147 | // I/O done here is blocking I/O, not green I/O, so we don't have to | |
148 | // worry about this being a native vs green mutex. | |
62682a34 | 149 | static LOCK: StaticMutex = StaticMutex::new(); |
c34b1796 | 150 | let _g = LOCK.lock(); |
1a4d82fc JJ |
151 | |
152 | try!(writeln!(w, "stack backtrace:")); | |
153 | ||
154 | let mut cx = Context { writer: w, last_error: None, idx: 0 }; | |
155 | return match unsafe { | |
156 | uw::_Unwind_Backtrace(trace_fn, | |
157 | &mut cx as *mut Context as *mut libc::c_void) | |
158 | } { | |
159 | uw::_URC_NO_REASON => { | |
160 | match cx.last_error { | |
161 | Some(err) => Err(err), | |
162 | None => Ok(()) | |
163 | } | |
164 | } | |
165 | _ => Ok(()), | |
166 | }; | |
167 | ||
168 | extern fn trace_fn(ctx: *mut uw::_Unwind_Context, | |
169 | arg: *mut libc::c_void) -> uw::_Unwind_Reason_Code { | |
170 | let cx: &mut Context = unsafe { mem::transmute(arg) }; | |
c34b1796 AL |
171 | let mut ip_before_insn = 0; |
172 | let mut ip = unsafe { | |
173 | uw::_Unwind_GetIPInfo(ctx, &mut ip_before_insn) as *mut libc::c_void | |
174 | }; | |
175 | if !ip.is_null() && ip_before_insn == 0 { | |
176 | // this is a non-signaling frame, so `ip` refers to the address | |
177 | // after the calling instruction. account for that. | |
178 | ip = (ip as usize - 1) as *mut _; | |
179 | } | |
180 | ||
1a4d82fc JJ |
181 | // dladdr() on osx gets whiny when we use FindEnclosingFunction, and |
182 | // it appears to work fine without it, so we only use | |
183 | // FindEnclosingFunction on non-osx platforms. In doing so, we get a | |
184 | // slightly more accurate stack trace in the process. | |
185 | // | |
186 | // This is often because panic involves the last instruction of a | |
187 | // function being "call std::rt::begin_unwind", with no ret | |
188 | // instructions after it. This means that the return instruction | |
189 | // pointer points *outside* of the calling function, and by | |
190 | // unwinding it we go back to the original function. | |
c34b1796 | 191 | let symaddr = if cfg!(target_os = "macos") || cfg!(target_os = "ios") { |
1a4d82fc JJ |
192 | ip |
193 | } else { | |
194 | unsafe { uw::_Unwind_FindEnclosingFunction(ip) } | |
195 | }; | |
196 | ||
197 | // Don't print out the first few frames (they're not user frames) | |
198 | cx.idx += 1; | |
199 | if cx.idx <= 0 { return uw::_URC_NO_REASON } | |
200 | // Don't print ginormous backtraces | |
201 | if cx.idx > 100 { | |
202 | match write!(cx.writer, " ... <frames omitted>\n") { | |
203 | Ok(()) => {} | |
204 | Err(e) => { cx.last_error = Some(e); } | |
205 | } | |
206 | return uw::_URC_FAILURE | |
207 | } | |
208 | ||
209 | // Once we hit an error, stop trying to print more frames | |
210 | if cx.last_error.is_some() { return uw::_URC_FAILURE } | |
211 | ||
c34b1796 | 212 | match print(cx.writer, cx.idx, ip, symaddr) { |
1a4d82fc JJ |
213 | Ok(()) => {} |
214 | Err(e) => { cx.last_error = Some(e); } | |
215 | } | |
216 | ||
217 | // keep going | |
218 | return uw::_URC_NO_REASON | |
219 | } | |
220 | } | |
221 | ||
222 | #[cfg(any(target_os = "macos", target_os = "ios"))] | |
c34b1796 AL |
223 | fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, |
224 | _symaddr: *mut libc::c_void) -> io::Result<()> { | |
1a4d82fc JJ |
225 | use intrinsics; |
226 | #[repr(C)] | |
227 | struct Dl_info { | |
228 | dli_fname: *const libc::c_char, | |
229 | dli_fbase: *mut libc::c_void, | |
230 | dli_sname: *const libc::c_char, | |
231 | dli_saddr: *mut libc::c_void, | |
232 | } | |
233 | extern { | |
234 | fn dladdr(addr: *const libc::c_void, | |
235 | info: *mut Dl_info) -> libc::c_int; | |
236 | } | |
237 | ||
238 | let mut info: Dl_info = unsafe { intrinsics::init() }; | |
85aaf69f | 239 | if unsafe { dladdr(addr, &mut info) == 0 } { |
1a4d82fc JJ |
240 | output(w, idx,addr, None) |
241 | } else { | |
242 | output(w, idx, addr, Some(unsafe { | |
85aaf69f | 243 | CStr::from_ptr(info.dli_sname).to_bytes() |
1a4d82fc JJ |
244 | })) |
245 | } | |
246 | } | |
247 | ||
248 | #[cfg(not(any(target_os = "macos", target_os = "ios")))] | |
c34b1796 AL |
249 | fn print(w: &mut Write, idx: isize, addr: *mut libc::c_void, |
250 | symaddr: *mut libc::c_void) -> io::Result<()> { | |
85aaf69f | 251 | use env; |
c34b1796 | 252 | use os::unix::prelude::*; |
1a4d82fc JJ |
253 | use ptr; |
254 | ||
255 | //////////////////////////////////////////////////////////////////////// | |
256 | // libbacktrace.h API | |
257 | //////////////////////////////////////////////////////////////////////// | |
258 | type backtrace_syminfo_callback = | |
259 | extern "C" fn(data: *mut libc::c_void, | |
260 | pc: libc::uintptr_t, | |
261 | symname: *const libc::c_char, | |
262 | symval: libc::uintptr_t, | |
263 | symsize: libc::uintptr_t); | |
c34b1796 AL |
264 | type backtrace_full_callback = |
265 | extern "C" fn(data: *mut libc::c_void, | |
266 | pc: libc::uintptr_t, | |
267 | filename: *const libc::c_char, | |
268 | lineno: libc::c_int, | |
269 | function: *const libc::c_char) -> libc::c_int; | |
1a4d82fc JJ |
270 | type backtrace_error_callback = |
271 | extern "C" fn(data: *mut libc::c_void, | |
272 | msg: *const libc::c_char, | |
273 | errnum: libc::c_int); | |
274 | enum backtrace_state {} | |
275 | #[link(name = "backtrace", kind = "static")] | |
276 | #[cfg(not(test))] | |
277 | extern {} | |
278 | ||
279 | extern { | |
280 | fn backtrace_create_state(filename: *const libc::c_char, | |
281 | threaded: libc::c_int, | |
282 | error: backtrace_error_callback, | |
283 | data: *mut libc::c_void) | |
284 | -> *mut backtrace_state; | |
285 | fn backtrace_syminfo(state: *mut backtrace_state, | |
286 | addr: libc::uintptr_t, | |
287 | cb: backtrace_syminfo_callback, | |
288 | error: backtrace_error_callback, | |
289 | data: *mut libc::c_void) -> libc::c_int; | |
c34b1796 AL |
290 | fn backtrace_pcinfo(state: *mut backtrace_state, |
291 | addr: libc::uintptr_t, | |
292 | cb: backtrace_full_callback, | |
293 | error: backtrace_error_callback, | |
294 | data: *mut libc::c_void) -> libc::c_int; | |
1a4d82fc JJ |
295 | } |
296 | ||
297 | //////////////////////////////////////////////////////////////////////// | |
298 | // helper callbacks | |
299 | //////////////////////////////////////////////////////////////////////// | |
300 | ||
c34b1796 AL |
301 | type FileLine = (*const libc::c_char, libc::c_int); |
302 | ||
1a4d82fc JJ |
303 | extern fn error_cb(_data: *mut libc::c_void, _msg: *const libc::c_char, |
304 | _errnum: libc::c_int) { | |
305 | // do nothing for now | |
306 | } | |
307 | extern fn syminfo_cb(data: *mut libc::c_void, | |
308 | _pc: libc::uintptr_t, | |
309 | symname: *const libc::c_char, | |
310 | _symval: libc::uintptr_t, | |
311 | _symsize: libc::uintptr_t) { | |
312 | let slot = data as *mut *const libc::c_char; | |
313 | unsafe { *slot = symname; } | |
314 | } | |
c34b1796 AL |
315 | extern fn pcinfo_cb(data: *mut libc::c_void, |
316 | _pc: libc::uintptr_t, | |
317 | filename: *const libc::c_char, | |
318 | lineno: libc::c_int, | |
319 | _function: *const libc::c_char) -> libc::c_int { | |
320 | if !filename.is_null() { | |
321 | let slot = data as *mut &mut [FileLine]; | |
322 | let buffer = unsafe {ptr::read(slot)}; | |
323 | ||
324 | // if the buffer is not full, add file:line to the buffer | |
325 | // and adjust the buffer for next possible calls to pcinfo_cb. | |
326 | if !buffer.is_empty() { | |
327 | buffer[0] = (filename, lineno); | |
328 | unsafe { ptr::write(slot, &mut buffer[1..]); } | |
329 | } | |
330 | } | |
331 | ||
332 | 0 | |
333 | } | |
1a4d82fc JJ |
334 | |
335 | // The libbacktrace API supports creating a state, but it does not | |
336 | // support destroying a state. I personally take this to mean that a | |
337 | // state is meant to be created and then live forever. | |
338 | // | |
339 | // I would love to register an at_exit() handler which cleans up this | |
340 | // state, but libbacktrace provides no way to do so. | |
341 | // | |
342 | // With these constraints, this function has a statically cached state | |
343 | // that is calculated the first time this is requested. Remember that | |
344 | // backtracing all happens serially (one global lock). | |
345 | // | |
346 | // An additionally oddity in this function is that we initialize the | |
347 | // filename via self_exe_name() to pass to libbacktrace. It turns out | |
348 | // that on Linux libbacktrace seamlessly gets the filename of the | |
349 | // current executable, but this fails on freebsd. by always providing | |
350 | // it, we make sure that libbacktrace never has a reason to not look up | |
351 | // the symbols. The libbacktrace API also states that the filename must | |
352 | // be in "permanent memory", so we copy it to a static and then use the | |
353 | // static as the pointer. | |
354 | // | |
355 | // FIXME: We also call self_exe_name() on DragonFly BSD. I haven't | |
356 | // tested if this is required or not. | |
357 | unsafe fn init_state() -> *mut backtrace_state { | |
358 | static mut STATE: *mut backtrace_state = 0 as *mut backtrace_state; | |
359 | static mut LAST_FILENAME: [libc::c_char; 256] = [0; 256]; | |
360 | if !STATE.is_null() { return STATE } | |
361 | let selfname = if cfg!(target_os = "freebsd") || | |
85aaf69f | 362 | cfg!(target_os = "dragonfly") || |
c34b1796 | 363 | cfg!(target_os = "bitrig") || |
c1a9b12d | 364 | cfg!(target_os = "netbsd") || |
85aaf69f SL |
365 | cfg!(target_os = "openbsd") { |
366 | env::current_exe().ok() | |
1a4d82fc JJ |
367 | } else { |
368 | None | |
369 | }; | |
370 | let filename = match selfname { | |
371 | Some(path) => { | |
c34b1796 | 372 | let bytes = path.as_os_str().as_bytes(); |
1a4d82fc JJ |
373 | if bytes.len() < LAST_FILENAME.len() { |
374 | let i = bytes.iter(); | |
375 | for (slot, val) in LAST_FILENAME.iter_mut().zip(i) { | |
376 | *slot = *val as libc::c_char; | |
377 | } | |
378 | LAST_FILENAME.as_ptr() | |
379 | } else { | |
380 | ptr::null() | |
381 | } | |
382 | } | |
383 | None => ptr::null(), | |
384 | }; | |
385 | STATE = backtrace_create_state(filename, 0, error_cb, | |
386 | ptr::null_mut()); | |
387 | return STATE | |
388 | } | |
389 | ||
390 | //////////////////////////////////////////////////////////////////////// | |
391 | // translation | |
392 | //////////////////////////////////////////////////////////////////////// | |
393 | ||
394 | // backtrace errors are currently swept under the rug, only I/O | |
395 | // errors are reported | |
396 | let state = unsafe { init_state() }; | |
397 | if state.is_null() { | |
398 | return output(w, idx, addr, None) | |
399 | } | |
85aaf69f | 400 | let mut data = ptr::null(); |
1a4d82fc JJ |
401 | let data_addr = &mut data as *mut *const libc::c_char; |
402 | let ret = unsafe { | |
c34b1796 | 403 | backtrace_syminfo(state, symaddr as libc::uintptr_t, |
1a4d82fc JJ |
404 | syminfo_cb, error_cb, |
405 | data_addr as *mut libc::c_void) | |
406 | }; | |
407 | if ret == 0 || data.is_null() { | |
c34b1796 | 408 | try!(output(w, idx, addr, None)); |
1a4d82fc | 409 | } else { |
c34b1796 AL |
410 | try!(output(w, idx, addr, Some(unsafe { CStr::from_ptr(data).to_bytes() }))); |
411 | } | |
412 | ||
413 | // pcinfo may return an arbitrary number of file:line pairs, | |
414 | // in the order of stack trace (i.e. inlined calls first). | |
415 | // in order to avoid allocation, we stack-allocate a fixed size of entries. | |
416 | const FILELINE_SIZE: usize = 32; | |
417 | let mut fileline_buf = [(ptr::null(), -1); FILELINE_SIZE]; | |
418 | let ret; | |
419 | let fileline_count; | |
420 | { | |
421 | let mut fileline_win: &mut [FileLine] = &mut fileline_buf; | |
422 | let fileline_addr = &mut fileline_win as *mut &mut [FileLine]; | |
423 | ret = unsafe { | |
424 | backtrace_pcinfo(state, addr as libc::uintptr_t, | |
425 | pcinfo_cb, error_cb, | |
426 | fileline_addr as *mut libc::c_void) | |
427 | }; | |
428 | fileline_count = FILELINE_SIZE - fileline_win.len(); | |
429 | } | |
430 | if ret == 0 { | |
431 | for (i, &(file, line)) in fileline_buf[..fileline_count].iter().enumerate() { | |
432 | if file.is_null() { continue; } // just to be sure | |
433 | let file = unsafe { CStr::from_ptr(file).to_bytes() }; | |
434 | try!(output_fileline(w, file, line, i == FILELINE_SIZE - 1)); | |
435 | } | |
1a4d82fc | 436 | } |
c34b1796 AL |
437 | |
438 | Ok(()) | |
1a4d82fc JJ |
439 | } |
440 | ||
441 | // Finally, after all that work above, we can emit a symbol. | |
c34b1796 AL |
442 | fn output(w: &mut Write, idx: isize, addr: *mut libc::c_void, |
443 | s: Option<&[u8]>) -> io::Result<()> { | |
1a4d82fc JJ |
444 | try!(write!(w, " {:2}: {:2$?} - ", idx, addr, HEX_WIDTH)); |
445 | match s.and_then(|s| str::from_utf8(s).ok()) { | |
446 | Some(string) => try!(demangle(w, string)), | |
447 | None => try!(write!(w, "<unknown>")), | |
448 | } | |
85aaf69f | 449 | w.write_all(&['\n' as u8]) |
1a4d82fc JJ |
450 | } |
451 | ||
c34b1796 AL |
452 | #[allow(dead_code)] |
453 | fn output_fileline(w: &mut Write, file: &[u8], line: libc::c_int, | |
454 | more: bool) -> io::Result<()> { | |
455 | let file = str::from_utf8(file).unwrap_or("<unknown>"); | |
456 | // prior line: " ##: {:2$} - func" | |
457 | try!(write!(w, " {:3$}at {}:{}", "", file, line, HEX_WIDTH)); | |
458 | if more { | |
459 | try!(write!(w, " <... and possibly more>")); | |
460 | } | |
461 | w.write_all(&['\n' as u8]) | |
462 | } | |
463 | ||
1a4d82fc JJ |
464 | /// Unwind library interface used for backtraces |
465 | /// | |
466 | /// Note that dead code is allowed as here are just bindings | |
467 | /// iOS doesn't use all of them it but adding more | |
468 | /// platform-specific configs pollutes the code too much | |
469 | #[allow(non_camel_case_types)] | |
470 | #[allow(non_snake_case)] | |
471 | #[allow(dead_code)] | |
472 | mod uw { | |
473 | pub use self::_Unwind_Reason_Code::*; | |
474 | ||
475 | use libc; | |
476 | ||
477 | #[repr(C)] | |
478 | pub enum _Unwind_Reason_Code { | |
479 | _URC_NO_REASON = 0, | |
480 | _URC_FOREIGN_EXCEPTION_CAUGHT = 1, | |
481 | _URC_FATAL_PHASE2_ERROR = 2, | |
482 | _URC_FATAL_PHASE1_ERROR = 3, | |
483 | _URC_NORMAL_STOP = 4, | |
484 | _URC_END_OF_STACK = 5, | |
485 | _URC_HANDLER_FOUND = 6, | |
486 | _URC_INSTALL_CONTEXT = 7, | |
487 | _URC_CONTINUE_UNWIND = 8, | |
488 | _URC_FAILURE = 9, // used only by ARM EABI | |
489 | } | |
490 | ||
491 | pub enum _Unwind_Context {} | |
492 | ||
493 | pub type _Unwind_Trace_Fn = | |
494 | extern fn(ctx: *mut _Unwind_Context, | |
495 | arg: *mut libc::c_void) -> _Unwind_Reason_Code; | |
496 | ||
497 | extern { | |
498 | // No native _Unwind_Backtrace on iOS | |
499 | #[cfg(not(all(target_os = "ios", target_arch = "arm")))] | |
500 | pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, | |
501 | trace_argument: *mut libc::c_void) | |
502 | -> _Unwind_Reason_Code; | |
503 | ||
c34b1796 | 504 | // available since GCC 4.2.0, should be fine for our purpose |
85aaf69f | 505 | #[cfg(all(not(all(target_os = "android", target_arch = "arm")), |
1a4d82fc | 506 | not(all(target_os = "linux", target_arch = "arm"))))] |
c34b1796 AL |
507 | pub fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, |
508 | ip_before_insn: *mut libc::c_int) | |
509 | -> libc::uintptr_t; | |
1a4d82fc JJ |
510 | |
511 | #[cfg(all(not(target_os = "android"), | |
512 | not(all(target_os = "linux", target_arch = "arm"))))] | |
513 | pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) | |
514 | -> *mut libc::c_void; | |
515 | } | |
516 | ||
517 | // On android, the function _Unwind_GetIP is a macro, and this is the | |
518 | // expansion of the macro. This is all copy/pasted directly from the | |
519 | // header file with the definition of _Unwind_GetIP. | |
85aaf69f | 520 | #[cfg(any(all(target_os = "android", target_arch = "arm"), |
1a4d82fc JJ |
521 | all(target_os = "linux", target_arch = "arm")))] |
522 | pub unsafe fn _Unwind_GetIP(ctx: *mut _Unwind_Context) -> libc::uintptr_t { | |
523 | #[repr(C)] | |
524 | enum _Unwind_VRS_Result { | |
525 | _UVRSR_OK = 0, | |
526 | _UVRSR_NOT_IMPLEMENTED = 1, | |
527 | _UVRSR_FAILED = 2, | |
528 | } | |
529 | #[repr(C)] | |
530 | enum _Unwind_VRS_RegClass { | |
531 | _UVRSC_CORE = 0, | |
532 | _UVRSC_VFP = 1, | |
533 | _UVRSC_FPA = 2, | |
534 | _UVRSC_WMMXD = 3, | |
535 | _UVRSC_WMMXC = 4, | |
536 | } | |
537 | #[repr(C)] | |
538 | enum _Unwind_VRS_DataRepresentation { | |
539 | _UVRSD_UINT32 = 0, | |
540 | _UVRSD_VFPX = 1, | |
541 | _UVRSD_FPAX = 2, | |
542 | _UVRSD_UINT64 = 3, | |
543 | _UVRSD_FLOAT = 4, | |
544 | _UVRSD_DOUBLE = 5, | |
545 | } | |
546 | ||
547 | type _Unwind_Word = libc::c_uint; | |
548 | extern { | |
549 | fn _Unwind_VRS_Get(ctx: *mut _Unwind_Context, | |
550 | klass: _Unwind_VRS_RegClass, | |
551 | word: _Unwind_Word, | |
552 | repr: _Unwind_VRS_DataRepresentation, | |
553 | data: *mut libc::c_void) | |
554 | -> _Unwind_VRS_Result; | |
555 | } | |
556 | ||
557 | let mut val: _Unwind_Word = 0; | |
558 | let ptr = &mut val as *mut _Unwind_Word; | |
559 | let _ = _Unwind_VRS_Get(ctx, _Unwind_VRS_RegClass::_UVRSC_CORE, 15, | |
560 | _Unwind_VRS_DataRepresentation::_UVRSD_UINT32, | |
561 | ptr as *mut libc::c_void); | |
562 | (val & !1) as libc::uintptr_t | |
563 | } | |
564 | ||
c34b1796 AL |
565 | // This function doesn't exist on Android or ARM/Linux, so make it same |
566 | // to _Unwind_GetIP | |
567 | #[cfg(any(all(target_os = "android", target_arch = "arm"), | |
568 | all(target_os = "linux", target_arch = "arm")))] | |
569 | pub unsafe fn _Unwind_GetIPInfo(ctx: *mut _Unwind_Context, | |
570 | ip_before_insn: *mut libc::c_int) | |
571 | -> libc::uintptr_t | |
572 | { | |
573 | *ip_before_insn = 0; | |
574 | _Unwind_GetIP(ctx) | |
575 | } | |
576 | ||
1a4d82fc JJ |
577 | // This function also doesn't exist on Android or ARM/Linux, so make it |
578 | // a no-op | |
579 | #[cfg(any(target_os = "android", | |
580 | all(target_os = "linux", target_arch = "arm")))] | |
581 | pub unsafe fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) | |
582 | -> *mut libc::c_void | |
583 | { | |
584 | pc | |
585 | } | |
586 | } |