Implement PLT relocations for SimpleJIT

This commit is contained in:
bjorn3
2020-11-12 16:19:16 +01:00
parent eaa2c5b3c2
commit 5458473765
2 changed files with 79 additions and 6 deletions

View File

@@ -18,7 +18,7 @@ use cranelift_native;
use libc;
use log::info;
use std::collections::HashMap;
use std::convert::TryInto;
use std::convert::{TryFrom, TryInto};
use std::ffi::CString;
use std::io::Write;
use std::ptr;
@@ -131,8 +131,10 @@ pub struct SimpleJITModule {
memory: MemoryHandle,
declarations: ModuleDeclarations,
function_got_entries: SecondaryMap<FuncId, Option<NonNull<*const u8>>>,
function_plt_entries: SecondaryMap<FuncId, Option<NonNull<[u8; 16]>>>,
data_object_got_entries: SecondaryMap<DataId, Option<NonNull<*const u8>>>,
libcall_got_entries: HashMap<ir::LibCall, NonNull<*const u8>>,
libcall_plt_entries: HashMap<ir::LibCall, NonNull<[u8; 16]>>,
compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
functions_to_finalize: Vec<FuncId>,
@@ -168,6 +170,18 @@ impl SimpleJITModule {
.or_else(|| lookup_with_dlsym(name))
}
unsafe fn write_plt_entry_bytes(plt_ptr: *mut [u8; 16], got_ptr: *mut *const u8) {
assert!(cfg!(target_arch = "x86_64"), "PLT is currently only supported on x86_64");
// jmp *got_ptr; ud2; ud2; ud2; ud2; ud2
let mut plt_val = [
0xff, 0x25, 0, 0, 0, 0, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b, 0x0f, 0x0b,
];
let what = got_ptr as isize - 4;
let at = plt_ptr as isize + 2;
plt_val[2..6].copy_from_slice(&i32::to_ne_bytes(i32::try_from(what - at).unwrap()));
std::ptr::write(plt_ptr, plt_val);
}
fn get_address(&self, name: &ir::ExternalName) -> *const u8 {
match *name {
ir::ExternalName::User { .. } => {
@@ -234,6 +248,29 @@ impl SimpleJITModule {
}
}
fn get_plt_address(&self, name: &ir::ExternalName) -> *const u8 {
match *name {
ir::ExternalName::User { .. } => {
if ModuleDeclarations::is_function(name) {
let func_id = FuncId::from_name(name);
self.function_plt_entries[func_id]
.unwrap()
.as_ptr()
.cast::<u8>()
} else {
unreachable!("PLT relocations can only have functions as target");
}
}
ir::ExternalName::LibCall(ref libcall) => self
.libcall_plt_entries
.get(libcall)
.unwrap_or_else(|| panic!("can't resolve libcall {}", libcall))
.as_ptr()
.cast::<u8>(),
_ => panic!("invalid ExternalName {}", name),
}
}
/// Returns the address of a finalized function.
pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
let info = &self.compiled_functions[func_id];
@@ -293,6 +330,7 @@ impl SimpleJITModule {
func_blob.perform_relocations(
|name| unreachable!("non-GOT/PLT relocation in function {} to {}", func, name),
|name| self.get_got_address(name),
|name| self.get_plt_address(name),
);
}
for data in std::mem::take(&mut self.data_objects_to_finalize) {
@@ -304,6 +342,7 @@ impl SimpleJITModule {
data.perform_relocations(
|name| self.get_address(name),
|name| self.get_got_address(name),
|name| self.get_plt_address(name),
);
}
@@ -321,6 +360,7 @@ impl SimpleJITModule {
};
let mut libcall_got_entries = HashMap::new();
let mut libcall_plt_entries = HashMap::new();
// Pre-create a GOT entry for each libcall.
for &libcall in ir::LibCall::all_libcalls() {
@@ -334,15 +374,27 @@ impl SimpleJITModule {
.cast::<*const u8>();
libcall_got_entries.insert(libcall, NonNull::new(got_entry).unwrap());
let sym = (builder.libcall_names)(libcall);
if let Some(addr) = builder
let addr = if let Some(addr) = builder
.symbols
.get(&sym)
.copied()
.or_else(|| lookup_with_dlsym(&sym))
{
unsafe {
std::ptr::write(got_entry, addr);
}
addr
} else {
continue;
};
unsafe {
std::ptr::write(got_entry, addr);
}
let plt_entry = memory
.code
.allocate(std::mem::size_of::<[u8; 16]>(), EXECUTABLE_DATA_ALIGNMENT)
.unwrap()
.cast::<[u8; 16]>();
libcall_plt_entries.insert(libcall, NonNull::new(plt_entry).unwrap());
unsafe {
Self::write_plt_entry_bytes(plt_entry, got_entry);
}
}
@@ -353,8 +405,10 @@ impl SimpleJITModule {
memory,
declarations: ModuleDeclarations::default(),
function_got_entries: SecondaryMap::new(),
function_plt_entries: SecondaryMap::new(),
data_object_got_entries: SecondaryMap::new(),
libcall_got_entries,
libcall_plt_entries,
compiled_functions: SecondaryMap::new(),
compiled_data_objects: SecondaryMap::new(),
functions_to_finalize: Vec::new(),
@@ -397,6 +451,16 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
unsafe {
std::ptr::write(got_entry, val);
}
let plt_entry = self
.memory
.code
.allocate(std::mem::size_of::<[u8; 16]>(), EXECUTABLE_DATA_ALIGNMENT)
.unwrap()
.cast::<[u8; 16]>();
self.function_plt_entries[id] = Some(NonNull::new(plt_entry).unwrap());
unsafe {
Self::write_plt_entry_bytes(plt_entry, got_entry);
}
}
Ok(id)
}

View File

@@ -15,6 +15,7 @@ impl CompiledBlob {
&self,
get_address: impl Fn(&ExternalName) -> *const u8,
get_got_entry: impl Fn(&ExternalName) -> *const u8,
get_plt_entry: impl Fn(&ExternalName) -> *const u8,
) {
use std::ptr::write_unaligned;
@@ -62,7 +63,15 @@ impl CompiledBlob {
write_unaligned(at as *mut i32, pcrel)
};
}
Reloc::X86CallPLTRel4 => todo!("PLT relocation"),
Reloc::X86CallPLTRel4 => {
let base = get_plt_entry(name);
let what = unsafe { base.offset(isize::try_from(addend).unwrap()) };
let pcrel = i32::try_from((what as isize) - (at as isize)).unwrap();
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut i32, pcrel)
};
}
_ => unimplemented!(),
}
}