Skip to content

Commit

Permalink
read/elf: add support for .dynamic sections (gimli-rs#345)
Browse files Browse the repository at this point in the history
Previously we only supported segments.

Also add helpers for determining the value type of dynamic entries.
  • Loading branch information
philipc authored Aug 7, 2021
1 parent 882a29e commit f1f15b9
Show file tree
Hide file tree
Showing 3 changed files with 159 additions and 40 deletions.
106 changes: 66 additions & 40 deletions examples/readobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}

Expand Down Expand Up @@ -541,14 +503,14 @@ 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),
SHT_GNU_VERDEF => print_gnu_verdef(p, endian, data, elf, sections, section),
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 =>
Expand Down Expand Up @@ -755,6 +717,22 @@ mod elf {
}
}

fn print_section_dynamic<Elf: FileHeader>(
p: &mut Printer<impl Write>,
endian: Elf::Endian,
data: &[u8],
elf: &Elf,
sections: &SectionTable<Elf>,
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<Elf: FileHeader>(
p: &mut Printer<impl Write>,
endian: Elf::Endian,
Expand Down Expand Up @@ -810,6 +788,54 @@ mod elf {
}
}

fn print_dynamic<Elf: FileHeader>(
p: &mut Printer<impl Write>,
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<Elf: FileHeader>(
p: &mut Printer<impl Write>,
endian: Elf::Endian,
Expand Down
53 changes: 53 additions & 0 deletions src/read/elf/dynamic.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use core::convert::TryInto;
use core::fmt::Debug;

use crate::elf;
Expand All @@ -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<u32> {
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<Endian: endian::Endian> Dyn for elf::Dyn32<Endian> {
Expand Down
40 changes: 40 additions & 0 deletions src/read/elf/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Option<(&'data [Elf::Dyn], SectionIndex)>> {
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.
Expand Down Expand Up @@ -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<Option<(&'data [<Self::Elf as FileHeader>::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.
Expand Down

0 comments on commit f1f15b9

Please sign in to comment.