From 0ab34dcab1637c49f63f0b7bca7d2672f46aac31 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Tue, 10 Aug 2021 23:16:37 +0200 Subject: [PATCH 01/20] elf.dynamic: Parse DT_VERDEF + DT_VERDEFNUM dynamic tags into DynamicInfo struct --- src/elf/dynamic.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/elf/dynamic.rs b/src/elf/dynamic.rs index 8de3b77e..9de72add 100644 --- a/src/elf/dynamic.rs +++ b/src/elf/dynamic.rs @@ -614,6 +614,8 @@ macro_rules! elf_dynamic_info_std_impl { pub pltrelsz: usize, pub pltrel: $size, pub jmprel: usize, + pub verdef: $size, + pub verdefnum: $size, pub verneed: $size, pub verneednum: $size, pub versym: $size, @@ -658,6 +660,8 @@ macro_rules! elf_dynamic_info_std_impl { DT_JMPREL => { self.jmprel = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0) as usize } // .rela.plt + DT_VERDEF => self.verdef = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), + DT_VERDEFNUM => self.verdefnum = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), DT_VERNEED => self.verneed = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), DT_VERNEEDNUM => self.verneednum = dynamic.d_val as _, DT_VERSYM => self.versym = vm_to_offset(phdrs, dynamic.d_val).unwrap_or(0), @@ -750,6 +754,8 @@ macro_rules! elf_dynamic_info_std_impl { .field("pltrelsz", &self.pltrelsz) .field("pltrel", &self.pltrel) .field("jmprel", &format_args!("0x{:x}", self.jmprel)) + .field("verdef", &format_args!("0x{:x}", self.verdef)) + .field("verdefnum", &self.verdefnum) .field("verneed", &format_args!("0x{:x}", self.verneed)) .field("verneednum", &self.verneednum) .field("versym", &format_args!("0x{:x}", self.versym)) From 7a1a6e67ddee4da98c71e78acd548b316829cee9 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Fri, 13 Aug 2021 23:05:24 +0200 Subject: [PATCH 02/20] elf.symver: add struct definitions and reading part for .gnu.version_r (SHT_GNU_verdef) section --- src/elf/mod.rs | 9 ++ src/elf/symver.rs | 312 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 321 insertions(+) create mode 100644 src/elf/symver.rs diff --git a/src/elf/mod.rs b/src/elf/mod.rs index 9101d95e..1a4a685d 100644 --- a/src/elf/mod.rs +++ b/src/elf/mod.rs @@ -53,6 +53,7 @@ pub mod dynamic; #[macro_use] pub mod reloc; pub mod note; +pub mod symver; macro_rules! if_sylvan { ($($i:item)*) => ($( @@ -78,6 +79,7 @@ if_sylvan! { pub use dynamic::Dynamic; pub use reloc::Reloc; pub use reloc::RelocSection; + pub use symver::VerneedSection; pub type ProgramHeaders = Vec; pub type SectionHeaders = Vec; @@ -130,6 +132,9 @@ if_sylvan! { pub entry: u64, /// Whether the binary is little endian or not pub little_endian: bool, + /// Contains the version needed information from the optional section + /// [`SHT_GNU_VERNEED`][section_header::SHT_GNU_VERNEED] (GNU extenstion). + pub verneed : Option>, ctx: Ctx, } @@ -232,6 +237,7 @@ if_sylvan! { entry: misc.entry, little_endian: misc.little_endian, ctx: misc.ctx, + verneed : None, }) } @@ -334,6 +340,8 @@ if_sylvan! { } } + let verneed = symver::VerneedSection::parse(bytes, §ion_headers, ctx)?; + Ok(Elf { header, program_headers, @@ -356,6 +364,7 @@ if_sylvan! { entry: misc.entry, little_endian: misc.little_endian, ctx: ctx, + verneed, }) } } diff --git a/src/elf/symver.rs b/src/elf/symver.rs new file mode 100644 index 00000000..574cb595 --- /dev/null +++ b/src/elf/symver.rs @@ -0,0 +1,312 @@ +//! Symbol versioning +//! +//! Implementation of the GNU symbol versioning extension according to +//! [LSB Core Specification - Symbol Versioning][lsb-symver]. +//! +//! List the dependencies of an ELF file that have version needed information along with the +//! versions needed for each dependency. +//! ```rust +//! use goblin::error::Error; +//! +//! pub fn show_verneed(bytes: &[u8]) -> Result<(), Error> { +//! let binary = goblin::elf::Elf::parse(&bytes)?; +//! +//! if let Some(verneed) = binary.verneed { +//! for need_file in verneed.iter() { +//! println!( +//! "Depend on {:?} with version(s):", +//! verneed.symstr.get_at(need_file.vn_file as usize) +//! ); +//! for need_ver in need_file.iter() { +//! println!("{:?}", verneed.symstr.get_at(need_ver.vna_name as usize)); +//! } +//! } +//! } +//! +//! Ok(()) +//! } +//! ``` +//! +//! [lsb-symver]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html + +macro_rules! if_cfg_elf { + ($($i:item)*) => ($( + #[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] + $i + )*) +} + +if_cfg_elf! { + +use crate::container; +use crate::strtab::Strtab; +use crate::elf::section_header::{SectionHeader, SHT_GNU_VERNEED}; +use crate::error::{Error, Result}; +use scroll::Pread; + +/// An ELF `Version Need` entry Elfxx_Verneed. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVerneed { + /// Version of structure. This value is currently set to 1, and will be reset if the versioning + /// implementation is incompatibly altered. + vn_version: u16, + /// Number of associated verneed array entries. + vn_cnt: u16, + /// Offset to the file name string in the section header, in bytes. + vn_file: u32, + /// Offset to a corresponding entry in the vernaux array, in bytes. + vn_aux: u32, + /// Offset to the next verneed entry, in bytes. + vn_next: u32, +} + +/// An ELF `Version Need Auxiliary` entry Elfxx_Vernaux. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVernaux { + /// Dependency name hash value (ELF hash function). + vna_hash: u32, + /// Dependency information flag bitmask. + vna_flags: u16, + /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 + /// controls whether or not the object is hidden; if this bit is set, the object cannot be used + /// and the static linker will ignore the symbol's presence in the object. + vna_other: u16, + /// Offset to the dependency name string in the section header, in bytes. + vna_name: u32, + /// Offset to the next vernaux entry, in bytes. + vna_next: u32, +} + +/// Helper struct to iterate over [`Version Needed`][Verneed] and [`Version Needed +/// Auxiliary`][Vernaux] entries. +#[derive(Debug)] +pub struct VerneedSection<'a> { + /// String table used to resolve version strings. + pub symstr: Strtab<'a>, + bytes: &'a [u8], + count: usize, + ctx: container::Ctx, +} + +impl<'a> VerneedSection<'a> { + /// Try to parse the optional [`SHT_GNU_VERNEED`] section. + pub fn parse( + bytes: &'a [u8], + shdrs: &'_ [SectionHeader], + ctx: container::Ctx, + ) -> Result>> { + // Get fields needed from optional `version needed` section. + let (link_idx, offset, size, count) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { + ( + shdr.sh_link as usize, // Encodes the string table. + shdr.sh_offset as usize, + shdr.sh_size as usize, + shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. + ) + } else { + return Ok(None); + }; + + // Get string table which is used to resolve version strings. + let symstr = { + // Linked section refers to string table. + let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( + "Section header of string table for SHT_GNU_VERNEED section not found!".into(), + ))?; + + Strtab::parse( + bytes, + shdr_link.sh_offset as usize, + shdr_link.sh_size as usize, + 0x0, /* Delimiter */ + )? + }; + + // Get a slice of bytes of the `version needed` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; + + Ok(Some(VerneedSection { + symstr, + bytes, + count, + ctx, + })) + } + + /// Get an iterator over the [`Verneed`] entries. + pub fn iter(&'a self) -> VerneedIterator<'a> { + VerneedIterator { + bytes: self.bytes, + count: self.count, + index: 0, + offset: 0, + ctx: self.ctx, + } + } +} + +/// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. +pub struct VerneedIterator<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, +} + +impl<'a> Iterator for VerneedIterator<'a> { + type Item = Verneed<'a>; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. + let ElfVerneed { + vn_version, + vn_cnt, + vn_file, + vn_aux, + vn_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Get a slice of bytes of the `Vernaux` entries. + // + // | Verneed | .. | Verneed | Vernaux | Vernaux | .. | Verneed | .. + // ^--------------^ + // offset ^---------^ + // vn_aux + // ^----------------------------------^ + // vn_next + // + // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. + let len = if vn_next > 0 { + (vn_next - vn_aux) as usize + } else { + // For the last entry, ElfVerneed->vn_next == 0. + // Therefore we compute the remaining length of bytes buffer. + self.bytes.len() - self.offset - vn_aux as usize + }; + let bytes: &'a [u8] = self + .bytes + .pread_with(self.offset + vn_aux as usize, len) + .unwrap(); + + // Bump the offset to the next ElfVerneed entry. + self.offset += vn_next as usize; + + Some(Verneed { + vn_version, + vn_cnt, + vn_file, + vn_aux, + vn_next, + bytes, + ctx: self.ctx, + }) + } + } +} + +/// An ELF [`Version Need`][lsb-verneed] entry . +/// +/// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG +#[derive(Debug)] +pub struct Verneed<'a> { + pub vn_version: u16, + pub vn_cnt: u16, + pub vn_file: u32, + pub vn_aux: u32, + pub vn_next: u32, + + bytes: &'a [u8], + ctx: container::Ctx, +} + +impl<'a> Verneed<'a> { + /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. + pub fn iter(&'a self) -> VernauxIterator<'a> { + VernauxIterator { + bytes: self.bytes, + count: self.vn_cnt as usize, + index: 0, + offset: 0, + ctx: self.ctx, + } + } +} + +/// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. +pub struct VernauxIterator<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, +} + +impl<'a> Iterator for VernauxIterator<'a> { + type Item = Vernaux; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + // Safe to unwrap as the length of the byte slice was validated in VerneedIterator::next. + let ElfVernaux { + vna_hash, + vna_flags, + vna_other, + vna_name, + vna_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Bump the offset to the next ElfVernaux entry. + self.offset += vna_next as usize; + + Some(Vernaux { + vna_hash, + vna_flags, + vna_other, + vna_name, + vna_next, + }) + } + } +} + +/// An ELF [`Version Need Auxiliary`][lsb-vernaux] entry. +/// +/// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG +#[derive(Debug)] +pub struct Vernaux { + pub vna_hash: u32, + pub vna_flags: u16, + pub vna_other: u16, + pub vna_name: u32, + pub vna_next: u32, +} + +#[cfg(test)] +mod test { + use super::{ElfVernaux, ElfVerneed}; + use core::mem::size_of; + + #[test] + fn check_size() { + assert_eq!(16, size_of::()); + assert_eq!(16, size_of::()); + } +} + +} From dc1e26bc25fc3d0551f4c6921981bb0a9d452e5a Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Fri, 13 Aug 2021 23:08:10 +0200 Subject: [PATCH 03/20] elf.symver: add tests for parsing .gnu.version_r section To get started commit binaries ... --- tests/bins/elf/symver/Makefile | 22 +++++++ tests/bins/elf/symver/lib.c | 24 ++++++++ tests/bins/elf/symver/lib.ver | 11 ++++ tests/bins/elf/symver/lib32.so | Bin 0 -> 14808 bytes tests/bins/elf/symver/lib64.so | Bin 0 -> 15720 bytes tests/bins/elf/symver/main.c | 30 ++++++++++ tests/bins/elf/symver/prog32 | Bin 0 -> 15292 bytes tests/bins/elf/symver/prog64 | Bin 0 -> 16280 bytes tests/elf.rs | 102 +++++++++++++++++++++++++++++++++ 9 files changed, 189 insertions(+) create mode 100644 tests/bins/elf/symver/Makefile create mode 100644 tests/bins/elf/symver/lib.c create mode 100644 tests/bins/elf/symver/lib.ver create mode 100755 tests/bins/elf/symver/lib32.so create mode 100755 tests/bins/elf/symver/lib64.so create mode 100644 tests/bins/elf/symver/main.c create mode 100755 tests/bins/elf/symver/prog32 create mode 100755 tests/bins/elf/symver/prog64 diff --git a/tests/bins/elf/symver/Makefile b/tests/bins/elf/symver/Makefile new file mode 100644 index 00000000..77fb466b --- /dev/null +++ b/tests/bins/elf/symver/Makefile @@ -0,0 +1,22 @@ +# Build shared libraries (32/64) with versioned symbols and applications (32/64). + +RELF = readelf -W --dynamic --dyn-syms --version-info +COMP = $(CC) -m$* -Os -o $@ $^ + +all: prog32 prog64 lib32.so lib64.so + +prog%: main.c lib%.so + $(COMP) -Wl,-rpath=$(CURDIR) -ldl + +# Symbol versioning does not work with LTO enabled. +# Just turn it off explicitly in case it's enabled by default. +lib%.so: lib.c + $(COMP) -shared -fPIC -Wl,--version-script=lib.ver -fno-lto + +elf: all + $(RELF) prog32 lib32.so + $(RELF) prog64 lib64.so + +clean: + $(RM) prog32 lib32.so + $(RM) prog64 lib64.so diff --git a/tests/bins/elf/symver/lib.c b/tests/bins/elf/symver/lib.c new file mode 100644 index 00000000..f956a66f --- /dev/null +++ b/tests/bins/elf/symver/lib.c @@ -0,0 +1,24 @@ +#include + +// Bind function symbols to version nodes. +// +// ..@ -> Is the unversioned symbol. +// ..@@.. -> Is the default symbol. +// +// For details check: +// https://sourceware.org/binutils/docs/ld/VERSION.html#VERSION +__asm__ (".symver some_func_v0,some_func@"); +__asm__ (".symver some_func_v1,some_func@v1"); +__asm__ (".symver some_func_v2,some_func@@v2"); + +void some_func_v0() { + puts("some_func_v0"); +} + +void some_func_v1() { + puts("some_func_v1"); +} + +void some_func_v2() { + puts("some_func_v2"); +} diff --git a/tests/bins/elf/symver/lib.ver b/tests/bins/elf/symver/lib.ver new file mode 100644 index 00000000..d070a492 --- /dev/null +++ b/tests/bins/elf/symver/lib.ver @@ -0,0 +1,11 @@ +v1 { + global: + some_func; + local: + *; +}; + +v2 { + global: + some_func; +} v1; diff --git a/tests/bins/elf/symver/lib32.so b/tests/bins/elf/symver/lib32.so new file mode 100755 index 0000000000000000000000000000000000000000..244c24f17054a425ff96a2a3a4a4db75a0a9c15a GIT binary patch literal 14808 zcmeHOe{38_6`ng^lOyr3x>hNPOOMEHT_}s~gf>u#obA|YsGT~t1C&5F>$|n@;O^Gk z?HN0wBI(hRV63LCkXltjNR5Q3`b)I5LLlMv02PW5M1cASNC;7M6_#+-6q(j^`M%km z^Tss=@n@N5y?gJ?`{vE;o7o%h?aY@4hle7Oh)~oZ8U(G;Rw4FKe%SZy5eczPbcriP zhx98vWk<|s2q92UMuVhIeh~%aOTzvH48^Pf4%c5bgjoHU&mHh3z*_)gyeQ>|!25h& zfgsb*7-ScC#lx`4$~XR!n|=U(#a-}`m46iet^~>gz8*fZ@*jpD=39vEqkCcu{&4@} zXp7AH5K^{7Hh>QtIwJn^^b2ik`?fxNZ2KP`JanS*LV0iRF=DR(GiSs^L}W?Tj|Mti zG6vgaTw>e0{XD!2`ZjRqsk09GYVc$8w+Zq_VxJWypbRMkau@hW$ZmtqwiG3xFgJ=4 zP(BTQ@KPbJfou~^(jM7hmqPXzfm3v;c*Q%e3`p9-G^g`U!7RB}(KStB9-J64({|CG z&X!!eI59Gia|-r^HJP)0-{u}OHD{Sq*@BhJ9=F9z*)55Zlef*Oav>#h*~yeqa*P|O z?e2vtW_!eJuQ)J#uz$epHM;9skFneDV9cMSbE6efP050of^;JApFH}}N&%0~Mje5VT{5XEJ8oprfDL#dI5A(T#!ra^cPuzn#mDoPMdY+EuSU%7MmMuv7uHh!S^0W?w{QWiAPSc z1I&H2e14_<+MAxUgv#7=eHMFYw?JNi%{|xmgSUhE_E>JCP5OJ*b>C0z{KG_wlj+Ie?l?_-tIb&LcyD6hkeFlt+N;Q>wzCd{~KY?uc9Gzn0tf| z3=DiU(S=>-*Cl#-j2>)#ek-vLielKK&HNqX1%sl@+P~a@zddBjJ^QJmTXM@&Q$|XxZ`C*5yqV%={Sx-{ryX-T z=S*5TGwnLXl4+IaM9Rs}Va!+?+wPZ?V2+w)6J9%pEoAwgluy&Um89$ zU2@EfRY>FTK`2&l*ub^yzwJ=p$iV^NabKB(HwS&VdH)!SaDpM|jd@`B*8aX>^VXrE z@xcjmqOX5=5C4waa`}n-O$7R zgDLI?^NbU5TtCTNKUKSwTu1K#6W0&sdP`g|9{}^3xQ@&3StOy8dO*aJm;;6D z28NuVBOsVE_BSCFj_V3pF5Dp;)w3ZS*Bdg=eJN7yK7s!l;%^R?6iqL2LDKr$~} zU&z-(SES-Cp@4YeCK5hB3SE)H{Tu~c%aLCHJRT;b@)1j&{5x<9m^iNEYySrKC-|9T z;^caW4R|Y1Ild&u;&}WO%>EI_^GYkgF}I3TyR55lA_=2=bKE~dIIi2Rz%hm*rF$q~ z`)rr%ar*`w+akrFP={L*aa>mw(WjU>R@4PtHwt+F^M6dX+Y06!l{tYVeF_6zV}O8^ S%6~z^xFl}!Cgp@rss94#pFvyz literal 0 HcmV?d00001 diff --git a/tests/bins/elf/symver/lib64.so b/tests/bins/elf/symver/lib64.so new file mode 100755 index 0000000000000000000000000000000000000000..6fa3e779185849688acdfc93990f0e9af0784fd5 GIT binary patch literal 15720 zcmeHOeTZCF6~D8)X*Z_HY|^C0q?#8=?Y1|O^n+=K!(O@WrwjhZ96Hp|If`a&$Xxm6ZDWV`H^$(eTK?|*JDwVAt8PB=*{NB91 znJLmgMC9J(y>rg*eB67_yEAj&zVp5^J~`2qN-4B%^-0B2&}Wc96#WN;Arh#AY6G1= zsP5FZJuTHuoOg?XK$XPhKGv~HWSb7lkU*hlSoi0#Gc`}2V|qLen}t6@ew)?xrntxCdk4_y6bNOn1u275e)v05n z#Y#Cpm7Ol;jjp{WoH>^bXA9+Qv2Z@G7V1$=)heZYI9o5zsA6Gy#;;ZU1I!)xD6wjB zP%RFrBa=rDkA_43kiXvy)U3`Px~i$=uH`PK^7qT?*)*?*JLxDJW6~%^>qb3st63|o zf* z2M)P$$lzG3MpEjv1kP(2Z6$%5tQgH|0@vTa;;=n13{972?V8Jg%Ye&(%Ye&(%Ye&( z%fSC@2L77<(BCr`-ssIdy6%PhmCAhWsi>>@yUc}O^?x#vda(n{v( zKOf3Gz1p2gJ)8Ocjc6+cI4uEsn~mA@uCbOsk9T>vL~2#vcRF)nc!=4|rB|bknMa3@ zlhEAe(fPF*8r-w%#)wUQ;p(iP-t|=;hPpc2;(0zC@AWw|k(Es}shx(u&V1VNZ!zCZ zJo(4@iZi6cx|7R*%Ye&(%Ye&(%Ye&(%Ye&(%Ye&(%Ye(k|8xf8r)=TkKz>ikKSI@g!ldE2eM zkJCf7J%sUdwks6FBdRv_O>El!sq}_NdY9Fq9S_{QZ;yVw$&bUv=qON3Jj-P43>{eu zD-w1#fsyE%%Ye&(%Ye&(%Ye&(%Ye&(%Ye&(%Ye&()E}bWkn0?|K9uVkxxL>I)hormz#o!KcmX8a8q@4rOGIBtj>NIZIK ztmlh7(`~Aje{gctta{WVqobej`l*uo9&d2aAM^(-dt2oB_Oz)Z z=JnM@b;^OnhaWv+*Z6{k?-m^E74{z^CPi1JKQIXLJF4;971p28D)mKTxL?%Y0$)SfcX7vXT5n8leobQvG$79!D|4ieX6h3c=@^fNRw0cnw4E)#H z{^q*x=LNUxzF*dH=u=@`_cwIFzPSEhi*(|e2ksYn2EKt3ky3Xk6#y}GH{o6UDYdTS z-GsNRPv1|tw>E^9PH6j^6&DF_SD!xJAp2 z@u-<<*2%(W~*5$l+V>mr6qDnNFiB9YfPzpo`w^K6DLQGjfdmM$EaXG z@o&CCrK5AlwMsajE$66cKcOE=3N*g4v&Tn{9UUcm`d$dfGZHs5c9I~f{FffWBa;sv z9+?auoR~N@J{3-l9G)Df7X^59t$;y5_5APl@&LaR5F`u!trrjCaR!N?^lwZZ^FMg$ zU`=f0*Op3AcA98ZHFO?axf135xpLiKs8$y8)o4liF*IE-6mxqEIi(Hev$c8U=a$M8 z%+RQ6G>iFatxzei5km12T}Yi#@t;P~9GHc1dCz1CF$#)Any$^-p_#KAq zTmGHii-NuUyh2nX#QlwaYq3AjFaEGrez*^cCH(CEKTUexd%z$26VP7z9^l45`^>^0 z`-W45aaZuieg_o$8#2*N!a-D@BaZhH@W=iEl)i~|!}wtbI!{{Ot3YmuA?WADpU2PR z#P|i)@4WEd2L9OJfO=wR&maC5iQz8c|B&pDK(B})#*hAyzsJS@u-IY$1`0d&=UmwS z-y)22D%-x+@00vaVINB-x?voUc|VZ!_rx!zFcgZPKhVpTKlVSMZ+7qp{5{Jb`vXwy z(_wFq|A*pFk8(`&*3!F01i|_P{V6G!$Mx&?j3wxOMvTvP|Id&n>3_yErl8A4jL*1! z0G=a;_lWXwjrVJKkI=S<40?@x(+Pj<4_EgRNDX#IYRSM~AqB4;^dIk^_=|~JLlPy+ zI*y0y&)1ht;E(sY8uzoeQl37yPUsi(Em9@@RUozlhI%4`zz_5vq)7UEfwA<&A9l7s zWPU}MZ7_c92m3qtb6?4J3jvIYd8~rJ{ca@ulN4(&b|xl}fZJtY{5j@(Jj@5*3+oBP g*olqXnSF-+1v(OqCF +#include + +// Links against default symbol in the lib.so. +extern void some_func(); + +int main() { + // Call the default version. + some_func(); + +#ifdef _GNU_SOURCE + typedef void (*fnptr)(); + + // Unversioned & version lookup. + fnptr fn_v0 = (fnptr)dlsym(RTLD_DEFAULT, "some_func"); + fnptr fn_v1 = (fnptr)dlvsym(RTLD_DEFAULT, "some_func", "v1"); + fnptr fn_v2 = (fnptr)dlvsym(RTLD_DEFAULT, "some_func", "v2"); + + assert(fn_v0 != 0); + assert(fn_v1 != 0); + assert(fn_v2 != 0); + + fn_v0(); + fn_v1(); + fn_v2(); +#endif + + return 0; +} diff --git a/tests/bins/elf/symver/prog32 b/tests/bins/elf/symver/prog32 new file mode 100755 index 0000000000000000000000000000000000000000..f6981446cd424bf2fe3a0e00d9a61bc957b9d7e9 GIT binary patch literal 15292 zcmeHOeQaA-6~A`U)ZK#XcIn8rYkR`1B75JXbh>73hR)oWun_ksc6~b_q*@i z7(H_rli)X1^KF3vmkmQ}C00@R8|f4DvD9ii@zx%D3rlBR&nkVwDHw z?}NWv6GHJS)Fmtb2KZrKMr@y_yb*q9|AT0Y%=!=_oK6jd)A8l$RHjf2<+GtS%hv!O zbH8TYX7T0U{#3jBvmIyK&)@awU%b9@XR&VQcUeCOW*&+0?h#Werbl>Ybg!)gCfj76 z*e>gkScgnvF0TU9FULm{8dO>K+^61}qVA+Fhp`ws>nYNp^eF?9{>L2lt1dqMBd1U2U3}QIFTL-qpxF$95Tbvt&w9rPhK7K zj)JHh_KeNm7H>VabqZQaB;-7(>i?5r(FOv0Z#IGfqc#rs&jc3vR z`8;~X?XvZF&9y%ML~?ISsoGT|YyKiF%e2lk*}aD%Jxx9}_P?i3_HI5*J@1 zCE9AD#2L8cX28vWn*lciZU)>8xEXLW;AY_eEdw7cyy?QG(zLI701M*r$_HT~CQD~c z)7(2{c+FQ!ulTm9WxPCf^9A1dnFqN3CYcB7aZFM@eG=8o16<`N=|26cw0T#WWZ~&4 zX*bg5;q*aiH__H;KMlLw8>}szI%7U^ar4C6TOQn0y;}Bb8~U}OG~HNzikKIDXsWMo z%fq7jBiN-^>L)r+l}n3R%ECwO!XxF<5>+;cvdPV-O0NX=9xpVOOA$KCrACN{8=%|_ z<(=#Io+~uX^(}$#c4Dg=5Mkoz-l|$kT zpYv}Y^WFOHLlb|%N%r+`tn!|Ays{flBg!T1H?m6yk3(#D(0t)M8mzwfp=q9-Yzo?e zW;H8p^%MiVd>;dRsTq3taUSnEX52XW_%s4w9IrttOVbS-IGll>SIWk1Vqc|v=r-U) zl{aSNFJD+L1W=otF-p_5OOH)dUaH>w(gm;Qjn~c_4U_vXGURdNDomNa4d!tKoZNm9 zF|BAr^86U6x(UGX$^pbhoINkHQ=-s3Svq6&X>n<~-h3g5Lh~yuLZE63@+nQ((of?gVE1a2JAOiXhU&JCnPE&DHSVF*&|{5*aJ+J%QuW3VLoVZ9Qjq zuRO-iY%z`CGRa^42~njZ9<+YX2ZgsVkvMGXY?vsvUzuDM8xEc7L%m7}0$@g=!Z!E`J`L2*}97BU*D5H>VCaQln%_L+uZtR|dY<|x)`yrF>o93&KZ$LKT zw*F1X7DzrJAbB3!C_KA;o*U|GYrlwcJ~ZIdfnOrdy##d3_h}F<;3C@C0V({xE`R+e z7hY}D7R3YC-*5+1?>wUV{`J^&Zg^nCC5s_P~5^VRxX;TPOxmieS5vV{`xP}ME!v$Yoq>#(!6%RHsTLP z{mmWzWgY&OwSH}lzad)j2ck#(_0gk#U-Ymak4tzSyiGrZSa=O7iDl=(&!P+vm)s1v z8E`Y;X28vWn*lciZU)>8xEXLW;AY^z$iUP1z_$;KTS@snDdjU@N`Ak(5PSm6H`u&M z!$+)Q89r3M4wB!P-T=vav+Ke9)|B_6wEyy-B>m2BxoHPc9?`74dXPbR&u`bEzZ3ctlLhh$zgR!M0*snIbw~s2cxm9ZRdnfFJ$QS3xVy|EPk@X`mK$>V2;|@IQ?}zNFy!>|?I3gskh)gJ4=>+oyvUxdxMMOx%-6bo>^wVKsmev97- zTM?UC0sA171azK%$0hzM%9llO00l7Ke?oljW8pXw-%q?Y8-E+@+4H+j48!Ji@tTcK zJN)clk@aoTAon|LnTFUvYI!DWB=F!2-}VorbR#w_Qkj&20{elQuJ>*1?C;;IcWqwR z-ruu+ov!0r8q67aqcAiC-0ao^-5Ajax%nWEXz{E*oX!r!(t6y;=JI;1P!xmNkB0iU{r|Na?X$F1AZgWQ^P{F_ep8XF|L%GO`O*9G1>*&VlNB*T(4DPQ7zo2lqP6 z1z__+$JTYxwLR@~J#uRVitvv@@9efCba!kN`kLPLtE0X8`mU}`o&9=$baihhqsmPV zgZYB&s+f5S8NnmSf4vi2X!M^$m|Ra6z$Cx5w#Vf74a{JjS4*xj~THm zIpal?I=5lW#=>R_;tpxpbe0JWFfa2xN zMiHlfXjD%=3fMqCZ?`NTx#~K0rl_&umX#~+Lz%O~#1U15u*JiO4L}+>OD2_;%3#~e zs0hi4Je({clLF3;3^$m%nq5o2p75>0@AST_H~GCF5_LBK6z7 z5(Nt9+*5WbuE&AX5LB`PL8^G%19c8-1GXE%fviF2I)y|WIR@rhg}5C!kPkv>F{C{j4?vr@Qj&00gO zQXoi$JLG15kAQa8C`&Z}+CnrFQObsED7*F;WAcfN+zyQZ~6iEz> zI)GTtcKQ3?44m?zNXDS#r>BeuwT{gmGj19@8<4h z;~+mAf83L~`<(ZAKhC-Do_lld-gCa%)888m1eBB-b+sZXw$5Q zlX5~wJLl7jG3cno^ruo@ziIaKVmI3hQX5qEy<#OlN_&Is*B#bjloz)-v=Wr|u7e-t z<-->Fn)h;hD}4_J=TnfsP8H3hM|NzBW|A$LbhbF%GTpwTWyiKiAs5-IyFmSF#X)0g z*TAp}9#M0|iGM%^D&!!`Fwuo`)*w{){qUI z2gM{CD#Xztg>-TV_hGOXM(e8>Qmk+l{s6dY_P4FT?_Ytx0DKsSw_FAy>?I)&;;7c{ zE#Nm-u~%l_&~7uC%BRND1uK;w+TE4OWm7|mkxWXN=J;eTYZk0T-ZD+cw5nktH<>cW zirG<>%oGk!;`0!JX(kGV6nw`L=?tXN>4Z6!&L%SH+bB4O%qRz+Or*0)42FHxV!H8s8YtAb5_tkZp=JUKd-xOZ+{82eaXxCY-o<5 zM)Cyd7EjqIm;8Zbno2Dm*Ya;jrYYUxoR*&@nWj{Wk3xS|E__>aYi|el7(GH6v)ivl-I(nl*nQ{A zRn?rFmGilf^lXMXh78i2!+c9$mD5+csza4##sh zw#|LvK>Viof%&mWL-QT*poA#OU)!sFr4u(BrI(DEmzM^I`e*C*BOeI|Jg`iw4b9!l{?xxC9S5LR#mk3{nT{Y;jnXUDTI24Ht0CAM zm+)J|!YKOqRNXcZ0rx4+kGW4brq6eA9Jbs)iXXqef41Y6&nOk&-&gv5e7LXlR(vR4 zdVkoMZMg~bzWz&3(->R0=QPs(_~{y}x$Tea-(PwIUv_utFL8Uri^j}+z_{X1#lO<{ zbFv-3Ic~c9K<&}UB-G=!JW@gW#m|7B0Y3wN2K)^88SpdUXTZ;Zp8-Dueg;;|Kmc!F zmfvZVBq1Nz49pP48ZCkExb*lO$7a|AFtGTKbNbg5p_=jyLftM}W zHoZrBAJ|X$x@|uJ%)DXSYw&bT5AIX~x9wGd>Bhi$YgX0Y%RIgF|33VpWYE2eUR9}! zanL*ADa0QnBGlL$YPz=J>|5)PsB1P|_36tm-b^&v+=SzP*y&#|Izx@K!Ow-7j@HCO z;k#<%p)E6Y@lb2<;n0?@P&giHfbq0&lFJgh8ph)_Jx|hUegl_&(`*Y zwj8bN4z=EOmJw>7S+y$^%ZAz!2k|;X;Z6yo->ba<_s=7a`+X6G^oySXKLdUS{0#UR z@H60Nz|Vl60Y3wN20ls#WZj*tv!fTVROp$DipaDUkc!9;Y6-6{8Gp{WhEYGjpuU#n zW4ylaT#-fMcj%{XJO(rFXB=Y``&i7W?ds~fGQ0(=lrIgp zwME(@tzxhO(s-;@uiIWVjl%#m4{+kbY(TH*tJ%|f;%YqoUJ$@w-Wf&(MaJ8es8_S^ z0UuN$HO1>z$%o1=9K=89;csMKu0OJ+GKC|c&Q(T?q~+`G%euZn$@QU?`1mdkiZ9o* z@IOK&pgvw+Z!7#t{r{D=U$2rEiRCg@J74;ZLLt7lKd*ufsEx`yF5YH)S^uj=e5}?t zmUZQI{KB6PKA>=w#e{Lt1|L-TmObNWEBN)u!=rq^k?*Th^OZ43+5tpiW-r}+Dt zXT>?-sh?i^&EUi0?82Qoj*H;x0Qk)xq?JmHq+7B%D}^5-vFv^%ZCZ(OmCmLu5c~tB zX%6n~85+95>>VEH8tU6KV4Aq^jOML^Rm8FtEZ#3~12C;gbCfm$6c8<$GsiQzkwnH! zTDg3|OcbZpXl`;Uld@7tM5t;(3;ok(BA-tjHd9$Ee^`y>6WAq?EKW`yhKoxwshbt1 zrrEnUzPrcl8R({c2bBg%Wp>{%5Z~R`RjJY26hPsoYW5f$!RX$r%w7F^I^+H3p5ESl zJwxVDytBWDqSD?1bF@&@U0wcFH-?{d|KqNRR5D>D;P2Yp5OeQN_`uEvkCxn>eGj}* z!lSR;bYUh7ITO1alC&YCZx0-k>8x2Sq>zW@Q=k+=9w`(!vi`BzG_jvUvPc^}s_x#< zdp10MaBtP9P^kzuR9J}-pp|!ULTc$Oc92Y|h#rUI*&d| zcxPV=jnoL4Pb3NxDv~^$rOhK6t-Pbb4vs=Pm#q*?=<=ydf(*Gfm9bPrry^pdrctLs z2whHpW0BMZ54(vZd_{6Robl&)NF9L_lj%{knnNsnM05jAFr->Va2ZTubXU6||2vN6 zhq%??UhkA9{(mkg^Fvl^Td5O%5(mx6#J`>W1!K^;i_F)A+kuMPf13RTX@1}?az7QW z4;8n+%tr*nV#w(AzaISCnBz(RW!@_&=P&bKZ~qhUr#Y|q%e+`{iQA<xGUR@aF`G;BESE&;| zfWz${W~l@6tQ!pWm)*YK|y0j+L!sX?CX{NyEOi(h@X6qZ-S%q7k}9=cakS; zTgb^(#9#1T=-mD)#>TAwbkV1H>x?5Qnx zSyHUno*%_6C9I%}S9NmYjUH_}WOW)o8_w&F_dxA^5VoTHOYW{)G MIu_@696X}>H+T%!VgLXD literal 0 HcmV?d00001 diff --git a/tests/elf.rs b/tests/elf.rs index 3bd96f45..1fa1fa0c 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -228,3 +228,105 @@ fn test_oom() { &mut AlignedData(*include_bytes!("bins/elf/gnu_hash/hello.so")); test_oom(&mut aligned_data.0); } + +type SymverExpectation = std::collections::HashMap<&'static str, Vec<&'static str>>; + +fn check_symver_expectations( + bytes: &[u8], + expect: &SymverExpectation, +) -> Result<(), goblin::error::Error> { + let elf = Elf::parse(bytes)?; + + // We expect a version needed section. + assert!(elf.verneed.is_some()); + + // Safe to unwrap as asserted above. + let verneed = elf.verneed.unwrap(); + + // Resolve version strings. + let symstr = |idx: u32| verneed.symstr.get_at(idx as usize).unwrap(); + + // ELF file dependencies with version requirements. + let need_files: Vec<_> = verneed.iter().collect(); + assert_eq!( + expect.keys().len(), + need_files.len(), + "Expected different number of dependencies with version information!" + ); + + for need_file in &need_files { + // Get file name of the dependency. + let file_str = symstr(need_file.vn_file); + + // Check if we expect this dependency. + let expect_vers = expect.get(&file_str); + assert!( + expect_vers.is_some(), + "Unexpected FILE dependency {}!", + file_str + ); + let expect_vers = expect_vers.unwrap(); + + // Version dependencies for this file dependency. + let need_vers: Vec<_> = need_file.iter().collect(); + assert_eq!( + expect_vers.len(), + need_vers.len(), + "Expected different number of version dependencies for {}!", + file_str + ); + + for need_ver in &need_vers { + // Get version string. + let ver_str = symstr(need_ver.vna_name); + + // Check if we expect this version. + assert!( + expect_vers + .iter() + .find(|&expect_ver| &ver_str == expect_ver) + .is_some(), + "Unexpected VERSION dependency {}", + ver_str + ); + } + } + + Ok(()) +} + +#[rustfmt::skip] +#[test] +fn test_symver_verneed() -> Result<(), goblin::error::Error> { + // NOTE: Expected Files & Symbol Versions depend on build system of the test ELF binaries. + // When rebuilding the referenced ELF file the version information must be checked and + // potentially updated: + // > readelf -V + + let expect_lib32: SymverExpectation = [ + ("libc.so.6", vec!["GLIBC_2.0", "GLIBC_2.1.3"]) + ].iter().cloned().collect(); + + let expect_lib64: SymverExpectation = [ + ("libc.so.6", vec!["GLIBC_2.2.5"]) + ].iter().cloned().collect(); + + let expect_prog32: SymverExpectation = [ + ("libc.so.6", vec!["GLIBC_2.0", "GLIBC_2.1.3"]), + ("libdl.so.2", vec!["GLIBC_2.0", "GLIBC_2.1"]), + ("lib32.so", vec!["v2"]), + ].iter().cloned().collect(); + + let expect_prog64: SymverExpectation = [ + ("libdl.so.2", vec!["GLIBC_2.2.5"]), + ("libc.so.6", vec!["GLIBC_2.2.5"]), + ("lib64.so", vec!["v2"]), + ].iter().cloned().collect(); + + check_symver_expectations(include_bytes!("bins/elf/symver/lib32.so"), &expect_lib32)?; + check_symver_expectations(include_bytes!("bins/elf/symver/lib64.so"), &expect_lib64)?; + check_symver_expectations(include_bytes!("bins/elf/symver/prog32"), &expect_prog32)?; + check_symver_expectations(include_bytes!("bins/elf/symver/prog64"), &expect_prog64)?; + + Ok(()) +} From 85ed28da58385edd453fb1d2abc861e7a8e75f02 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sat, 14 Aug 2021 00:13:49 +0200 Subject: [PATCH 04/20] elf.symver: make structs used by user more ergonomic by using usize --- src/elf/symver.rs | 46 +++++++++++++++++++++++----------------------- tests/elf.rs | 2 +- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 574cb595..3082f119 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -15,10 +15,10 @@ //! for need_file in verneed.iter() { //! println!( //! "Depend on {:?} with version(s):", -//! verneed.symstr.get_at(need_file.vn_file as usize) +//! verneed.symstr.get_at(need_file.vn_file) //! ); //! for need_ver in need_file.iter() { -//! println!("{:?}", verneed.symstr.get_at(need_ver.vna_name as usize)); +//! println!("{:?}", verneed.symstr.get_at(need_ver.vna_name)); //! } //! } //! } @@ -204,11 +204,11 @@ impl<'a> Iterator for VerneedIterator<'a> { self.offset += vn_next as usize; Some(Verneed { - vn_version, - vn_cnt, - vn_file, - vn_aux, - vn_next, + vn_version : vn_version as usize, + vn_cnt : vn_cnt as usize, + vn_file : vn_file as usize, + vn_aux : vn_aux as usize, + vn_next : vn_next as usize, bytes, ctx: self.ctx, }) @@ -221,11 +221,11 @@ impl<'a> Iterator for VerneedIterator<'a> { /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG #[derive(Debug)] pub struct Verneed<'a> { - pub vn_version: u16, - pub vn_cnt: u16, - pub vn_file: u32, - pub vn_aux: u32, - pub vn_next: u32, + pub vn_version: usize, + pub vn_cnt: usize, + pub vn_file: usize, + pub vn_aux: usize, + pub vn_next: usize, bytes: &'a [u8], ctx: container::Ctx, @@ -236,7 +236,7 @@ impl<'a> Verneed<'a> { pub fn iter(&'a self) -> VernauxIterator<'a> { VernauxIterator { bytes: self.bytes, - count: self.vn_cnt as usize, + count: self.vn_cnt, index: 0, offset: 0, ctx: self.ctx, @@ -275,11 +275,11 @@ impl<'a> Iterator for VernauxIterator<'a> { self.offset += vna_next as usize; Some(Vernaux { - vna_hash, - vna_flags, - vna_other, - vna_name, - vna_next, + vna_hash : vna_hash as usize, + vna_flags : vna_flags as usize, + vna_other : vna_other as usize, + vna_name : vna_name as usize, + vna_next : vna_next as usize, }) } } @@ -290,11 +290,11 @@ impl<'a> Iterator for VernauxIterator<'a> { /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG #[derive(Debug)] pub struct Vernaux { - pub vna_hash: u32, - pub vna_flags: u16, - pub vna_other: u16, - pub vna_name: u32, - pub vna_next: u32, + pub vna_hash: usize, + pub vna_flags: usize, + pub vna_other: usize, + pub vna_name: usize, + pub vna_next: usize, } #[cfg(test)] diff --git a/tests/elf.rs b/tests/elf.rs index 1fa1fa0c..15250bb1 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -244,7 +244,7 @@ fn check_symver_expectations( let verneed = elf.verneed.unwrap(); // Resolve version strings. - let symstr = |idx: u32| verneed.symstr.get_at(idx as usize).unwrap(); + let symstr = |idx| verneed.symstr.get_at(idx).unwrap(); // ELF file dependencies with version requirements. let need_files: Vec<_> = verneed.iter().collect(); From de9a4e7a8d35d119ba1d801d8308a6282e1812bf Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sat, 14 Aug 2021 21:39:13 +0200 Subject: [PATCH 05/20] elf.symver: API updates Implement some std traits: - IntoIter - ExactSizeIterator - FusedIterator Rename iterator structs similar to std. Add inline attributes. --- src/elf/symver.rs | 65 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 3082f119..39ea922e 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -3,6 +3,8 @@ //! Implementation of the GNU symbol versioning extension according to //! [LSB Core Specification - Symbol Versioning][lsb-symver]. //! +//! # Examples +//! //! List the dependencies of an ELF file that have version needed information along with the //! versions needed for each dependency. //! ```rust @@ -43,6 +45,7 @@ use crate::strtab::Strtab; use crate::elf::section_header::{SectionHeader, SHT_GNU_VERNEED}; use crate::error::{Error, Result}; use scroll::Pread; +use core::iter::FusedIterator; /// An ELF `Version Need` entry Elfxx_Verneed. /// @@ -141,8 +144,19 @@ impl<'a> VerneedSection<'a> { } /// Get an iterator over the [`Verneed`] entries. - pub fn iter(&'a self) -> VerneedIterator<'a> { - VerneedIterator { + #[inline] + pub fn iter(&'a self) -> VerneedIter<'a> { + self.into_iter() + } +} + +impl<'a> IntoIterator for &'_ VerneedSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerneedIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VerneedIter { bytes: self.bytes, count: self.count, index: 0, @@ -153,7 +167,7 @@ impl<'a> VerneedSection<'a> { } /// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. -pub struct VerneedIterator<'a> { +pub struct VerneedIter<'a> { bytes: &'a [u8], count: usize, index: usize, @@ -161,7 +175,7 @@ pub struct VerneedIterator<'a> { ctx: container::Ctx, } -impl<'a> Iterator for VerneedIterator<'a> { +impl<'a> Iterator for VerneedIter<'a> { type Item = Verneed<'a>; fn next(&mut self) -> Option { @@ -192,7 +206,7 @@ impl<'a> Iterator for VerneedIterator<'a> { (vn_next - vn_aux) as usize } else { // For the last entry, ElfVerneed->vn_next == 0. - // Therefore we compute the remaining length of bytes buffer. + // Therefore we compute the remaining length of the bytes buffer. self.bytes.len() - self.offset - vn_aux as usize }; let bytes: &'a [u8] = self @@ -214,8 +228,18 @@ impl<'a> Iterator for VerneedIterator<'a> { }) } } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } } +impl ExactSizeIterator for VerneedIter<'_> {} + +impl FusedIterator for VerneedIter<'_> {} + /// An ELF [`Version Need`][lsb-verneed] entry . /// /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG @@ -233,8 +257,19 @@ pub struct Verneed<'a> { impl<'a> Verneed<'a> { /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. - pub fn iter(&'a self) -> VernauxIterator<'a> { - VernauxIterator { + #[inline] + pub fn iter(&'a self) -> VernauxIter<'a> { + self.into_iter() + } +} + +impl<'a> IntoIterator for &'_ Verneed<'a> { + type Item = as Iterator>::Item; + type IntoIter = VernauxIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VernauxIter { bytes: self.bytes, count: self.vn_cnt, index: 0, @@ -245,7 +280,7 @@ impl<'a> Verneed<'a> { } /// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. -pub struct VernauxIterator<'a> { +pub struct VernauxIter<'a> { bytes: &'a [u8], count: usize, index: usize, @@ -253,7 +288,7 @@ pub struct VernauxIterator<'a> { ctx: container::Ctx, } -impl<'a> Iterator for VernauxIterator<'a> { +impl<'a> Iterator for VernauxIter<'a> { type Item = Vernaux; fn next(&mut self) -> Option { @@ -262,7 +297,7 @@ impl<'a> Iterator for VernauxIterator<'a> { } else { self.index += 1; - // Safe to unwrap as the length of the byte slice was validated in VerneedIterator::next. + // Safe to unwrap as the length of the byte slice was validated in VerneedIter::next. let ElfVernaux { vna_hash, vna_flags, @@ -283,8 +318,18 @@ impl<'a> Iterator for VernauxIterator<'a> { }) } } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } } +impl ExactSizeIterator for VernauxIter<'_> {} + +impl FusedIterator for VernauxIter<'_> {} + /// An ELF [`Version Need Auxiliary`][lsb-vernaux] entry. /// /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG From 61c00ef68b0355a2d54b22052048694ef56608a7 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 15:59:41 +0200 Subject: [PATCH 06/20] elf.symver: add struct definitions and reading part for .gnu.version_d (SHT_GNU_verdef) section + doc example --- src/elf/mod.rs | 7 + src/elf/symver.rs | 341 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 340 insertions(+), 8 deletions(-) diff --git a/src/elf/mod.rs b/src/elf/mod.rs index 1a4a685d..eedb7ed4 100644 --- a/src/elf/mod.rs +++ b/src/elf/mod.rs @@ -79,6 +79,7 @@ if_sylvan! { pub use dynamic::Dynamic; pub use reloc::Reloc; pub use reloc::RelocSection; + pub use symver::VerdefSection; pub use symver::VerneedSection; pub type ProgramHeaders = Vec; @@ -132,6 +133,9 @@ if_sylvan! { pub entry: u64, /// Whether the binary is little endian or not pub little_endian: bool, + /// Contains the version definition information from the optional section + /// [`SHT_GNU_VERDEF`][section_header::SHT_GNU_VERDEF] (GNU extenstion). + pub verdef : Option>, /// Contains the version needed information from the optional section /// [`SHT_GNU_VERNEED`][section_header::SHT_GNU_VERNEED] (GNU extenstion). pub verneed : Option>, @@ -237,6 +241,7 @@ if_sylvan! { entry: misc.entry, little_endian: misc.little_endian, ctx: misc.ctx, + verdef : None, verneed : None, }) } @@ -340,6 +345,7 @@ if_sylvan! { } } + let verdef = symver::VerdefSection::parse(bytes, §ion_headers, ctx)?; let verneed = symver::VerneedSection::parse(bytes, §ion_headers, ctx)?; Ok(Elf { @@ -364,6 +370,7 @@ if_sylvan! { entry: misc.entry, little_endian: misc.little_endian, ctx: ctx, + verdef, verneed, }) } diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 39ea922e..d70fcaa9 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -5,8 +5,8 @@ //! //! # Examples //! -//! List the dependencies of an ELF file that have version needed information along with the -//! versions needed for each dependency. +//! List the dependencies of an ELF file that have [version needed][lsb-verneed] information along +//! with the versions needed for each dependency. //! ```rust //! use goblin::error::Error; //! @@ -29,7 +29,35 @@ //! } //! ``` //! +//! List the [version defined][lsb-verdef] information of an ELF file, effectively listing the version +//! defined by this ELF file. +//! ```rust +//! use goblin::error::Error; +//! +//! pub fn show_verdef(bytes: &[u8]) -> Result<(), Error> { +//! let binary = goblin::elf::Elf::parse(&bytes)?; +//! +//! if let Some(verdef) = &binary.verdef { +//! for def in verdef.iter() { +//! for (n, aux) in def.iter().enumerate() { +//! let name = verdef.symstr.get_at(aux.vda_name); +//! match n { +//! 0 => print!("Name: {:?}", name), +//! 1 => print!(" Parent: {:?}", name), +//! _ => print!(", {:?}", name), +//! } +//! } +//! print!("\n"); +//! } +//! } +//! +//! Ok(()) +//! } +//! ``` +//! //! [lsb-symver]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html +//! [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERRQMTS +//! [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERDEFS macro_rules! if_cfg_elf { ($($i:item)*) => ($( @@ -42,11 +70,49 @@ if_cfg_elf! { use crate::container; use crate::strtab::Strtab; -use crate::elf::section_header::{SectionHeader, SHT_GNU_VERNEED}; +use crate::elf::section_header::{SectionHeader, SHT_GNU_VERNEED, SHT_GNU_VERDEF}; use crate::error::{Error, Result}; use scroll::Pread; use core::iter::FusedIterator; +/******************** + * ELF Structures * + ********************/ + +/// An ELF `Version Definition` entry Elfxx_Verdef. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVerdef { + /// Version revision. This field shall be set to 1. + vd_version : u16, + /// Version information flag bitmask. + vd_flags : u16, + /// Version index numeric value referencing the SHT_GNU_versym section. + vd_ndx : u16, + /// Number of associated verdaux array entries. + vd_cnt: u16, + /// Version name hash value (ELF hash function). + vd_hash : u32, + /// Offset in bytes to a corresponding entry in an array of Elfxx_Verdaux structures. + vd_aux : u32, + /// Offset to the next verdef entry, in bytes. + vd_next : u32, +} + +/// An ELF `Version Definition Auxiliary` entry Elfxx_Verdaux. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVerdaux { + /// Offset to the version or dependency name string in the section header, in bytes. + vda_name: u32, + /// Offset to the next verdaux entry, in bytes. + vda_next : u32, +} + /// An ELF `Version Need` entry Elfxx_Verneed. /// /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG @@ -86,8 +152,264 @@ struct ElfVernaux { vna_next: u32, } -/// Helper struct to iterate over [`Version Needed`][Verneed] and [`Version Needed -/// Auxiliary`][Vernaux] entries. +/************************ + * Version Definition * + ************************/ + +/// Helper struct to iterate over [Version Definition][Verdef] and [Version Definition +/// Auxiliary][Verdaux] entries. +#[derive(Debug)] +pub struct VerdefSection<'a> { + /// String table used to resolve version strings. + pub symstr: Strtab<'a>, + bytes: &'a [u8], + count: usize, + ctx: container::Ctx, +} + +impl<'a> VerdefSection<'a> { + pub fn parse(bytes: &'a[u8], shdrs : &'_ [SectionHeader], ctx: container::Ctx) -> Result>> { + // Get fields needed from optional `version definition` section. + let (link_idx, offset, size, count) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { + ( + shdr.sh_link as usize, // Encodes the string table. + shdr.sh_offset as usize, + shdr.sh_size as usize, + shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. + ) + } else { + return Ok(None); + }; + + // Get string table which is used to resolve version strings. + let symstr = { + // Linked section refers to string table. + let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( + "Section header of string table for SHT_GNU_VERDEF section not found!".into(), + ))?; + + Strtab::parse( + bytes, + shdr_link.sh_offset as usize, + shdr_link.sh_size as usize, + 0x0, /* Delimiter */ + )? + }; + + // Get a slice of bytes of the `version definition` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; + + Ok(Some(VerdefSection { + symstr, + bytes, + count, + ctx, + })) + } + + /// Get an iterator over [`Verdef`] entries. + #[inline] + pub fn iter(&'a self) -> VerdefIter<'a> { + self.into_iter() + } +} + +impl<'a> IntoIterator for &'_ VerdefSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerdefIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VerdefIter { + bytes: self.bytes, + count: self.count, + index: 0, + offset: 0, + ctx: self.ctx, + } + } +} + +/// Iterator over the [`Verdef`] entries from the [`SHT_GNU_VERDEF`] section. +pub struct VerdefIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, +} + +impl<'a> Iterator for VerdefIter<'a> { + type Item = Verdef<'a>; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. + let ElfVerdef { + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, + vd_aux, + vd_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Get a slice of bytes of the `Verdaux` entries. + // + // | Verdef | .. | Verdef | Verdaux | Verdaux | .. | Verdef | .. + // ^-------------^ + // offset ^---------^ + // vd_aux + // ^---------------------------------^ + // vd_next + // + // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. + let len = if vd_next > 0 { + (vd_next - vd_aux) as usize + } else { + // For the last entry, ElfVerdef->vd_next == 0. + // Therefore we compute the remaining length of the bytes buffer. + self.bytes.len() - self.offset - vd_aux as usize + }; + let bytes : &'a [u8] = self + .bytes + .pread_with(self.offset + vd_aux as usize, len) + .unwrap(); + + // Bump the offset to the next ElfVerdef entry. + self.offset += vd_next as usize; + + Some(Verdef{ + vd_version: vd_version as usize, + vd_flags: vd_flags as usize, + vd_ndx: vd_ndx as usize, + vd_cnt: vd_cnt as usize, + vd_hash: vd_hash as usize, + vd_aux: vd_aux as usize, + vd_next: vd_next as usize, + bytes, + ctx: self.ctx, + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } +} + +impl ExactSizeIterator for VerdefIter<'_> {} + +impl FusedIterator for VerdefIter<'_> {} + +/// An ELF [Version Definition][lsb-verdef] entry . +/// +/// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES +#[derive(Debug)] +pub struct Verdef<'a> { + pub vd_version: usize, + pub vd_flags: usize, + pub vd_ndx: usize, + pub vd_cnt: usize, + pub vd_hash: usize, + pub vd_aux : usize, + pub vd_next: usize, + + bytes: &'a [u8], + ctx: container::Ctx, +} + +impl<'a> Verdef<'a> { + /// Get an iterator over the [`Verdaux`] entries of this [`Verdef`] entry. + #[inline] + pub fn iter(&'a self) -> VerdauxIter<'a> { + self.into_iter() + } +} + +impl<'a> IntoIterator for &'_ Verdef<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerdauxIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + VerdauxIter { + bytes: self.bytes, + count: self.vd_cnt, + index: 0, + offset: 0, + ctx: self.ctx, + } + } +} + +/// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. +pub struct VerdauxIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, +} + +impl<'a> Iterator for VerdauxIter<'a> { + type Item = Verdaux; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + // Safe to unwrap as length of the byte slice was validated in the VerdefIter::next. + let ElfVerdaux { + vda_name, + vda_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Bump the offset to the next ElfVerdaux entry. + self.offset += vda_next as usize; + + Some(Verdaux { + vda_name: vda_name as usize, + vda_next: vda_next as usize, + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } +} + +impl ExactSizeIterator for VerdauxIter<'_> {} + +impl FusedIterator for VerdauxIter<'_> {} + +/// An ELF [Version Definition Auxiliary][lsb-verdaux] entry. +/// +/// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS +#[derive(Debug)] +pub struct Verdaux { + pub vda_name: usize, + pub vda_next: usize, +} + +/************************** + * Version Requirements * + **************************/ + +/// Helper struct to iterate over [Version Needed][Verneed] and [Version Needed +/// Auxiliary][Vernaux] entries. #[derive(Debug)] pub struct VerneedSection<'a> { /// String table used to resolve version strings. @@ -183,6 +505,7 @@ impl<'a> Iterator for VerneedIter<'a> { None } else { self.index += 1; + // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. let ElfVerneed { vn_version, @@ -240,7 +563,7 @@ impl ExactSizeIterator for VerneedIter<'_> {} impl FusedIterator for VerneedIter<'_> {} -/// An ELF [`Version Need`][lsb-verneed] entry . +/// An ELF [Version Need][lsb-verneed] entry . /// /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG #[derive(Debug)] @@ -330,7 +653,7 @@ impl ExactSizeIterator for VernauxIter<'_> {} impl FusedIterator for VernauxIter<'_> {} -/// An ELF [`Version Need Auxiliary`][lsb-vernaux] entry. +/// An ELF [Version Need Auxiliary][lsb-vernaux] entry. /// /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG #[derive(Debug)] @@ -344,11 +667,13 @@ pub struct Vernaux { #[cfg(test)] mod test { - use super::{ElfVernaux, ElfVerneed}; + use super::{ElfVerdef, ElfVerdaux, ElfVernaux, ElfVerneed}; use core::mem::size_of; #[test] fn check_size() { + assert_eq!(20, size_of::()); + assert_eq!(8, size_of::()); assert_eq!(16, size_of::()); assert_eq!(16, size_of::()); } From 753ad138c195e8bddbffdcd21b83535dd1eb5937 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 17:59:51 +0200 Subject: [PATCH 07/20] elf.symver: Use sub-module as feature guard Use sub-module rather than macro to implement feature guard, as `cargo fmt` doesn't seem to see through the macro based approach. --- src/elf/symver.rs | 1089 ++++++++++++++++++++++----------------------- tests/elf.rs | 6 +- 2 files changed, 546 insertions(+), 549 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index d70fcaa9..f0b4940f 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -17,10 +17,10 @@ //! for need_file in verneed.iter() { //! println!( //! "Depend on {:?} with version(s):", -//! verneed.symstr.get_at(need_file.vn_file) +//! verneed.verstr.get_at(need_file.vn_file) //! ); //! for need_ver in need_file.iter() { -//! println!("{:?}", verneed.symstr.get_at(need_ver.vna_name)); +//! println!("{:?}", verneed.verstr.get_at(need_ver.vna_name)); //! } //! } //! } @@ -40,7 +40,7 @@ //! if let Some(verdef) = &binary.verdef { //! for def in verdef.iter() { //! for (n, aux) in def.iter().enumerate() { -//! let name = verdef.symstr.get_at(aux.vda_name); +//! let name = verdef.verstr.get_at(aux.vda_name); //! match n { //! 0 => print!("Name: {:?}", name), //! 1 => print!(" Parent: {:?}", name), @@ -59,624 +59,621 @@ //! [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERRQMTS //! [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERDEFS -macro_rules! if_cfg_elf { - ($($i:item)*) => ($( - #[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] - $i - )*) -} +#[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] +pub use symver_impl::*; + +#[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] +mod symver_impl { + use crate::container; + use crate::elf::section_header::{SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED}; + use crate::error::{Error, Result}; + use crate::strtab::Strtab; + use core::iter::FusedIterator; + use scroll::Pread; + + /******************** + * ELF Structures * + ********************/ + + /// An ELF `Version Definition` entry Elfxx_Verdef. + /// + /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES + #[repr(C)] + #[derive(Debug, Pread)] + struct ElfVerdef { + /// Version revision. This field shall be set to 1. + vd_version: u16, + /// Version information flag bitmask. + vd_flags: u16, + /// Version index numeric value referencing the SHT_GNU_versym section. + vd_ndx: u16, + /// Number of associated verdaux array entries. + vd_cnt: u16, + /// Version name hash value (ELF hash function). + vd_hash: u32, + /// Offset in bytes to a corresponding entry in an array of Elfxx_Verdaux structures. + vd_aux: u32, + /// Offset to the next verdef entry, in bytes. + vd_next: u32, + } -if_cfg_elf! { - -use crate::container; -use crate::strtab::Strtab; -use crate::elf::section_header::{SectionHeader, SHT_GNU_VERNEED, SHT_GNU_VERDEF}; -use crate::error::{Error, Result}; -use scroll::Pread; -use core::iter::FusedIterator; - -/******************** - * ELF Structures * - ********************/ - -/// An ELF `Version Definition` entry Elfxx_Verdef. -/// -/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES -#[repr(C)] -#[derive(Debug, Pread)] -struct ElfVerdef { - /// Version revision. This field shall be set to 1. - vd_version : u16, - /// Version information flag bitmask. - vd_flags : u16, - /// Version index numeric value referencing the SHT_GNU_versym section. - vd_ndx : u16, - /// Number of associated verdaux array entries. - vd_cnt: u16, - /// Version name hash value (ELF hash function). - vd_hash : u32, - /// Offset in bytes to a corresponding entry in an array of Elfxx_Verdaux structures. - vd_aux : u32, - /// Offset to the next verdef entry, in bytes. - vd_next : u32, -} + /// An ELF `Version Definition Auxiliary` entry Elfxx_Verdaux. + /// + /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS + #[repr(C)] + #[derive(Debug, Pread)] + struct ElfVerdaux { + /// Offset to the version or dependency name string in the section header, in bytes. + vda_name: u32, + /// Offset to the next verdaux entry, in bytes. + vda_next: u32, + } -/// An ELF `Version Definition Auxiliary` entry Elfxx_Verdaux. -/// -/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS -#[repr(C)] -#[derive(Debug, Pread)] -struct ElfVerdaux { - /// Offset to the version or dependency name string in the section header, in bytes. - vda_name: u32, - /// Offset to the next verdaux entry, in bytes. - vda_next : u32, -} + /// An ELF `Version Need` entry Elfxx_Verneed. + /// + /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG + #[repr(C)] + #[derive(Debug, Pread)] + struct ElfVerneed { + /// Version of structure. This value is currently set to 1, and will be reset if the versioning + /// implementation is incompatibly altered. + vn_version: u16, + /// Number of associated verneed array entries. + vn_cnt: u16, + /// Offset to the file name string in the section header, in bytes. + vn_file: u32, + /// Offset to a corresponding entry in the vernaux array, in bytes. + vn_aux: u32, + /// Offset to the next verneed entry, in bytes. + vn_next: u32, + } -/// An ELF `Version Need` entry Elfxx_Verneed. -/// -/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG -#[repr(C)] -#[derive(Debug, Pread)] -struct ElfVerneed { - /// Version of structure. This value is currently set to 1, and will be reset if the versioning - /// implementation is incompatibly altered. - vn_version: u16, - /// Number of associated verneed array entries. - vn_cnt: u16, - /// Offset to the file name string in the section header, in bytes. - vn_file: u32, - /// Offset to a corresponding entry in the vernaux array, in bytes. - vn_aux: u32, - /// Offset to the next verneed entry, in bytes. - vn_next: u32, -} + /// An ELF `Version Need Auxiliary` entry Elfxx_Vernaux. + /// + /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG + #[repr(C)] + #[derive(Debug, Pread)] + struct ElfVernaux { + /// Dependency name hash value (ELF hash function). + vna_hash: u32, + /// Dependency information flag bitmask. + vna_flags: u16, + /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 + /// controls whether or not the object is hidden; if this bit is set, the object cannot be used + /// and the static linker will ignore the symbol's presence in the object. + vna_other: u16, + /// Offset to the dependency name string in the section header, in bytes. + vna_name: u32, + /// Offset to the next vernaux entry, in bytes. + vna_next: u32, + } -/// An ELF `Version Need Auxiliary` entry Elfxx_Vernaux. -/// -/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG -#[repr(C)] -#[derive(Debug, Pread)] -struct ElfVernaux { - /// Dependency name hash value (ELF hash function). - vna_hash: u32, - /// Dependency information flag bitmask. - vna_flags: u16, - /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 - /// controls whether or not the object is hidden; if this bit is set, the object cannot be used - /// and the static linker will ignore the symbol's presence in the object. - vna_other: u16, - /// Offset to the dependency name string in the section header, in bytes. - vna_name: u32, - /// Offset to the next vernaux entry, in bytes. - vna_next: u32, -} + /************************ + * Version Definition * + ************************/ -/************************ - * Version Definition * - ************************/ - -/// Helper struct to iterate over [Version Definition][Verdef] and [Version Definition -/// Auxiliary][Verdaux] entries. -#[derive(Debug)] -pub struct VerdefSection<'a> { - /// String table used to resolve version strings. - pub symstr: Strtab<'a>, - bytes: &'a [u8], - count: usize, - ctx: container::Ctx, -} + /// Helper struct to iterate over [Version Definition][Verdef] and [Version Definition + /// Auxiliary][Verdaux] entries. + #[derive(Debug)] + pub struct VerdefSection<'a> { + /// String table used to resolve version strings. + pub verstr: Strtab<'a>, + bytes: &'a [u8], + count: usize, + ctx: container::Ctx, + } -impl<'a> VerdefSection<'a> { - pub fn parse(bytes: &'a[u8], shdrs : &'_ [SectionHeader], ctx: container::Ctx) -> Result>> { - // Get fields needed from optional `version definition` section. - let (link_idx, offset, size, count) = - if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { - ( - shdr.sh_link as usize, // Encodes the string table. - shdr.sh_offset as usize, - shdr.sh_size as usize, - shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. - ) - } else { - return Ok(None); + impl<'a> VerdefSection<'a> { + pub fn parse( + bytes: &'a [u8], + shdrs: &'_ [SectionHeader], + ctx: container::Ctx, + ) -> Result>> { + // Get fields needed from optional `version definition` section. + let (link_idx, offset, size, count) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { + ( + shdr.sh_link as usize, // Encodes the string table. + shdr.sh_offset as usize, + shdr.sh_size as usize, + shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. + ) + } else { + return Ok(None); + }; + + // Get string table which is used to resolve version strings. + let verstr = { + // Linked section refers to string table. + let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( + "Section header of string table for SHT_GNU_VERDEF section not found!".into(), + ))?; + + Strtab::parse( + bytes, + shdr_link.sh_offset as usize, + shdr_link.sh_size as usize, + 0x0, /* Delimiter */ + )? }; - // Get string table which is used to resolve version strings. - let symstr = { - // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( - "Section header of string table for SHT_GNU_VERDEF section not found!".into(), - ))?; + // Get a slice of bytes of the `version definition` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - Strtab::parse( + Ok(Some(VerdefSection { + verstr, bytes, - shdr_link.sh_offset as usize, - shdr_link.sh_size as usize, - 0x0, /* Delimiter */ - )? - }; - - // Get a slice of bytes of the `version definition` section content. - let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - - Ok(Some(VerdefSection { - symstr, - bytes, - count, - ctx, - })) - } + count, + ctx, + })) + } - /// Get an iterator over [`Verdef`] entries. - #[inline] - pub fn iter(&'a self) -> VerdefIter<'a> { - self.into_iter() + /// Get an iterator over [`Verdef`] entries. + #[inline] + pub fn iter(&'a self) -> VerdefIter<'a> { + self.into_iter() + } } -} -impl<'a> IntoIterator for &'_ VerdefSection<'a> { - type Item = as Iterator>::Item; - type IntoIter = VerdefIter<'a>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - VerdefIter { - bytes: self.bytes, - count: self.count, - index: 0, - offset: 0, - ctx: self.ctx, + impl<'a> IntoIterator for &'_ VerdefSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerdefIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VerdefIter { + bytes: self.bytes, + count: self.count, + index: 0, + offset: 0, + ctx: self.ctx, + } } } -} -/// Iterator over the [`Verdef`] entries from the [`SHT_GNU_VERDEF`] section. -pub struct VerdefIter<'a> { - bytes: &'a [u8], - count: usize, - index: usize, - offset: usize, - ctx: container::Ctx, -} + /// Iterator over the [`Verdef`] entries from the [`SHT_GNU_VERDEF`] section. + pub struct VerdefIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, + } -impl<'a> Iterator for VerdefIter<'a> { - type Item = Verdef<'a>; - - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; - - // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. - let ElfVerdef { - vd_version, - vd_flags, - vd_ndx, - vd_cnt, - vd_hash, - vd_aux, - vd_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - - // Get a slice of bytes of the `Verdaux` entries. - // - // | Verdef | .. | Verdef | Verdaux | Verdaux | .. | Verdef | .. - // ^-------------^ - // offset ^---------^ - // vd_aux - // ^---------------------------------^ - // vd_next - // - // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. - let len = if vd_next > 0 { - (vd_next - vd_aux) as usize + impl<'a> Iterator for VerdefIter<'a> { + type Item = Verdef<'a>; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None } else { - // For the last entry, ElfVerdef->vd_next == 0. - // Therefore we compute the remaining length of the bytes buffer. - self.bytes.len() - self.offset - vd_aux as usize - }; - let bytes : &'a [u8] = self - .bytes - .pread_with(self.offset + vd_aux as usize, len) - .unwrap(); - - // Bump the offset to the next ElfVerdef entry. - self.offset += vd_next as usize; - - Some(Verdef{ - vd_version: vd_version as usize, - vd_flags: vd_flags as usize, - vd_ndx: vd_ndx as usize, - vd_cnt: vd_cnt as usize, - vd_hash: vd_hash as usize, - vd_aux: vd_aux as usize, - vd_next: vd_next as usize, - bytes, - ctx: self.ctx, - }) + self.index += 1; + + // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. + let ElfVerdef { + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, + vd_aux, + vd_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Get a slice of bytes of the `Verdaux` entries. + // + // | Verdef | .. | Verdef | Verdaux | Verdaux | .. | Verdef | .. + // ^-------------^ + // offset ^---------^ + // vd_aux + // ^---------------------------------^ + // vd_next + // + // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. + let len = if vd_next > 0 { + (vd_next - vd_aux) as usize + } else { + // For the last entry, ElfVerdef->vd_next == 0. + // Therefore we compute the remaining length of the bytes buffer. + self.bytes.len() - self.offset - vd_aux as usize + }; + let bytes: &'a [u8] = self + .bytes + .pread_with(self.offset + vd_aux as usize, len) + .unwrap(); + + // Bump the offset to the next ElfVerdef entry. + self.offset += vd_next as usize; + + Some(Verdef { + vd_version: vd_version as usize, + vd_flags: vd_flags as usize, + vd_ndx: vd_ndx as usize, + vd_cnt: vd_cnt as usize, + vd_hash: vd_hash as usize, + vd_aux: vd_aux as usize, + vd_next: vd_next as usize, + bytes, + ctx: self.ctx, + }) + } } - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; - (len, Some(len)) + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } } -} -impl ExactSizeIterator for VerdefIter<'_> {} - -impl FusedIterator for VerdefIter<'_> {} - -/// An ELF [Version Definition][lsb-verdef] entry . -/// -/// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES -#[derive(Debug)] -pub struct Verdef<'a> { - pub vd_version: usize, - pub vd_flags: usize, - pub vd_ndx: usize, - pub vd_cnt: usize, - pub vd_hash: usize, - pub vd_aux : usize, - pub vd_next: usize, - - bytes: &'a [u8], - ctx: container::Ctx, -} + impl ExactSizeIterator for VerdefIter<'_> {} + + impl FusedIterator for VerdefIter<'_> {} + + /// An ELF [Version Definition][lsb-verdef] entry . + /// + /// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES + #[derive(Debug)] + pub struct Verdef<'a> { + pub vd_version: usize, + pub vd_flags: usize, + pub vd_ndx: usize, + pub vd_cnt: usize, + pub vd_hash: usize, + pub vd_aux: usize, + pub vd_next: usize, + + bytes: &'a [u8], + ctx: container::Ctx, + } -impl<'a> Verdef<'a> { - /// Get an iterator over the [`Verdaux`] entries of this [`Verdef`] entry. - #[inline] - pub fn iter(&'a self) -> VerdauxIter<'a> { - self.into_iter() + impl<'a> Verdef<'a> { + /// Get an iterator over the [`Verdaux`] entries of this [`Verdef`] entry. + #[inline] + pub fn iter(&'a self) -> VerdauxIter<'a> { + self.into_iter() + } } -} -impl<'a> IntoIterator for &'_ Verdef<'a> { - type Item = as Iterator>::Item; - type IntoIter = VerdauxIter<'a>; - - fn into_iter(self) -> Self::IntoIter { - VerdauxIter { - bytes: self.bytes, - count: self.vd_cnt, - index: 0, - offset: 0, - ctx: self.ctx, + impl<'a> IntoIterator for &'_ Verdef<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerdauxIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + VerdauxIter { + bytes: self.bytes, + count: self.vd_cnt, + index: 0, + offset: 0, + ctx: self.ctx, + } } } -} -/// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. -pub struct VerdauxIter<'a> { - bytes: &'a [u8], - count: usize, - index: usize, - offset: usize, - ctx: container::Ctx, -} + /// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. + pub struct VerdauxIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, + } -impl<'a> Iterator for VerdauxIter<'a> { - type Item = Verdaux; + impl<'a> Iterator for VerdauxIter<'a> { + type Item = Verdaux; - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; - // Safe to unwrap as length of the byte slice was validated in the VerdefIter::next. - let ElfVerdaux { - vda_name, - vda_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + // Safe to unwrap as length of the byte slice was validated in the VerdefIter::next. + let ElfVerdaux { vda_name, vda_next } = + self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - // Bump the offset to the next ElfVerdaux entry. - self.offset += vda_next as usize; + // Bump the offset to the next ElfVerdaux entry. + self.offset += vda_next as usize; - Some(Verdaux { - vda_name: vda_name as usize, - vda_next: vda_next as usize, - }) + Some(Verdaux { + vda_name: vda_name as usize, + vda_next: vda_next as usize, + }) + } } - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; - (len, Some(len)) + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } } -} -impl ExactSizeIterator for VerdauxIter<'_> {} + impl ExactSizeIterator for VerdauxIter<'_> {} -impl FusedIterator for VerdauxIter<'_> {} + impl FusedIterator for VerdauxIter<'_> {} -/// An ELF [Version Definition Auxiliary][lsb-verdaux] entry. -/// -/// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS -#[derive(Debug)] -pub struct Verdaux { - pub vda_name: usize, - pub vda_next: usize, -} + /// An ELF [Version Definition Auxiliary][lsb-verdaux] entry. + /// + /// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS + #[derive(Debug)] + pub struct Verdaux { + pub vda_name: usize, + pub vda_next: usize, + } -/************************** - * Version Requirements * - **************************/ - -/// Helper struct to iterate over [Version Needed][Verneed] and [Version Needed -/// Auxiliary][Vernaux] entries. -#[derive(Debug)] -pub struct VerneedSection<'a> { - /// String table used to resolve version strings. - pub symstr: Strtab<'a>, - bytes: &'a [u8], - count: usize, - ctx: container::Ctx, -} + /************************** + * Version Requirements * + **************************/ -impl<'a> VerneedSection<'a> { - /// Try to parse the optional [`SHT_GNU_VERNEED`] section. - pub fn parse( + /// Helper struct to iterate over [Version Needed][Verneed] and [Version Needed + /// Auxiliary][Vernaux] entries. + #[derive(Debug)] + pub struct VerneedSection<'a> { + /// String table used to resolve version strings. + pub verstr: Strtab<'a>, bytes: &'a [u8], - shdrs: &'_ [SectionHeader], + count: usize, ctx: container::Ctx, - ) -> Result>> { - // Get fields needed from optional `version needed` section. - let (link_idx, offset, size, count) = - if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { - ( - shdr.sh_link as usize, // Encodes the string table. - shdr.sh_offset as usize, - shdr.sh_size as usize, - shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. - ) - } else { - return Ok(None); + } + + impl<'a> VerneedSection<'a> { + /// Try to parse the optional [`SHT_GNU_VERNEED`] section. + pub fn parse( + bytes: &'a [u8], + shdrs: &'_ [SectionHeader], + ctx: container::Ctx, + ) -> Result>> { + // Get fields needed from optional `version needed` section. + let (link_idx, offset, size, count) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { + ( + shdr.sh_link as usize, // Encodes the string table. + shdr.sh_offset as usize, + shdr.sh_size as usize, + shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. + ) + } else { + return Ok(None); + }; + + // Get string table which is used to resolve version strings. + let verstr = { + // Linked section refers to string table. + let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( + "Section header of string table for SHT_GNU_VERNEED section not found!".into(), + ))?; + + Strtab::parse( + bytes, + shdr_link.sh_offset as usize, + shdr_link.sh_size as usize, + 0x0, /* Delimiter */ + )? }; - // Get string table which is used to resolve version strings. - let symstr = { - // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( - "Section header of string table for SHT_GNU_VERNEED section not found!".into(), - ))?; + // Get a slice of bytes of the `version needed` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - Strtab::parse( + Ok(Some(VerneedSection { + verstr, bytes, - shdr_link.sh_offset as usize, - shdr_link.sh_size as usize, - 0x0, /* Delimiter */ - )? - }; - - // Get a slice of bytes of the `version needed` section content. - let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - - Ok(Some(VerneedSection { - symstr, - bytes, - count, - ctx, - })) - } + count, + ctx, + })) + } - /// Get an iterator over the [`Verneed`] entries. - #[inline] - pub fn iter(&'a self) -> VerneedIter<'a> { - self.into_iter() + /// Get an iterator over the [`Verneed`] entries. + #[inline] + pub fn iter(&'a self) -> VerneedIter<'a> { + self.into_iter() + } } -} -impl<'a> IntoIterator for &'_ VerneedSection<'a> { - type Item = as Iterator>::Item; - type IntoIter = VerneedIter<'a>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - VerneedIter { - bytes: self.bytes, - count: self.count, - index: 0, - offset: 0, - ctx: self.ctx, + impl<'a> IntoIterator for &'_ VerneedSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerneedIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VerneedIter { + bytes: self.bytes, + count: self.count, + index: 0, + offset: 0, + ctx: self.ctx, + } } } -} -/// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. -pub struct VerneedIter<'a> { - bytes: &'a [u8], - count: usize, - index: usize, - offset: usize, - ctx: container::Ctx, -} + /// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. + pub struct VerneedIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, + } + + impl<'a> Iterator for VerneedIter<'a> { + type Item = Verneed<'a>; -impl<'a> Iterator for VerneedIter<'a> { - type Item = Verneed<'a>; - - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; - - // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. - let ElfVerneed { - vn_version, - vn_cnt, - vn_file, - vn_aux, - vn_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - - // Get a slice of bytes of the `Vernaux` entries. - // - // | Verneed | .. | Verneed | Vernaux | Vernaux | .. | Verneed | .. - // ^--------------^ - // offset ^---------^ - // vn_aux - // ^----------------------------------^ - // vn_next - // - // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. - let len = if vn_next > 0 { - (vn_next - vn_aux) as usize + fn next(&mut self) -> Option { + if self.index >= self.count { + None } else { - // For the last entry, ElfVerneed->vn_next == 0. - // Therefore we compute the remaining length of the bytes buffer. - self.bytes.len() - self.offset - vn_aux as usize - }; - let bytes: &'a [u8] = self - .bytes - .pread_with(self.offset + vn_aux as usize, len) - .unwrap(); - - // Bump the offset to the next ElfVerneed entry. - self.offset += vn_next as usize; - - Some(Verneed { - vn_version : vn_version as usize, - vn_cnt : vn_cnt as usize, - vn_file : vn_file as usize, - vn_aux : vn_aux as usize, - vn_next : vn_next as usize, - bytes, - ctx: self.ctx, - }) + self.index += 1; + + // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. + let ElfVerneed { + vn_version, + vn_cnt, + vn_file, + vn_aux, + vn_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Get a slice of bytes of the `Vernaux` entries. + // + // | Verneed | .. | Verneed | Vernaux | Vernaux | .. | Verneed | .. + // ^--------------^ + // offset ^---------^ + // vn_aux + // ^----------------------------------^ + // vn_next + // + // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. + let len = if vn_next > 0 { + (vn_next - vn_aux) as usize + } else { + // For the last entry, ElfVerneed->vn_next == 0. + // Therefore we compute the remaining length of the bytes buffer. + self.bytes.len() - self.offset - vn_aux as usize + }; + let bytes: &'a [u8] = self + .bytes + .pread_with(self.offset + vn_aux as usize, len) + .unwrap(); + + // Bump the offset to the next ElfVerneed entry. + self.offset += vn_next as usize; + + Some(Verneed { + vn_version: vn_version as usize, + vn_cnt: vn_cnt as usize, + vn_file: vn_file as usize, + vn_aux: vn_aux as usize, + vn_next: vn_next as usize, + bytes, + ctx: self.ctx, + }) + } } - } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; - (len, Some(len)) + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } } -} - -impl ExactSizeIterator for VerneedIter<'_> {} -impl FusedIterator for VerneedIter<'_> {} + impl ExactSizeIterator for VerneedIter<'_> {} -/// An ELF [Version Need][lsb-verneed] entry . -/// -/// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG -#[derive(Debug)] -pub struct Verneed<'a> { - pub vn_version: usize, - pub vn_cnt: usize, - pub vn_file: usize, - pub vn_aux: usize, - pub vn_next: usize, + impl FusedIterator for VerneedIter<'_> {} - bytes: &'a [u8], - ctx: container::Ctx, -} + /// An ELF [Version Need][lsb-verneed] entry . + /// + /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG + #[derive(Debug)] + pub struct Verneed<'a> { + pub vn_version: usize, + pub vn_cnt: usize, + pub vn_file: usize, + pub vn_aux: usize, + pub vn_next: usize, -impl<'a> Verneed<'a> { - /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. - #[inline] - pub fn iter(&'a self) -> VernauxIter<'a> { - self.into_iter() + bytes: &'a [u8], + ctx: container::Ctx, } -} -impl<'a> IntoIterator for &'_ Verneed<'a> { - type Item = as Iterator>::Item; - type IntoIter = VernauxIter<'a>; - - #[inline] - fn into_iter(self) -> Self::IntoIter { - VernauxIter { - bytes: self.bytes, - count: self.vn_cnt, - index: 0, - offset: 0, - ctx: self.ctx, + impl<'a> Verneed<'a> { + /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. + #[inline] + pub fn iter(&'a self) -> VernauxIter<'a> { + self.into_iter() } } -} - -/// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. -pub struct VernauxIter<'a> { - bytes: &'a [u8], - count: usize, - index: usize, - offset: usize, - ctx: container::Ctx, -} -impl<'a> Iterator for VernauxIter<'a> { - type Item = Vernaux; - - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; - - // Safe to unwrap as the length of the byte slice was validated in VerneedIter::next. - let ElfVernaux { - vna_hash, - vna_flags, - vna_other, - vna_name, - vna_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - - // Bump the offset to the next ElfVernaux entry. - self.offset += vna_next as usize; - - Some(Vernaux { - vna_hash : vna_hash as usize, - vna_flags : vna_flags as usize, - vna_other : vna_other as usize, - vna_name : vna_name as usize, - vna_next : vna_next as usize, - }) + impl<'a> IntoIterator for &'_ Verneed<'a> { + type Item = as Iterator>::Item; + type IntoIter = VernauxIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VernauxIter { + bytes: self.bytes, + count: self.vn_cnt, + index: 0, + offset: 0, + ctx: self.ctx, + } } } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; - (len, Some(len)) + /// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. + pub struct VernauxIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, } -} -impl ExactSizeIterator for VernauxIter<'_> {} + impl<'a> Iterator for VernauxIter<'a> { + type Item = Vernaux; -impl FusedIterator for VernauxIter<'_> {} + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + // Safe to unwrap as the length of the byte slice was validated in VerneedIter::next. + let ElfVernaux { + vna_hash, + vna_flags, + vna_other, + vna_name, + vna_next, + } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + + // Bump the offset to the next ElfVernaux entry. + self.offset += vna_next as usize; + + Some(Vernaux { + vna_hash: vna_hash as usize, + vna_flags: vna_flags as usize, + vna_other: vna_other as usize, + vna_name: vna_name as usize, + vna_next: vna_next as usize, + }) + } + } -/// An ELF [Version Need Auxiliary][lsb-vernaux] entry. -/// -/// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG -#[derive(Debug)] -pub struct Vernaux { - pub vna_hash: usize, - pub vna_flags: usize, - pub vna_other: usize, - pub vna_name: usize, - pub vna_next: usize, -} + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (len, Some(len)) + } + } + + impl ExactSizeIterator for VernauxIter<'_> {} + + impl FusedIterator for VernauxIter<'_> {} -#[cfg(test)] -mod test { - use super::{ElfVerdef, ElfVerdaux, ElfVernaux, ElfVerneed}; - use core::mem::size_of; - - #[test] - fn check_size() { - assert_eq!(20, size_of::()); - assert_eq!(8, size_of::()); - assert_eq!(16, size_of::()); - assert_eq!(16, size_of::()); + /// An ELF [Version Need Auxiliary][lsb-vernaux] entry. + /// + /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG + #[derive(Debug)] + pub struct Vernaux { + pub vna_hash: usize, + pub vna_flags: usize, + pub vna_other: usize, + pub vna_name: usize, + pub vna_next: usize, } -} + #[cfg(test)] + mod test { + use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed}; + use core::mem::size_of; + + #[test] + fn check_size() { + assert_eq!(20, size_of::()); + assert_eq!(8, size_of::()); + assert_eq!(16, size_of::()); + assert_eq!(16, size_of::()); + } + } } diff --git a/tests/elf.rs b/tests/elf.rs index 15250bb1..89ac629a 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -244,7 +244,7 @@ fn check_symver_expectations( let verneed = elf.verneed.unwrap(); // Resolve version strings. - let symstr = |idx| verneed.symstr.get_at(idx).unwrap(); + let verstr = |idx| verneed.verstr.get_at(idx).unwrap(); // ELF file dependencies with version requirements. let need_files: Vec<_> = verneed.iter().collect(); @@ -256,7 +256,7 @@ fn check_symver_expectations( for need_file in &need_files { // Get file name of the dependency. - let file_str = symstr(need_file.vn_file); + let file_str = verstr(need_file.vn_file); // Check if we expect this dependency. let expect_vers = expect.get(&file_str); @@ -278,7 +278,7 @@ fn check_symver_expectations( for need_ver in &need_vers { // Get version string. - let ver_str = symstr(need_ver.vna_name); + let ver_str = verstr(need_ver.vna_name); // Check if we expect this version. assert!( From 8b882af801a76e8674294b88a542f1a992db238d Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 18:49:34 +0200 Subject: [PATCH 08/20] add tests for parsing .gnu.version_d section --- tests/elf.rs | 145 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 123 insertions(+), 22 deletions(-) diff --git a/tests/elf.rs b/tests/elf.rs index 89ac629a..65c63a10 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -233,23 +233,47 @@ type SymverExpectation = std::collections::HashMap<&'static str, Vec<&'static st fn check_symver_expectations( bytes: &[u8], - expect: &SymverExpectation, + expect_verneed: &SymverExpectation, + expect_verdef: &SymverExpectation, ) -> Result<(), goblin::error::Error> { let elf = Elf::parse(bytes)?; - // We expect a version needed section. - assert!(elf.verneed.is_some()); + if expect_verneed.is_empty() { + // We dont expect a version definition section. + assert!(elf.verneed.is_none()); + } else { + // We expect a version definition section. + assert!(elf.verneed.is_some()); - // Safe to unwrap as asserted above. - let verneed = elf.verneed.unwrap(); + let verneed = elf.verneed.as_ref().unwrap(); + check_symver_expectations_verneed(verneed, expect_verneed); + } + + if expect_verdef.is_empty() { + // We dont expect a version definition section. + assert!(elf.verdef.is_none()); + } else { + // We expect a version definition section. + assert!(elf.verdef.is_some()); + + let verdef = elf.verdef.as_ref().unwrap(); + check_symver_expectations_verdef(verdef, expect_verdef); + } + + Ok(()) +} +fn check_symver_expectations_verneed( + verneed: &goblin::elf::VerneedSection<'_>, + expect_verneed: &SymverExpectation, +) { // Resolve version strings. let verstr = |idx| verneed.verstr.get_at(idx).unwrap(); // ELF file dependencies with version requirements. let need_files: Vec<_> = verneed.iter().collect(); assert_eq!( - expect.keys().len(), + expect_verneed.keys().len(), need_files.len(), "Expected different number of dependencies with version information!" ); @@ -259,7 +283,7 @@ fn check_symver_expectations( let file_str = verstr(need_file.vn_file); // Check if we expect this dependency. - let expect_vers = expect.get(&file_str); + let expect_vers = expect_verneed.get(&file_str); assert!( expect_vers.is_some(), "Unexpected FILE dependency {}!", @@ -282,51 +306,128 @@ fn check_symver_expectations( // Check if we expect this version. assert!( - expect_vers - .iter() - .find(|&expect_ver| &ver_str == expect_ver) - .is_some(), + expect_vers.iter().any(|&expect_ver| ver_str == expect_ver), "Unexpected VERSION dependency {}", ver_str ); } } +} - Ok(()) +fn check_symver_expectations_verdef( + verdef: &goblin::elf::VerdefSection<'_>, + expect_verdef: &SymverExpectation, +) { + // Resolve version strings. + let verstr = |idx| verdef.verstr.get_at(idx).unwrap(); + + // ELF version deinitions. + let defined_vers: Vec<_> = verdef.iter().collect(); + assert_eq!( + expect_verdef.keys().len(), + defined_vers.len(), + "Expected different number of defined versions!" + ); + + for defined_ver in &defined_vers { + // [0] Defined version + // [1..] Parent nodes + let verdaux: Vec<_> = defined_ver.iter().collect(); + assert!(verdaux.len() >= 1); + let version = &verdaux[0]; + let parents = &verdaux[1..]; + + let version_str = verstr(version.vda_name); + + // Check if we expect this dependency. + let expect_parents = expect_verdef.get(&version_str); + assert!( + expect_parents.is_some(), + "Unexpected VERSION definition {}!", + version_str + ); + let expect_parents = expect_parents.unwrap(); + + // Validate name of parent version nodes if we expect + assert_eq!( + expect_parents.len(), + parents.len(), + "Expected different number of parent nodes for {}!", + version_str + ); + + for parent in parents { + // Get parent string. + let parent_str = verstr(parent.vda_name); + + // Check if we expect this parent. + assert!( + expect_parents + .iter() + .any(|&expect_parent| parent_str == expect_parent), + "Unexpected PARENT node {}", + parent_str + ); + } + } } #[rustfmt::skip] #[test] -fn test_symver_verneed() -> Result<(), goblin::error::Error> { +fn test_symver() -> Result<(), goblin::error::Error> { // NOTE: Expected Files & Symbol Versions depend on build system of the test ELF binaries. // When rebuilding the referenced ELF file the version information must be checked and // potentially updated: // > readelf -V - - let expect_lib32: SymverExpectation = [ + // + // verneed - SymverExpectation + // keys : file dependencies + // value: vector of version dependencies for given file (key) + // + // verdef - SymverExpectation + // keys : defined version nodes + // value: vector of parent nodes for given version node (key) + + let expect_lib32_verneed: SymverExpectation = [ ("libc.so.6", vec!["GLIBC_2.0", "GLIBC_2.1.3"]) ].iter().cloned().collect(); - let expect_lib64: SymverExpectation = [ + let expect_lib32_verdef: SymverExpectation = [ + ("lib32.so", vec![]), + ("v1", vec![]), + ("v2", vec!["v1"]), + ].iter().cloned().collect(); + + let expect_lib64_verneed: SymverExpectation = [ ("libc.so.6", vec!["GLIBC_2.2.5"]) ].iter().cloned().collect(); - let expect_prog32: SymverExpectation = [ + let expect_lib64_verdef: SymverExpectation = [ + ("lib64.so", vec![]), + ("v1", vec![]), + ("v2", vec!["v1"]), + ].iter().cloned().collect(); + + let expect_prog32_verneed: SymverExpectation = [ ("libc.so.6", vec!["GLIBC_2.0", "GLIBC_2.1.3"]), ("libdl.so.2", vec!["GLIBC_2.0", "GLIBC_2.1"]), ("lib32.so", vec!["v2"]), ].iter().cloned().collect(); - let expect_prog64: SymverExpectation = [ + let expect_prog32_verdef = SymverExpectation::new(); + + let expect_prog64_verneed: SymverExpectation = [ ("libdl.so.2", vec!["GLIBC_2.2.5"]), ("libc.so.6", vec!["GLIBC_2.2.5"]), ("lib64.so", vec!["v2"]), ].iter().cloned().collect(); - check_symver_expectations(include_bytes!("bins/elf/symver/lib32.so"), &expect_lib32)?; - check_symver_expectations(include_bytes!("bins/elf/symver/lib64.so"), &expect_lib64)?; - check_symver_expectations(include_bytes!("bins/elf/symver/prog32"), &expect_prog32)?; - check_symver_expectations(include_bytes!("bins/elf/symver/prog64"), &expect_prog64)?; + let expect_prog64_verdef = SymverExpectation::new(); + + check_symver_expectations(include_bytes!("bins/elf/symver/lib32.so"), &expect_lib32_verneed, &expect_lib32_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/lib64.so"), &expect_lib64_verneed, &expect_lib64_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/prog32"), &expect_prog32_verneed, &expect_prog32_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/prog64"), &expect_prog64_verneed, &expect_prog64_verdef)?; Ok(()) } From 89697c46229dfe30f897b4c43c5214acb3a4ca19 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 19:47:25 +0200 Subject: [PATCH 09/20] elf.symver: add struct definitions and reading part for .gnu.version (SHT_GNU_versym) section --- src/elf/mod.rs | 13 +++-- src/elf/symver.rs | 136 +++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 142 insertions(+), 7 deletions(-) diff --git a/src/elf/mod.rs b/src/elf/mod.rs index eedb7ed4..2fddbad5 100644 --- a/src/elf/mod.rs +++ b/src/elf/mod.rs @@ -79,8 +79,7 @@ if_sylvan! { pub use dynamic::Dynamic; pub use reloc::Reloc; pub use reloc::RelocSection; - pub use symver::VerdefSection; - pub use symver::VerneedSection; + pub use symver::{VersymSection, VerdefSection, VerneedSection}; pub type ProgramHeaders = Vec; pub type SectionHeaders = Vec; @@ -133,6 +132,9 @@ if_sylvan! { pub entry: u64, /// Whether the binary is little endian or not pub little_endian: bool, + /// Contains the symbol version information from the optional section + /// [`SHT_GNU_VERSYM`][section_header::SHT_GNU_VERSYM] (GNU extenstion). + pub versym : Option>, /// Contains the version definition information from the optional section /// [`SHT_GNU_VERDEF`][section_header::SHT_GNU_VERDEF] (GNU extenstion). pub verdef : Option>, @@ -241,8 +243,9 @@ if_sylvan! { entry: misc.entry, little_endian: misc.little_endian, ctx: misc.ctx, - verdef : None, - verneed : None, + versym: None, + verdef: None, + verneed: None, }) } @@ -345,6 +348,7 @@ if_sylvan! { } } + let versym = symver::VersymSection::parse(bytes, §ion_headers, ctx)?; let verdef = symver::VerdefSection::parse(bytes, §ion_headers, ctx)?; let verneed = symver::VerneedSection::parse(bytes, §ion_headers, ctx)?; @@ -370,6 +374,7 @@ if_sylvan! { entry: misc.entry, little_endian: misc.little_endian, ctx: ctx, + versym, verdef, verneed, }) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index f0b4940f..ce1f8226 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -65,7 +65,9 @@ pub use symver_impl::*; #[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] mod symver_impl { use crate::container; - use crate::elf::section_header::{SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED}; + use crate::elf::section_header::{ + SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED, SHT_GNU_VERSYM, + }; use crate::error::{Error, Result}; use crate::strtab::Strtab; use core::iter::FusedIterator; @@ -75,6 +77,15 @@ mod symver_impl { * ELF Structures * ********************/ + /// An ELF `Symbol Version` entry. + /// + /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL + #[repr(C)] + #[derive(Debug, Pread)] + struct ElfVersym { + vs_val: u16, + } + /// An ELF `Version Definition` entry Elfxx_Verdef. /// /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES @@ -148,6 +159,124 @@ mod symver_impl { vna_next: u32, } + /******************** + * Symbol Version * + ********************/ + + /// Helper struct to iterate over [Symbol Version][Versym] entries. + #[derive(Debug)] + pub struct VersymSection<'a> { + bytes: &'a [u8], + ctx: container::Ctx, + } + + impl<'a> VersymSection<'a> { + pub fn parse( + bytes: &'a [u8], + shdrs: &'_ [SectionHeader], + ctx: container::Ctx, + ) -> Result>> { + // Get fields needed from optional `symbol version` section. + let (offset, size) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERSYM) { + (shdr.sh_offset as usize, shdr.sh_size as usize) + } else { + return Ok(None); + }; + + // Get a slice of bytes of the `version definition` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; + + Ok(Some(VersymSection { bytes, ctx })) + } + + /// Get an iterator over the [`Versym`] entries. + #[inline] + pub fn iter(&'a self) -> VersymIter<'a> { + self.into_iter() + } + + /// Number of [`Versym`] entries. + #[inline] + pub fn len(&self) -> usize { + let entsize = core::mem::size_of::(); + + self.bytes.len() / entsize + } + + /// Get [`Versym`] entry at index. + #[inline] + pub fn get_at(&self, idx: usize) -> Option { + let entsize = core::mem::size_of::(); + let offset = idx.checked_mul(entsize)?; + + self.bytes + .pread_with::(offset, self.ctx.le) + .ok() + .map(|vs| Versym { + vs_val: vs.vs_val as usize, + }) + } + } + + impl<'a> IntoIterator for &'_ VersymSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VersymIter<'a>; + + fn into_iter(self) -> Self::IntoIter { + VersymIter { + bytes: self.bytes, + offset: 0, + ctx: self.ctx, + } + } + } + + /// Iterator over the [`Versym`] entries from the [`SHT_GNU_VERSYM`] section. + pub struct VersymIter<'a> { + bytes: &'a [u8], + offset: usize, + ctx: container::Ctx, + } + + impl<'a> Iterator for VersymIter<'a> { + type Item = Versym; + + fn next(&mut self) -> Option { + if self.offset >= self.bytes.len() { + None + } else { + // Safe to unwrap as the length of the byte slice was validated in VersymSection::parse. + let ElfVersym { vs_val } = self + .bytes + .gread_with(&mut self.offset, self.ctx.le) + .unwrap(); + + Some(Versym { + vs_val: vs_val as usize, + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.bytes.len() - self.offset; + (len, Some(len)) + } + } + + /// An ELF [Symbol Version][lsb-versym] entry. + /// + /// [lsb-versym]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL + #[derive(Debug)] + pub struct Versym { + pub vs_val: usize, + } + + impl ExactSizeIterator for VersymIter<'_> {} + + impl FusedIterator for VersymIter<'_> {} + /************************ * Version Definition * ************************/ @@ -208,7 +337,7 @@ mod symver_impl { })) } - /// Get an iterator over [`Verdef`] entries. + /// Get an iterator over the [`Verdef`] entries. #[inline] pub fn iter(&'a self) -> VerdefIter<'a> { self.into_iter() @@ -665,11 +794,12 @@ mod symver_impl { #[cfg(test)] mod test { - use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed}; + use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed, ElfVersym}; use core::mem::size_of; #[test] fn check_size() { + assert_eq!(2, size_of::()); assert_eq!(20, size_of::()); assert_eq!(8, size_of::()); assert_eq!(16, size_of::()); From b472420a485ce69c4c73c5fefd1e32475a5adb2e Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 21:35:50 +0200 Subject: [PATCH 10/20] elf.symver: add tests for parsing .gnu.version section --- tests/elf.rs | 77 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/tests/elf.rs b/tests/elf.rs index 65c63a10..e7cd3cf9 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -233,11 +233,23 @@ type SymverExpectation = std::collections::HashMap<&'static str, Vec<&'static st fn check_symver_expectations( bytes: &[u8], + expect_versym: &Vec, expect_verneed: &SymverExpectation, expect_verdef: &SymverExpectation, ) -> Result<(), goblin::error::Error> { let elf = Elf::parse(bytes)?; + if expect_versym.is_empty() { + // We dont expect a symbol version section. + assert!(elf.versym.is_none()); + } else { + // We expect a symbol version section. + assert!(elf.versym.is_some()); + + let versym = elf.versym.as_ref().unwrap(); + check_symver_expectations_versym(versym, &elf.dynsyms, expect_versym); + } + if expect_verneed.is_empty() { // We dont expect a version definition section. assert!(elf.verneed.is_none()); @@ -263,6 +275,28 @@ fn check_symver_expectations( Ok(()) } +fn check_symver_expectations_versym( + versym: &goblin::elf::VersymSection<'_>, + dynsyms: &goblin::elf::Symtab<'_>, + expect_versym: &Vec, +) { + // VERSYM section must contain one entry per DYNSYM. + assert_eq!(dynsyms.len(), versym.len()); + + // Check length computation + iteration count. + assert_eq!(expect_versym.len(), versym.len()); + assert_eq!(expect_versym.len(), versym.iter().count()); + + // Check symbol version identifier. + for versym in versym.iter() { + assert!( + expect_versym.iter().any(|&expect| expect == versym.vs_val), + "Unexpected SYMBOL VERSION index {}", + versym.vs_val + ); + } +} + fn check_symver_expectations_verneed( verneed: &goblin::elf::VerneedSection<'_>, expect_verneed: &SymverExpectation, @@ -380,6 +414,9 @@ fn test_symver() -> Result<(), goblin::error::Error> { // potentially updated: // > readelf -V // + // versym - Vec + // value: symbol version identifier + // // verneed - SymverExpectation // keys : file dependencies // value: vector of version dependencies for given file (key) @@ -388,6 +425,14 @@ fn test_symver() -> Result<(), goblin::error::Error> { // keys : defined version nodes // value: vector of parent nodes for given version node (key) + // lib32 + + let expect_lib32_versym : Vec = vec![ + 0,0,4,5, + 0,0,3,0x8001, + 0x8002,2,3, + ]; + let expect_lib32_verneed: SymverExpectation = [ ("libc.so.6", vec!["GLIBC_2.0", "GLIBC_2.1.3"]) ].iter().cloned().collect(); @@ -398,6 +443,14 @@ fn test_symver() -> Result<(), goblin::error::Error> { ("v2", vec!["v1"]), ].iter().cloned().collect(); + // lib64 + + let expect_lib64_versym :Vec = vec![ + 0,0,4,0, + 0,4,3,0x8001, + 0x8002,2,3, + ]; + let expect_lib64_verneed: SymverExpectation = [ ("libc.so.6", vec!["GLIBC_2.2.5"]) ].iter().cloned().collect(); @@ -408,6 +461,14 @@ fn test_symver() -> Result<(), goblin::error::Error> { ("v2", vec!["v1"]), ].iter().cloned().collect(); + // prog32 + + let expect_prog32_versym : Vec = vec![ + 0,2,0,3, + 4,0,5,6, + 0,5,1, + ]; + let expect_prog32_verneed: SymverExpectation = [ ("libc.so.6", vec!["GLIBC_2.0", "GLIBC_2.1.3"]), ("libdl.so.2", vec!["GLIBC_2.0", "GLIBC_2.1"]), @@ -416,6 +477,14 @@ fn test_symver() -> Result<(), goblin::error::Error> { let expect_prog32_verdef = SymverExpectation::new(); + // prog64 + + let expect_prog64_versym : Vec = vec![ + 0,2,0,3, + 3,4,0,0, + 4,3, + ]; + let expect_prog64_verneed: SymverExpectation = [ ("libdl.so.2", vec!["GLIBC_2.2.5"]), ("libc.so.6", vec!["GLIBC_2.2.5"]), @@ -424,10 +493,10 @@ fn test_symver() -> Result<(), goblin::error::Error> { let expect_prog64_verdef = SymverExpectation::new(); - check_symver_expectations(include_bytes!("bins/elf/symver/lib32.so"), &expect_lib32_verneed, &expect_lib32_verdef)?; - check_symver_expectations(include_bytes!("bins/elf/symver/lib64.so"), &expect_lib64_verneed, &expect_lib64_verdef)?; - check_symver_expectations(include_bytes!("bins/elf/symver/prog32"), &expect_prog32_verneed, &expect_prog32_verdef)?; - check_symver_expectations(include_bytes!("bins/elf/symver/prog64"), &expect_prog64_verneed, &expect_prog64_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/lib32.so"), &expect_lib32_versym, &expect_lib32_verneed, &expect_lib32_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/lib64.so"), &expect_lib64_versym, &expect_lib64_verneed, &expect_lib64_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/prog32"), &expect_prog32_versym, &expect_prog32_verneed, &expect_prog32_verdef)?; + check_symver_expectations(include_bytes!("bins/elf/symver/prog64"), &expect_prog64_versym, &expect_prog64_verneed, &expect_prog64_verdef)?; Ok(()) } From fa03ec3090de7e3f316868daf3c93b1b44c83f8b Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 21:40:54 +0200 Subject: [PATCH 11/20] elf.symver: fix clippy warnings --- src/elf/symver.rs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index ce1f8226..3945dd84 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -196,6 +196,12 @@ mod symver_impl { self.into_iter() } + /// True if there are no [`Versym`] entries. + #[inline] + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } + /// Number of [`Versym`] entries. #[inline] pub fn len(&self) -> usize { @@ -314,9 +320,12 @@ mod symver_impl { // Get string table which is used to resolve version strings. let verstr = { // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( - "Section header of string table for SHT_GNU_VERDEF section not found!".into(), - ))?; + let shdr_link = shdrs.get(link_idx).ok_or_else(|| { + Error::Malformed( + "Section header of string table for SHT_GNU_VERDEF section not found!" + .into(), + ) + })?; Strtab::parse( bytes, @@ -569,9 +578,12 @@ mod symver_impl { // Get string table which is used to resolve version strings. let verstr = { // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or(Error::Malformed( - "Section header of string table for SHT_GNU_VERNEED section not found!".into(), - ))?; + let shdr_link = shdrs.get(link_idx).ok_or_else(|| { + Error::Malformed( + "Section header of string table for SHT_GNU_VERNEED section not found!" + .into(), + ) + })?; Strtab::parse( bytes, From 8bd8bc872f263b35fe53e0ef918a4d2c283aba0d Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 15 Aug 2021 22:33:05 +0200 Subject: [PATCH 12/20] elf.symver: added some convenience methods to Verdef --- src/elf/symver.rs | 85 +++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 3945dd84..b5a2c65f 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -73,6 +73,23 @@ mod symver_impl { use core::iter::FusedIterator; use scroll::Pread; + /****************** + * ELF Constants * + ******************/ + + // Versym constants. + + pub const VER_NDX_LOCAL: usize = 0; + pub const VER_NDX_GLOBAL: usize = 1; + pub const VERSYM_HIDDEN: usize = 0x8000; + pub const VERSYM_VERSION: usize = 0x7fff; + + // Verdef constants. + + pub const VER_FLG_BASE: usize = 0x1; + pub const VER_FLG_WEAK: usize = 0x2; + pub const VER_FLG_INFO: usize = 0x4; + /******************** * ELF Structures * ********************/ @@ -271,6 +288,10 @@ mod symver_impl { } } + impl ExactSizeIterator for VersymIter<'_> {} + + impl FusedIterator for VersymIter<'_> {} + /// An ELF [Symbol Version][lsb-versym] entry. /// /// [lsb-versym]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL @@ -279,9 +300,33 @@ mod symver_impl { pub vs_val: usize, } - impl ExactSizeIterator for VersymIter<'_> {} + impl Versym { + /// Returns true if the symbol is local and not available outside the object according to + /// [`VER_NDX_LOCAL`]. + #[inline] + pub fn is_local(&self) -> bool { + self.vs_val == VER_NDX_LOCAL + } - impl FusedIterator for VersymIter<'_> {} + /// Returns true if the symbol is defined in this object and globally available according + /// to [`VER_NDX_GLOBAL`]. + #[inline] + pub fn is_global(&self) -> bool { + self.vs_val == VER_NDX_GLOBAL + } + + /// Returns true if the `hidden` bit is set according to the [`VERSYM_HIDDEN`] bitmask. + #[inline] + pub fn is_hidden(&self) -> bool { + (self.vs_val & VERSYM_HIDDEN) == VERSYM_HIDDEN + } + + /// Returns the symbol version index according to the [`VERSYM_VERSION`] bitmask. + #[inline] + pub fn version(&self) -> usize { + self.vs_val & VERSYM_VERSION + } + } /************************ * Version Definition * @@ -807,6 +852,7 @@ mod symver_impl { #[cfg(test)] mod test { use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed, ElfVersym}; + use super::{Versym, VERSYM_HIDDEN, VER_NDX_GLOBAL, VER_NDX_LOCAL}; use core::mem::size_of; #[test] @@ -817,5 +863,40 @@ mod symver_impl { assert_eq!(16, size_of::()); assert_eq!(16, size_of::()); } + + #[test] + fn check_versym() { + let local = Versym { + vs_val: VER_NDX_LOCAL, + }; + assert_eq!(true, local.is_local()); + assert_eq!(false, local.is_global()); + assert_eq!(false, local.is_hidden()); + assert_eq!(VER_NDX_LOCAL, local.version()); + + let global = Versym { + vs_val: VER_NDX_GLOBAL, + }; + assert_eq!(false, global.is_local()); + assert_eq!(true, global.is_global()); + assert_eq!(false, global.is_hidden()); + assert_eq!(VER_NDX_GLOBAL, global.version()); + + let hidden = Versym { + vs_val: VERSYM_HIDDEN, + }; + assert_eq!(false, hidden.is_local()); + assert_eq!(false, hidden.is_global()); + assert_eq!(true, hidden.is_hidden()); + assert_eq!(0, hidden.version()); + + let hidden = Versym { + vs_val: VERSYM_HIDDEN | 0x123, + }; + assert_eq!(false, hidden.is_local()); + assert_eq!(false, hidden.is_global()); + assert_eq!(true, hidden.is_hidden()); + assert_eq!(0x123, hidden.version()); + } } } From 097d6e56213bc26e6b72fbc5f5ee537d9b9c9f8a Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Mon, 16 Aug 2021 21:43:13 +0200 Subject: [PATCH 13/20] elf.symver: apply review feedback - Remove & properly handle unwrap in VersymIter::next - Fix VersymIter::size_hint - Adapt datatypes of fields in exposed ELF structs --- src/elf/symver.rs | 89 +++++++++++++++++++++++------------------------ tests/elf.rs | 14 ++++---- 2 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index b5a2c65f..10761187 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -79,10 +79,10 @@ mod symver_impl { // Versym constants. - pub const VER_NDX_LOCAL: usize = 0; - pub const VER_NDX_GLOBAL: usize = 1; - pub const VERSYM_HIDDEN: usize = 0x8000; - pub const VERSYM_VERSION: usize = 0x7fff; + pub const VER_NDX_LOCAL: u16 = 0; + pub const VER_NDX_GLOBAL: u16 = 1; + pub const VERSYM_HIDDEN: u16 = 0x8000; + pub const VERSYM_VERSION: u16 = 0x7fff; // Verdef constants. @@ -236,9 +236,7 @@ mod symver_impl { self.bytes .pread_with::(offset, self.ctx.le) .ok() - .map(|vs| Versym { - vs_val: vs.vs_val as usize, - }) + .map(|ElfVersym { vs_val }| Versym { vs_val }) } } @@ -269,21 +267,22 @@ mod symver_impl { if self.offset >= self.bytes.len() { None } else { - // Safe to unwrap as the length of the byte slice was validated in VersymSection::parse. - let ElfVersym { vs_val } = self - .bytes - .gread_with(&mut self.offset, self.ctx.le) - .unwrap(); - - Some(Versym { - vs_val: vs_val as usize, - }) + self.bytes + .gread_with::(&mut self.offset, self.ctx.le) + .ok() + .map(|ElfVersym { vs_val }| Versym { vs_val }) + .or_else(|| { + // self.bytes are not a multiple of ElfVersym. + // Adjust offset to continue yielding Nones. + self.offset = self.bytes.len(); + None + }) } } #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.bytes.len() - self.offset; + let len = (self.bytes.len() - self.offset) / core::mem::size_of::(); (len, Some(len)) } } @@ -297,7 +296,7 @@ mod symver_impl { /// [lsb-versym]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL #[derive(Debug)] pub struct Versym { - pub vs_val: usize, + pub vs_val: u16, } impl Versym { @@ -323,7 +322,7 @@ mod symver_impl { /// Returns the symbol version index according to the [`VERSYM_VERSION`] bitmask. #[inline] - pub fn version(&self) -> usize { + pub fn version(&self) -> u16 { self.vs_val & VERSYM_VERSION } } @@ -469,11 +468,11 @@ mod symver_impl { self.offset += vd_next as usize; Some(Verdef { - vd_version: vd_version as usize, - vd_flags: vd_flags as usize, - vd_ndx: vd_ndx as usize, - vd_cnt: vd_cnt as usize, - vd_hash: vd_hash as usize, + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, vd_aux: vd_aux as usize, vd_next: vd_next as usize, bytes, @@ -498,11 +497,11 @@ mod symver_impl { /// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES #[derive(Debug)] pub struct Verdef<'a> { - pub vd_version: usize, - pub vd_flags: usize, - pub vd_ndx: usize, - pub vd_cnt: usize, - pub vd_hash: usize, + pub vd_version: u16, + pub vd_flags: u16, + pub vd_ndx: u16, + pub vd_cnt: u16, + pub vd_hash: u32, pub vd_aux: usize, pub vd_next: usize, @@ -536,8 +535,8 @@ mod symver_impl { /// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. pub struct VerdauxIter<'a> { bytes: &'a [u8], - count: usize, - index: usize, + count: u16, + index: u16, offset: usize, ctx: container::Ctx, } @@ -567,7 +566,7 @@ mod symver_impl { #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; + let len = usize::from(self.count - self.index); (len, Some(len)) } } @@ -725,8 +724,8 @@ mod symver_impl { self.offset += vn_next as usize; Some(Verneed { - vn_version: vn_version as usize, - vn_cnt: vn_cnt as usize, + vn_version, + vn_cnt, vn_file: vn_file as usize, vn_aux: vn_aux as usize, vn_next: vn_next as usize, @@ -752,8 +751,8 @@ mod symver_impl { /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG #[derive(Debug)] pub struct Verneed<'a> { - pub vn_version: usize, - pub vn_cnt: usize, + pub vn_version: u16, + pub vn_cnt: u16, pub vn_file: usize, pub vn_aux: usize, pub vn_next: usize, @@ -789,8 +788,8 @@ mod symver_impl { /// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. pub struct VernauxIter<'a> { bytes: &'a [u8], - count: usize, - index: usize, + count: u16, + index: u16, offset: usize, ctx: container::Ctx, } @@ -817,9 +816,9 @@ mod symver_impl { self.offset += vna_next as usize; Some(Vernaux { - vna_hash: vna_hash as usize, - vna_flags: vna_flags as usize, - vna_other: vna_other as usize, + vna_hash, + vna_flags, + vna_other, vna_name: vna_name as usize, vna_next: vna_next as usize, }) @@ -828,7 +827,7 @@ mod symver_impl { #[inline] fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; + let len = usize::from(self.count - self.index); (len, Some(len)) } } @@ -842,9 +841,9 @@ mod symver_impl { /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG #[derive(Debug)] pub struct Vernaux { - pub vna_hash: usize, - pub vna_flags: usize, - pub vna_other: usize, + pub vna_hash: u32, + pub vna_flags: u16, + pub vna_other: u16, pub vna_name: usize, pub vna_next: usize, } diff --git a/tests/elf.rs b/tests/elf.rs index e7cd3cf9..26223a74 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -233,7 +233,7 @@ type SymverExpectation = std::collections::HashMap<&'static str, Vec<&'static st fn check_symver_expectations( bytes: &[u8], - expect_versym: &Vec, + expect_versym: &Vec, expect_verneed: &SymverExpectation, expect_verdef: &SymverExpectation, ) -> Result<(), goblin::error::Error> { @@ -278,7 +278,7 @@ fn check_symver_expectations( fn check_symver_expectations_versym( versym: &goblin::elf::VersymSection<'_>, dynsyms: &goblin::elf::Symtab<'_>, - expect_versym: &Vec, + expect_versym: &Vec, ) { // VERSYM section must contain one entry per DYNSYM. assert_eq!(dynsyms.len(), versym.len()); @@ -414,7 +414,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { // potentially updated: // > readelf -V // - // versym - Vec + // versym - Vec // value: symbol version identifier // // verneed - SymverExpectation @@ -427,7 +427,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { // lib32 - let expect_lib32_versym : Vec = vec![ + let expect_lib32_versym : Vec = vec![ 0,0,4,5, 0,0,3,0x8001, 0x8002,2,3, @@ -445,7 +445,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { // lib64 - let expect_lib64_versym :Vec = vec![ + let expect_lib64_versym :Vec = vec![ 0,0,4,0, 0,4,3,0x8001, 0x8002,2,3, @@ -463,7 +463,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { // prog32 - let expect_prog32_versym : Vec = vec![ + let expect_prog32_versym : Vec = vec![ 0,2,0,3, 4,0,5,6, 0,5,1, @@ -479,7 +479,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { // prog64 - let expect_prog64_versym : Vec = vec![ + let expect_prog64_versym : Vec = vec![ 0,2,0,3, 3,4,0,0, 4,3, From b2d780f4b3511a1cc2977f9828c1418937f64b56 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Mon, 16 Aug 2021 23:02:27 +0200 Subject: [PATCH 14/20] elf.symver: apply review feedback - Remove calls to unwrap and validate offsets in all Iterator implementations - Adapt datatypes of fields in exposed ELF structs --- src/elf/symver.rs | 248 ++++++++++++++++++++++++---------------------- 1 file changed, 127 insertions(+), 121 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 10761187..65184227 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -86,9 +86,9 @@ mod symver_impl { // Verdef constants. - pub const VER_FLG_BASE: usize = 0x1; - pub const VER_FLG_WEAK: usize = 0x2; - pub const VER_FLG_INFO: usize = 0x4; + pub const VER_FLG_BASE: u16 = 0x1; + pub const VER_FLG_WEAK: u16 = 0x2; + pub const VER_FLG_INFO: u16 = 0x4; /******************** * ELF Structures * @@ -431,52 +431,48 @@ mod symver_impl { } else { self.index += 1; - // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. - let ElfVerdef { - vd_version, - vd_flags, - vd_ndx, - vd_cnt, - vd_hash, - vd_aux, - vd_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - - // Get a slice of bytes of the `Verdaux` entries. - // - // | Verdef | .. | Verdef | Verdaux | Verdaux | .. | Verdef | .. - // ^-------------^ - // offset ^---------^ - // vd_aux - // ^---------------------------------^ - // vd_next - // - // Safe to unwrap as the length of the byte slice was validated in VerdefSection::parse. - let len = if vd_next > 0 { - (vd_next - vd_aux) as usize - } else { - // For the last entry, ElfVerdef->vd_next == 0. - // Therefore we compute the remaining length of the bytes buffer. - self.bytes.len() - self.offset - vd_aux as usize + let do_next = |iter: &mut Self| { + let ElfVerdef { + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, + vd_aux, + vd_next, + } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + + // Validate offset to first ElfVerdaux entry. + let offset = iter.offset.checked_add(vd_aux as usize)?; + + // Validate if offset is valid index into bytes slice. + if offset >= iter.bytes.len() { + return None; + } + + // Get a slice of bytes starting with the first ElfVerdaux entry. + let bytes: &'a [u8] = &iter.bytes[offset..]; + + // Bump the offset to the next ElfVerdef entry. + iter.offset = iter.offset.checked_add(vd_next as usize)?; + + Some(Verdef { + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, + vd_aux, + vd_next, + bytes, + ctx: iter.ctx, + }) }; - let bytes: &'a [u8] = self - .bytes - .pread_with(self.offset + vd_aux as usize, len) - .unwrap(); - - // Bump the offset to the next ElfVerdef entry. - self.offset += vd_next as usize; - - Some(Verdef { - vd_version, - vd_flags, - vd_ndx, - vd_cnt, - vd_hash, - vd_aux: vd_aux as usize, - vd_next: vd_next as usize, - bytes, - ctx: self.ctx, + + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None }) } } @@ -502,8 +498,8 @@ mod symver_impl { pub vd_ndx: u16, pub vd_cnt: u16, pub vd_hash: u32, - pub vd_aux: usize, - pub vd_next: usize, + pub vd_aux: u32, + pub vd_next: u32, bytes: &'a [u8], ctx: container::Ctx, @@ -550,16 +546,23 @@ mod symver_impl { } else { self.index += 1; - // Safe to unwrap as length of the byte slice was validated in the VerdefIter::next. - let ElfVerdaux { vda_name, vda_next } = - self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); + let do_next = |iter: &mut Self| { + let ElfVerdaux { vda_name, vda_next } = + iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; - // Bump the offset to the next ElfVerdaux entry. - self.offset += vda_next as usize; + // Bump the offset to the next ElfVerdaux entry. + iter.offset = iter.offset.checked_add(vda_next as usize)?; + + Some(Verdaux { + vda_name: vda_name as usize, + vda_next, + }) + }; - Some(Verdaux { - vda_name: vda_name as usize, - vda_next: vda_next as usize, + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None }) } } @@ -581,7 +584,7 @@ mod symver_impl { #[derive(Debug)] pub struct Verdaux { pub vda_name: usize, - pub vda_next: usize, + pub vda_next: u32, } /************************** @@ -689,48 +692,44 @@ mod symver_impl { } else { self.index += 1; - // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. - let ElfVerneed { - vn_version, - vn_cnt, - vn_file, - vn_aux, - vn_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - - // Get a slice of bytes of the `Vernaux` entries. - // - // | Verneed | .. | Verneed | Vernaux | Vernaux | .. | Verneed | .. - // ^--------------^ - // offset ^---------^ - // vn_aux - // ^----------------------------------^ - // vn_next - // - // Safe to unwrap as the length of the byte slice was validated in VerneedSection::parse. - let len = if vn_next > 0 { - (vn_next - vn_aux) as usize - } else { - // For the last entry, ElfVerneed->vn_next == 0. - // Therefore we compute the remaining length of the bytes buffer. - self.bytes.len() - self.offset - vn_aux as usize + let do_next = |iter: &mut Self| { + let ElfVerneed { + vn_version, + vn_cnt, + vn_file, + vn_aux, + vn_next, + } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + + // Validate offset to first ElfVernaux entry. + let offset = iter.offset.checked_add(vn_aux as usize)?; + + // Validate if offset is valid index into bytes slice. + if offset >= iter.bytes.len() { + return None; + } + + // Get a slice of bytes starting with the first ElfVernaux entry. + let bytes: &'a [u8] = &iter.bytes[offset..]; + + // Bump the offset to the next ElfVerneed entry. + iter.offset = iter.offset.checked_add(vn_next as usize)?; + + Some(Verneed { + vn_version, + vn_cnt, + vn_file: vn_file as usize, + vn_aux, + vn_next, + bytes, + ctx: iter.ctx, + }) }; - let bytes: &'a [u8] = self - .bytes - .pread_with(self.offset + vn_aux as usize, len) - .unwrap(); - - // Bump the offset to the next ElfVerneed entry. - self.offset += vn_next as usize; - - Some(Verneed { - vn_version, - vn_cnt, - vn_file: vn_file as usize, - vn_aux: vn_aux as usize, - vn_next: vn_next as usize, - bytes, - ctx: self.ctx, + + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None }) } } @@ -754,8 +753,8 @@ mod symver_impl { pub vn_version: u16, pub vn_cnt: u16, pub vn_file: usize, - pub vn_aux: usize, - pub vn_next: usize, + pub vn_aux: u32, + pub vn_next: u32, bytes: &'a [u8], ctx: container::Ctx, @@ -803,24 +802,31 @@ mod symver_impl { } else { self.index += 1; - // Safe to unwrap as the length of the byte slice was validated in VerneedIter::next. - let ElfVernaux { - vna_hash, - vna_flags, - vna_other, - vna_name, - vna_next, - } = self.bytes.pread_with(self.offset, self.ctx.le).unwrap(); - - // Bump the offset to the next ElfVernaux entry. - self.offset += vna_next as usize; - - Some(Vernaux { - vna_hash, - vna_flags, - vna_other, - vna_name: vna_name as usize, - vna_next: vna_next as usize, + let do_next = |iter: &mut Self| { + let ElfVernaux { + vna_hash, + vna_flags, + vna_other, + vna_name, + vna_next, + } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + + // Bump the offset to the next ElfVernaux entry. + iter.offset = iter.offset.checked_add(vna_next as usize)?; + + Some(Vernaux { + vna_hash, + vna_flags, + vna_other, + vna_name: vna_name as usize, + vna_next, + }) + }; + + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None }) } } @@ -845,7 +851,7 @@ mod symver_impl { pub vna_flags: u16, pub vna_other: u16, pub vna_name: usize, - pub vna_next: usize, + pub vna_next: u32, } #[cfg(test)] From 7c413015bdd88e23ffcaff936b090699cc4519e2 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Tue, 17 Aug 2021 22:05:40 +0200 Subject: [PATCH 15/20] elf.symver: apply review feedback - Return 0 as lower bound in size_hint impls (better hint for corrupt ELFs) - Verdef/Verdaux/Verneed/Vernaux Iterator: Start yielding None on the next call if there is no valid next index --- src/elf/symver.rs | 28 ++++++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 65184227..adeff134 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -456,6 +456,11 @@ mod symver_impl { // Bump the offset to the next ElfVerdef entry. iter.offset = iter.offset.checked_add(vd_next as usize)?; + // Start yielding None on the next call if there is no next offset. + if vd_next == 0 { + iter.index = iter.count; + } + Some(Verdef { vd_version, vd_flags, @@ -480,7 +485,7 @@ mod symver_impl { #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.count - self.index; - (len, Some(len)) + (0, Some(len)) } } @@ -553,6 +558,11 @@ mod symver_impl { // Bump the offset to the next ElfVerdaux entry. iter.offset = iter.offset.checked_add(vda_next as usize)?; + // Start yielding None on the next call if there is no next offset. + if vda_next == 0 { + iter.index = iter.count; + } + Some(Verdaux { vda_name: vda_name as usize, vda_next, @@ -570,7 +580,7 @@ mod symver_impl { #[inline] fn size_hint(&self) -> (usize, Option) { let len = usize::from(self.count - self.index); - (len, Some(len)) + (0, Some(len)) } } @@ -715,6 +725,11 @@ mod symver_impl { // Bump the offset to the next ElfVerneed entry. iter.offset = iter.offset.checked_add(vn_next as usize)?; + // Start yielding None on the next call if there is no next offset. + if vn_next == 0 { + iter.index = iter.count; + } + Some(Verneed { vn_version, vn_cnt, @@ -737,7 +752,7 @@ mod symver_impl { #[inline] fn size_hint(&self) -> (usize, Option) { let len = self.count - self.index; - (len, Some(len)) + (0, Some(len)) } } @@ -814,6 +829,11 @@ mod symver_impl { // Bump the offset to the next ElfVernaux entry. iter.offset = iter.offset.checked_add(vna_next as usize)?; + // Start yielding None on the next call if there is no next offset. + if vna_next == 0 { + iter.index = iter.count; + } + Some(Vernaux { vna_hash, vna_flags, @@ -834,7 +854,7 @@ mod symver_impl { #[inline] fn size_hint(&self) -> (usize, Option) { let len = usize::from(self.count - self.index); - (len, Some(len)) + (0, Some(len)) } } From 8dad2eb3962ce442072c503500945cc159331b4b Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Tue, 17 Aug 2021 22:46:56 +0200 Subject: [PATCH 16/20] elf.symver: add doc comments to constants and move field doc comments to exposed structs --- src/elf/symver.rs | 51 +++++++++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 22 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index adeff134..3daf7071 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -79,15 +79,22 @@ mod symver_impl { // Versym constants. + /// Constant describing a local symbol, see [`Versym::is_local`]. pub const VER_NDX_LOCAL: u16 = 0; + /// Constant describing a global symbol, see [`Versym::is_global`]. pub const VER_NDX_GLOBAL: u16 = 1; + /// Bitmask to check hidden bit, see [`Versym::is_hidden`]. pub const VERSYM_HIDDEN: u16 = 0x8000; + /// Bitmask to get version information, see [`Versym::version`]. pub const VERSYM_VERSION: u16 = 0x7fff; // Verdef constants. + /// Bitmask to check `base` flag in [`Verdef::vd_flags`]. pub const VER_FLG_BASE: u16 = 0x1; + /// Bitmask to check `weak` flag in [`Verdef::vd_flags`]. pub const VER_FLG_WEAK: u16 = 0x2; + /// Bitmask to check `info` flag in [`Verdef::vd_flags`]. pub const VER_FLG_INFO: u16 = 0x4; /******************** @@ -109,19 +116,12 @@ mod symver_impl { #[repr(C)] #[derive(Debug, Pread)] struct ElfVerdef { - /// Version revision. This field shall be set to 1. vd_version: u16, - /// Version information flag bitmask. vd_flags: u16, - /// Version index numeric value referencing the SHT_GNU_versym section. vd_ndx: u16, - /// Number of associated verdaux array entries. vd_cnt: u16, - /// Version name hash value (ELF hash function). vd_hash: u32, - /// Offset in bytes to a corresponding entry in an array of Elfxx_Verdaux structures. vd_aux: u32, - /// Offset to the next verdef entry, in bytes. vd_next: u32, } @@ -131,9 +131,7 @@ mod symver_impl { #[repr(C)] #[derive(Debug, Pread)] struct ElfVerdaux { - /// Offset to the version or dependency name string in the section header, in bytes. vda_name: u32, - /// Offset to the next verdaux entry, in bytes. vda_next: u32, } @@ -143,16 +141,10 @@ mod symver_impl { #[repr(C)] #[derive(Debug, Pread)] struct ElfVerneed { - /// Version of structure. This value is currently set to 1, and will be reset if the versioning - /// implementation is incompatibly altered. vn_version: u16, - /// Number of associated verneed array entries. vn_cnt: u16, - /// Offset to the file name string in the section header, in bytes. vn_file: u32, - /// Offset to a corresponding entry in the vernaux array, in bytes. vn_aux: u32, - /// Offset to the next verneed entry, in bytes. vn_next: u32, } @@ -162,17 +154,10 @@ mod symver_impl { #[repr(C)] #[derive(Debug, Pread)] struct ElfVernaux { - /// Dependency name hash value (ELF hash function). vna_hash: u32, - /// Dependency information flag bitmask. vna_flags: u16, - /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 - /// controls whether or not the object is hidden; if this bit is set, the object cannot be used - /// and the static linker will ignore the symbol's presence in the object. vna_other: u16, - /// Offset to the dependency name string in the section header, in bytes. vna_name: u32, - /// Offset to the next vernaux entry, in bytes. vna_next: u32, } @@ -498,12 +483,19 @@ mod symver_impl { /// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES #[derive(Debug)] pub struct Verdef<'a> { + /// Version revision. This field shall be set to 1. pub vd_version: u16, + /// Version information flag bitmask. pub vd_flags: u16, + /// Version index numeric 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 Elfxx_Verdaux structures. pub vd_aux: u32, + /// Offset to the next verdef entry, in bytes. pub vd_next: u32, bytes: &'a [u8], @@ -593,7 +585,9 @@ mod symver_impl { /// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS #[derive(Debug)] pub struct Verdaux { + /// Offset to the version or dependency name string in the section header, in bytes. pub vda_name: usize, + /// Offset to the next verdaux entry, in bytes. pub vda_next: u32, } @@ -765,10 +759,16 @@ mod symver_impl { /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG #[derive(Debug)] pub struct Verneed<'a> { + /// Version of structure. This value is currently set to 1, and will be reset if the versioning + /// implementation is incompatibly altered. pub vn_version: u16, + /// Number of associated verneed array entries. pub vn_cnt: u16, + /// Offset to the file name string in the section header, in bytes. pub vn_file: usize, + /// Offset to a corresponding entry in the vernaux array, in bytes. pub vn_aux: u32, + /// Offset to the next verneed entry, in bytes. pub vn_next: u32, bytes: &'a [u8], @@ -867,10 +867,17 @@ mod symver_impl { /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG #[derive(Debug)] pub struct Vernaux { + /// Dependency name hash value (ELF hash function). pub vna_hash: u32, + /// Dependency information flag bitmask. pub vna_flags: u16, + /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 + /// controls whether or not the object is hidden; if this bit is set, the object cannot be used + /// and the static linker will ignore the symbol's presence in the object. pub vna_other: u16, + /// Offset to the dependency name string in the section header, in bytes. pub vna_name: usize, + /// Offset to the next vernaux entry, in bytes. pub vna_next: u32, } From 49d2b3842f8d6fa8dc194985ae58d22b00b4c847 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Sun, 5 Sep 2021 12:36:43 +0200 Subject: [PATCH 17/20] elf.symver: fix comments in test --- tests/elf.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/elf.rs b/tests/elf.rs index 26223a74..a23095c8 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -262,10 +262,10 @@ fn check_symver_expectations( } if expect_verdef.is_empty() { - // We dont expect a version definition section. + // We dont expect a version needed section. assert!(elf.verdef.is_none()); } else { - // We expect a version definition section. + // We expect a version needed section. assert!(elf.verdef.is_some()); let verdef = elf.verdef.as_ref().unwrap(); @@ -355,7 +355,7 @@ fn check_symver_expectations_verdef( // Resolve version strings. let verstr = |idx| verdef.verstr.get_at(idx).unwrap(); - // ELF version deinitions. + // ELF version definitions. let defined_vers: Vec<_> = verdef.iter().collect(); assert_eq!( expect_verdef.keys().len(), @@ -425,7 +425,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { // keys : defined version nodes // value: vector of parent nodes for given version node (key) - // lib32 + // lib32 expectations let expect_lib32_versym : Vec = vec![ 0,0,4,5, @@ -443,7 +443,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { ("v2", vec!["v1"]), ].iter().cloned().collect(); - // lib64 + // lib64 expectations let expect_lib64_versym :Vec = vec![ 0,0,4,0, @@ -461,7 +461,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { ("v2", vec!["v1"]), ].iter().cloned().collect(); - // prog32 + // prog32 expectations let expect_prog32_versym : Vec = vec![ 0,2,0,3, @@ -477,7 +477,7 @@ fn test_symver() -> Result<(), goblin::error::Error> { let expect_prog32_verdef = SymverExpectation::new(); - // prog64 + // prog64 expectations let expect_prog64_versym : Vec = vec![ 0,2,0,3, From 74e6fb0363f6eaceea1cb3a0c7c1a3fbef4dec80 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Mon, 6 Sep 2021 21:22:20 +0200 Subject: [PATCH 18/20] elf.symver: move cfg guard globally on module --- src/elf/mod.rs | 1 + src/elf/symver.rs | 1518 ++++++++++++++++++++++----------------------- 2 files changed, 755 insertions(+), 764 deletions(-) diff --git a/src/elf/mod.rs b/src/elf/mod.rs index 2fddbad5..bb423834 100644 --- a/src/elf/mod.rs +++ b/src/elf/mod.rs @@ -53,6 +53,7 @@ pub mod dynamic; #[macro_use] pub mod reloc; pub mod note; +#[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] pub mod symver; macro_rules! if_sylvan { diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 3daf7071..c4da1009 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -59,876 +59,866 @@ //! [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERRQMTS //! [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERDEFS -#[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] -pub use symver_impl::*; - -#[cfg(all(any(feature = "elf32", feature = "elf64"), feature = "alloc"))] -mod symver_impl { - use crate::container; - use crate::elf::section_header::{ - SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED, SHT_GNU_VERSYM, - }; - use crate::error::{Error, Result}; - use crate::strtab::Strtab; - use core::iter::FusedIterator; - use scroll::Pread; - - /****************** - * ELF Constants * - ******************/ - - // Versym constants. - - /// Constant describing a local symbol, see [`Versym::is_local`]. - pub const VER_NDX_LOCAL: u16 = 0; - /// Constant describing a global symbol, see [`Versym::is_global`]. - pub const VER_NDX_GLOBAL: u16 = 1; - /// Bitmask to check hidden bit, see [`Versym::is_hidden`]. - pub const VERSYM_HIDDEN: u16 = 0x8000; - /// Bitmask to get version information, see [`Versym::version`]. - pub const VERSYM_VERSION: u16 = 0x7fff; - - // Verdef constants. - - /// Bitmask to check `base` flag in [`Verdef::vd_flags`]. - pub const VER_FLG_BASE: u16 = 0x1; - /// Bitmask to check `weak` flag in [`Verdef::vd_flags`]. - pub const VER_FLG_WEAK: u16 = 0x2; - /// Bitmask to check `info` flag in [`Verdef::vd_flags`]. - pub const VER_FLG_INFO: u16 = 0x4; - - /******************** - * ELF Structures * - ********************/ - - /// An ELF `Symbol Version` entry. - /// - /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL - #[repr(C)] - #[derive(Debug, Pread)] - struct ElfVersym { - vs_val: u16, - } - - /// An ELF `Version Definition` entry Elfxx_Verdef. - /// - /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES - #[repr(C)] - #[derive(Debug, Pread)] - struct ElfVerdef { - vd_version: u16, - vd_flags: u16, - vd_ndx: u16, - vd_cnt: u16, - vd_hash: u32, - vd_aux: u32, - vd_next: u32, - } - - /// An ELF `Version Definition Auxiliary` entry Elfxx_Verdaux. - /// - /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS - #[repr(C)] - #[derive(Debug, Pread)] - struct ElfVerdaux { - vda_name: u32, - vda_next: u32, - } - - /// An ELF `Version Need` entry Elfxx_Verneed. - /// - /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG - #[repr(C)] - #[derive(Debug, Pread)] - struct ElfVerneed { - vn_version: u16, - vn_cnt: u16, - vn_file: u32, - vn_aux: u32, - vn_next: u32, - } - - /// An ELF `Version Need Auxiliary` entry Elfxx_Vernaux. - /// - /// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG - #[repr(C)] - #[derive(Debug, Pread)] - struct ElfVernaux { - vna_hash: u32, - vna_flags: u16, - vna_other: u16, - vna_name: u32, - vna_next: u32, - } - - /******************** - * Symbol Version * - ********************/ - - /// Helper struct to iterate over [Symbol Version][Versym] entries. - #[derive(Debug)] - pub struct VersymSection<'a> { +use crate::container; +use crate::elf::section_header::{SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED, SHT_GNU_VERSYM}; +use crate::error::{Error, Result}; +use crate::strtab::Strtab; +use core::iter::FusedIterator; +use scroll::Pread; + +/****************** + * ELF Constants * + ******************/ + +// Versym constants. + +/// Constant describing a local symbol, see [`Versym::is_local`]. +pub const VER_NDX_LOCAL: u16 = 0; +/// Constant describing a global symbol, see [`Versym::is_global`]. +pub const VER_NDX_GLOBAL: u16 = 1; +/// Bitmask to check hidden bit, see [`Versym::is_hidden`]. +pub const VERSYM_HIDDEN: u16 = 0x8000; +/// Bitmask to get version information, see [`Versym::version`]. +pub const VERSYM_VERSION: u16 = 0x7fff; + +// Verdef constants. + +/// Bitmask to check `base` flag in [`Verdef::vd_flags`]. +pub const VER_FLG_BASE: u16 = 0x1; +/// Bitmask to check `weak` flag in [`Verdef::vd_flags`]. +pub const VER_FLG_WEAK: u16 = 0x2; +/// Bitmask to check `info` flag in [`Verdef::vd_flags`]. +pub const VER_FLG_INFO: u16 = 0x4; + +/******************** + * ELF Structures * + ********************/ + +/// An ELF `Symbol Version` entry. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVersym { + vs_val: u16, +} + +/// An ELF `Version Definition` entry Elfxx_Verdef. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVerdef { + vd_version: u16, + vd_flags: u16, + vd_ndx: u16, + vd_cnt: u16, + vd_hash: u32, + vd_aux: u32, + vd_next: u32, +} + +/// An ELF `Version Definition Auxiliary` entry Elfxx_Verdaux. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVerdaux { + vda_name: u32, + vda_next: u32, +} + +/// An ELF `Version Need` entry Elfxx_Verneed. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVerneed { + vn_version: u16, + vn_cnt: u16, + vn_file: u32, + vn_aux: u32, + vn_next: u32, +} + +/// An ELF `Version Need Auxiliary` entry Elfxx_Vernaux. +/// +/// https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG +#[repr(C)] +#[derive(Debug, Pread)] +struct ElfVernaux { + vna_hash: u32, + vna_flags: u16, + vna_other: u16, + vna_name: u32, + vna_next: u32, +} + +/******************** + * Symbol Version * + ********************/ + +/// Helper struct to iterate over [Symbol Version][Versym] entries. +#[derive(Debug)] +pub struct VersymSection<'a> { + bytes: &'a [u8], + ctx: container::Ctx, +} + +impl<'a> VersymSection<'a> { + pub fn parse( bytes: &'a [u8], + shdrs: &'_ [SectionHeader], ctx: container::Ctx, - } + ) -> Result>> { + // Get fields needed from optional `symbol version` section. + let (offset, size) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERSYM) { + (shdr.sh_offset as usize, shdr.sh_size as usize) + } else { + return Ok(None); + }; - impl<'a> VersymSection<'a> { - pub fn parse( - bytes: &'a [u8], - shdrs: &'_ [SectionHeader], - ctx: container::Ctx, - ) -> Result>> { - // Get fields needed from optional `symbol version` section. - let (offset, size) = - if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERSYM) { - (shdr.sh_offset as usize, shdr.sh_size as usize) - } else { - return Ok(None); - }; - - // Get a slice of bytes of the `version definition` section content. - let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - - Ok(Some(VersymSection { bytes, ctx })) - } + // Get a slice of bytes of the `version definition` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - /// Get an iterator over the [`Versym`] entries. - #[inline] - pub fn iter(&'a self) -> VersymIter<'a> { - self.into_iter() - } + Ok(Some(VersymSection { bytes, ctx })) + } - /// True if there are no [`Versym`] entries. - #[inline] - pub fn is_empty(&self) -> bool { - self.bytes.is_empty() - } + /// Get an iterator over the [`Versym`] entries. + #[inline] + pub fn iter(&'a self) -> VersymIter<'a> { + self.into_iter() + } - /// Number of [`Versym`] entries. - #[inline] - pub fn len(&self) -> usize { - let entsize = core::mem::size_of::(); + /// True if there are no [`Versym`] entries. + #[inline] + pub fn is_empty(&self) -> bool { + self.bytes.is_empty() + } - self.bytes.len() / entsize - } + /// Number of [`Versym`] entries. + #[inline] + pub fn len(&self) -> usize { + let entsize = core::mem::size_of::(); - /// Get [`Versym`] entry at index. - #[inline] - pub fn get_at(&self, idx: usize) -> Option { - let entsize = core::mem::size_of::(); - let offset = idx.checked_mul(entsize)?; + self.bytes.len() / entsize + } - self.bytes - .pread_with::(offset, self.ctx.le) - .ok() - .map(|ElfVersym { vs_val }| Versym { vs_val }) - } + /// Get [`Versym`] entry at index. + #[inline] + pub fn get_at(&self, idx: usize) -> Option { + let entsize = core::mem::size_of::(); + let offset = idx.checked_mul(entsize)?; + + self.bytes + .pread_with::(offset, self.ctx.le) + .ok() + .map(|ElfVersym { vs_val }| Versym { vs_val }) } +} - impl<'a> IntoIterator for &'_ VersymSection<'a> { - type Item = as Iterator>::Item; - type IntoIter = VersymIter<'a>; +impl<'a> IntoIterator for &'_ VersymSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VersymIter<'a>; - fn into_iter(self) -> Self::IntoIter { - VersymIter { - bytes: self.bytes, - offset: 0, - ctx: self.ctx, - } + fn into_iter(self) -> Self::IntoIter { + VersymIter { + bytes: self.bytes, + offset: 0, + ctx: self.ctx, } } +} - /// Iterator over the [`Versym`] entries from the [`SHT_GNU_VERSYM`] section. - pub struct VersymIter<'a> { - bytes: &'a [u8], - offset: usize, - ctx: container::Ctx, - } +/// Iterator over the [`Versym`] entries from the [`SHT_GNU_VERSYM`] section. +pub struct VersymIter<'a> { + bytes: &'a [u8], + offset: usize, + ctx: container::Ctx, +} - impl<'a> Iterator for VersymIter<'a> { - type Item = Versym; +impl<'a> Iterator for VersymIter<'a> { + type Item = Versym; - fn next(&mut self) -> Option { - if self.offset >= self.bytes.len() { - None - } else { - self.bytes - .gread_with::(&mut self.offset, self.ctx.le) - .ok() - .map(|ElfVersym { vs_val }| Versym { vs_val }) - .or_else(|| { - // self.bytes are not a multiple of ElfVersym. - // Adjust offset to continue yielding Nones. - self.offset = self.bytes.len(); - None - }) - } + fn next(&mut self) -> Option { + if self.offset >= self.bytes.len() { + None + } else { + self.bytes + .gread_with::(&mut self.offset, self.ctx.le) + .ok() + .map(|ElfVersym { vs_val }| Versym { vs_val }) + .or_else(|| { + // self.bytes are not a multiple of ElfVersym. + // Adjust offset to continue yielding Nones. + self.offset = self.bytes.len(); + None + }) } + } - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = (self.bytes.len() - self.offset) / core::mem::size_of::(); - (len, Some(len)) - } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = (self.bytes.len() - self.offset) / core::mem::size_of::(); + (len, Some(len)) } +} - impl ExactSizeIterator for VersymIter<'_> {} +impl ExactSizeIterator for VersymIter<'_> {} - impl FusedIterator for VersymIter<'_> {} +impl FusedIterator for VersymIter<'_> {} - /// An ELF [Symbol Version][lsb-versym] entry. - /// - /// [lsb-versym]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL - #[derive(Debug)] - pub struct Versym { - pub vs_val: u16, - } +/// An ELF [Symbol Version][lsb-versym] entry. +/// +/// [lsb-versym]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#SYMVERTBL +#[derive(Debug)] +pub struct Versym { + pub vs_val: u16, +} - impl Versym { - /// Returns true if the symbol is local and not available outside the object according to - /// [`VER_NDX_LOCAL`]. - #[inline] - pub fn is_local(&self) -> bool { - self.vs_val == VER_NDX_LOCAL - } +impl Versym { + /// Returns true if the symbol is local and not available outside the object according to + /// [`VER_NDX_LOCAL`]. + #[inline] + pub fn is_local(&self) -> bool { + self.vs_val == VER_NDX_LOCAL + } - /// Returns true if the symbol is defined in this object and globally available according - /// to [`VER_NDX_GLOBAL`]. - #[inline] - pub fn is_global(&self) -> bool { - self.vs_val == VER_NDX_GLOBAL - } + /// Returns true if the symbol is defined in this object and globally available according + /// to [`VER_NDX_GLOBAL`]. + #[inline] + pub fn is_global(&self) -> bool { + self.vs_val == VER_NDX_GLOBAL + } - /// Returns true if the `hidden` bit is set according to the [`VERSYM_HIDDEN`] bitmask. - #[inline] - pub fn is_hidden(&self) -> bool { - (self.vs_val & VERSYM_HIDDEN) == VERSYM_HIDDEN - } + /// Returns true if the `hidden` bit is set according to the [`VERSYM_HIDDEN`] bitmask. + #[inline] + pub fn is_hidden(&self) -> bool { + (self.vs_val & VERSYM_HIDDEN) == VERSYM_HIDDEN + } - /// Returns the symbol version index according to the [`VERSYM_VERSION`] bitmask. - #[inline] - pub fn version(&self) -> u16 { - self.vs_val & VERSYM_VERSION - } + /// Returns the symbol version index according to the [`VERSYM_VERSION`] bitmask. + #[inline] + pub fn version(&self) -> u16 { + self.vs_val & VERSYM_VERSION } +} - /************************ - * Version Definition * - ************************/ +/************************ + * Version Definition * + ************************/ + +/// Helper struct to iterate over [Version Definition][Verdef] and [Version Definition +/// Auxiliary][Verdaux] entries. +#[derive(Debug)] +pub struct VerdefSection<'a> { + /// String table used to resolve version strings. + pub verstr: Strtab<'a>, + bytes: &'a [u8], + count: usize, + ctx: container::Ctx, +} - /// Helper struct to iterate over [Version Definition][Verdef] and [Version Definition - /// Auxiliary][Verdaux] entries. - #[derive(Debug)] - pub struct VerdefSection<'a> { - /// String table used to resolve version strings. - pub verstr: Strtab<'a>, +impl<'a> VerdefSection<'a> { + pub fn parse( bytes: &'a [u8], - count: usize, + shdrs: &'_ [SectionHeader], ctx: container::Ctx, - } - - impl<'a> VerdefSection<'a> { - pub fn parse( - bytes: &'a [u8], - shdrs: &'_ [SectionHeader], - ctx: container::Ctx, - ) -> Result>> { - // Get fields needed from optional `version definition` section. - let (link_idx, offset, size, count) = - if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { - ( - shdr.sh_link as usize, // Encodes the string table. - shdr.sh_offset as usize, - shdr.sh_size as usize, - shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. - ) - } else { - return Ok(None); - }; - - // Get string table which is used to resolve version strings. - let verstr = { - // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or_else(|| { - Error::Malformed( - "Section header of string table for SHT_GNU_VERDEF section not found!" - .into(), - ) - })?; - - Strtab::parse( - bytes, - shdr_link.sh_offset as usize, - shdr_link.sh_size as usize, - 0x0, /* Delimiter */ - )? + ) -> Result>> { + // Get fields needed from optional `version definition` section. + let (link_idx, offset, size, count) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { + ( + shdr.sh_link as usize, // Encodes the string table. + shdr.sh_offset as usize, + shdr.sh_size as usize, + shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. + ) + } else { + return Ok(None); }; - // Get a slice of bytes of the `version definition` section content. - let bytes: &'a [u8] = bytes.pread_with(offset, size)?; + // Get string table which is used to resolve version strings. + let verstr = { + // Linked section refers to string table. + let shdr_link = shdrs.get(link_idx).ok_or_else(|| { + Error::Malformed( + "Section header of string table for SHT_GNU_VERDEF section not found!".into(), + ) + })?; - Ok(Some(VerdefSection { - verstr, + Strtab::parse( bytes, - count, - ctx, - })) - } + shdr_link.sh_offset as usize, + shdr_link.sh_size as usize, + 0x0, /* Delimiter */ + )? + }; - /// Get an iterator over the [`Verdef`] entries. - #[inline] - pub fn iter(&'a self) -> VerdefIter<'a> { - self.into_iter() - } - } + // Get a slice of bytes of the `version definition` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - impl<'a> IntoIterator for &'_ VerdefSection<'a> { - type Item = as Iterator>::Item; - type IntoIter = VerdefIter<'a>; + Ok(Some(VerdefSection { + verstr, + bytes, + count, + ctx, + })) + } - #[inline] - fn into_iter(self) -> Self::IntoIter { - VerdefIter { - bytes: self.bytes, - count: self.count, - index: 0, - offset: 0, - ctx: self.ctx, - } - } + /// Get an iterator over the [`Verdef`] entries. + #[inline] + pub fn iter(&'a self) -> VerdefIter<'a> { + self.into_iter() } +} - /// Iterator over the [`Verdef`] entries from the [`SHT_GNU_VERDEF`] section. - pub struct VerdefIter<'a> { - bytes: &'a [u8], - count: usize, - index: usize, - offset: usize, - ctx: container::Ctx, +impl<'a> IntoIterator for &'_ VerdefSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerdefIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VerdefIter { + bytes: self.bytes, + count: self.count, + index: 0, + offset: 0, + ctx: self.ctx, + } } +} - impl<'a> Iterator for VerdefIter<'a> { - type Item = Verdef<'a>; +/// Iterator over the [`Verdef`] entries from the [`SHT_GNU_VERDEF`] section. +pub struct VerdefIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, +} - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; - - let do_next = |iter: &mut Self| { - let ElfVerdef { - vd_version, - vd_flags, - vd_ndx, - vd_cnt, - vd_hash, - vd_aux, - vd_next, - } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; - - // Validate offset to first ElfVerdaux entry. - let offset = iter.offset.checked_add(vd_aux as usize)?; - - // Validate if offset is valid index into bytes slice. - if offset >= iter.bytes.len() { - return None; - } - - // Get a slice of bytes starting with the first ElfVerdaux entry. - let bytes: &'a [u8] = &iter.bytes[offset..]; - - // Bump the offset to the next ElfVerdef entry. - iter.offset = iter.offset.checked_add(vd_next as usize)?; - - // Start yielding None on the next call if there is no next offset. - if vd_next == 0 { - iter.index = iter.count; - } - - Some(Verdef { - vd_version, - vd_flags, - vd_ndx, - vd_cnt, - vd_hash, - vd_aux, - vd_next, - bytes, - ctx: iter.ctx, - }) - }; - - do_next(self).or_else(|| { - // Adjust current index to count in case of an error. - self.index = self.count; - None +impl<'a> Iterator for VerdefIter<'a> { + type Item = Verdef<'a>; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + let do_next = |iter: &mut Self| { + let ElfVerdef { + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, + vd_aux, + vd_next, + } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + + // Validate offset to first ElfVerdaux entry. + let offset = iter.offset.checked_add(vd_aux as usize)?; + + // Validate if offset is valid index into bytes slice. + if offset >= iter.bytes.len() { + return None; + } + + // Get a slice of bytes starting with the first ElfVerdaux entry. + let bytes: &'a [u8] = &iter.bytes[offset..]; + + // Bump the offset to the next ElfVerdef entry. + iter.offset = iter.offset.checked_add(vd_next as usize)?; + + // Start yielding None on the next call if there is no next offset. + if vd_next == 0 { + iter.index = iter.count; + } + + Some(Verdef { + vd_version, + vd_flags, + vd_ndx, + vd_cnt, + vd_hash, + vd_aux, + vd_next, + bytes, + ctx: iter.ctx, }) - } - } + }; - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; - (0, Some(len)) + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None + }) } } - impl ExactSizeIterator for VerdefIter<'_> {} - - impl FusedIterator for VerdefIter<'_> {} - - /// An ELF [Version Definition][lsb-verdef] entry . - /// - /// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES - #[derive(Debug)] - pub struct Verdef<'a> { - /// Version revision. This field shall be set to 1. - pub vd_version: u16, - /// Version information flag bitmask. - pub vd_flags: u16, - /// Version index numeric 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 Elfxx_Verdaux structures. - pub vd_aux: u32, - /// Offset to the next verdef entry, in bytes. - pub vd_next: u32, - - bytes: &'a [u8], - ctx: container::Ctx, + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (0, Some(len)) } +} - impl<'a> Verdef<'a> { - /// Get an iterator over the [`Verdaux`] entries of this [`Verdef`] entry. - #[inline] - pub fn iter(&'a self) -> VerdauxIter<'a> { - self.into_iter() - } +impl ExactSizeIterator for VerdefIter<'_> {} + +impl FusedIterator for VerdefIter<'_> {} + +/// An ELF [Version Definition][lsb-verdef] entry . +/// +/// [lsb-verdef]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFENTRIES +#[derive(Debug)] +pub struct Verdef<'a> { + /// Version revision. This field shall be set to 1. + pub vd_version: u16, + /// Version information flag bitmask. + pub vd_flags: u16, + /// Version index numeric 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 Elfxx_Verdaux structures. + pub vd_aux: u32, + /// Offset to the next verdef entry, in bytes. + pub vd_next: u32, + + bytes: &'a [u8], + ctx: container::Ctx, +} + +impl<'a> Verdef<'a> { + /// Get an iterator over the [`Verdaux`] entries of this [`Verdef`] entry. + #[inline] + pub fn iter(&'a self) -> VerdauxIter<'a> { + self.into_iter() } +} - impl<'a> IntoIterator for &'_ Verdef<'a> { - type Item = as Iterator>::Item; - type IntoIter = VerdauxIter<'a>; +impl<'a> IntoIterator for &'_ Verdef<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerdauxIter<'a>; - fn into_iter(self) -> Self::IntoIter { - VerdauxIter { - bytes: self.bytes, - count: self.vd_cnt, - index: 0, - offset: 0, - ctx: self.ctx, - } + fn into_iter(self) -> Self::IntoIter { + VerdauxIter { + bytes: self.bytes, + count: self.vd_cnt, + index: 0, + offset: 0, + ctx: self.ctx, } } +} - /// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. - pub struct VerdauxIter<'a> { - bytes: &'a [u8], - count: u16, - index: u16, - offset: usize, - ctx: container::Ctx, - } - - impl<'a> Iterator for VerdauxIter<'a> { - type Item = Verdaux; +/// Iterator over the [`Verdaux`] entries for an specific [`Verdef`] entry. +pub struct VerdauxIter<'a> { + bytes: &'a [u8], + count: u16, + index: u16, + offset: usize, + ctx: container::Ctx, +} - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; +impl<'a> Iterator for VerdauxIter<'a> { + type Item = Verdaux; - let do_next = |iter: &mut Self| { - let ElfVerdaux { vda_name, vda_next } = - iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; - // Bump the offset to the next ElfVerdaux entry. - iter.offset = iter.offset.checked_add(vda_next as usize)?; + let do_next = |iter: &mut Self| { + let ElfVerdaux { vda_name, vda_next } = + iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; - // Start yielding None on the next call if there is no next offset. - if vda_next == 0 { - iter.index = iter.count; - } + // Bump the offset to the next ElfVerdaux entry. + iter.offset = iter.offset.checked_add(vda_next as usize)?; - Some(Verdaux { - vda_name: vda_name as usize, - vda_next, - }) - }; + // Start yielding None on the next call if there is no next offset. + if vda_next == 0 { + iter.index = iter.count; + } - do_next(self).or_else(|| { - // Adjust current index to count in case of an error. - self.index = self.count; - None + Some(Verdaux { + vda_name: vda_name as usize, + vda_next, }) - } - } + }; - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = usize::from(self.count - self.index); - (0, Some(len)) + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None + }) } } - impl ExactSizeIterator for VerdauxIter<'_> {} + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = usize::from(self.count - self.index); + (0, Some(len)) + } +} + +impl ExactSizeIterator for VerdauxIter<'_> {} - impl FusedIterator for VerdauxIter<'_> {} +impl FusedIterator for VerdauxIter<'_> {} - /// An ELF [Version Definition Auxiliary][lsb-verdaux] entry. - /// - /// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS - #[derive(Debug)] - pub struct Verdaux { - /// Offset to the version or dependency name string in the section header, in bytes. - pub vda_name: usize, - /// Offset to the next verdaux entry, in bytes. - pub vda_next: u32, - } +/// An ELF [Version Definition Auxiliary][lsb-verdaux] entry. +/// +/// [lsb-verdaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERDEFEXTS +#[derive(Debug)] +pub struct Verdaux { + /// Offset to the version or dependency name string in the section header, in bytes. + pub vda_name: usize, + /// Offset to the next verdaux entry, in bytes. + pub vda_next: u32, +} - /************************** - * Version Requirements * - **************************/ +/************************** + * Version Requirements * + **************************/ + +/// Helper struct to iterate over [Version Needed][Verneed] and [Version Needed +/// Auxiliary][Vernaux] entries. +#[derive(Debug)] +pub struct VerneedSection<'a> { + /// String table used to resolve version strings. + pub verstr: Strtab<'a>, + bytes: &'a [u8], + count: usize, + ctx: container::Ctx, +} - /// Helper struct to iterate over [Version Needed][Verneed] and [Version Needed - /// Auxiliary][Vernaux] entries. - #[derive(Debug)] - pub struct VerneedSection<'a> { - /// String table used to resolve version strings. - pub verstr: Strtab<'a>, +impl<'a> VerneedSection<'a> { + /// Try to parse the optional [`SHT_GNU_VERNEED`] section. + pub fn parse( bytes: &'a [u8], - count: usize, + shdrs: &'_ [SectionHeader], ctx: container::Ctx, - } - - impl<'a> VerneedSection<'a> { - /// Try to parse the optional [`SHT_GNU_VERNEED`] section. - pub fn parse( - bytes: &'a [u8], - shdrs: &'_ [SectionHeader], - ctx: container::Ctx, - ) -> Result>> { - // Get fields needed from optional `version needed` section. - let (link_idx, offset, size, count) = - if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { - ( - shdr.sh_link as usize, // Encodes the string table. - shdr.sh_offset as usize, - shdr.sh_size as usize, - shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. - ) - } else { - return Ok(None); - }; - - // Get string table which is used to resolve version strings. - let verstr = { - // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or_else(|| { - Error::Malformed( - "Section header of string table for SHT_GNU_VERNEED section not found!" - .into(), - ) - })?; - - Strtab::parse( - bytes, - shdr_link.sh_offset as usize, - shdr_link.sh_size as usize, - 0x0, /* Delimiter */ - )? + ) -> Result>> { + // Get fields needed from optional `version needed` section. + let (link_idx, offset, size, count) = + if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { + ( + shdr.sh_link as usize, // Encodes the string table. + shdr.sh_offset as usize, + shdr.sh_size as usize, + shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. + ) + } else { + return Ok(None); }; - // Get a slice of bytes of the `version needed` section content. - let bytes: &'a [u8] = bytes.pread_with(offset, size)?; + // Get string table which is used to resolve version strings. + let verstr = { + // Linked section refers to string table. + let shdr_link = shdrs.get(link_idx).ok_or_else(|| { + Error::Malformed( + "Section header of string table for SHT_GNU_VERNEED section not found!".into(), + ) + })?; - Ok(Some(VerneedSection { - verstr, + Strtab::parse( bytes, - count, - ctx, - })) - } - - /// Get an iterator over the [`Verneed`] entries. - #[inline] - pub fn iter(&'a self) -> VerneedIter<'a> { - self.into_iter() - } - } + shdr_link.sh_offset as usize, + shdr_link.sh_size as usize, + 0x0, /* Delimiter */ + )? + }; - impl<'a> IntoIterator for &'_ VerneedSection<'a> { - type Item = as Iterator>::Item; - type IntoIter = VerneedIter<'a>; + // Get a slice of bytes of the `version needed` section content. + let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - #[inline] - fn into_iter(self) -> Self::IntoIter { - VerneedIter { - bytes: self.bytes, - count: self.count, - index: 0, - offset: 0, - ctx: self.ctx, - } - } + Ok(Some(VerneedSection { + verstr, + bytes, + count, + ctx, + })) } - /// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. - pub struct VerneedIter<'a> { - bytes: &'a [u8], - count: usize, - index: usize, - offset: usize, - ctx: container::Ctx, + /// Get an iterator over the [`Verneed`] entries. + #[inline] + pub fn iter(&'a self) -> VerneedIter<'a> { + self.into_iter() } +} - impl<'a> Iterator for VerneedIter<'a> { - type Item = Verneed<'a>; - - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; - - let do_next = |iter: &mut Self| { - let ElfVerneed { - vn_version, - vn_cnt, - vn_file, - vn_aux, - vn_next, - } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; - - // Validate offset to first ElfVernaux entry. - let offset = iter.offset.checked_add(vn_aux as usize)?; - - // Validate if offset is valid index into bytes slice. - if offset >= iter.bytes.len() { - return None; - } - - // Get a slice of bytes starting with the first ElfVernaux entry. - let bytes: &'a [u8] = &iter.bytes[offset..]; - - // Bump the offset to the next ElfVerneed entry. - iter.offset = iter.offset.checked_add(vn_next as usize)?; - - // Start yielding None on the next call if there is no next offset. - if vn_next == 0 { - iter.index = iter.count; - } - - Some(Verneed { - vn_version, - vn_cnt, - vn_file: vn_file as usize, - vn_aux, - vn_next, - bytes, - ctx: iter.ctx, - }) - }; - - do_next(self).or_else(|| { - // Adjust current index to count in case of an error. - self.index = self.count; - None - }) - } - } +impl<'a> IntoIterator for &'_ VerneedSection<'a> { + type Item = as Iterator>::Item; + type IntoIter = VerneedIter<'a>; - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.count - self.index; - (0, Some(len)) + #[inline] + fn into_iter(self) -> Self::IntoIter { + VerneedIter { + bytes: self.bytes, + count: self.count, + index: 0, + offset: 0, + ctx: self.ctx, } } +} - impl ExactSizeIterator for VerneedIter<'_> {} - - impl FusedIterator for VerneedIter<'_> {} +/// Iterator over the [`Verneed`] entries from the [`SHT_GNU_VERNEED`] section. +pub struct VerneedIter<'a> { + bytes: &'a [u8], + count: usize, + index: usize, + offset: usize, + ctx: container::Ctx, +} - /// An ELF [Version Need][lsb-verneed] entry . - /// - /// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG - #[derive(Debug)] - pub struct Verneed<'a> { - /// Version of structure. This value is currently set to 1, and will be reset if the versioning - /// implementation is incompatibly altered. - pub vn_version: u16, - /// Number of associated verneed array entries. - pub vn_cnt: u16, - /// Offset to the file name string in the section header, in bytes. - pub vn_file: usize, - /// Offset to a corresponding entry in the vernaux array, in bytes. - pub vn_aux: u32, - /// Offset to the next verneed entry, in bytes. - pub vn_next: u32, +impl<'a> Iterator for VerneedIter<'a> { + type Item = Verneed<'a>; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + let do_next = |iter: &mut Self| { + let ElfVerneed { + vn_version, + vn_cnt, + vn_file, + vn_aux, + vn_next, + } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + + // Validate offset to first ElfVernaux entry. + let offset = iter.offset.checked_add(vn_aux as usize)?; + + // Validate if offset is valid index into bytes slice. + if offset >= iter.bytes.len() { + return None; + } + + // Get a slice of bytes starting with the first ElfVernaux entry. + let bytes: &'a [u8] = &iter.bytes[offset..]; + + // Bump the offset to the next ElfVerneed entry. + iter.offset = iter.offset.checked_add(vn_next as usize)?; + + // Start yielding None on the next call if there is no next offset. + if vn_next == 0 { + iter.index = iter.count; + } + + Some(Verneed { + vn_version, + vn_cnt, + vn_file: vn_file as usize, + vn_aux, + vn_next, + bytes, + ctx: iter.ctx, + }) + }; - bytes: &'a [u8], - ctx: container::Ctx, + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None + }) + } } - impl<'a> Verneed<'a> { - /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. - #[inline] - pub fn iter(&'a self) -> VernauxIter<'a> { - self.into_iter() - } + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.count - self.index; + (0, Some(len)) } +} - impl<'a> IntoIterator for &'_ Verneed<'a> { - type Item = as Iterator>::Item; - type IntoIter = VernauxIter<'a>; +impl ExactSizeIterator for VerneedIter<'_> {} + +impl FusedIterator for VerneedIter<'_> {} + +/// An ELF [Version Need][lsb-verneed] entry . +/// +/// [lsb-verneed]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDFIG +#[derive(Debug)] +pub struct Verneed<'a> { + /// Version of structure. This value is currently set to 1, and will be reset if the versioning + /// implementation is incompatibly altered. + pub vn_version: u16, + /// Number of associated verneed array entries. + pub vn_cnt: u16, + /// Offset to the file name string in the section header, in bytes. + pub vn_file: usize, + /// Offset to a corresponding entry in the vernaux array, in bytes. + pub vn_aux: u32, + /// Offset to the next verneed entry, in bytes. + pub vn_next: u32, + + bytes: &'a [u8], + ctx: container::Ctx, +} - #[inline] - fn into_iter(self) -> Self::IntoIter { - VernauxIter { - bytes: self.bytes, - count: self.vn_cnt, - index: 0, - offset: 0, - ctx: self.ctx, - } - } +impl<'a> Verneed<'a> { + /// Get an iterator over the [`Vernaux`] entries of this [`Verneed`] entry. + #[inline] + pub fn iter(&'a self) -> VernauxIter<'a> { + self.into_iter() } +} - /// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. - pub struct VernauxIter<'a> { - bytes: &'a [u8], - count: u16, - index: u16, - offset: usize, - ctx: container::Ctx, +impl<'a> IntoIterator for &'_ Verneed<'a> { + type Item = as Iterator>::Item; + type IntoIter = VernauxIter<'a>; + + #[inline] + fn into_iter(self) -> Self::IntoIter { + VernauxIter { + bytes: self.bytes, + count: self.vn_cnt, + index: 0, + offset: 0, + ctx: self.ctx, + } } +} - impl<'a> Iterator for VernauxIter<'a> { - type Item = Vernaux; +/// Iterator over the [`Vernaux`] entries for an specific [`Verneed`] entry. +pub struct VernauxIter<'a> { + bytes: &'a [u8], + count: u16, + index: u16, + offset: usize, + ctx: container::Ctx, +} - fn next(&mut self) -> Option { - if self.index >= self.count { - None - } else { - self.index += 1; - - let do_next = |iter: &mut Self| { - let ElfVernaux { - vna_hash, - vna_flags, - vna_other, - vna_name, - vna_next, - } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; - - // Bump the offset to the next ElfVernaux entry. - iter.offset = iter.offset.checked_add(vna_next as usize)?; - - // Start yielding None on the next call if there is no next offset. - if vna_next == 0 { - iter.index = iter.count; - } - - Some(Vernaux { - vna_hash, - vna_flags, - vna_other, - vna_name: vna_name as usize, - vna_next, - }) - }; - - do_next(self).or_else(|| { - // Adjust current index to count in case of an error. - self.index = self.count; - None +impl<'a> Iterator for VernauxIter<'a> { + type Item = Vernaux; + + fn next(&mut self) -> Option { + if self.index >= self.count { + None + } else { + self.index += 1; + + let do_next = |iter: &mut Self| { + let ElfVernaux { + vna_hash, + vna_flags, + vna_other, + vna_name, + vna_next, + } = iter.bytes.pread_with(iter.offset, iter.ctx.le).ok()?; + + // Bump the offset to the next ElfVernaux entry. + iter.offset = iter.offset.checked_add(vna_next as usize)?; + + // Start yielding None on the next call if there is no next offset. + if vna_next == 0 { + iter.index = iter.count; + } + + Some(Vernaux { + vna_hash, + vna_flags, + vna_other, + vna_name: vna_name as usize, + vna_next, }) - } - } + }; - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = usize::from(self.count - self.index); - (0, Some(len)) + do_next(self).or_else(|| { + // Adjust current index to count in case of an error. + self.index = self.count; + None + }) } } - impl ExactSizeIterator for VernauxIter<'_> {} - - impl FusedIterator for VernauxIter<'_> {} - - /// An ELF [Version Need Auxiliary][lsb-vernaux] entry. - /// - /// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG - #[derive(Debug)] - pub struct Vernaux { - /// Dependency name hash value (ELF hash function). - pub vna_hash: u32, - /// Dependency information flag bitmask. - pub vna_flags: u16, - /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 - /// controls whether or not the object is hidden; if this bit is set, the object cannot be used - /// and the static linker will ignore the symbol's presence in the object. - pub vna_other: u16, - /// Offset to the dependency name string in the section header, in bytes. - pub vna_name: usize, - /// Offset to the next vernaux entry, in bytes. - pub vna_next: u32, - } - - #[cfg(test)] - mod test { - use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed, ElfVersym}; - use super::{Versym, VERSYM_HIDDEN, VER_NDX_GLOBAL, VER_NDX_LOCAL}; - use core::mem::size_of; - - #[test] - fn check_size() { - assert_eq!(2, size_of::()); - assert_eq!(20, size_of::()); - assert_eq!(8, size_of::()); - assert_eq!(16, size_of::()); - assert_eq!(16, size_of::()); - } - - #[test] - fn check_versym() { - let local = Versym { - vs_val: VER_NDX_LOCAL, - }; - assert_eq!(true, local.is_local()); - assert_eq!(false, local.is_global()); - assert_eq!(false, local.is_hidden()); - assert_eq!(VER_NDX_LOCAL, local.version()); - - let global = Versym { - vs_val: VER_NDX_GLOBAL, - }; - assert_eq!(false, global.is_local()); - assert_eq!(true, global.is_global()); - assert_eq!(false, global.is_hidden()); - assert_eq!(VER_NDX_GLOBAL, global.version()); + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = usize::from(self.count - self.index); + (0, Some(len)) + } +} - let hidden = Versym { - vs_val: VERSYM_HIDDEN, - }; - assert_eq!(false, hidden.is_local()); - assert_eq!(false, hidden.is_global()); - assert_eq!(true, hidden.is_hidden()); - assert_eq!(0, hidden.version()); +impl ExactSizeIterator for VernauxIter<'_> {} + +impl FusedIterator for VernauxIter<'_> {} + +/// An ELF [Version Need Auxiliary][lsb-vernaux] entry. +/// +/// [lsb-vernaux]: https://refspecs.linuxbase.org/LSB_5.0.0/LSB-Core-generic/LSB-Core-generic/symversion.html#VERNEEDEXTFIG +#[derive(Debug)] +pub struct Vernaux { + /// Dependency name hash value (ELF hash function). + pub vna_hash: u32, + /// Dependency information flag bitmask. + pub vna_flags: u16, + /// Object file version identifier used in the .gnu.version symbol version array. Bit number 15 + /// controls whether or not the object is hidden; if this bit is set, the object cannot be used + /// and the static linker will ignore the symbol's presence in the object. + pub vna_other: u16, + /// Offset to the dependency name string in the section header, in bytes. + pub vna_name: usize, + /// Offset to the next vernaux entry, in bytes. + pub vna_next: u32, +} - let hidden = Versym { - vs_val: VERSYM_HIDDEN | 0x123, - }; - assert_eq!(false, hidden.is_local()); - assert_eq!(false, hidden.is_global()); - assert_eq!(true, hidden.is_hidden()); - assert_eq!(0x123, hidden.version()); - } +#[cfg(test)] +mod test { + use super::{ElfVerdaux, ElfVerdef, ElfVernaux, ElfVerneed, ElfVersym}; + use super::{Versym, VERSYM_HIDDEN, VER_NDX_GLOBAL, VER_NDX_LOCAL}; + use core::mem::size_of; + + #[test] + fn check_size() { + assert_eq!(2, size_of::()); + assert_eq!(20, size_of::()); + assert_eq!(8, size_of::()); + assert_eq!(16, size_of::()); + assert_eq!(16, size_of::()); + } + + #[test] + fn check_versym() { + let local = Versym { + vs_val: VER_NDX_LOCAL, + }; + assert_eq!(true, local.is_local()); + assert_eq!(false, local.is_global()); + assert_eq!(false, local.is_hidden()); + assert_eq!(VER_NDX_LOCAL, local.version()); + + let global = Versym { + vs_val: VER_NDX_GLOBAL, + }; + assert_eq!(false, global.is_local()); + assert_eq!(true, global.is_global()); + assert_eq!(false, global.is_hidden()); + assert_eq!(VER_NDX_GLOBAL, global.version()); + + let hidden = Versym { + vs_val: VERSYM_HIDDEN, + }; + assert_eq!(false, hidden.is_local()); + assert_eq!(false, hidden.is_global()); + assert_eq!(true, hidden.is_hidden()); + assert_eq!(0, hidden.version()); + + let hidden = Versym { + vs_val: VERSYM_HIDDEN | 0x123, + }; + assert_eq!(false, hidden.is_local()); + assert_eq!(false, hidden.is_global()); + assert_eq!(true, hidden.is_hidden()); + assert_eq!(0x123, hidden.version()); } } From f029db3e41bf4de593851992cca7f65d539797f6 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Wed, 8 Sep 2021 22:17:59 +0200 Subject: [PATCH 19/20] elf.symver: apply review feedback - impl From for Versym - remove '_ lifetime annotation on SectionHeader slice --- src/elf/symver.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index c4da1009..01f3e76d 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -168,7 +168,7 @@ pub struct VersymSection<'a> { impl<'a> VersymSection<'a> { pub fn parse( bytes: &'a [u8], - shdrs: &'_ [SectionHeader], + shdrs: &[SectionHeader], ctx: container::Ctx, ) -> Result>> { // Get fields needed from optional `symbol version` section. @@ -214,7 +214,7 @@ impl<'a> VersymSection<'a> { self.bytes .pread_with::(offset, self.ctx.le) .ok() - .map(|ElfVersym { vs_val }| Versym { vs_val }) + .map(Versym::from) } } @@ -248,7 +248,7 @@ impl<'a> Iterator for VersymIter<'a> { self.bytes .gread_with::(&mut self.offset, self.ctx.le) .ok() - .map(|ElfVersym { vs_val }| Versym { vs_val }) + .map(Versym::from) .or_else(|| { // self.bytes are not a multiple of ElfVersym. // Adjust offset to continue yielding Nones. @@ -305,6 +305,12 @@ impl Versym { } } +impl From for Versym { + fn from(ElfVersym { vs_val }: ElfVersym) -> Self { + Versym { vs_val } + } +} + /************************ * Version Definition * ************************/ @@ -323,7 +329,7 @@ pub struct VerdefSection<'a> { impl<'a> VerdefSection<'a> { pub fn parse( bytes: &'a [u8], - shdrs: &'_ [SectionHeader], + shdrs: &[SectionHeader], ctx: container::Ctx, ) -> Result>> { // Get fields needed from optional `version definition` section. @@ -602,7 +608,7 @@ impl<'a> VerneedSection<'a> { /// Try to parse the optional [`SHT_GNU_VERNEED`] section. pub fn parse( bytes: &'a [u8], - shdrs: &'_ [SectionHeader], + shdrs: &[SectionHeader], ctx: container::Ctx, ) -> Result>> { // Get fields needed from optional `version needed` section. From 5bc5c52738fccf3ccfe243ad6d7f4d13ce143124 Mon Sep 17 00:00:00 2001 From: Johannes Stoelp Date: Wed, 8 Sep 2021 22:39:08 +0200 Subject: [PATCH 20/20] elf.symver: apply review feedback - remove string tables from VerdefSection / VerneedSection --- src/elf/symver.rs | 66 ++++++----------------------------------------- tests/elf.rs | 23 ++++++++++------- 2 files changed, 21 insertions(+), 68 deletions(-) diff --git a/src/elf/symver.rs b/src/elf/symver.rs index 01f3e76d..0dd55e3d 100644 --- a/src/elf/symver.rs +++ b/src/elf/symver.rs @@ -17,10 +17,10 @@ //! for need_file in verneed.iter() { //! println!( //! "Depend on {:?} with version(s):", -//! verneed.verstr.get_at(need_file.vn_file) +//! binary.dynstrtab.get_at(need_file.vn_file) //! ); //! for need_ver in need_file.iter() { -//! println!("{:?}", verneed.verstr.get_at(need_ver.vna_name)); +//! println!("{:?}", binary.dynstrtab.get_at(need_ver.vna_name)); //! } //! } //! } @@ -40,7 +40,7 @@ //! if let Some(verdef) = &binary.verdef { //! for def in verdef.iter() { //! for (n, aux) in def.iter().enumerate() { -//! let name = verdef.verstr.get_at(aux.vda_name); +//! let name = binary.dynstrtab.get_at(aux.vda_name); //! match n { //! 0 => print!("Name: {:?}", name), //! 1 => print!(" Parent: {:?}", name), @@ -61,8 +61,7 @@ use crate::container; use crate::elf::section_header::{SectionHeader, SHT_GNU_VERDEF, SHT_GNU_VERNEED, SHT_GNU_VERSYM}; -use crate::error::{Error, Result}; -use crate::strtab::Strtab; +use crate::error::Result; use core::iter::FusedIterator; use scroll::Pread; @@ -320,7 +319,6 @@ impl From for Versym { #[derive(Debug)] pub struct VerdefSection<'a> { /// String table used to resolve version strings. - pub verstr: Strtab<'a>, bytes: &'a [u8], count: usize, ctx: container::Ctx, @@ -333,10 +331,9 @@ impl<'a> VerdefSection<'a> { ctx: container::Ctx, ) -> Result>> { // Get fields needed from optional `version definition` section. - let (link_idx, offset, size, count) = + let (offset, size, count) = if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERDEF) { ( - shdr.sh_link as usize, // Encodes the string table. shdr.sh_offset as usize, shdr.sh_size as usize, shdr.sh_info as usize, // Encodes the number of ElfVerdef entries. @@ -345,32 +342,10 @@ impl<'a> VerdefSection<'a> { return Ok(None); }; - // Get string table which is used to resolve version strings. - let verstr = { - // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or_else(|| { - Error::Malformed( - "Section header of string table for SHT_GNU_VERDEF section not found!".into(), - ) - })?; - - Strtab::parse( - bytes, - shdr_link.sh_offset as usize, - shdr_link.sh_size as usize, - 0x0, /* Delimiter */ - )? - }; - // Get a slice of bytes of the `version definition` section content. let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - Ok(Some(VerdefSection { - verstr, - bytes, - count, - ctx, - })) + Ok(Some(VerdefSection { bytes, count, ctx })) } /// Get an iterator over the [`Verdef`] entries. @@ -597,8 +572,6 @@ pub struct Verdaux { /// Auxiliary][Vernaux] entries. #[derive(Debug)] pub struct VerneedSection<'a> { - /// String table used to resolve version strings. - pub verstr: Strtab<'a>, bytes: &'a [u8], count: usize, ctx: container::Ctx, @@ -612,10 +585,9 @@ impl<'a> VerneedSection<'a> { ctx: container::Ctx, ) -> Result>> { // Get fields needed from optional `version needed` section. - let (link_idx, offset, size, count) = + let (offset, size, count) = if let Some(shdr) = shdrs.iter().find(|shdr| shdr.sh_type == SHT_GNU_VERNEED) { ( - shdr.sh_link as usize, // Encodes the string table. shdr.sh_offset as usize, shdr.sh_size as usize, shdr.sh_info as usize, // Encodes the number of ElfVerneed entries. @@ -624,32 +596,10 @@ impl<'a> VerneedSection<'a> { return Ok(None); }; - // Get string table which is used to resolve version strings. - let verstr = { - // Linked section refers to string table. - let shdr_link = shdrs.get(link_idx).ok_or_else(|| { - Error::Malformed( - "Section header of string table for SHT_GNU_VERNEED section not found!".into(), - ) - })?; - - Strtab::parse( - bytes, - shdr_link.sh_offset as usize, - shdr_link.sh_size as usize, - 0x0, /* Delimiter */ - )? - }; - // Get a slice of bytes of the `version needed` section content. let bytes: &'a [u8] = bytes.pread_with(offset, size)?; - Ok(Some(VerneedSection { - verstr, - bytes, - count, - ctx, - })) + Ok(Some(VerneedSection { bytes, count, ctx })) } /// Get an iterator over the [`Verneed`] entries. diff --git a/tests/elf.rs b/tests/elf.rs index a23095c8..5a10ed42 100644 --- a/tests/elf.rs +++ b/tests/elf.rs @@ -1,8 +1,10 @@ use goblin::elf::section_header::SHT_GNU_HASH; -use goblin::elf::sym::Sym; +use goblin::elf::sym::{Sym, Symtab}; +use goblin::elf::symver::{VerdefSection, VerneedSection, VersymSection}; use goblin::elf::Elf; use goblin::elf32::gnu_hash::GnuHash as GnuHash32; use goblin::elf64::gnu_hash::GnuHash as GnuHash64; +use goblin::strtab::Strtab; #[repr(C)] #[repr(align(64))] // Align to cache lines @@ -39,7 +41,6 @@ fn parse_text_section_size_lazy(base: &[u8]) -> Result { use goblin::container::{Container, Ctx}; use goblin::elf::SectionHeader; - use goblin::strtab::Strtab; let ctx = Ctx { le: scroll::Endian::Little, @@ -258,7 +259,7 @@ fn check_symver_expectations( assert!(elf.verneed.is_some()); let verneed = elf.verneed.as_ref().unwrap(); - check_symver_expectations_verneed(verneed, expect_verneed); + check_symver_expectations_verneed(verneed, expect_verneed, &elf.dynstrtab); } if expect_verdef.is_empty() { @@ -269,15 +270,15 @@ fn check_symver_expectations( assert!(elf.verdef.is_some()); let verdef = elf.verdef.as_ref().unwrap(); - check_symver_expectations_verdef(verdef, expect_verdef); + check_symver_expectations_verdef(verdef, expect_verdef, &elf.dynstrtab); } Ok(()) } fn check_symver_expectations_versym( - versym: &goblin::elf::VersymSection<'_>, - dynsyms: &goblin::elf::Symtab<'_>, + versym: &VersymSection<'_>, + dynsyms: &Symtab<'_>, expect_versym: &Vec, ) { // VERSYM section must contain one entry per DYNSYM. @@ -298,11 +299,12 @@ fn check_symver_expectations_versym( } fn check_symver_expectations_verneed( - verneed: &goblin::elf::VerneedSection<'_>, + verneed: &VerneedSection<'_>, expect_verneed: &SymverExpectation, + strtab: &Strtab<'_>, ) { // Resolve version strings. - let verstr = |idx| verneed.verstr.get_at(idx).unwrap(); + let verstr = |idx| strtab.get_at(idx).unwrap(); // ELF file dependencies with version requirements. let need_files: Vec<_> = verneed.iter().collect(); @@ -349,11 +351,12 @@ fn check_symver_expectations_verneed( } fn check_symver_expectations_verdef( - verdef: &goblin::elf::VerdefSection<'_>, + verdef: &VerdefSection<'_>, expect_verdef: &SymverExpectation, + strtab: &Strtab<'_>, ) { // Resolve version strings. - let verstr = |idx| verdef.verstr.get_at(idx).unwrap(); + let verstr = |idx| strtab.get_at(idx).unwrap(); // ELF version definitions. let defined_vers: Vec<_> = verdef.iter().collect();