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