use crate::{
RelocReason, Result,
arch::x86_64::relocation::X86_64Arch,
elf::{ElfRelEntry, ElfRelType, ElfRelocationType, ElfShdr},
memory::{ImageMemory, ImageMemoryExt, RegionAccess, VmAddr},
object::layout::{GotEntry, ObjectRelocKey, PltEntry, PltGotSection},
observer::RelocationObserver,
relocation::{
ObjectArch, RelocHelper, RelocValue, RelocationValueInput, RelocationValueProvider,
},
};
use elf::abi::*;
#[cfg_attr(not(target_arch = "x86_64"), allow(dead_code))]
pub(crate) const PLT_ENTRY_SIZE: usize = 16;
#[cfg_attr(not(target_arch = "x86_64"), allow(dead_code))]
pub(crate) const PLT_ENTRY: [u8; PLT_ENTRY_SIZE] = [
0xf3, 0x0f, 0x1e, 0xfa, 0xff, 0x25, 0, 0, 0, 0, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc,
];
enum ObjectWrite {
None,
Addr(VmAddr),
Word32(RelocValue<u32>),
SWord32(RelocValue<i32>),
}
impl ObjectArch for X86_64Arch {
type State = ();
#[allow(private_bounds)]
#[allow(private_interfaces)]
fn relocate<D, R, Tls, Obs, H, Memory>(
_state: &mut Self::State,
helper: &mut RelocHelper<'_, D, Self, R, Tls, Obs, H, Memory>,
rel: &ElfRelType<Self>,
target: &ElfShdr<<Self as crate::relocation::RelocationArch>::Layout>,
pltgot: &mut PltGotSection,
) -> Result<()>
where
D: 'static,
R: RegionAccess,
Tls: crate::tls::TlsResolver<Self>,
Obs: RelocationObserver<Self> + ?Sized,
Memory: ImageMemory,
{
let r_type = rel.r_type();
let place = VmAddr::new(target.sh_addr()) + rel.r_offset();
let append = rel.read_addend(helper.memory(), place)?;
let value_error = |reason| helper.reloc_error(rel, reason);
let relocation_target_value = |target| {
Self::object_relocation_value(r_type.raw() as usize, target, append, place.get())
};
let write_relocation_target = |memory: &Memory, target| -> Result<()> {
Self::write_object_value(
memory,
place,
relocation_target_value(target).map_err(value_error)?,
)
};
match r_type.raw() {
R_X86_64_NONE => {}
R_X86_64_64 => {
let sym = helper.symbol_addr(rel.r_symbol());
write_relocation_target(helper.memory(), sym.get())?;
}
R_X86_64_PC32 => {
let sym = helper.symbol_addr(rel.r_symbol());
write_relocation_target(helper.memory(), sym.get())?;
}
R_X86_64_PLT32 => {
let sym = helper.symbol_addr(rel.r_symbol());
match relocation_target_value(sym.get()) {
Ok(value) => Self::write_object_value(helper.memory(), place, value)?,
Err(RelocReason::IntConversionOutOfRange) => {
let key = ObjectRelocKey::new::<Self>(rel, append);
let plt_entry = pltgot.add_plt_entry(key);
let plt_entry_addr = match plt_entry {
PltEntry::Occupied(plt_entry_addr) => plt_entry_addr,
PltEntry::Vacant { plt, mut got } => {
let plt_entry_addr = VmAddr::from_ptr(plt.as_ptr());
got.update(sym);
let call_offset = VmAddr::new(
got.get_addr()
.get()
.wrapping_sub(plt_entry_addr.get())
.wrapping_sub(10),
);
let call_offset_val =
call_offset.try_into_sword32().map_err(value_error)?;
plt[6..10].copy_from_slice(&call_offset_val.to_ne_bytes());
plt_entry_addr
}
};
write_relocation_target(helper.memory(), plt_entry_addr.get())?;
}
Err(reason) => return Err(value_error(reason)),
}
}
R_X86_64_GOTPCREL => {
let sym = helper.symbol_addr(rel.r_symbol());
let key = ObjectRelocKey::new::<Self>(rel, append);
let got_entry = pltgot.add_got_entry(key);
let got_entry_addr = match got_entry {
GotEntry::Occupied(got_entry_addr) => got_entry_addr,
GotEntry::Vacant(mut got) => {
got.update(sym);
got.get_addr()
}
};
write_relocation_target(helper.memory(), got_entry_addr.get())?;
}
R_X86_64_32 => {
let sym = helper.symbol_addr(rel.r_symbol());
write_relocation_target(helper.memory(), sym.get())?;
}
R_X86_64_32S => {
let sym = helper.symbol_addr(rel.r_symbol());
write_relocation_target(helper.memory(), sym.get())?;
}
_ => return Err(value_error(RelocReason::UnknownSymbol)),
}
Ok(())
}
#[inline]
fn needs_got(r_type: ElfRelocationType) -> bool {
r_type.raw() == R_X86_64_GOTPCREL
}
#[inline]
fn needs_plt(r_type: ElfRelocationType) -> bool {
r_type.raw() == R_X86_64_PLT32
}
}
impl X86_64Arch {
#[inline]
fn object_relocation_value(
r_type: usize,
target: usize,
append: isize,
place: usize,
) -> core::result::Result<ObjectWrite, RelocReason> {
<Self as RelocationValueProvider>::relocation_value(
RelocationValueInput {
relocation_type: r_type,
target,
addend: append,
place,
},
|_| ObjectWrite::None,
ObjectWrite::Addr,
ObjectWrite::Word32,
ObjectWrite::SWord32,
)
}
#[inline]
fn write_object_value<Memory>(
memory: &Memory,
place: VmAddr,
value: ObjectWrite,
) -> crate::Result<()>
where
Memory: ImageMemory,
{
unsafe {
match value {
ObjectWrite::None => {}
ObjectWrite::Addr(value) => memory.write_value(place, value.get())?,
ObjectWrite::Word32(value) => memory.write_value(place, value.into_inner())?,
ObjectWrite::SWord32(value) => memory.write_value(place, value.into_inner())?,
}
}
Ok(())
}
}