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> },
#[error("Wrong file type")]
WrongMagic,
#[error("No dynamic section")]
NoDynamicSection,
#[error("No section table")]
NoSectionTable,
}
#[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)]
pub 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)]
#[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)]
pub struct Id {
magic: [u8; 4],
class: Bits,
pub data: Endianness,
version: u8,
osabi: ABI,
abi_version: u8,
padding: [u8; 7],
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
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<u16> {
fn from(id: Endianness, u: u16) -> Self {
if id == Endianness::LITTLE {
Endian(u.to_le())
} else if id == Endianness::BIG {
Endian(u.to_be())
} 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!()
}
}
}
impl Endian<Pht> {
fn from(id: Endianness, u: Pht) -> Self {
if id == Endianness::LITTLE {
Endian(Pht(u.0.to_le()))
} else if id == Endianness::BIG {
Endian(Pht(u.0.to_be()))
} else {
panic!()
}
}
}
impl Endian<DynTag64> {
fn from(id: Endianness, u: DynTag64) -> Self {
if id == Endianness::LITTLE {
Endian(DynTag64(u.0.to_le()))
} else if id == Endianness::BIG {
Endian(DynTag64(u.0.to_be()))
} else {
panic!()
}
}
}
impl Endian<DynTag32> {
fn from(id: Endianness, u: DynTag32) -> Self {
if id == Endianness::LITTLE {
Endian(DynTag32(u.0.to_le()))
} else if id == Endianness::BIG {
Endian(DynTag32(u.0.to_be()))
} else {
panic!()
}
}
}
impl EndianT for Pht {
fn endian(e: Endian<Self>, id: Endianness) -> Self {
if id == Endianness::LITTLE {
Pht(u32::from_le(e.0 .0))
} else if id == Endianness::BIG {
Pht(u32::from_be(e.0 .0))
} 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 ProgramHeaderBits<'a> {
B32(&'a ProgramHeader<u32>),
B64(&'a ProgramHeader<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),
}
}
pub 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),
}
}
pub fn set_addr(&mut self, h: Endianness, u: usize) {
match self {
SectionHeaderMut_::B32(s) => s.addr = <Endian<u32>>::from(h, u as u32),
SectionHeaderMut_::B64(s) => s.addr = <Endian<u64>>::from(h, u as u64),
}
}
pub fn offset(&self, h: Endianness) -> u64 {
match self {
SectionHeaderMut_::B32(s) => EndianT::endian(s.offset, h) as u64,
SectionHeaderMut_::B64(s) => EndianT::endian(s.offset, h),
}
}
pub fn addr(&self, h: Endianness) -> u64 {
match self {
SectionHeaderMut_::B32(s) => EndianT::endian(s.addr, h) as u64,
SectionHeaderMut_::B64(s) => EndianT::endian(s.addr, h),
}
}
}
impl<'a> ProgramHeaderMut_<'a> {
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),
}
}
pub fn set_vaddr(&mut self, h: Endianness, u: usize) {
match self {
ProgramHeaderMut_::B32(s) => s.vaddr = <Endian<u32>>::from(h, u as u32),
ProgramHeaderMut_::B64(s) => s.vaddr = <Endian<u64>>::from(h, u as u64),
}
self.set_paddr(h, u);
}
pub fn set_paddr(&mut self, h: Endianness, u: usize) {
match self {
ProgramHeaderMut_::B32(s) => s.paddr = <Endian<u32>>::from(h, u as u32),
ProgramHeaderMut_::B64(s) => s.paddr = <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),
}
}
pub 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),
}
}
pub fn set_type(&mut self, h: Endianness, u: Pht) {
match self {
ProgramHeaderMut_::B32(s) => s.type_ = <Endian<Pht>>::from(h, u),
ProgramHeaderMut_::B64(s) => s.type_ = <Endian<Pht>>::from(h, u),
}
}
pub fn set_flags(&mut self, h: Endianness, u: u32) {
match self {
ProgramHeaderMut_::B32(s) => s.flags32 = <Endian<u32>>::from(h, u),
ProgramHeaderMut_::B64(s) => s.flags64 = <Endian<u32>>::from(h, u),
}
}
pub fn set_align(&mut self, h: Endianness, u: u64) {
match self {
ProgramHeaderMut_::B32(s) => s.align = <Endian<u32>>::from(h, u as u32),
ProgramHeaderMut_::B64(s) => s.align = <Endian<u64>>::from(h, u),
}
}
fn type_(&self, id: Endianness) -> Pht {
match self {
ProgramHeaderMut_::B32(h) => EndianT::endian(h.type_, id),
ProgramHeaderMut_::B64(h) => EndianT::endian(h.type_, id),
}
}
pub fn filesz(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderMut_::B32(s) => EndianT::endian(s.filesz, h) as u64,
ProgramHeaderMut_::B64(s) => EndianT::endian(s.filesz, h),
}
}
pub fn memsz(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderMut_::B32(s) => EndianT::endian(s.memsz, h) as u64,
ProgramHeaderMut_::B64(s) => EndianT::endian(s.memsz, h),
}
}
}
impl<'a> ProgramHeaderBits<'a> {
fn offset(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderBits::B32(s) => EndianT::endian(s.offset, h) as u64,
ProgramHeaderBits::B64(s) => EndianT::endian(s.offset, h),
}
}
fn type_(&self, id: Endianness) -> Pht {
match self {
ProgramHeaderBits::B32(h) => EndianT::endian(h.type_, id),
ProgramHeaderBits::B64(h) => EndianT::endian(h.type_, id),
}
}
fn filesz(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderBits::B32(s) => EndianT::endian(s.filesz, h) as u64,
ProgramHeaderBits::B64(s) => EndianT::endian(s.filesz, h),
}
}
fn flags(&self, h: Endianness) -> u32 {
match self {
ProgramHeaderBits::B32(s) => EndianT::endian(s.flags32, h),
ProgramHeaderBits::B64(s) => EndianT::endian(s.flags64, h),
}
}
fn vaddr(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderBits::B32(s) => EndianT::endian(s.vaddr, h) as u64,
ProgramHeaderBits::B64(s) => EndianT::endian(s.vaddr, h),
}
}
fn memsz(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderBits::B32(s) => EndianT::endian(s.memsz, h) as u64,
ProgramHeaderBits::B64(s) => EndianT::endian(s.memsz, h),
}
}
pub fn align(&self, h: Endianness) -> u64 {
match self {
ProgramHeaderBits::B32(s) => EndianT::endian(s.align, h) as u64,
ProgramHeaderBits::B64(s) => EndianT::endian(s.align, h),
}
}
}
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),
}
}
pub fn addr(&self, h: Endianness) -> u64 {
match self {
SectionHeader_::B32(s) => EndianT::endian(s.addr, h) as u64,
SectionHeader_::B64(s) => EndianT::endian(s.addr, 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);
impl Pht {
const LOAD: Self = Pht(1);
const PHDR: Self = Pht(6);
}
#[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>,
}
pub struct Elf<'a> {
map: memmap::MmapMut,
f: &'a std::fs::File,
new_needed: Option<Vec<Vec<u8>>>,
new_runpath: Vec<u8>,
new_interp: Vec<u8>,
}
impl<'f> Elf<'f> {
pub fn open(file: &'f std::fs::File) -> Result<Self, Error> {
let map = unsafe { memmap::MmapOptions::new().map_mut(&file)? };
let e = Elf {
map,
f: file,
new_needed: None,
new_runpath: Vec::new(),
new_interp: Vec::new(),
};
if e.map.len() >= std::mem::size_of::<Header<u32>>()
&& e.id().magic == [0x7f, 0x45, 0x4c, 0x46]
{
Ok(e)
} else {
Err(Error::WrongMagic)
}
}
pub fn dynsym(&self) -> Dynsym {
let header = self.header();
let id = self.id();
let names = self.names(id.data, &header).unwrap();
for i in 0..header.shnum(id.data) {
let s = self.section(i as usize).unwrap();
let name = names.name(s.name(id.data) as usize).unwrap();
if name.to_bytes() == b".dynsym" {
let offset = s.offset(id.data) as usize;
let size = s.size(id.data) as usize;
if id.class == Bits::B32 {
return Dynsym::B32(unsafe {
std::slice::from_raw_parts(
self.map.as_ptr().add(offset) as *const u8 as *const Elf32Sym,
size / std::mem::size_of::<Elf32Sym>(),
)
});
} else if id.class == Bits::B64 {
return Dynsym::B64(unsafe {
std::slice::from_raw_parts(
self.map.as_ptr().add(offset) as *const u8 as *const Elf64Sym,
size / std::mem::size_of::<Elf64Sym>(),
)
});
}
}
}
Dynsym::B64(&[])
}
pub fn verneed(&self) -> Vec<(Verneed, Vec<Vernaux>)> {
let header = self.header();
let id = self.id();
let names = self.names(id.data, &header).unwrap();
let mut result = Vec::new();
for i in 0..header.shnum(id.data) {
let s = self.section(i as usize).unwrap();
let name = names.name(s.name(id.data) as usize).unwrap();
if name.to_bytes() == b".gnu.version_r" {
let offset = s.offset(id.data) as usize;
let size = s.size(id.data) as usize;
unsafe {
let mut current = self.map.as_ptr().add(offset) as *const u8;
loop {
assert!((current as usize) < offset + size);
let mut aux = Vec::new();
let mut aux_ptr =
current.add((&*(current as *const Verneed)).aux as usize) as *const u8;
loop {
assert!((aux_ptr as usize) < offset + size);
aux.push(*(aux_ptr as *const Vernaux));
let next = (&*(aux_ptr as *const Vernaux)).next as usize;
if next == 0 {
break;
}
aux_ptr = aux_ptr.add(next);
}
result.push((*(current as *const Verneed), aux));
let next = (&*(current as *const Vernaux)).next as usize;
if next == 0 {
break;
}
current = current.add(next);
}
}
}
}
result
}
pub fn debug_svg(&self) -> String {
use std::fmt::Write;
let mut svg = String::new();
let mut x = 0;
writeln!(
svg,
r#"<rect x="0" y="0" width="{}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">File id</text>"#,
std::mem::size_of::<Id>(),
std::mem::size_of::<Id>()/2
)
.unwrap();
x += std::mem::size_of::<Id>();
let id = self.id();
let header_w = if let Bits::B64 = id.class {
std::mem::size_of::<Header<u64>>()
} else {
std::mem::size_of::<Header<u32>>()
};
writeln!(
svg,
r#"<rect x="{x}" y="0" width="{}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">File header</text>"#,
header_w,
x + header_w / 2,
)
.unwrap();
x += header_w;
let h = self.header();
let shoff = h.shoff(id.data);
let shw = h.shnum(id.data) * h.shentsize(id.data);
writeln!(
svg,
r#"<rect x="{shoff}" y="0" width="{shw}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">Section headers</text>"#,
shoff as usize + shw as usize/2,
)
.unwrap();
x = x.max(shoff as usize + shw as usize);
let phoff = h.phoff(id.data);
let phw = h.phnum(id.data) * h.phentsize(id.data);
writeln!(
svg,
r#"<rect x="{phoff}" y="0" width="{phw}" height="10" fill="none" stroke="black" /><text text-anchor="middle" x="{}" y="10">Program headers</text>"#,
phoff as usize + phw as usize/2,
)
.unwrap();
x = x.max(phoff as usize + phw as usize);
let mut y = 10;
for i in 0..h.shnum(id.data) {
let s = self.section(i as usize).unwrap();
let off = s.offset(id.data);
let sz = s.size(id.data);
let names = self.names(id.data, &h).unwrap();
let name = names.name(s.name(id.data) as usize).unwrap();
let xoff = off + sz / 2;
x = x.max(off as usize + sz as usize);
writeln!(
svg,
r#"<rect x="{off}" y="{y}" width="{sz}" height="10" fill="none" stroke="blue" /><text text-anchor="middle" x="{xoff}" y="{y}" transform="rotate(90, {}, {y})">s{}: {}</text>"#,
xoff,
i,
name.to_str().unwrap()
)
.unwrap();
y += 10;
}
for i in 0..h.phnum(id.data) {
let s = self.program(i as usize);
let off = s.offset(id.data);
let sz = s.filesz(id.data);
let xoff = off + sz / 2;
writeln!(
svg,
r#"<rect x="{off}" y="{y}" width="{sz}" height="10" fill="none" stroke="red" /><text text-anchor="middle" x="{xoff}" y="{}">p{}: {}</text>"#,
y+10,
i,
s.flags(id.data)
)
.unwrap();
y += 10;
}
for i in 0..h.phnum(id.data) {
let s = self.program(i as usize);
let off = s.vaddr(id.data);
let sz = s.memsz(id.data);
let xoff = off + sz / 2;
x = x.max(off as usize + sz as usize);
writeln!(
svg,
r#"<rect x="{off}" y="{y}" width="{sz}" height="10" fill="none" stroke="green" /><text text-anchor="middle" x="{xoff}" y="{}">p{}: {}</text>"#,
y+10,
i,
s.flags(id.data),
)
.unwrap();
y += 10;
}
let mut p = 4096;
let yy = y + 50;
let mut pages = String::new();
while p < x {
writeln!(
pages,
r#"<path d="M {p} -40 V {}" stroke="gray" stroke-dasharray="4" /><text text-anchor="middle" x="{p}" y="0">{p}</text>"#,
y + 10,
)
.unwrap();
p += 4096;
}
format!(
r#"<svg viewBox="0 -40 {x} {yy}" xmlns="http://www.w3.org/2000/svg">{pages}{svg}</svg>"#,
)
}
fn section<'a>(&'a self, n: usize) -> Result<SectionHeader_<'a>, Error> {
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(_) => {
if ph + std::mem::size_of::<SectionHeader<u32>>() > self.map.len() {
return Err(Error::NoSectionTable);
}
assert_eq!(ph % std::mem::align_of::<SectionHeader<u32>>(), 0);
Ok(SectionHeader_::B32(unsafe {
&*((self.map.as_ptr()).add(ph) as *const u8 as *const SectionHeader<u32>)
}))
}
Header_::B64(_) => {
if ph + std::mem::size_of::<SectionHeader<u64>>() > self.map.len() {
return Err(Error::NoSectionTable);
}
assert_eq!(ph % std::mem::align_of::<SectionHeader<u64>>(), 0);
Ok(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<'a>(&'a self, n: usize) -> ProgramHeaderBits<'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);
ProgramHeaderBits::B32(unsafe {
&*((self.map.as_ptr()).add(ph) as *const u8 as *const 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);
ProgramHeaderBits::B64(unsafe {
&*((self.map.as_ptr()).add(ph) as *const u8 as *const ProgramHeader<u64>)
})
}
}
}
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_) -> Result<Names<'a>, Error> {
let s = self.section(header.shstrndx(id) as usize)?;
let offset = s.offset(id) as usize;
let size = s.size(id) as usize;
Ok(Names(&self.map[offset..offset + size]))
}
pub 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();
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!(),
}
}
pub fn parse(&self) -> Result<Option<Parsed>, Error> {
let id = self.id();
let header = self.header();
let names = match self.names(id.data, &header) {
Ok(names) => names,
Err(Error::NoSectionTable) => return Ok(None),
Err(e) => return Err(e),
};
let mut dynstr = Names(&[][..]);
let mut dynamic = None;
let mut interp = None;
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 = Some(&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;
assert!(offset + size <= self.map.len());
dynamic = Some(unsafe {
dynamic_from_raw(id.class, self.map.as_ptr().add(offset), size)
});
}
}
if let Some(dynamic) = dynamic {
Ok(Some(Parsed {
data: id.data,
dynstr,
dynamic,
interp,
}))
} else {
Ok(None)
}
}
pub fn section_bytes(&self, name_: &[u8]) -> Result<Option<&[u8]>, Error> {
let id = self.id();
let header = self.header();
let names = self.names(id.data, &header)?;
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() == name_ {
let offset = s.offset(id.data) as usize;
let size = s.size(id.data) as usize;
return Ok(Some(&self.map[offset..offset + size]));
}
}
Ok(None)
}
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() {
let n = names
.name(EndianT::endian(sec.name, id) as usize)?
.to_bytes();
if n == 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 = self.f.metadata().unwrap().len();
for s in self
.sections::<U>()
.iter()
.map(|sec| EndianT::endian(sec.offset, id).into())
.chain(std::iter::once(header.shoff(id)))
{
if s > off && s < next_off {
next_off = s;
}
}
next_off
}
fn update_section(&mut self, name: &[u8], new: &[u8]) -> Result<(), 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::<u32>(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 {
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(())
} else {
Ok(self.update_section_allocate(id, new, interp_i))
}
}
fn update_section_allocate(&mut self, id: Endianness, new: &[u8], interp_i: usize) {
let new_len = new.len();
let h = self.header();
let mut sections: Vec<_> = (0..h.shnum(id))
.map(|p_| {
let sh = self.section(p_ as usize).unwrap();
(
sh.offset(id) as usize,
sh.size(id) as usize,
sh.type_(id),
p_ as isize,
)
})
.collect();
sections.push((
h.shoff(id) as usize,
h.shentsize(id) as usize * h.shnum(id) as usize,
Sht::NULL,
-1,
));
sections.sort_by_key(|(o, _, _, _)| *o);
let s = sections
.iter()
.position(|(_, _, _, i)| *i == interp_i as isize)
.unwrap();
let mut last_vaddr = 0;
let mut page_size = 0;
let mut programs: Vec<_> = (0..h.phnum(id))
.map(|p_| {
let sh = self.program(p_ as usize);
last_vaddr = last_vaddr.max((sh.vaddr(id) + sh.memsz(id)) as usize);
let type_ = sh.type_(id);
if type_ == Pht::LOAD {
page_size = sh.align(id) as usize;
}
(
sh.offset(id) as usize,
sh.filesz(id) as usize,
type_,
sh.flags(id),
p_ as isize,
)
})
.collect();
let mut rounded_last_vaddr = align_next(last_vaddr as usize, page_size);
programs.sort_by_key(|(o, _, _, _, _)| *o);
let s_off = sections[s].0;
let p = programs
.iter()
.position(|(o, e, t, _, _)| *o <= s_off && *o + *e > s_off && *t == Pht::LOAD)
.expect("Section isn't loaded, no need to do anything");
let s_len = sections[s].1;
let end0 = s_off + s_len;
let mut end = end0;
let shoff = h.shoff(id) as usize;
let mut new_offset = shoff;
for (off, len, _, _) in sections.iter().skip(s + 1) {
if *off >= end + new_len {
if end <= programs[p].0 + programs[p].1 {
new_offset = end;
break;
}
}
end = end.max(*off + *len)
}
if new_offset < shoff {
unsafe {
std::ptr::copy(
new.as_ptr(),
self.map.as_mut_ptr().add(new_offset),
new.len(),
)
}
let mut sec = self.section_mut(interp_i);
let addr = new_offset + sec.addr(id) as usize - sec.offset(id) as usize;
sec.set_addr(id, addr);
sec.set_offset(id, new_offset);
sec.set_size(id, new.len());
if new_offset == programs[p].0 + programs[p].1 {
let mut p = self.program_mut(programs[p].4 as usize);
p.set_filesz(id, p.filesz(id) as usize + new.len());
p.set_memsz(id, p.memsz(id) as usize + new.len());
}
for (o, l, _, _, p) in &programs {
if *o == s_off && *l == s_len {
let mut p = self.program_mut(*p as usize);
p.set_offset(id, new_offset);
p.set_filesz(id, new.len());
p.set_memsz(id, new.len());
p.set_vaddr(id, addr);
}
}
} else {
let pht_end = h.phoff(id) as usize + h.phentsize(id) as usize * h.phnum(id) as usize;
let first_section_after_pht = sections
.iter()
.filter_map(|(o, _, _, _)| {
if *o >= pht_end as usize {
Some(*o)
} else {
None
}
})
.min()
.unwrap_or(0);
let new_offset;
if first_section_after_pht < pht_end + h.phentsize(id) as usize {
let new_segments = 3;
let limit = pht_end + new_segments * h.phentsize(id) as usize;
let mut init = 0;
let mut end = 0;
let mut init_off = 0;
let mut end_off = 0;
for (n, (o, _, _, _)) in sections.iter().enumerate() {
if *o >= pht_end && init == 0 {
init = n;
init_off = *o;
}
if *o >= limit && end == 0 {
end = n;
end_off = *o;
}
}
let current_len = sections
.iter()
.map(|(o, l, t, _)| if *t != Sht::NOBITS { o + l } else { 0 })
.max()
.unwrap_or(self.f.metadata().unwrap().len() as usize);
let rounded_len = align_next(current_len, page_size);
let rounded_moved = align_next(end_off - init_off, page_size as usize);
new_offset = rounded_len as usize + rounded_moved;
self.f
.set_len((rounded_len + rounded_moved + new.len()) as u64)
.unwrap();
self.map = unsafe { memmap::MmapOptions::new().map_mut(self.f).unwrap() };
unsafe {
std::ptr::copy(
self.map.as_ptr().add(init_off),
self.map.as_mut_ptr().add(rounded_len as usize),
end_off - init_off,
);
}
let mut pi = 0;
for s in init..end {
let sec_n = sections[s].3;
let mut sec = self.section_mut(sec_n as usize);
let new_offset = rounded_len as usize + sec.offset(id) as usize - init_off;
sec.set_offset(id, new_offset);
sec.set_addr(id, new_offset + rounded_last_vaddr - rounded_len as usize);
while pi < programs.len() && programs[pi].0 <= sections[s].0 {
if programs[pi].0 == sections[s].0 && programs[pi].2 != Pht::LOAD {
let mut p = self.program_mut(programs[pi].4 as usize);
p.set_offset(id, new_offset);
p.set_vaddr(id, new_offset + rounded_last_vaddr - rounded_len as usize);
programs[pi].0 = new_offset;
}
pi += 1;
}
}
self.add_load_segment(
id,
rounded_len as usize,
end_off - init_off,
rounded_last_vaddr,
4,
page_size,
);
rounded_last_vaddr = rounded_last_vaddr + rounded_moved as usize;
} else {
let current_len = sections
.iter()
.map(|(o, l, t, _)| if *t != Sht::NOBITS { o + l } else { 0 })
.max()
.unwrap_or(self.f.metadata().unwrap().len() as usize);
let rounded_len = align_next(current_len, page_size);
new_offset = rounded_len;
self.f.set_len((rounded_len + new.len()) as u64).unwrap();
self.map = unsafe { memmap::MmapOptions::new().map_mut(self.f).unwrap() };
}
assert_eq!(new_offset % 4096, rounded_last_vaddr % 4096);
let mut sec = self.section_mut(interp_i);
let old_offset = sec.offset(id) as usize;
sec.set_offset(id, new_offset);
sec.set_size(id, new.len());
sec.set_addr(id, rounded_last_vaddr as usize);
for (o, _, ty, _, p) in &programs {
if *o == old_offset && *ty != Pht::LOAD {
let mut p = self.program_mut(*p as usize);
p.set_offset(id, new_offset);
p.set_filesz(id, new.len());
p.set_memsz(id, new.len());
p.set_vaddr(id, rounded_last_vaddr as usize);
}
}
unsafe {
std::ptr::copy(
new.as_ptr(),
self.map.as_mut_ptr().add(new_offset),
new.len(),
)
}
self.add_load_segment(
id,
new_offset,
new.len(),
rounded_last_vaddr as usize,
6,
page_size,
);
}
}
fn add_load_segment(
&mut self,
id: Endianness,
offset: usize,
len: usize,
vaddr: usize,
flags: u32,
align: usize,
) {
let mut hh = self.header_mut();
let phentsize = hh.phentsize(id) as usize;
let phnum = hh.phnum(id);
hh.set_phnum(id, phnum + 1);
let mut p = self.program_mut(phnum as usize);
p.set_offset(id, offset);
p.set_memsz(id, len);
p.set_filesz(id, len);
p.set_vaddr(id, vaddr);
p.set_type(id, Pht::LOAD);
p.set_flags(id, flags);
p.set_align(id, align as u64);
for p in 0..phnum {
let mut p = self.program_mut(p as usize);
if p.type_(id) == Pht::PHDR {
p.set_filesz(id, p.filesz(id) as usize + phentsize);
p.set_memsz(id, p.memsz(id) as usize + phentsize);
break;
}
}
}
pub fn set_runpath(&mut self, runpath: &[u8]) -> &mut Self {
self.new_runpath.clear();
self.new_runpath.extend(runpath);
self
}
pub fn add_needed(&mut self, needed: &[u8]) -> Result<&mut Self, Error> {
if self.new_needed.is_none() {
let Some(parsed) = self.parse()? else {
return Err(Error::NoDynamicSection);
};
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)
}
pub fn retain_needed<F: FnMut(&[u8]) -> bool>(
&mut self,
mut retain: F,
) -> Result<&mut Self, Error> {
if self.new_needed.is_none() {
let Some(parsed) = self.parse()? else {
return Err(Error::NoDynamicSection);
};
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,
data: Endianness,
dynstr: &mut Vec<u8>,
dynamic: &mut OwnedDynamic,
runpath: Option<&[u8]>,
) -> Result<(), Error> {
let initial_dynamic_len = match dynamic {
OwnedDynamic::B32(d) => d.len(),
OwnedDynamic::B64(d) => d.len(),
};
remove_trailing_needed_runpath(data, dynstr, dynamic);
let id = self.id().data;
if self.new_runpath.is_empty() {
if let Some(runpath) = runpath {
set_runpath(id, dynstr, dynamic, &runpath)
}
} else {
set_runpath(id, dynstr, dynamic, &self.new_runpath)
}
let id = self.id().data;
if let Some(ref mut needed) = self.new_needed {
retain_needed(id, dynstr, dynamic, needed);
}
match dynamic {
OwnedDynamic::B32(d) => {
while d.len() < initial_dynamic_len {
d.push(u32::dyn_(id, u32::tag_nul(), 0));
}
}
OwnedDynamic::B64(d) => {
while d.len() < initial_dynamic_len {
d.push(u64::dyn_(id, u64::tag_nul(), 0));
}
}
}
Ok(())
}
pub fn update(mut self, into: Option<&std::path::Path>) -> Result<bool, Error> {
let Some(p) = self.parse()? else {
return Ok(false);
};
let mut dynstr_owned = p.dynstr.0.to_vec();
let mut dynamic_owned = p.dynamic.to_owned();
let interp_owned = p.interp.to_owned();
let p = Parsed {
data: p.data,
dynamic: dynamic_owned.as_ref(),
dynstr: Names(&dynstr_owned),
interp: interp_owned.as_deref(),
};
let runpath = p.runpath()?.map(|x| x.to_bytes_with_nul().to_vec());
self.update_sections(
p.data,
&mut dynstr_owned,
&mut dynamic_owned,
runpath.as_deref(),
)?;
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 = self.new_needed.take();
let id = self.id().data;
if !new_runpath.is_empty() {
set_runpath(id, &mut dynstr_owned, &mut dynamic_owned, &new_runpath)
}
let into_file = if let Some(into) = into {
let mut f = std::fs::OpenOptions::new()
.read(true)
.write(true)
.create(true)
.open(&into)?;
std::fs::set_permissions(into, self.f.metadata()?.permissions())?;
std::io::copy(&mut self.f, &mut f)?;
Some(f)
} else {
None
};
let mut elf = if let Some(ref file) = into_file {
Elf::open(&file)?
} else {
self
};
if !new_interp.is_empty() {
elf.update_section(b".interp", &new_interp).unwrap()
}
if new_needed.is_some() || !new_runpath.is_empty() {
elf.update_section(b".dynstr", &dynstr_owned).unwrap();
elf.update_section(b".dynamic", dynamic_owned.as_bytes())
.unwrap();
}
let id = elf.id().data;
match dynamic_owned {
OwnedDynamic::B32(ref mut v) => {
for t in v.iter_mut() {
elf.update_dynamic::<u32>(id, t)?
}
}
OwnedDynamic::B64(ref mut v) => {
for t in v.iter_mut() {
elf.update_dynamic::<u64>(id, t)?
}
}
}
elf.update_section(b".dynamic", dynamic_owned.as_bytes())
.unwrap();
Ok(true)
}
fn update_dynamic<F: Flags>(&self, id: Endianness, f: &mut F::Dyn) -> Result<(), Error> {
if F::tag(id, *f) == F::dt_strtab() {
let s = self.find_section::<F>(b".dynstr").unwrap().unwrap();
let sec = self.section(s)?;
F::set_d_un(id, f, sec.addr(id) as usize)
} else if F::tag(id, *f) == F::dt_strsz() {
let s = self.find_section::<F>(b".dynstr").unwrap().unwrap();
let sec = self.section(s)?;
F::set_d_un(id, f, sec.size(id) as usize)
} else if F::tag(id, *f) == F::dt_gnuhash() {
let s = self.find_section::<F>(b".gnu.hash").unwrap().unwrap();
let sec = self.section(s)?;
F::set_d_un(id, f, sec.addr(id) as usize)
}
Ok(())
}
pub fn set_interpreter(&mut self, new: &[u8]) -> &mut Self {
self.new_interp = new.to_vec();
self
}
}
#[derive(Debug)]
pub struct Parsed<'a> {
data: Endianness,
dynamic: Dynamic<'a>,
dynstr: Names<'a>,
interp: Option<&'a [u8]>,
}
#[derive(Debug, Clone)]
enum OwnedDynamic {
B32(Vec<Elf32Dyn>),
B64(Vec<Elf64Dyn>),
}
impl OwnedDynamic {
fn as_ref(&self) -> Dynamic {
match self {
OwnedDynamic::B32(d) => Dynamic::B32(d),
OwnedDynamic::B64(d) => Dynamic::B64(d),
}
}
fn as_bytes(&self) -> &[u8] {
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>(
id: Endianness,
dynstr: &mut Vec<u8>,
dynamic: &mut Vec<F::Dyn>,
) {
let mut off: Vec<F::Dyn> = dynamic
.iter()
.filter_map(|x| {
if F::tag(id, *x) == F::dt_needed() || F::tag(id, *x) == F::dt_runpath() {
Some(*x)
} else {
None
}
})
.collect();
off.sort_by_key(|x| F::d_un(id, *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(id, p) as usize..]).unwrap();
let len = c.count_bytes() + 1;
if F::d_un(id, p) as usize + len >= dynstr.len() {
dynstr.truncate(F::d_un(id, p) as usize);
removed.insert(p);
} else {
off.push(p);
break;
}
}
dynamic.retain(|x| !removed.contains(x))
}
fn remove_trailing_needed_runpath(
id: Endianness,
dynstr: &mut Vec<u8>,
dynamic: &mut OwnedDynamic,
) {
match dynamic {
OwnedDynamic::B32(b) => remove_trailing_needed_runpath_::<u32>(id, dynstr, b),
OwnedDynamic::B64(b) => remove_trailing_needed_runpath_::<u64>(id, dynstr, b),
}
}
fn set_runpath_<F: Flags>(
id: Endianness,
dynstr: &mut Vec<u8>,
dynamic: &mut Vec<F::Dyn>,
rpath: &[u8],
) {
for x in dynamic.iter_mut() {
if F::tag(id, *x) == F::dt_runpath() {
let current = &mut dynstr[F::d_un(id, *x)..];
let pos = current.iter().position(|x| *x == 0).unwrap();
if pos + 1 >= rpath.len() {
let current = &mut current[..rpath.len()];
current.clone_from_slice(rpath);
} else {
let offset = dynstr.len();
dynstr.extend(rpath);
F::set_d_un(id, x, offset);
}
return;
}
}
let offset = dynstr.len();
dynstr.extend(rpath);
if dynamic.len() >= 2 && F::tag(id, dynamic[dynamic.len() - 2]) == F::tag_nul() {
dynamic.pop();
}
dynamic.insert(0, F::dyn_(id, F::dt_runpath(), offset));
}
fn retain_needed(
id: Endianness,
dynstr: &mut Vec<u8>,
dynamic: &mut OwnedDynamic,
needed: &mut Vec<Vec<u8>>,
) {
match dynamic {
OwnedDynamic::B32(b) => retain_needed_::<u32>(id, dynstr, b, needed),
OwnedDynamic::B64(b) => retain_needed_::<u64>(id, dynstr, b, needed),
}
}
fn retain_needed_<F: Flags>(
id: Endianness,
dynstr: &mut Vec<u8>,
dynamic: &mut Vec<F::Dyn>,
needed: &[Vec<u8>],
) {
let initial_dynamic_len = dynamic.len();
let mut offsets = HashMap::new();
for d in dynamic.iter() {
if F::tag(id, *d) == F::dt_needed() {
let off = F::d_un(id, *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(id, d) == F::tag_nul() {
break;
} else if F::tag(id, 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(id, &mut d, *off);
new_dynamic.push(d)
} else {
F::set_d_un(id, &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_(id, F::dt_needed(), *off))
} else {
new_dynamic.push(F::dyn_(
id,
F::dt_needed(),
dynstr.len() + extra_dynstr.len(),
));
extra_dynstr.extend(needed.as_slice());
}
}
dynstr.extend(&extra_dynstr);
new_dynamic.push(F::dyn_(id, F::tag_nul(), 0));
while new_dynamic.len() < initial_dynamic_len {
new_dynamic.push(F::dyn_(id, F::tag_nul(), 0));
}
*dynamic = new_dynamic;
}
fn set_runpath(id: Endianness, dynstr: &mut Vec<u8>, dynamic: &mut OwnedDynamic, rpath: &[u8]) {
match dynamic {
OwnedDynamic::B32(b) => set_runpath_::<u32>(id, dynstr, b, rpath),
OwnedDynamic::B64(b) => set_runpath_::<u64>(id, 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> {
pub fn runpath(&self) -> Result<Option<&'a CStr>, Error> {
let mut rpath = None;
match self.dynamic {
Dynamic::B64(dyn_) => {
for d in dyn_ {
if EndianT::endian(d.tag, self.data) == <u64 as Flags>::dt_runpath() {
rpath = Some(
self.dynstr
.name(EndianT::endian(d.d_un, self.data) as usize)?,
);
}
}
}
Dynamic::B32(dyn_) => {
for d in dyn_ {
if EndianT::endian(d.tag, self.data) == <u32 as Flags>::dt_runpath() {
rpath = Some(
self.dynstr
.name(EndianT::endian(d.d_un, self.data) as usize)?,
);
}
}
}
}
Ok(rpath)
}
pub fn needed<'b>(&'b self) -> impl Iterator<Item = Result<&'a CStr, Error>> + 'b {
match self.dynamic {
Dynamic::B64(dyn_) => Either::A(dyn_.iter().filter_map(|d| {
if EndianT::endian(d.tag, self.data) == <u64 as Flags>::dt_needed() {
Some(
self.dynstr
.name(EndianT::endian(d.d_un, self.data) as usize),
)
} else {
None
}
})),
Dynamic::B32(dyn_) => Either::B(dyn_.iter().filter_map(|d| {
if EndianT::endian(d.tag, self.data) == <u32 as Flags>::dt_needed() {
Some(
self.dynstr
.name(EndianT::endian(d.d_un, self.data) 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()
}
pub fn interpreter(&self) -> Result<Option<&'a CStr>, Error> {
Ok(self.interp.map(|x| CStr::from_bytes_until_nul(x).unwrap()))
}
}
#[derive(Debug)]
enum Dynamic<'a> {
B32(&'a [Elf32Dyn]),
B64(&'a [Elf64Dyn]),
}
#[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_(id: Endianness, tag: Self::DynTag, d_un: usize) -> Self::Dyn;
fn d_un(id: Endianness, _: Self::Dyn) -> usize;
fn set_d_un(id: Endianness, _: &mut Self::Dyn, _: usize);
fn tag(id: Endianness, _: Self::Dyn) -> Self::DynTag;
fn tag_nul() -> Self::DynTag;
fn dt_needed() -> Self::DynTag {
Self::DynTag::from(1)
}
fn dt_strsz() -> Self::DynTag {
Self::DynTag::from(0x0a)
}
fn dt_gnuhash() -> Self::DynTag {
Self::DynTag::from(0x000000006ffffef5)
}
fn dt_strtab() -> Self::DynTag {
Self::DynTag::from(0x5)
}
fn dt_runpath() -> Self::DynTag {
Self::DynTag::from(0x1d)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
struct DynTag32(u32);
impl EndianT for DynTag32 {
fn endian(e: Endian<Self>, id: Endianness) -> Self {
DynTag32(EndianT::endian(Endian(e.0 .0), id))
}
}
impl std::convert::From<u64> for DynTag32 {
fn from(e: u64) -> Self {
Self(e as u32)
}
}
impl EndianT for DynTag64 {
fn endian(e: Endian<Self>, id: Endianness) -> Self {
DynTag64(EndianT::endian(Endian(e.0 .0), id))
}
}
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_(id: Endianness, tag: Self::DynTag, d_un: usize) -> Self::Dyn {
Elf32Dyn {
tag: <Endian<DynTag32>>::from(id, tag),
d_un: <Endian<u32>>::from(id, d_un as u32),
}
}
fn set_d_un(id: Endianness, x: &mut Self::Dyn, val: usize) {
x.d_un = <Endian<u32>>::from(id, val as u32)
}
fn tag_nul() -> Self::DynTag {
DynTag32(0)
}
fn d_un(id: Endianness, x: Self::Dyn) -> usize {
EndianT::endian(x.d_un, id) as usize
}
fn tag(id: Endianness, x: Self::Dyn) -> Self::DynTag {
EndianT::endian(x.tag, id)
}
}
impl Flags for u64 {
type F32 = ();
type F64 = u32;
type DynTag = DynTag64;
type Dyn = Elf64Dyn;
fn dyn_(e: Endianness, tag: Self::DynTag, d_un: usize) -> Self::Dyn {
Elf64Dyn {
tag: <Endian<DynTag64>>::from(e, tag),
d_un: <Endian<u64>>::from(e, d_un as u64),
}
}
fn set_d_un(id: Endianness, x: &mut Self::Dyn, val: usize) {
x.d_un = <Endian<u64>>::from(id, val as u64)
}
fn tag_nul() -> Self::DynTag {
DynTag64(0)
}
fn d_un(id: Endianness, x: Self::Dyn) -> usize {
EndianT::endian(x.d_un, id) as usize
}
fn tag(id: Endianness, x: Self::Dyn) -> Self::DynTag {
EndianT::endian(x.tag, id)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
struct Elf32Dyn {
tag: Endian<DynTag32>,
d_un: Endian<u32>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
struct Elf64Dyn {
tag: Endian<DynTag64>,
d_un: Endian<u64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Elf32Sym {
name: Endian<u32>,
value: Endian<u32>,
size: Endian<u32>,
info: u8,
other: u8,
shndx: Endian<u16>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Elf64Sym {
name: Endian<u32>,
info: u8,
other: u8,
shndx: Endian<u16>,
value: Endian<u64>,
size: Endian<u64>,
}
#[derive(Debug)]
pub enum Dynsym<'a> {
B32(&'a [Elf32Sym]),
B64(&'a [Elf64Sym]),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Verneed {
version: u16,
cnt: u16,
file: u32,
aux: u32,
next: u32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[repr(C)]
pub struct Vernaux {
hash: u32,
flags: u16,
other: u16,
name: u32,
next: u32,
}
#[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);
const NOBITS: Self = Sht(8);
}
#[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> {
pub fn set_phnum(&mut self, id: Endianness, off: u16) {
match self {
HeaderMut_::B32(h) => h.phnum = <Endian<u16>>::from(id, off),
HeaderMut_::B64(h) => h.phnum = <Endian<u16>>::from(id, off),
}
}
pub fn phnum(&self, id: Endianness) -> u16 {
match self {
HeaderMut_::B32(h) => EndianT::endian(h.phnum, id),
HeaderMut_::B64(h) => EndianT::endian(h.phnum, id),
}
}
pub fn phentsize(&self, id: Endianness) -> u16 {
match self {
HeaderMut_::B32(h) => EndianT::endian(h.phentsize, id),
HeaderMut_::B64(h) => EndianT::endian(h.phentsize, id),
}
}
}
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),
}
}
pub 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),
}
}
pub 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),
}
}
}
unsafe fn dynamic_from_raw<'a>(class: Bits, ptr: *const u8, size: usize) -> Dynamic<'a> {
match class {
Bits::B32 => {
assert_eq!((ptr as usize) % std::mem::align_of::<Elf32Dyn>(), 0);
Dynamic::B32(unsafe {
std::slice::from_raw_parts(
ptr as *const Elf32Dyn,
size / std::mem::size_of::<Elf32Dyn>(),
)
})
}
Bits::B64 => {
assert_eq!((ptr as usize) % std::mem::align_of::<Elf64Dyn>(), 0);
Dynamic::B64(unsafe {
std::slice::from_raw_parts(
ptr as *const Elf64Dyn,
size / std::mem::size_of::<Elf64Dyn>(),
)
})
}
_ => panic!(),
}
}