diff options
Diffstat (limited to 'vendor/gimli/examples/dwarf-validate.rs')
-rw-r--r-- | vendor/gimli/examples/dwarf-validate.rs | 267 |
1 files changed, 267 insertions, 0 deletions
diff --git a/vendor/gimli/examples/dwarf-validate.rs b/vendor/gimli/examples/dwarf-validate.rs new file mode 100644 index 000000000..1eabccc11 --- /dev/null +++ b/vendor/gimli/examples/dwarf-validate.rs @@ -0,0 +1,267 @@ +// Allow clippy lints when building without clippy. +#![allow(unknown_lints)] + +use gimli::{AttributeValue, UnitHeader}; +use object::{Object, ObjectSection}; +use rayon::prelude::*; +use std::borrow::{Borrow, Cow}; +use std::env; +use std::fs; +use std::io::{self, BufWriter, Write}; +use std::iter::Iterator; +use std::path::{Path, PathBuf}; +use std::process; +use std::sync::Mutex; +use typed_arena::Arena; + +trait Reader: gimli::Reader<Offset = usize> + Send + Sync { + type SyncSendEndian: gimli::Endianity + Send + Sync; +} + +impl<'input, Endian> Reader for gimli::EndianSlice<'input, Endian> +where + Endian: gimli::Endianity + Send + Sync, +{ + type SyncSendEndian = Endian; +} + +struct ErrorWriter<W: Write + Send> { + inner: Mutex<(W, usize)>, + path: PathBuf, +} + +impl<W: Write + Send> ErrorWriter<W> { + #[allow(clippy::needless_pass_by_value)] + fn error(&self, s: String) { + let mut lock = self.inner.lock().unwrap(); + writeln!(&mut lock.0, "DWARF error in {}: {}", self.path.display(), s).unwrap(); + lock.1 += 1; + } +} + +fn main() { + let mut w = BufWriter::new(io::stdout()); + let mut errors = 0; + for arg in env::args_os().skip(1) { + let path = Path::new(&arg); + let file = match fs::File::open(&path) { + Ok(file) => file, + Err(err) => { + eprintln!("Failed to open file '{}': {}", path.display(), err); + errors += 1; + continue; + } + }; + let file = match unsafe { memmap::Mmap::map(&file) } { + Ok(mmap) => mmap, + Err(err) => { + eprintln!("Failed to map file '{}': {}", path.display(), &err); + errors += 1; + continue; + } + }; + let file = match object::File::parse(&*file) { + Ok(file) => file, + Err(err) => { + eprintln!("Failed to parse file '{}': {}", path.display(), err); + errors += 1; + continue; + } + }; + + let endian = if file.is_little_endian() { + gimli::RunTimeEndian::Little + } else { + gimli::RunTimeEndian::Big + }; + let mut error_writer = ErrorWriter { + inner: Mutex::new((&mut w, 0)), + path: path.to_owned(), + }; + validate_file(&mut error_writer, &file, endian); + errors += error_writer.inner.into_inner().unwrap().1; + } + // Flush any errors. + drop(w); + if errors > 0 { + process::exit(1); + } +} + +fn validate_file<W, Endian>(w: &mut ErrorWriter<W>, file: &object::File, endian: Endian) +where + W: Write + Send, + Endian: gimli::Endianity + Send + Sync, +{ + let arena = Arena::new(); + + fn load_section<'a, 'file, 'input, S, Endian>( + arena: &'a Arena<Cow<'file, [u8]>>, + file: &'file object::File<'input>, + endian: Endian, + ) -> S + where + S: gimli::Section<gimli::EndianSlice<'a, Endian>>, + Endian: gimli::Endianity + Send + Sync, + 'file: 'input, + 'a: 'file, + { + let data = match file.section_by_name(S::section_name()) { + Some(ref section) => section + .uncompressed_data() + .unwrap_or(Cow::Borrowed(&[][..])), + None => Cow::Borrowed(&[][..]), + }; + let data_ref = (*arena.alloc(data)).borrow(); + S::from(gimli::EndianSlice::new(data_ref, endian)) + } + + // Variables representing sections of the file. The type of each is inferred from its use in the + // validate_info function below. + let debug_abbrev = &load_section(&arena, file, endian); + let debug_info = &load_section(&arena, file, endian); + + validate_info(w, debug_info, debug_abbrev); +} + +struct UnitSummary { + // True if we successfully parsed all the DIEs and attributes in the compilation unit + internally_valid: bool, + offset: gimli::DebugInfoOffset, + die_offsets: Vec<gimli::UnitOffset>, + global_die_references: Vec<(gimli::UnitOffset, gimli::DebugInfoOffset)>, +} + +fn validate_info<W, R>( + w: &mut ErrorWriter<W>, + debug_info: &gimli::DebugInfo<R>, + debug_abbrev: &gimli::DebugAbbrev<R>, +) where + W: Write + Send, + R: Reader, +{ + let mut units = Vec::new(); + let mut units_iter = debug_info.units(); + let mut last_offset = 0; + loop { + let u = match units_iter.next() { + Err(err) => { + w.error(format!( + "Can't read unit header at offset {:#x}, stopping reading units: {}", + last_offset, err + )); + break; + } + Ok(None) => break, + Ok(Some(u)) => u, + }; + last_offset = u.offset().as_debug_info_offset().unwrap().0 + u.length_including_self(); + units.push(u); + } + let process_unit = |unit: UnitHeader<R>| -> UnitSummary { + let unit_offset = unit.offset().as_debug_info_offset().unwrap(); + let mut ret = UnitSummary { + internally_valid: false, + offset: unit_offset, + die_offsets: Vec::new(), + global_die_references: Vec::new(), + }; + let abbrevs = match unit.abbreviations(debug_abbrev) { + Ok(abbrevs) => abbrevs, + Err(err) => { + w.error(format!( + "Invalid abbrevs for unit {:#x}: {}", + unit_offset.0, &err + )); + return ret; + } + }; + let mut entries = unit.entries(&abbrevs); + let mut unit_refs = Vec::new(); + loop { + let (_, entry) = match entries.next_dfs() { + Err(err) => { + w.error(format!( + "Invalid DIE for unit {:#x}: {}", + unit_offset.0, &err + )); + return ret; + } + Ok(None) => break, + Ok(Some(entry)) => entry, + }; + ret.die_offsets.push(entry.offset()); + + let mut attrs = entry.attrs(); + loop { + let attr = match attrs.next() { + Err(err) => { + w.error(format!( + "Invalid attribute for unit {:#x} at DIE {:#x}: {}", + unit_offset.0, + entry.offset().0, + &err + )); + return ret; + } + Ok(None) => break, + Ok(Some(attr)) => attr, + }; + match attr.value() { + AttributeValue::UnitRef(offset) => { + unit_refs.push((entry.offset(), offset)); + } + AttributeValue::DebugInfoRef(offset) => { + ret.global_die_references.push((entry.offset(), offset)); + } + _ => (), + } + } + } + ret.internally_valid = true; + ret.die_offsets.shrink_to_fit(); + ret.global_die_references.shrink_to_fit(); + + // Check intra-unit references + for (from, to) in unit_refs { + if ret.die_offsets.binary_search(&to).is_err() { + w.error(format!( + "Invalid intra-unit reference in unit {:#x} from DIE {:#x} to {:#x}", + unit_offset.0, from.0, to.0 + )); + } + } + + ret + }; + let processed_units = units.into_par_iter().map(process_unit).collect::<Vec<_>>(); + + let check_unit = |summary: &UnitSummary| { + if !summary.internally_valid { + return; + } + for &(from, to) in summary.global_die_references.iter() { + let u = match processed_units.binary_search_by_key(&to, |v| v.offset) { + Ok(i) => &processed_units[i], + Err(i) => { + if i > 0 { + &processed_units[i - 1] + } else { + w.error(format!("Invalid cross-unit reference in unit {:#x} from DIE {:#x} to global DIE {:#x}: no unit found", + summary.offset.0, from.0, to.0)); + continue; + } + } + }; + if !u.internally_valid { + continue; + } + let to_offset = gimli::UnitOffset(to.0 - u.offset.0); + if u.die_offsets.binary_search(&to_offset).is_err() { + w.error(format!("Invalid cross-unit reference in unit {:#x} from DIE {:#x} to global DIE {:#x}: unit at {:#x} contains no DIE {:#x}", + summary.offset.0, from.0, to.0, u.offset.0, to_offset.0)); + } + } + }; + processed_units.par_iter().for_each(check_unit); +} |