use goblin::{
elf::{Dyn, Elf},
elf64::{
dynamic::{DF_1_PIE, DT_FLAGS_1},
program_header::PT_DYNAMIC,
},
error::Error as GoblinError,
};
use scroll::{Pread, Pwrite};
use std::{
fs, mem,
path::{Path, PathBuf},
};
use thiserror::Error;
#[derive(Debug, Error)]
pub enum Error {
#[error("Unable to parse ELF file")]
UnableToParseElf(GoblinError),
#[error("Unable to serialize ELF file")]
UnableToSerializeElf(GoblinError),
#[error("Unable to found DT_FLAGS_1 position")]
NoDTFlags1Found,
#[error("Unable to find PT_DYNAMIC section")]
NoDynamicSectionFound,
#[error("DT_FLAGS_1 flag crosscheck failed")]
FlagCrosscheckFailed,
#[error("IOError")]
IOError(#[from] std::io::Error),
}
pub fn patch_pie_binary_if_needed(
#[allow(unused_variables)] path: impl AsRef<Path>,
) -> Result<Option<PathBuf>, Error> {
let mut bytes = fs::read(path.as_ref())?;
let elf = Elf::parse(&bytes).map_err(Error::UnableToParseElf)?;
let Some(dynamic) = elf.dynamic else {
return Ok(None);
};
if dynamic.info.flags_1 & DF_1_PIE == 0 {
return Ok(None);
}
let (dyn_idx, _) = dynamic
.dyns
.iter()
.enumerate()
.find(|(_, d)| d.d_tag == DT_FLAGS_1)
.ok_or(Error::NoDTFlags1Found)?;
let header = elf
.program_headers
.iter()
.find(|h| h.p_type == PT_DYNAMIC)
.ok_or(Error::NoDynamicSectionFound)?;
let dyn_offset = header.p_offset as usize + dyn_idx * mem::size_of::<Dyn>();
let mut dyn_item = bytes
.pread::<Dyn>(dyn_offset)
.map_err(Error::UnableToSerializeElf)?;
if dyn_item.d_tag != DT_FLAGS_1 || dyn_item.d_val != dynamic.info.flags_1 {
return Err(Error::FlagCrosscheckFailed);
}
dyn_item.d_val &= !DF_1_PIE;
bytes
.pwrite(dyn_item, dyn_offset)
.map_err(Error::UnableToSerializeElf)?;
let path = path.as_ref().with_extension("patched");
fs::write(&path, bytes)?;
Ok(Some(path))
}