+ //! # Fast ELF editing library.
+ //!
+ //! ELF is the main format of library and executable files on most
+ //! Unix systems, with the notable exceptions of Apple's various
+ //! platforms.
+ //!
+ //! This library does the same as the existing `patchelf` tool, but
+ //! with a time and space complexity that do not depend on the file
+ //! size.
+ //!
+ //! # Use of CStr in this crate
+ //!
+ //! This library tries to avoid doing things more than once. In
+ //! particular, ELF files comprise many strings represented as a Rust
+ //! `CStr`, i.e. a string ended with a 0 byte. We usually convert
+ //! these to the more efficient `&[u8]` type as soon as
+ //! possible. Moreover, we keep the zero byte itself in the `&[u8]`
+ //! while doing so in order to make our code smaller (and avoid some
+ //! edge cases) when writing the strings.
+ //!
+ //! This has the advantage of speed, but the inconvenience that you
+ //! have to provide the 0 byte in your slices when interacting with
+ //! this crate. See the example below for examples.
+ //!
+ //! # Example
+ //!
+ //! In this example, we modify the binary from the GNU Hello package
+ //! from Ubuntu "Noble Numbat" (24.04). Note that this makes our tests
+ //! GPL 3, since the binary of GNU Hello is included in the test
+ //! binary.
+ //!
+ //! ```
+ //! use std::io::Write;
+ //! use elfedit::*;
+ //! let mut p = tempfile::tempfile().unwrap();
+ //! p.write_all(include_bytes!("hello")).unwrap();
+ //! let mut elf = Elf::open(&p).unwrap();
+ //!
+ //! let new_interp = b"/nix/store/maxa3xhmxggrc5v2vc0c3pjb79hjlkp9-glibc-2.40-66/lib/ld-linux-x86-64.so.2\0";
+ //!
+ //! let new_needed = b"ma.bibliotheque.so.19\0";
+ //! let new_needed2 = b"ma.bibliotheque.so.24\0";
+ //! let runpath = b"a:b:c\0";
+ //! elf.set_runpath(runpath)
+ //! .set_interpreter(new_interp)
+ //! .add_needed(new_needed).unwrap()
+ //! .add_needed(new_needed2).unwrap();
+ //! elf.update().unwrap();
+ //!
+ //! let elf = Elf::open(&p).unwrap();
+ //! let p = elf.parse().unwrap();
+ //! assert_eq!(p.interpreter().unwrap().to_bytes_with_nul(), new_interp);
+ //! assert_eq!(p.runpath().unwrap().unwrap().to_bytes_with_nul(), runpath);
+ //! let needed: Result<Vec<_>, _> = p.needed().map(|x| x.map(|x| x.to_bytes_with_nul())).collect();
+ //! let needed = needed.unwrap();
+ //! assert_eq!(
+ //! needed,
+ //! &[
+ //! &b"libc.so.6\0"[..],
+ //! &b"ma.bibliotheque.so.19\0"[..],
+ //! &b"ma.bibliotheque.so.24\0"[..]
+ //! ]
+ //! );
+ //! ```
+
+ use std::collections::HashMap;
+ use std::ffi::CStr;
+ use thiserror::*;
+
+ #[derive(Debug, Error)]
+ pub enum Error {
+ #[error(transparent)]
+ IO(#[from] std::io::Error),
+ #[error(transparent)]
+ FromBytesWithNul(#[from] std::ffi::FromBytesWithNulError),
+ #[error(transparent)]
+ FromBytesUntilNul(#[from] std::ffi::FromBytesUntilNulError),
+ #[error("Section not found: {name:?}")]
+ SectionNotFound { name: Vec<u8> },
+ }
+
+ #[derive(Debug, Copy, Clone, PartialEq)]
+ struct Bits(u8);
+
+ impl Bits {
+ const B32: Self = Bits(1);
+ const B64: Self = Bits(2);
+ }
+
+ #[derive(Debug, Copy, Clone, PartialEq)]
+ struct Endianness(u8);
+
+ impl Endianness {
+ const LITTLE: Self = Endianness(1);
+ const BIG: Self = Endianness(2);
+ }
+
+ #[allow(dead_code)]
+ #[derive(Debug, Copy, Clone)]
+ struct ABI(u8);
+
+ fn align_next(x: usize, mul: usize) -> usize {
+ (x + mul - 1) & !(mul - 1)
+ }
+
+ #[allow(dead_code)]
+ impl ABI {
+ const SYSTEM_V: Self = ABI(0);
+ const HPUX: Self = ABI(1);
+ const NET_BSD: Self = ABI(2);
+ const LINUX: Self = ABI(3);
+ const GNU_HURD: Self = ABI(4);
+ const SOLARIS: Self = ABI(6);
+ const AIX: Self = ABI(7);
+ const IRIX: Self = ABI(8);
+ const FREE_BSD: Self = ABI(9);
+ const TRU64: Self = ABI(10);
+ const NOVELL_MODESTO: Self = ABI(11);
+ const OPEN_BSD: Self = ABI(12);
+ const OPEN_VMS: Self = ABI(13);
+ const NON_STOP_KERNEL: Self = ABI(14);
+ const AROS: Self = ABI(15);
+ const FENIX_OS: Self = ABI(16);
+ const NUXI_CLOUD_ABI: Self = ABI(17);
+ const OPEN_VOS: Self = ABI(18);
+ }
+
+ #[allow(dead_code)]
+ #[derive(Debug, Copy, Clone)]
+ struct Type(u8);
+
+ #[allow(dead_code)]
+ impl Type {
+ const NONE: Self = Type(0);
+ const REPOSITIONABLE: Self = Type(1);
+ const EXECUTABLE: Self = Type(2);
+ const SHARED_OBJECT: Self = Type(3);
+ const CORE: Self = Type(4);
+ }
+
+ #[derive(Debug, Copy, Clone)]
+ #[repr(C)]
+ struct Id {
+ magic: [u8; 4],
+ class: Bits,
+ data: Endianness,
+ version: u8,
+ osabi: ABI,
+ abi_version: u8,
+ padding: [u8; 7],
+ }
+
+ #[derive(Debug, Copy, Clone)]
+ struct Endian<U>(U);
+
+ trait EndianT: Sized {
+ fn endian(e: Endian<Self>, id: Endianness) -> Self;
+ }
+
+ impl EndianT for u16 {
+ fn endian(e: Endian<Self>, id: Endianness) -> Self {
+ if id == Endianness::LITTLE {
+ u16::from_le(e.0)
+ } else if id == Endianness::BIG {
+ u16::from_be(e.0)
+ } else {
+ panic!()
+ }
+ }
+ }
+
+ impl EndianT for u32 {
+ fn endian(e: Endian<Self>, id: Endianness) -> Self {
+ if id == Endianness::LITTLE {
+ u32::from_le(e.0)
+ } else if id == Endianness::BIG {
+ u32::from_be(e.0)
+ } else {
+ panic!()
+ }
+ }
+ }
+
+ impl EndianT for u64 {
+ fn endian(e: Endian<Self>, id: Endianness) -> Self {
+ if id == Endianness::LITTLE {
+ u64::from_le(e.0)
+ } else if id == Endianness::BIG {
+ u64::from_be(e.0)
+ } else {
+ panic!()
+ }
+ }
+ }
+
+ impl EndianT for Sht {
+ fn endian(e: Endian<Self>, id: Endianness) -> Sht {
+ if id == Endianness::LITTLE {
+ Sht(u32::from_le(e.0 .0))
+ } else if id == Endianness::BIG {
+ Sht(u32::from_be(e.0 .0))
+ } else {
+ panic!()
+ }
+ }
+ }
+
+ impl Endian<u32> {
+ fn from(id: Endianness, u: u32) -> Self {
+ if id == Endianness::LITTLE {
+ Endian(u.to_le())
+ } else if id == Endianness::BIG {
+ Endian(u.to_be())
+ } else {
+ panic!()
+ }
+ }
+ }
+
+ impl Endian<u64> {
+ fn from(id: Endianness, u: u64) -> Self {
+ if id == Endianness::LITTLE {
+ Endian(u.to_le())
+ } else if id == Endianness::BIG {
+ Endian(u.to_be())
+ } else {
+ panic!()
+ }
+ }
+ }
+
+ #[derive(Debug, Copy, Clone)]
+ #[repr(C)]
+ struct Header<Offset> {
+ type_: Endian<u16>,
+ machine: Endian<u16>,
+ version: Endian<u32>,
+ entry: Endian<Offset>,
+ phoff: Endian<Offset>,
+ shoff: Endian<Offset>,
+ flags: Endian<u32>,
+ ehsize: Endian<u16>,
+ phentsize: Endian<u16>,
+ phnum: Endian<u16>,
+ shentsize: Endian<u16>,
+ shnum: Endian<u16>,
+ shstrndx: Endian<u16>,
+ }
+
+ #[derive(Debug, Copy, Clone)]
+ #[repr(C)]
+ struct SectionHeader<Offset> {
+ name: Endian<u32>,
+ type_: Endian<Sht>,
+ flags: Endian<Offset>,
+ addr: Endian<Offset>,
+ offset: Endian<Offset>,
+ size: Endian<Offset>,
+ link: Endian<u32>,
+ info: Endian<u32>,
+ addralign: Endian<Offset>,
+ entsize: Endian<Offset>,
+ }
+
+ #[derive(Debug)]
+ enum SectionHeader_<'a> {
+ B32(&'a SectionHeader<u32>),
+ B64(&'a SectionHeader<u64>),
+ }
+
+ #[derive(Debug)]
+ enum SectionHeaderMut_<'a> {
+ B32(&'a mut SectionHeader<u32>),
+ B64(&'a mut SectionHeader<u64>),
+ }
+
+ #[derive(Debug)]
+ enum ProgramHeaderMut_<'a> {
+ B32(&'a mut ProgramHeader<u32>),
+ B64(&'a mut ProgramHeader<u64>),
+ }
+
+ impl<'a> SectionHeaderMut_<'a> {
+ fn set_size(&mut self, h: Endianness, u: usize) {
+ match self {
+ SectionHeaderMut_::B32(s) => s.size = <Endian<u32>>::from(h, u as u32),
+ SectionHeaderMut_::B64(s) => s.size = <Endian<u64>>::from(h, u as u64),
+ }
+ }
+ fn set_offset(&mut self, h: Endianness, u: usize) {
+ match self {
+ SectionHeaderMut_::B32(s) => s.offset = <Endian<u32>>::from(h, u as u32),
+ SectionHeaderMut_::B64(s) => s.offset = <Endian<u64>>::from(h, u as u64),
+ }
+ }
+ }
+
+ impl<'a> ProgramHeaderMut_<'a> {
+ fn offset(&self, h: Endianness) -> u64 {
+ match self {
+ ProgramHeaderMut_::B32(s) => EndianT::endian(s.offset, h) as u64,
+ ProgramHeaderMut_::B64(s) => EndianT::endian(s.offset, h),
+ }
+ }
+ fn set_filesz(&mut self, h: Endianness, u: usize) {
+ match self {
+ ProgramHeaderMut_::B32(s) => s.filesz = <Endian<u32>>::from(h, u as u32),
+ ProgramHeaderMut_::B64(s) => s.filesz = <Endian<u64>>::from(h, u as u64),
+ }
+ }
+ fn set_memsz(&mut self, h: Endianness, u: usize) {
+ match self {
+ ProgramHeaderMut_::B32(s) => s.memsz = <Endian<u32>>::from(h, u as u32),
+ ProgramHeaderMut_::B64(s) => s.memsz = <Endian<u64>>::from(h, u as u64),
+ }
+ }
+ fn set_offset(&mut self, h: Endianness, u: usize) {
+ match self {
+ ProgramHeaderMut_::B32(s) => s.offset = <Endian<u32>>::from(h, u as u32),
+ ProgramHeaderMut_::B64(s) => s.offset = <Endian<u64>>::from(h, u as u64),
+ }
+ }
+ }
+
+ impl<'a> SectionHeader_<'a> {
+ fn offset(&self, h: Endianness) -> u64 {
+ match self {
+ SectionHeader_::B32(s) => EndianT::endian(s.offset, h) as u64,
+ SectionHeader_::B64(s) => EndianT::endian(s.offset, h),
+ }
+ }
+ fn size(&self, h: Endianness) -> u64 {
+ match self {
+ SectionHeader_::B32(s) => EndianT::endian(s.size, h) as u64,
+ SectionHeader_::B64(s) => EndianT::endian(s.size, h),
+ }
+ }
+
+ fn name(&self, id: Endianness) -> u32 {
+ match self {
+ SectionHeader_::B32(h) => EndianT::endian(h.name, id),
+ SectionHeader_::B64(h) => EndianT::endian(h.name, id),
+ }
+ }
+
+ fn type_(&self, id: Endianness) -> Sht {
+ match self {
+ SectionHeader_::B32(h) => EndianT::endian(h.type_, id),
+ SectionHeader_::B64(h) => EndianT::endian(h.type_, id),
+ }
+ }
+ }
+
+ #[derive(Debug, Copy, Clone, PartialEq)]
+ struct Pht(u32);
+
+ #[allow(dead_code)]
+ impl Pht {
+ const NULL: Self = Pht(0);
+ const LOAD: Self = Pht(1);
+ const DYNAMIC: Self = Pht(2);
+ const INTERP: Self = Pht(3);
+ const NOTE: Self = Pht(4);
+ const SHLIB: Self = Pht(5);
+ const PHDR: Self = Pht(6);
+ const TLS: Self = Pht(7);
+ const LOOS: Self = Pht(0x60000000);
+ const HIOS: Self = Pht(0x6fffffff);
+ const LOPROC: Self = Pht(0x70000000);
+ const HIPROC: Self = Pht(0x7fffffff);
+ }
+
+ #[derive(Debug, Copy, Clone)]
+ #[repr(C)]
+ struct ProgramHeader<Offset: Flags> {
+ type_: Endian<Pht>,
+ flags64: Endian<Offset::F64>,
+ offset: Endian<Offset>,
+ vaddr: Endian<Offset>,
+ paddr: Endian<Offset>,
+ filesz: Endian<Offset>,
+ memsz: Endian<Offset>,
+ flags32: Endian<Offset::F32>,
+ align: Endian<Offset>,
+ }
+
+ /// An open ELF file.
+ ///
+ /// Most operations on this type follow a "builder pattern", meaning
+ /// that they don't actually update the underlying file until the
+ /// `.update()` method is called.
+ pub struct Elf<'a> {
+ map: memmap::MmapMut,
+ f: Option<&'a std::fs::File>,
+ new_needed: Option<Vec<Vec<u8>>>,
+ new_runpath: Vec<u8>,
+ new_interp: Vec<u8>,
+ }
+
+ impl<'f> Elf<'f> {
+ /// Create a builder around the provided file. Maps `file` in memory.
+ pub fn open(file: &'f std::fs::File) -> Result<Self, std::io::Error> {
+ let map = unsafe { memmap::MmapOptions::new().map_mut(&file).unwrap() };
+ Ok(Elf {
+ map,
+ f: Some(file),
+ new_needed: None,
+ new_runpath: Vec::new(),
+ new_interp: Vec::new(),
+ })
+ }
+
+ fn section<'a>(&'a self, n: usize) -> SectionHeader_<'a> {
+ let h = self.header();
+ let id = self.id().data;
+ assert!(n < h.shnum(id) as usize);
+ let ph = h.shoff(id) as usize + n * h.shentsize(id) as usize;
+ match h {
+ Header_::B32(_) => {
+ assert!(ph + std::mem::size_of::<SectionHeader<u32>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<SectionHeader<u32>>(), 0);
+ SectionHeader_::B32(unsafe {
+ &*((self.map.as_ptr()).add(ph) as *const u8 as *const SectionHeader<u32>)
+ })
+ }
+ Header_::B64(_) => {
+ assert!(ph + std::mem::size_of::<SectionHeader<u64>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<SectionHeader<u64>>(), 0);
+ SectionHeader_::B64(unsafe {
+ &*((self.map.as_ptr()).add(ph) as *const u8 as *const SectionHeader<u64>)
+ })
+ }
+ }
+ }
+
+ fn sections<U>(&self) -> &[SectionHeader<U>] {
+ let h = self.header();
+ let id = self.id().data;
+ if self.id().class == Bits::B64 {
+ assert_eq!(std::mem::size_of::<U>(), 8)
+ } else {
+ assert_eq!(std::mem::size_of::<U>(), 4)
+ }
+ let ph = h.shoff(id) as usize;
+ let shnum = h.shnum(id) as usize;
+
+ assert!(ph + std::mem::size_of::<SectionHeader<U>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<SectionHeader<U>>(), 0);
+ unsafe {
+ std::slice::from_raw_parts(
+ self.map.as_ptr().add(ph) as *const u8 as *const SectionHeader<U>,
+ shnum,
+ )
+ }
+ }
+
+ fn program_mut<'a>(&'a mut self, n: usize) -> ProgramHeaderMut_<'a> {
+ let h = self.header();
+ let id = self.id().data;
+ assert!(n < h.phnum(id) as usize);
+ let ph = h.phoff(id) as usize + n * h.phentsize(id) as usize;
+ match h {
+ Header_::B32(_) => {
+ assert!(ph + std::mem::size_of::<ProgramHeader<u32>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<ProgramHeader<u32>>(), 0);
+ ProgramHeaderMut_::B32(unsafe {
+ &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut ProgramHeader<u32>)
+ })
+ }
+ Header_::B64(_) => {
+ assert!(ph + std::mem::size_of::<ProgramHeader<u64>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<ProgramHeader<u32>>(), 0);
+ ProgramHeaderMut_::B64(unsafe {
+ &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut ProgramHeader<u64>)
+ })
+ }
+ }
+ }
+
+ fn section_mut<'a>(&'a mut self, n: usize) -> SectionHeaderMut_<'a> {
+ let h = self.header();
+ let id = self.id().data;
+ assert!(n < h.shnum(id) as usize);
+ let ph = h.shoff(id) as usize + n * h.shentsize(id) as usize;
+ match h {
+ Header_::B32(_) => {
+ assert!(ph + std::mem::size_of::<SectionHeader<u32>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<SectionHeader<u32>>(), 0);
+ SectionHeaderMut_::B32(unsafe {
+ &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut SectionHeader<u32>)
+ })
+ }
+ Header_::B64(_) => {
+ assert!(ph + std::mem::size_of::<SectionHeader<u64>>() <= self.map.len());
+ assert_eq!(ph % std::mem::align_of::<SectionHeader<u64>>(), 0);
+ SectionHeaderMut_::B64(unsafe {
+ &mut *((self.map.as_mut_ptr()).add(ph) as *mut u8 as *mut SectionHeader<u64>)
+ })
+ }
+ }
+ }
+
+ fn names<'a>(&'a self, id: Endianness, header: &Header_) -> Names<'a> {
+ let s = self.section(header.shstrndx(id) as usize);
+ let offset = s.offset(id) as usize;
+ let size = s.size(id) as usize;
+ Names(&self.map[offset..offset + size])
+ }
+
+ fn id(&self) -> &Id {
+ assert!(self.map.len() >= std::mem::size_of::<Header<u32>>());
+ unsafe { &*(self.map.as_ptr() as *const u8 as *const Id) }
+ }
+
+ fn header(&self) -> Header_ {
+ let id = self.id();
+ // The aligment is guaranteed here by design of the
+ // ELF file. The size isn't, however.
+ match id.class {
+ Bits::B32 => {
+ assert!(
+ std::mem::size_of::<Id>() + std::mem::size_of::<Header<u32>>()
+ <= self.map.len()
+ );
+
+ let header_: &Header<u32> = unsafe {
+ &*(self.map.as_ptr().add(std::mem::size_of::<Id>()) as *const u8
+ as *const Header<u32>)
+ };
+ Header_::B32(header_)
+ }
+ Bits::B64 => {
+ assert!(
+ std::mem::size_of::<Id>() + std::mem::size_of::<Header<u64>>()
+ <= self.map.len()
+ );
+ let header_: &Header<u64> = unsafe {
+ &*(self.map.as_ptr().add(std::mem::size_of::<Id>()) as *const u8
+ as *const Header<u64>)
+ };
+ Header_::B64(header_)
+ }
+ _ => panic!(),
+ }
+ }
+
+ fn header_mut(&mut self) -> HeaderMut_ {
+ let id = self.id();
+ match id.class {
+ Bits::B32 => {
+ assert!(
+ std::mem::size_of::<Id>() + std::mem::size_of::<Header<u32>>()
+ <= self.map.len()
+ );
+ let header_: &mut Header<u32> = unsafe {
+ &mut *(self.map.as_mut_ptr().add(std::mem::size_of::<Id>()) as *mut u8
+ as *mut Header<u32>)
+ };
+ HeaderMut_::B32(header_)
+ }
+ Bits::B64 => {
+ assert!(
+ std::mem::size_of::<Id>() + std::mem::size_of::<Header<u64>>()
+ <= self.map.len()
+ );
+ let header_: &mut Header<u64> = unsafe {
+ &mut *(self.map.as_mut_ptr().add(std::mem::size_of::<Id>()) as *mut u8
+ as *mut Header<u64>)
+ };
+ HeaderMut_::B64(header_)
+ }
+ _ => panic!(),
+ }
+ }
+
+ /// Paress the sections relevant for linking (.interp, .dynstr, .dynamic).
+ ///
+ /// The sections are stored in an unordered array in the ELF file,
+ /// so this avoids going through the array for each query.
+ pub fn parse(&self) -> Result<Parsed, Error> {
+ let id = self.id();
+ let header = self.header();
+ let names = self.names(id.data, &header);
+ let mut dynstr = Names(&[][..]);
+ let mut dynamic = Dynamic::B32(&[]);
+ let mut interp = &[][..];
+ for i in 0..header.shnum(id.data) {
+ let s = self.section(i as usize);
+ let name = names.name(s.name(id.data) as usize)?;
+ if name.to_bytes() == b".interp" {
+ let offset = s.offset(id.data) as usize;
+ let size = s.size(id.data) as usize;
+ interp = &self.map[offset..offset + size];
+ }
+
+ if name.to_bytes() == b".dynstr" {
+ let offset = s.offset(id.data) as usize;
+ let size = s.size(id.data) as usize;
+ dynstr = Names(&self.map[offset..offset + size]);
+ }
+
+ if s.type_(id.data) == Sht::DYNAMIC {
+ let offset = s.offset(id.data) as usize;
+ let size = s.size(id.data) as usize;
+ dynamic = match id.class {
+ Bits::B32 => {
+ assert_eq!(offset % std::mem::align_of::<Elf32Dyn>(), 0);
+ assert!(offset + size <= self.map.len());
+ Dynamic::B32(unsafe {
+ std::slice::from_raw_parts(
+ self.map.as_ptr().add(offset) as *const u8 as *const Elf32Dyn,
+ size / std::mem::size_of::<Elf32Dyn>(),
+ )
+ })
+ },
+ Bits::B64 => {Dynamic::B64(unsafe {
+ assert_eq!(offset % std::mem::align_of::<Elf64Dyn>(), 0);
+ assert!(offset + size <= self.map.len());
+ std::slice::from_raw_parts(
+ self.map.as_ptr().add(offset) as *const u8 as *const Elf64Dyn,
+ size / std::mem::size_of::<Elf64Dyn>(),
+ )
+ })
+ },
+ _ => panic!(),
+ };
+ }
+ }
+
+ Ok(Parsed {
+ dynstr,
+ dynamic,
+ interp,
+ })
+ }
+
+ fn find_section<U>(&self, name: &[u8]) -> Result<Option<usize>, Error> {
+ let id = self.id().data;
+ let header = self.header();
+ let names = self.names(id, &header);
+ for (i, sec) in self.sections::<U>().into_iter().enumerate() {
+ if names
+ .name(EndianT::endian(sec.name, id) as usize)?
+ .to_bytes()
+ == name
+ {
+ return Ok(Some(i));
+ }
+ }
+ Ok(None)
+ }
+
+ fn find_next_off<U: EndianT + Copy + Into<u64>>(&self, off: u64) -> u64 {
+ let id = self.id().data;
+ let header = self.header();
+ let mut next_off = header.shoff(id);
+ for sec in self.sections::<U>() {
+ let s: u64 = EndianT::endian(sec.offset, id).into();
+ if s > off && s < next_off {
+ next_off = s;
+ }
+ }
+ next_off
+ }
+
+ /// Updates a section. This is a low-level function and doesn't
+ /// normally need to be called.
+ pub fn update_section(&mut self, name: &[u8], new: &[u8]) -> Result<usize, Error> {
+ let id = self.id().data;
+ let class = self.id().class;
+
+ let interp_i = if class == Bits::B64 {
+ self.find_section::<u64>(name)?
+ } else {
+ self.find_section::<u64>(name)?
+ };
+ let Some(interp_i) = interp_i else {
+ return Err(Error::SectionNotFound {
+ name: name.to_vec(),
+ });
+ };
+
+ let off = self.section(interp_i as usize).offset(id);
+ let next_off = if class == Bits::B64 {
+ self.find_next_off::<u64>(off) as u64
+ } else {
+ self.find_next_off::<u32>(off) as u64
+ };
+
+ let new_len = new.len();
+
+ if new_len <= (next_off - off) as usize {
+ // Easy
+ let off = self.section(interp_i as usize).offset(id);
+ assert!(off as usize + new.len() <= self.map.len());
+ unsafe {
+ self.map
+ .as_mut_ptr()
+ .add(off as usize)
+ .write_bytes(0, (next_off - off) as usize);
+ std::ptr::copy(
+ new.as_ptr() as *const i8,
+ self.map.as_mut_ptr().add(off as usize) as *mut i8,
+ new.len(),
+ );
+ }
+ self.section_mut(interp_i as usize).set_size(id, new_len);
+ Ok(0)
+ } else {
+ Ok(self.update_section_allocate(id, new, interp_i, off as u64))
+ }
+ }
+
+ fn update_section_allocate(
+ &mut self,
+ id: Endianness,
+ new: &[u8],
+ interp_i: usize,
+ off: u64,
+ ) -> usize {
+ let new_len = new.len();
+ let h = self.header();
+ let shnum = h.shnum(id);
+ let class = self.id().class;
+
+ // First find a large enough deleted section = gap
+ // between sections.
+
+ let mut sections: Vec<_> = if class == Bits::B32 {
+ self.sections::<u32>()
+ .iter()
+ .map(|s| {
+ (
+ EndianT::endian(s.offset, id) as usize,
+ EndianT::endian(s.size, id) as usize,
+ )
+ })
+ .collect()
+ } else {
+ self.sections::<u64>()
+ .iter()
+ .map(|s| {
+ (
+ EndianT::endian(s.offset, id) as usize,
+ EndianT::endian(s.size, id) as usize,
+ )
+ })
+ .collect()
+ };
+ sections.sort();
+
+ let extra_size = new_len;
+ let extra_size = match class {
+ Bits::B32 => (extra_size + 3) & !3,
+ Bits::B64 => (extra_size + 7) & !7,
+ _ => panic!(),
+ };
+
+ let mut end = 0;
+ let mut new_offset = h.shoff(id) as usize;
+ let mut gap = usize::MAX;
+
+ for (off, len) in sections.iter() {
+ if end > 0 && *off >= end + extra_size && *off - (end + extra_size) < gap {
+ new_offset = end;
+ gap = *off - (end + extra_size)
+ }
+ end = align_next(*off + len, 8)
+ }
+
+ let shentsize = h.shentsize(id);
+ let section_table_len = shnum as usize * shentsize as usize;
+ let shoff = h.shoff(id) as usize;
+ let total = shoff + section_table_len;
+
+ assert!(extra_size + total <= self.map.len());
+
+ if new_offset == shoff {
+ // No space was found.
+ assert!(shoff as usize + extra_size + section_table_len <= self.map.len());
+ unsafe {
+ // Moving just the section header table
+ std::ptr::copy(
+ self.map.as_ptr().add(shoff as usize),
+ self.map.as_mut_ptr().add(shoff as usize + extra_size),
+ section_table_len,
+ );
+ self.header_mut().set_shoff(id, shoff as usize + extra_size);
+ }
+ }
+
+ assert!(new_offset as usize + new.len() <= self.map.len());
+ unsafe {
+ // Write the new value
+ self.map
+ .as_mut_ptr()
+ .add(new_offset as usize)
+ .write_bytes(0, extra_size);
+ std::ptr::copy(
+ new.as_ptr() as *const i8,
+ self.map.as_mut_ptr().add(new_offset as usize) as *mut i8,
+ new.len(),
+ );
+ }
+
+ // Update the section
+ let mut s = self.section_mut(interp_i as usize);
+ s.set_offset(id, new_offset);
+ s.set_size(id, new_len);
+
+ // Do the same in the program header table
+ let phnum = self.header().phnum(id);
+ for i in 0..phnum {
+ let mut p = self.program_mut(i as usize);
+ let p_off = p.offset(id);
+ if p_off == off {
+ // the new offset is the old shoff
+ p.set_offset(id, new_offset as usize);
+ p.set_filesz(id, new_len);
+ p.set_memsz(id, new_len);
+ }
+ }
+ extra_size
+ }
+
+ /// Sets the runpath. Note that this doesn't modify the underlying
+ /// file yet, and can be changed at any later time.
+ pub fn set_runpath(&mut self, runpath: &[u8]) -> &mut Self {
+ self.new_runpath.clear();
+ self.new_runpath.extend(runpath);
+ self
+ }
+
+ /// Add a needed library. This is useful for example for dynamic
+ /// libraries loaded using `dlopen`.
+ pub fn add_needed(&mut self, needed: &[u8]) -> Result<&mut Self, Error> {
+ if self.new_needed.is_none() {
+ let parsed = self.parse()?;
+ let owned = parsed.current_needed_owned()?;
+ self.new_needed = Some(owned)
+ }
+ if let Some(ref mut n) = self.new_needed {
+ if n.iter().all(|x| x != needed) {
+ n.push(needed.to_vec().into());
+ }
+ }
+ Ok(self)
+ }
+
+ /// Remove multiple needed libraries in a single pass. This
+ /// function works like `Vec::retain`: the `retain` function is
+ /// passed nedeed library names in order, and these items are kept
+ /// if and only if `retain` returns `true`.
+ pub fn retain_needed<F: FnMut(&[u8]) -> bool>(
+ &mut self,
+ mut retain: F,
+ ) -> Result<&mut Self, Error> {
+ if self.new_needed.is_none() {
+ let parsed = self.parse()?;
+ let owned = parsed.current_needed_owned()?;
+ self.new_needed = Some(owned);
+ }
+ if let Some(ref mut n) = self.new_needed {
+ n.retain(|x| retain(&*x));
+ }
+ Ok(self)
+ }
+
+ fn update_sections(
+ &mut self,
+ dynstr: &mut Vec<u8>,
+ dynamic: &mut OwnedDynamic,
+ ) -> Result<(), Error> {
+ // Start by saving the current runpath
+ let parsed = self.parse()?;
+ let runpath = parsed.runpath()?.map(|x| x.to_bytes_with_nul().to_vec());
+ remove_trailing_needed_runpath(dynstr, dynamic);
+ if self.new_runpath.is_empty() {
+ if let Some(runpath) = runpath {
+ set_runpath(dynstr, dynamic, &runpath)
+ }
+ } else {
+ set_runpath(dynstr, dynamic, &self.new_runpath)
+ }
+
+ if let Some(ref mut needed) = self.new_needed {
+ retain_needed(dynstr, dynamic, needed);
+ }
+ Ok(())
+ }
+
+ pub fn update(mut self) -> Result<(), Error> {
+ let p = self.parse()?;
+ let mut dynstr_owned = p.dynstr.0.to_vec();
+ let mut dynamic_owned = p.dynamic.to_owned();
+
+ self.update_sections(&mut dynstr_owned, &mut dynamic_owned)?;
+ let extra_room = ((self.new_interp.len() + 7) & !7)
+ + dynstr_owned.len()
+ + dynamic_owned.as_bytes().len();
+ let f = self.f.take().unwrap();
+
+ let new_interp = std::mem::replace(&mut self.new_interp, Vec::new());
+ let new_runpath = std::mem::replace(&mut self.new_runpath, Vec::new());
+ let new_needed = std::mem::replace(&mut self.new_needed, None);
+
+ std::mem::drop(self);
+
+ let current_len = f.metadata()?.len();
+ f.set_len(current_len + extra_room as u64)?;
+
+ let mut elf = Elf::open(f)?;
+
+ let mut extra = 0;
+ if !new_interp.is_empty() {
+ extra += elf.update_section(b".interp", &new_interp).unwrap()
+ }
+ if !new_runpath.is_empty() {
+ set_runpath(&mut dynstr_owned, &mut dynamic_owned, &new_runpath)
+ }
+
+ if new_needed.is_some() || !new_runpath.is_empty() {
+ extra += elf.update_section(b".dynstr", &dynstr_owned).unwrap()
+ + elf
+ .update_section(b".dynamic", dynamic_owned.as_bytes())
+ .unwrap();
+ }
+ f.set_len(current_len + extra as u64)?;
+ Ok(())
+ }
+
+ pub fn set_interpreter(&mut self, new: &[u8]) -> &mut Self {
+ self.new_interp = new.to_vec();
+ self
+ }
+ }
+
+ /// An index to some of the sections of the file.
+ ///
+ /// This is used to avoid going through the list of sections for each
+ /// request. There are rarely many sections, but since the underlying
+ /// ELF file is memory-mapped, merely loading the list could incur
+ /// unnecessary IO.
+ #[derive(Debug)]
+ pub struct Parsed<'a> {
+ dynamic: Dynamic<'a>,
+ dynstr: Names<'a>,
+ interp: &'a [u8],
+ }
+
+ #[derive(Debug, Clone)]
+ enum OwnedDynamic {
+ B32(Vec<Elf32Dyn>),
+ B64(Vec<Elf64Dyn>),
+ }
+
+ impl OwnedDynamic {
+ fn as_bytes(&self) -> &[u8] {
+ // The `unsafe` here are always satisfied since the inputs to
+ // `from_raw_parts` are taken from valid `Vec`s.
+ match self {
+ OwnedDynamic::B32(x) => unsafe {
+ std::slice::from_raw_parts(
+ x.as_ptr() as *const u8,
+ x.len() * std::mem::size_of::<Elf32Dyn>(),
+ )
+ },
+ OwnedDynamic::B64(x) => unsafe {
+ std::slice::from_raw_parts(
+ x.as_ptr() as *const u8,
+ x.len() * std::mem::size_of::<Elf64Dyn>(),
+ )
+ },
+ }
+ }
+ }
+
+ impl<'a> Dynamic<'a> {
+ fn to_owned(&self) -> OwnedDynamic {
+ match self {
+ Dynamic::B32(x) => OwnedDynamic::B32(x.to_vec()),
+ Dynamic::B64(x) => OwnedDynamic::B64(x.to_vec()),
+ }
+ }
+ }
+
+ fn remove_trailing_needed_runpath_<F: Flags>(dynstr: &mut Vec<u8>, dynamic: &mut Vec<F::Dyn>) {
+ // Put the tags last
+ let mut off: Vec<F::Dyn> = dynamic
+ .iter()
+ .filter_map(|x| {
+ if F::tag(*x) == F::dt_needed() || F::tag(*x) == F::dt_runpath() {
+ Some(*x)
+ } else {
+ None
+ }
+ })
+ .collect();
+ off.sort_by_key(|x| F::d_un(*x));
+ let mut removed = std::collections::HashSet::new();
+ while let Some(p) = off.pop() {
+ let c = CStr::from_bytes_until_nul(&dynstr[F::d_un(p) as usize..]).unwrap();
+ let len = c.count_bytes() + 1;
+ if F::d_un(p) as usize + len >= dynstr.len() {
+ dynstr.truncate(F::d_un(p) as usize);
+ removed.insert(p);
+ } else {
+ off.push(p);
+ break;
+ }
+ }
+
+ dynamic.retain(|x| !removed.contains(x))
+ }
+
+ fn remove_trailing_needed_runpath(dynstr: &mut Vec<u8>, dynamic: &mut OwnedDynamic) {
+ match dynamic {
+ OwnedDynamic::B32(b) => remove_trailing_needed_runpath_::<u32>(dynstr, b),
+ OwnedDynamic::B64(b) => remove_trailing_needed_runpath_::<u64>(dynstr, b),
+ }
+ }
+
+ fn set_runpath_<F: Flags>(dynstr: &mut Vec<u8>, dynamic: &mut Vec<F::Dyn>, rpath: &[u8]) {
+ let mut n = 0;
+ for (i, x) in dynamic.iter_mut().enumerate() {
+ if F::tag(*x) == F::dt_runpath() {
+ // Test for rpath size
+ let current = &mut dynstr[F::d_un(*x)..];
+ let pos = current.iter().position(|x| *x == 0).unwrap();
+ if pos + 1 >= rpath.len() {
+ // If the current CStr representing the rpath is at least
+ // as long as the new rpath, replace
+ let current = &mut current[..rpath.len()];
+ current.clone_from_slice(rpath);
+ } else {
+ // Else, insert at the end.
+ let offset = dynstr.len();
+ dynstr.extend(rpath);
+ F::set_d_un(x, offset);
+ }
+ return;
+ } else if F::tag(*x) != F::tag_nul() {
+ n = i
+ }
+ }
+ let offset = dynstr.len();
+ dynstr.extend(rpath);
+ dynamic.insert(n, F::dyn_(F::dt_runpath(), offset))
+ }
+
+ fn retain_needed(dynstr: &mut Vec<u8>, dynamic: &mut OwnedDynamic, needed: &mut Vec<Vec<u8>>) {
+ match dynamic {
+ OwnedDynamic::B32(b) => retain_needed_::<u32>(dynstr, b, needed),
+ OwnedDynamic::B64(b) => retain_needed_::<u64>(dynstr, b, needed),
+ }
+ }
+
+ fn retain_needed_<F: Flags>(dynstr: &mut Vec<u8>, dynamic: &mut Vec<F::Dyn>, needed: &[Vec<u8>]) {
+ // Try to reuse existing strings from needed libs.
+ let mut offsets = HashMap::new();
+ for d in dynamic.iter() {
+ if F::tag(*d) == F::dt_needed() {
+ let off = F::d_un(*d);
+ if off < dynstr.len() {
+ if let Ok(c) = CStr::from_bytes_with_nul(&dynstr[off..]) {
+ offsets.insert(c.to_bytes_with_nul(), off);
+ }
+ }
+ }
+ }
+
+ let mut new_dynamic = Vec::with_capacity(dynamic.len() + needed.len());
+ let mut needed_it = needed.iter();
+ let mut extra_dynstr = Vec::with_capacity(needed.len() * 20);
+ for mut d in dynamic.drain(..) {
+ if F::tag(d) == F::tag_nul() {
+ break;
+ } else if F::tag(d) != F::dt_needed() {
+ new_dynamic.push(d);
+ } else if let Some(needed) = needed_it.next() {
+ if let Some(off) = offsets.get(needed.as_slice()) {
+ F::set_d_un(&mut d, *off);
+ new_dynamic.push(d)
+ } else {
+ F::set_d_un(&mut d, dynstr.len() + extra_dynstr.len());
+ extra_dynstr.extend(needed.as_slice());
+ new_dynamic.push(d)
+ }
+ }
+ }
+
+ for needed in needed_it {
+ if let Some(off) = offsets.get(needed.as_slice()) {
+ new_dynamic.push(F::dyn_(F::dt_needed(), *off))
+ } else {
+ new_dynamic.push(F::dyn_(F::dt_needed(), dynstr.len() + extra_dynstr.len()));
+ extra_dynstr.extend(needed.as_slice());
+ }
+ }
+
+ dynstr.extend(&extra_dynstr);
+ new_dynamic.push(F::dyn_(F::tag_nul(), 0));
+ if new_dynamic.len() % 1 == 1 {
+ // Make the length even. I couldn't find it in the spec, but
+ // ld seems to do that.
+ new_dynamic.push(F::dyn_(F::tag_nul(), 0));
+ }
+ *dynamic = new_dynamic;
+ }
+
+ fn set_runpath(dynstr: &mut Vec<u8>, dynamic: &mut OwnedDynamic, rpath: &[u8]) {
+ match dynamic {
+ OwnedDynamic::B32(b) => set_runpath_::<u32>(dynstr, b, rpath),
+ OwnedDynamic::B64(b) => set_runpath_::<u64>(dynstr, b, rpath),
+ }
+ }
+
+ enum Either<I, A: Iterator<Item = I>, B: Iterator<Item = I>> {
+ A(A),
+ B(B),
+ }
+
+ impl<I, A: Iterator<Item = I>, B: Iterator<Item = I>> Iterator for Either<I, A, B> {
+ type Item = I;
+ fn next(&mut self) -> Option<I> {
+ match self {
+ Either::A(a) => a.next(),
+ Either::B(b) => b.next(),
+ }
+ }
+ }
+
+ impl<'a> Parsed<'a> {
+ /// Return the runpath of the ELF file.
+ ///
+ /// Note that we actually mean "runpath" and not "rpath", which
+ /// was used in the past with a more complex and less flexible
+ /// semantics. This crate doesn't handle the rpath field at the
+ /// moment. More information <a
+ /// href="https://en.wikipedia.org/wiki/Rpath">on Wikipedia</a>.
+ ///
+ /// The error case of this function comes from invalid ELF file.
+ pub fn runpath(&self) -> Result<Option<&'a CStr>, Error> {
+ let mut rpath = None;
+ match self.dynamic {
+ Dynamic::B64(dyn_) => {
+ for d in dyn_ {
+ if d.tag == <u64 as Flags>::dt_runpath() {
+ rpath = Some(self.dynstr.name(d.d_un as usize)?);
+ }
+ }
+ }
+ Dynamic::B32(dyn_) => {
+ for d in dyn_ {
+ if d.tag == <u32 as Flags>::dt_runpath() {
+ rpath = Some(self.dynstr.name(d.d_un as usize)?);
+ }
+ }
+ }
+ }
+ Ok(rpath)
+ }
+
+ /// Iterator on the needed libraries.
+ ///
+ /// When launching a binary, the "interpreter" will typically
+ /// resolve these from the runpath and load them in memory before
+ /// start.
+ ///
+ /// The error case of this function comes from invalid ELF file.
+ pub fn needed(&self) -> impl Iterator<Item = Result<&'a CStr, Error>> {
+ match self.dynamic {
+ Dynamic::B64(dyn_) => Either::A(dyn_.iter().filter_map(|d| {
+ if d.tag == <u64 as Flags>::dt_needed() {
+ Some(self.dynstr.name(d.d_un as usize))
+ } else {
+ None
+ }
+ })),
+ Dynamic::B32(dyn_) => Either::B(dyn_.iter().filter_map(|d| {
+ if d.tag == <u32 as Flags>::dt_needed() {
+ Some(self.dynstr.name(d.d_un as usize))
+ } else {
+ None
+ }
+ })),
+ }
+ }
+
+ fn current_needed_owned(&self) -> Result<Vec<Vec<u8>>, Error> {
+ self.needed()
+ .map(|x| x.map(|x| x.to_bytes_with_nul().to_vec()))
+ .collect()
+ }
+
+ /// Intepreter of the ELF file.
+ ///
+ /// The interpreter (often found at `/lib/ld.so`) is the program
+ /// that resolves the dynamic libraries "needed" by the ELF, using
+ /// the runpath variable.
+ ///
+ /// The error case of this function comes from invalid ELF file.
+ pub fn interpreter(&self) -> Result<&'a CStr, Error> {
+ Ok(CStr::from_bytes_until_nul(self.interp).unwrap())
+ }
+ }
+
+ #[derive(Debug)]
+ enum Dynamic<'a> {
+ B32(&'a [Elf32Dyn]),
+ B64(&'a [Elf64Dyn]),
+ }
+
+ impl<'a> Iterator for Dynamic<'a> {
+ type Item = Elf64Dyn;
+ fn next(&mut self) -> Option<Self::Item> {
+ match self {
+ Dynamic::B32(x) => {
+ if x.is_empty() {
+ None
+ } else {
+ let (a, b) = x.split_at(1);
+ *x = b;
+ let a = a[0];
+ Some(Elf64Dyn {
+ tag: DynTag64(a.tag.0 as u64),
+ d_un: a.d_un as u64,
+ })
+ }
+ }
+ Dynamic::B64(x) => {
+ if x.is_empty() {
+ None
+ } else {
+ let (a, b) = x.split_at(1);
+ *x = b;
+ Some(a[0])
+ }
+ }
+ }
+ }
+ }
+
+ #[derive(Debug)]
+ struct Names<'a>(&'a [u8]);
+
+ impl<'a> Names<'a> {
+ fn name(&self, n: usize) -> Result<&'a CStr, Error> {
+ Ok(CStr::from_bytes_until_nul(&self.0[n..])?)
+ }
+ }
+
+ trait Flags {
+ type F32: std::fmt::Debug;
+ type F64: std::fmt::Debug;
+ type DynTag: std::fmt::Debug + std::convert::From<u64> + PartialEq;
+ type Dyn: Copy + std::fmt::Debug + PartialEq + Eq + std::hash::Hash;
+
+ fn dyn_(tag: Self::DynTag, d_un: usize) -> Self::Dyn;
+
+ fn d_un(_: Self::Dyn) -> usize;
+ fn set_d_un(_: &mut Self::Dyn, _: usize);
+ fn tag(_: Self::Dyn) -> Self::DynTag;
+ fn tag_nul() -> Self::DynTag;
+
+ fn dt_needed() -> Self::DynTag {
+ Self::DynTag::from(1)
+ }
+
+ fn dt_runpath() -> Self::DynTag {
+ Self::DynTag::from(29)
+ }
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ struct DynTag32(u32);
+
+ impl std::convert::From<u64> for DynTag32 {
+ fn from(e: u64) -> Self {
+ Self(e as u32)
+ }
+ }
+
+ impl std::convert::From<u64> for DynTag64 {
+ fn from(e: u64) -> Self {
+ Self(e)
+ }
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ struct DynTag64(u64);
+
+ impl Flags for u32 {
+ type F32 = u32;
+ type F64 = ();
+ type DynTag = DynTag32;
+ type Dyn = Elf32Dyn;
+
+ fn dyn_(tag: Self::DynTag, d_un: usize) -> Self::Dyn {
+ Elf32Dyn {
+ tag,
+ d_un: d_un as u32,
+ }
+ }
+ fn set_d_un(x: &mut Self::Dyn, val: usize) {
+ x.d_un = val as u32
+ }
+ fn tag_nul() -> Self::DynTag {
+ DynTag32(0)
+ }
+
+ fn d_un(x: Self::Dyn) -> usize {
+ x.d_un as usize
+ }
+ fn tag(x: Self::Dyn) -> Self::DynTag {
+ x.tag
+ }
+ }
+
+ impl Flags for u64 {
+ type F32 = ();
+ type F64 = u32;
+ type DynTag = DynTag64;
+ type Dyn = Elf64Dyn;
+
+ fn dyn_(tag: Self::DynTag, d_un: usize) -> Self::Dyn {
+ Elf64Dyn {
+ tag,
+ d_un: d_un as u64,
+ }
+ }
+ fn set_d_un(x: &mut Self::Dyn, val: usize) {
+ x.d_un = val as u64
+ }
+ fn tag_nul() -> Self::DynTag {
+ DynTag64(0)
+ }
+
+ fn d_un(x: Self::Dyn) -> usize {
+ x.d_un as usize
+ }
+ fn tag(x: Self::Dyn) -> Self::DynTag {
+ x.tag
+ }
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ struct Elf32Dyn {
+ tag: DynTag32,
+ d_un: u32,
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
+ struct Elf64Dyn {
+ tag: DynTag64,
+ d_un: u64,
+ }
+
+ #[derive(Debug, Clone, Copy, PartialEq, Eq)]
+ struct Sht(u32);
+
+ #[allow(dead_code)]
+ impl Sht {
+ const NULL: Self = Sht(0);
+ const PROGBITS: Self = Sht(1);
+ const SYMTAB: Self = Sht(2);
+ const STRTAB: Self = Sht(3);
+ const RELA: Self = Sht(4);
+ const HASH: Self = Sht(5);
+ const DYNAMIC: Self = Sht(6);
+ const NOTE: Self = Sht(7);
+ }
+
+ #[derive(Debug, Clone, Copy)]
+ enum Header_<'a> {
+ B32(&'a Header<u32>),
+ B64(&'a Header<u64>),
+ }
+
+ #[derive(Debug)]
+ enum HeaderMut_<'a> {
+ B32(&'a mut Header<u32>),
+ B64(&'a mut Header<u64>),
+ }
+
+ impl<'a> HeaderMut_<'a> {
+ fn set_shoff(&mut self, id: Endianness, off: usize) {
+ match self {
+ HeaderMut_::B32(h) => h.shoff = <Endian<u32>>::from(id, off as u32),
+ HeaderMut_::B64(h) => h.shoff = <Endian<u64>>::from(id, off as u64),
+ }
+ }
+ }
+
+ impl<'a> Header_<'a> {
+ fn shstrndx(&self, id: Endianness) -> u16 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.shstrndx, id),
+ Header_::B64(h) => EndianT::endian(h.shstrndx, id),
+ }
+ }
+
+ fn shnum(&self, id: Endianness) -> u16 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.shnum, id),
+ Header_::B64(h) => EndianT::endian(h.shnum, id),
+ }
+ }
+
+ fn shoff(&self, id: Endianness) -> u64 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.shoff, id) as u64,
+ Header_::B64(h) => EndianT::endian(h.shoff, id),
+ }
+ }
+
+ fn shentsize(&self, id: Endianness) -> u16 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.shentsize, id),
+ Header_::B64(h) => EndianT::endian(h.shentsize, id),
+ }
+ }
+
+ fn phentsize(&self, id: Endianness) -> u16 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.phentsize, id),
+ Header_::B64(h) => EndianT::endian(h.phentsize, id),
+ }
+ }
+
+ fn phnum(&self, id: Endianness) -> u16 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.phnum, id),
+ Header_::B64(h) => EndianT::endian(h.phnum, id),
+ }
+ }
+
+ fn phoff(&self, id: Endianness) -> u64 {
+ match self {
+ Header_::B32(h) => EndianT::endian(h.phoff, id) as u64,
+ Header_::B64(h) => EndianT::endian(h.phoff, id),
+ }
+ }
+ }