175 lines
6.6 KiB
Rust
175 lines
6.6 KiB
Rust
use elf::relocation::Rela;
|
|
|
|
use crate::{
|
|
object::{DynamicSymbol, ObjectMapping},
|
|
Error, State,
|
|
};
|
|
|
|
pub enum RelValue {
|
|
QWord(i64),
|
|
#[allow(unused)] // unused on x86_64
|
|
QDWord(i64, i64),
|
|
}
|
|
|
|
impl RelValue {
|
|
pub fn write(&self, mapping: &mut ObjectMapping, offset: u64) {
|
|
let addr = mapping.base + offset as usize;
|
|
unsafe {
|
|
match *self {
|
|
Self::QWord(value) => {
|
|
(addr as *mut i64).write_volatile(value);
|
|
}
|
|
Self::QDWord(word0, word1) => {
|
|
(addr as *mut i64).write_volatile(word0);
|
|
(addr as *mut i64).add(1).write_volatile(word1);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub trait RelocationExt {
|
|
fn resolve<'a, F: Fn(u32) -> Result<&'a DynamicSymbol, Error>>(
|
|
&'a self,
|
|
state: &mut State,
|
|
tls_module_id: Option<usize>,
|
|
image_symbol: F,
|
|
image_base: usize,
|
|
vma_start: usize,
|
|
) -> Result<Option<RelValue>, Error>;
|
|
}
|
|
|
|
#[cfg(any(target_arch = "x86_64", rust_analyzer))]
|
|
impl RelocationExt for Rela {
|
|
fn resolve<'a, F: Fn(u32) -> Result<&'a DynamicSymbol, Error>>(
|
|
&'a self,
|
|
state: &mut State,
|
|
tls_module_id: Option<usize>,
|
|
image_symbol: F,
|
|
image_base: usize,
|
|
vma_start: usize,
|
|
) -> Result<Option<RelValue>, Error> {
|
|
let image_base = image_base as i64;
|
|
let symbol = image_symbol(self.r_sym)?;
|
|
|
|
let base_value = match self.r_type {
|
|
elf::abi::R_X86_64_JUMP_SLOT => {
|
|
// TODO lazy binding could be implemented here
|
|
state.lookup_resolved_symbol(&symbol.name)
|
|
}
|
|
elf::abi::R_X86_64_GLOB_DAT => state.lookup_resolved_glob_dat(&symbol.name),
|
|
elf::abi::R_X86_64_64 => state.lookup_resolved_symbol(&symbol.name),
|
|
// Retrieve raw symbol value, as this might be an offset into local TLS struct
|
|
elf::abi::R_X86_64_DTPMOD64 | elf::abi::R_X86_64_DTPOFF64 => {
|
|
symbol.raw.st_value as usize
|
|
}
|
|
_ => {
|
|
if let Some(value) = symbol.value {
|
|
value
|
|
} else if symbol.raw.st_symtype() == elf::abi::STT_NOTYPE {
|
|
0
|
|
} else {
|
|
return Err(Error::CannotRelocate);
|
|
}
|
|
}
|
|
} as i64;
|
|
|
|
match self.r_type {
|
|
// Direct 64 bit
|
|
elf::abi::R_X86_64_64 => Ok(Some(RelValue::QWord(base_value + self.r_addend))),
|
|
elf::abi::R_X86_64_JUMP_SLOT => Ok(Some(RelValue::QWord(base_value))),
|
|
elf::abi::R_X86_64_COPY => todo!("{:?}: R_X86_64_COPY", symbol.name),
|
|
// GLOB_DAT
|
|
elf::abi::R_X86_64_GLOB_DAT => Ok(Some(RelValue::QWord(base_value))),
|
|
// Adjust by image base
|
|
elf::abi::R_X86_64_RELATIVE => Ok(Some(RelValue::QWord(image_base + self.r_addend))),
|
|
|
|
// ID of module containing this symbol (if not present assume local symbol)
|
|
elf::abi::R_X86_64_DTPMOD64 => {
|
|
// TODO I'm not sure if per-object TLS is really needed:
|
|
// all TLS segments could just be collected into a single one, simplifying
|
|
// __tls_get_addr()
|
|
if let Some((module_id, _)) = state.lookup_resolved_tls_symbol(&symbol.name) {
|
|
Ok(Some(RelValue::QWord(module_id as _)))
|
|
} else {
|
|
Ok(Some(RelValue::QWord(tls_module_id.unwrap() as _)))
|
|
}
|
|
}
|
|
// Offset in module's TLS block
|
|
elf::abi::R_X86_64_DTPOFF64 => {
|
|
if let Some((_, value)) = state.lookup_resolved_tls_symbol(&symbol.name) {
|
|
Ok(Some(RelValue::QWord(self.r_addend + value as i64)))
|
|
} else {
|
|
Ok(Some(RelValue::QWord(self.r_addend + base_value)))
|
|
}
|
|
}
|
|
|
|
ty => Err(Error::UnsupportedRelocation(ty)),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
|
impl RelocationExt for Rela {
|
|
fn resolve<'a, F: Fn(u32) -> Result<&'a DynamicSymbol, Error>>(
|
|
&'a self,
|
|
state: &mut State,
|
|
tls_module_id: Option<usize>,
|
|
image_symbol: F,
|
|
image_base: usize,
|
|
vma_start: usize,
|
|
) -> Result<Option<RelValue>, Error> {
|
|
let image_base = image_base as i64;
|
|
let symbol = image_symbol(self.r_sym)?;
|
|
|
|
let base_value = match self.r_type {
|
|
elf::abi::R_AARCH64_ABS64 => state.lookup_resolved_symbol(&symbol.name),
|
|
// TODO lazy binding?
|
|
elf::abi::R_AARCH64_JUMP_SLOT => state.lookup_resolved_symbol(&symbol.name),
|
|
elf::abi::R_AARCH64_GLOB_DAT => state.lookup_resolved_glob_dat(&symbol.name),
|
|
elf::abi::R_AARCH64_TLSDESC => symbol.value.unwrap_or(symbol.raw.st_value as usize),
|
|
_ => {
|
|
if let Some(value) = symbol.value {
|
|
value
|
|
} else if symbol.raw.st_symtype() == elf::abi::STT_NOTYPE {
|
|
0
|
|
} else {
|
|
return Err(Error::CannotRelocate);
|
|
}
|
|
}
|
|
} as i64;
|
|
|
|
match self.r_type {
|
|
// Adjust by image base
|
|
elf::abi::R_AARCH64_RELATIVE => Ok(Some(RelValue::QWord(image_base + self.r_addend))),
|
|
elf::abi::R_AARCH64_JUMP_SLOT => Ok(Some(RelValue::QWord(base_value))),
|
|
// GLOB_DAT
|
|
elf::abi::R_AARCH64_GLOB_DAT => Ok(Some(RelValue::QWord(base_value))),
|
|
// Direct 64 bit
|
|
elf::abi::R_AARCH64_ABS64 => Ok(Some(RelValue::QWord(base_value + self.r_addend))),
|
|
elf::abi::R_AARCH64_TLSDESC => {
|
|
let word0 = crate::builtins::__dl_tlsdesc_static as usize;
|
|
|
|
let reloc_offset =
|
|
unsafe { *((image_base as usize + self.r_offset as usize) as *const i64) };
|
|
|
|
let word1 = if let Some((module_id, offset)) =
|
|
state.lookup_resolved_tls_symbol(&symbol.name)
|
|
{
|
|
// FIXME not really sure about this code
|
|
let tls_base = state.tls_table[module_id].data.base;
|
|
|
|
base_value + reloc_offset + (tls_base + offset) as i64
|
|
} else {
|
|
let local_tls = state.tls_table[tls_module_id.unwrap() - 1].data.base;
|
|
|
|
local_tls as i64 + reloc_offset + self.r_addend
|
|
};
|
|
|
|
Ok(Some(RelValue::QDWord(word0 as _, word1)))
|
|
}
|
|
_ => Err(Error::UnsupportedRelocation(self.r_type)),
|
|
}
|
|
}
|
|
}
|