1use goblin::{
2 elf::{Dyn, Elf},
3 elf64::{
4 dynamic::{DF_1_PIE, DT_FLAGS_1},
5 program_header::PT_DYNAMIC,
6 },
7 error::Error as GoblinError,
8};
9use scroll::{Pread, Pwrite};
10use std::{
11 fs, mem,
12 path::{Path, PathBuf},
13};
14use thiserror::Error;
15
16#[derive(Debug, Error)]
17pub enum Error {
18 #[error("Unable to parse ELF file")]
19 UnableToParseElf(GoblinError),
20
21 #[error("Unable to serialize ELF file")]
22 UnableToSerializeElf(GoblinError),
23
24 #[error("Unable to found DT_FLAGS_1 position")]
25 NoDTFlags1Found,
26
27 #[error("Unable to find PT_DYNAMIC section")]
28 NoDynamicSectionFound,
29
30 #[error("DT_FLAGS_1 flag crosscheck failed")]
31 FlagCrosscheckFailed,
32
33 #[error("IOError")]
34 IOError(#[from] std::io::Error),
35}
36
37pub fn patch_pie_binary_if_needed(
61 #[allow(unused_variables)] path: impl AsRef<Path>,
62) -> Result<Option<PathBuf>, Error> {
63 let mut bytes = fs::read(path.as_ref())?;
64 let elf = Elf::parse(&bytes).map_err(Error::UnableToParseElf)?;
65
66 let Some(dynamic) = elf.dynamic else {
67 return Ok(None);
68 };
69 if dynamic.info.flags_1 & DF_1_PIE == 0 {
70 return Ok(None);
71 }
72
73 let (dyn_idx, _) = dynamic
74 .dyns
75 .iter()
76 .enumerate()
77 .find(|(_, d)| d.d_tag == DT_FLAGS_1)
78 .ok_or(Error::NoDTFlags1Found)?;
79
80 let header = elf
82 .program_headers
83 .iter()
84 .find(|h| h.p_type == PT_DYNAMIC)
85 .ok_or(Error::NoDynamicSectionFound)?;
86
87 let dyn_offset = header.p_offset as usize + dyn_idx * mem::size_of::<Dyn>();
89
90 let mut dyn_item = bytes
92 .pread::<Dyn>(dyn_offset)
93 .map_err(Error::UnableToSerializeElf)?;
94
95 if dyn_item.d_tag != DT_FLAGS_1 || dyn_item.d_val != dynamic.info.flags_1 {
96 return Err(Error::FlagCrosscheckFailed);
97 }
98
99 dyn_item.d_val &= !DF_1_PIE;
101 bytes
102 .pwrite(dyn_item, dyn_offset)
103 .map_err(Error::UnableToSerializeElf)?;
104
105 let path = path.as_ref().with_extension("patched");
106 fs::write(&path, bytes)?;
107
108 Ok(Some(path))
109}