diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 5ed2e3fce3..e7a5d72e97 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -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>>, + function_plt_entries: SecondaryMap>>, data_object_got_entries: SecondaryMap>>, libcall_got_entries: HashMap>, + libcall_plt_entries: HashMap>, compiled_functions: SecondaryMap>, compiled_data_objects: SecondaryMap>, functions_to_finalize: Vec, @@ -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::() + } 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::(), + _ => 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) } diff --git a/cranelift/simplejit/src/compiled_blob.rs b/cranelift/simplejit/src/compiled_blob.rs index 5c9b268203..d44497ae9e 100644 --- a/cranelift/simplejit/src/compiled_blob.rs +++ b/cranelift/simplejit/src/compiled_blob.rs @@ -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!(), } }