Skip to content

Commit

Permalink
read/elf: add VersionTable
Browse files Browse the repository at this point in the history
This allows looking up symbol versions.  Used in:
- hash table find functions
- readobj example output
  • Loading branch information
philipc committed Aug 7, 2021
1 parent 7c4360a commit ff64ae2
Show file tree
Hide file tree
Showing 5 changed files with 308 additions and 42 deletions.
94 changes: 66 additions & 28 deletions examples/readobj.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,11 @@ mod elf {
section: &Elf::SectionHeader,
) {
if let Ok(Some(symbols)) = section.symbols(endian, data, sections, section_index) {
let versions = if section.sh_type(endian) == SHT_DYNSYM {
sections.versions(endian, data).ok()
} else {
None
};
let os_stt = match elf.e_ident().os_abi {
ELFOSABI_GNU => FLAGS_STT_GNU,
ELFOSABI_HPUX => FLAGS_STT_HP,
Expand Down Expand Up @@ -601,6 +606,13 @@ mod elf {
symbol.st_name(endian),
symbol.name(endian, symbols.strings()).ok(),
);
if let Some(versions) = versions.as_ref() {
let version_index = versions.version_index(endian, index);
if let Some(version) = versions.version(version_index) {
p.field_string("Version", version_index.0, Some(version.name()));
p.flags(version_index.0, 0, FLAGS_VERSYM);
}
}
p.field_hex("Value", symbol.st_value(endian).into());
p.field_hex("Size", symbol.st_size(endian).into());
p.field_enums("Type", symbol.st_type(), &[FLAGS_STT, os_stt, proc_stt]);
Expand Down Expand Up @@ -813,17 +825,23 @@ mod elf {
});
}
/* TODO: add this in a test somewhere
if let Ok(Some(hash_table)) = section.hash(endian, data) {
if let Ok(symbols) = _sections.symbols(endian, data, SHT_DYNSYM) {
for symbol in symbols.symbols() {
let name = symbols.symbol_name(endian, symbol).unwrap();
if !symbol.is_definition(endian) {
continue;
if let Ok(Some((hash_table, link))) = section.hash(endian, data) {
if let Ok(symbols) = _sections.symbol_table_by_index(endian, data, link) {
if let Ok(versions) = _sections.versions(endian, data) {
for (index, symbol) in symbols.symbols().iter().enumerate() {
let name = symbols.symbol_name(endian, symbol).unwrap();
if !symbol.is_definition(endian) {
continue;
}
let hash = hash(name);
let version = versions.version(versions.version_index(endian, index));
let (hash_index, hash_symbol) = hash_table
.find(endian, name, hash, version, &symbols, &versions)
.unwrap();
let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap();
assert_eq!(name, hash_name);
assert_eq!(index, hash_index);
}
let hash = hash(name);
let hash_symbol = hash_table.find(endian, name, hash, &symbols).unwrap();
let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap();
assert_eq!(name, hash_name);
}
}
}
Expand All @@ -847,14 +865,25 @@ mod elf {
});
}
/* TODO: add this in a test somewhere
if let Ok(Some(hash_table)) = section.gnu_hash(endian, data) {
if let Ok(symbols) = _sections.symbols(endian, data, SHT_DYNSYM) {
for symbol in &symbols.symbols()[hash_table.symbol_base() as usize..] {
let name = symbols.symbol_name(endian, symbol).unwrap();
let hash = gnu_hash(name);
let hash_symbol = hash_table.find(endian, name, hash, &symbols).unwrap();
let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap();
assert_eq!(name, hash_name);
if let Ok(Some((hash_table, link))) = section.gnu_hash(endian, data) {
if let Ok(symbols) = _sections.symbol_table_by_index(endian, data, link) {
if let Ok(versions) = _sections.versions(endian, data) {
for (index, symbol) in symbols
.symbols()
.iter()
.enumerate()
.skip(hash_table.symbol_base() as usize)
{
let name = symbols.symbol_name(endian, symbol).unwrap();
let hash = gnu_hash(name);
let version = versions.version(versions.version_index(endian, index));
let (hash_index, hash_symbol) = hash_table
.find(endian, name, hash, version, &symbols, &versions)
.unwrap();
let hash_name = symbols.symbol_name(endian, hash_symbol).unwrap();
assert_eq!(name, hash_name);
assert_eq!(index, hash_index);
}
}
}
}
Expand All @@ -878,8 +907,7 @@ mod elf {
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("Index", verdef.vd_ndx.get(endian));
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));
Expand Down Expand Up @@ -927,8 +955,7 @@ mod elf {
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("Index", vernaux.vna_other.get(endian));
p.field_string(
"Name",
vernaux.vna_name.get(endian),
Expand All @@ -947,15 +974,25 @@ mod elf {
endian: Elf::Endian,
data: &[u8],
_elf: &Elf,
_sections: &SectionTable<Elf>,
sections: &SectionTable<Elf>,
section: &Elf::SectionHeader,
) {
if let Ok(Some((syms, _link))) = section.gnu_versym(endian, data) {
for sym in syms {
let versions = sections.versions(endian, data).ok();
for (index, sym) in syms.iter().enumerate() {
let sym = VersionIndex(sym.0.get(endian));
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
p.field("Index", index);
if sym.0 <= VER_NDX_GLOBAL {
p.field_enum("Version", sym.0, FLAGS_VER_NDX);
} else {
let name = versions
.as_ref()
.and_then(|versions| versions.version(sym))
.map(|version| version.name());
p.field_string("Version", sym.0, name);
};
p.flags(sym.0, 0, FLAGS_VERSYM);
});
}
}
Expand Down Expand Up @@ -3360,7 +3397,8 @@ mod elf {
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);
static FLAGS_VER_NDX: &[Flag<u16>] = &flags!(VER_NDX_LOCAL, VER_NDX_GLOBAL);
static FLAGS_VERSYM: &[Flag<u16>] = &flags!(VERSYM_HIDDEN);
}

mod macho {
Expand Down
10 changes: 7 additions & 3 deletions src/elf.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1694,6 +1694,11 @@ pub const DF_1_PIE: u32 = 0x0800_0000;
#[repr(C)]
pub struct Versym<E: Endian>(pub U16<E>);

/// Symbol is hidden.
pub const VERSYM_HIDDEN: u16 = 0x8000;
/// Symbol version index.
pub const VERSYM_VERSION: u16 = 0x7fff;

/// Version definition sections
#[derive(Debug, Clone, Copy)]
#[repr(C)]
Expand All @@ -1720,9 +1725,10 @@ 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).
// Legal values for vd_flags (version information flags).
/// Version definition of file itself
pub const VER_FLG_BASE: u16 = 0x1;
// Legal values for vd_flags and vna_flags (version information flags).
/// Weak version identifier
pub const VER_FLG_WEAK: u16 = 0x2;

Expand All @@ -1731,8 +1737,6 @@ pub const VER_FLG_WEAK: u16 = 0x2;
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)]
Expand Down
29 changes: 19 additions & 10 deletions src/read/elf/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::elf;
use crate::read::{ReadError, ReadRef, Result};
use crate::{U32, U64};

use super::{FileHeader, Sym, SymbolTable};
use super::{FileHeader, Sym, SymbolTable, Version, VersionTable};

/// A SysV symbol hash table in an ELF file.
#[derive(Debug)]
Expand Down Expand Up @@ -39,23 +39,27 @@ impl<'data, Elf: FileHeader> HashTable<'data, Elf> {
self.chains.len() as u32
}

/// Use the hash table to find the symbol table entry with the given name and hash.
/// Use the hash table to find the symbol table entry with the given name, hash and version.
pub fn find<R: ReadRef<'data>>(
&self,
endian: Elf::Endian,
name: &[u8],
hash: u32,
version: Option<&Version>,
symbols: &SymbolTable<'data, Elf, R>,
) -> Option<&'data Elf::Sym> {
versions: &VersionTable<'data, Elf>,
) -> Option<(usize, &'data Elf::Sym)> {
// Get the chain start from the bucket for this hash.
let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize;
// Avoid infinite loop.
let mut i = 0;
let strings = symbols.strings();
while index != 0 && i < self.chains.len() {
if let Ok(symbol) = symbols.symbol(index) {
if symbol.name(endian, strings) == Ok(name) {
return Some(symbol);
if symbol.name(endian, strings) == Ok(name)
&& versions.matches(endian, index, version)
{
return Some((index, symbol));
}
}
index = self.chains.get(index)?.get(endian) as usize;
Expand Down Expand Up @@ -150,14 +154,16 @@ impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> {
None
}

/// Use the hash table to find the symbol table entry with the given name and hash.
/// Use the hash table to find the symbol table entry with the given name, hash, and version.
pub fn find<R: ReadRef<'data>>(
&self,
endian: Elf::Endian,
name: &[u8],
hash: u32,
version: Option<&Version>,
symbols: &SymbolTable<'data, Elf, R>,
) -> Option<&'data Elf::Sym> {
versions: &VersionTable<'data, Elf>,
) -> Option<(usize, &'data Elf::Sym)> {
let word_bits = mem::size_of::<Elf::Word>() as u32 * 8;

// Test against bloom filter.
Expand All @@ -184,7 +190,7 @@ impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> {
}

// Get the chain start from the bucket for this hash.
let index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize;
let mut index = self.buckets[(hash as usize) % self.buckets.len()].get(endian) as usize;
if index == 0 {
return None;
}
Expand All @@ -198,13 +204,16 @@ impl<'data, Elf: FileHeader> GnuHashTable<'data, Elf> {
for (symbol, value) in symbols.iter().zip(values.iter()) {
let value = value.get(endian);
if value | 1 == hash | 1 {
if symbol.name(endian, strings) == Ok(name) {
return Some(symbol);
if symbol.name(endian, strings) == Ok(name)
&& versions.matches(endian, index, version)
{
return Some((index, symbol));
}
}
if value & 1 != 0 {
break;
}
index += 1;
}
None
}
Expand Down
18 changes: 17 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, VerdefIterator, VerneedIterator,
NoteIterator, RelocationSections, SymbolTable, VerdefIterator, VerneedIterator, VersionTable,
};

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

/// Returns the symbol version table.
///
/// Returns an empty table if symbol versions are not present.
pub fn versions(&self, endian: Elf::Endian, data: R) -> read::Result<VersionTable<'data, Elf>> {
let (versyms, strings) = match self.gnu_versym(endian, data)? {
Some((versyms, link)) => (
versyms,
self.symbol_table_by_index(endian, data, link)?.strings(),
),
None => return Ok(VersionTable::default()),
};
let verdefs = self.gnu_verdef(endian, data)?.map(|x| x.0);
let verneeds = self.gnu_verneed(endian, data)?.map(|x| x.0);
VersionTable::new(endian, versyms, verdefs, verneeds, strings)
}
}

/// An iterator over the sections of an `ElfFile32`.
Expand Down
Loading

0 comments on commit ff64ae2

Please sign in to comment.