1 use core
::fmt
::{self, Write}
;
2 use core
::mem
::{size_of, transmute}
;
3 use core
::slice
::from_raw_parts
;
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)]
15 f
: extern "C" fn(info
: &dl_phdr_info
, size
: usize, data
: &mut DsoPrinter
<'_
, '_
>) -> i32,
16 data
: &mut DsoPrinter
<'_
, '_
>,
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.
23 const PT_LOAD
: u32 = 1;
24 const PT_NOTE
: u32 = 4;
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. Eventually 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.
33 #[allow(non_camel_case_types)]
38 phdr
: *const Elf_Phdr
,
47 fn program_headers(&self) -> PhdrIter
<'_
> {
49 phdrs
: self.phdr_slice(),
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) }
61 phdrs
: &'a
[Elf_Phdr
],
65 impl<'a
> Iterator
for PhdrIter
<'a
> {
67 fn next(&mut self) -> Option
<Self::Item
> {
68 self.phdrs
.split_first().map(|(phdr
, new_phdrs
)| {
69 self.phdrs
= new_phdrs
;
78 // Elf_Phdr represents a 64-bit ELF program header in the endianness of the target
80 #[allow(non_camel_case_types)]
81 #[derive(Clone, Debug)]
94 // Phdr represents a valid ELF program header and its contents.
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
> {
109 self.base
.add(self.phdr
.p_offset
as usize),
110 self.phdr
.p_memsz
as usize,
116 // The note type for build IDs.
117 const NT_GNU_BUILD_ID
: u32 = 3;
119 // Elf_Nhdr represents an ELF note header in the endianness of the target.
120 #[allow(non_camel_case_types)]
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.
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.
140 struct NoteIter
<'a
> {
145 impl<'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 {
151 base
: from_raw_parts(base
, size
),
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.
161 fn align_to(x
: usize, to
: usize) -> usize {
162 (x
+ to
- 1) & (!to
+ 1)
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.
170 fn take_bytes_align4
<'a
>(num
: usize, bytes
: &mut &'a
[u8]) -> Option
<&'a
[u8]> {
171 if bytes
.len() < align_to(num
, 4) {
174 let (out
, bytes_new
) = bytes
.split_at(num
);
175 *bytes
= &bytes_new
[align_to(num
, 4) - num
..];
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.
183 fn take_nhdr
<'a
>(bytes
: &mut &'a
[u8]) -> Option
<&'a Elf_Nhdr
> {
184 if size_of
::<Elf_Nhdr
>() > bytes
.len() {
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
>()..];
195 impl<'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
{
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
)?
;
219 /// Indicates that a segment is executable.
220 const PERM_X
: u32 = 0b00000001;
221 /// Indicates that a segment is writable.
222 const PERM_W
: u32 = 0b00000010;
223 /// Indicates that a segment is readable.
224 const PERM_R
: u32 = 0b00000100;
226 impl core
::fmt
::Display
for Perm
{
227 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
242 /// Represents an ELF segment at runtime.
244 /// Gives the runtime virtual address of this segment's contents.
246 /// Gives the memory size of this segment's contents.
248 /// Gives the module virtual address of this segment with the ELF file.
250 /// Gives the permissions found in the ELF file. These permissions are not
251 /// necessarily the permissions present at runtime however.
255 /// Lets one iterate over Segments from a DSO.
256 struct SegmentIter
<'a
> {
257 phdrs
: &'a
[Elf_Phdr
],
261 impl Iterator
for SegmentIter
<'_
> {
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
{
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
),
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.
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).
288 /// On Fuchsia virtually all binaries have build IDs but this is not a strict
289 /// requirement. There's no way to match up DSO information with a real ELF
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.
295 phdrs
: &'a
[Elf_Phdr
],
299 /// Returns an iterator over Segments in this DSO.
300 fn segments(&self) -> SegmentIter
<'_
> {
302 phdrs
: self.phdrs
.as_ref(),
308 struct HexSlice
<'a
> {
312 impl fmt
::Display
for HexSlice
<'_
> {
313 fn fmt(&self, f
: &mut fmt
::Formatter
<'_
>) -> fmt
::Result
{
314 for byte
in self.bytes
{
315 write
!(f
, "{:02x}", byte
)?
;
321 fn 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
);
334 /// These errors encode issues that arise while parsing information about
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.
346 /// Calls either 'dso' or 'error' for each DSO linked into the process by the
351 /// * `visitor` - A DsoPrinter that will have one of eats methods called foreach DSO.
352 fn for_each_dso(mut visitor
: &mut DsoPrinter
<'_
, '_
>) {
353 extern "C" fn callback(
356 visitor
: &mut DsoPrinter
<'_
, '_
>,
358 // dl_iterate_phdr ensures that info.name will point to a valid
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
) {
366 return visitor
.error(Error
::NameError(err
)) as i32;
369 let build_id
= match get_build_id(info
) {
370 Some(build_id
) => build_id
,
372 return visitor
.error(Error
::BuildIDError
) as i32;
378 phdrs
: info
.phdr_slice(),
379 base
: info
.addr
as usize,
382 unsafe { dl_iterate_phdr(callback, &mut visitor) }
;
385 struct DsoPrinter
<'a
, 'b
> {
386 writer
: &'a
mut core
::fmt
::Formatter
<'b
>,
388 error
: core
::fmt
::Result
,
391 impl DsoPrinter
<'_
, '_
> {
392 fn dso(&mut self, dso
: Dso
<'_
>) -> bool
{
396 "{{{{{{module:{:#x}:{}:elf:{}}}}}}}\n",
400 bytes
: dso
.build_id
.as_ref()
403 for seg
in dso
.segments() {
406 "{{{{{{mmap:{:#x}:{:#x}:load:{:#x}:{}:{:#x}}}}}}}\n",
407 seg
.addr
, seg
.size
, self.module_count
, seg
.flags
, seg
.mod_rel_addr
410 self.module_count
+= 1;
416 self.error
= Err(err
);
421 fn error(&mut self, _error
: Error
) -> bool
{
426 /// This function prints the Fuchsia symbolizer markup for all information contained in a DSO.
427 pub fn print_dso_context(out
: &mut core
::fmt
::Formatter
<'_
>) -> core
::fmt
::Result
{
428 out
.write_str("{{{reset:begin}}}\n")?
;
429 let mut visitor
= DsoPrinter
{
434 for_each_dso(&mut visitor
);
438 /// This function prints the Fuchsia symbolizer markup to end the backtrace.
439 pub fn finish_context(out
: &mut core
::fmt
::Formatter
<'_
>) -> core
::fmt
::Result
{
440 out
.write_str("{{{reset:end}}}\n")