Skip to content

Commit

Permalink
read/elf: symbol versions
Browse files Browse the repository at this point in the history
  • Loading branch information
philipc committed Jul 21, 2021
1 parent bc43a6f commit 3be242c
Show file tree
Hide file tree
Showing 4 changed files with 295 additions and 5 deletions.
89 changes: 89 additions & 0 deletions examples/readobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -542,6 +542,9 @@ mod elf {
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 =>
Expand Down Expand Up @@ -856,6 +859,90 @@ mod elf {
*/
}

fn print_gnu_verdef<Elf: FileHeader>(
p: &mut Printer<impl Write>,
endian: Elf::Endian,
data: &[u8],
_elf: &Elf,
_sections: &SectionTable<Elf>,
section: &Elf::SectionHeader,
) {
if let Ok(Some(mut verdefs)) = section.gnu_verdef(endian, data) {
while let Ok(Some((verdef, mut verdauxs))) = verdefs.next() {
p.group("VersionDefinition", |p| {
// TODO: names
p.field("Version", verdef.vd_version.get(endian));
p.field_hex("Flags", verdef.vd_flags.get(endian));
p.flags(verdef.vd_flags.get(endian), 0, FLAGS_VER_FLG);
p.field("Index", verdef.vd_ndx.get(endian) & !VER_NDX_HIDDEN);
p.flags(verdef.vd_ndx.get(endian), 0, FLAGS_VER_NDX);
p.field("AuxCount", verdef.vd_cnt.get(endian));
p.field_hex("Hash", verdef.vd_hash.get(endian));
p.field("AuxOffset", verdef.vd_aux.get(endian));
p.field("NextOffset", verdef.vd_next.get(endian));
while let Ok(Some(verdaux)) = verdauxs.next() {
p.group("Aux", |p| {
p.field_hex("Name", verdaux.vda_name.get(endian));
p.field("NextOffset", verdaux.vda_next.get(endian));
});
}
});
}
}
}

fn print_gnu_verneed<Elf: FileHeader>(
p: &mut Printer<impl Write>,
endian: Elf::Endian,
data: &[u8],
_elf: &Elf,
_sections: &SectionTable<Elf>,
section: &Elf::SectionHeader,
) {
if let Ok(Some(mut verneeds)) = section.gnu_verneed(endian, data) {
while let Ok(Some((verneed, mut vernauxs))) = verneeds.next() {
p.group("VersionNeed", |p| {
// TODO: names
p.field("Version", verneed.vn_version.get(endian));
p.field("AuxCount", verneed.vn_cnt.get(endian));
p.field_hex("Filename", verneed.vn_file.get(endian));
p.field("AuxOffset", verneed.vn_aux.get(endian));
p.field("NextOffset", verneed.vn_next.get(endian));
while let Ok(Some(vernaux)) = vernauxs.next() {
p.group("Aux", |p| {
p.field_hex("Hash", vernaux.vna_hash.get(endian));
p.field_hex("Flags", vernaux.vna_flags.get(endian));
p.flags(vernaux.vna_flags.get(endian), 0, FLAGS_VER_FLG);
p.field("Index", vernaux.vna_other.get(endian) & !VER_NDX_HIDDEN);
p.flags(vernaux.vna_other.get(endian), 0, FLAGS_VER_NDX);
p.field_hex("Name", vernaux.vna_name.get(endian));
p.field("NextOffset", vernaux.vna_next.get(endian));
});
}
});
}
}
}

fn print_gnu_versym<Elf: FileHeader>(
p: &mut Printer<impl Write>,
endian: Elf::Endian,
data: &[u8],
_elf: &Elf,
_sections: &SectionTable<Elf>,
section: &Elf::SectionHeader,
) {
if let Ok(Some(syms)) = section.gnu_versym(endian, data) {
for sym in syms {
p.group("VersionSymbol", |p| {
p.field("Version", sym.0.get(endian) & !VER_NDX_HIDDEN);
p.flags(sym.0.get(endian), 0, FLAGS_VER_NDX);
// TODO: version name
});
}
}
}

static FLAGS_EI_CLASS: &[Flag<u8>] = &flags!(ELFCLASSNONE, ELFCLASS32, ELFCLASS64);
static FLAGS_EI_DATA: &[Flag<u8>] = &flags!(ELFDATANONE, ELFDATA2LSB, ELFDATA2MSB);
static FLAGS_EV: &[Flag<u8>] = &flags!(EV_NONE, EV_CURRENT);
Expand Down Expand Up @@ -3254,6 +3341,8 @@ mod elf {
DF_1_STUB,
DF_1_PIE,
);
static FLAGS_VER_FLG: &[Flag<u16>] = &flags!(VER_FLG_BASE, VER_FLG_WEAK);
static FLAGS_VER_NDX: &[Flag<u16>] = &flags!(VER_NDX_HIDDEN);
}

