1588 lines
51 KiB
Rust
1588 lines
51 KiB
Rust
//! Parsing GNU extension sections for dynamic symbol versioning `.gnu.version.*`
|
|
use crate::abi;
|
|
use crate::endian::EndianParse;
|
|
use crate::file::Class;
|
|
use crate::parse::{ParseAt, ParseError, ParsingTable};
|
|
use crate::string_table::StringTable;
|
|
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct SymbolRequirement<'data> {
|
|
pub file: &'data str,
|
|
pub name: &'data str,
|
|
pub hash: u32,
|
|
pub flags: u16,
|
|
pub hidden: bool,
|
|
}
|
|
|
|
pub struct SymbolDefinition<'data, E: EndianParse> {
|
|
pub hash: u32,
|
|
pub flags: u16,
|
|
pub names: SymbolNamesIterator<'data, E>,
|
|
pub hidden: bool,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct SymbolNamesIterator<'data, E: EndianParse> {
|
|
vda_iter: VerDefAuxIterator<'data, E>,
|
|
strtab: &'data StringTable<'data>,
|
|
}
|
|
|
|
impl<'data, E: EndianParse> SymbolNamesIterator<'data, E> {
|
|
pub fn new(vda_iter: VerDefAuxIterator<'data, E>, strtab: &'data StringTable<'data>) -> Self {
|
|
SymbolNamesIterator { vda_iter, strtab }
|
|
}
|
|
}
|
|
|
|
impl<'data, E: EndianParse> Iterator for SymbolNamesIterator<'data, E> {
|
|
type Item = Result<&'data str, ParseError>;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
let vda = self.vda_iter.next();
|
|
match vda {
|
|
Some(vda) => Some(self.strtab.get(vda.vda_name as usize)),
|
|
None => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct SymbolVersionTable<'data, E: EndianParse> {
|
|
version_ids: VersionIndexTable<'data, E>,
|
|
|
|
verneeds: Option<(VerNeedIterator<'data, E>, StringTable<'data>)>,
|
|
verdefs: Option<(VerDefIterator<'data, E>, StringTable<'data>)>,
|
|
}
|
|
|
|
impl<'data, E: EndianParse> SymbolVersionTable<'data, E> {
|
|
pub fn new(
|
|
version_ids: VersionIndexTable<'data, E>,
|
|
verneeds: Option<(VerNeedIterator<'data, E>, StringTable<'data>)>,
|
|
verdefs: Option<(VerDefIterator<'data, E>, StringTable<'data>)>,
|
|
) -> Self {
|
|
SymbolVersionTable {
|
|
version_ids,
|
|
verneeds,
|
|
verdefs,
|
|
}
|
|
}
|
|
|
|
pub fn get_requirement(&self, sym_idx: usize) -> Result<Option<SymbolRequirement>, ParseError> {
|
|
let (verneeds, verneed_strs) = match self.verneeds {
|
|
Some(verneeds) => verneeds,
|
|
None => {
|
|
return Ok(None);
|
|
}
|
|
};
|
|
|
|
let ver_ndx = self.version_ids.get(sym_idx)?;
|
|
let iter = verneeds;
|
|
for (vn, vna_iter) in iter {
|
|
for vna in vna_iter {
|
|
if vna.vna_other != ver_ndx.index() {
|
|
continue;
|
|
}
|
|
|
|
let file = verneed_strs.get(vn.vn_file as usize)?;
|
|
let name = verneed_strs.get(vna.vna_name as usize)?;
|
|
let hash = vna.vna_hash;
|
|
let hidden = ver_ndx.is_hidden();
|
|
return Ok(Some(SymbolRequirement {
|
|
file,
|
|
name,
|
|
hash,
|
|
flags: vna.vna_flags,
|
|
hidden,
|
|
}));
|
|
}
|
|
}
|
|
|
|
// Maybe we should treat this as a ParseError instead of returning an
|
|
// empty Option? This can only happen if .gnu.versions[N] contains an
|
|
// index that doesn't exist, which is likely a file corruption or
|
|
// programmer error (i.e asking for a requirement for a defined symbol)
|
|
Ok(None)
|
|
}
|
|
|
|
pub fn get_definition(
|
|
&self,
|
|
sym_idx: usize,
|
|
) -> Result<Option<SymbolDefinition<E>>, ParseError> {
|
|
let (ref verdefs, ref verdef_strs) = match self.verdefs {
|
|
Some(ref verdefs) => verdefs,
|
|
None => {
|
|
return Ok(None);
|
|
}
|
|
};
|
|
|
|
let ver_ndx = self.version_ids.get(sym_idx)?;
|
|
let iter = *verdefs;
|
|
for (vd, vda_iter) in iter {
|
|
if vd.vd_ndx != ver_ndx.index() {
|
|
continue;
|
|
}
|
|
|
|
let flags = vd.vd_flags;
|
|
let hash = vd.vd_hash;
|
|
let hidden = ver_ndx.is_hidden();
|
|
return Ok(Some(SymbolDefinition {
|
|
hash,
|
|
flags,
|
|
names: SymbolNamesIterator {
|
|
vda_iter,
|
|
strtab: verdef_strs,
|
|
},
|
|
hidden,
|
|
}));
|
|
}
|
|
|
|
// Maybe we should treat this as a ParseError instead of returning an
|
|
// empty Option? This can only happen if .gnu.versions[N] contains an
|
|
// index that doesn't exist, which is likely a file corruption or
|
|
// programmer error (i.e asking for a definition for an undefined symbol)
|
|
Ok(None)
|
|
}
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// _ //
|
|
// __ _ _ __ _ _ __ _____ _ __ ___(_) ___ _ __ //
|
|
// / _` | '_ \| | | | \ \ / / _ \ '__/ __| |/ _ \| '_ \ //
|
|
// _ | (_| | | | | |_| | _ \ V / __/ | \__ \ | (_) | | | | //
|
|
// (_) \__, |_| |_|\__,_| (_) \_/ \___|_| |___/_|\___/|_| |_| //
|
|
// |___/ //
|
|
////////////////////////////////////////////////////////////////////
|
|
|
|
pub type VersionIndexTable<'data, E> = ParsingTable<'data, E, VersionIndex>;
|
|
|
|
/// The special GNU extension section .gnu.version has a section type of SHT_GNU_VERSYM.
|
|
/// This section shall have the same number of entries as the Dynamic Symbol Table in
|
|
/// the .dynsym section. The .gnu.version section shall contain an array of
|
|
/// elements of type Elfxx_Half (both of which are 16-bit unsigned integers).
|
|
///
|
|
/// The .gnu.version section and VersionIndex values act as a lookup table for specifying
|
|
/// the version defined for or required by the corresponding symbol in the Dynamic Symbol Table.
|
|
///
|
|
/// For example, the symbol at index N in the .dynsym Symbol Table will have a VersionIndex
|
|
/// value located in the versym table at .gnu.version\[N\] which identifies
|
|
/// structures in the .gnu.version_d and .gnu.version_r sections. These values
|
|
/// are located in identifiers provided by the the vna_other member of the VerNeedAux
|
|
/// structure or the vd_ndx member of the VerDef structure.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct VersionIndex(pub u16);
|
|
|
|
impl VersionIndex {
|
|
pub fn index(&self) -> u16 {
|
|
self.0 & abi::VER_NDX_VERSION
|
|
}
|
|
|
|
pub fn is_local(&self) -> bool {
|
|
self.index() == abi::VER_NDX_LOCAL
|
|
}
|
|
|
|
pub fn is_global(&self) -> bool {
|
|
self.index() == abi::VER_NDX_GLOBAL
|
|
}
|
|
|
|
pub fn is_hidden(&self) -> bool {
|
|
(self.0 & abi::VER_NDX_HIDDEN) != 0
|
|
}
|
|
}
|
|
|
|
impl ParseAt for VersionIndex {
|
|
fn parse_at<E: EndianParse>(
|
|
endian: E,
|
|
_class: Class,
|
|
offset: &mut usize,
|
|
data: &[u8],
|
|
) -> Result<Self, ParseError> {
|
|
Ok(VersionIndex(endian.parse_u16_at(offset, data)?))
|
|
}
|
|
|
|
#[inline]
|
|
fn size_for(_class: Class) -> usize {
|
|
core::mem::size_of::<u16>()
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// _ _ //
|
|
// __ _ _ __ _ _ __ _____ _ __ ___(_) ___ _ __ __| | //
|
|
// / _` | '_ \| | | | \ \ / / _ \ '__/ __| |/ _ \| '_ \ / _` | //
|
|
// _ | (_| | | | | |_| | _ \ V / __/ | \__ \ | (_) | | | | | (_| | //
|
|
// (_) \__, |_| |_|\__,_| (_) \_/ \___|_| |___/_|\___/|_| |_|_____\__,_| //
|
|
// |___/ |_____| //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// The special GNU extension section .gnu.version_d has a section type of SHT_GNU_VERDEF
|
|
/// This section shall contain symbol version definitions. The number of entries
|
|
/// in this section shall be contained in the DT_VERDEFNUM entry of the Dynamic
|
|
/// Section .dynamic, and also the sh_info member of the section header.
|
|
/// The sh_link member of the section header shall point to the section that
|
|
/// contains the strings referenced by this section.
|
|
///
|
|
/// The .gnu.version_d section shall contain an array of VerDef structures
|
|
/// optionally followed by an array of VerDefAux structures.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct VerDef {
|
|
/// Version information flag bitmask.
|
|
pub vd_flags: u16,
|
|
/// VersionIndex value referencing the SHT_GNU_VERSYM section.
|
|
pub vd_ndx: u16,
|
|
/// Number of associated verdaux array entries.
|
|
pub vd_cnt: u16,
|
|
/// Version name hash value (ELF hash function).
|
|
pub vd_hash: u32,
|
|
/// Offset in bytes to a corresponding entry in an array of VerDefAux structures.
|
|
vd_aux: u32,
|
|
/// Offset to the next VerDef entry, in bytes.
|
|
vd_next: u32,
|
|
}
|
|
|
|
impl ParseAt for VerDef {
|
|
fn parse_at<E: EndianParse>(
|
|
endian: E,
|
|
_class: Class,
|
|
offset: &mut usize,
|
|
data: &[u8],
|
|
) -> Result<Self, ParseError> {
|
|
let vd_version = endian.parse_u16_at(offset, data)?;
|
|
if vd_version != abi::VER_DEF_CURRENT {
|
|
return Err(ParseError::UnsupportedVersion((
|
|
vd_version as u64,
|
|
abi::VER_DEF_CURRENT as u64,
|
|
)));
|
|
}
|
|
|
|
Ok(VerDef {
|
|
vd_flags: endian.parse_u16_at(offset, data)?,
|
|
vd_ndx: endian.parse_u16_at(offset, data)?,
|
|
vd_cnt: endian.parse_u16_at(offset, data)?,
|
|
vd_hash: endian.parse_u32_at(offset, data)?,
|
|
vd_aux: endian.parse_u32_at(offset, data)?,
|
|
vd_next: endian.parse_u32_at(offset, data)?,
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
fn size_for(_class: Class) -> usize {
|
|
ELFVERDEFSIZE
|
|
}
|
|
}
|
|
|
|
const ELFVERDEFSIZE: usize = 20;
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct VerDefIterator<'data, E: EndianParse> {
|
|
endian: E,
|
|
class: Class,
|
|
/// The number of entries in this iterator is given by the .dynamic DT_VERDEFNUM entry
|
|
/// and also in the .gnu.version_d section header's sh_info field.
|
|
count: u64,
|
|
data: &'data [u8],
|
|
offset: usize,
|
|
}
|
|
|
|
impl<'data, E: EndianParse> VerDefIterator<'data, E> {
|
|
pub fn new(
|
|
endian: E,
|
|
class: Class,
|
|
count: u64,
|
|
starting_offset: usize,
|
|
data: &'data [u8],
|
|
) -> Self {
|
|
VerDefIterator {
|
|
endian,
|
|
class,
|
|
count,
|
|
data,
|
|
offset: starting_offset,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'data, E: EndianParse> Iterator for VerDefIterator<'data, E> {
|
|
type Item = (VerDef, VerDefAuxIterator<'data, E>);
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.data.is_empty() || self.count == 0 {
|
|
return None;
|
|
}
|
|
|
|
let mut start = self.offset;
|
|
let vd = VerDef::parse_at(self.endian, self.class, &mut start, self.data).ok()?;
|
|
let vda_iter = VerDefAuxIterator::new(
|
|
self.endian,
|
|
self.class,
|
|
vd.vd_cnt,
|
|
self.offset + vd.vd_aux as usize,
|
|
self.data,
|
|
);
|
|
|
|
// If offset overflows, silently end iteration
|
|
match self.offset.checked_add(vd.vd_next as usize) {
|
|
Some(new_off) => self.offset = new_off,
|
|
None => self.count = 0,
|
|
}
|
|
self.count -= 1;
|
|
|
|
// Silently end iteration early if the next link stops pointing somewhere new
|
|
// TODO: Make this an error condition by allowing the iterator to yield a ParseError
|
|
if self.count > 0 && vd.vd_next == 0 {
|
|
self.count = 0
|
|
}
|
|
Some((vd, vda_iter))
|
|
}
|
|
}
|
|
|
|
/// Version Definition Auxiliary Entries from the .gnu.version_d section
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct VerDefAux {
|
|
/// Offset to the version or dependency name string in the linked string table, in bytes.
|
|
pub vda_name: u32,
|
|
/// Offset to the next VerDefAux entry, in bytes.
|
|
vda_next: u32,
|
|
}
|
|
|
|
impl ParseAt for VerDefAux {
|
|
fn parse_at<E: EndianParse>(
|
|
endian: E,
|
|
_class: Class,
|
|
offset: &mut usize,
|
|
data: &[u8],
|
|
) -> Result<Self, ParseError> {
|
|
Ok(VerDefAux {
|
|
vda_name: endian.parse_u32_at(offset, data)?,
|
|
vda_next: endian.parse_u32_at(offset, data)?,
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
fn size_for(_class: Class) -> usize {
|
|
8
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct VerDefAuxIterator<'data, E: EndianParse> {
|
|
endian: E,
|
|
class: Class,
|
|
count: u16,
|
|
data: &'data [u8],
|
|
offset: usize,
|
|
}
|
|
|
|
impl<'data, E: EndianParse> VerDefAuxIterator<'data, E> {
|
|
pub fn new(
|
|
endian: E,
|
|
class: Class,
|
|
count: u16,
|
|
starting_offset: usize,
|
|
data: &'data [u8],
|
|
) -> Self {
|
|
VerDefAuxIterator {
|
|
endian,
|
|
class,
|
|
count,
|
|
data,
|
|
offset: starting_offset,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'data, E: EndianParse> Iterator for VerDefAuxIterator<'data, E> {
|
|
type Item = VerDefAux;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.data.is_empty() || self.count == 0 {
|
|
return None;
|
|
}
|
|
|
|
// N.B. This offset handling is maybe unnecessary, but faithful to the
|
|
// spec. As far as I've observed, VerDefAux entries for a VerDef are all
|
|
// encoded sequentially after the VerDef, so we could likely just
|
|
// use the normal pattern here and pass in &mut self.offset here.
|
|
//
|
|
// The spec claims that "The section shall contain an array of
|
|
// Elfxx_Verdef structures, optionally followed by an array of
|
|
// Elfxx_Verdaux structures." This reads a bit ambiguously
|
|
// (is there one big array of Verdefs followed by one big array of
|
|
// Verdauxs?). If so, the vd_next and vda_next links seem unnecessary
|
|
// given the vd_cnt field. In practice, it appears that all the VerDefAux
|
|
// fields for a given VerDef are sequentially following the VerDef, meaning
|
|
// they're contiguous, but intersersed. The _next fields could theoretically
|
|
// give non-contiguous linked-list-like configurations, though (but only linking
|
|
// forward, not backward, since the link is a u32).
|
|
//
|
|
// The vd_next and vda_next fields are also not "pointers" i.e. offsets from
|
|
// the start of the section, but rather "increments" in telling how far to
|
|
// advance from where you just read the containing struct for where you should
|
|
// read the next. Given the sequentially-following nature described, these vd_next
|
|
// and vda_next fields end up being 0x14 and 0x8 (the size of the VerDef and
|
|
// VerDefAux structs).
|
|
//
|
|
// So observationally, we could likely get away with using self.offset and count here
|
|
// and ignoring the vda_next field, but that'd break things if they weren't contiguous.
|
|
let mut start = self.offset;
|
|
let vda = VerDefAux::parse_at(self.endian, self.class, &mut start, self.data).ok()?;
|
|
|
|
// If offset overflows, silently end iteration
|
|
match self.offset.checked_add(vda.vda_next as usize) {
|
|
Some(new_off) => self.offset = new_off,
|
|
None => self.count = 0,
|
|
}
|
|
self.count -= 1;
|
|
|
|
// Silently end iteration early if the next link stops pointing somewhere new
|
|
// TODO: Make this an error condition by allowing the iterator to yield a ParseError
|
|
if self.count > 0 && vda.vda_next == 0 {
|
|
self.count = 0
|
|
}
|
|
Some(vda)
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
// _ //
|
|
// __ _ _ __ _ _ __ _____ _ __ ___(_) ___ _ __ _ __ //
|
|
// / _` | '_ \| | | | \ \ / / _ \ '__/ __| |/ _ \| '_ \ | '__| //
|
|
// _ | (_| | | | | |_| | _ \ V / __/ | \__ \ | (_) | | | | | | //
|
|
// (_) \__, |_| |_|\__,_| (_) \_/ \___|_| |___/_|\___/|_| |_|_____|_| //
|
|
// |___/ |_____| //
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
/// The GNU extension section .gnu.version_r has a section type of SHT_GNU_VERNEED.
|
|
/// This section contains required symbol version definitions. The number of
|
|
/// entries in this section shall be contained in the DT_VERNEEDNUM entry of the
|
|
/// Dynamic Section .dynamic and also the sh_info member of the section header.
|
|
/// The sh_link member of the section header shall point to the referenced
|
|
/// string table section.
|
|
///
|
|
/// The section shall contain an array of VerNeed structures optionally
|
|
/// followed by an array of VerNeedAux structures.
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct VerNeed {
|
|
/// Number of associated verneed array entries.
|
|
pub vn_cnt: u16,
|
|
/// Offset to the file name string in the linked string table, in bytes.
|
|
pub vn_file: u32,
|
|
/// Offset to a corresponding entry in the VerNeedAux array, in bytes.
|
|
vn_aux: u32,
|
|
/// Offset to the next VerNeed entry, in bytes.
|
|
vn_next: u32,
|
|
}
|
|
|
|
impl ParseAt for VerNeed {
|
|
fn parse_at<E: EndianParse>(
|
|
endian: E,
|
|
_class: Class,
|
|
offset: &mut usize,
|
|
data: &[u8],
|
|
) -> Result<Self, ParseError> {
|
|
let vd_version = endian.parse_u16_at(offset, data)?;
|
|
if vd_version != abi::VER_NEED_CURRENT {
|
|
return Err(ParseError::UnsupportedVersion((
|
|
vd_version as u64,
|
|
abi::VER_DEF_CURRENT as u64,
|
|
)));
|
|
}
|
|
Ok(VerNeed {
|
|
vn_cnt: endian.parse_u16_at(offset, data)?,
|
|
vn_file: endian.parse_u32_at(offset, data)?,
|
|
vn_aux: endian.parse_u32_at(offset, data)?,
|
|
vn_next: endian.parse_u32_at(offset, data)?,
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
fn size_for(_class: Class) -> usize {
|
|
ELFVERNEEDSIZE
|
|
}
|
|
}
|
|
|
|
const ELFVERNEEDSIZE: usize = 16;
|
|
|
|
#[derive(Debug, Copy, Clone)]
|
|
pub struct VerNeedIterator<'data, E: EndianParse> {
|
|
endian: E,
|
|
class: Class,
|
|
/// The number of entries in this iterator is given by the .dynamic DT_VERNEEDNUM entry
|
|
/// and also in the .gnu.version_r section header's sh_info field.
|
|
count: u64,
|
|
data: &'data [u8],
|
|
offset: usize,
|
|
}
|
|
|
|
impl<'data, E: EndianParse> VerNeedIterator<'data, E> {
|
|
pub fn new(
|
|
endian: E,
|
|
class: Class,
|
|
count: u64,
|
|
starting_offset: usize,
|
|
data: &'data [u8],
|
|
) -> Self {
|
|
VerNeedIterator {
|
|
endian,
|
|
class,
|
|
count,
|
|
data,
|
|
offset: starting_offset,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'data, E: EndianParse> Iterator for VerNeedIterator<'data, E> {
|
|
type Item = (VerNeed, VerNeedAuxIterator<'data, E>);
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.data.is_empty() || self.count == 0 {
|
|
return None;
|
|
}
|
|
|
|
let mut start = self.offset;
|
|
let vn = VerNeed::parse_at(self.endian, self.class, &mut start, self.data).ok()?;
|
|
let vna_iter = VerNeedAuxIterator::new(
|
|
self.endian,
|
|
self.class,
|
|
vn.vn_cnt,
|
|
self.offset + vn.vn_aux as usize,
|
|
self.data,
|
|
);
|
|
|
|
// If offset overflows, silently end iteration
|
|
match self.offset.checked_add(vn.vn_next as usize) {
|
|
Some(new_off) => self.offset = new_off,
|
|
None => self.count = 0,
|
|
}
|
|
self.count -= 1;
|
|
|
|
// Silently end iteration early if the next link stops pointing somewhere new
|
|
// TODO: Make this an error condition by allowing the iterator to yield a ParseError
|
|
if self.count > 0 && vn.vn_next == 0 {
|
|
self.count = 0
|
|
}
|
|
Some((vn, vna_iter))
|
|
}
|
|
}
|
|
|
|
/// Version Need Auxiliary Entries from the .gnu.version_r section
|
|
#[derive(Debug, PartialEq, Eq)]
|
|
pub struct VerNeedAux {
|
|
/// Dependency name hash value (ELF hash function).
|
|
pub vna_hash: u32,
|
|
/// Dependency information flag bitmask.
|
|
pub vna_flags: u16,
|
|
/// VersionIndex value used in the .gnu.version symbol version array.
|
|
pub vna_other: u16,
|
|
/// Offset to the dependency name string in the linked string table, in bytes.
|
|
pub vna_name: u32,
|
|
/// Offset to the next vernaux entry, in bytes.
|
|
vna_next: u32,
|
|
}
|
|
|
|
impl ParseAt for VerNeedAux {
|
|
fn parse_at<E: EndianParse>(
|
|
endian: E,
|
|
_class: Class,
|
|
offset: &mut usize,
|
|
data: &[u8],
|
|
) -> Result<Self, ParseError> {
|
|
Ok(VerNeedAux {
|
|
vna_hash: endian.parse_u32_at(offset, data)?,
|
|
vna_flags: endian.parse_u16_at(offset, data)?,
|
|
vna_other: endian.parse_u16_at(offset, data)?,
|
|
vna_name: endian.parse_u32_at(offset, data)?,
|
|
vna_next: endian.parse_u32_at(offset, data)?,
|
|
})
|
|
}
|
|
|
|
#[inline]
|
|
fn size_for(_class: Class) -> usize {
|
|
16
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct VerNeedAuxIterator<'data, E: EndianParse> {
|
|
endian: E,
|
|
class: Class,
|
|
count: u16,
|
|
data: &'data [u8],
|
|
offset: usize,
|
|
}
|
|
|
|
impl<'data, E: EndianParse> VerNeedAuxIterator<'data, E> {
|
|
pub fn new(
|
|
endian: E,
|
|
class: Class,
|
|
count: u16,
|
|
starting_offset: usize,
|
|
data: &'data [u8],
|
|
) -> Self {
|
|
VerNeedAuxIterator {
|
|
endian,
|
|
class,
|
|
count,
|
|
data,
|
|
offset: starting_offset,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<'data, E: EndianParse> Iterator for VerNeedAuxIterator<'data, E> {
|
|
type Item = VerNeedAux;
|
|
fn next(&mut self) -> Option<Self::Item> {
|
|
if self.data.is_empty() || self.count == 0 {
|
|
return None;
|
|
}
|
|
|
|
let mut start = self.offset;
|
|
let vna = VerNeedAux::parse_at(self.endian, self.class, &mut start, self.data).ok()?;
|
|
|
|
// If offset overflows, silently end iteration
|
|
match self.offset.checked_add(vna.vna_next as usize) {
|
|
Some(new_off) => self.offset = new_off,
|
|
None => self.count = 0,
|
|
}
|
|
self.count -= 1;
|
|
|
|
// Silently end iteration early if the next link stops pointing somewhere new
|
|
// TODO: Make this an error condition by allowing the iterator to yield a ParseError
|
|
if self.count > 0 && vna.vna_next == 0 {
|
|
self.count = 0
|
|
}
|
|
Some(vna)
|
|
}
|
|
}
|
|
|
|
//////////////////////////////
|
|
// _____ _ //
|
|
// |_ _|__ ___| |_ ___ //
|
|
// | |/ _ \/ __| __/ __| //
|
|
// | | __/\__ \ |_\__ \ //
|
|
// |_|\___||___/\__|___/ //
|
|
// //
|
|
//////////////////////////////
|
|
|
|
#[cfg(test)]
|
|
mod iter_tests {
|
|
use super::*;
|
|
use crate::endian::LittleEndian;
|
|
|
|
#[rustfmt::skip]
|
|
const GNU_VERNEED_STRINGS: [u8; 65] = [
|
|
// ZLIB_1.2.0 (0x1)
|
|
0x00, 0x5a, 0x4c, 0x49, 0x42, 0x5f, 0x31, 0x2e, 0x32, 0x2e, 0x30, 0x00,
|
|
// GLIBC_2.33 (0xC)
|
|
0x47, 0x4c, 0x49, 0x42, 0x43, 0x5f, 0x32, 0x2e, 0x33, 0x33, 0x00,
|
|
// GLIBC_2.2.5 (0x17)
|
|
0x47, 0x4c, 0x49, 0x42, 0x43, 0x5f, 0x32, 0x2e, 0x32, 0x2e, 0x35, 0x00,
|
|
// libz.so.1 (0x23)
|
|
0x6c, 0x69, 0x62, 0x7a, 0x2e, 0x73, 0x6f, 0x2e, 0x31, 0x00,
|
|
// libc.so.6 (0x2D)
|
|
0x6c, 0x69, 0x62, 0x63, 0x2e, 0x73, 0x6f, 0x2e, 0x36, 0x00,
|
|
// GLIBC_2.3 (0x37)
|
|
0x47, 0x4c, 0x49, 0x42, 0x43, 0x5f, 0x32, 0x2e, 0x33, 0x00,
|
|
];
|
|
|
|
#[rustfmt::skip]
|
|
const GNU_VERNEED_DATA: [u8; 96] = [
|
|
// {vn_version, vn_cnt, vn_file, vn_aux, vn_next }
|
|
0x01, 0x00, 0x01, 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0xc0, 0xe5, 0x27, 0x08, 0x00, 0x00, 0x0a, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// {vn_version, vn_cnt, vn_file, vn_aux, vn_next }
|
|
0x01, 0x00, 0x03, 0x00, 0x2D, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0x13, 0x69, 0x69, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0x0c, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0xb3, 0x91, 0x96, 0x06, 0x00, 0x00, 0x0b, 0x00, 0x17, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0x94, 0x91, 0x96, 0x06, 0x00, 0x00, 0x09, 0x00, 0x37, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
];
|
|
|
|
#[test]
|
|
fn verneed_iter() {
|
|
let iter = VerNeedIterator::new(LittleEndian, Class::ELF64, 2, 0, &GNU_VERNEED_DATA);
|
|
let entries: Vec<(VerNeed, Vec<VerNeedAux>)> =
|
|
iter.map(|(vn, iter)| (vn, iter.collect())).collect();
|
|
|
|
assert_eq!(entries.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn verneed_iter_early_termination_on_broken_next_link() {
|
|
// set count = 3 even though there's only 2 entries
|
|
let iter = VerNeedIterator::new(LittleEndian, Class::ELF64, 3, 0, &GNU_VERNEED_DATA);
|
|
let entries: Vec<(VerNeed, Vec<VerNeedAux>)> =
|
|
iter.map(|(vn, iter)| (vn, iter.collect())).collect();
|
|
|
|
// TODO: make this a ParseError condition instead of silently returning only the good data.
|
|
assert_eq!(entries.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn verneedaux_iter_one_entry() {
|
|
let mut iter =
|
|
VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 1, 0x10, &GNU_VERNEED_DATA);
|
|
let aux1 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1,
|
|
VerNeedAux {
|
|
vna_hash: 0x0827e5c0,
|
|
vna_flags: 0,
|
|
vna_other: 0x0a,
|
|
vna_name: 0x01,
|
|
vna_next: 0
|
|
}
|
|
);
|
|
assert!(iter.next().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn verneedaux_iter_multiple_entries() {
|
|
let mut iter =
|
|
VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 3, 0x30, &GNU_VERNEED_DATA);
|
|
let aux1 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1,
|
|
VerNeedAux {
|
|
vna_hash: 0x0d696913,
|
|
vna_flags: 0,
|
|
vna_other: 0x0c,
|
|
vna_name: 0x0c,
|
|
vna_next: 0x10
|
|
}
|
|
);
|
|
let aux2 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux2,
|
|
VerNeedAux {
|
|
vna_hash: 0x069691b3,
|
|
vna_flags: 0,
|
|
vna_other: 0x0b,
|
|
vna_name: 0x17,
|
|
vna_next: 0x10
|
|
}
|
|
);
|
|
let aux3 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux3,
|
|
VerNeedAux {
|
|
vna_hash: 0x06969194,
|
|
vna_flags: 0,
|
|
vna_other: 0x09,
|
|
vna_name: 0x37,
|
|
vna_next: 0
|
|
}
|
|
);
|
|
assert!(iter.next().is_none());
|
|
}
|
|
|
|
// Hypothetical case where VerDefAux entries are non-contiguous
|
|
#[test]
|
|
fn verneedaux_iter_two_lists_interspersed() {
|
|
#[rustfmt::skip]
|
|
let data: [u8; 64] = [
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0xc0, 0xe5, 0x27, 0x08, 0x00, 0x00, 0x0a, 0x00, 0xcc, 0x0c, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0x13, 0x69, 0x69, 0x0d, 0x00, 0x00, 0x0c, 0x00, 0xd7, 0x0c, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0xb3, 0x91, 0x96, 0x06, 0x00, 0x00, 0x0b, 0x00, 0xe1, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// {vn_hash, vn_flags, vn_other, vn_name, vn_next }
|
|
0x94, 0x91, 0x96, 0x06, 0x00, 0x00, 0x09, 0x00, 0xec, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
];
|
|
|
|
let mut iter1 = VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 2, 0, &data);
|
|
let mut iter2 = VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 2, 0x10, &data);
|
|
|
|
let aux1_1 = iter1.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1_1,
|
|
VerNeedAux {
|
|
vna_hash: 0x0827e5c0,
|
|
vna_flags: 0,
|
|
vna_other: 0x0a,
|
|
vna_name: 0x0ccc,
|
|
vna_next: 0x20,
|
|
}
|
|
);
|
|
let aux2_1 = iter2.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux2_1,
|
|
VerNeedAux {
|
|
vna_hash: 0x0d696913,
|
|
vna_flags: 0,
|
|
vna_other: 0x0c,
|
|
vna_name: 0x0cd7,
|
|
vna_next: 0x20
|
|
}
|
|
);
|
|
let aux1_2 = iter1.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1_2,
|
|
VerNeedAux {
|
|
vna_hash: 0x069691b3,
|
|
vna_flags: 0,
|
|
vna_other: 0x0b,
|
|
vna_name: 0x0ce1,
|
|
vna_next: 0
|
|
}
|
|
);
|
|
let aux2_2 = iter2.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux2_2,
|
|
VerNeedAux {
|
|
vna_hash: 0x06969194,
|
|
vna_flags: 0,
|
|
vna_other: 0x09,
|
|
vna_name: 0x0cec,
|
|
vna_next: 0
|
|
}
|
|
);
|
|
assert!(iter1.next().is_none());
|
|
assert!(iter2.next().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn verneedaux_iter_early_termination_on_broken_next_link() {
|
|
// set count = 7 even though there's only 1 entry
|
|
let iter = VerNeedAuxIterator::new(LittleEndian, Class::ELF64, 7, 0x10, &GNU_VERNEED_DATA);
|
|
let entries: Vec<VerNeedAux> = iter.collect();
|
|
|
|
// TODO: make this a ParseError condition instead of silently returning only the good data.
|
|
assert_eq!(entries.len(), 1);
|
|
}
|
|
|
|
#[rustfmt::skip]
|
|
const GNU_VERDEF_STRINGS: [u8; 34] = [
|
|
// LIBCTF_1.0 (0x1)
|
|
0x00, 0x4c, 0x49, 0x42, 0x43, 0x54, 0x46, 0x5f, 0x31, 0x2e, 0x30, 0x00,
|
|
// LIBCTF_1.1 (0xC)
|
|
0x4c, 0x49, 0x42, 0x43, 0x54, 0x46, 0x5f, 0x31, 0x2e, 0x31, 0x00,
|
|
// LIBCTF_1.2 (0x17)
|
|
0x4c, 0x49, 0x42, 0x43, 0x54, 0x46, 0x5f, 0x31, 0x2e, 0x32, 0x00,
|
|
];
|
|
|
|
// Sample .gnu.version_d section contents
|
|
#[rustfmt::skip]
|
|
const GNU_VERDEF_DATA: [u8; 128] = [
|
|
// {vd_version, vd_flags, vd_ndx, vd_cnt
|
|
0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00,
|
|
// vd_hash, vd_aux,
|
|
0xb0, 0x7a, 0x07, 0x0b, 0x14, 0x00, 0x00, 0x00,
|
|
// vd_next}, {vda_name,
|
|
0x1c, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
// vda_next}, {vd_version, vd_flags,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
// vd_ndx, vd_cnt, vd_hash,
|
|
0x02, 0x00, 0x01, 0x00, 0x70, 0x2f, 0x8f, 0x08,
|
|
// vd_aux, vd_next},
|
|
0x14, 0x00, 0x00, 0x00, 0x1c, 0x00, 0x00, 0x00,
|
|
// {vda_name, vda_next},
|
|
0x0c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// {vd_version, vd_flags, vd_ndx, vd_cnt
|
|
0x01, 0x00, 0x00, 0x00, 0x03, 0x00, 0x02, 0x00,
|
|
// vd_hash, vd_aux,
|
|
0x71, 0x2f, 0x8f, 0x08, 0x14, 0x00, 0x00, 0x00,
|
|
// vd_next}, {vda_name,
|
|
0x24, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
|
|
// vda_next}, {vda_name,
|
|
0x08, 0x00, 0x00, 0x00, 0x0c, 0x00, 0x00, 0x00,
|
|
// vda_next}, {vd_version, vd_flags,
|
|
0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
|
|
// vd_ndx, vd_cnt, vd_hash,
|
|
0x04, 0x00, 0x02, 0x00, 0x72, 0x2f, 0x8f, 0x08,
|
|
// vd_aux, vd_next},
|
|
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
// {vda_name, vda_next},
|
|
0x0c, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00,
|
|
// {vda_name, vda_next},
|
|
0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
];
|
|
|
|
#[test]
|
|
fn verdef_iter() {
|
|
let iter = VerDefIterator::new(LittleEndian, Class::ELF64, 4, 0, &GNU_VERDEF_DATA);
|
|
let entries: Vec<(VerDef, Vec<VerDefAux>)> =
|
|
iter.map(|(vd, iter)| (vd, iter.collect())).collect();
|
|
|
|
assert_eq!(entries.len(), 4);
|
|
|
|
assert_eq!(
|
|
entries,
|
|
vec![
|
|
(
|
|
VerDef {
|
|
vd_flags: 1,
|
|
vd_ndx: 1,
|
|
vd_cnt: 1,
|
|
vd_hash: 0x0B077AB0,
|
|
vd_aux: 20,
|
|
vd_next: 28,
|
|
},
|
|
vec![VerDefAux {
|
|
vda_name: 0x1,
|
|
vda_next: 0
|
|
}]
|
|
),
|
|
(
|
|
VerDef {
|
|
vd_flags: 0,
|
|
vd_ndx: 2,
|
|
vd_cnt: 1,
|
|
vd_hash: 0x088f2f70,
|
|
vd_aux: 20,
|
|
vd_next: 28,
|
|
},
|
|
vec![VerDefAux {
|
|
vda_name: 0xC,
|
|
vda_next: 0
|
|
}]
|
|
),
|
|
(
|
|
VerDef {
|
|
vd_flags: 0,
|
|
vd_ndx: 3,
|
|
vd_cnt: 2,
|
|
vd_hash: 0x088f2f71,
|
|
vd_aux: 20,
|
|
vd_next: 36,
|
|
},
|
|
vec![
|
|
VerDefAux {
|
|
vda_name: 0x17,
|
|
vda_next: 8
|
|
},
|
|
VerDefAux {
|
|
vda_name: 0xC,
|
|
vda_next: 0
|
|
}
|
|
]
|
|
),
|
|
(
|
|
VerDef {
|
|
vd_flags: 0,
|
|
vd_ndx: 4,
|
|
vd_cnt: 2,
|
|
vd_hash: 0x088f2f72,
|
|
vd_aux: 20,
|
|
vd_next: 0,
|
|
},
|
|
vec![
|
|
VerDefAux {
|
|
vda_name: 0xC,
|
|
vda_next: 8
|
|
},
|
|
VerDefAux {
|
|
vda_name: 0x17,
|
|
vda_next: 0
|
|
}
|
|
]
|
|
),
|
|
]
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn verdef_iter_early_termination_on_broken_next_link() {
|
|
// set count = 7 even though there's only 4 entries
|
|
let iter = VerDefIterator::new(LittleEndian, Class::ELF64, 7, 0, &GNU_VERDEF_DATA);
|
|
let entries: Vec<(VerDef, Vec<VerDefAux>)> =
|
|
iter.map(|(vn, iter)| (vn, iter.collect())).collect();
|
|
|
|
// TODO: make this a ParseError condition instead of silently returning only the good data.
|
|
assert_eq!(entries.len(), 4);
|
|
}
|
|
|
|
#[test]
|
|
fn verdefaux_iter_one_entry() {
|
|
let mut iter =
|
|
VerDefAuxIterator::new(LittleEndian, Class::ELF64, 1, 0x14, &GNU_VERDEF_DATA);
|
|
let aux1 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1,
|
|
VerDefAux {
|
|
vda_name: 0x01,
|
|
vda_next: 0
|
|
}
|
|
);
|
|
assert!(iter.next().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn verdefaux_iter_multiple_entries() {
|
|
let mut iter =
|
|
VerDefAuxIterator::new(LittleEndian, Class::ELF64, 2, 0x4C, &GNU_VERDEF_DATA);
|
|
let aux1 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1,
|
|
VerDefAux {
|
|
vda_name: 0x17,
|
|
vda_next: 8
|
|
}
|
|
);
|
|
let aux1 = iter.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1,
|
|
VerDefAux {
|
|
vda_name: 0xC,
|
|
vda_next: 0
|
|
}
|
|
);
|
|
assert!(iter.next().is_none());
|
|
}
|
|
|
|
// Hypothetical case where VerDefAux entries are non-contiguous
|
|
#[test]
|
|
fn verdefaux_iter_two_lists_interspersed() {
|
|
#[rustfmt::skip]
|
|
let data: [u8; 32] = [
|
|
0x01, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // list 1 entry 1
|
|
0xA1, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, // list 2 entry 1
|
|
0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // list 1 entry 2
|
|
0xA2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // list 2 entry 2
|
|
];
|
|
|
|
let mut iter1 = VerDefAuxIterator::new(LittleEndian, Class::ELF64, 2, 0, &data);
|
|
let mut iter2 = VerDefAuxIterator::new(LittleEndian, Class::ELF64, 2, 8, &data);
|
|
|
|
let aux1_1 = iter1.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1_1,
|
|
VerDefAux {
|
|
vda_name: 0x0001,
|
|
vda_next: 0x10,
|
|
}
|
|
);
|
|
let aux2_1 = iter2.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux2_1,
|
|
VerDefAux {
|
|
vda_name: 0x00A1,
|
|
vda_next: 0x10,
|
|
}
|
|
);
|
|
let aux1_2 = iter1.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux1_2,
|
|
VerDefAux {
|
|
vda_name: 0x0002,
|
|
vda_next: 0,
|
|
}
|
|
);
|
|
let aux2_2 = iter2.next().expect("Failed to parse");
|
|
assert_eq!(
|
|
aux2_2,
|
|
VerDefAux {
|
|
vda_name: 0x00A2,
|
|
vda_next: 0,
|
|
}
|
|
);
|
|
assert!(iter1.next().is_none());
|
|
assert!(iter2.next().is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn verdefaux_iter_early_termination_on_broken_next_link() {
|
|
// set count = 7 even though there's only 1 entry
|
|
let iter = VerDefAuxIterator::new(LittleEndian, Class::ELF64, 7, 0x14, &GNU_VERDEF_DATA);
|
|
let entries: Vec<VerDefAux> = iter.collect();
|
|
|
|
// TODO: make this a ParseError condition instead of silently returning only the good data.
|
|
assert_eq!(entries.len(), 1);
|
|
}
|
|
|
|
#[test]
|
|
fn version_table() {
|
|
let ver_idx_buf: [u8; 10] = [0x02, 0x00, 0x03, 0x00, 0x09, 0x00, 0x0A, 0x00, 0xff, 0xff];
|
|
let version_ids = VersionIndexTable::new(LittleEndian, Class::ELF64, &ver_idx_buf);
|
|
let verdefs = VerDefIterator::new(LittleEndian, Class::ELF64, 4, 0, &GNU_VERDEF_DATA);
|
|
let verneed_strs = StringTable::new(&GNU_VERNEED_STRINGS);
|
|
let verneeds = VerNeedIterator::new(LittleEndian, Class::ELF64, 2, 0, &GNU_VERNEED_DATA);
|
|
let verdef_strs = StringTable::new(&GNU_VERDEF_STRINGS);
|
|
|
|
let table = SymbolVersionTable::new(
|
|
version_ids,
|
|
Some((verneeds, verneed_strs)),
|
|
Some((verdefs, verdef_strs)),
|
|
);
|
|
|
|
let def1 = table
|
|
.get_definition(0)
|
|
.expect("Failed to parse definition")
|
|
.expect("Failed to find def");
|
|
assert_eq!(def1.hash, 0x088f2f70);
|
|
assert_eq!(def1.flags, 0);
|
|
let def1_names: Vec<&str> = def1
|
|
.names
|
|
.map(|res| res.expect("Failed to parse"))
|
|
.collect();
|
|
assert_eq!(def1_names, ["LIBCTF_1.1"]);
|
|
assert!(!def1.hidden);
|
|
|
|
let def2 = table
|
|
.get_definition(1)
|
|
.expect("Failed to parse definition")
|
|
.expect("Failed to find def");
|
|
assert_eq!(def2.hash, 0x088f2f71);
|
|
assert_eq!(def2.flags, 0);
|
|
let def2_names: Vec<&str> = def2
|
|
.names
|
|
.map(|res| res.expect("Failed to parse"))
|
|
.collect();
|
|
assert_eq!(def2_names, ["LIBCTF_1.2", "LIBCTF_1.1"]);
|
|
assert!(!def2.hidden);
|
|
|
|
let req1 = table
|
|
.get_requirement(2)
|
|
.expect("Failed to parse definition")
|
|
.expect("Failed to find req");
|
|
assert_eq!(
|
|
req1,
|
|
SymbolRequirement {
|
|
file: "libc.so.6",
|
|
name: "GLIBC_2.3",
|
|
hash: 0x6969194,
|
|
flags: 0,
|
|
hidden: false
|
|
}
|
|
);
|
|
|
|
let req2 = table
|
|
.get_requirement(3)
|
|
.expect("Failed to parse definition")
|
|
.expect("Failed to find req");
|
|
assert_eq!(
|
|
req2,
|
|
SymbolRequirement {
|
|
file: "libz.so.1",
|
|
name: "ZLIB_1.2.0",
|
|
hash: 0x827E5C0,
|
|
flags: 0,
|
|
hidden: false
|
|
}
|
|
);
|
|
|
|
// The last version_index points to non-existent definitions. Maybe we should treat
|
|
// this as a ParseError instead of returning an empty Option? This can only happen
|
|
// if .gnu.versions[N] contains an index that doesn't exist, which is likely a file corruption
|
|
// or programmer error (i.e asking for a definition for an undefined symbol)
|
|
assert!(table.get_definition(4).expect("Failed to parse").is_none());
|
|
assert!(table.get_requirement(4).expect("Failed to parse").is_none());
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod parse_tests {
|
|
use super::*;
|
|
use crate::endian::{BigEndian, LittleEndian};
|
|
use crate::parse::{test_parse_for, test_parse_fuzz_too_short};
|
|
|
|
#[test]
|
|
fn parse_verndx32_lsb() {
|
|
test_parse_for(LittleEndian, Class::ELF32, VersionIndex(0x0100));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx32_msb() {
|
|
test_parse_for(BigEndian, Class::ELF32, VersionIndex(0x0001));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx64_lsb() {
|
|
test_parse_for(LittleEndian, Class::ELF64, VersionIndex(0x0100));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx64_msb() {
|
|
test_parse_for(BigEndian, Class::ELF64, VersionIndex(0x0001));
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx32_lsb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VersionIndex>(LittleEndian, Class::ELF32);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx32_msb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VersionIndex>(BigEndian, Class::ELF32);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx64_lsb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VersionIndex>(LittleEndian, Class::ELF64);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verndx64_msb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VersionIndex>(BigEndian, Class::ELF64);
|
|
}
|
|
|
|
//
|
|
// VerDef
|
|
//
|
|
#[test]
|
|
fn parse_verdef32_lsb() {
|
|
let mut data = [0u8; ELFVERDEFSIZE];
|
|
for n in 0..ELFVERDEFSIZE {
|
|
data[n] = n as u8;
|
|
}
|
|
data[0] = 1;
|
|
data[1] = 0;
|
|
|
|
let mut offset = 0;
|
|
let entry = VerDef::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref())
|
|
.expect("Failed to parse VerDef");
|
|
|
|
assert_eq!(
|
|
entry,
|
|
VerDef {
|
|
vd_flags: 0x0302,
|
|
vd_ndx: 0x0504,
|
|
vd_cnt: 0x0706,
|
|
vd_hash: 0x0B0A0908,
|
|
vd_aux: 0x0F0E0D0C,
|
|
vd_next: 0x13121110,
|
|
}
|
|
);
|
|
assert_eq!(offset, ELFVERDEFSIZE);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdef32_fuzz_too_short() {
|
|
let mut data = [0u8; ELFVERDEFSIZE];
|
|
data[1] = 1;
|
|
for n in 0..ELFVERDEFSIZE {
|
|
let buf = data.split_at(n).0;
|
|
let mut offset: usize = 0;
|
|
let error = VerDef::parse_at(BigEndian, Class::ELF32, &mut offset, buf)
|
|
.expect_err("Expected an error");
|
|
assert!(
|
|
matches!(error, ParseError::SliceReadError(_)),
|
|
"Unexpected Error type found: {error}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdef64_msb() {
|
|
let mut data = [0u8; ELFVERDEFSIZE];
|
|
for n in 2..ELFVERDEFSIZE {
|
|
data[n] = n as u8;
|
|
}
|
|
data[1] = 1;
|
|
|
|
let mut offset = 0;
|
|
let entry = VerDef::parse_at(BigEndian, Class::ELF64, &mut offset, data.as_ref())
|
|
.expect("Failed to parse VerDef");
|
|
|
|
assert_eq!(
|
|
entry,
|
|
VerDef {
|
|
vd_flags: 0x0203,
|
|
vd_ndx: 0x0405,
|
|
vd_cnt: 0x0607,
|
|
vd_hash: 0x08090A0B,
|
|
vd_aux: 0x0C0D0E0F,
|
|
vd_next: 0x10111213,
|
|
}
|
|
);
|
|
assert_eq!(offset, ELFVERDEFSIZE);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdef64_fuzz_too_short() {
|
|
let mut data = [0u8; ELFVERDEFSIZE];
|
|
data[1] = 1;
|
|
for n in 0..ELFVERDEFSIZE {
|
|
let buf = data.split_at(n).0;
|
|
let mut offset: usize = 0;
|
|
let error = VerDef::parse_at(BigEndian, Class::ELF64, &mut offset, buf)
|
|
.expect_err("Expected an error");
|
|
assert!(
|
|
matches!(error, ParseError::SliceReadError(_)),
|
|
"Unexpected Error type found: {error}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdef_bad_version_errors() {
|
|
let data = [0u8; ELFVERDEFSIZE];
|
|
// version is 0, which is not 1, which is bad :)
|
|
|
|
let mut offset = 0;
|
|
let err = VerDef::parse_at(BigEndian, Class::ELF64, &mut offset, data.as_ref())
|
|
.expect_err("Expected an error");
|
|
assert!(
|
|
matches!(err, ParseError::UnsupportedVersion((0, 1))),
|
|
"Unexpected Error type found: {err}"
|
|
);
|
|
}
|
|
|
|
//
|
|
// VerDefAux
|
|
//
|
|
#[test]
|
|
fn parse_verdefaux32_lsb() {
|
|
test_parse_for(
|
|
LittleEndian,
|
|
Class::ELF32,
|
|
VerDefAux {
|
|
vda_name: 0x03020100,
|
|
vda_next: 0x07060504,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux32_msb() {
|
|
test_parse_for(
|
|
BigEndian,
|
|
Class::ELF32,
|
|
VerDefAux {
|
|
vda_name: 0x00010203,
|
|
vda_next: 0x04050607,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux64_lsb() {
|
|
test_parse_for(
|
|
LittleEndian,
|
|
Class::ELF64,
|
|
VerDefAux {
|
|
vda_name: 0x03020100,
|
|
vda_next: 0x07060504,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux64_msb() {
|
|
test_parse_for(
|
|
BigEndian,
|
|
Class::ELF64,
|
|
VerDefAux {
|
|
vda_name: 0x00010203,
|
|
vda_next: 0x04050607,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux32_lsb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerDefAux>(LittleEndian, Class::ELF32);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux32_msb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerDefAux>(BigEndian, Class::ELF32);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux64_lsb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerDefAux>(LittleEndian, Class::ELF64);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verdefaux64_msb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerDefAux>(BigEndian, Class::ELF64);
|
|
}
|
|
|
|
//
|
|
// VerNeed
|
|
//
|
|
#[test]
|
|
fn parse_verneed32_lsb() {
|
|
let mut data = [0u8; ELFVERNEEDSIZE];
|
|
for n in 0..ELFVERNEEDSIZE {
|
|
data[n] = n as u8;
|
|
}
|
|
data[0] = 1;
|
|
data[1] = 0;
|
|
|
|
let mut offset = 0;
|
|
let entry = VerNeed::parse_at(LittleEndian, Class::ELF32, &mut offset, data.as_ref())
|
|
.expect("Failed to parse VerNeed");
|
|
|
|
assert_eq!(
|
|
entry,
|
|
VerNeed {
|
|
vn_cnt: 0x0302,
|
|
vn_file: 0x07060504,
|
|
vn_aux: 0x0B0A0908,
|
|
vn_next: 0x0F0E0D0C,
|
|
}
|
|
);
|
|
assert_eq!(offset, ELFVERNEEDSIZE);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneed32_fuzz_too_short() {
|
|
let mut data = [0u8; ELFVERNEEDSIZE];
|
|
data[1] = 1;
|
|
for n in 0..ELFVERNEEDSIZE {
|
|
let buf = data.split_at(n).0;
|
|
let mut offset: usize = 0;
|
|
let error = VerNeed::parse_at(BigEndian, Class::ELF32, &mut offset, buf)
|
|
.expect_err("Expected an error");
|
|
assert!(
|
|
matches!(error, ParseError::SliceReadError(_)),
|
|
"Unexpected Error type found: {error}"
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneed64_msb() {
|
|
let mut data = [0u8; ELFVERNEEDSIZE];
|
|
for n in 0..ELFVERNEEDSIZE {
|
|
data[n] = n as u8;
|
|
}
|
|
data[1] = 1;
|
|
|
|
let mut offset = 0;
|
|
let entry = VerNeed::parse_at(BigEndian, Class::ELF64, &mut offset, data.as_ref())
|
|
.expect("Failed to parse VerNeed");
|
|
|
|
assert_eq!(
|
|
entry,
|
|
VerNeed {
|
|
vn_cnt: 0x0203,
|
|
vn_file: 0x04050607,
|
|
vn_aux: 0x08090A0B,
|
|
vn_next: 0x0C0D0E0F,
|
|
}
|
|
);
|
|
assert_eq!(offset, ELFVERNEEDSIZE);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneed64_fuzz_too_short() {
|
|
let mut data = [0u8; ELFVERNEEDSIZE];
|
|
data[1] = 1;
|
|
for n in 0..ELFVERNEEDSIZE {
|
|
let buf = data.split_at(n).0;
|
|
let mut offset: usize = 0;
|
|
let error = VerNeed::parse_at(BigEndian, Class::ELF64, &mut offset, buf)
|
|
.expect_err("Expected an error");
|
|
assert!(
|
|
matches!(error, ParseError::SliceReadError(_)),
|
|
"Unexpected Error type found: {error}"
|
|
);
|
|
}
|
|
}
|
|
|
|
//
|
|
// VerNeedAux
|
|
//
|
|
#[test]
|
|
fn parse_verneedaux32_lsb() {
|
|
test_parse_for(
|
|
LittleEndian,
|
|
Class::ELF32,
|
|
VerNeedAux {
|
|
vna_hash: 0x03020100,
|
|
vna_flags: 0x0504,
|
|
vna_other: 0x0706,
|
|
vna_name: 0x0B0A0908,
|
|
vna_next: 0x0F0E0D0C,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux32_msb() {
|
|
test_parse_for(
|
|
BigEndian,
|
|
Class::ELF32,
|
|
VerNeedAux {
|
|
vna_hash: 0x00010203,
|
|
vna_flags: 0x0405,
|
|
vna_other: 0x0607,
|
|
vna_name: 0x08090A0B,
|
|
vna_next: 0x0C0D0E0F,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux64_lsb() {
|
|
test_parse_for(
|
|
LittleEndian,
|
|
Class::ELF64,
|
|
VerNeedAux {
|
|
vna_hash: 0x03020100,
|
|
vna_flags: 0x0504,
|
|
vna_other: 0x0706,
|
|
vna_name: 0x0B0A0908,
|
|
vna_next: 0x0F0E0D0C,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux64_msb() {
|
|
test_parse_for(
|
|
BigEndian,
|
|
Class::ELF64,
|
|
VerNeedAux {
|
|
vna_hash: 0x00010203,
|
|
vna_flags: 0x0405,
|
|
vna_other: 0x0607,
|
|
vna_name: 0x08090A0B,
|
|
vna_next: 0x0C0D0E0F,
|
|
},
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux32_lsb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerNeedAux>(LittleEndian, Class::ELF32);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux32_msb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerNeedAux>(BigEndian, Class::ELF32);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux64_lsb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerNeedAux>(LittleEndian, Class::ELF64);
|
|
}
|
|
|
|
#[test]
|
|
fn parse_verneedaux64_msb_fuzz_too_short() {
|
|
test_parse_fuzz_too_short::<_, VerNeedAux>(BigEndian, Class::ELF64);
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod version_index_tests {
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn is_local() {
|
|
let idx = VersionIndex(0);
|
|
assert!(idx.is_local());
|
|
}
|
|
|
|
#[test]
|
|
fn is_global() {
|
|
let idx = VersionIndex(1);
|
|
assert!(idx.is_global());
|
|
}
|
|
|
|
#[test]
|
|
fn index_visible() {
|
|
let idx = VersionIndex(42);
|
|
assert_eq!(idx.index(), 42);
|
|
assert!(!idx.is_hidden());
|
|
}
|
|
|
|
#[test]
|
|
fn index_hidden() {
|
|
let idx = VersionIndex(42 | abi::VER_NDX_HIDDEN);
|
|
assert_eq!(idx.index(), 42);
|
|
assert!(idx.is_hidden());
|
|
}
|
|
}
|