From f0011f4221c21eecdca4c7a1bf1a0ee2f2288a32 Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Sat, 7 Aug 2021 15:08:46 +1000 Subject: [PATCH] read/elf: add support for .dynamic sections (#345) Previously we only supported segments. Also add helpers for determining the value type of dynamic entries. --- examples/readobj.rs | 106 +++++++++++++++++++++++++--------------- src/read/elf/dynamic.rs | 53 ++++++++++++++++++++ src/read/elf/section.rs | 40 +++++++++++++++ 3 files changed, 159 insertions(+), 40 deletions(-) diff --git a/examples/readobj.rs b/examples/readobj.rs index 72b2b86c..6cb4ce6d 100644 --- a/examples/readobj.rs +++ b/examples/readobj.rs @@ -443,45 +443,7 @@ mod elf { } } - let proc = match elf.e_machine(endian) { - EM_SPARC => FLAGS_DT_SPARC, - EM_MIPS => FLAGS_DT_MIPS, - EM_ALPHA => FLAGS_DT_ALPHA, - EM_PPC => FLAGS_DT_PPC, - EM_PPC64 => FLAGS_DT_PPC64, - EM_IA_64 => FLAGS_DT_IA_64, - EM_ALTERA_NIOS2 => FLAGS_DT_NIOS2, - _ => &[], - }; - for d in dynamic { - let tag = d.d_tag(endian).into(); - let val = d.d_val(endian).into(); - p.group("Dynamic", |p| { - if let Ok(tag) = tag.try_into() { - p.field_enums("Tag", tag, &[FLAGS_DT, proc]); - if tag == DT_NEEDED { - p.field_string( - "Value", - val, - val.try_into().ok().and_then(|val| dynstr.get(val).ok()), - ); - } else { - p.field_hex("Value", val); - if tag == DT_FLAGS { - p.flags(val, 0, FLAGS_DF); - } else if tag == DT_FLAGS_1 { - p.flags(val, 0, FLAGS_DF_1); - } - } - } else { - p.field_hex("Tag", tag); - p.field_hex("Value", val); - } - }); - if tag == DT_NULL.into() { - break; - } - } + print_dynamic(p, endian, elf, dynamic, dynstr); } } @@ -541,6 +503,7 @@ mod elf { SHT_REL => print_section_rel(p, endian, data, elf, sections, section), SHT_RELA => print_section_rela(p, endian, data, elf, sections, section), SHT_NOTE => print_section_notes(p, endian, data, elf, section), + SHT_DYNAMIC => print_section_dynamic(p, endian, data, elf, sections, section), SHT_GROUP => print_section_group(p, endian, data, elf, sections, section), SHT_HASH => print_hash(p, endian, data, elf, sections, section), SHT_GNU_HASH => print_gnu_hash(p, endian, data, elf, sections, section), @@ -548,7 +511,6 @@ mod elf { SHT_GNU_VERNEED => print_gnu_verneed(p, endian, data, elf, sections, section), SHT_GNU_VERSYM => print_gnu_versym(p, endian, data, elf, sections, section), // TODO: - //SHT_DYNAMIC => //SHT_SHLIB => //SHT_INIT_ARRAY => //SHT_FINI_ARRAY => @@ -755,6 +717,22 @@ mod elf { } } + fn print_section_dynamic( + p: &mut Printer, + endian: Elf::Endian, + data: &[u8], + elf: &Elf, + sections: &SectionTable, + section: &Elf::SectionHeader, + ) { + if let Ok(Some((dynamic, index))) = section.dynamic(endian, data) { + let strings = sections + .strings(endian, data, index) + .unwrap_or(StringTable::default()); + print_dynamic(p, endian, elf, dynamic, strings); + } + } + fn print_section_group( p: &mut Printer, endian: Elf::Endian, @@ -810,6 +788,54 @@ mod elf { } } + fn print_dynamic( + p: &mut Printer, + endian: Elf::Endian, + elf: &Elf, + dynamic: &[Elf::Dyn], + dynstr: StringTable, + ) { + let proc = match elf.e_machine(endian) { + EM_SPARC => FLAGS_DT_SPARC, + EM_MIPS => FLAGS_DT_MIPS, + EM_ALPHA => FLAGS_DT_ALPHA, + EM_PPC => FLAGS_DT_PPC, + EM_PPC64 => FLAGS_DT_PPC64, + EM_IA_64 => FLAGS_DT_IA_64, + EM_ALTERA_NIOS2 => FLAGS_DT_NIOS2, + _ => &[], + }; + for d in dynamic { + let tag = d.d_tag(endian).into(); + let val = d.d_val(endian).into(); + p.group("Dynamic", |p| { + if let Ok(tag) = tag.try_into() { + p.field_enums("Tag", tag, &[FLAGS_DT, proc]); + if d.is_string(endian) { + p.field_string( + "Value", + val, + val.try_into().ok().and_then(|val| dynstr.get(val).ok()), + ); + } else { + p.field_hex("Value", val); + if tag == DT_FLAGS { + p.flags(val, 0, FLAGS_DF); + } else if tag == DT_FLAGS_1 { + p.flags(val, 0, FLAGS_DF_1); + } + } + } else { + p.field_hex("Tag", tag); + p.field_hex("Value", val); + } + }); + if tag == DT_NULL.into() { + break; + } + } + } + fn print_hash( p: &mut Printer, endian: Elf::Endian, diff --git a/src/read/elf/dynamic.rs b/src/read/elf/dynamic.rs index dd7c2557..ba51be55 100644 --- a/src/read/elf/dynamic.rs +++ b/src/read/elf/dynamic.rs @@ -1,3 +1,4 @@ +use core::convert::TryInto; use core::fmt::Debug; use crate::elf; @@ -12,6 +13,58 @@ pub trait Dyn: Debug + Pod { fn d_tag(&self, endian: Self::Endian) -> Self::Word; fn d_val(&self, endian: Self::Endian) -> Self::Word; + + /// Try to convert the tag to a `u32`. + fn tag32(&self, endian: Self::Endian) -> Option { + self.d_tag(endian).into().try_into().ok() + } + + /// Return true if the value is an offset in the dynamic string table. + fn is_string(&self, endian: Self::Endian) -> bool { + if let Some(tag) = self.tag32(endian) { + match tag { + elf::DT_NEEDED + | elf::DT_SONAME + | elf::DT_RPATH + | elf::DT_RUNPATH + | elf::DT_AUXILIARY + | elf::DT_FILTER => true, + _ => false, + } + } else { + false + } + } + + /// Return true if the value is an address. + fn is_address(&self, endian: Self::Endian) -> bool { + if let Some(tag) = self.tag32(endian) { + match tag { + elf::DT_PLTGOT + | elf::DT_HASH + | elf::DT_STRTAB + | elf::DT_SYMTAB + | elf::DT_RELA + | elf::DT_INIT + | elf::DT_FINI + | elf::DT_SYMBOLIC + | elf::DT_REL + | elf::DT_DEBUG + | elf::DT_JMPREL + | elf::DT_FINI_ARRAY + | elf::DT_INIT_ARRAY + | elf::DT_PREINIT_ARRAY + | elf::DT_SYMTAB_SHNDX + | elf::DT_VERDEF + | elf::DT_VERNEED + | elf::DT_VERSYM + | elf::DT_ADDRRNGLO..=elf::DT_ADDRRNGHI => true, + _ => false, + } + } else { + false + } + } } impl Dyn for elf::Dyn32 { diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index 6e1a6ff6..fa99581d 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -148,6 +148,25 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> { RelocationSections::parse(endian, self, symbol_section) } + /// Return the contents of a dynamic section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if there is no `SHT_DYNAMIC` section. + /// Returns `Err` for invalid values. + pub fn dynamic( + &self, + endian: Elf::Endian, + data: R, + ) -> read::Result> { + for section in self.sections { + if let Some(dynamic) = section.dynamic(endian, data)? { + return Ok(Some(dynamic)); + } + } + Ok(None) + } + /// Return the header of a SysV hash section. /// /// Returns `Ok(None)` if there is no SysV GNU hash section. @@ -726,6 +745,27 @@ pub trait SectionHeader: Debug + Pod { Ok(Some((rela, link))) } + /// Return entries in a dynamic section. + /// + /// Also returns the linked string table index. + /// + /// Returns `Ok(None)` if the section type is not `SHT_DYNAMIC`. + /// Returns `Err` for invalid values. + fn dynamic<'data, R: ReadRef<'data>>( + &self, + endian: Self::Endian, + data: R, + ) -> read::Result::Dyn], SectionIndex)>> { + if self.sh_type(endian) != elf::SHT_DYNAMIC { + return Ok(None); + } + let dynamic = self + .data_as_array(endian, data) + .read_error("Invalid ELF dynamic section offset or size")?; + let link = SectionIndex(self.sh_link(endian) as usize); + Ok(Some((dynamic, link))) + } + /// Return a note iterator for the section data. /// /// Returns `Ok(None)` if the section does not contain notes.