From 11a3bdfc6ad8e0987630519e401ffcefe5a62109 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 14:13:06 +0100 Subject: [PATCH 01/12] Catch overflows when performing relocations --- cranelift/simplejit/src/compiled_blob.rs | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/cranelift/simplejit/src/compiled_blob.rs b/cranelift/simplejit/src/compiled_blob.rs index dc8563dabdfb..e00fbeecf02c 100644 --- a/cranelift/simplejit/src/compiled_blob.rs +++ b/cranelift/simplejit/src/compiled_blob.rs @@ -21,27 +21,24 @@ impl CompiledBlob { } in &self.relocs { debug_assert!((offset as usize) < self.size); - let at = unsafe { self.ptr.offset(offset as isize) }; + let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) }; let base = get_definition(name); - // TODO: Handle overflow. - let what = unsafe { base.offset(addend as isize) }; + let what = unsafe { base.offset(isize::try_from(addend).unwrap()) }; match reloc { Reloc::Abs4 => { - // TODO: Handle overflow. #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] unsafe { - write_unaligned(at as *mut u32, what as u32) + write_unaligned(at as *mut u32, u32::try_from(what as usize).unwrap()) }; } Reloc::Abs8 => { #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] unsafe { - write_unaligned(at as *mut u64, what as u64) + write_unaligned(at as *mut u64, u64::try_from(what as usize).unwrap()) }; } Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => { - // TODO: Handle overflow. - let pcrel = ((what as isize) - (at as isize)) as i32; + 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) From eaa2c5b3c25accd1a46fb9bb5bf10ee9a7ae02ac Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 15:06:52 +0100 Subject: [PATCH 02/12] Implement GOT relocations in SimpleJIT --- cranelift/codegen/src/ir/libcall.rs | 28 ++++ cranelift/simplejit/src/backend.rs | 167 +++++++++++++++++++++-- cranelift/simplejit/src/compiled_blob.rs | 26 +++- 3 files changed, 207 insertions(+), 14 deletions(-) diff --git a/cranelift/codegen/src/ir/libcall.rs b/cranelift/codegen/src/ir/libcall.rs index 9dc134e48073..632b04a4c315 100644 --- a/cranelift/codegen/src/ir/libcall.rs +++ b/cranelift/codegen/src/ir/libcall.rs @@ -63,6 +63,7 @@ pub enum LibCall { /// Elf __tls_get_addr ElfTlsGetAddr, + // When adding a new variant make sure to add it to `all_libcalls` too. } impl fmt::Display for LibCall { @@ -136,6 +137,33 @@ impl LibCall { _ => return None, }) } + + /// Get a list of all known `LibCall`'s. + pub fn all_libcalls() -> &'static [LibCall] { + use LibCall::*; + &[ + Probestack, + UdivI64, + SdivI64, + UremI64, + SremI64, + IshlI64, + UshrI64, + SshrI64, + CeilF32, + CeilF64, + FloorF32, + FloorF64, + TruncF32, + TruncF64, + NearestF32, + NearestF64, + Memcpy, + Memset, + Memmove, + ElfTlsGetAddr, + ] + } } /// Get a function reference for `libcall` in `func`, following the signature diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index d211eb5feb51..5ed2e3fce3cb 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -22,6 +22,7 @@ use std::convert::TryInto; use std::ffi::CString; use std::io::Write; use std::ptr; +use std::ptr::NonNull; use target_lexicon::PointerWidth; #[cfg(windows)] use winapi; @@ -73,7 +74,7 @@ impl SimpleJITBuilder { isa: Box, libcall_names: Box String + Send + Sync>, ) -> Self { - debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code"); + assert!(isa.flags().is_pic(), "SimpleJIT requires PIC code"); let symbols = HashMap::new(); Self { isa, @@ -129,6 +130,9 @@ pub struct SimpleJITModule { libcall_names: Box String>, memory: MemoryHandle, declarations: ModuleDeclarations, + function_got_entries: SecondaryMap>>, + data_object_got_entries: SecondaryMap>>, + libcall_got_entries: HashMap>, compiled_functions: SecondaryMap>, compiled_data_objects: SecondaryMap>, functions_to_finalize: Vec, @@ -164,7 +168,7 @@ impl SimpleJITModule { .or_else(|| lookup_with_dlsym(name)) } - fn get_definition(&self, name: &ir::ExternalName) -> *const u8 { + fn get_address(&self, name: &ir::ExternalName) -> *const u8 { match *name { ir::ExternalName::User { .. } => { let (name, linkage) = if ModuleDeclarations::is_function(name) { @@ -203,10 +207,37 @@ impl SimpleJITModule { } } + fn get_got_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_got_entries[func_id] + .unwrap() + .as_ptr() + .cast::() + } else { + let data_id = DataId::from_name(name); + self.data_object_got_entries[data_id] + .unwrap() + .as_ptr() + .cast::() + } + } + ir::ExternalName::LibCall(ref libcall) => self + .libcall_got_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]; - debug_assert!( + assert!( !self.functions_to_finalize.iter().any(|x| *x == func_id), "function not yet finalized" ); @@ -218,7 +249,7 @@ impl SimpleJITModule { /// Returns the address and size of a finalized data object. pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) { let info = &self.compiled_data_objects[data_id]; - debug_assert!( + assert!( !self.data_objects_to_finalize.iter().any(|x| *x == data_id), "data object not yet finalized" ); @@ -255,19 +286,25 @@ impl SimpleJITModule { pub fn finalize_definitions(&mut self) { for func in std::mem::take(&mut self.functions_to_finalize) { let decl = self.declarations.get_function_decl(func); - debug_assert!(decl.linkage.is_definable()); - let func = self.compiled_functions[func] + assert!(decl.linkage.is_definable()); + let func_blob = self.compiled_functions[func] .as_ref() .expect("function must be compiled before it can be finalized"); - func.perform_relocations(|name| self.get_definition(name)); + func_blob.perform_relocations( + |name| unreachable!("non-GOT/PLT relocation in function {} to {}", func, name), + |name| self.get_got_address(name), + ); } for data in std::mem::take(&mut self.data_objects_to_finalize) { let decl = self.declarations.get_data_decl(data); - debug_assert!(decl.linkage.is_definable()); + assert!(decl.linkage.is_definable()); let data = self.compiled_data_objects[data] .as_ref() .expect("data object must be compiled before it can be finalized"); - data.perform_relocations(|name| self.get_definition(name)); + data.perform_relocations( + |name| self.get_address(name), + |name| self.get_got_address(name), + ); } // Now that we're done patching, prepare the memory for execution! @@ -277,18 +314,47 @@ impl SimpleJITModule { /// Create a new `SimpleJITModule`. pub fn new(builder: SimpleJITBuilder) -> Self { - let memory = MemoryHandle { + let mut memory = MemoryHandle { code: Memory::new(), readonly: Memory::new(), writable: Memory::new(), }; + let mut libcall_got_entries = HashMap::new(); + + // Pre-create a GOT entry for each libcall. + for &libcall in ir::LibCall::all_libcalls() { + let got_entry = memory + .writable + .allocate( + std::mem::size_of::<*const u8>(), + std::mem::align_of::<*const u8>().try_into().unwrap(), + ) + .unwrap() + .cast::<*const u8>(); + libcall_got_entries.insert(libcall, NonNull::new(got_entry).unwrap()); + let sym = (builder.libcall_names)(libcall); + if let Some(addr) = builder + .symbols + .get(&sym) + .copied() + .or_else(|| lookup_with_dlsym(&sym)) + { + unsafe { + std::ptr::write(got_entry, addr); + } + } + } + Self { isa: builder.isa, symbols: builder.symbols, libcall_names: builder.libcall_names, memory, declarations: ModuleDeclarations::default(), + function_got_entries: SecondaryMap::new(), + data_object_got_entries: SecondaryMap::new(), + libcall_got_entries, compiled_functions: SecondaryMap::new(), compiled_data_objects: SecondaryMap::new(), functions_to_finalize: Vec::new(), @@ -315,6 +381,23 @@ impl<'simple_jit_backend> Module for SimpleJITModule { let (id, _decl) = self .declarations .declare_function(name, linkage, signature)?; + if self.function_got_entries[id].is_none() { + let got_entry = self + .memory + .writable + .allocate( + std::mem::size_of::<*const u8>(), + std::mem::align_of::<*const u8>().try_into().unwrap(), + ) + .unwrap() + .cast::<*const u8>(); + self.function_got_entries[id] = Some(NonNull::new(got_entry).unwrap()); + // FIXME populate got entries with a null pointer when defined + let val = self.lookup_symbol(name).unwrap_or(std::ptr::null()); + unsafe { + std::ptr::write(got_entry, val); + } + } Ok(id) } @@ -329,9 +412,63 @@ impl<'simple_jit_backend> Module for SimpleJITModule { let (id, _decl) = self .declarations .declare_data(name, linkage, writable, tls)?; + if self.data_object_got_entries[id].is_none() { + let got_entry = self + .memory + .writable + .allocate( + std::mem::size_of::<*const u8>(), + std::mem::align_of::<*const u8>().try_into().unwrap(), + ) + .unwrap() + .cast::<*const u8>(); + self.data_object_got_entries[id] = Some(NonNull::new(got_entry).unwrap()); + // FIXME populate got entries with a null pointer when defined + let val = self.lookup_symbol(name).unwrap_or(std::ptr::null()); + unsafe { + std::ptr::write(got_entry, val); + } + } Ok(id) } + /// Use this when you're building the IR of a function to reference a function. + /// + /// TODO: Coalesce redundant decls and signatures. + /// TODO: Look into ways to reduce the risk of using a FuncRef in the wrong function. + fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef { + let decl = self.declarations.get_function_decl(func); + let signature = in_func.import_signature(decl.signature.clone()); + in_func.import_function(ir::ExtFuncData { + name: ir::ExternalName::user(0, func.as_u32()), + signature, + colocated: false, + }) + } + + /// Use this when you're building the IR of a function to reference a data object. + /// + /// TODO: Same as above. + fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { + let decl = self.declarations.get_data_decl(data); + func.create_global_value(ir::GlobalValueData::Symbol { + name: ir::ExternalName::user(1, data.as_u32()), + offset: ir::immediates::Imm64::new(0), + colocated: false, + tls: decl.tls, + }) + } + + /// TODO: Same as above. + fn declare_func_in_data(&self, func: FuncId, ctx: &mut DataContext) -> ir::FuncRef { + ctx.import_function(ir::ExternalName::user(0, func.as_u32())) + } + + /// TODO: Same as above. + fn declare_data_in_data(&self, data: DataId, ctx: &mut DataContext) -> ir::GlobalValue { + ctx.import_global_value(ir::ExternalName::user(1, data.as_u32())) + } + fn define_function( &mut self, id: FuncId, @@ -381,7 +518,11 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: reloc_sink.relocs, }); + // FIXME immediately perform relocations self.functions_to_finalize.push(id); + unsafe { + std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); + } Ok(ModuleCompiledFunction { size: code_size }) } @@ -425,6 +566,9 @@ impl<'simple_jit_backend> Module for SimpleJITModule { relocs: relocs.to_vec(), }); self.functions_to_finalize.push(id); + unsafe { + std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); + } Ok(ModuleCompiledFunction { size: total_size }) } @@ -489,6 +633,9 @@ impl<'simple_jit_backend> Module for SimpleJITModule { self.compiled_data_objects[id] = Some(CompiledBlob { ptr, size, relocs }); self.data_objects_to_finalize.push(id); + unsafe { + std::ptr::write(self.data_object_got_entries[id].unwrap().as_ptr(), ptr); + } Ok(()) } diff --git a/cranelift/simplejit/src/compiled_blob.rs b/cranelift/simplejit/src/compiled_blob.rs index e00fbeecf02c..5c9b2682038d 100644 --- a/cranelift/simplejit/src/compiled_blob.rs +++ b/cranelift/simplejit/src/compiled_blob.rs @@ -1,6 +1,7 @@ use cranelift_codegen::binemit::Reloc; use cranelift_codegen::ir::ExternalName; use cranelift_module::RelocRecord; +use std::convert::TryFrom; #[derive(Clone)] pub(crate) struct CompiledBlob { @@ -10,7 +11,11 @@ pub(crate) struct CompiledBlob { } impl CompiledBlob { - pub(crate) fn perform_relocations(&self, get_definition: impl Fn(&ExternalName) -> *const u8) { + pub(crate) fn perform_relocations( + &self, + get_address: impl Fn(&ExternalName) -> *const u8, + get_got_entry: impl Fn(&ExternalName) -> *const u8, + ) { use std::ptr::write_unaligned; for &RelocRecord { @@ -22,29 +27,42 @@ impl CompiledBlob { { debug_assert!((offset as usize) < self.size); let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) }; - let base = get_definition(name); - let what = unsafe { base.offset(isize::try_from(addend).unwrap()) }; match reloc { Reloc::Abs4 => { + let base = get_address(name); + let what = unsafe { base.offset(isize::try_from(addend).unwrap()) }; #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] unsafe { write_unaligned(at as *mut u32, u32::try_from(what as usize).unwrap()) }; } Reloc::Abs8 => { + let base = get_address(name); + let what = unsafe { base.offset(isize::try_from(addend).unwrap()) }; #[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))] unsafe { write_unaligned(at as *mut u64, u64::try_from(what as usize).unwrap()) }; } Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => { + let base = get_address(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) }; } - Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"), + Reloc::X86GOTPCRel4 => { + let base = get_got_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) + }; + } + Reloc::X86CallPLTRel4 => todo!("PLT relocation"), _ => unimplemented!(), } } From 5458473765bd044da013e8ad08a2018ec4733b9b Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 16:19:16 +0100 Subject: [PATCH 03/12] Implement PLT relocations for SimpleJIT --- cranelift/simplejit/src/backend.rs | 74 ++++++++++++++++++++++-- cranelift/simplejit/src/compiled_blob.rs | 11 +++- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 5ed2e3fce3cb..e7a5d72e97cf 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 5c9b2682038d..d44497ae9ee6 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!(), } } From 8a4749af51273dde8b38ee60c583774260edbda2 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 16:33:04 +0100 Subject: [PATCH 04/12] Immediately perform relocations when defining a function --- cranelift/simplejit/src/backend.rs | 36 ++++++++++++------------------ 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index e7a5d72e97cf..e5f90e9db883 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -137,7 +137,6 @@ pub struct SimpleJITModule { libcall_plt_entries: HashMap>, compiled_functions: SecondaryMap>, compiled_data_objects: SecondaryMap>, - functions_to_finalize: Vec, data_objects_to_finalize: Vec, } @@ -171,7 +170,10 @@ impl SimpleJITModule { } 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"); + 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, @@ -274,10 +276,6 @@ impl SimpleJITModule { /// 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]; - assert!( - !self.functions_to_finalize.iter().any(|x| *x == func_id), - "function not yet finalized" - ); info.as_ref() .expect("function must be compiled before it can be finalized") .ptr @@ -321,18 +319,6 @@ impl SimpleJITModule { /// Use `get_finalized_function` and `get_finalized_data` to obtain the final /// artifacts. pub fn finalize_definitions(&mut self) { - for func in std::mem::take(&mut self.functions_to_finalize) { - let decl = self.declarations.get_function_decl(func); - assert!(decl.linkage.is_definable()); - let func_blob = self.compiled_functions[func] - .as_ref() - .expect("function must be compiled before it can be finalized"); - 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) { let decl = self.declarations.get_data_decl(data); assert!(decl.linkage.is_definable()); @@ -411,7 +397,6 @@ impl SimpleJITModule { libcall_plt_entries, compiled_functions: SecondaryMap::new(), compiled_data_objects: SecondaryMap::new(), - functions_to_finalize: Vec::new(), data_objects_to_finalize: Vec::new(), } } @@ -582,11 +567,14 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: reloc_sink.relocs, }); - // FIXME immediately perform relocations - self.functions_to_finalize.push(id); unsafe { std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); } + self.compiled_functions[id].as_ref().unwrap().perform_relocations( + |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), + |name| self.get_got_address(name), + |name| self.get_plt_address(name), + ); Ok(ModuleCompiledFunction { size: code_size }) } @@ -629,10 +617,14 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: relocs.to_vec(), }); - self.functions_to_finalize.push(id); unsafe { std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); } + self.compiled_functions[id].as_ref().unwrap().perform_relocations( + |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), + |name| self.get_got_address(name), + |name| self.get_plt_address(name), + ); Ok(ModuleCompiledFunction { size: total_size }) } From bf9e5d94480c085bb7c8d6165ade1ccd1a4444f0 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 16:41:23 +0100 Subject: [PATCH 05/12] Use a PLT reference for function relocations in data objects This ensures that all functions can be replaced without having to perform relocations again. --- cranelift/simplejit/src/backend.rs | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index e5f90e9db883..97abd744f61f 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -188,14 +188,7 @@ impl SimpleJITModule { match *name { ir::ExternalName::User { .. } => { let (name, linkage) = if ModuleDeclarations::is_function(name) { - let func_id = FuncId::from_name(name); - match &self.compiled_functions[func_id] { - Some(compiled) => return compiled.ptr, - None => { - let decl = self.declarations.get_function_decl(func_id); - (&decl.name, decl.linkage) - } - } + return self.get_plt_address(name); } else { let data_id = DataId::from_name(name); match &self.compiled_data_objects[data_id] { From cdbbcf7e13dd3a29993158847a9204a96d389ff2 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 18:58:28 +0100 Subject: [PATCH 06/12] Add plt entries to perf jit map --- cranelift/simplejit/src/backend.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 97abd744f61f..38b61c04d1f9 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -435,6 +435,11 @@ impl<'simple_jit_backend> Module for SimpleJITModule { .allocate(std::mem::size_of::<[u8; 16]>(), EXECUTABLE_DATA_ALIGNMENT) .unwrap() .cast::<[u8; 16]>(); + self.record_function_for_perf( + plt_entry as *mut _, + std::mem::size_of::<[u8; 16]>(), + &format!("{}@plt", name), + ); self.function_plt_entries[id] = Some(NonNull::new(plt_entry).unwrap()); unsafe { Self::write_plt_entry_bytes(plt_entry, got_entry); From 03c0e7e67811095f78927dbd1f3b070916ee528c Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 18:58:40 +0100 Subject: [PATCH 07/12] Rustfmt --- cranelift/simplejit/src/backend.rs | 26 ++++++++++++++++---------- 1 file changed, 16 insertions(+), 10 deletions(-) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 38b61c04d1f9..bb7ddef6e1d0 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -568,11 +568,14 @@ impl<'simple_jit_backend> Module for SimpleJITModule { unsafe { std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); } - self.compiled_functions[id].as_ref().unwrap().perform_relocations( - |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), - |name| self.get_got_address(name), - |name| self.get_plt_address(name), - ); + self.compiled_functions[id] + .as_ref() + .unwrap() + .perform_relocations( + |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), + |name| self.get_got_address(name), + |name| self.get_plt_address(name), + ); Ok(ModuleCompiledFunction { size: code_size }) } @@ -618,11 +621,14 @@ impl<'simple_jit_backend> Module for SimpleJITModule { unsafe { std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); } - self.compiled_functions[id].as_ref().unwrap().perform_relocations( - |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), - |name| self.get_got_address(name), - |name| self.get_plt_address(name), - ); + self.compiled_functions[id] + .as_ref() + .unwrap() + .perform_relocations( + |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), + |name| self.get_got_address(name), + |name| self.get_plt_address(name), + ); Ok(ModuleCompiledFunction { size: total_size }) } From 86d3dc9510bd98de6bd9cc6793d2fc8ee5d17fa1 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 19:29:05 +0100 Subject: [PATCH 08/12] Add prepare_for_function_redefine --- Cargo.lock | 1 + cranelift/module/src/module.rs | 94 ++++++++++++++++++++++++++++++ cranelift/simplejit/Cargo.toml | 1 + cranelift/simplejit/src/backend.rs | 22 +++++++ 4 files changed, 118 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 7e22a7d85aaa..36dc8c1618a1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -477,6 +477,7 @@ dependencies = [ name = "cranelift-simplejit" version = "0.68.0" dependencies = [ + "anyhow", "cranelift", "cranelift-codegen", "cranelift-entity", diff --git a/cranelift/module/src/module.rs b/cranelift/module/src/module.rs index eb4e668b98bc..2af5000568d2 100644 --- a/cranelift/module/src/module.rs +++ b/cranelift/module/src/module.rs @@ -490,3 +490,97 @@ pub trait Module { /// Define a data object, producing the data contents from the given `DataContext`. fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()>; } + +impl Module for &mut M { + fn isa(&self) -> &dyn isa::TargetIsa { + (**self).isa() + } + + fn declarations(&self) -> &ModuleDeclarations { + (**self).declarations() + } + + fn get_name(&self, name: &str) -> Option { + (**self).get_name(name) + } + + fn target_config(&self) -> isa::TargetFrontendConfig { + (**self).target_config() + } + + fn make_context(&self) -> Context { + (**self).make_context() + } + + fn clear_context(&self, ctx: &mut Context) { + (**self).clear_context(ctx) + } + + fn make_signature(&self) -> ir::Signature { + (**self).make_signature() + } + + fn clear_signature(&self, sig: &mut ir::Signature) { + (**self).clear_signature(sig) + } + + fn declare_function( + &mut self, + name: &str, + linkage: Linkage, + signature: &ir::Signature, + ) -> ModuleResult { + (**self).declare_function(name, linkage, signature) + } + + fn declare_data( + &mut self, + name: &str, + linkage: Linkage, + writable: bool, + tls: bool, + ) -> ModuleResult { + (**self).declare_data(name, linkage, writable, tls) + } + + fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef { + (**self).declare_func_in_func(func, in_func) + } + + fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { + (**self).declare_data_in_func(data, func) + } + + fn declare_func_in_data(&self, func: FuncId, ctx: &mut DataContext) -> ir::FuncRef { + (**self).declare_func_in_data(func, ctx) + } + + fn declare_data_in_data(&self, data: DataId, ctx: &mut DataContext) -> ir::GlobalValue { + (**self).declare_data_in_data(data, ctx) + } + + fn define_function( + &mut self, + func: FuncId, + ctx: &mut Context, + trap_sink: &mut TS, + ) -> ModuleResult + where + TS: binemit::TrapSink, + { + (**self).define_function(func, ctx, trap_sink) + } + + fn define_function_bytes( + &mut self, + func: FuncId, + bytes: &[u8], + relocs: &[RelocRecord], + ) -> ModuleResult { + (**self).define_function_bytes(func, bytes, relocs) + } + + fn define_data(&mut self, data: DataId, data_ctx: &DataContext) -> ModuleResult<()> { + (**self).define_data(data, data_ctx) + } +} diff --git a/cranelift/simplejit/Cargo.toml b/cranelift/simplejit/Cargo.toml index 9c7bea2c3dd7..ea8165035a42 100644 --- a/cranelift/simplejit/Cargo.toml +++ b/cranelift/simplejit/Cargo.toml @@ -14,6 +14,7 @@ cranelift-module = { path = "../module", version = "0.68.0" } cranelift-native = { path = "../native", version = "0.68.0" } cranelift-codegen = { path = "../codegen", version = "0.68.0", default-features = false, features = ["std"] } cranelift-entity = { path = "../entity", version = "0.68.0" } +anyhow = "1.0" region = "2.2.0" libc = { version = "0.2.42" } errno = "0.2.4" diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index bb7ddef6e1d0..2c90157875e5 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -393,6 +393,28 @@ impl SimpleJITModule { data_objects_to_finalize: Vec::new(), } } + + /// Allow a single future `define_function` on a previously defined function. This allows for + /// hot code swapping and lazy compilation of functions. + pub fn prepare_for_function_redefine(&mut self, func_id: FuncId) -> ModuleResult<()> { + let decl = self.declarations.get_function_decl(func_id); + if !decl.linkage.is_definable() { + return Err(ModuleError::InvalidImportDefinition(decl.name.clone())); + } + + if self.compiled_functions[func_id].is_none() { + return Err(ModuleError::Backend(anyhow::anyhow!( + "Tried to redefine not yet defined function {}", + decl.name + ))); + } + + self.compiled_functions[func_id] = None; + + // FIXME return some kind of handle that allows for deallocating the function + + Ok(()) + } } impl<'simple_jit_backend> Module for SimpleJITModule { From 8a35cbaf0d5c99dd408b0b8b51af26a800a509c4 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 12 Nov 2020 19:49:42 +0100 Subject: [PATCH 09/12] Enable PIC in SimpleJITBuilder::new --- cranelift/simplejit/src/backend.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 2c90157875e5..71dd5c07de06 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -51,6 +51,7 @@ impl SimpleJITBuilder { // which might not reach all definitions; we can't handle that here, so // we require long-range relocation types. flag_builder.set("use_colocated_libcalls", "false").unwrap(); + flag_builder.set("is_pic", "true").unwrap(); let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { panic!("host machine is not supported: {}", msg); }); From d777ec675ca77463f6a04320e76d5026e2bd6bc4 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Fri, 13 Nov 2020 09:28:51 +0100 Subject: [PATCH 10/12] Transparently change non-PLT libcall relocations to PLT relocations --- cranelift/simplejit/src/backend.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index 71dd5c07de06..eb6610907cf1 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -595,7 +595,18 @@ impl<'simple_jit_backend> Module for SimpleJITModule { .as_ref() .unwrap() .perform_relocations( - |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), + |name| match *name { + ir::ExternalName::User { .. } => { + unreachable!("non GOT or PLT relocation in function {} to {}", id, name) + } + 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), + }, |name| self.get_got_address(name), |name| self.get_plt_address(name), ); From 69d041faf17db7c396a928704388e5a557770e34 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 3 Dec 2020 18:49:18 +0100 Subject: [PATCH 11/12] Restore support for non-pic code in SimpleJIT --- cranelift/simplejit/src/backend.rs | 157 +++++++++++++++++++++-------- 1 file changed, 116 insertions(+), 41 deletions(-) diff --git a/cranelift/simplejit/src/backend.rs b/cranelift/simplejit/src/backend.rs index eb6610907cf1..67ba0f8a37b5 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -36,6 +36,7 @@ pub struct SimpleJITBuilder { isa: Box, symbols: HashMap, libcall_names: Box String + Send + Sync>, + hotswap_enabled: bool, } impl SimpleJITBuilder { @@ -75,12 +76,12 @@ impl SimpleJITBuilder { isa: Box, libcall_names: Box String + Send + Sync>, ) -> Self { - assert!(isa.flags().is_pic(), "SimpleJIT requires PIC code"); let symbols = HashMap::new(); Self { isa, symbols, libcall_names, + hotswap_enabled: false, } } @@ -119,6 +120,13 @@ impl SimpleJITBuilder { } self } + + /// Enable or disable hotswap support. See [`SimpleJITModule::prepare_for_function_redefine`] + /// for more information. + pub fn hotswap(&mut self, enabled: bool) -> &mut Self { + self.hotswap_enabled = enabled; + self + } } /// A `SimpleJITModule` implements `Module` and emits code and data into memory where it can be @@ -127,6 +135,7 @@ impl SimpleJITBuilder { /// See the `SimpleJITBuilder` for a convenient way to construct `SimpleJITModule` instances. pub struct SimpleJITModule { isa: Box, + hotswap_enabled: bool, symbols: HashMap, libcall_names: Box String>, memory: MemoryHandle, @@ -138,6 +147,7 @@ pub struct SimpleJITModule { libcall_plt_entries: HashMap>, compiled_functions: SecondaryMap>, compiled_data_objects: SecondaryMap>, + functions_to_finalize: Vec, data_objects_to_finalize: Vec, } @@ -189,7 +199,18 @@ impl SimpleJITModule { match *name { ir::ExternalName::User { .. } => { let (name, linkage) = if ModuleDeclarations::is_function(name) { - return self.get_plt_address(name); + if self.hotswap_enabled { + return self.get_plt_address(name); + } else { + let func_id = FuncId::from_name(name); + match &self.compiled_functions[func_id] { + Some(compiled) => return compiled.ptr, + None => { + let decl = self.declarations.get_function_decl(func_id); + (&decl.name, decl.linkage) + } + } + } } else { let data_id = DataId::from_name(name); match &self.compiled_data_objects[data_id] { @@ -270,6 +291,10 @@ impl SimpleJITModule { /// 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]; + assert!( + !self.functions_to_finalize.iter().any(|x| *x == func_id), + "function not yet finalized" + ); info.as_ref() .expect("function must be compiled before it can be finalized") .ptr @@ -313,6 +338,19 @@ impl SimpleJITModule { /// Use `get_finalized_function` and `get_finalized_data` to obtain the final /// artifacts. pub fn finalize_definitions(&mut self) { + for func in std::mem::take(&mut self.functions_to_finalize) { + let decl = self.declarations.get_function_decl(func); + assert!(decl.linkage.is_definable()); + let func = self.compiled_functions[func] + .as_ref() + .expect("function must be compiled before it can be finalized"); + func.perform_relocations( + |name| self.get_address(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) { let decl = self.declarations.get_data_decl(data); assert!(decl.linkage.is_definable()); @@ -333,6 +371,13 @@ impl SimpleJITModule { /// Create a new `SimpleJITModule`. pub fn new(builder: SimpleJITBuilder) -> Self { + if builder.hotswap_enabled { + assert!( + builder.isa.flags().is_pic(), + "Hotswapping requires PIC code" + ); + } + let mut memory = MemoryHandle { code: Memory::new(), readonly: Memory::new(), @@ -342,8 +387,13 @@ 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() { + // Pre-create a GOT and PLT entry for each libcall. + let all_libcalls = if builder.isa.flags().is_pic() { + ir::LibCall::all_libcalls() + } else { + &[] // Not PIC, so no GOT and PLT entries necessary + }; + for &libcall in all_libcalls { let got_entry = memory .writable .allocate( @@ -380,6 +430,7 @@ impl SimpleJITModule { Self { isa: builder.isa, + hotswap_enabled: builder.hotswap_enabled, symbols: builder.symbols, libcall_names: builder.libcall_names, memory, @@ -391,13 +442,17 @@ impl SimpleJITModule { libcall_plt_entries, compiled_functions: SecondaryMap::new(), compiled_data_objects: SecondaryMap::new(), + functions_to_finalize: Vec::new(), data_objects_to_finalize: Vec::new(), } } /// Allow a single future `define_function` on a previously defined function. This allows for /// hot code swapping and lazy compilation of functions. + /// + /// This requires hotswap support to be enabled first using [`SimpleJITBuilder::hotswap`]. pub fn prepare_for_function_redefine(&mut self, func_id: FuncId) -> ModuleResult<()> { + assert!(self.hotswap_enabled, "Hotswap support is not enabled"); let decl = self.declarations.get_function_decl(func_id); if !decl.linkage.is_definable() { return Err(ModuleError::InvalidImportDefinition(decl.name.clone())); @@ -436,7 +491,7 @@ impl<'simple_jit_backend> Module for SimpleJITModule { let (id, _decl) = self .declarations .declare_function(name, linkage, signature)?; - if self.function_got_entries[id].is_none() { + if self.function_got_entries[id].is_none() && self.isa.flags().is_pic() { let got_entry = self .memory .writable @@ -482,7 +537,7 @@ impl<'simple_jit_backend> Module for SimpleJITModule { let (id, _decl) = self .declarations .declare_data(name, linkage, writable, tls)?; - if self.data_object_got_entries[id].is_none() { + if self.data_object_got_entries[id].is_none() && self.isa.flags().is_pic() { let got_entry = self .memory .writable @@ -509,10 +564,11 @@ impl<'simple_jit_backend> Module for SimpleJITModule { fn declare_func_in_func(&self, func: FuncId, in_func: &mut ir::Function) -> ir::FuncRef { let decl = self.declarations.get_function_decl(func); let signature = in_func.import_signature(decl.signature.clone()); + let colocated = !self.hotswap_enabled && decl.linkage.is_final(); in_func.import_function(ir::ExtFuncData { name: ir::ExternalName::user(0, func.as_u32()), signature, - colocated: false, + colocated, }) } @@ -521,10 +577,11 @@ impl<'simple_jit_backend> Module for SimpleJITModule { /// TODO: Same as above. fn declare_data_in_func(&self, data: DataId, func: &mut ir::Function) -> ir::GlobalValue { let decl = self.declarations.get_data_decl(data); + let colocated = !self.hotswap_enabled && decl.linkage.is_final(); func.create_global_value(ir::GlobalValueData::Symbol { name: ir::ExternalName::user(1, data.as_u32()), offset: ir::immediates::Imm64::new(0), - colocated: false, + colocated, tls: decl.tls, }) } @@ -588,28 +645,36 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: reloc_sink.relocs, }); - unsafe { - std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); + + if self.isa.flags().is_pic() { + unsafe { + std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); + } + } + + if self.hotswap_enabled { + self.compiled_functions[id] + .as_ref() + .unwrap() + .perform_relocations( + |name| match *name { + ir::ExternalName::User { .. } => { + unreachable!("non GOT or PLT relocation in function {} to {}", id, name) + } + 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), + }, + |name| self.get_got_address(name), + |name| self.get_plt_address(name), + ); + } else { + self.functions_to_finalize.push(id); } - self.compiled_functions[id] - .as_ref() - .unwrap() - .perform_relocations( - |name| match *name { - ir::ExternalName::User { .. } => { - unreachable!("non GOT or PLT relocation in function {} to {}", id, name) - } - 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), - }, - |name| self.get_got_address(name), - |name| self.get_plt_address(name), - ); Ok(ModuleCompiledFunction { size: code_size }) } @@ -652,17 +717,25 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: relocs.to_vec(), }); - unsafe { - std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); + + if self.isa.flags().is_pic() { + unsafe { + std::ptr::write(self.function_got_entries[id].unwrap().as_ptr(), ptr); + } + } + + if self.hotswap_enabled { + self.compiled_functions[id] + .as_ref() + .unwrap() + .perform_relocations( + |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), + |name| self.get_got_address(name), + |name| self.get_plt_address(name), + ); + } else { + self.functions_to_finalize.push(id); } - self.compiled_functions[id] - .as_ref() - .unwrap() - .perform_relocations( - |name| unreachable!("non GOT or PLT relocation in function {} to {}", id, name), - |name| self.get_got_address(name), - |name| self.get_plt_address(name), - ); Ok(ModuleCompiledFunction { size: total_size }) } @@ -727,8 +800,10 @@ impl<'simple_jit_backend> Module for SimpleJITModule { self.compiled_data_objects[id] = Some(CompiledBlob { ptr, size, relocs }); self.data_objects_to_finalize.push(id); - unsafe { - std::ptr::write(self.data_object_got_entries[id].unwrap().as_ptr(), ptr); + if self.isa.flags().is_pic() { + unsafe { + std::ptr::write(self.data_object_got_entries[id].unwrap().as_ptr(), ptr); + } } Ok(()) From 937a3fde4000aa69e7f8c9bfff1261da4220d452 Mon Sep 17 00:00:00 2001 From: bjorn3 Date: Thu, 3 Dec 2020 18:57:03 +0100 Subject: [PATCH 12/12] Fix simplejit tests for x64 by disabling is_pic --- .../simplejit/examples/simplejit-minimal.rs | 12 ++++++- cranelift/simplejit/tests/basic.rs | 32 +++++++++++++++++-- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/cranelift/simplejit/examples/simplejit-minimal.rs b/cranelift/simplejit/examples/simplejit-minimal.rs index ae06c07b95d5..7274c4d5e75c 100644 --- a/cranelift/simplejit/examples/simplejit-minimal.rs +++ b/cranelift/simplejit/examples/simplejit-minimal.rs @@ -1,12 +1,22 @@ use cranelift::prelude::*; use cranelift_codegen::binemit::NullTrapSink; +use cranelift_codegen::settings::{self, Configurable}; use cranelift_module::{default_libcall_names, Linkage, Module}; use cranelift_simplejit::{SimpleJITBuilder, SimpleJITModule}; use std::mem; fn main() { + let mut flag_builder = settings::builder(); + flag_builder.set("use_colocated_libcalls", "false").unwrap(); + // FIXME set back to true once the x64 backend supports it. + flag_builder.set("is_pic", "false").unwrap(); + let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { + panic!("host machine is not supported: {}", msg); + }); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut module: SimpleJITModule = - SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names())); + SimpleJITModule::new(SimpleJITBuilder::with_isa(isa, default_libcall_names())); + let mut ctx = module.make_context(); let mut func_ctx = FunctionBuilderContext::new(); diff --git a/cranelift/simplejit/tests/basic.rs b/cranelift/simplejit/tests/basic.rs index 0b9176543336..fc3f5b0056cb 100644 --- a/cranelift/simplejit/tests/basic.rs +++ b/cranelift/simplejit/tests/basic.rs @@ -1,6 +1,7 @@ use cranelift_codegen::binemit::NullTrapSink; use cranelift_codegen::ir::*; use cranelift_codegen::isa::CallConv; +use cranelift_codegen::settings::{self, Configurable}; use cranelift_codegen::{ir::types::I16, Context}; use cranelift_entity::EntityRef; use cranelift_frontend::*; @@ -9,8 +10,17 @@ use cranelift_simplejit::*; #[test] fn error_on_incompatible_sig_in_declare_function() { + let mut flag_builder = settings::builder(); + flag_builder.set("use_colocated_libcalls", "false").unwrap(); + // FIXME set back to true once the x64 backend supports it. + flag_builder.set("is_pic", "false").unwrap(); + let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { + panic!("host machine is not supported: {}", msg); + }); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut module: SimpleJITModule = - SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names())); + SimpleJITModule::new(SimpleJITBuilder::with_isa(isa, default_libcall_names())); + let mut sig = Signature { params: vec![AbiParam::new(types::I64)], returns: vec![], @@ -58,8 +68,16 @@ fn define_simple_function(module: &mut SimpleJITModule) -> FuncId { #[test] #[should_panic(expected = "Result::unwrap()` on an `Err` value: DuplicateDefinition(\"abc\")")] fn panic_on_define_after_finalize() { + let mut flag_builder = settings::builder(); + flag_builder.set("use_colocated_libcalls", "false").unwrap(); + // FIXME set back to true once the x64 backend supports it. + flag_builder.set("is_pic", "false").unwrap(); + let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { + panic!("host machine is not supported: {}", msg); + }); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut module: SimpleJITModule = - SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names())); + SimpleJITModule::new(SimpleJITBuilder::with_isa(isa, default_libcall_names())); define_simple_function(&mut module); define_simple_function(&mut module); @@ -140,8 +158,16 @@ fn switch_error() { #[test] fn libcall_function() { + let mut flag_builder = settings::builder(); + flag_builder.set("use_colocated_libcalls", "false").unwrap(); + // FIXME set back to true once the x64 backend supports it. + flag_builder.set("is_pic", "false").unwrap(); + let isa_builder = cranelift_native::builder().unwrap_or_else(|msg| { + panic!("host machine is not supported: {}", msg); + }); + let isa = isa_builder.finish(settings::Flags::new(flag_builder)); let mut module: SimpleJITModule = - SimpleJITModule::new(SimpleJITBuilder::new(default_libcall_names())); + SimpleJITModule::new(SimpleJITBuilder::with_isa(isa, default_libcall_names())); let sig = Signature { params: vec![],