mod macho {
Expand Down
102 changes: 98 additions & 4 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1696,10 +1696,99 @@ pub const DF_1_STUB: u32 = 0x0400_0000;
#[allow(missing_docs)]
pub const DF_1_PIE: u32 = 0x0800_0000;

// TODO: ELF*_Verdef, VER_DEF_*, VER_FLG_*, VER_NDX_*
// TODO: Elf*_Verdaux
// TODO: Elf*_Verneed, VER_NEED_*
// TODO: Elf*_Vernaux, VER_FLG_*
/// Version symbol information
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Versym<E: Endian>(pub U16<E>);

/// Version definition sections
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Verdef<E: Endian> {
/// Version revision
pub vd_version: U16<E>,
/// Version information
pub vd_flags: U16<E>,
/// Version Index
pub vd_ndx: U16<E>,
/// Number of associated aux entries
pub vd_cnt: U16<E>,
/// Version name hash value
pub vd_hash: U32<E>,
/// Offset in bytes to verdaux array
pub vd_aux: U32<E>,
/// Offset in bytes to next verdef entry
pub vd_next: U32<E>,
}

// Legal values for vd_version (version revision).
/// No version
pub const VER_DEF_NONE: u16 = 0;
/// Current version
pub const VER_DEF_CURRENT: u16 = 1;

// Legal values for vd_flags and vna_flags (version information flags).
/// Version definition of file itself
pub const VER_FLG_BASE: u16 = 0x1;
/// Weak version identifier
pub const VER_FLG_WEAK: u16 = 0x2;

// Versym symbol index values.
/// Symbol is local.
pub const VER_NDX_LOCAL: u16 = 0;
/// Symbol is global.
pub const VER_NDX_GLOBAL: u16 = 1;
/// Symbol is hidden.
pub const VER_NDX_HIDDEN: u16 = 0x8000;

/// Auxiliary version information.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Verdaux<E: Endian> {
/// Version or dependency names
pub vda_name: U32<E>,
/// Offset in bytes to next verdaux
pub vda_next: U32<E>,
}

/// Version dependency.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Verneed<E: Endian> {
/// Version of structure
pub vn_version: U16<E>,
/// Number of associated aux entries
pub vn_cnt: U16<E>,
/// Offset of filename for this dependency
pub vn_file: U32<E>,
/// Offset in bytes to vernaux array
pub vn_aux: U32<E>,
/// Offset in bytes to next verneed entry
pub vn_next: U32<E>,
}

// Legal values for vn_version (version revision).
/// No version
pub const VER_NEED_NONE: u16 = 0;
/// Current version
pub const VER_NEED_CURRENT: u16 = 1;

/// Auxiliary needed version information.
#[derive(Debug, Clone, Copy)]
#[repr(C)]
pub struct Vernaux<E: Endian> {
/// Hash value of dependency name
pub vna_hash: U32<E>,
/// Dependency specific information
pub vna_flags: U16<E>,
/// Version Index
pub vna_other: U16<E>,
/// Dependency name string offset
pub vna_name: U32<E>,
/// Offset in bytes to next vernaux entry
pub vna_next: U32<E>,
}

// TODO: Elf*_auxv_t, AT_*

/// Note section entry header.
Expand Down Expand Up @@ -6154,6 +6243,11 @@ unsafe_impl_endian_pod!(
ProgramHeader64,
Dyn32,
Dyn64,
Versym,
Verdef,
Verdaux,
Verneed,
Vernaux,
NoteHeader32,
NoteHeader64,
HashHeader,
Expand Down
3 changes: 3 additions & 0 deletions src/read/elf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,6 @@ pub use note::*;

mod hash;
pub use hash::*;

mod version;
pub use version::*;
106 changes: 105 additions & 1 deletion src/read/elf/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::read::{

use super::{
CompressionHeader, ElfFile, ElfSectionRelocationIterator, FileHeader, GnuHashTable, HashTable,
NoteIterator, RelocationSections, SymbolTable,
NoteIterator, RelocationSections, SymbolTable, VerdefIterator, VerneedIterator,
};

/// The table of section headers in an ELF file.
Expand Down Expand Up @@ -200,6 +200,57 @@ impl<'data, Elf: FileHeader, R: ReadRef<'data>> SectionTable<'data, Elf, R> {
}
Ok(None)
}

