summaryrefslogtreecommitdiffstats
path: root/vendor/gimli/examples/dwarf-validate.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gimli/examples/dwarf-validate.rs')
-rw-r--r--vendor/gimli/examples/dwarf-validate.rs267
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);
+}