]> git.proxmox.com Git - rustc.git/blob - library/backtrace/src/symbolize/gimli.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / library / backtrace / src / symbolize / gimli.rs
1 //! Support for symbolication using the `gimli` crate on crates.io
2 //!
3 //! This is the default symbolication implementation for Rust.
4
5 use self::gimli::read::EndianSlice;
6 use self::gimli::NativeEndian as Endian;
7 use self::mmap::Mmap;
8 use self::stash::Stash;
9 use super::BytesOrWideString;
10 use super::ResolveWhat;
11 use super::SymbolName;
12 use addr2line::gimli;
13 use core::convert::TryInto;
14 use core::mem;
15 use core::u32;
16 use libc::c_void;
17 use mystd::ffi::OsString;
18 use mystd::fs::File;
19 use mystd::path::Path;
20 use mystd::prelude::v1::*;
21
22 #[cfg(backtrace_in_libstd)]
23 mod mystd {
24 pub use crate::*;
25 }
26 #[cfg(not(backtrace_in_libstd))]
27 extern crate std as mystd;
28
29 cfg_if::cfg_if! {
30 if #[cfg(windows)] {
31 #[path = "gimli/mmap_windows.rs"]
32 mod mmap;
33 } else if #[cfg(any(
34 target_os = "android",
35 target_os = "freebsd",
36 target_os = "fuchsia",
37 target_os = "haiku",
38 target_os = "ios",
39 target_os = "linux",
40 target_os = "macos",
41 target_os = "openbsd",
42 target_os = "solaris",
43 target_os = "illumos",
44 ))] {
45 #[path = "gimli/mmap_unix.rs"]
46 mod mmap;
47 } else {
48 #[path = "gimli/mmap_fake.rs"]
49 mod mmap;
50 }
51 }
52
53 mod stash;
54
55 const MAPPINGS_CACHE_SIZE: usize = 4;
56
57 struct Mapping {
58 // 'static lifetime is a lie to hack around lack of support for self-referential structs.
59 cx: Context<'static>,
60 _map: Mmap,
61 _stash: Stash,
62 }
63
64 enum Either<A, B> {
65 #[allow(dead_code)]
66 A(A),
67 B(B),
68 }
69
70 impl Mapping {
71 /// Creates a `Mapping` by ensuring that the `data` specified is used to
72 /// create a `Context` and it can only borrow from that or the `Stash` of
73 /// decompressed sections or auxiliary data.
74 fn mk<F>(data: Mmap, mk: F) -> Option<Mapping>
75 where
76 F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Context<'a>>,
77 {
78 Mapping::mk_or_other(data, move |data, stash| {
79 let cx = mk(data, stash)?;
80 Some(Either::B(cx))
81 })
82 }
83
84 /// Creates a `Mapping` from `data`, or if the closure decides to, returns a
85 /// different mapping.
86 fn mk_or_other<F>(data: Mmap, mk: F) -> Option<Mapping>
87 where
88 F: for<'a> FnOnce(&'a [u8], &'a Stash) -> Option<Either<Mapping, Context<'a>>>,
89 {
90 let stash = Stash::new();
91 let cx = match mk(&data, &stash)? {
92 Either::A(mapping) => return Some(mapping),
93 Either::B(cx) => cx,
94 };
95 Some(Mapping {
96 // Convert to 'static lifetimes since the symbols should
97 // only borrow `map` and `stash` and we're preserving them below.
98 cx: unsafe { core::mem::transmute::<Context<'_>, Context<'static>>(cx) },
99 _map: data,
100 _stash: stash,
101 })
102 }
103 }
104
105 struct Context<'a> {
106 dwarf: addr2line::Context<EndianSlice<'a, Endian>>,
107 object: Object<'a>,
108 }
109
110 impl<'data> Context<'data> {
111 fn new(
112 stash: &'data Stash,
113 object: Object<'data>,
114 sup: Option<Object<'data>>,
115 ) -> Option<Context<'data>> {
116 let mut sections = gimli::Dwarf::load(|id| -> Result<_, ()> {
117 let data = object.section(stash, id.name()).unwrap_or(&[]);
118 Ok(EndianSlice::new(data, Endian))
119 })
120 .ok()?;
121
122 if let Some(sup) = sup {
123 sections
124 .load_sup(|id| -> Result<_, ()> {
125 let data = sup.section(stash, id.name()).unwrap_or(&[]);
126 Ok(EndianSlice::new(data, Endian))
127 })
128 .ok()?;
129 }
130 let dwarf = addr2line::Context::from_dwarf(sections).ok()?;
131
132 Some(Context { dwarf, object })
133 }
134 }
135
136 fn mmap(path: &Path) -> Option<Mmap> {
137 let file = File::open(path).ok()?;
138 let len = file.metadata().ok()?.len().try_into().ok()?;
139 unsafe { Mmap::map(&file, len) }
140 }
141
142 cfg_if::cfg_if! {
143 if #[cfg(windows)] {
144 mod coff;
145 use self::coff::Object;
146 } else if #[cfg(any(
147 target_os = "macos",
148 target_os = "ios",
149 target_os = "tvos",
150 target_os = "watchos",
151 ))] {
152 mod macho;
153 use self::macho::Object;
154 } else {
155 mod elf;
156 use self::elf::Object;
157 }
158 }
159
160 cfg_if::cfg_if! {
161 if #[cfg(windows)] {
162 mod libs_windows;
163 use libs_windows::native_libraries;
164 } else if #[cfg(any(
165 target_os = "macos",
166 target_os = "ios",
167 target_os = "tvos",
168 target_os = "watchos",
169 ))] {
170 mod libs_macos;
171 use libs_macos::native_libraries;
172 } else if #[cfg(target_os = "illumos")] {
173 mod libs_illumos;
174 use libs_illumos::native_libraries;
175 } else if #[cfg(all(
176 any(
177 target_os = "linux",
178 target_os = "fuchsia",
179 target_os = "freebsd",
180 target_os = "openbsd",
181 all(target_os = "android", feature = "dl_iterate_phdr"),
182 ),
183 not(target_env = "uclibc"),
184 ))] {
185 mod libs_dl_iterate_phdr;
186 use libs_dl_iterate_phdr::native_libraries;
187 } else if #[cfg(target_env = "libnx")] {
188 mod libs_libnx;
189 use libs_libnx::native_libraries;
190 } else if #[cfg(target_os = "haiku")] {
191 mod libs_haiku;
192 use libs_haiku::native_libraries;
193 } else {
194 // Everything else should doesn't know how to load native libraries.
195 fn native_libraries() -> Vec<Library> {
196 Vec::new()
197 }
198 }
199 }
200
201 #[derive(Default)]
202 struct Cache {
203 /// All known shared libraries that have been loaded.
204 libraries: Vec<Library>,
205
206 /// Mappings cache where we retain parsed dwarf information.
207 ///
208 /// This list has a fixed capacity for its entire lifetime which never
209 /// increases. The `usize` element of each pair is an index into `libraries`
210 /// above where `usize::max_value()` represents the current executable. The
211 /// `Mapping` is corresponding parsed dwarf information.
212 ///
213 /// Note that this is basically an LRU cache and we'll be shifting things
214 /// around in here as we symbolize addresses.
215 mappings: Vec<(usize, Mapping)>,
216 }
217
218 struct Library {
219 name: OsString,
220 /// Segments of this library loaded into memory, and where they're loaded.
221 segments: Vec<LibrarySegment>,
222 /// The "bias" of this library, typically where it's loaded into memory.
223 /// This value is added to each segment's stated address to get the actual
224 /// virtual memory address that the segment is loaded into. Additionally
225 /// this bias is subtracted from real virtual memory addresses to index into
226 /// debuginfo and the symbol table.
227 bias: usize,
228 }
229
230 struct LibrarySegment {
231 /// The stated address of this segment in the object file. This is not
232 /// actually where the segment is loaded, but rather this address plus the
233 /// containing library's `bias` is where to find it.
234 stated_virtual_memory_address: usize,
235 /// The size of this segment in memory.
236 len: usize,
237 }
238
239 // unsafe because this is required to be externally synchronized
240 pub unsafe fn clear_symbol_cache() {
241 Cache::with_global(|cache| cache.mappings.clear());
242 }
243
244 impl Cache {
245 fn new() -> Cache {
246 Cache {
247 mappings: Vec::with_capacity(MAPPINGS_CACHE_SIZE),
248 libraries: native_libraries(),
249 }
250 }
251
252 // unsafe because this is required to be externally synchronized
253 unsafe fn with_global(f: impl FnOnce(&mut Self)) {
254 // A very small, very simple LRU cache for debug info mappings.
255 //
256 // The hit rate should be very high, since the typical stack doesn't cross
257 // between many shared libraries.
258 //
259 // The `addr2line::Context` structures are pretty expensive to create. Its
260 // cost is expected to be amortized by subsequent `locate` queries, which
261 // leverage the structures built when constructing `addr2line::Context`s to
262 // get nice speedups. If we didn't have this cache, that amortization would
263 // never happen, and symbolicating backtraces would be ssssllllooooowwww.
264 static mut MAPPINGS_CACHE: Option<Cache> = None;
265
266 f(MAPPINGS_CACHE.get_or_insert_with(|| Cache::new()))
267 }
268
269 fn avma_to_svma(&self, addr: *const u8) -> Option<(usize, *const u8)> {
270 self.libraries
271 .iter()
272 .enumerate()
273 .filter_map(|(i, lib)| {
274 // First up, test if this `lib` has any segment containing the
275 // `addr` (handling relocation). If this check passes then we
276 // can continue below and actually translate the address.
277 //
278 // Note that we're using `wrapping_add` here to avoid overflow
279 // checks. It's been seen in the wild that the SVMA + bias
280 // computation overflows. It seems a bit odd that would happen
281 // but there's not a huge amount we can do about it other than
282 // probably just ignore those segments since they're likely
283 // pointing off into space. This originally came up in
284 // rust-lang/backtrace-rs#329.
285 if !lib.segments.iter().any(|s| {
286 let svma = s.stated_virtual_memory_address;
287 let start = svma.wrapping_add(lib.bias);
288 let end = start.wrapping_add(s.len);
289 let address = addr as usize;
290 start <= address && address < end
291 }) {
292 return None;
293 }
294
295 // Now that we know `lib` contains `addr`, we can offset with
296 // the bias to find the stated virtual memory address.
297 let svma = (addr as usize).wrapping_sub(lib.bias);
298 Some((i, svma as *const u8))
299 })
300 .next()
301 }
302
303 fn mapping_for_lib<'a>(&'a mut self, lib: usize) -> Option<&'a mut Context<'a>> {
304 let idx = self.mappings.iter().position(|(idx, _)| *idx == lib);
305
306 // Invariant: after this conditional completes without early returning
307 // from an error, the cache entry for this path is at index 0.
308
309 if let Some(idx) = idx {
310 // When the mapping is already in the cache, move it to the front.
311 if idx != 0 {
312 let entry = self.mappings.remove(idx);
313 self.mappings.insert(0, entry);
314 }
315 } else {
316 // When the mapping is not in the cache, create a new mapping,
317 // insert it into the front of the cache, and evict the oldest cache
318 // entry if necessary.
319 let name = &self.libraries[lib].name;
320 let mapping = Mapping::new(name.as_ref())?;
321
322 if self.mappings.len() == MAPPINGS_CACHE_SIZE {
323 self.mappings.pop();
324 }
325
326 self.mappings.insert(0, (lib, mapping));
327 }
328
329 let cx: &'a mut Context<'static> = &mut self.mappings[0].1.cx;
330 // don't leak the `'static` lifetime, make sure it's scoped to just
331 // ourselves
332 Some(unsafe { mem::transmute::<&'a mut Context<'static>, &'a mut Context<'a>>(cx) })
333 }
334 }
335
336 pub unsafe fn resolve(what: ResolveWhat<'_>, cb: &mut dyn FnMut(&super::Symbol)) {
337 let addr = what.address_or_ip();
338 let mut call = |sym: Symbol<'_>| {
339 // Extend the lifetime of `sym` to `'static` since we are unfortunately
340 // required to here, but it's only ever going out as a reference so no
341 // reference to it should be persisted beyond this frame anyway.
342 let sym = mem::transmute::<Symbol<'_>, Symbol<'static>>(sym);
343 (cb)(&super::Symbol { inner: sym });
344 };
345
346 Cache::with_global(|cache| {
347 let (lib, addr) = match cache.avma_to_svma(addr as *const u8) {
348 Some(pair) => pair,
349 None => return,
350 };
351
352 // Finally, get a cached mapping or create a new mapping for this file, and
353 // evaluate the DWARF info to find the file/line/name for this address.
354 let cx = match cache.mapping_for_lib(lib) {
355 Some(cx) => cx,
356 None => return,
357 };
358 let mut any_frames = false;
359 if let Ok(mut frames) = cx.dwarf.find_frames(addr as u64) {
360 while let Ok(Some(frame)) = frames.next() {
361 any_frames = true;
362 let name = match frame.function {
363 Some(f) => Some(f.name.slice()),
364 None => cx.object.search_symtab(addr as u64),
365 };
366 call(Symbol::Frame {
367 addr: addr as *mut c_void,
368 location: frame.location,
369 name,
370 });
371 }
372 }
373 if !any_frames {
374 if let Some((object_cx, object_addr)) = cx.object.search_object_map(addr as u64) {
375 if let Ok(mut frames) = object_cx.dwarf.find_frames(object_addr) {
376 while let Ok(Some(frame)) = frames.next() {
377 any_frames = true;
378 call(Symbol::Frame {
379 addr: addr as *mut c_void,
380 location: frame.location,
381 name: frame.function.map(|f| f.name.slice()),
382 });
383 }
384 }
385 }
386 }
387 if !any_frames {
388 if let Some(name) = cx.object.search_symtab(addr as u64) {
389 call(Symbol::Symtab {
390 addr: addr as *mut c_void,
391 name,
392 });
393 }
394 }
395 });
396 }
397
398 pub enum Symbol<'a> {
399 /// We were able to locate frame information for this symbol, and
400 /// `addr2line`'s frame internally has all the nitty gritty details.
401 Frame {
402 addr: *mut c_void,
403 location: Option<addr2line::Location<'a>>,
404 name: Option<&'a [u8]>,
405 },
406 /// Couldn't find debug information, but we found it in the symbol table of
407 /// the elf executable.
408 Symtab { addr: *mut c_void, name: &'a [u8] },
409 }
410
411 impl Symbol<'_> {
412 pub fn name(&self) -> Option<SymbolName<'_>> {
413 match self {
414 Symbol::Frame { name, .. } => {
415 let name = name.as_ref()?;
416 Some(SymbolName::new(name))
417 }
418 Symbol::Symtab { name, .. } => Some(SymbolName::new(name)),
419 }
420 }
421
422 pub fn addr(&self) -> Option<*mut c_void> {
423 match self {
424 Symbol::Frame { addr, .. } => Some(*addr),
425 Symbol::Symtab { .. } => None,
426 }
427 }
428
429 pub fn filename_raw(&self) -> Option<BytesOrWideString<'_>> {
430 match self {
431 Symbol::Frame { location, .. } => {
432 let file = location.as_ref()?.file?;
433 Some(BytesOrWideString::Bytes(file.as_bytes()))
434 }
435 Symbol::Symtab { .. } => None,
436 }
437 }
438
439 pub fn filename(&self) -> Option<&Path> {
440 match self {
441 Symbol::Frame { location, .. } => {
442 let file = location.as_ref()?.file?;
443 Some(Path::new(file))
444 }
445 Symbol::Symtab { .. } => None,
446 }
447 }
448
449 pub fn lineno(&self) -> Option<u32> {
450 match self {
451 Symbol::Frame { location, .. } => location.as_ref()?.line,
452 Symbol::Symtab { .. } => None,
453 }
454 }
455
456 pub fn colno(&self) -> Option<u32> {
457 match self {
458 Symbol::Frame { location, .. } => location.as_ref()?.column,
459 Symbol::Symtab { .. } => None,
460 }
461 }
462 }