]>
Commit | Line | Data |
---|---|---|
9ffffee4 FG |
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] | |
32 | fn fmt(&self, f: &mut fmt::Formatter) -> ::std::result::Result<(), fmt::Error> { | |
33 | Debug::fmt(self, f) | |
34 | } | |
35 | } | |
36 | ||
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 { | |
57 | fn from(err: gimli::Error) -> Self { | |
58 | Error::GimliError(err) | |
59 | } | |
60 | } | |
61 | ||
62 | impl From<io::Error> for Error { | |
63 | fn from(_: io::Error) -> Self { | |
64 | Error::IoError | |
65 | } | |
66 | } | |
67 | ||
68 | impl From<object::read::Error> for Error { | |
69 | fn from(err: object::read::Error) -> Self { | |
70 | Error::ObjectError(err) | |
71 | } | |
72 | } | |
73 | ||
74 | pub type Result<T> = result::Result<T, Error>; | |
75 | ||
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 | ||
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> { | |
229 | 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 | ||
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 | ||
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 | ||
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 | ||
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] | |
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] | |
286 | fn endian(&self) -> Self::Endian { | |
287 | self.reader.endian() | |
288 | } | |
289 | ||
290 | #[inline] | |
291 | fn len(&self) -> Self::Offset { | |
292 | self.reader.len() | |
293 | } | |
294 | ||
295 | #[inline] | |
296 | fn empty(&mut self) { | |
297 | self.reader.empty() | |
298 | } | |
299 | ||
300 | #[inline] | |
301 | fn truncate(&mut self, len: Self::Offset) -> gimli::Result<()> { | |
302 | self.reader.truncate(len) | |
303 | } | |
304 | ||
305 | #[inline] | |
306 | fn offset_from(&self, base: &Self) -> Self::Offset { | |
307 | self.reader.offset_from(&base.reader) | |
308 | } | |
309 | ||
310 | #[inline] | |
311 | fn offset_id(&self) -> gimli::ReaderOffsetId { | |
312 | self.reader.offset_id() | |
313 | } | |
314 | ||
315 | #[inline] | |
316 | fn lookup_offset_id(&self, id: gimli::ReaderOffsetId) -> Option<Self::Offset> { | |
317 | self.reader.lookup_offset_id(id) | |
318 | } | |
319 | ||
320 | #[inline] | |
321 | fn find(&self, byte: u8) -> gimli::Result<Self::Offset> { | |
322 | self.reader.find(byte) | |
323 | } | |
324 | ||
325 | #[inline] | |
326 | fn skip(&mut self, len: Self::Offset) -> gimli::Result<()> { | |
327 | self.reader.skip(len) | |
328 | } | |
329 | ||
330 | #[inline] | |
331 | fn to_slice(&self) -> gimli::Result<Cow<[u8]>> { | |
332 | self.reader.to_slice() | |
333 | } | |
334 | ||
335 | #[inline] | |
336 | fn to_string(&self) -> gimli::Result<Cow<str>> { | |
337 | self.reader.to_string() | |
338 | } | |
339 | ||
340 | #[inline] | |
341 | fn to_string_lossy(&self) -> gimli::Result<Cow<str>> { | |
342 | self.reader.to_string_lossy() | |
343 | } | |
344 | ||
345 | #[inline] | |
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 | ||
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 | ||
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 { memmap2::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 { memmap2::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 | ||
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 | ||
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 | ||
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 | ||
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!( | |
790 | w, | |
791 | " ra_register: {}", | |
792 | register_name(cie.return_address_register()) | |
793 | )?; | |
794 | if let Some(encoding) = cie.lsda_encoding() { | |
795 | writeln!( | |
796 | w, | |
797 | " lsda_encoding: {}/{}", | |
798 | encoding.application(), | |
799 | encoding.format() | |
800 | )?; | |
801 | } | |
802 | if let Some((encoding, personality)) = cie.personality_with_encoding() { | |
803 | write!( | |
804 | w, | |
805 | " personality: {}/{} ", | |
806 | encoding.application(), | |
807 | encoding.format() | |
808 | )?; | |
809 | dump_pointer(w, personality)?; | |
810 | writeln!(w)?; | |
811 | } | |
812 | if let Some(encoding) = cie.fde_address_encoding() { | |
813 | writeln!( | |
814 | w, | |
815 | " fde_encoding: {}/{}", | |
816 | encoding.application(), | |
817 | encoding.format() | |
818 | )?; | |
819 | } | |
820 | let instructions = cie.instructions(&eh_frame, &bases); | |
821 | dump_cfi_instructions(w, instructions, true, register_name)?; | |
822 | writeln!(w)?; | |
823 | } | |
824 | Some(gimli::CieOrFde::Fde(partial)) => { | |
825 | let mut offset = None; | |
826 | let fde = partial.parse(|_, bases, o| { | |
827 | offset = Some(o); | |
828 | cies.entry(o) | |
829 | .or_insert_with(|| eh_frame.cie_from_offset(bases, o)) | |
830 | .clone() | |
831 | })?; | |
832 | ||
833 | writeln!(w)?; | |
834 | writeln!(w, "{:#010x}: FDE", fde.offset())?; | |
835 | writeln!(w, " length: {:#010x}", fde.entry_len())?; | |
836 | writeln!(w, " CIE_pointer: {:#010x}", offset.unwrap().0)?; | |
837 | // TODO: symbolicate the start address like the canonical dwarfdump does. | |
838 | writeln!(w, " start_addr: {:#018x}", fde.initial_address())?; | |
839 | writeln!( | |
840 | w, | |
841 | " range_size: {:#018x} (end_addr = {:#018x})", | |
842 | fde.len(), | |
843 | fde.initial_address() + fde.len() | |
844 | )?; | |
845 | if let Some(lsda) = fde.lsda() { | |
846 | write!(w, " lsda: ")?; | |
847 | dump_pointer(w, lsda)?; | |
848 | writeln!(w)?; | |
849 | } | |
850 | let instructions = fde.instructions(&eh_frame, &bases); | |
851 | dump_cfi_instructions(w, instructions, false, register_name)?; | |
852 | writeln!(w)?; | |
853 | } | |
854 | } | |
855 | } | |
856 | } | |
857 | ||
858 | fn dump_pointer<W: Write>(w: &mut W, p: gimli::Pointer) -> Result<()> { | |
859 | match p { | |
860 | gimli::Pointer::Direct(p) => { | |
861 | write!(w, "{:#018x}", p)?; | |
862 | } | |
863 | gimli::Pointer::Indirect(p) => { | |
864 | write!(w, "({:#018x})", p)?; | |
865 | } | |
866 | } | |
867 | Ok(()) | |
868 | } | |
869 | ||
870 | #[allow(clippy::unneeded_field_pattern)] | |
871 | fn dump_cfi_instructions<R: Reader, W: Write>( | |
872 | w: &mut W, | |
873 | mut insns: gimli::CallFrameInstructionIter<R>, | |
874 | is_initial: bool, | |
875 | register_name: &dyn Fn(gimli::Register) -> Cow<'static, str>, | |
876 | ) -> Result<()> { | |
877 | use gimli::CallFrameInstruction::*; | |
878 | ||
879 | // TODO: we need to actually evaluate these instructions as we iterate them | |
880 | // so we can print the initialized state for CIEs, and each unwind row's | |
881 | // registers for FDEs. | |
882 | // | |
883 | // TODO: We should print DWARF expressions for the CFI instructions that | |
884 | // embed DWARF expressions within themselves. | |
885 | ||
886 | if !is_initial { | |
887 | writeln!(w, " Instructions:")?; | |
888 | } | |
889 | ||
890 | loop { | |
891 | match insns.next() { | |
892 | Err(e) => { | |
893 | writeln!(w, "Failed to decode CFI instruction: {}", e)?; | |
894 | return Ok(()); | |
895 | } | |
896 | Ok(None) => { | |
897 | if is_initial { | |
898 | writeln!(w, " Instructions: Init State:")?; | |
899 | } | |
900 | return Ok(()); | |
901 | } | |
902 | Ok(Some(op)) => match op { | |
903 | SetLoc { address } => { | |
904 | writeln!(w, " DW_CFA_set_loc ({:#x})", address)?; | |
905 | } | |
906 | AdvanceLoc { delta } => { | |
907 | writeln!(w, " DW_CFA_advance_loc ({})", delta)?; | |
908 | } | |
909 | DefCfa { register, offset } => { | |
910 | writeln!( | |
911 | w, | |
912 | " DW_CFA_def_cfa ({}, {})", | |
913 | register_name(register), | |
914 | offset | |
915 | )?; | |
916 | } | |
917 | DefCfaSf { | |
918 | register, | |
919 | factored_offset, | |
920 | } => { | |
921 | writeln!( | |
922 | w, | |
923 | " DW_CFA_def_cfa_sf ({}, {})", | |
924 | register_name(register), | |
925 | factored_offset | |
926 | )?; | |
927 | } | |
928 | DefCfaRegister { register } => { | |
929 | writeln!( | |
930 | w, | |
931 | " DW_CFA_def_cfa_register ({})", | |
932 | register_name(register) | |
933 | )?; | |
934 | } | |
935 | DefCfaOffset { offset } => { | |
936 | writeln!(w, " DW_CFA_def_cfa_offset ({})", offset)?; | |
937 | } | |
938 | DefCfaOffsetSf { factored_offset } => { | |
939 | writeln!( | |
940 | w, | |
941 | " DW_CFA_def_cfa_offset_sf ({})", | |
942 | factored_offset | |
943 | )?; | |
944 | } | |
945 | DefCfaExpression { expression: _ } => { | |
946 | writeln!(w, " DW_CFA_def_cfa_expression (...)")?; | |
947 | } | |
948 | Undefined { register } => { | |
949 | writeln!( | |
950 | w, | |
951 | " DW_CFA_undefined ({})", | |
952 | register_name(register) | |
953 | )?; | |
954 | } | |
955 | SameValue { register } => { | |
956 | writeln!( | |
957 | w, | |
958 | " DW_CFA_same_value ({})", | |
959 | register_name(register) | |
960 | )?; | |
961 | } | |
962 | Offset { | |
963 | register, | |
964 | factored_offset, | |
965 | } => { | |
966 | writeln!( | |
967 | w, | |
968 | " DW_CFA_offset ({}, {})", | |
969 | register_name(register), | |
970 | factored_offset | |
971 | )?; | |
972 | } | |
973 | OffsetExtendedSf { | |
974 | register, | |
975 | factored_offset, | |
976 | } => { | |
977 | writeln!( | |
978 | w, | |
979 | " DW_CFA_offset_extended_sf ({}, {})", | |
980 | register_name(register), | |
981 | factored_offset | |
982 | )?; | |
983 | } | |
984 | ValOffset { | |
985 | register, | |
986 | factored_offset, | |
987 | } => { | |
988 | writeln!( | |
989 | w, | |
990 | " DW_CFA_val_offset ({}, {})", | |
991 | register_name(register), | |
992 | factored_offset | |
993 | )?; | |
994 | } | |
995 | ValOffsetSf { | |
996 | register, | |
997 | factored_offset, | |
998 | } => { | |
999 | writeln!( | |
1000 | w, | |
1001 | " DW_CFA_val_offset_sf ({}, {})", | |
1002 | register_name(register), | |
1003 | factored_offset | |
1004 | )?; | |
1005 | } | |
1006 | Register { | |
1007 | dest_register, | |
1008 | src_register, | |
1009 | } => { | |
1010 | writeln!( | |
1011 | w, | |
1012 | " DW_CFA_register ({}, {})", | |
1013 | register_name(dest_register), | |
1014 | register_name(src_register) | |
1015 | )?; | |
1016 | } | |
1017 | Expression { | |
1018 | register, | |
1019 | expression: _, | |
1020 | } => { | |
1021 | writeln!( | |
1022 | w, | |
1023 | " DW_CFA_expression ({}, ...)", | |
1024 | register_name(register) | |
1025 | )?; | |
1026 | } | |
1027 | ValExpression { | |
1028 | register, | |
1029 | expression: _, | |
1030 | } => { | |
1031 | writeln!( | |
1032 | w, | |
1033 | " DW_CFA_val_expression ({}, ...)", | |
1034 | register_name(register) | |
1035 | )?; | |
1036 | } | |
1037 | Restore { register } => { | |
1038 | writeln!( | |
1039 | w, | |
1040 | " DW_CFA_restore ({})", | |
1041 | register_name(register) | |
1042 | )?; | |
1043 | } | |
1044 | RememberState => { | |
1045 | writeln!(w, " DW_CFA_remember_state")?; | |
1046 | } | |
1047 | RestoreState => { | |
1048 | writeln!(w, " DW_CFA_restore_state")?; | |
1049 | } | |
1050 | ArgsSize { size } => { | |
1051 | writeln!(w, " DW_CFA_GNU_args_size ({})", size)?; | |
1052 | } | |
1053 | Nop => { | |
1054 | writeln!(w, " DW_CFA_nop")?; | |
1055 | } | |
1056 | }, | |
1057 | } | |
1058 | } | |
1059 | } | |
1060 | ||
1061 | fn dump_dwp<R: Reader, W: Write + Send>( | |
1062 | w: &mut W, | |
1063 | dwp: &gimli::DwarfPackage<R>, | |
1064 | dwo_parent: &gimli::Dwarf<R>, | |
1065 | dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, | |
1066 | flags: &Flags, | |
1067 | ) -> Result<()> | |
1068 | where | |
1069 | R::Endian: Send + Sync, | |
1070 | { | |
1071 | if dwp.cu_index.unit_count() != 0 { | |
1072 | writeln!( | |
1073 | w, | |
1074 | "\n.debug_cu_index: version = {}, sections = {}, units = {}, slots = {}", | |
1075 | dwp.cu_index.version(), | |
1076 | dwp.cu_index.section_count(), | |
1077 | dwp.cu_index.unit_count(), | |
1078 | dwp.cu_index.slot_count(), | |
1079 | )?; | |
1080 | for i in 1..=dwp.cu_index.unit_count() { | |
1081 | writeln!(w, "\nCU index {}", i)?; | |
1082 | dump_dwp_sections( | |
1083 | w, | |
1084 | &dwp, | |
1085 | dwo_parent, | |
1086 | dwo_parent_units, | |
1087 | flags, | |
1088 | dwp.cu_index.sections(i)?, | |
1089 | )?; | |
1090 | } | |
1091 | } | |
1092 | ||
1093 | if dwp.tu_index.unit_count() != 0 { | |
1094 | writeln!( | |
1095 | w, | |
1096 | "\n.debug_tu_index: version = {}, sections = {}, units = {}, slots = {}", | |
1097 | dwp.tu_index.version(), | |
1098 | dwp.tu_index.section_count(), | |
1099 | dwp.tu_index.unit_count(), | |
1100 | dwp.tu_index.slot_count(), | |
1101 | )?; | |
1102 | for i in 1..=dwp.tu_index.unit_count() { | |
1103 | writeln!(w, "\nTU index {}", i)?; | |
1104 | dump_dwp_sections( | |
1105 | w, | |
1106 | &dwp, | |
1107 | dwo_parent, | |
1108 | dwo_parent_units, | |
1109 | flags, | |
1110 | dwp.tu_index.sections(i)?, | |
1111 | )?; | |
1112 | } | |
1113 | } | |
1114 | ||
1115 | Ok(()) | |
1116 | } | |
1117 | ||
1118 | fn dump_dwp_sections<R: Reader, W: Write + Send>( | |
1119 | w: &mut W, | |
1120 | dwp: &gimli::DwarfPackage<R>, | |
1121 | dwo_parent: &gimli::Dwarf<R>, | |
1122 | dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, | |
1123 | flags: &Flags, | |
1124 | sections: gimli::UnitIndexSectionIterator<R>, | |
1125 | ) -> Result<()> | |
1126 | where | |
1127 | R::Endian: Send + Sync, | |
1128 | { | |
1129 | for section in sections.clone() { | |
1130 | writeln!( | |
1131 | w, | |
1132 | " {}: offset = 0x{:x}, size = 0x{:x}", | |
1133 | section.section.dwo_name().unwrap(), | |
1134 | section.offset, | |
1135 | section.size | |
1136 | )?; | |
1137 | } | |
1138 | let dwarf = dwp.sections(sections, dwo_parent)?; | |
1139 | if flags.info { | |
1140 | dump_info(w, &dwarf, dwo_parent_units, flags)?; | |
1141 | dump_types(w, &dwarf, dwo_parent_units, flags)?; | |
1142 | } | |
1143 | if flags.line { | |
1144 | dump_line(w, &dwarf)?; | |
1145 | } | |
1146 | Ok(()) | |
1147 | } | |
1148 | ||
1149 | fn dump_info<R: Reader, W: Write + Send>( | |
1150 | w: &mut W, | |
1151 | dwarf: &gimli::Dwarf<R>, | |
1152 | dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, | |
1153 | flags: &Flags, | |
1154 | ) -> Result<()> | |
1155 | where | |
1156 | R::Endian: Send + Sync, | |
1157 | { | |
1158 | writeln!(w, "\n.debug_info")?; | |
1159 | ||
1160 | let units = match dwarf.units().collect::<Vec<_>>() { | |
1161 | Ok(units) => units, | |
1162 | Err(err) => { | |
1163 | writeln_error( | |
1164 | w, | |
1165 | dwarf, | |
1166 | Error::GimliError(err), | |
1167 | "Failed to read unit headers", | |
1168 | )?; | |
1169 | return Ok(()); | |
1170 | } | |
1171 | }; | |
1172 | let process_unit = |header: UnitHeader<R>, buf: &mut Vec<u8>| -> Result<()> { | |
1173 | dump_unit(buf, header, dwarf, dwo_parent_units, flags)?; | |
1174 | if !flags | |
1175 | .match_units | |
1176 | .as_ref() | |
1177 | .map(|r| r.is_match(&buf)) | |
1178 | .unwrap_or(true) | |
1179 | { | |
1180 | buf.clear(); | |
1181 | } | |
1182 | Ok(()) | |
1183 | }; | |
1184 | // Don't use more than 16 cores even if available. No point in soaking hundreds | |
1185 | // of cores if you happen to have them. | |
1186 | parallel_output(w, 16, units, process_unit) | |
1187 | } | |
1188 | ||
1189 | fn dump_types<R: Reader, W: Write>( | |
1190 | w: &mut W, | |
1191 | dwarf: &gimli::Dwarf<R>, | |
1192 | dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, | |
1193 | flags: &Flags, | |
1194 | ) -> Result<()> { | |
1195 | writeln!(w, "\n.debug_types")?; | |
1196 | ||
1197 | let mut iter = dwarf.type_units(); | |
1198 | while let Some(header) = iter.next()? { | |
1199 | dump_unit(w, header, dwarf, dwo_parent_units, flags)?; | |
1200 | } | |
1201 | Ok(()) | |
1202 | } | |
1203 | ||
1204 | fn dump_unit<R: Reader, W: Write>( | |
1205 | w: &mut W, | |
1206 | header: UnitHeader<R>, | |
1207 | dwarf: &gimli::Dwarf<R>, | |
1208 | dwo_parent_units: Option<&HashMap<gimli::DwoId, gimli::Unit<R>>>, | |
1209 | flags: &Flags, | |
1210 | ) -> Result<()> { | |
1211 | write!(w, "\nUNIT<")?; | |
1212 | match header.offset() { | |
1213 | UnitSectionOffset::DebugInfoOffset(o) => { | |
1214 | write!(w, ".debug_info+0x{:08x}", o.0)?; | |
1215 | } | |
1216 | UnitSectionOffset::DebugTypesOffset(o) => { | |
1217 | write!(w, ".debug_types+0x{:08x}", o.0)?; | |
1218 | } | |
1219 | } | |
1220 | writeln!(w, ">: length = 0x{:x}, format = {:?}, version = {}, address_size = {}, abbrev_offset = 0x{:x}", | |
1221 | header.unit_length(), | |
1222 | header.format(), | |
1223 | header.version(), | |
1224 | header.address_size(), | |
1225 | header.debug_abbrev_offset().0, | |
1226 | )?; | |
1227 | ||
1228 | match header.type_() { | |
1229 | UnitType::Compilation | UnitType::Partial => (), | |
1230 | UnitType::Type { | |
1231 | type_signature, | |
1232 | type_offset, | |
1233 | } | |
1234 | | UnitType::SplitType { | |
1235 | type_signature, | |
1236 | type_offset, | |
1237 | } => { | |
1238 | write!(w, " signature = ")?; | |
1239 | dump_type_signature(w, type_signature)?; | |
1240 | writeln!(w)?; | |
1241 | writeln!(w, " type_offset = 0x{:x}", type_offset.0,)?; | |
1242 | } | |
1243 | UnitType::Skeleton(dwo_id) | UnitType::SplitCompilation(dwo_id) => { | |
1244 | write!(w, " dwo_id = ")?; | |
1245 | writeln!(w, "0x{:016x}", dwo_id.0)?; | |
1246 | } | |
1247 | } | |
1248 | ||
1249 | let mut unit = match dwarf.unit(header) { | |
1250 | Ok(unit) => unit, | |
1251 | Err(err) => { | |
1252 | writeln_error(w, dwarf, err.into(), "Failed to parse unit root entry")?; | |
1253 | return Ok(()); | |
1254 | } | |
1255 | }; | |
1256 | ||
1257 | if let Some(dwo_parent_units) = dwo_parent_units { | |
1258 | if let Some(dwo_id) = unit.dwo_id { | |
1259 | if let Some(parent_unit) = dwo_parent_units.get(&dwo_id) { | |
1260 | unit.copy_relocated_attributes(parent_unit); | |
1261 | } | |
1262 | } | |
1263 | } | |
1264 | ||
1265 | let entries_result = dump_entries(w, unit, dwarf, flags); | |
1266 | if let Err(err) = entries_result { | |
1267 | writeln_error(w, dwarf, err, "Failed to dump entries")?; | |
1268 | } | |
1269 | Ok(()) | |
1270 | } | |
1271 | ||
1272 | fn spaces(buf: &mut String, len: usize) -> &str { | |
1273 | while buf.len() < len { | |
1274 | buf.push(' '); | |
1275 | } | |
1276 | &buf[..len] | |
1277 | } | |
1278 | ||
1279 | // " GOFF=0x{:08x}" adds exactly 16 spaces. | |
1280 | const GOFF_SPACES: usize = 16; | |
1281 | ||
1282 | fn write_offset<R: Reader, W: Write>( | |
1283 | w: &mut W, | |
1284 | unit: &gimli::Unit<R>, | |
1285 | offset: gimli::UnitOffset<R::Offset>, | |
1286 | flags: &Flags, | |
1287 | ) -> Result<()> { | |
1288 | write!(w, "<0x{:08x}", offset.0)?; | |
1289 | if flags.goff { | |
1290 | let goff = match offset.to_unit_section_offset(unit) { | |
1291 | UnitSectionOffset::DebugInfoOffset(o) => o.0, | |
1292 | UnitSectionOffset::DebugTypesOffset(o) => o.0, | |
1293 | }; | |
1294 | write!(w, " GOFF=0x{:08x}", goff)?; | |
1295 | } | |
1296 | write!(w, ">")?; | |
1297 | Ok(()) | |
1298 | } | |
1299 | ||
1300 | fn dump_entries<R: Reader, W: Write>( | |
1301 | w: &mut W, | |
1302 | unit: gimli::Unit<R>, | |
1303 | dwarf: &gimli::Dwarf<R>, | |
1304 | flags: &Flags, | |
1305 | ) -> Result<()> { | |
1306 | let mut spaces_buf = String::new(); | |
1307 | ||
1308 | let mut entries = unit.entries_raw(None)?; | |
1309 | while !entries.is_empty() { | |
1310 | let offset = entries.next_offset(); | |
1311 | let depth = entries.next_depth(); | |
1312 | let abbrev = entries.read_abbreviation()?; | |
1313 | ||
1314 | let mut indent = if depth >= 0 { | |
1315 | depth as usize * 2 + 2 | |
1316 | } else { | |
1317 | 2 | |
1318 | }; | |
1319 | write!(w, "<{}{}>", if depth < 10 { " " } else { "" }, depth)?; | |
1320 | write_offset(w, &unit, offset, flags)?; | |
1321 | writeln!( | |
1322 | w, | |
1323 | "{}{}", | |
1324 | spaces(&mut spaces_buf, indent), | |
1325 | abbrev.map(|x| x.tag()).unwrap_or(gimli::DW_TAG_null) | |
1326 | )?; | |
1327 | ||
1328 | indent += 18; | |
1329 | if flags.goff { | |
1330 | indent += GOFF_SPACES; | |
1331 | } | |
1332 | ||
1333 | for spec in abbrev.map(|x| x.attributes()).unwrap_or(&[]) { | |
1334 | let attr = entries.read_attribute(*spec)?; | |
1335 | w.write_all(spaces(&mut spaces_buf, indent).as_bytes())?; | |
1336 | if let Some(n) = attr.name().static_string() { | |
1337 | let right_padding = 27 - std::cmp::min(27, n.len()); | |
1338 | write!(w, "{}{} ", n, spaces(&mut spaces_buf, right_padding))?; | |
1339 | } else { | |
1340 | write!(w, "{:27} ", attr.name())?; | |
1341 | } | |
1342 | if flags.raw { | |
1343 | writeln!(w, "{:?}", attr.raw_value())?; | |
1344 | } else { | |
1345 | match dump_attr_value(w, &attr, &unit, dwarf) { | |
1346 | Ok(_) => (), | |
1347 | Err(err) => writeln_error(w, dwarf, err, "Failed to dump attribute value")?, | |
1348 | }; | |
1349 | } | |
1350 | } | |
1351 | } | |
1352 | Ok(()) | |
1353 | } | |
1354 | ||
1355 | fn dump_attr_value<R: Reader, W: Write>( | |
1356 | w: &mut W, | |
1357 | attr: &gimli::Attribute<R>, | |
1358 | unit: &gimli::Unit<R>, | |
1359 | dwarf: &gimli::Dwarf<R>, | |
1360 | ) -> Result<()> { | |
1361 | let value = attr.value(); | |
1362 | match value { | |
1363 | gimli::AttributeValue::Addr(address) => { | |
1364 | writeln!(w, "0x{:08x}", address)?; | |
1365 | } | |
1366 | gimli::AttributeValue::Block(data) => { | |
1367 | for byte in data.to_slice()?.iter() { | |
1368 | write!(w, "{:02x}", byte)?; | |
1369 | } | |
1370 | writeln!(w)?; | |
1371 | } | |
1372 | gimli::AttributeValue::Data1(_) | |
1373 | | gimli::AttributeValue::Data2(_) | |
1374 | | gimli::AttributeValue::Data4(_) | |
1375 | | gimli::AttributeValue::Data8(_) => { | |
1376 | if let (Some(udata), Some(sdata)) = (attr.udata_value(), attr.sdata_value()) { | |
1377 | if sdata >= 0 { | |
1378 | writeln!(w, "{}", udata)?; | |
1379 | } else { | |
1380 | writeln!(w, "{} ({})", udata, sdata)?; | |
1381 | } | |
1382 | } else { | |
1383 | writeln!(w, "{:?}", value)?; | |
1384 | } | |
1385 | } | |
1386 | gimli::AttributeValue::Sdata(data) => { | |
1387 | match attr.name() { | |
1388 | gimli::DW_AT_data_member_location => { | |
1389 | writeln!(w, "{}", data)?; | |
1390 | } | |
1391 | _ => { | |
1392 | if data >= 0 { | |
1393 | writeln!(w, "0x{:08x}", data)?; | |
1394 | } else { | |
1395 | writeln!(w, "0x{:08x} ({})", data, data)?; | |
1396 | } | |
1397 | } | |
1398 | }; | |
1399 | } | |
1400 | gimli::AttributeValue::Udata(data) => { | |
1401 | match attr.name() { | |
1402 | gimli::DW_AT_high_pc => { | |
1403 | writeln!(w, "<offset-from-lowpc>{}", data)?; | |
1404 | } | |
1405 | gimli::DW_AT_data_member_location => { | |
1406 | if let Some(sdata) = attr.sdata_value() { | |
1407 | // This is a DW_FORM_data* value. | |
1408 | // libdwarf-dwarfdump displays this as signed too. | |
1409 | if sdata >= 0 { | |
1410 | writeln!(w, "{}", data)?; | |
1411 | } else { | |
1412 | writeln!(w, "{} ({})", data, sdata)?; | |
1413 | } | |
1414 | } else { | |
1415 | writeln!(w, "{}", data)?; | |
1416 | } | |
1417 | } | |
1418 | gimli::DW_AT_lower_bound | gimli::DW_AT_upper_bound => { | |
1419 | writeln!(w, "{}", data)?; | |
1420 | } | |
1421 | _ => { | |
1422 | writeln!(w, "0x{:08x}", data)?; | |
1423 | } | |
1424 | }; | |
1425 | } | |
1426 | gimli::AttributeValue::Exprloc(ref data) => { | |
1427 | if let gimli::AttributeValue::Exprloc(_) = attr.raw_value() { | |
1428 | write!(w, "len 0x{:04x}: ", data.0.len())?; | |
1429 | for byte in data.0.to_slice()?.iter() { | |
1430 | write!(w, "{:02x}", byte)?; | |
1431 | } | |
1432 | write!(w, ": ")?; | |
1433 | } | |
1434 | dump_exprloc(w, unit.encoding(), data)?; | |
1435 | writeln!(w)?; | |
1436 | } | |
1437 | gimli::AttributeValue::Flag(true) => { | |
1438 | writeln!(w, "yes")?; | |
1439 | } | |
1440 | gimli::AttributeValue::Flag(false) => { | |
1441 | writeln!(w, "no")?; | |
1442 | } | |
1443 | gimli::AttributeValue::SecOffset(offset) => { | |
1444 | writeln!(w, "0x{:08x}", offset)?; | |
1445 | } | |
1446 | gimli::AttributeValue::DebugAddrBase(base) => { | |
1447 | writeln!(w, "<.debug_addr+0x{:08x}>", base.0)?; | |
1448 | } | |
1449 | gimli::AttributeValue::DebugAddrIndex(index) => { | |
1450 | write!(w, "(indirect address, index {:#x}): ", index.0)?; | |
1451 | let address = dwarf.address(unit, index)?; | |
1452 | writeln!(w, "0x{:08x}", address)?; | |
1453 | } | |
1454 | gimli::AttributeValue::UnitRef(offset) => { | |
1455 | write!(w, "0x{:08x}", offset.0)?; | |
1456 | match offset.to_unit_section_offset(unit) { | |
1457 | UnitSectionOffset::DebugInfoOffset(goff) => { | |
1458 | write!(w, "<.debug_info+0x{:08x}>", goff.0)?; | |
1459 | } | |
1460 | UnitSectionOffset::DebugTypesOffset(goff) => { | |
1461 | write!(w, "<.debug_types+0x{:08x}>", goff.0)?; | |
1462 | } | |
1463 | } | |
1464 | writeln!(w)?; | |
1465 | } | |
1466 | gimli::AttributeValue::DebugInfoRef(offset) => { | |
1467 | writeln!(w, "<.debug_info+0x{:08x}>", offset.0)?; | |
1468 | } | |
1469 | gimli::AttributeValue::DebugInfoRefSup(offset) => { | |
1470 | writeln!(w, "<.debug_info(sup)+0x{:08x}>", offset.0)?; | |
1471 | } | |
1472 | gimli::AttributeValue::DebugLineRef(offset) => { | |
1473 | writeln!(w, "<.debug_line+0x{:08x}>", offset.0)?; | |
1474 | } | |
1475 | gimli::AttributeValue::LocationListsRef(offset) => { | |
1476 | dump_loc_list(w, offset, unit, dwarf)?; | |
1477 | } | |
1478 | gimli::AttributeValue::DebugLocListsBase(base) => { | |
1479 | writeln!(w, "<.debug_loclists+0x{:08x}>", base.0)?; | |
1480 | } | |
1481 | gimli::AttributeValue::DebugLocListsIndex(index) => { | |
1482 | write!(w, "(indirect location list, index {:#x}): ", index.0)?; | |
1483 | let offset = dwarf.locations_offset(unit, index)?; | |
1484 | dump_loc_list(w, offset, unit, dwarf)?; | |
1485 | } | |
1486 | gimli::AttributeValue::DebugMacinfoRef(offset) => { | |
1487 | writeln!(w, "<.debug_macinfo+0x{:08x}>", offset.0)?; | |
1488 | } | |
1489 | gimli::AttributeValue::DebugMacroRef(offset) => { | |
1490 | writeln!(w, "<.debug_macro+0x{:08x}>", offset.0)?; | |
1491 | } | |
1492 | gimli::AttributeValue::RangeListsRef(offset) => { | |
1493 | let offset = dwarf.ranges_offset_from_raw(unit, offset); | |
1494 | dump_range_list(w, offset, unit, dwarf)?; | |
1495 | } | |
1496 | gimli::AttributeValue::DebugRngListsBase(base) => { | |
1497 | writeln!(w, "<.debug_rnglists+0x{:08x}>", base.0)?; | |
1498 | } | |
1499 | gimli::AttributeValue::DebugRngListsIndex(index) => { | |
1500 | write!(w, "(indirect range list, index {:#x}): ", index.0)?; | |
1501 | let offset = dwarf.ranges_offset(unit, index)?; | |
1502 | dump_range_list(w, offset, unit, dwarf)?; | |
1503 | } | |
1504 | gimli::AttributeValue::DebugTypesRef(signature) => { | |
1505 | dump_type_signature(w, signature)?; | |
1506 | writeln!(w, " <type signature>")?; | |
1507 | } | |
1508 | gimli::AttributeValue::DebugStrRef(offset) => { | |
1509 | if let Ok(s) = dwarf.debug_str.get_str(offset) { | |
1510 | writeln!(w, "{}", s.to_string_lossy()?)?; | |
1511 | } else { | |
1512 | writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?; | |
1513 | } | |
1514 | } | |
1515 | gimli::AttributeValue::DebugStrRefSup(offset) => { | |
1516 | if let Some(s) = dwarf | |
1517 | .sup() | |
1518 | .and_then(|sup| sup.debug_str.get_str(offset).ok()) | |
1519 | { | |
1520 | writeln!(w, "{}", s.to_string_lossy()?)?; | |
1521 | } else { | |
1522 | writeln!(w, "<.debug_str(sup)+0x{:08x}>", offset.0)?; | |
1523 | } | |
1524 | } | |
1525 | gimli::AttributeValue::DebugStrOffsetsBase(base) => { | |
1526 | writeln!(w, "<.debug_str_offsets+0x{:08x}>", base.0)?; | |
1527 | } | |
1528 | gimli::AttributeValue::DebugStrOffsetsIndex(index) => { | |
1529 | write!(w, "(indirect string, index {:#x}): ", index.0)?; | |
1530 | let offset = dwarf.debug_str_offsets.get_str_offset( | |
1531 | unit.encoding().format, | |
1532 | unit.str_offsets_base, | |
1533 | index, | |
1534 | )?; | |
1535 | if let Ok(s) = dwarf.debug_str.get_str(offset) { | |
1536 | writeln!(w, "{}", s.to_string_lossy()?)?; | |
1537 | } else { | |
1538 | writeln!(w, "<.debug_str+0x{:08x}>", offset.0)?; | |
1539 | } | |
1540 | } | |
1541 | gimli::AttributeValue::DebugLineStrRef(offset) => { | |
1542 | if let Ok(s) = dwarf.debug_line_str.get_str(offset) { | |
1543 | writeln!(w, "{}", s.to_string_lossy()?)?; | |
1544 | } else { | |
1545 | writeln!(w, "<.debug_line_str=0x{:08x}>", offset.0)?; | |
1546 | } | |
1547 | } | |
1548 | gimli::AttributeValue::String(s) => { | |
1549 | writeln!(w, "{}", s.to_string_lossy()?)?; | |
1550 | } | |
1551 | gimli::AttributeValue::Encoding(value) => { | |
1552 | writeln!(w, "{}", value)?; | |
1553 | } | |
1554 | gimli::AttributeValue::DecimalSign(value) => { | |
1555 | writeln!(w, "{}", value)?; | |
1556 | } | |
1557 | gimli::AttributeValue::Endianity(value) => { | |
1558 | writeln!(w, "{}", value)?; | |
1559 | } | |
1560 | gimli::AttributeValue::Accessibility(value) => { | |
1561 | writeln!(w, "{}", value)?; | |
1562 | } | |
1563 | gimli::AttributeValue::Visibility(value) => { | |
1564 | writeln!(w, "{}", value)?; | |
1565 | } | |
1566 | gimli::AttributeValue::Virtuality(value) => { | |
1567 | writeln!(w, "{}", value)?; | |
1568 | } | |
1569 | gimli::AttributeValue::Language(value) => { | |
1570 | writeln!(w, "{}", value)?; | |
1571 | } | |
1572 | gimli::AttributeValue::AddressClass(value) => { | |
1573 | writeln!(w, "{}", value)?; | |
1574 | } | |
1575 | gimli::AttributeValue::IdentifierCase(value) => { | |
1576 | writeln!(w, "{}", value)?; | |
1577 | } | |
1578 | gimli::AttributeValue::CallingConvention(value) => { | |
1579 | writeln!(w, "{}", value)?; | |
1580 | } | |
1581 | gimli::AttributeValue::Inline(value) => { | |
1582 | writeln!(w, "{}", value)?; | |
1583 | } | |
1584 | gimli::AttributeValue::Ordering(value) => { | |
1585 | writeln!(w, "{}", value)?; | |
1586 | } | |
1587 | gimli::AttributeValue::FileIndex(value) => { | |
1588 | write!(w, "0x{:08x}", value)?; | |
1589 | dump_file_index(w, value, unit, dwarf)?; | |
1590 | writeln!(w)?; | |
1591 | } | |
1592 | gimli::AttributeValue::DwoId(value) => { | |
1593 | writeln!(w, "0x{:016x}", value.0)?; | |
1594 | } | |
1595 | } | |
1596 | ||
1597 | Ok(()) | |
1598 | } | |
1599 | ||
1600 | fn dump_type_signature<W: Write>(w: &mut W, signature: gimli::DebugTypeSignature) -> Result<()> { | |
1601 | write!(w, "0x{:016x}", signature.0)?; | |
1602 | Ok(()) | |
1603 | } | |
1604 | ||
1605 | fn dump_file_index<R: Reader, W: Write>( | |
1606 | w: &mut W, | |
1607 | file_index: u64, | |
1608 | unit: &gimli::Unit<R>, | |
1609 | dwarf: &gimli::Dwarf<R>, | |
1610 | ) -> Result<()> { | |
1611 | if file_index == 0 && unit.header.version() <= 4 { | |
1612 | return Ok(()); | |
1613 | } | |
1614 | let header = match unit.line_program { | |
1615 | Some(ref program) => program.header(), | |
1616 | None => return Ok(()), | |
1617 | }; | |
1618 | let file = match header.file(file_index) { | |
1619 | Some(file) => file, | |
1620 | None => { | |
1621 | writeln!(w, "Unable to get header for file {}", file_index)?; | |
1622 | return Ok(()); | |
1623 | } | |
1624 | }; | |
1625 | write!(w, " ")?; | |
1626 | if let Some(directory) = file.directory(header) { | |
1627 | let directory = dwarf.attr_string(unit, directory)?; | |
1628 | let directory = directory.to_string_lossy()?; | |
1629 | if file.directory_index() != 0 && !directory.starts_with('/') { | |
1630 | if let Some(ref comp_dir) = unit.comp_dir { | |
1631 | write!(w, "{}/", comp_dir.to_string_lossy()?,)?; | |
1632 | } | |
1633 | } | |
1634 | write!(w, "{}/", directory)?; | |
1635 | } | |
1636 | write!( | |
1637 | w, | |
1638 | "{}", | |
1639 | dwarf | |
1640 | .attr_string(unit, file.path_name())? | |
1641 | .to_string_lossy()? | |
1642 | )?; | |
1643 | Ok(()) | |
1644 | } | |
1645 | ||
1646 | fn dump_exprloc<R: Reader, W: Write>( | |
1647 | w: &mut W, | |
1648 | encoding: gimli::Encoding, | |
1649 | data: &gimli::Expression<R>, | |
1650 | ) -> Result<()> { | |
1651 | let mut pc = data.0.clone(); | |
1652 | let mut space = false; | |
1653 | while pc.len() != 0 { | |
1654 | let pc_clone = pc.clone(); | |
1655 | match gimli::Operation::parse(&mut pc, encoding) { | |
1656 | Ok(op) => { | |
1657 | if space { | |
1658 | write!(w, " ")?; | |
1659 | } else { | |
1660 | space = true; | |
1661 | } | |
1662 | dump_op(w, encoding, pc_clone, op)?; | |
1663 | } | |
1664 | Err(gimli::Error::InvalidExpression(op)) => { | |
1665 | writeln!(w, "WARNING: unsupported operation 0x{:02x}", op.0)?; | |
1666 | return Ok(()); | |
1667 | } | |
1668 | Err(gimli::Error::UnsupportedRegister(register)) => { | |
1669 | writeln!(w, "WARNING: unsupported register {}", register)?; | |
1670 | return Ok(()); | |
1671 | } | |
1672 | Err(gimli::Error::UnexpectedEof(_)) => { | |
1673 | writeln!(w, "WARNING: truncated or malformed expression")?; | |
1674 | return Ok(()); | |
1675 | } | |
1676 | Err(e) => { | |
1677 | writeln!(w, "WARNING: unexpected operation parse error: {}", e)?; | |
1678 | return Ok(()); | |
1679 | } | |
1680 | } | |
1681 | } | |
1682 | Ok(()) | |
1683 | } | |
1684 | ||
1685 | fn dump_op<R: Reader, W: Write>( | |
1686 | w: &mut W, | |
1687 | encoding: gimli::Encoding, | |
1688 | mut pc: R, | |
1689 | op: gimli::Operation<R>, | |
1690 | ) -> Result<()> { | |
1691 | let dwop = gimli::DwOp(pc.read_u8()?); | |
1692 | write!(w, "{}", dwop)?; | |
1693 | match op { | |
1694 | gimli::Operation::Deref { | |
1695 | base_type, size, .. | |
1696 | } => { | |
1697 | if dwop == gimli::DW_OP_deref_size || dwop == gimli::DW_OP_xderef_size { | |
1698 | write!(w, " {}", size)?; | |
1699 | } | |
1700 | if base_type != UnitOffset(0) { | |
1701 | write!(w, " type 0x{:08x}", base_type.0)?; | |
1702 | } | |
1703 | } | |
1704 | gimli::Operation::Pick { index } => { | |
1705 | if dwop == gimli::DW_OP_pick { | |
1706 | write!(w, " {}", index)?; | |
1707 | } | |
1708 | } | |
1709 | gimli::Operation::PlusConstant { value } => { | |
1710 | write!(w, " {}", value as i64)?; | |
1711 | } | |
1712 | gimli::Operation::Bra { target } => { | |
1713 | write!(w, " {}", target)?; | |
1714 | } | |
1715 | gimli::Operation::Skip { target } => { | |
1716 | write!(w, " {}", target)?; | |
1717 | } | |
1718 | gimli::Operation::SignedConstant { value } => match dwop { | |
1719 | gimli::DW_OP_const1s | |
1720 | | gimli::DW_OP_const2s | |
1721 | | gimli::DW_OP_const4s | |
1722 | | gimli::DW_OP_const8s | |
1723 | | gimli::DW_OP_consts => { | |
1724 | write!(w, " {}", value)?; | |
1725 | } | |
1726 | _ => {} | |
1727 | }, | |
1728 | gimli::Operation::UnsignedConstant { value } => match dwop { | |
1729 | gimli::DW_OP_const1u | |
1730 | | gimli::DW_OP_const2u | |
1731 | | gimli::DW_OP_const4u | |
1732 | | gimli::DW_OP_const8u | |
1733 | | gimli::DW_OP_constu => { | |
1734 | write!(w, " {}", value)?; | |
1735 | } | |
1736 | _ => { | |
1737 | // These have the value encoded in the operation, eg DW_OP_lit0. | |
1738 | } | |
1739 | }, | |
1740 | gimli::Operation::Register { register } => { | |
1741 | if dwop == gimli::DW_OP_regx { | |
1742 | write!(w, " {}", register.0)?; | |
1743 | } | |
1744 | } | |
1745 | gimli::Operation::RegisterOffset { | |
1746 | register, | |
1747 | offset, | |
1748 | base_type, | |
1749 | } => { | |
1750 | if dwop >= gimli::DW_OP_breg0 && dwop <= gimli::DW_OP_breg31 { | |
1751 | write!(w, "{:+}", offset)?; | |
1752 | } else { | |
1753 | write!(w, " {}", register.0)?; | |
1754 | if offset != 0 { | |
1755 | write!(w, "{:+}", offset)?; | |
1756 | } | |
1757 | if base_type != UnitOffset(0) { | |
1758 | write!(w, " type 0x{:08x}", base_type.0)?; | |
1759 | } | |
1760 | } | |
1761 | } | |
1762 | gimli::Operation::FrameOffset { offset } => { | |
1763 | write!(w, " {}", offset)?; | |
1764 | } | |
1765 | gimli::Operation::Call { offset } => match offset { | |
1766 | gimli::DieReference::UnitRef(gimli::UnitOffset(offset)) => { | |
1767 | write!(w, " 0x{:08x}", offset)?; | |
1768 | } | |
1769 | gimli::DieReference::DebugInfoRef(gimli::DebugInfoOffset(offset)) => { | |
1770 | write!(w, " 0x{:08x}", offset)?; | |
1771 | } | |
1772 | }, | |
1773 | gimli::Operation::Piece { | |
1774 | size_in_bits, | |
1775 | bit_offset: None, | |
1776 | } => { | |
1777 | write!(w, " {}", size_in_bits / 8)?; | |
1778 | } | |
1779 | gimli::Operation::Piece { | |
1780 | size_in_bits, | |
1781 | bit_offset: Some(bit_offset), | |
1782 | } => { | |
1783 | write!(w, " 0x{:08x} offset 0x{:08x}", size_in_bits, bit_offset)?; | |
1784 | } | |
1785 | gimli::Operation::ImplicitValue { data } => { | |
1786 | let data = data.to_slice()?; | |
1787 | write!(w, " 0x{:08x} contents 0x", data.len())?; | |
1788 | for byte in data.iter() { | |
1789 | write!(w, "{:02x}", byte)?; | |
1790 | } | |
1791 | } | |
1792 | gimli::Operation::ImplicitPointer { value, byte_offset } => { | |
1793 | write!(w, " 0x{:08x} {}", value.0, byte_offset)?; | |
1794 | } | |
1795 | gimli::Operation::EntryValue { expression } => { | |
1796 | write!(w, "(")?; | |
1797 | dump_exprloc(w, encoding, &gimli::Expression(expression))?; | |
1798 | write!(w, ")")?; | |
1799 | } | |
1800 | gimli::Operation::ParameterRef { offset } => { | |
1801 | write!(w, " 0x{:08x}", offset.0)?; | |
1802 | } | |
1803 | gimli::Operation::Address { address } => { | |
1804 | write!(w, " 0x{:08x}", address)?; | |
1805 | } | |
1806 | gimli::Operation::AddressIndex { index } => { | |
1807 | write!(w, " 0x{:08x}", index.0)?; | |
1808 | } | |
1809 | gimli::Operation::ConstantIndex { index } => { | |
1810 | write!(w, " 0x{:08x}", index.0)?; | |
1811 | } | |
1812 | gimli::Operation::TypedLiteral { base_type, value } => { | |
1813 | write!(w, " type 0x{:08x} contents 0x", base_type.0)?; | |
1814 | for byte in value.to_slice()?.iter() { | |
1815 | write!(w, "{:02x}", byte)?; | |
1816 | } | |
1817 | } | |
1818 | gimli::Operation::Convert { base_type } => { | |
1819 | write!(w, " type 0x{:08x}", base_type.0)?; | |
1820 | } | |
1821 | gimli::Operation::Reinterpret { base_type } => { | |
1822 | write!(w, " type 0x{:08x}", base_type.0)?; | |
1823 | } | |
1824 | gimli::Operation::WasmLocal { index } | |
1825 | | gimli::Operation::WasmGlobal { index } | |
1826 | | gimli::Operation::WasmStack { index } => { | |
1827 | let wasmop = pc.read_u8()?; | |
1828 | write!(w, " 0x{:x} 0x{:x}", wasmop, index)?; | |
1829 | } | |
1830 | gimli::Operation::Drop | |
1831 | | gimli::Operation::Swap | |
1832 | | gimli::Operation::Rot | |
1833 | | gimli::Operation::Abs | |
1834 | | gimli::Operation::And | |
1835 | | gimli::Operation::Div | |
1836 | | gimli::Operation::Minus | |
1837 | | gimli::Operation::Mod | |
1838 | | gimli::Operation::Mul | |
1839 | | gimli::Operation::Neg | |
1840 | | gimli::Operation::Not | |
1841 | | gimli::Operation::Or | |
1842 | | gimli::Operation::Plus | |
1843 | | gimli::Operation::Shl | |
1844 | | gimli::Operation::Shr | |
1845 | | gimli::Operation::Shra | |
1846 | | gimli::Operation::Xor | |
1847 | | gimli::Operation::Eq | |
1848 | | gimli::Operation::Ge | |
1849 | | gimli::Operation::Gt | |
1850 | | gimli::Operation::Le | |
1851 | | gimli::Operation::Lt | |
1852 | | gimli::Operation::Ne | |
1853 | | gimli::Operation::Nop | |
1854 | | gimli::Operation::PushObjectAddress | |
1855 | | gimli::Operation::TLS | |
1856 | | gimli::Operation::CallFrameCFA | |
1857 | | gimli::Operation::StackValue => {} | |
1858 | }; | |
1859 | Ok(()) | |
1860 | } | |
1861 | ||
1862 | fn dump_loc_list<R: Reader, W: Write>( | |
1863 | w: &mut W, | |
1864 | offset: gimli::LocationListsOffset<R::Offset>, | |
1865 | unit: &gimli::Unit<R>, | |
1866 | dwarf: &gimli::Dwarf<R>, | |
1867 | ) -> Result<()> { | |
1868 | let raw_locations = dwarf.raw_locations(unit, offset)?; | |
1869 | let raw_locations: Vec<_> = raw_locations.collect()?; | |
1870 | let mut locations = dwarf.locations(unit, offset)?; | |
1871 | writeln!( | |
1872 | w, | |
1873 | "<loclist at {}+0x{:08x} with {} entries>", | |
1874 | if unit.encoding().version < 5 { | |
1875 | ".debug_loc" | |
1876 | } else { | |
1877 | ".debug_loclists" | |
1878 | }, | |
1879 | offset.0, | |
1880 | raw_locations.len() | |
1881 | )?; | |
1882 | for (i, raw) in raw_locations.iter().enumerate() { | |
1883 | write!(w, "\t\t\t[{:2}]", i)?; | |
1884 | match *raw { | |
1885 | gimli::RawLocListEntry::BaseAddress { addr } => { | |
1886 | writeln!(w, "<new base address 0x{:08x}>", addr)?; | |
1887 | } | |
1888 | gimli::RawLocListEntry::BaseAddressx { addr } => { | |
1889 | let addr_val = dwarf.address(unit, addr)?; | |
1890 | writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?; | |
1891 | } | |
1892 | gimli::RawLocListEntry::StartxEndx { | |
1893 | begin, | |
1894 | end, | |
1895 | ref data, | |
1896 | } => { | |
1897 | let begin_val = dwarf.address(unit, begin)?; | |
1898 | let end_val = dwarf.address(unit, end)?; | |
1899 | let location = locations.next()?.unwrap(); | |
1900 | write!( | |
1901 | w, | |
1902 | "<startx-endx \ | |
1903 | low-off: [{}]0x{:08x} addr 0x{:08x} \ | |
1904 | high-off: [{}]0x{:08x} addr 0x{:08x}>", | |
1905 | begin.0, begin_val, location.range.begin, end.0, end_val, location.range.end | |
1906 | )?; | |
1907 | dump_exprloc(w, unit.encoding(), data)?; | |
1908 | writeln!(w)?; | |
1909 | } | |
1910 | gimli::RawLocListEntry::StartxLength { | |
1911 | begin, | |
1912 | length, | |
1913 | ref data, | |
1914 | } => { | |
1915 | let begin_val = dwarf.address(unit, begin)?; | |
1916 | let location = locations.next()?.unwrap(); | |
1917 | write!( | |
1918 | w, | |
1919 | "<start-length \ | |
1920 | low-off: [{}]0x{:08x} addr 0x{:08x} \ | |
1921 | high-off: 0x{:08x} addr 0x{:08x}>", | |
1922 | begin.0, begin_val, location.range.begin, length, location.range.end | |
1923 | )?; | |
1924 | dump_exprloc(w, unit.encoding(), data)?; | |
1925 | writeln!(w)?; | |
1926 | } | |
1927 | gimli::RawLocListEntry::AddressOrOffsetPair { | |
1928 | begin, | |
1929 | end, | |
1930 | ref data, | |
1931 | } | |
1932 | | gimli::RawLocListEntry::OffsetPair { | |
1933 | begin, | |
1934 | end, | |
1935 | ref data, | |
1936 | } => { | |
1937 | let location = locations.next()?.unwrap(); | |
1938 | write!( | |
1939 | w, | |
1940 | "<offset pair \ | |
1941 | low-off: 0x{:08x} addr 0x{:08x} \ | |
1942 | high-off: 0x{:08x} addr 0x{:08x}>", | |
1943 | begin, location.range.begin, end, location.range.end | |
1944 | )?; | |
1945 | dump_exprloc(w, unit.encoding(), data)?; | |
1946 | writeln!(w)?; | |
1947 | } | |
1948 | gimli::RawLocListEntry::DefaultLocation { ref data } => { | |
1949 | write!(w, "<default location>")?; | |
1950 | dump_exprloc(w, unit.encoding(), data)?; | |
1951 | writeln!(w)?; | |
1952 | } | |
1953 | gimli::RawLocListEntry::StartEnd { | |
1954 | begin, | |
1955 | end, | |
1956 | ref data, | |
1957 | } => { | |
1958 | let location = locations.next()?.unwrap(); | |
1959 | write!( | |
1960 | w, | |
1961 | "<start-end \ | |
1962 | low-off: 0x{:08x} addr 0x{:08x} \ | |
1963 | high-off: 0x{:08x} addr 0x{:08x}>", | |
1964 | begin, location.range.begin, end, location.range.end | |
1965 | )?; | |
1966 | dump_exprloc(w, unit.encoding(), data)?; | |
1967 | writeln!(w)?; | |
1968 | } | |
1969 | gimli::RawLocListEntry::StartLength { | |
1970 | begin, | |
1971 | length, | |
1972 | ref data, | |
1973 | } => { | |
1974 | let location = locations.next()?.unwrap(); | |
1975 | write!( | |
1976 | w, | |
1977 | "<start-length \ | |
1978 | low-off: 0x{:08x} addr 0x{:08x} \ | |
1979 | high-off: 0x{:08x} addr 0x{:08x}>", | |
1980 | begin, location.range.begin, length, location.range.end | |
1981 | )?; | |
1982 | dump_exprloc(w, unit.encoding(), data)?; | |
1983 | writeln!(w)?; | |
1984 | } | |
1985 | }; | |
1986 | } | |
1987 | Ok(()) | |
1988 | } | |
1989 | ||
1990 | fn dump_range_list<R: Reader, W: Write>( | |
1991 | w: &mut W, | |
1992 | offset: gimli::RangeListsOffset<R::Offset>, | |
1993 | unit: &gimli::Unit<R>, | |
1994 | dwarf: &gimli::Dwarf<R>, | |
1995 | ) -> Result<()> { | |
1996 | let raw_ranges = dwarf.raw_ranges(unit, offset)?; | |
1997 | let raw_ranges: Vec<_> = raw_ranges.collect()?; | |
1998 | let mut ranges = dwarf.ranges(unit, offset)?; | |
1999 | writeln!( | |
2000 | w, | |
2001 | "<rnglist at {}+0x{:08x} with {} entries>", | |
2002 | if unit.encoding().version < 5 { | |
2003 | ".debug_ranges" | |
2004 | } else { | |
2005 | ".debug_rnglists" | |
2006 | }, | |
2007 | offset.0, | |
2008 | raw_ranges.len() | |
2009 | )?; | |
2010 | for (i, raw) in raw_ranges.iter().enumerate() { | |
2011 | write!(w, "\t\t\t[{:2}] ", i)?; | |
2012 | match *raw { | |
2013 | gimli::RawRngListEntry::AddressOrOffsetPair { begin, end } => { | |
2014 | let range = ranges.next()?.unwrap(); | |
2015 | writeln!( | |
2016 | w, | |
2017 | "<address pair \ | |
2018 | low-off: 0x{:08x} addr 0x{:08x} \ | |
2019 | high-off: 0x{:08x} addr 0x{:08x}>", | |
2020 | begin, range.begin, end, range.end | |
2021 | )?; | |
2022 | } | |
2023 | gimli::RawRngListEntry::BaseAddress { addr } => { | |
2024 | writeln!(w, "<new base address 0x{:08x}>", addr)?; | |
2025 | } | |
2026 | gimli::RawRngListEntry::BaseAddressx { addr } => { | |
2027 | let addr_val = dwarf.address(unit, addr)?; | |
2028 | writeln!(w, "<new base addressx [{}]0x{:08x}>", addr.0, addr_val)?; | |
2029 | } | |
2030 | gimli::RawRngListEntry::StartxEndx { begin, end } => { | |
2031 | let begin_val = dwarf.address(unit, begin)?; | |
2032 | let end_val = dwarf.address(unit, end)?; | |
2033 | let range = if begin_val == end_val { | |
2034 | gimli::Range { | |
2035 | begin: begin_val, | |
2036 | end: end_val, | |
2037 | } | |
2038 | } else { | |
2039 | ranges.next()?.unwrap() | |
2040 | }; | |
2041 | writeln!( | |
2042 | w, | |
2043 | "<startx-endx \ | |
2044 | low-off: [{}]0x{:08x} addr 0x{:08x} \ | |
2045 | high-off: [{}]0x{:08x} addr 0x{:08x}>", | |
2046 | begin.0, begin_val, range.begin, end.0, end_val, range.end | |
2047 | )?; | |
2048 | } | |
2049 | gimli::RawRngListEntry::StartxLength { begin, length } => { | |
2050 | let begin_val = dwarf.address(unit, begin)?; | |
2051 | let range = ranges.next()?.unwrap(); | |
2052 | writeln!( | |
2053 | w, | |
2054 | "<startx-length \ | |
2055 | low-off: [{}]0x{:08x} addr 0x{:08x} \ | |
2056 | high-off: 0x{:08x} addr 0x{:08x}>", | |
2057 | begin.0, begin_val, range.begin, length, range.end | |
2058 | )?; | |
2059 | } | |
2060 | gimli::RawRngListEntry::OffsetPair { begin, end } => { | |
2061 | let range = ranges.next()?.unwrap(); | |
2062 | writeln!( | |
2063 | w, | |
2064 | "<offset pair \ | |
2065 | low-off: 0x{:08x} addr 0x{:08x} \ | |
2066 | high-off: 0x{:08x} addr 0x{:08x}>", | |
2067 | begin, range.begin, end, range.end | |
2068 | )?; | |
2069 | } | |
2070 | gimli::RawRngListEntry::StartEnd { begin, end } => { | |
2071 | let range = if begin == end { | |
2072 | gimli::Range { begin, end } | |
2073 | } else { | |
2074 | ranges.next()?.unwrap() | |
2075 | }; | |
2076 | writeln!( | |
2077 | w, | |
2078 | "<start-end \ | |
2079 | low-off: 0x{:08x} addr 0x{:08x} \ | |
2080 | high-off: 0x{:08x} addr 0x{:08x}>", | |
2081 | begin, range.begin, end, range.end | |
2082 | )?; | |
2083 | } | |
2084 | gimli::RawRngListEntry::StartLength { begin, length } => { | |
2085 | let range = ranges.next()?.unwrap(); | |
2086 | writeln!( | |
2087 | w, | |
2088 | "<start-length \ | |
2089 | low-off: 0x{:08x} addr 0x{:08x} \ | |
2090 | high-off: 0x{:08x} addr 0x{:08x}>", | |
2091 | begin, range.begin, length, range.end | |
2092 | )?; | |
2093 | } | |
2094 | }; | |
2095 | } | |
2096 | Ok(()) | |
2097 | } | |
2098 | ||
2099 | fn dump_line<R: Reader, W: Write>(w: &mut W, dwarf: &gimli::Dwarf<R>) -> Result<()> { | |
2100 | let mut iter = dwarf.units(); | |
2101 | while let Some(header) = iter.next()? { | |
2102 | writeln!( | |
2103 | w, | |
2104 | "\n.debug_line: line number info for unit at .debug_info offset 0x{:08x}", | |
2105 | header.offset().as_debug_info_offset().unwrap().0 | |
2106 | )?; | |
2107 | let unit = match dwarf.unit(header) { | |
2108 | Ok(unit) => unit, | |
2109 | Err(err) => { | |
2110 | writeln_error( | |
2111 | w, | |
2112 | dwarf, | |
2113 | err.into(), | |
2114 | "Failed to parse unit root entry for dump_line", | |
2115 | )?; | |
2116 | continue; | |
2117 | } | |
2118 | }; | |
2119 | match dump_line_program(w, &unit, dwarf) { | |
2120 | Ok(_) => (), | |
2121 | Err(Error::IoError) => return Err(Error::IoError), | |
2122 | Err(err) => writeln_error(w, dwarf, err, "Failed to dump line program")?, | |
2123 | } | |
2124 | } | |
2125 | Ok(()) | |
2126 | } | |
2127 | ||
2128 | fn dump_line_program<R: Reader, W: Write>( | |
2129 | w: &mut W, | |
2130 | unit: &gimli::Unit<R>, | |
2131 | dwarf: &gimli::Dwarf<R>, | |
2132 | ) -> Result<()> { | |
2133 | if let Some(program) = unit.line_program.clone() { | |
2134 | { | |
2135 | let header = program.header(); | |
2136 | writeln!(w)?; | |
2137 | writeln!( | |
2138 | w, | |
2139 | "Offset: 0x{:x}", | |
2140 | header.offset().0 | |
2141 | )?; | |
2142 | writeln!( | |
2143 | w, | |
2144 | "Length: {}", | |
2145 | header.unit_length() | |
2146 | )?; | |
2147 | writeln!( | |
2148 | w, | |
2149 | "DWARF version: {}", | |
2150 | header.version() | |
2151 | )?; | |
2152 | writeln!( | |
2153 | w, | |
2154 | "Address size: {}", | |
2155 | header.address_size() | |
2156 | )?; | |
2157 | writeln!( | |
2158 | w, | |
2159 | "Prologue length: {}", | |
2160 | header.header_length() | |
2161 | )?; | |
2162 | writeln!( | |
2163 | w, | |
2164 | "Minimum instruction length: {}", | |
2165 | header.minimum_instruction_length() | |
2166 | )?; | |
2167 | writeln!( | |
2168 | w, | |
2169 | "Maximum operations per instruction: {}", | |
2170 | header.maximum_operations_per_instruction() | |
2171 | )?; | |
2172 | writeln!( | |
2173 | w, | |
2174 | "Default is_stmt: {}", | |
2175 | header.default_is_stmt() | |
2176 | )?; | |
2177 | writeln!( | |
2178 | w, | |
2179 | "Line base: {}", | |
2180 | header.line_base() | |
2181 | )?; | |
2182 | writeln!( | |
2183 | w, | |
2184 | "Line range: {}", | |
2185 | header.line_range() | |
2186 | )?; | |
2187 | writeln!( | |
2188 | w, | |
2189 | "Opcode base: {}", | |
2190 | header.opcode_base() | |
2191 | )?; | |
2192 | ||
2193 | writeln!(w)?; | |
2194 | writeln!(w, "Opcodes:")?; | |
2195 | for (i, length) in header | |
2196 | .standard_opcode_lengths() | |
2197 | .to_slice()? | |
2198 | .iter() | |
2199 | .enumerate() | |
2200 | { | |
2201 | writeln!(w, " Opcode {} has {} args", i + 1, length)?; | |
2202 | } | |
2203 | ||
2204 | let base = if header.version() >= 5 { 0 } else { 1 }; | |
2205 | writeln!(w)?; | |
2206 | writeln!(w, "The Directory Table:")?; | |
2207 | for (i, dir) in header.include_directories().iter().enumerate() { | |
2208 | writeln!( | |
2209 | w, | |
2210 | " {} {}", | |
2211 | base + i, | |
2212 | dwarf.attr_string(unit, dir.clone())?.to_string_lossy()? | |
2213 | )?; | |
2214 | } | |
2215 | ||
2216 | writeln!(w)?; | |
2217 | writeln!(w, "The File Name Table")?; | |
2218 | write!(w, " Entry\tDir\tTime\tSize")?; | |
2219 | if header.file_has_md5() { | |
2220 | write!(w, "\tMD5\t\t\t\t")?; | |
2221 | } | |
2222 | writeln!(w, "\tName")?; | |
2223 | for (i, file) in header.file_names().iter().enumerate() { | |
2224 | write!( | |
2225 | w, | |
2226 | " {}\t{}\t{}\t{}", | |
2227 | base + i, | |
2228 | file.directory_index(), | |
2229 | file.timestamp(), | |
2230 | file.size(), | |
2231 | )?; | |
2232 | if header.file_has_md5() { | |
2233 | let md5 = file.md5(); | |
2234 | write!(w, "\t")?; | |
2235 | for i in 0..16 { | |
2236 | write!(w, "{:02X}", md5[i])?; | |
2237 | } | |
2238 | } | |
2239 | writeln!( | |
2240 | w, | |
2241 | "\t{}", | |
2242 | dwarf | |
2243 | .attr_string(unit, file.path_name())? | |
2244 | .to_string_lossy()? | |
2245 | )?; | |
2246 | } | |
2247 | ||
2248 | writeln!(w)?; | |
2249 | writeln!(w, "Line Number Instructions:")?; | |
2250 | let mut instructions = header.instructions(); | |
2251 | while let Some(instruction) = instructions.next_instruction(header)? { | |
2252 | writeln!(w, " {}", instruction)?; | |
2253 | } | |
2254 | ||
2255 | writeln!(w)?; | |
2256 | writeln!(w, "Line Number Rows:")?; | |
2257 | writeln!(w, "<pc> [lno,col]")?; | |
2258 | } | |
2259 | let mut rows = program.rows(); | |
2260 | let mut file_index = std::u64::MAX; | |
2261 | while let Some((header, row)) = rows.next_row()? { | |
2262 | let line = match row.line() { | |
2263 | Some(line) => line.get(), | |
2264 | None => 0, | |
2265 | }; | |
2266 | let column = match row.column() { | |
2267 | gimli::ColumnType::Column(column) => column.get(), | |
2268 | gimli::ColumnType::LeftEdge => 0, | |
2269 | }; | |
2270 | write!(w, "0x{:08x} [{:4},{:2}]", row.address(), line, column)?; | |
2271 | if row.is_stmt() { | |
2272 | write!(w, " NS")?; | |
2273 | } | |
2274 | if row.basic_block() { | |
2275 | write!(w, " BB")?; | |
2276 | } | |
2277 | if row.end_sequence() { | |
2278 | write!(w, " ET")?; | |
2279 | } | |
2280 | if row.prologue_end() { | |
2281 | write!(w, " PE")?; | |
2282 | } | |
2283 | if row.epilogue_begin() { | |
2284 | write!(w, " EB")?; | |
2285 | } | |
2286 | if row.isa() != 0 { | |
2287 | write!(w, " IS={}", row.isa())?; | |
2288 | } | |
2289 | if row.discriminator() != 0 { | |
2290 | write!(w, " DI={}", row.discriminator())?; | |
2291 | } | |
2292 | if file_index != row.file_index() { | |
2293 | file_index = row.file_index(); | |
2294 | if let Some(file) = row.file(header) { | |
2295 | if let Some(directory) = file.directory(header) { | |
2296 | write!( | |
2297 | w, | |
2298 | " uri: \"{}/{}\"", | |
2299 | dwarf.attr_string(unit, directory)?.to_string_lossy()?, | |
2300 | dwarf | |
2301 | .attr_string(unit, file.path_name())? | |
2302 | .to_string_lossy()? | |
2303 | )?; | |
2304 | } else { | |
2305 | write!( | |
2306 | w, | |
2307 | " uri: \"{}\"", | |
2308 | dwarf | |
2309 | .attr_string(unit, file.path_name())? | |
2310 | .to_string_lossy()? | |
2311 | )?; | |
2312 | } | |
2313 | } | |
2314 | } | |
2315 | writeln!(w)?; | |
2316 | } | |
2317 | } | |
2318 | Ok(()) | |
2319 | } | |
2320 | ||
2321 | fn dump_pubnames<R: Reader, W: Write>( | |
2322 | w: &mut W, | |
2323 | debug_pubnames: &gimli::DebugPubNames<R>, | |
2324 | debug_info: &gimli::DebugInfo<R>, | |
2325 | ) -> Result<()> { | |
2326 | writeln!(w, "\n.debug_pubnames")?; | |
2327 | ||
2328 | let mut cu_offset; | |
2329 | let mut cu_die_offset = gimli::DebugInfoOffset(0); | |
2330 | let mut prev_cu_offset = None; | |
2331 | let mut pubnames = debug_pubnames.items(); | |
2332 | while let Some(pubname) = pubnames.next()? { | |
2333 | cu_offset = pubname.unit_header_offset(); | |
2334 | if Some(cu_offset) != prev_cu_offset { | |
2335 | let cu = debug_info.header_from_offset(cu_offset)?; | |
2336 | cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size()); | |
2337 | prev_cu_offset = Some(cu_offset); | |
2338 | } | |
2339 | let die_in_cu = pubname.die_offset(); | |
2340 | let die_in_sect = cu_offset.0 + die_in_cu.0; | |
2341 | writeln!(w, | |
2342 | "global die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'", | |
2343 | die_in_sect, | |
2344 | cu_die_offset.0, | |
2345 | die_in_cu.0, | |
2346 | cu_offset.0, | |
2347 | pubname.name().to_string_lossy()? | |
2348 | )?; | |
2349 | } | |
2350 | Ok(()) | |
2351 | } | |
2352 | ||
2353 | fn dump_pubtypes<R: Reader, W: Write>( | |
2354 | w: &mut W, | |
2355 | debug_pubtypes: &gimli::DebugPubTypes<R>, | |
2356 | debug_info: &gimli::DebugInfo<R>, | |
2357 | ) -> Result<()> { | |
2358 | writeln!(w, "\n.debug_pubtypes")?; | |
2359 | ||
2360 | let mut cu_offset; | |
2361 | let mut cu_die_offset = gimli::DebugInfoOffset(0); | |
2362 | let mut prev_cu_offset = None; | |
2363 | let mut pubtypes = debug_pubtypes.items(); | |
2364 | while let Some(pubtype) = pubtypes.next()? { | |
2365 | cu_offset = pubtype.unit_header_offset(); | |
2366 | if Some(cu_offset) != prev_cu_offset { | |
2367 | let cu = debug_info.header_from_offset(cu_offset)?; | |
2368 | cu_die_offset = gimli::DebugInfoOffset(cu_offset.0 + cu.header_size()); | |
2369 | prev_cu_offset = Some(cu_offset); | |
2370 | } | |
2371 | let die_in_cu = pubtype.die_offset(); | |
2372 | let die_in_sect = cu_offset.0 + die_in_cu.0; | |
2373 | writeln!(w, | |
2374 | "pubtype die-in-sect 0x{:08x}, cu-in-sect 0x{:08x}, die-in-cu 0x{:08x}, cu-header-in-sect 0x{:08x} '{}'", | |
2375 | die_in_sect, | |
2376 | cu_die_offset.0, | |
2377 | die_in_cu.0, | |
2378 | cu_offset.0, | |
2379 | pubtype.name().to_string_lossy()? | |
2380 | )?; | |
2381 | } | |
2382 | Ok(()) | |
2383 | } | |
2384 | ||
2385 | fn dump_aranges<R: Reader, W: Write>( | |
2386 | w: &mut W, | |
2387 | debug_aranges: &gimli::DebugAranges<R>, | |
2388 | ) -> Result<()> { | |
2389 | writeln!(w, "\n.debug_aranges")?; | |
2390 | ||
2391 | let mut headers = debug_aranges.headers(); | |
2392 | while let Some(header) = headers.next()? { | |
2393 | writeln!( | |
2394 | w, | |
2395 | "Address Range Header: length = 0x{:08x}, version = 0x{:04x}, cu_offset = 0x{:08x}, addr_size = 0x{:02x}, seg_size = 0x{:02x}", | |
2396 | header.length(), | |
2397 | header.encoding().version, | |
2398 | header.debug_info_offset().0, | |
2399 | header.encoding().address_size, | |
2400 | header.segment_size(), | |
2401 | )?; | |
2402 | let mut aranges = header.entries(); | |
2403 | while let Some(arange) = aranges.next()? { | |
2404 | let range = arange.range(); | |
2405 | if let Some(segment) = arange.segment() { | |
2406 | writeln!( | |
2407 | w, | |
2408 | "[0x{:016x}, 0x{:016x}) segment 0x{:x}", | |
2409 | range.begin, range.end, segment | |
2410 | )?; | |
2411 | } else { | |
2412 | writeln!(w, "[0x{:016x}, 0x{:016x})", range.begin, range.end)?; | |
2413 | } | |
2414 | } | |
2415 | } | |
2416 | Ok(()) | |
2417 | } |