1 // Allow clippy lints when building without clippy.
2 #![allow(unknown_lints)]
3
4 use fallible_iterator::FallibleIterator;
5 use gimli::{Section, UnitHeader, UnitOffset, UnitSectionOffset, UnitType, UnwindSection};
6 use object::{Object, ObjectSection, ObjectSymbol};
7 use regex::bytes::Regex;
8 use std::borrow::{Borrow, Cow};
9 use std::cmp::min;
10 use std::collections::HashMap;
11 use std::env;
12 use std::fmt::{self, Debug};
13 use std::fs;
14 use std::io;
15 use std::io::{BufWriter, Write};
16 use std::iter::Iterator;
17 use std::mem;
18 use std::process;
19 use std::result;
20 use std::sync::{Condvar, Mutex};
21 use typed_arena::Arena;
22
23 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
24 pub enum Error {
25 GimliError(gimli::Error),
26 ObjectError(object::read::Error),
27 IoError,
28 }
29
30 impl fmt::Display for Error {
31 #[inline]
fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error>32 fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> {
33 Debug::fmt(self, f)
34 }
35 }
36
writeln_error<W: Write, R: Reader>( w: &mut W, dwarf: &gimli::Dwarf<R>, err: Error, msg: &str, ) -> io::Result<()>37 fn writeln_error<W: Write, R: Reader>(
38 w: &mut W,
39 dwarf: &gimli::Dwarf<R>,
40 err: Error,
41 msg: &str,
42 ) -> io::Result<()> {
43 writeln!(
44 w,
45 "{}: {}",
46 msg,
47 match err {
48 Error::GimliError(err) => dwarf.format_error(err),
49 Error::ObjectError(err) =>
50 format!("{}:{:?}", "An object error occurred while reading", err),
51 Error::IoError => "An I/O error occurred while writing.".to_string(),
52 }
53 )
54 }
55
56 impl From<gimli::Error> for Error {
from(err: gimli::Error) -> Self57 fn from(err: gimli::Error) -> Self {
58 Error::GimliError(err)
59 }
60 }
61
62 impl From<io::Error> for Error {
from(_: io::Error) -> Self63 fn from(_: io::Error) -> Self {
64 Error::IoError
65 }
66 }
67
68 impl From<object::read::Error> for Error {
from(err: object::read::Error) -> Self69 fn from(err: object::read::Error) -> Self {
70 Error::ObjectError(err)
71 }
72 }
73
74 pub type Result<T> = result::Result<T, Error>;
75
parallel_output<W, II, F>(w: &mut W, max_workers: usize, iter: II, f: F) -> Result<()> where W: Write + Send, F: Sync + Fn(II::Item, &mut Vec<u8>) -> Result<()>, II: IntoIterator, II::IntoIter: Send,76 fn parallel_output<W, II, F>(w: &mut W, max_workers: usize, iter: II, f: F) -> Result<()>
77 where
78 W: Write + Send,
79 F: Sync + Fn(II::Item, &mut Vec<u8>) -> Result<()>,
80 II: IntoIterator,
81 II::IntoIter: Send,
82 {
83 struct ParallelOutputState<I, W> {
84 iterator: I,
85 current_worker: usize,
86 result: Result<()>,
87 w: W,
88 }
89
90 let state = Mutex::new(ParallelOutputState {
91 iterator: iter.into_iter().fuse(),
92 current_worker: 0,
93 result: Ok(()),
94 w,
95 });
96 let workers = min(max_workers, num_cpus::get());
97 let mut condvars = Vec::new();
98 for _ in 0..workers {
99 condvars.push(Condvar::new());
100 }
101 {
102 let state_ref = &state;
103 let f_ref = &f;
104 let condvars_ref = &condvars;
105 crossbeam::scope(|scope| {
106 for i in 0..workers {
107 scope.spawn(move |_| {
108 let mut v = Vec::new();
109 let mut lock = state_ref.lock().unwrap();
110 while lock.current_worker != i {
111 lock = condvars_ref[i].wait(lock).unwrap();
112 }
113 loop {
114 let item = if lock.result.is_ok() {
115 lock.iterator.next()
116 } else {
117 None
118 };
119 lock.current_worker = (i + 1) % workers;
120 condvars_ref[lock.current_worker].notify_one();
121 mem::drop(lock);
122
123 let ret = if let Some(item) = item {
124 v.clear();
125 f_ref(item, &mut v)
126 } else {
127 return;
128 };
129
130 lock = state_ref.lock().unwrap();
131 while lock.current_worker != i {
132 lock = condvars_ref[i].wait(lock).unwrap();
133 }
134 if lock.result.is_ok() {
135 let ret2 = lock.w.write_all(&v);
136 if ret.is_err() {
137 lock.result = ret;
138 } else {
139 lock.result = ret2.map_err(Error::from);
140 }
141 }
142 }
143 });
144 }
145 })
146 .unwrap();
147 }
148 state.into_inner().unwrap().result
149 }
150
151 trait Reader: gimli::Reader<Offset = usize> + Send + Sync {}
152
153 impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> where
154 Endian: gimli::Endianity + Send + Sync
155 {
156 }
157
158 type RelocationMap = HashMap<usize, object::Relocation>;
159
add_relocations( relocations: &mut RelocationMap, file: &object::File, section: &object::Section, )160 fn add_relocations(
161 relocations: &mut RelocationMap,
162 file: &object::File,
163 section: &object::Section,
164 ) {
165 for (offset64, mut relocation) in section.relocations() {
166 let offset = offset64 as usize;
167 if offset as u64 != offset64 {
168 continue;
169 }
170 let offset = offset as usize;
171 match relocation.kind() {
172 object::RelocationKind::Absolute => {
173 match relocation.target() {
174 object::RelocationTarget::Symbol(symbol_idx) => {
175 match file.symbol_by_index(symbol_idx) {
176 Ok(symbol) => {
177 let addend =
178 symbol.address().wrapping_add(relocation.addend() as u64);
179 relocation.set_addend(addend as i64);
180 }
181 Err(_) => {
182 eprintln!(
183 "Relocation with invalid symbol for section {} at offset 0x{:08x}",
184 section.name().unwrap(),
185 offset
186 );
187 }
188 }
189 }
190 _ => {}
191 }
192 if relocations.insert(offset, relocation).is_some() {
193 eprintln!(
194 "Multiple relocations for section {} at offset 0x{:08x}",
195 section.name().unwrap(),
196 offset
197 );
198 }
199 }
200 _ => {
201 eprintln!(
202 "Unsupported relocation for section {} at offset 0x{:08x}",
203 section.name().unwrap(),
204 offset
205 );
206 }
207 }
208 }
209 }
210
211 /// Apply relocations to addresses and offsets during parsing,
212 /// instead of requiring the data to be fully relocated prior
213 /// to parsing.
214 ///
215 /// Pros
216 /// - allows readonly buffers, we don't need to implement writing of values back to buffers
217 /// - potentially allows us to handle addresses and offsets differently
218 /// - potentially allows us to add metadata from the relocation (eg symbol names)
219 /// Cons
220 /// - maybe incomplete
221 #[derive(Debug, Clone)]
222 struct Relocate<'a, R: gimli::Reader<Offset = usize>> {
223 relocations: &'a RelocationMap,
224 section: R,
225 reader: R,
226 }
227
228 impl<'a, R: gimli::Reader<Offset = usize>> Relocate<'a, R> {
relocate(&self, offset: usize, value: u64) -> u64229 fn relocate(&self, offset: usize, value: u64) -> u64 {
230 if let Some(relocation) = self.relocations.get(&offset) {
231 match relocation.kind() {
232 object::RelocationKind::Absolute => {
233 if relocation.has_implicit_addend() {
234 // Use the explicit addend too, because it may have the symbol value.
235 return value.wrapping_add(relocation.addend() as u64);
236 } else {
237 return relocation.addend() as u64;
238 }
239 }
240 _ => {}
241 }
242 };
243 value
244 }
245 }
246
247 impl<'a, R: gimli::Reader<Offset = usize>> gimli::Reader for Relocate<'a, R> {
248 type Endian = R::Endian;
249 type Offset = R::Offset;
250
read_address(&mut self, address_size: u8) -> gimli::Result<u64>251 fn read_address(&mut self, address_size: u8) -> gimli::Result<u64> {
252 let offset = self.reader.offset_from(&self.section);
253 let value = self.reader.read_address(address_size)?;
254 Ok(self.relocate(offset, value))
255 }
256
read_length(&mut self, format: gimli::Format) -> gimli::Result<usize>257 fn read_length(&mut self, format: gimli::Format) -> gimli::Result<usize> {
258 let offset = self.reader.offset_from(&self.section);
259 let value = self.reader.read_length(format)?;
260 <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
261 }
262
read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize>263 fn read_offset(&mut self, format: gimli::Format) -> gimli::Result<usize> {
264 let offset = self.reader.offset_from(&self.section);
265 let value = self.reader.read_offset(format)?;
266 <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
267 }
268
read_sized_offset(&mut self, size: u8) -> gimli::Result<usize>269 fn read_sized_offset(&mut self, size: u8) -> gimli::Result<usize> {
270 let offset = self.reader.offset_from(&self.section);
271 let value = self.reader.read_sized_offset(size)?;
272 <usize as gimli::ReaderOffset>::from_u64(self.relocate(offset, value as u64))
273 }
274
275 #[inline]
split(&mut self, len: Self::Offset) -> gimli::Result<Self>276 fn split(&mut self, len: Self::Offset) -> gimli::Result<Self> {
277 let mut other = self.clone();
278 other.reader.truncate(len)?;
279 self.reader.skip(len)?;
280 Ok(other)
281 }
282
283 // All remaining methods simply delegate to `self.reader`.
284
285 #[inline]
endian(&self) -> Self::Endian286 fn endian(&self) -> Self::Endian {
287 self.reader.endian()
288 }
289
290 #[inline]
len(&self) -> Self::Offset291 fn len(&self) -> Self::Offset {
292 self.reader.len()
293 }
294
295 #[inline]
empty(&mut self)296 fn empty(&mut self) {
297 self.reader.empty()
298 }
299
300 #[inline]
truncate(&mut self, len: Self::Offset) -> gimli::Result<()>301 fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> {
302 self.reader.truncate(len)
303 }
304
305 #[inline]
offset_from(&self, base: &Self) -> Self::Offset306 fn offset_from(&self, base: &Self) -> Self::Offset {
307 self.reader.offset_from(&base.reader)
308 }
309
310 #[inline]
offset_id(&self) -> gimli::ReaderOffsetId311 fn offset_id(&self) -> gimli::ReaderOffsetId {
312 self.reader.offset_id()
313 }
314
315 #[inline]
lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset>316 fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset> {
317 self.reader.lookup_offset_id(id)
318 }
319
320 #[inline]
find(&self, byte: u8) -> gimli::Result<Self::Offset>321 fn find(&self, byte: u8) -> gimli::Result<Self::Offset> {
322 self.reader.find(byte)
323 }
324
325 #[inline]
skip(&mut self, len: Self::Offset) -> gimli::Result<()>326 fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> {
327 self.reader.skip(len)
328 }
329
330 #[inline]
to_slice(&self) -> gimli::Result<Cow<[u8]>>331 fn to_slice(&self) -> gimli::Result<Cow<[u8]>> {
332 self.reader.to_slice()
333 }
334
335 #[inline]
to_string(&self) -> gimli::Result<Cow<str>>336 fn to_string(&self) -> gimli::Result<Cow<str>> {
337 self.reader.to_string()
338 }
339
340 #[inline]
to_string_lossy(&self) -> gimli::Result<Cow<str>>341 fn to_string_lossy(&self) -> gimli::Result<Cow<str>> {
342 self.reader.to_string_lossy()
343 }
344
345 #[inline]
read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()>346 fn read_slice(&mut self, buf: &mut [u8]) -> gimli::Result<()> {
347 self.reader.read_slice(buf)
348 }
349 }
350
351 impl<'a, R: Reader> Reader for Relocate<'a, R> {}
352
353 #[derive(Default)]
354 struct Flags<'a> {
355 eh_frame: bool,
356 goff: bool,
357 info: bool,
358 line: bool,
359 pubnames: bool,
360 pubtypes: bool,
361 aranges: bool,
362 dwo: bool,
363 dwp: bool,
364 dwo_parent: Option<object::File<'a>>,
365 sup: Option<object::File<'a>>,
366 raw: bool,
367 match_units: Option<Regex>,
368 }
369
print_usage(opts: &getopts::Options) -> !370 fn print_usage(opts: &getopts::Options) -> ! {
371 let brief = format!("Usage: {} <options> <file>", env::args().next().unwrap());
372 write!(&mut io::stderr(), "{}", opts.usage(&brief)).ok();
373 process::exit(1);
374 }
375
main()376 fn main() {
377 let mut opts = getopts::Options::new();
378 opts.optflag(
379 "",
380 "eh-frame",
381 "print .eh-frame exception handling frame information",
382 );
383 opts.optflag("G", "", "show global die offsets");
384 opts.optflag("i", "", "print .debug_info and .debug_types sections");
385 opts.optflag("l", "", "print .debug_line section");
386 opts.optflag("p", "", "print .debug_pubnames section");
387 opts.optflag("r", "", "print .debug_aranges section");
388 opts.optflag("y", "", "print .debug_pubtypes section");
389 opts.optflag(
390 "",
391 "dwo",
392 "print the .dwo versions of the selected sections",
393 );
394 opts.optflag(
395 "",
396 "dwp",
397 "print the .dwp versions of the selected sections",
398 );
399 opts.optopt(
400 "",
401 "dwo-parent",
402 "use the specified file as the parent of the dwo or dwp (e.g. for .debug_addr)",
403 "library path",
404 );
405 opts.optflag("", "raw", "print raw data values");
406 opts.optopt(
407 "u",
408 "match-units",
409 "print compilation units whose output matches a regex",
410 "REGEX",
411 );
412 opts.optopt("", "sup", "path to supplementary object file", "PATH");
413
414 let matches = match opts.parse(env::args().skip(1)) {
415 Ok(m) => m,
416 Err(e) => {
417 writeln!(&mut io::stderr(), "{:?}\n", e).ok();
418 print_usage(&opts);
419 }
420 };
421 if matches.free.is_empty() {
422 print_usage(&opts);
423 }
424
425 let mut all = true;
426 let mut flags = Flags::default();
427 if matches.opt_present("eh-frame") {
428 flags.eh_frame = true;
429 all = false;
430 }
431 if matches.opt_present("G") {
432 flags.goff = true;
433 }
434 if matches.opt_present("i") {
435 flags.info = true;
436 all = false;
437 }
438 if matches.opt_present("l") {
439 flags.line = true;
440 all = false;
441 }
442 if matches.opt_present("p") {
443 flags.pubnames = true;
444 all = false;
445 }
446 if matches.opt_present("y") {
447 flags.pubtypes = true;
448 all = false;
449 }
450 if matches.opt_present("r") {
451 flags.aranges = true;
452 all = false;
453 }
454 if matches.opt_present("dwo") {
455 flags.dwo = true;
456 }
457 if matches.opt_present("dwp") {
458 flags.dwp = true;
459 }
460 if matches.opt_present("raw") {
461 flags.raw = true;
462 }
463 if all {
464 // .eh_frame is excluded even when printing all information.
465 // cosmetic flags like -G must be set explicitly too.
466 flags.info = true;
467 flags.line = true;
468 flags.pubnames = true;
469 flags.pubtypes = true;
470 flags.aranges = true;
471 }
472 flags.match_units = if let Some(r) = matches.opt_str("u") {
473 match Regex::new(&r) {
474 Ok(r) => Some(r),
475 Err(e) => {
476 eprintln!("Invalid regular expression {}: {}", r, e);
477 process::exit(1);
478 }
479 }
480 } else {
481 None
482 };
483
484 let arena_mmap = Arena::new();
485 let load_file = |path| {
486 let file = match fs::File::open(&path) {
487 Ok(file) => file,
488 Err(err) => {
489 eprintln!("Failed to open file '{}': {}", path, err);
490 process::exit(1);
491 }
492 };
493 let mmap = match unsafe { memmap::Mmap::map(&file) } {
494 Ok(mmap) => mmap,
495 Err(err) => {
496 eprintln!("Failed to map file '{}': {}", path, err);
497 process::exit(1);
498 }
499 };
500 let mmap_ref = (*arena_mmap.alloc(mmap)).borrow();
501 match object::File::parse(&**mmap_ref) {
502 Ok(file) => Some(file),
503 Err(err) => {
504 eprintln!("Failed to parse file '{}': {}", path, err);
505 process::exit(1);
506 }
507 }
508 };
509
510 flags.sup = matches.opt_str("sup").and_then(load_file);
511 flags.dwo_parent = matches.opt_str("dwo-parent").and_then(load_file);
512 if flags.dwo_parent.is_some() && !flags.dwo && !flags.dwp {
513 eprintln!("--dwo-parent also requires --dwo or --dwp");
514 process::exit(1);
515 }
516 if flags.dwo_parent.is_none() && flags.dwp {
517 eprintln!("--dwp also requires --dwo-parent");
518 process::exit(1);
519 }
520
521 for file_path in &matches.free {
522 if matches.free.len() != 1 {
523 println!("{}", file_path);
524 println!();
525 }
526
527 let file = match fs::File::open(&file_path) {
528 Ok(file) => file,
529 Err(err) => {
530 eprintln!("Failed to open file '{}': {}", file_path, err);
531 continue;
532 }
533 };
534 let file = match unsafe { memmap::Mmap::map(&file) } {
535 Ok(mmap) => mmap,
536 Err(err) => {
537 eprintln!("Failed to map file '{}': {}", file_path, err);
538 continue;
539 }
540 };
541 let file = match object::File::parse(&*file) {
542 Ok(file) => file,
543 Err(err) => {
544 eprintln!("Failed to parse file '{}': {}", file_path, err);
545 continue;
546 }
547 };
548
549 let endian = if file.is_little_endian() {
550 gimli::RunTimeEndian::Little
551 } else {
552 gimli::RunTimeEndian::Big
553 };
554 let ret = dump_file(&file, endian, &flags);
555 match ret {
556 Ok(_) => (),
557 Err(err) => eprintln!("Failed to dump '{}': {}", file_path, err,),
558 }
559 }
560 }
561
empty_file_section<'input, 'arena, Endian: gimli::Endianity>( endian: Endian, arena_relocations: &'arena Arena<RelocationMap>, ) -> Relocate<'arena, gimli::EndianSlice<'arena, Endian>>562 fn empty_file_section<'input, 'arena, Endian: gimli::Endianity>(
563 endian: Endian,
564 arena_relocations: &'arena Arena<RelocationMap>,
565 ) -> Relocate<'arena, gimli::EndianSlice<'arena, Endian>> {
566 let reader = gimli::EndianSlice::new(&[], endian);
567 let section = reader;
568 let relocations = RelocationMap::default();
569 let relocations = (*arena_relocations.alloc(relocations)).borrow();
570 Relocate {
571 relocations,
572 section,
573 reader,
574 }
575 }
576
load_file_section<'input, 'arena, Endian: gimli::Endianity>( id: gimli::SectionId, file: &object::File<'input>, endian: Endian, is_dwo: bool, arena_data: &'arena Arena<Cow<'input, [u8]>>, arena_relocations: &'arena Arena<RelocationMap>, ) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>>577 fn load_file_section<'input, 'arena, Endian: gimli::Endianity>(
578 id: gimli::SectionId,
579 file: &object::File<'input>,
580 endian: Endian,
581 is_dwo: bool,
582 arena_data: &'arena Arena<Cow<'input, [u8]>>,
583 arena_relocations: &'arena Arena<RelocationMap>,
584 ) -> Result<Relocate<'arena, gimli::EndianSlice<'arena, Endian>>> {
585 let mut relocations = RelocationMap::default();
586 let name = if is_dwo {
587 id.dwo_name()
588 } else {
589 Some(id.name())
590 };
591
592 let data = match name.and_then(|name| file.section_by_name(&name)) {
593 Some(ref section) => {
594 // DWO sections never have relocations, so don't bother.
595 if !is_dwo {
596 add_relocations(&mut relocations, file, section);
597 }
598 section.uncompressed_data()?
599 }
600 // Use a non-zero capacity so that `ReaderOffsetId`s are unique.
601 None => Cow::Owned(Vec::with_capacity(1)),
602 };
603 let data_ref = (*arena_data.alloc(data)).borrow();
604 let reader = gimli::EndianSlice::new(data_ref, endian);
605 let section = reader;
606 let relocations = (*arena_relocations.alloc(relocations)).borrow();
607 Ok(Relocate {
608 relocations,
609 section,
610 reader,
611 })
612 }
613
dump_file<Endian>(file: &object::File, endian: Endian, flags: &Flags) -> Result<()> where Endian: gimli::Endianity + Send + Sync,614 fn dump_file<Endian>(file: &object::File, endian: Endian, flags: &Flags) -> Result<()>
615 where
616 Endian: gimli::Endianity + Send + Sync,
617 {
618 let arena_data = Arena::new();
619 let arena_relocations = Arena::new();
620
621 let dwo_parent = if let Some(dwo_parent_file) = flags.dwo_parent.as_ref() {
622 let mut load_dwo_parent_section = |id: gimli::SectionId| -> Result<_> {
623 load_file_section(
624 id,
625 dwo_parent_file,
626 endian,
627 false,
628 &arena_data,
629 &arena_relocations,
630 )
631 };
632 Some(gimli::Dwarf::load(&mut load_dwo_parent_section)?)
633 } else {
634 None
635 };
636 let dwo_parent = dwo_parent.as_ref();
637
638 let dwo_parent_units = if let Some(dwo_parent) = dwo_parent {
639 Some(
640 match dwo_parent
641 .units()
642 .map(|unit_header| dwo_parent.unit(unit_header))
643 .filter_map(|unit| Ok(unit.dwo_id.map(|dwo_id| (dwo_id, unit))))
644 .collect()
645 {
646 Ok(units) => units,
647 Err(err) => {
648 eprintln!("Failed to process --dwo-parent units: {}", err);
649 return Ok(());
650 }
651 },
652 )
653 } else {
654 None
655 };
656 let dwo_parent_units = dwo_parent_units.as_ref();
657
658 let mut load_section = |id: gimli::SectionId| -> Result<_> {
659 load_file_section(
660 id,
661 file,
662 endian,
663 flags.dwo || flags.dwp,
664 &arena_data,
665 &arena_relocations,
666 )
667 };
668
669 let w = &mut BufWriter::new(io::stdout());
670 if flags.dwp {
671 let empty = empty_file_section(endian, &arena_relocations);
672 let dwp = gimli::DwarfPackage::load(&mut load_section, empty)?;
673 dump_dwp(w, &dwp, dwo_parent.unwrap(), dwo_parent_units, flags)?;
674 w.flush()?;
675 return Ok(());
676 }
677
678 let mut dwarf = gimli::Dwarf::load(&mut load_section)?;
679 if flags.dwo {
680 dwarf.file_type = gimli::DwarfFileType::Dwo;
681 if let Some(dwo_parent) = dwo_parent {
682 dwarf.debug_addr = dwo_parent.debug_addr.clone();
683 dwarf
684 .ranges
685 .set_debug_ranges(dwo_parent.ranges.debug_ranges().clone());
686 }
687 }
688
689 if let Some(sup_file) = flags.sup.as_ref() {
690 let mut load_sup_section = |id: gimli::SectionId| -> Result<_> {
691 // Note: we really only need the `.debug_str` section,
692 // but for now we load them all.
693 load_file_section(id, sup_file, endian, false, &arena_data, &arena_relocations)
694 };
695 dwarf.load_sup(&mut load_sup_section)?;
696 }
697
698 if flags.eh_frame {
699 let eh_frame = gimli::EhFrame::load(&mut load_section).unwrap();
700 dump_eh_frame(w, file, eh_frame)?;
701 }
702 if flags.info {
703 dump_info(w, &dwarf, dwo_parent_units, flags)?;
704 dump_types(w, &dwarf, dwo_parent_units, flags)?;
705 }
706 if flags.line {
707 dump_line(w, &dwarf)?;
708 }
709 if flags.pubnames {
710 let debug_pubnames = &gimli::Section::load(&mut load_section).unwrap();
711 dump_pubnames(w, debug_pubnames, &dwarf.debug_info)?;
712 }
713 if flags.aranges {
714 let debug_aranges = &gimli::Section::load(&mut load_section).unwrap();
715 dump_aranges(w, debug_aranges)?;
716 }
717 if flags.pubtypes {
718 let debug_pubtypes = &gimli::Section::load(&mut load_section).unwrap();
719 dump_pubtypes(w, debug_pubtypes, &dwarf.debug_info)?;
720 }
721 w.flush()?;
722 Ok(())
723 }
724
dump_eh_frame<R: Reader, W: Write>( w: &mut W, file: &object::File, mut eh_frame: gimli::EhFrame<R>, ) -> Result<()>725 fn dump_eh_frame<R: Reader, W: Write>(
726 w: &mut W,
727 file: &object::File,
728 mut eh_frame: gimli::EhFrame<R>,
729 ) -> Result<()> {
730 // TODO: this might be better based on the file format.
731 let address_size = file
732 .architecture()
733 .address_size()
734 .map(|w| w.bytes())
735 .unwrap_or(mem::size_of::<usize>() as u8);
736 eh_frame.set_address_size(address_size);
737
738 fn register_name_none(_: gimli::Register) -> Option<&'static str> {
739 None
740 }
741 let arch_register_name = match file.architecture() {
742 object::Architecture::Arm | object::Architecture::Aarch64 => gimli::Arm::register_name,
743 object::Architecture::I386 => gimli::X86::register_name,
744 object::Architecture::X86_64 => gimli::X86_64::register_name,
745 _ => register_name_none,
746 };
747 let register_name = &|register| match arch_register_name(register) {
748 Some(name) => Cow::Borrowed(name),
749 None => Cow::Owned(format!("{}", register.0)),
750 };
751
752 let mut bases = gimli::BaseAddresses::default();
753 if let Some(section) = file.section_by_name(".eh_frame_hdr") {
754 bases = bases.set_eh_frame_hdr(section.address());
755 }
756 if let Some(section) = file.section_by_name(".eh_frame") {
757 bases = bases.set_eh_frame(section.address());
758 }
759 if let Some(section) = file.section_by_name(".text") {
760 bases = bases.set_text(section.address());
761 }
762 if let Some(section) = file.section_by_name(".got") {
763 bases = bases.set_got(section.address());
764 }
765
766 // TODO: Print "__eh_frame" here on macOS, and more generally use the
767 // section that we're actually looking at, which is what the canonical
768 // dwarfdump does.
769 writeln!(
770 w,
771 "Exception handling frame information for section .eh_frame"
772 )?;
773
774 let mut cies = HashMap::new();
775
776 let mut entries = eh_frame.entries(&bases);
777 loop {
778 match entries.next()? {
779 None => return Ok(()),
780 Some(gimli::CieOrFde::Cie(cie)) => {
781 writeln!(w)?;
782 writeln!(w, "{:#010x}: CIE", cie.offset())?;
783 writeln!(w, " length: {:#010x}", cie.entry_len())?;
784 // TODO: CIE_id
785 writeln!(w, " version: {:#04x}", cie.version())?;
786 // TODO: augmentation
787 writeln!(w, " code_align: {}", cie.code_alignment_factor())?;
788 writeln!(w, " data_align: {}", cie.data_alignment_factor())?;
789 writeln!(w, " ra_register: {:#x}", cie.return_address_register().0)?;
790 if let Some(encoding) = cie.lsda_encoding() {
791 writeln!(w, " lsda_encoding: {:#02x}", encoding.0)?;
792 }
793 if let Some((encoding, personality)) = cie.personality_with_encoding() {
794 write!(w, " personality: {:#02x} ", encoding.0)?;
795 dump_pointer(w, personality)?;
796 writeln!(w)?;
797 }
798 if let Some(encoding) = cie.fde_address_encoding() {
799 writeln!(w, " fde_encoding: {:#02x}", encoding.0)?;
800 }
801 let instructions = cie.instructions(&eh_frame, &bases);
802 dump_cfi_instructions(w, instructions, true, register_name)?;
803 writeln!(w)?;
804 }
805 Some(gimli::CieOrFde::Fde(partial)) => {
806 let mut offset = None;
807 let fde = partial.parse(|_, bases, o| {
808 offset = Some(o);
809 cies.entry(o)
810 .or_insert_with(|| eh_frame.cie_from_offset(bases, o))
811 .clone()
812 })?;
813
814 writeln!(w)?;
815 writeln!(w, "{:#010x}: FDE", fde.offset())?;
816 writeln!(w, " length: {:#010x}", fde.entry_len())?;
817 writeln!(w, " CIE_pointer: {:#010x}", offset.unwrap().0)?;
818 // TODO: symbolicate the start address like the canonical dwarfdump does.
819 writeln!(w, " start_addr: {:#018x}", fde.initial_address())?;
820 writeln!(
821 w,
822 " range_size: {:#018x} (end_addr = {:#018x})",
823 fde.len(),
824 fde.initial_address() + fde.len()
825 )?;
826 if let Some(lsda) = fde.lsda() {
827 write!(w, " lsda: ")?;
828 dump_pointer(w, lsda)?;
829 writeln!(w)?;
830 }
831 let instructions = fde.instructions(&eh_frame, &bases);
832 dump_cfi_instructions(w, instructions, false, register_name)?;
833 writeln!(w)?;
834 }
835 }
836 }
837 }
838
dump_pointer<W: Write>(w: &mut W, p: gimli::Pointer) -> Result<()>839 fn dump_pointer<W: Write>(w: &mut W, p: gimli::Pointer) -> Result<()> {
840 match p {
841 gimli::Pointer::Direct(p) => {
842 write!(w, "{:#018x}", p)?;
843 }
844 gimli::Pointer::Indirect(p) => {
845 write!(w, "({:#018x})", p)?;
846 }
847 }
848 Ok(())
849 }
850
851 #[allow(clippy::unneeded_field_pattern)]
dump_cfi_instructions<R: Reader, W: Write>( w: &mut W, mut insns: gimli::CallFrameInstructionIter<R>, is_initial: bool, register_name: &dyn Fn(gimli::Register) -> Cow<'static, str>, ) -> Result<()>852 fn dump_cfi_instructions<R: Reader, W: Write>(
853 w: &mut W,
854 mut insns: gimli::CallFrameInstructionIter<R>,
855 is_initial: bool,
856 register_name: &dyn Fn(gimli::Register) -> Cow<'static, str>,
857 ) -> Result<()> {
858 use gimli::CallFrameInstruction::*;
859
860 // TODO: we need to actually evaluate these instructions as we iterate them
861 // so we can print the initialized state for CIEs, and each unwind row's
862 // registers for FDEs.
863 //
864 // TODO: We should print DWARF expressions for the CFI instructions that
865 // embed DWARF expressions within themselves.
866
867 if !is_initial {
868 writeln!(w, " Instructions:")?;
869 }
870
871 loop {
872 match insns.next() {
873 Err(e) => {
874 writeln!(w, "Failed to decode CFI instruction: {}", e)?;
875 return Ok(());
876 }
877 Ok(None) => {
878 if is_initial {
879 writeln!(w, " Instructions: Init State:")?;
880 }
881 return Ok(());
882 }
883 Ok(Some(op)) => match op {
884 SetLoc { address } => {
885 writeln!(w, " DW_CFA_set_loc ({:#x})", address)?;
886 }
887 AdvanceLoc { delta } => {
888 writeln!(w, " DW_CFA_advance_loc ({})", delta)?;
889 }
890 DefCfa { register, offset } => {
891 writeln!(
892 w,
893 " DW_CFA_def_cfa ({}, {})",
894 register_name(register),
895 offset
896 )?;
897 }
898 DefCfaSf {
899 register,
900 factored_offset,
901 } => {
902 writeln!(
903 w,
904 " DW_CFA_def_cfa_sf ({}, {})",
905 register_name(register),
906 factored_offset
907 )?;
908 }
909 DefCfaRegister { register } => {
910 writeln!(
911 w,
912 " DW_CFA_def_cfa_register ({})",
913 register_name(register)
914 )?;
915 }
916 DefCfaOffset { offset } => {
917 writeln!(w, " DW_CFA_def_cfa_offset ({})", offset)?;
918 }
919 DefCfaOffsetSf { factored_offset } => {
920 writeln!(
921 w,
922 " DW_CFA_def_cfa_offset_sf ({})",
923 factored_offset
924 )?;
925 }
926 DefCfaExpression { expression: _ } => {
927 writeln!(w, " DW_CFA_def_cfa_expression (...)")?;
928 }
929 Undefined { register } => {
930 writeln!(
931 w,
932 " DW_CFA_undefined ({})",
933 register_name(register)
934 )?;
935 }
936 SameValue { register } => {
937 writeln!(
938 w,
939 " DW_CFA_same_value ({})",
940 register_name(register)
941 )?;
942 }
943 Offset {
944 register,
945 factored_offset,
946 } => {
947 writeln!(
948 w,
949 " DW_CFA_offset ({}, {})",
950 register_name(register),
951 factored_offset
952 )?;
953 }
954 OffsetExtendedSf {
955 register,
956 factored_offset,
957 } => {
958 writeln!(
959 w,
960 " DW_CFA_offset_extended_sf ({}, {})",
961 register_name(register),
962 factored_offset
963 )?;
964 }
965 ValOffset {
966 register,
967 factored_offset,
968 } => {
969 writeln!(
970 w,
971 " DW_CFA_val_offset ({}, {})",
972 register_name(register),
973 factored_offset
974 )?;
975 }
976 ValOffsetSf {
977 register,
978 factored_offset,
979 } => {
980 writeln!(
981 w,
982 " DW_CFA_val_offset_sf ({}, {})",
983 register_name(register),
984 factored_offset
985 )?;
986 }
987 Register {
988 dest_register,
989 src_register,
990 } => {
991 writeln!(
992 w,
993 " DW_CFA_register ({}, {})",
994 register_name(dest_register),
995 register_name(src_register)
996 )?;
997 }
998 Expression {
999 register,
1000 expression: _,
1001 } => {
1002 writeln!(
1003 w,
1004 " DW_CFA_expression ({}, ...)",
1005 register_name(register)
1006 )?;
1007 }
1008 ValExpression {
1009 register,
1010 expression: _,
1011 } => {
1012 writeln!(
1013 w,
1014 " DW_CFA_val_expression ({}, ...)",
1015 register_name(register)
1016 )?;
1017 }
1018 Restore { register } => {
1019 writeln!(
1020 w,
1021 " DW_CFA_restore ({})",
1022 register_name(register)
1023 )?;
1024 }
1025 RememberState => {
1026 writeln!(w, " DW_CFA_remember_state")?;
1027 }
1028 RestoreState => {
1029 writeln!(w, " DW_CFA_restore_state")?;
1030 }
1031 ArgsSize { size } => {
1032 writeln!(w, " DW_CFA_GNU_args_size ({})", size)?;
1033 }
1034 Nop => {
1035 writeln!(w, " DW_CFA_nop")?;
1036 }
1037 },
1038 }
1039 }
1040 }
1041
dump_dwp<R: Reader, W: Write + Send>( w: &mut W, dwp: &gimli::DwarfPackage<R>, dwo_parent: &gimli::Dwarf<R>, dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, flags: &Flags, ) -> Result<()> where R::Endian: Send + Sync,1042 fn dump_dwp<R: Reader, W: Write + Send>(
1043 w: &mut W,
1044 dwp: &gimli::DwarfPackage<R>,
1045 dwo_parent: &gimli::Dwarf<R>,
1046 dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
1047 flags: &Flags,
1048 ) -> Result<()>
1049 where
1050 R::Endian: Send + Sync,
1051 {
1052 if dwp.cu_index.unit_count() != 0 {
1053 writeln!(
1054 w,
1055 "\n.debug_cu_index: version = {}, sections = {}, units = {}, slots = {}",
1056 dwp.cu_index.version(),
1057 dwp.cu_index.section_count(),
1058 dwp.cu_index.unit_count(),
1059 dwp.cu_index.slot_count(),
1060 )?;
1061 for i in 1..=dwp.cu_index.unit_count() {
1062 writeln!(w, "\nCU index {}", i)?;
1063 dump_dwp_sections(
1064 w,
1065 &dwp,
1066 dwo_parent,
1067 dwo_parent_units,
1068 flags,
1069 dwp.cu_index.sections(i)?,
1070 )?;
1071 }
1072 }
1073
1074 if dwp.tu_index.unit_count() != 0 {
1075 writeln!(
1076 w,
1077 "\n.debug_tu_index: version = {}, sections = {}, units = {}, slots = {}",
1078 dwp.tu_index.version(),
1079 dwp.tu_index.section_count(),
1080 dwp.tu_index.unit_count(),
1081 dwp.tu_index.slot_count(),
1082 )?;
1083 for i in 1..=dwp.tu_index.unit_count() {
1084 writeln!(w, "\nTU index {}", i)?;
1085 dump_dwp_sections(
1086 w,
1087 &dwp,
1088 dwo_parent,
1089 dwo_parent_units,
1090 flags,
1091 dwp.tu_index.sections(i)?,
1092 )?;
1093 }
1094 }
1095
1096 Ok(())
1097 }
1098
dump_dwp_sections<R: Reader, W: Write + Send>( w: &mut W, dwp: &gimli::DwarfPackage<R>, dwo_parent: &gimli::Dwarf<R>, dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, flags: &Flags, sections: gimli::UnitIndexSectionIterator<R>, ) -> Result<()> where R::Endian: Send + Sync,1099 fn dump_dwp_sections<R: Reader, W: Write + Send>(
1100 w: &mut W,
1101 dwp: &gimli::DwarfPackage<R>,
1102 dwo_parent: &gimli::Dwarf<R>,
1103 dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
1104 flags: &Flags,
1105 sections: gimli::UnitIndexSectionIterator<R>,
1106 ) -> Result<()>
1107 where
1108 R::Endian: Send + Sync,
1109 {
1110 for section in sections.clone() {
1111 writeln!(
1112 w,
1113 " {}: offset = 0x{:x}, size = 0x{:x}",
1114 section.section.dwo_name().unwrap(),
1115 section.offset,
1116 section.size
1117 )?;
1118 }
1119 let dwarf = dwp.sections(sections, dwo_parent)?;
1120 if flags.info {
1121 dump_info(w, &dwarf, dwo_parent_units, flags)?;
1122 dump_types(w, &dwarf, dwo_parent_units, flags)?;
1123 }
1124 if flags.line {
1125 dump_line(w, &dwarf)?;
1126 }
1127 Ok(())
1128 }
1129
dump_info<R: Reader, W: Write + Send>( w: &mut W, dwarf: &gimli::Dwarf<R>, dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, flags: &Flags, ) -> Result<()> where R::Endian: Send + Sync,1130 fn dump_info<R: Reader, W: Write + Send>(
1131 w: &mut W,
1132 dwarf: &gimli::Dwarf<R>,
1133 dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
1134 flags: &Flags,
1135 ) -> Result<()>
1136 where
1137 R::Endian: Send + Sync,
1138 {
1139 writeln!(w, "\n.debug_info")?;
1140
1141 let units = match dwarf.units().collect::<Vec<_>>() {
1142 Ok(units) => units,
1143 Err(err) => {
1144 writeln_error(
1145 w,
1146 dwarf,
1147 Error::GimliError(err),
1148 "Failed to read unit headers",
1149 )?;
1150 return Ok(());
1151 }
1152 };
1153 let process_unit = |header: UnitHeader<R>, buf: &mut Vec<u8>| -> Result<()> {
1154 dump_unit(buf, header, dwarf, dwo_parent_units, flags)?;
1155 if !flags
1156 .match_units
1157 .as_ref()
1158 .map(|r| r.is_match(&buf))
1159 .unwrap_or(true)
1160 {
1161 buf.clear();
1162 }
1163 Ok(())
1164 };
1165 // Don't use more than 16 cores even if available. No point in soaking hundreds
1166 // of cores if you happen to have them.
1167 parallel_output(w, 16, units, process_unit)
1168 }
1169
dump_types<R: Reader, W: Write>( w: &mut W, dwarf: &gimli::Dwarf<R>, dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, flags: &Flags, ) -> Result<()>1170 fn dump_types<R: Reader, W: Write>(
1171 w: &mut W,
1172 dwarf: &gimli::Dwarf<R>,
1173 dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
1174 flags: &Flags,
1175 ) -> Result<()> {
1176 writeln!(w, "\n.debug_types")?;
1177
1178 let mut iter = dwarf.type_units();
1179 while let Some(header) = iter.next()? {
1180 dump_unit(w, header, dwarf, dwo_parent_units, flags)?;
1181 }
1182 Ok(())
1183 }
1184
dump_unit<R: Reader, W: Write>( w: &mut W, header: UnitHeader<R>, dwarf: &gimli::Dwarf<R>, dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, flags: &Flags, ) -> Result<()>1185 fn dump_unit<R: Reader, W: Write>(
1186 w: &mut W,
1187 header: UnitHeader<R>,
1188 dwarf: &gimli::Dwarf<R>,
1189 dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>,
1190 flags: &Flags,
1191 ) -> Result<()> {
1192 write!(w, "\nUNIT<")?;
1193 match header.offset() {
1194 UnitSectionOffset::DebugInfoOffset(o) => {
1195 write!(w, ".debug_info+0x{:08x}", o.0)?;
1196 }
1197 UnitSectionOffset::DebugTypesOffset(o) => {
1198 write!(w, ".debug_types+0x{:08x}", o.0)?;
1199 }
1200 }
1201 writeln!(w, ">: length = 0x{:x}, format = {:?}, version = {}, address_size = {}, abbrev_offset = 0x{:x}",
1202 header.unit_length(),
1203 header.format(),
1204 header.version(),
1205 header.address_size(),
1206 header.debug_abbrev_offset().0,
1207 )?;
1208
1209 match header.type_() {
1210 UnitType::Compilation | UnitType::Partial => (),
1211 UnitType::Type {
1212 type_signature,
1213 type_offset,
1214 }
1215 | UnitType::SplitType {
1216 type_signature,
1217 type_offset,
1218 } => {
1219 write!(w, " signature = ")?;
1220 dump_type_signature(w, type_signature)?;
1221 writeln!(w)?;
1222 writeln!(w, " type_offset = 0x{:x}", type_offset.0,)?;
1223 }
1224 UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => {
1225 write!(w, " dwo_id = ")?;
1226 writeln!(w, "0x{:016x}", dwo_id.0)?;
1227 }
1228 }
1229
1230 let mut unit = match dwarf.unit(header) {
1231 Ok(unit) => unit,
1232 Err(err) => {
1233 writeln_error(w, dwarf, err.into(), "Failed to parse unit root entry")?;
1234 return Ok(());
1235 }
1236 };
1237
1238 if let Some(dwo_parent_units) = dwo_parent_units {
1239 if let Some(dwo_id) = unit.dwo_id {
1240 if let Some(parent_unit) = dwo_parent_units.get(&dwo_id) {
1241 unit.copy_relocated_attributes(parent_unit);
1242 }
1243 }
1244 }
1245
1246 let entries_result = dump_entries(w, unit, dwarf, flags);
1247 if let Err(err) = entries_result {
1248 writeln_error(w, dwarf, err, "Failed to dump entries")?;
1249 }
1250 Ok(())
1251 }
1252
spaces(buf: &mut String, len: usize) -> &str1253 fn spaces(buf: &mut String, len: usize) -> &str {
1254 while buf.len() < len {
1255 buf.push(' ');
1256 }
1257 &buf[..len]
1258 }
1259
1260 // " GOFF=0x{:08x}" adds exactly 16 spaces.
1261 const GOFF_SPACES: usize = 16;
1262
write_offset<R: Reader, W: Write>( w: &mut W, unit: &gimli::Unit<R>, offset: gimli::UnitOffset<R::Offset>, flags: &Flags, ) -> Result<()>1263 fn write_offset<R: Reader, W: Write>(
1264 w: &mut W,
1265 unit: &gimli::Unit<R>,
1266 offset: gimli::UnitOffset<R::Offset>,
1267 flags: &Flags,
1268 ) -> Result<()> {
1269 write!(w, "<0x{:08x}", offset.0)?;
1270 if flags.goff {
1271 let goff = match offset.to_unit_section_offset(unit) {
1272 UnitSectionOffset::DebugInfoOffset(o) => o.0,
1273 UnitSectionOffset::DebugTypesOffset(o) => o.0,
1274 };
1275 write!(w, " GOFF=0x{:08x}", goff)?;
1276 }
1277 write!(w, ">")?;
1278 Ok(())
1279 }
1280
dump_entries<R: Reader, W: Write>( w: &mut W, unit: gimli::Unit<R>, dwarf: &gimli::Dwarf<R>, flags: &Flags, ) -> Result<()>1281 fn dump_entries<R: Reader, W: Write>(
1282 w: &mut W,
1283 unit: gimli::Unit<R>,
1284 dwarf: &gimli::Dwarf<R>,
1285 flags: &Flags,
1286 ) -> Result<()> {
1287 let mut spaces_buf = String::new();
1288
1289 let mut entries = unit.entries_raw(None)?;
1290 while !entries.is_empty() {
1291 let offset = entries.next_offset();
1292 let depth = entries.next_depth();
1293 let abbrev = entries.read_abbreviation()?;
1294
1295 let mut indent = if depth >= 0 {
1296 depth as usize * 2 + 2
1297 } else {
1298 2
1299 };
1300 write!(w, "<{}{}>", if depth < 10 { " " } else { "" }, depth)?;
1301 write_offset(w, &unit, offset, flags)?;
1302 writeln!(
1303 w,
1304 "{}{}",
1305 spaces(&mut spaces_buf, indent),
1306 abbrev.map(|x| x.tag()).unwrap_or(gimli::DW_TAG_null)
1307 )?;
1308
1309 indent += 18;
1310 if flags.goff {
1311 indent += GOFF_SPACES;
1312 }
1313
1314 for spec in abbrev.map(|x| x.attributes()).unwrap_or(&[]) {
1315 let attr = entries.read_attribute(*spec)?;
1316 w.write_all(spaces(&mut spaces_buf, indent).as_bytes())?;
1317 if let Some(n) = attr.name().static_string() {
1318 let right_padding = 27 - std::cmp::min(27, n.len());
1319 write!(w, "{}{} ", n, spaces(&mut spaces_buf, right_padding))?;
1320 } else {
1321 write!(w, "{:27} ", attr.name())?;
1322 }
1323 if flags.raw {
1324 writeln!(w, "{:?}", attr.raw_value())?;
1325 } else {
1326 match dump_attr_value(w, &attr, &unit, dwarf) {
1327 Ok(_) => (),
1328 Err(err) => writeln_error(w, dwarf, err, "Failed to dump attribute value")?,
1329 };
1330 }
1331 }
1332 }
1333 Ok(())
1334 }
1335
dump_attr_value<R: Reader, W: Write>( w: &mut W, attr: &gimli::Attribute<R>, unit: &gimli::Unit<R>, dwarf: &gimli::Dwarf<R>, ) -> Result<()>1336 fn dump_attr_value<R: Reader, W: Write>(
1337 w: &mut W,
1338 attr: &gimli::Attribute<R>,
1339 unit: &gimli::Unit<R>,
1340 dwarf: &gimli::Dwarf<R>,
1341 ) -> Result<()> {
1342 let value = attr.value();
1343 match value {
1344 gimli::AttributeValue::Addr(address) => {
1345 writeln!(w, "0x{:08x}", address)?;
1346 }
1347 gimli::AttributeValue::Block(data) => {
1348 for byte in data.to_slice()?.iter() {
1349 write!(w, "{:02x}", byte)?;
1350 }
1351 writeln!(w)?;
1352 }
1353 gimli::AttributeValue::Data1(_)
1354 | gimli::AttributeValue::Data2(_)
1355 | gimli::AttributeValue::Data4(_)
1356 | gimli::AttributeValue::Data8(_) => {
1357 if let (Some(udata), Some(sdata)) = (attr.udata_value(), attr.sdata_value()) {
1358 if sdata >= 0 {
1359 writeln!(w, "{}", udata)?;
1360 } else {
1361 writeln!(w, "{} ({})", udata, sdata)?;
1362 }
1363 } else {
1364 writeln!(w, "{:?}", value)?;
1365 }
1366 }
1367 gimli::AttributeValue::Sdata(data) => {
1368 match attr.name() {
1369 gimli::DW_AT_data_member_location => {
1370 writeln!(w, "{}", data)?;
1371 }
1372 _ => {
1373 if data >= 0 {
1374 writeln!(w, "0x{:08x}", data)?;
1375 } else {
1376 writeln!(w, "0x{:08x} ({})", data, data)?;
1377 }
1378 }
1379 };
1380 }
1381 gimli::AttributeValue::Udata(data) => {
1382 match attr.name() {
1383 gimli::DW_AT_high_pc => {
1384 writeln!(w, "<offset-from-lowpc>{}", data)?;
1385 }
1386 gimli::DW_AT_data_member_location => {
1387 if let Some(sdata) = attr.sdata_value() {
1388 // This is a DW_FORM_data* value.
1389 // libdwarf-dwarfdump displays this as signed too.
1390 if sdata >= 0 {
1391 writeln!(w, "{}", data)?;
1392 } else {
1393 writeln!(w, "{} ({})", data, sdata)?;
1394 }
1395 } else {
1396 writeln!(w, "{}", data)?;
1397 }
1398 }
1399 gimli::DW_AT_lower_bound | gimli::DW_AT_upper_bound => {
1400 writeln!(w, "{}", data)?;
1401 }
1402 _ => {
1403 writeln!(w, "0x{:08x}", data)?;
1404 }
1405 };
1406 }
1407 gimli::AttributeValue::Exprloc(ref data) => {
1408 if let gimli::AttributeValue::Exprloc(_) = attr.raw_value() {
1409 write!(w, "len 0x{:04x}: ", data.0.len())?;
1410 for byte in data.0.to_slice()?.iter() {
1411 write!(w, "{:02x}", byte)?;
1412 }
1413 write!(w, ": ")?;
1414 }
1415 dump_exprloc(w, unit.encoding(), data)?;
1416 writeln!(w)?;
1417 }
1418 gimli::AttributeValue::Flag(true) => {
1419 writeln!(w, "yes")?;
1420 }
1421 gimli::AttributeValue::Flag(false) => {
1422 writeln!(w, "no")?;
1423 }
1424 gimli::AttributeValue::SecOffset(offset) => {
1425 writeln!(w, "0x{:08x}", offset)?;
1426 }
1427 gimli::AttributeValue::DebugAddrBase(base) => {
1428 writeln!(w, "<.debug_addr+0x{:08x}>", base.0)?;
1429 }
1430 gimli::AttributeValue::DebugAddrIndex(index) => {
1431 write!(w, "(indirect address, index {:#x}): ", index.0)?;
1432 let address = dwarf.address(unit, index)?;
1433 writeln!(w, "0x{:08x}", address)?;
1434 }
1435 gimli::AttributeValue::UnitRef(offset) => {
1436 write!(w, "0x{:08x}", offset.0)?;
1437 match offset.to_unit_section_offset(unit) {
1438 UnitSectionOffset::DebugInfoOffset(goff) => {
1439 write!(w, "<.debug_info+0x{:08x}>", goff.0)?;
1440 }
1441 UnitSectionOffset::DebugTypesOffset(goff) => {
1442 write!(w, "<.debug_types+0x{:08x}>", goff.0)?;
1443 }
1444 }
1445 writeln!(w)?;
1446 }
1447 gimli::AttributeValue::DebugInfoRef(offset) => {
1448 writeln!(w, "<.debug_info+0x{:08x}>", offset.0)?;
1449 }
1450 gimli::AttributeValue::DebugInfoRefSup(offset) => {
1451 writeln!(w, "<.debug_info(sup)+0x{:08x}>", offset.0)?;
1452 }
1453 gimli::AttributeValue::DebugLineRef(offset) => {
1454 writeln!(w, "<.debug_line+0x{:08x}>", offset.0)?;
1455 }
1456 gimli::AttributeValue::LocationListsRef(offset) => {
1457 dump_loc_list(w, offset, unit, dwarf)?;
1458 }
1459 gimli::AttributeValue::DebugLocListsBase(base) => {
1460 writeln!(w, "<.debug_loclists+0x{:08x}>", base.0)?;
1461 }
1462 gimli::AttributeValue::DebugLocListsIndex(index) => {
1463 write!(w, "(indirect location list, index {:#x}): ", index.0)?;
1464 let offset = dwarf.locations_offset(unit, index)?;
1465 dump_loc_list(w, offset, unit, dwarf)?;
1466 }
1467 gimli::AttributeValue::DebugMacinfoRef(offset) => {
1468 writeln!(w, "<.debug_macinfo+0x{:08x}>", offset.0)?;
1469 }
1470 gimli::AttributeValue::DebugMacroRef(offset) => {
1471 writeln!(w, "<.debug_macro+0x{:08x}>", offset.0)?;
1472 }
1473 gimli::AttributeValue::RangeListsRef(offset) => {
1474 let offset = dwarf.ranges_offset_from_raw(unit, offset);
1475 dump_range_list(w, offset, unit, dwarf)?;
1476 }
1477 gimli::AttributeValue::DebugRngListsBase(base) => {
1478 writeln!(w, "<.debug_rnglists+0x{:08x}>", base.0)?;
1479 }
1480 gimli::AttributeValue::DebugRngListsIndex(index) => {
1481 write!(w, "(indirect range list, index {:#x}): ", index.0)?;
1482 let offset = dwarf.ranges_offset(unit, index)?;
1483 dump_range_list(w, offset, unit, dwarf)?;
1484 }
1485 gimli::AttributeValue::DebugTypesRef(signature) => {
1486 dump_type_signature(w, signature)?;
1487 writeln!(w, " <type signature>")?;
1488 }
1489 gimli::AttributeValue::DebugStrRef(offset) => {
1490 if let Ok(s) = dwarf.debug_str.get_str(offset) {
1491 writeln!(w, "{}", s.to_string_lossy()?)?;
1492 } else {
1493 writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?;
1494 }
1495 }
1496 gimli::AttributeValue::DebugStrRefSup(offset) => {
1497 if let Some(s) = dwarf
1498 .sup()
1499 .and_then(|sup| sup.debug_str.get_str(offset).ok())
1500 {
1501 writeln!(w, "{}", s.to_string_lossy()?)?;
1502 } else {
1503 writeln!(w, "<.debug_str(sup)+0x{:08x}>", offset.0)?;
1504 }
1505 }
1506 gimli::AttributeValue::DebugStrOffsetsBase(base) => {
1507 writeln!(w, "<.debug_str_offsets+0x{:08x}>", base.0)?;
1508 }
1509 gimli::AttributeValue::DebugStrOffsetsIndex(index) => {
1510 write!(w, "(indirect string, index {:#x}): ", index.0)?;
1511 let offset = dwarf.debug_str_offsets.get_str_offset(
1512 unit.encoding().format,
1513 unit.str_offsets_base,
1514 index,
1515 )?;
1516 if let Ok(s) = dwarf.debug_str.get_str(offset) {
1517 writeln!(w, "{}", s.to_string_lossy()?)?;
1518 } else {
1519 writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?;
1520 }
1521 }
1522 gimli::AttributeValue::DebugLineStrRef(offset) => {
1523 if let Ok(s) = dwarf.debug_line_str.get_str(offset) {
1524 writeln!(w, "{}", s.to_string_lossy()?)?;
1525 } else {
1526 writeln!(w, "<.debug_line_str=0x{:08x}>", offset.0)?;
1527 }
1528 }
1529 gimli::AttributeValue::String(s) => {
1530 writeln!(w, "{}", s.to_string_lossy()?)?;
1531 }
1532 gimli::AttributeValue::Encoding(value) => {
1533 writeln!(w, "{}", value)?;
1534 }
1535 gimli::AttributeValue::DecimalSign(value) => {
1536 writeln!(w, "{}", value)?;
1537 }
1538 gimli::AttributeValue::Endianity(value) => {
1539 writeln!(w, "{}", value)?;
1540 }
1541 gimli::AttributeValue::Accessibility(value) => {
1542 writeln!(w, "{}", value)?;
1543 }
1544 gimli::AttributeValue::Visibility(value) => {
1545 writeln!(w, "{}", value)?;
1546 }
1547 gimli::AttributeValue::Virtuality(value) => {
1548 writeln!(w, "{}", value)?;
1549 }
1550 gimli::AttributeValue::Language(value) => {
1551 writeln!(w, "{}", value)?;
1552 }
1553 gimli::AttributeValue::AddressClass(value) => {
1554 writeln!(w, "{}", value)?;
1555 }
1556 gimli::AttributeValue::IdentifierCase(value) => {
1557 writeln!(w, "{}", value)?;
1558 }
1559 gimli::AttributeValue::CallingConvention(value) => {
1560 writeln!(w, "{}", value)?;
1561 }
1562 gimli::AttributeValue::Inline(value) => {
1563 writeln!(w, "{}", value)?;
1564 }
1565 gimli::AttributeValue::Ordering(value) => {
1566 writeln!(w, "{}", value)?;
1567 }
1568 gimli::AttributeValue::FileIndex(value) => {
1569 write!(w, "0x{:08x}", value)?;
1570 dump_file_index(w, value, unit, dwarf)?;
1571 writeln!(w)?;
1572 }
1573 gimli::AttributeValue::DwoId(value) => {
1574 writeln!(w, "0x{:016x}", value.0)?;
1575 }
1576 }
1577
1578 Ok(())
1579 }
1580
dump_type_signature<W: Write>(w: &mut W, signature: gimli::DebugTypeSignature) -> Result<()>1581 fn dump_type_signature<W: Write>(w: &mut W, signature: gimli::DebugTypeSignature) -> Result<()> {
1582 write!(w, "0x{:016x}", signature.0)?;
1583 Ok(())
1584 }
1585
dump_file_index<R: Reader, W: Write>( w: &mut W, file: u64, unit: &gimli::Unit<R>, dwarf: &gimli::Dwarf<R>, ) -> Result<()>1586 fn dump_file_index<R: Reader, W: Write>(
1587 w: &mut W,
1588 file: u64,
1589 unit: &gimli::Unit<R>,
1590 dwarf: &gimli::Dwarf<R>,
1591 ) -> Result<()> {
1592 if file == 0 {
1593 return Ok(());
1594 }
1595 let header = match unit.line_program {
1596 Some(ref program) => program.header(),
1597 None => return Ok(()),
1598 };
1599 let file = match header.file(file) {
1600 Some(header) => header,
1601 None => {
1602 writeln!(w, "Unable to get header for file {}", file)?;
1603 return Ok(());
1604 }
1605 };
1606 write!(w, " ")?;
1607 if let Some(directory) = file.directory(header) {
1608 let directory = dwarf.attr_string(unit, directory)?;
1609 let directory = directory.to_string_lossy()?;
1610 if !directory.starts_with('/') {
1611 if let Some(ref comp_dir) = unit.comp_dir {
1612 write!(w, "{}/", comp_dir.to_string_lossy()?,)?;
1613 }
1614 }
1615 write!(w, "{}/", directory)?;
1616 }
1617 write!(
1618 w,
1619 "{}",
1620 dwarf
1621 .attr_string(unit, file.path_name())?
1622 .to_string_lossy()?
1623 )?;
1624 Ok(())
1625 }
1626
dump_exprloc<R: Reader, W: Write>( w: &mut W, encoding: gimli::Encoding, data: &gimli::Expression<R>, ) -> Result<()>1627 fn dump_exprloc<R: Reader, W: Write>(
1628 w: &mut W,
1629 encoding: gimli::Encoding,
1630 data: &gimli::Expression<R>,
1631 ) -> Result<()> {
1632 let mut pc = data.0.clone();
1633 let mut space = false;
1634 while pc.len() != 0 {
1635 let pc_clone = pc.clone();
1636 match gimli::Operation::parse(&mut pc, encoding) {
1637 Ok(op) => {
1638 if space {
1639 write!(w, " ")?;
1640 } else {
1641 space = true;
1642 }
1643 dump_op(w, encoding, pc_clone, op)?;
1644 }
1645 Err(gimli::Error::InvalidExpression(op)) => {
1646 writeln!(w, "WARNING: unsupported operation 0x{:02x}", op.0)?;
1647 return Ok(());
1648 }
1649 Err(gimli::Error::UnsupportedRegister(register)) => {
1650 writeln!(w, "WARNING: unsupported register {}", register)?;
1651 return Ok(());
1652 }
1653 Err(gimli::Error::UnexpectedEof(_)) => {
1654 writeln!(w, "WARNING: truncated or malformed expression")?;
1655 return Ok(());
1656 }
1657 Err(e) => {
1658 writeln!(w, "WARNING: unexpected operation parse error: {}", e)?;
1659 return Ok(());
1660 }
1661 }
1662 }
1663 Ok(())
1664 }
1665
dump_op<R: Reader, W: Write>( w: &mut W, encoding: gimli::Encoding, mut pc: R, op: gimli::Operation<R>, ) -> Result<()>1666 fn dump_op<R: Reader, W: Write>(
1667 w: &mut W,
1668 encoding: gimli::Encoding,
1669 mut pc: R,
1670 op: gimli::Operation<R>,
1671 ) -> Result<()> {
1672 let dwop = gimli::DwOp(pc.read_u8()?);
1673 write!(w, "{}", dwop)?;
1674 match op {
1675 gimli::Operation::Deref {
1676 base_type, size, ..
1677 } => {
1678 if dwop == gimli::DW_OP_deref_size || dwop == gimli::DW_OP_xderef_size {
1679 write!(w, " {}", size)?;
1680 }
1681 if base_type != UnitOffset(0) {
1682 write!(w, " type 0x{:08x}", base_type.0)?;
1683 }
1684 }
1685 gimli::Operation::Pick { index } => {
1686 if dwop == gimli::DW_OP_pick {
1687 write!(w, " {}", index)?;
1688 }
1689 }
1690 gimli::Operation::PlusConstant { value } => {
1691 write!(w, " {}", value as i64)?;
1692 }
1693 gimli::Operation::Bra { target } => {
1694 write!(w, " {}", target)?;
1695 }
1696 gimli::Operation::Skip { target } => {
1697 write!(w, " {}", target)?;
1698 }
1699 gimli::Operation::SignedConstant { value } => match dwop {
1700 gimli::DW_OP_const1s
1701 | gimli::DW_OP_const2s
1702 | gimli::DW_OP_const4s
1703 | gimli::DW_OP_const8s
1704 | gimli::DW_OP_consts => {
1705 write!(w, " {}", value)?;
1706 }
1707 _ => {}
1708 },
1709 gimli::Operation::UnsignedConstant { value } => match dwop {
1710 gimli::DW_OP_const1u
1711 | gimli::DW_OP_const2u
1712 | gimli::DW_OP_const4u
1713 | gimli::DW_OP_const8u
1714 | gimli::DW_OP_constu => {
1715 write!(w, " {}", value)?;
1716 }
1717 _ => {
1718 // These have the value encoded in the operation, eg DW_OP_lit0.
1719 }
1720 },
1721 gimli::Operation::Register { register } => {
1722 if dwop == gimli::DW_OP_regx {
1723 write!(w, " {}", register.0)?;
1724 }
1725 }
1726 gimli::Operation::RegisterOffset {
1727 register,
1728 offset,
1729 base_type,
1730 } => {
1731 if dwop >= gimli::DW_OP_breg0 && dwop <= gimli::DW_OP_breg31 {
1732 write!(w, "{:+}", offset)?;
1733 } else {
1734 write!(w, " {}", register.0)?;
1735 if offset != 0 {
1736 write!(w, "{:+}", offset)?;
1737 }
1738 if base_type != UnitOffset(0) {
1739 write!(w, " type 0x{:08x}", base_type.0)?;
1740 }
1741 }
1742 }
1743 gimli::Operation::FrameOffset { offset } => {
1744 write!(w, " {}", offset)?;
1745 }
1746 gimli::Operation::Call { offset } => match offset {
1747 gimli::DieReference::UnitRef(gimli::UnitOffset(offset)) => {
1748 write!(w, " 0x{:08x}", offset)?;
1749 }
1750 gimli::DieReference::DebugInfoRef(gimli::DebugInfoOffset(offset)) => {
1751 write!(w, " 0x{:08x}", offset)?;
1752 }
1753 },
1754 gimli::Operation::Piece {
1755 size_in_bits,
1756 bit_offset: None,
1757 } => {
1758 write!(w, " {}", size_in_bits / 8)?;
1759 }
1760 gimli::Operation::Piece {
1761 size_in_bits,
1762 bit_offset: Some(bit_offset),
1763 } => {
1764 write!(w, " 0x{:08x} offset 0x{:08x}", size_in_bits, bit_offset)?;
1765 }
1766 gimli::Operation::ImplicitValue { data } => {
1767 let data = data.to_slice()?;
1768 write!(w, " 0x{:08x} contents 0x", data.len())?;
1769 for byte in data.iter() {
1770 write!(w, "{:02x}", byte)?;
1771 }
1772 }
1773 gimli::Operation::ImplicitPointer { value, byte_offset } => {
1774 write!(w, " 0x{:08x} {}", value.0, byte_offset)?;
1775 }
1776 gimli::Operation::EntryValue { expression } => {
1777 write!(w, "(")?;
1778 dump_exprloc(w, encoding, &gimli::Expression(expression))?;
1779 write!(w, ")")?;
1780 }
1781 gimli::Operation::ParameterRef { offset } => {
1782 write!(w, " 0x{:08x}", offset.0)?;
1783 }
1784 gimli::Operation::Address { address } => {
1785 write!(w, " 0x{:08x}", address)?;
1786 }
1787 gimli::Operation::AddressIndex { index } => {
1788 write!(w, " 0x{:08x}", index.0)?;
1789 }
1790 gimli::Operation::ConstantIndex { index } => {
1791 write!(w, " 0x{:08x}", index.0)?;
1792 }
1793 gimli::Operation::TypedLiteral { base_type, value } => {
1794 write!(w, " type 0x{:08x} contents 0x", base_type.0)?;
1795 for byte in value.to_slice()?.iter() {
1796 write!(w, "{:02x}", byte)?;
1797 }
1798 }
1799 gimli::Operation::Convert { base_type } => {
1800 write!(w, " type 0x{:08x}", base_type.0)?;
1801 }
1802 gimli::Operation::Reinterpret { base_type } => {
1803 write!(w, " type 0x{:08x}", base_type.0)?;
1804 }
1805 gimli::Operation::WasmLocal { index }
1806 | gimli::Operation::WasmGlobal { index }
1807 | gimli::Operation::WasmStack { index } => {
1808 let wasmop = pc.read_u8()?;
1809 write!(w, " 0x{:x} 0x{:x}", wasmop, index)?;
1810 }
1811 gimli::Operation::Drop
1812 | gimli::Operation::Swap
1813 | gimli::Operation::Rot
1814 | gimli::Operation::Abs
1815 | gimli::Operation::And
1816 | gimli::Operation::Div
1817 | gimli::Operation::Minus
1818 | gimli::Operation::Mod
1819 | gimli::Operation::Mul
1820 | gimli::Operation::Neg
1821 | gimli::Operation::Not
1822 | gimli::Operation::Or
1823 | gimli::Operation::Plus
1824 | gimli::Operation::Shl
1825 | gimli::Operation::Shr
1826 | gimli::Operation::Shra
1827 | gimli::Operation::Xor
1828 | gimli::Operation::Eq
1829 | gimli::Operation::Ge
1830 | gimli::Operation::Gt
1831 | gimli::Operation::Le
1832 | gimli::Operation::Lt
1833 | gimli::Operation::Ne
1834 | gimli::Operation::Nop
1835 | gimli::Operation::PushObjectAddress
1836 | gimli::Operation::TLS
1837 | gimli::Operation::CallFrameCFA
1838 | gimli::Operation::StackValue => {}
1839 };
1840 Ok(())
1841 }
1842
dump_loc_list<R: Reader, W: Write>( w: &mut W, offset: gimli::LocationListsOffset<R::Offset>, unit: &gimli::Unit<R>, dwarf: &gimli::Dwarf<R>, ) -> Result<()>1843 fn dump_loc_list<R: Reader, W: Write>(
1844 w: &mut W,
1845 offset: gimli::LocationListsOffset<R::Offset>,
1846 unit: &gimli::Unit<R>,
1847 dwarf: &gimli::Dwarf<R>,
1848 ) -> Result<()> {
1849 let raw_locations = dwarf.raw_locations(unit, offset)?;
1850 let raw_locations: Vec<_> = raw_locations.collect()?;
1851 let mut locations = dwarf.locations(unit, offset)?;
1852 writeln!(
1853 w,
1854 "<loclist at {}+0x{:08x} with {} entries>",
1855 if unit.encoding().version < 5 {
1856 ".debug_loc"
1857 } else {
1858 ".debug_loclists"
1859 },
1860 offset.0,
1861 raw_locations.len()
1862 )?;
1863 for (i, raw) in raw_locations.iter().enumerate() {
1864 write!(w, "\t\t\t[{:2}]", i)?;
1865 match *raw {
1866 gimli::RawLocListEntry::BaseAddress { addr } => {
1867 writeln!(w, "<new base address 0x{:08x}>", addr)?;
1868 }
1869 gimli::RawLocListEntry::BaseAddressx { addr } => {
1870 let addr_val = dwarf.address(unit, addr)?;
1871 writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?;
1872 }
1873 gimli::RawLocListEntry::StartxEndx {
1874 begin,
1875 end,
1876 ref data,
1877 } => {
1878 let begin_val = dwarf.address(unit, begin)?;
1879 let end_val = dwarf.address(unit, end)?;
1880 let location = locations.next()?.unwrap();
1881 write!(
1882 w,
1883 "<startx-endx \
1884 low-off: [{}]0x{:08x} addr 0x{:08x} \
1885 high-off: [{}]0x{:08x} addr 0x{:08x}>",
1886 begin.0, begin_val, location.range.begin, end.0, end_val, location.range.end
1887 )?;
1888 dump_exprloc(w, unit.encoding(), data)?;
1889 writeln!(w)?;
1890 }
1891 gimli::RawLocListEntry::StartxLength {
1892 begin,
1893 length,
1894 ref data,
1895 } => {
1896 let begin_val = dwarf.address(unit, begin)?;
1897 let location = locations.next()?.unwrap();
1898 write!(
1899 w,
1900 "<start-length \
1901 low-off: [{}]0x{:08x} addr 0x{:08x} \
1902 high-off: 0x{:08x} addr 0x{:08x}>",
1903 begin.0, begin_val, location.range.begin, length, location.range.end
1904 )?;
1905 dump_exprloc(w, unit.encoding(), data)?;
1906 writeln!(w)?;
1907 }
1908 gimli::RawLocListEntry::AddressOrOffsetPair {
1909 begin,
1910 end,
1911 ref data,
1912 }
1913 | gimli::RawLocListEntry::OffsetPair {
1914 begin,
1915 end,
1916 ref data,
1917 } => {
1918 let location = locations.next()?.unwrap();
1919 write!(
1920 w,
1921 "<offset pair \
1922 low-off: 0x{:08x} addr 0x{:08x} \
1923 high-off: 0x{:08x} addr 0x{:08x}>",
1924 begin, location.range.begin, end, location.range.end
1925 )?;
1926 dump_exprloc(w, unit.encoding(), data)?;
1927 writeln!(w)?;
1928 }
1929 gimli::RawLocListEntry::DefaultLocation { ref data } => {
1930 write!(w, "<default location>")?;
1931 dump_exprloc(w, unit.encoding(), data)?;
1932 writeln!(w)?;
1933 }
1934 gimli::RawLocListEntry::StartEnd {
1935 begin,
1936 end,
1937 ref data,
1938 } => {
1939 let location = locations.next()?.unwrap();
1940 write!(
1941 w,
1942 "<start-end \
1943 low-off: 0x{:08x} addr 0x{:08x} \
1944 high-off: 0x{:08x} addr 0x{:08x}>",
1945 begin, location.range.begin, end, location.range.end
1946 )?;
1947 dump_exprloc(w, unit.encoding(), data)?;
1948 writeln!(w)?;
1949 }
1950 gimli::RawLocListEntry::StartLength {
1951 begin,
1952 length,
1953 ref data,
1954 } => {
1955 let location = locations.next()?.unwrap();
1956 write!(
1957 w,
1958 "<start-length \
1959 low-off: 0x{:08x} addr 0x{:08x} \
1960 high-off: 0x{:08x} addr 0x{:08x}>",
1961 begin, location.range.begin, length, location.range.end
1962 )?;
1963 dump_exprloc(w, unit.encoding(), data)?;
1964 writeln!(w)?;
1965 }
1966 };
1967 }
1968 Ok(())
1969 }
1970
dump_range_list<R: Reader, W: Write>( w: &mut W, offset: gimli::RangeListsOffset<R::Offset>, unit: &gimli::Unit<R>, dwarf: &gimli::Dwarf<R>, ) -> Result<()>1971 fn dump_range_list<R: Reader, W: Write>(
1972 w: &mut W,
1973 offset: gimli::RangeListsOffset<R::Offset>,
1974 unit: &gimli::Unit<R>,
1975 dwarf: &gimli::Dwarf<R>,
1976 ) -> Result<()> {
1977 let raw_ranges = dwarf.raw_ranges(unit, offset)?;
1978 let raw_ranges: Vec<_> = raw_ranges.collect()?;
1979 let mut ranges = dwarf.ranges(unit, offset)?;
1980 writeln!(
1981 w,
1982 "<rnglist at {}+0x{:08x} with {} entries>",
1983 if unit.encoding().version < 5 {
1984 ".debug_ranges"
1985 } else {
1986 ".debug_rnglists"
1987 },
1988 offset.0,
1989 raw_ranges.len()
1990 )?;
1991 for (i, raw) in raw_ranges.iter().enumerate() {
1992 write!(w, "\t\t\t[{:2}] ", i)?;
1993 match *raw {
1994 gimli::RawRngListEntry::AddressOrOffsetPair { begin, end } => {
1995 let range = ranges.next()?.unwrap();
1996 writeln!(
1997 w,
1998 "<address pair \
1999 low-off: 0x{:08x} addr 0x{:08x} \
2000 high-off: 0x{:08x} addr 0x{:08x}>",
2001 begin, range.begin, end, range.end
2002 )?;
2003 }
2004 gimli::RawRngListEntry::BaseAddress { addr } => {
2005 writeln!(w, "<new base address 0x{:08x}>", addr)?;
2006 }
2007 gimli::RawRngListEntry::BaseAddressx { addr } => {
2008 let addr_val = dwarf.address(unit, addr)?;
2009 writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?;
2010 }
2011 gimli::RawRngListEntry::StartxEndx { begin, end } => {
2012 let begin_val = dwarf.address(unit, begin)?;
2013 let end_val = dwarf.address(unit, end)?;
2014 let range = if begin_val == end_val {
2015 gimli::Range {
2016 begin: begin_val,
2017 end: end_val,
2018 }
2019 } else {
2020 ranges.next()?.unwrap()
2021 };
2022 writeln!(
2023 w,
2024 "<startx-endx \
2025 low-off: [{}]0x{:08x} addr 0x{:08x} \
2026 high-off: [{}]0x{:08x} addr 0x{:08x}>",
2027 begin.0, begin_val, range.begin, end.0, end_val, range.end
2028 )?;
2029 }
2030 gimli::RawRngListEntry::StartxLength { begin, length } => {
2031 let begin_val = dwarf.address(unit, begin)?;
2032 let range = ranges.next()?.unwrap();
2033 writeln!(
2034 w,
2035 "<startx-length \
2036 low-off: [{}]0x{:08x} addr 0x{:08x} \
2037 high-off: 0x{:08x} addr 0x{:08x}>",
2038 begin.0, begin_val, range.begin, length, range.end
2039 )?;
2040 }
2041 gimli::RawRngListEntry::OffsetPair { begin, end } => {
2042 let range = ranges.next()?.unwrap();
2043 writeln!(
2044 w,
2045 "<offset pair \
2046 low-off: 0x{:08x} addr 0x{:08x} \
2047 high-off: 0x{:08x} addr 0x{:08x}>",
2048 begin, range.begin, end, range.end
2049 )?;
2050 }
2051 gimli::RawRngListEntry::StartEnd { begin, end } => {
2052 let range = if begin == end {
2053 gimli::Range { begin, end }
2054 } else {
2055 ranges.next()?.unwrap()
2056 };
2057 writeln!(
2058 w,
2059 "<start-end \
2060 low-off: 0x{:08x} addr 0x{:08x} \
2061 high-off: 0x{:08x} addr 0x{:08x}>",
2062 begin, range.begin, end, range.end
2063 )?;
2064 }
2065 gimli::RawRngListEntry::StartLength { begin, length } => {
2066 let range = ranges.next()?.unwrap();
2067 writeln!(
2068 w,
2069 "<start-length \
2070 low-off: 0x{:08x} addr 0x{:08x} \
2071 high-off: 0x{:08x} addr 0x{:08x}>",
2072 begin, range.begin, length, range.end
2073 )?;
2074 }
2075 };
2076 }
2077 Ok(())
2078 }
2079
dump_line<R: Reader, W: Write>(w: &mut W, dwarf: &gimli::Dwarf<R>) -> Result<()>2080 fn dump_line<R: Reader, W: Write>(w: &mut W, dwarf: &gimli::Dwarf<R>) -> Result<()> {
2081 let mut iter = dwarf.units();
2082 while let Some(header) = iter.next()? {
2083 writeln!(
2084 w,
2085 "\n.debug_line: line number info for unit at .debug_info offset 0x{:08x}",
2086 header.offset().as_debug_info_offset().unwrap().0
2087 )?;
2088 let unit = match dwarf.unit(header) {
2089 Ok(unit) => unit,
2090 Err(err) => {
2091 writeln_error(
2092 w,
2093 dwarf,
2094 err.into(),
2095 "Failed to parse unit root entry for dump_line",
2096 )?;
2097 continue;
2098 }
2099 };
2100 match dump_line_program(w, &unit, dwarf) {
2101 Ok(_) => (),
2102 Err(Error::IoError) => return Err(Error::IoError),
2103 Err(err) => writeln_error(w, dwarf, err, "Failed to dump line program")?,
2104 }
2105 }
2106 Ok(())
2107 }
2108
dump_line_program<R: Reader, W: Write>( w: &mut W, unit: &gimli::Unit<R>, dwarf: &gimli::Dwarf<R>, ) -> Result<()>2109 fn dump_line_program<R: Reader, W: Write>(
2110 w: &mut W,
2111 unit: &gimli::Unit<R>,
2112 dwarf: &gimli::Dwarf<R>,
2113 ) -> Result<()> {
2114 if let Some(program) = unit.line_program.clone() {
2115 {
2116 let header = program.header();
2117 writeln!(w)?;
2118 writeln!(
2119 w,
2120 "Offset: 0x{:x}",
2121 header.offset().0
2122 )?;
2123 writeln!(
2124 w,
2125 "Length: {}",
2126 header.unit_length()
2127 )?;
2128 writeln!(
2129 w,
2130 "DWARF version: {}",
2131 header.version()
2132 )?;
2133 writeln!(
2134 w,
2135 "Address size: {}",
2136 header.address_size()
2137 )?;
2138 writeln!(
2139 w,
2140 "Prologue length: {}",
2141 header.header_length()
2142 )?;
2143 writeln!(
2144 w,
2145 "Minimum instruction length: {}",
2146 header.minimum_instruction_length()
2147 )?;
2148 writeln!(
2149 w,
2150 "Maximum operations per instruction: {}",
2151 header.maximum_operations_per_instruction()
2152 )?;
2153 writeln!(
2154 w,
2155 "Default is_stmt: {}",
2156 header.default_is_stmt()
2157 )?;
2158 writeln!(
2159 w,
2160 "Line base: {}",
2161 header.line_base()
2162 )?;
2163 writeln!(
2164 w,
2165 "Line range: {}",
2166 header.line_range()
2167 )?;
2168 writeln!(
2169 w,
2170 "Opcode base: {}",
2171 header.opcode_base()
2172 )?;
2173
2174 writeln!(w)?;
2175 writeln!(w, "Opcodes:")?;
2176 for (i, length) in header
2177 .standard_opcode_lengths()
2178 .to_slice()?
2179 .iter()
2180 .enumerate()
2181 {
2182 writeln!(w, " Opcode {} has {} args", i + 1, length)?;
2183 }
2184
2185 let base = if header.version() >= 5 { 0 } else { 1 };
2186 writeln!(w)?;
2187 writeln!(w, "The Directory Table:")?;
2188 for (i, dir) in header.include_directories().iter().enumerate() {
2189 writeln!(
2190 w,
2191 " {} {}",
2192 base + i,
2193 dwarf.attr_string(unit, dir.clone())?.to_string_lossy()?
2194 )?;
2195 }
2196
2197 writeln!(w)?;
2198 writeln!(w, "The File Name Table")?;
2199 write!(w, " Entry\tDir\tTime\tSize")?;
2200 if header.file_has_md5() {
2201 write!(w, "\tMD5\t\t\t\t")?;
2202 }
2203 writeln!(w, "\tName")?;
2204 for (i, file) in header.file_names().iter().enumerate() {
2205 write!(
2206 w,
2207 " {}\t{}\t{}\t{}",
2208 base + i,
2209 file.directory_index(),
2210 file.timestamp(),
2211 file.size(),
2212 )?;
2213 if header.file_has_md5() {
2214 let md5 = file.md5();
2215 write!(w, "\t")?;
2216 for i in 0..16 {
2217 write!(w, "{:02X}", md5[i])?;
2218 }
2219 }
2220 writeln!(
2221 w,
2222 "\t{}",
2223 dwarf
2224 .attr_string(unit, file.path_name())?
2225 .to_string_lossy()?
2226 )?;
2227 }
2228
2229 writeln!(w)?;
2230 writeln!(w, "Line Number Instructions:")?;
2231 let mut instructions = header.instructions();
2232 while let Some(instruction) = instructions.next_instruction(header)? {
2233 writeln!(w, " {}", instruction)?;
2234 }
2235
2236 writeln!(w)?;
2237 writeln!(w, "Line Number Rows:")?;
2238 writeln!(w, "<pc> [lno,col]")?;
2239 }
2240 let mut rows = program.rows();
2241 let mut file_index = 0;
2242 while let Some((header, row)) = rows.next_row()? {
2243 let line = match row.line() {
2244 Some(line) => line.get(),
2245 None => 0,
2246 };
2247 let column = match row.column() {
2248 gimli::ColumnType::Column(column) => column.get(),
2249 gimli::ColumnType::LeftEdge => 0,
2250 };
2251 write!(w, "0x{:08x} [{:4},{:2}]", row.address(), line, column)?;
2252 if row.is_stmt() {
2253 write!(w, " NS")?;
2254 }
2255 if row.basic_block() {
2256 write!(w, " BB")?;
2257 }
2258 if row.end_sequence() {
2259 write!(w, " ET")?;
2260 }
2261 if row.prologue_end() {
2262 write!(w, " PE")?;
2263 }
2264 if row.epilogue_begin() {
2265 write!(w, " EB")?;
2266 }
2267 if row.isa() != 0 {
2268 write!(w, " IS={}", row.isa())?;
2269 }
2270 if row.discriminator() != 0 {
2271 write!(w, " DI={}", row.discriminator())?;
2272 }
2273 if file_index != row.file_index() {
2274 file_index = row.file_index();
2275 if let Some(file) = row.file(header) {
2276 if let Some(directory) = file.directory(header) {
2277 write!(
2278 w,
2279 " uri: \"{}/{}\"",
2280 dwarf.attr_string(unit, directory)?.to_string_lossy()?,
2281 dwarf
2282 .attr_string(unit, file.path_name())?
2283 .to_string_lossy()?
2284 )?;
2285 } else {
2286 write!(
2287 w,
2288 " uri: \"{}\"",
2289 dwarf
2290 .attr_string(unit, file.path_name())?
2291 .to_string_lossy()?
2292 )?;
2293 }
2294 }
2295 }
2296 writeln!(w)?;
2297 }
2298 }
2299 Ok(())
2300 }
2301
dump_pubnames<R: Reader, W: Write>( w: &mut W, debug_pubnames: &gimli::DebugPubNames<R>, debug_info: &gimli::DebugInfo<R>, ) -> Result<()>2302 fn dump_pubnames<R: Reader, W: Write>(
2303 w: &mut W,
2304 debug_pubnames: &gimli::DebugPubNames<R>,
2305 debug_info: &gimli::DebugInfo<R>,
2306 ) -> Result<()> {
2307 writeln!(w, "\n.debug_pubnames")?;
2308
2309 let mut cu_offset;
2310 let mut cu_die_offset = gimli::DebugInfoOffset(0);
2311 let mut prev_cu_offset = None;
2312 let mut pubnames = debug_pubnames.items();
2313 while let Some(pubname) = pubnames.next()? {
2314 cu_offset = pubname.unit_header_offset();
2315 if Some(cu_offset) != prev_cu_offset {
2316 let cu = debug_info.header_from_offset(cu_offset)?;
2317 cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size());
2318 prev_cu_offset = Some(cu_offset);
2319 }
2320 let die_in_cu = pubname.die_offset();
2321 let die_in_sect = cu_offset.0 + die_in_cu.0;
2322 writeln!(w,
2323 "global die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'",
2324 die_in_sect,
2325 cu_die_offset.0,
2326 die_in_cu.0,
2327 cu_offset.0,
2328 pubname.name().to_string_lossy()?
2329 )?;
2330 }
2331 Ok(())
2332 }
2333
dump_pubtypes<R: Reader, W: Write>( w: &mut W, debug_pubtypes: &gimli::DebugPubTypes<R>, debug_info: &gimli::DebugInfo<R>, ) -> Result<()>2334 fn dump_pubtypes<R: Reader, W: Write>(
2335 w: &mut W,
2336 debug_pubtypes: &gimli::DebugPubTypes<R>,
2337 debug_info: &gimli::DebugInfo<R>,
2338 ) -> Result<()> {
2339 writeln!(w, "\n.debug_pubtypes")?;
2340
2341 let mut cu_offset;
2342 let mut cu_die_offset = gimli::DebugInfoOffset(0);
2343 let mut prev_cu_offset = None;
2344 let mut pubtypes = debug_pubtypes.items();
2345 while let Some(pubtype) = pubtypes.next()? {
2346 cu_offset = pubtype.unit_header_offset();
2347 if Some(cu_offset) != prev_cu_offset {
2348 let cu = debug_info.header_from_offset(cu_offset)?;
2349 cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size());
2350 prev_cu_offset = Some(cu_offset);
2351 }
2352 let die_in_cu = pubtype.die_offset();
2353 let die_in_sect = cu_offset.0 + die_in_cu.0;
2354 writeln!(w,
2355 "pubtype die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'",
2356 die_in_sect,
2357 cu_die_offset.0,
2358 die_in_cu.0,
2359 cu_offset.0,
2360 pubtype.name().to_string_lossy()?
2361 )?;
2362 }
2363 Ok(())
2364 }
2365
dump_aranges<R: Reader, W: Write>( w: &mut W, debug_aranges: &gimli::DebugAranges<R>, ) -> Result<()>2366 fn dump_aranges<R: Reader, W: Write>(
2367 w: &mut W,
2368 debug_aranges: &gimli::DebugAranges<R>,
2369 ) -> Result<()> {
2370 writeln!(w, "\n.debug_aranges")?;
2371
2372 let mut headers = debug_aranges.headers();
2373 while let Some(header) = headers.next()? {
2374 writeln!(
2375 w,
2376 "Address Range Header: length = 0x{:08x}, version = 0x{:04x}, cu_offset = 0x{:08x}, addr_size = 0x{:02x}, seg_size = 0x{:02x}",
2377 header.length(),
2378 header.encoding().version,
2379 header.debug_info_offset().0,
2380 header.encoding().address_size,
2381 header.segment_size(),
2382 )?;
2383 let mut aranges = header.entries();
2384 while let Some(arange) = aranges.next()? {
2385 let range = arange.range();
2386 if let Some(segment) = arange.segment() {
2387 writeln!(
2388 w,
2389 "[0x{:016x}, 0x{:016x}) segment 0x{:x}",
2390 range.begin, range.end, segment
2391 )?;
2392 } else {
2393 writeln!(w, "[0x{:016x}, 0x{:016x})", range.begin, range.end)?;
2394 }
2395 }
2396 }
2397 Ok(())
2398 }
2399