/// Return the contents of a `SHT_GNU_VERSYM` section.
///
/// Returns `Ok(None)` if there is no `SHT_GNU_VERSYM` section.
/// Returns `Err` for invalid values.
pub fn gnu_versym(
&self,
endian: Elf::Endian,
data: R,
) -> read::Result<Option<&'data [elf::Versym<Elf::Endian>]>> {
for section in self.sections {
if let Some(syms) = section.gnu_versym(endian, data)? {
return Ok(Some(syms));
}
}
Ok(None)
}

/// Return the contents of a `SHT_GNU_VERDEF` section.
///
/// Returns `Ok(None)` if there is no `SHT_GNU_VERDEF` section.
/// Returns `Err` for invalid values.
pub fn gnu_verdef(
&self,
endian: Elf::Endian,
data: R,
) -> read::Result<Option<VerdefIterator<'data, Elf>>> {
for section in self.sections {
if let Some(defs) = section.gnu_verdef(endian, data)? {
return Ok(Some(defs));
}
}
Ok(None)
}

/// Return the contents of a `SHT_GNU_VERNEED` section.
///
/// Returns `Ok(None)` if there is no `SHT_GNU_VERNEED` section.
/// Returns `Err` for invalid values.
pub fn gnu_verneed(
&self,
endian: Elf::Endian,
data: R,
) -> read::Result<Option<VerneedIterator<'data, Elf>>> {
for section in self.sections {
if let Some(needs) = section.gnu_verneed(endian, data)? {
return Ok(Some(needs));
}
}
Ok(None)
}
}

/// An iterator over the sections of an `ElfFile32`.
Expand Down Expand Up @@ -751,6 +802,59 @@ pub trait SectionHeader: Debug + Pod {
let hash = GnuHashTable::parse(endian, data)?;
Ok(Some(hash))
}

/// Return the contents of a `SHT_GNU_VERSYM` section.
///
/// Returns `Ok(None)` if the section type is not `SHT_GNU_VERSYM`.
/// Returns `Err` for invalid values.
fn gnu_versym<'data, R: ReadRef<'data>>(
&self,
endian: Self::Endian,
data: R,
) -> read::Result<Option<&'data [elf::Versym<Self::Endian>]>> {
if self.sh_type(endian) != elf::SHT_GNU_VERSYM {
return Ok(None);
}
self.data_as_array(endian, data)
.read_error("Invalid ELF GNU versym section offset or size")
.map(Some)
}

/// Return an iterator for the entries of a `SHT_GNU_VERDEF` section.
///
/// Returns `Ok(None)` if the section type is not `SHT_GNU_VERDEF`.
/// Returns `Err` for invalid values.
fn gnu_verdef<'data, R: ReadRef<'data>>(
&self,
endian: Self::Endian,
data: R,
) -> read::Result<Option<VerdefIterator<'data, Self::Elf>>> {
if self.sh_type(endian) != elf::SHT_GNU_VERDEF {
return Ok(None);
}
let data = self
.data_as_array(endian, data)
.read_error("Invalid ELF GNU verdef section offset or size")?;
Ok(Some(VerdefIterator::new(endian, data)))
}

/// Return an iterator for the entries of a `SHT_GNU_VERNEED` section.
///
/// Returns `Ok(None)` if the section type is not `SHT_GNU_VERNEED`.
/// Returns `Err` for invalid values.
fn gnu_verneed<'data, R: ReadRef<'data>>(
&self,
endian: Self::Endian,
data: R,
) -> read::Result<Option<VerneedIterator<'data, Self::Elf>>> {
if self.sh_type(endian) != elf::SHT_GNU_VERNEED {
return Ok(None);
}
let data = self
.data_as_array(endian, data)
.read_error("Invalid ELF GNU verneed section offset or size")?;
Ok(Some(VerneedIterator::new(endian, data)))
}
}

impl<Endian: endian::Endian> SectionHeader for elf::SectionHeader32<Endian> {
Expand Down

0 comments on commit 3be242c

Please sign in to comment.