]> git.proxmox.com Git - rustc.git/blame - library/backtrace/src/print/fuchsia.rs
New upstream version 1.63.0+dfsg1
[rustc.git] / library / backtrace / src / print / fuchsia.rs
CommitLineData
e1599b0c
XL
1use core::fmt::{self, Write};
2use core::mem::{size_of, transmute};
3use core::slice::from_raw_parts;
4use libc::c_char;
5
6extern "C" {
7 // dl_iterate_phdr takes a callback that will receive a dl_phdr_info pointer
8 // for every DSO that has been linked into the process. dl_iterate_phdr also
9 // ensures that the dynamic linker is locked from start to finish of the
10 // iteration. If the callback returns a non-zero value the iteration is
11 // terminated early. 'data' will be passed as the third argument to the
12 // callback on each call. 'size' gives the size of the dl_phdr_info.
13 #[allow(improper_ctypes)]
14 fn dl_iterate_phdr(
3dfed10e
XL
15 f: extern "C" fn(info: &dl_phdr_info, size: usize, data: &mut DsoPrinter<'_, '_>) -> i32,
16 data: &mut DsoPrinter<'_, '_>,
e1599b0c
XL
17 ) -> i32;
18}
19
20// We need to parse out the build ID and some basic program header data
21// which means that we need a bit of stuff from the ELF spec as well.
22
23const PT_LOAD: u32 = 1;
24const PT_NOTE: u32 = 4;
25
26// Now we have to replicate, bit for bit, the structure of the dl_phdr_info
27// type used by fuchsia's current dynamic linker. Chromium also has this ABI
28// boundary as well as crashpad. Eventully we'd like to move these cases to
29// use elf-search but we'd need to provide that in the SDK and that has not
30// yet been done. Thus we (and they) are stuck having to use this method
31// which incurs a tight coupling with the fuchsia libc.
32
33#[allow(non_camel_case_types)]
34#[repr(C)]
35struct dl_phdr_info {
36 addr: *const u8,
37 name: *const c_char,
38 phdr: *const Elf_Phdr,
39 phnum: u16,
40 adds: u64,
41 subs: u64,
42 tls_modid: usize,
43 tls_data: *const u8,
44}
45
46impl dl_phdr_info {
47 fn program_headers(&self) -> PhdrIter<'_> {
48 PhdrIter {
49 phdrs: self.phdr_slice(),
50 base: self.addr,
51 }
52 }
53 // We have no way of knowing of checking if e_phoff and e_phnum are valid.
54 // libc should ensure this for us however so it's safe to form a slice here.
55 fn phdr_slice(&self) -> &[Elf_Phdr] {
56 unsafe { from_raw_parts(self.phdr, self.phnum as usize) }
57 }
58}
59
60struct PhdrIter<'a> {
61 phdrs: &'a [Elf_Phdr],
62 base: *const u8,
63}
64
65impl<'a> Iterator for PhdrIter<'a> {
66 type Item = Phdr<'a>;
67 fn next(&mut self) -> Option<Self::Item> {
68 self.phdrs.split_first().map(|(phdr, new_phdrs)| {
69 self.phdrs = new_phdrs;
70 Phdr {
71 phdr,
72 base: self.base,
73 }
74 })
75 }
76}
77
78// Elf_Phdr represents a 64-bit ELF program header in the endianness of the target
79// architecture.
80#[allow(non_camel_case_types)]
81#[derive(Clone, Debug)]
82#[repr(C)]
83struct Elf_Phdr {
84 p_type: u32,
85 p_flags: u32,
86 p_offset: u64,
87 p_vaddr: u64,
88 p_paddr: u64,
89 p_filesz: u64,
90 p_memsz: u64,
91 p_align: u64,
92}
93
94// Phdr represents a valid ELF program header and its contents.
95struct Phdr<'a> {
96 phdr: &'a Elf_Phdr,
97 base: *const u8,
98}
99
100impl<'a> Phdr<'a> {
101 // We have no way of checking if p_addr or p_memsz are valid. Fuchsia's libc
102 // parses the notes first however so by virtue of being here these headers
103 // must be valid. NoteIter does not require the underlying data to be valid
104 // but it does require the bounds to be valid. We trust that libc has ensured
105 // that this is the case for us here.
106 fn notes(&self) -> NoteIter<'a> {
107 unsafe {
108 NoteIter::new(
109 self.base.add(self.phdr.p_offset as usize),
110 self.phdr.p_memsz as usize,
111 )
112 }
113 }
114}
115
116// The note type for build IDs.
117const NT_GNU_BUILD_ID: u32 = 3;
118
119// Elf_Nhdr represents an ELF note header in the endianness of the target.
120#[allow(non_camel_case_types)]
121#[repr(C)]
122struct Elf_Nhdr {
123 n_namesz: u32,
124 n_descsz: u32,
125 n_type: u32,
126}
127
128// Note represents an ELF note (header + contents). The name is left as a u8
129// slice because it is not always null terminated and rust makes it easy enough
130// to check that the bytes match eitherway.
131struct Note<'a> {
132 name: &'a [u8],
133 desc: &'a [u8],
134 tipe: u32,
135}
136
137// NoteIter lets you safely iterate over a note segment. It terminates as soon
138// as an error occurs or there are no more notes. If you iterate over invalid
139// data it will function as though no notes were found.
140struct NoteIter<'a> {
141 base: &'a [u8],
142 error: bool,
143}
144
145impl<'a> NoteIter<'a> {
146 // It is an invariant of function that the pointer and size given denote a
147 // valid range of bytes that can all be read. The contents of these bytes
148 // can be anything but the range must be valid for this to be safe.
149 unsafe fn new(base: *const u8, size: usize) -> Self {
150 NoteIter {
151 base: from_raw_parts(base, size),
152 error: false,
153 }
154 }
155}
156
157// align_to aligns 'x' to 'to'-byte alignment assuming 'to' is a power of 2.
158// This follows a standard pattern in C/C++ ELF parsing code where
159// (x + to - 1) & -to is used. Rust does not let you negate usize so I use
160// 2's-complement conversion to recreate that.
161fn align_to(x: usize, to: usize) -> usize {
162 (x + to - 1) & (!to + 1)
163}
164
165// take_bytes_align4 consumes num bytes from the slice (if present) and
166// additionally ensures that the final slice is properlly aligned. If an
167// either the number of bytes requested is too large or the slice can't be
168// realigned afterwards due to not enough remaining bytes existing, None is
169// returned and the slice is not modified.
170fn take_bytes_align4<'a>(num: usize, bytes: &mut &'a [u8]) -> Option<&'a [u8]> {
171 if bytes.len() < align_to(num, 4) {
172 return None;
173 }
174 let (out, bytes_new) = bytes.split_at(num);
175 *bytes = &bytes_new[align_to(num, 4) - num..];
176 Some(out)
177}
178
179// This function has no real invariants the caller must uphold other than
180// perhaps that 'bytes' should be aligned for performance (and on some
181// architectures correctness). The values in the Elf_Nhdr fields might
182// be nonsense but this function ensures no such thing.
183fn take_nhdr<'a>(bytes: &mut &'a [u8]) -> Option<&'a Elf_Nhdr> {
184 if size_of::<Elf_Nhdr>() > bytes.len() {
185 return None;
186 }
187 // This is safe as long as there is enough space and we just confirmed that
188 // in the if statement above so this should not be unsafe.
189 let out = unsafe { transmute::<*const u8, &'a Elf_Nhdr>(bytes.as_ptr()) };
190 // Note that sice_of::<Elf_Nhdr>() is always 4-byte aligned.
191 *bytes = &bytes[size_of::<Elf_Nhdr>()..];
192 Some(out)
193}
194
195impl<'a> Iterator for NoteIter<'a> {
196 type Item = Note<'a>;
197 fn next(&mut self) -> Option<Self::Item> {
198 // Check if we've reached the end.
199 if self.base.len() == 0 || self.error {
200 return None;
201 }
202 // We transmute out an nhdr but we carefully consider the resulting
203 // struct. We don't trust the namesz or descsz and we make no unsafe
204 // decisions based on the type. So even if we get out complete garbage
205 // we should still be safe.
206 let nhdr = take_nhdr(&mut self.base)?;
207 let name = take_bytes_align4(nhdr.n_namesz as usize, &mut self.base)?;
208 let desc = take_bytes_align4(nhdr.n_descsz as usize, &mut self.base)?;
209 Some(Note {
210 name: name,
211 desc: desc,
212 tipe: nhdr.n_type,
213 })
214 }
215}
216
217struct Perm(u32);
218
219/// Indicates that a segment is executable.
220const PERM_X: u32 = 0b00000001;
221/// Indicates that a segment is writable.
222const PERM_W: u32 = 0b00000010;
223/// Indicates that a segment is readable.
224const PERM_R: u32 = 0b00000100;
225
226impl core::fmt::Display for Perm {
3dfed10e 227 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
e1599b0c
XL
228 let v = self.0;
229 if v & PERM_R != 0 {
230 f.write_char('r')?
231 }
232 if v & PERM_W != 0 {
233 f.write_char('w')?
234 }
235 if v & PERM_X != 0 {
236 f.write_char('x')?
237 }
238 Ok(())
239 }
240}
241
242/// Represents an ELF segment at runtime.
243struct Segment {
244 /// Gives the runtime virtual address of this segment's contents.
245 addr: usize,
246 /// Gives the memory size of this segment's contents.
247 size: usize,
248 /// Gives the module virtual address of this segment with the ELF file.
249 mod_rel_addr: usize,
250 /// Gives the permissions found in the ELF file. These permissions are not
251 /// necessarily the permissions present at runtime however.
252 flags: Perm,
253}
254
255/// Lets one iterate over Segments from a DSO.
256struct SegmentIter<'a> {
257 phdrs: &'a [Elf_Phdr],
258 base: usize,
259}
260
261impl Iterator for SegmentIter<'_> {
262 type Item = Segment;
263
264 fn next(&mut self) -> Option<Self::Item> {
265 self.phdrs.split_first().and_then(|(phdr, new_phdrs)| {
266 self.phdrs = new_phdrs;
267 if phdr.p_type != PT_LOAD {
268 self.next()
269 } else {
270 Some(Segment {
271 addr: phdr.p_vaddr as usize + self.base,
272 size: phdr.p_memsz as usize,
273 mod_rel_addr: phdr.p_vaddr as usize,
274 flags: Perm(phdr.p_flags),
275 })
276 }
277 })
278 }
279}
280
281/// Represents an ELF DSO (Dynamic Shared Object). This type references
282/// the data stored in the actual DSO rather than making its own copy.
283struct Dso<'a> {
284 /// The dynamic linker always gives us a name, even if the name is empty.
285 /// In the case of the main executable this name will be empty. In the case
286 /// of a shared object it will be the soname (see DT_SONAME).
287 name: &'a str,
288 /// On Fuchsia virtually all binaries have build IDs but this is not a strict
923072b8 289 /// requirement. There's no way to match up DSO information with a real ELF
e1599b0c
XL
290 /// file afterwards if there is no build_id so we require that every DSO
291 /// have one here. DSO's without a build_id are ignored.
292 build_id: &'a [u8],
293
294 base: usize,
295 phdrs: &'a [Elf_Phdr],
296}
297
298impl Dso<'_> {
299 /// Returns an iterator over Segments in this DSO.
300 fn segments(&self) -> SegmentIter<'_> {
301 SegmentIter {
302 phdrs: self.phdrs.as_ref(),
303 base: self.base,
304 }
305 }
306}
307
308struct HexSlice<'a> {
309 bytes: &'a [u8],
310}
311
312impl fmt::Display for HexSlice<'_> {
3dfed10e 313 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
e1599b0c 314 for byte in self.bytes {
e74abb32 315 write!(f, "{:02x}", byte)?;
e1599b0c
XL
316 }
317 Ok(())
318 }
319}
320
321fn get_build_id<'a>(info: &'a dl_phdr_info) -> Option<&'a [u8]> {
322 for phdr in info.program_headers() {
323 if phdr.phdr.p_type == PT_NOTE {
324 for note in phdr.notes() {
325 if note.tipe == NT_GNU_BUILD_ID && (note.name == b"GNU\0" || note.name == b"GNU") {
326 return Some(note.desc);
327 }
328 }
329 }
330 }
331 None
332}
333
334/// These errors encode issues that arise while parsing information about
335/// each DSO.
336enum Error {
337 /// NameError means that an error occurred while converting a C style string
338 /// into a rust string.
339 NameError(core::str::Utf8Error),
340 /// BuildIDError means that we didn't find a build ID. This could either be
341 /// because the DSO had no build ID or because the segment containing the
342 /// build ID was malformed.
343 BuildIDError,
344}
345
346/// Calls either 'dso' or 'error' for each DSO linked into the process by the
347/// dynamic linker.
348///
349/// # Arguments
350///
351/// * `visitor` - A DsoPrinter that will have one of eats methods called foreach DSO.
3dfed10e
XL
352fn for_each_dso(mut visitor: &mut DsoPrinter<'_, '_>) {
353 extern "C" fn callback(
354 info: &dl_phdr_info,
355 _size: usize,
356 visitor: &mut DsoPrinter<'_, '_>,
357 ) -> i32 {
e1599b0c
XL
358 // dl_iterate_phdr ensures that info.name will point to a valid
359 // location.
360 let name_len = unsafe { libc::strlen(info.name) };
361 let name_slice: &[u8] =
362 unsafe { core::slice::from_raw_parts(info.name as *const u8, name_len) };
363 let name = match core::str::from_utf8(name_slice) {
364 Ok(name) => name,
365 Err(err) => {
366 return visitor.error(Error::NameError(err)) as i32;
367 }
368 };
369 let build_id = match get_build_id(info) {
370 Some(build_id) => build_id,
371 None => {
372 return visitor.error(Error::BuildIDError) as i32;
373 }
374 };
375 visitor.dso(Dso {
376 name: name,
377 build_id: build_id,
378 phdrs: info.phdr_slice(),
379 base: info.addr as usize,
380 }) as i32
381 }
382 unsafe { dl_iterate_phdr(callback, &mut visitor) };
383}
384
385struct DsoPrinter<'a, 'b> {
386 writer: &'a mut core::fmt::Formatter<'b>,
387 module_count: usize,
388 error: core::fmt::Result,
389}
390
391impl DsoPrinter<'_, '_> {
392 fn dso(&mut self, dso: Dso<'_>) -> bool {
393 let mut write = || {
394 write!(
395 self.writer,
396 "{{{{{{module:{:#x}:{}:elf:{}}}}}}}\n",
397 self.module_count,
398 dso.name,
399 HexSlice {
400 bytes: dso.build_id.as_ref()
401 }
402 )?;
403 for seg in dso.segments() {
404 write!(
405 self.writer,
406 "{{{{{{mmap:{:#x}:{:#x}:load:{:#x}:{}:{:#x}}}}}}}\n",
407 seg.addr, seg.size, self.module_count, seg.flags, seg.mod_rel_addr
408 )?;
409 }
410 self.module_count += 1;
411 Ok(())
412 };
413 match write() {
414 Ok(()) => false,
415 Err(err) => {
416 self.error = Err(err);
417 true
418 }
419 }
420 }
421 fn error(&mut self, _error: Error) -> bool {
422 false
423 }
424}
425
426/// This function prints the Fuchsia symbolizer markup for all information contained in a DSO.
427pub fn print_dso_context(out: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
428 out.write_str("{{{reset}}}\n")?;
429 let mut visitor = DsoPrinter {
430 writer: out,
431 module_count: 0,
432 error: Ok(()),
433 };
434 for_each_dso(&mut visitor);
435 visitor.error
436}