diff --git a/Cargo.lock b/Cargo.lock index b5e646f2a6..acb6cebb70 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -531,6 +531,7 @@ dependencies = [ name = "cranelift-simplejit" version = "0.68.0" dependencies = [ + "anyhow", "cranelift", "cranelift-codegen", "cranelift-entity", diff --git a/cranelift/codegen/src/ir/libcall.rs b/cranelift/codegen/src/ir/libcall.rs index 9dc134e480..632b04a4c3 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/module/src/module.rs b/cranelift/module/src/module.rs index eb4e668b98..2af5000568 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 9c7bea2c3d..ea8165035a 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/examples/simplejit-minimal.rs b/cranelift/simplejit/examples/simplejit-minimal.rs index ae06c07b95..7274c4d5e7 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/src/backend.rs b/cranelift/simplejit/src/backend.rs index d211eb5feb..67ba0f8a37 100644 --- a/cranelift/simplejit/src/backend.rs +++ b/cranelift/simplejit/src/backend.rs @@ -18,10 +18,11 @@ 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; +use std::ptr::NonNull; use target_lexicon::PointerWidth; #[cfg(windows)] use winapi; @@ -35,6 +36,7 @@ pub struct SimpleJITBuilder { isa: Box, symbols: HashMap, libcall_names: Box String + Send + Sync>, + hotswap_enabled: bool, } impl SimpleJITBuilder { @@ -50,6 +52,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); }); @@ -73,12 +76,12 @@ impl SimpleJITBuilder { isa: Box, libcall_names: Box String + Send + Sync>, ) -> Self { - debug_assert!(!isa.flags().is_pic(), "SimpleJIT requires non-PIC code"); let symbols = HashMap::new(); Self { isa, symbols, libcall_names, + hotswap_enabled: false, } } @@ -117,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 @@ -125,10 +135,16 @@ 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, 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, @@ -164,16 +180,35 @@ impl SimpleJITModule { .or_else(|| lookup_with_dlsym(name)) } - fn get_definition(&self, name: &ir::ExternalName) -> *const u8 { + 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 { .. } => { 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) + 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 { @@ -203,10 +238,60 @@ 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), + } + } + + 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]; - debug_assert!( + assert!( !self.functions_to_finalize.iter().any(|x| *x == func_id), "function not yet finalized" ); @@ -218,7 +303,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 +340,28 @@ 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()); + 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_definition(name)); + 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); - 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), + |name| self.get_plt_address(name), + ); } // Now that we're done patching, prepare the memory for execution! @@ -277,24 +371,106 @@ impl SimpleJITModule { /// Create a new `SimpleJITModule`. pub fn new(builder: SimpleJITBuilder) -> Self { - let memory = MemoryHandle { + if builder.hotswap_enabled { + assert!( + builder.isa.flags().is_pic(), + "Hotswapping requires PIC code" + ); + } + + let mut memory = MemoryHandle { code: Memory::new(), readonly: Memory::new(), writable: Memory::new(), }; + let mut libcall_got_entries = HashMap::new(); + let mut libcall_plt_entries = HashMap::new(); + + // 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( + 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); + let addr = if let Some(addr) = builder + .symbols + .get(&sym) + .copied() + .or_else(|| lookup_with_dlsym(&sym)) + { + 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); + } + } + Self { isa: builder.isa, + hotswap_enabled: builder.hotswap_enabled, symbols: builder.symbols, libcall_names: builder.libcall_names, 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(), 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())); + } + + 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 { @@ -315,6 +491,38 @@ 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() && self.isa.flags().is_pic() { + 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); + } + let plt_entry = self + .memory + .code + .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); + } + } Ok(id) } @@ -329,9 +537,65 @@ 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() && self.isa.flags().is_pic() { + 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()); + 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, + }) + } + + /// 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); + 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, + 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 +645,36 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: reloc_sink.relocs, }); - self.functions_to_finalize.push(id); + + 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); + } Ok(ModuleCompiledFunction { size: code_size }) } @@ -424,7 +717,25 @@ impl<'simple_jit_backend> Module for SimpleJITModule { size, relocs: relocs.to_vec(), }); - self.functions_to_finalize.push(id); + + 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); + } Ok(ModuleCompiledFunction { size: total_size }) } @@ -489,6 +800,11 @@ impl<'simple_jit_backend> Module for SimpleJITModule { self.compiled_data_objects[id] = Some(CompiledBlob { ptr, size, relocs }); self.data_objects_to_finalize.push(id); + if self.isa.flags().is_pic() { + 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 dc8563dabd..d44497ae9e 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,12 @@ 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, + get_plt_entry: impl Fn(&ExternalName) -> *const u8, + ) { use std::ptr::write_unaligned; for &RelocRecord { @@ -21,33 +27,51 @@ impl CompiledBlob { } in &self.relocs { debug_assert!((offset as usize) < self.size); - let at = unsafe { self.ptr.offset(offset as isize) }; - let base = get_definition(name); - // TODO: Handle overflow. - let what = unsafe { base.offset(addend as isize) }; + let at = unsafe { self.ptr.offset(isize::try_from(offset).unwrap()) }; match reloc { Reloc::Abs4 => { - // TODO: Handle overflow. + 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, what as u32) + 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, 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 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 => { + 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 => { + 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) }; } - Reloc::X86GOTPCRel4 | Reloc::X86CallPLTRel4 => panic!("unexpected PIC relocation"), _ => unimplemented!(), } } diff --git a/cranelift/simplejit/tests/basic.rs b/cranelift/simplejit/tests/basic.rs index 0b91765433..fc3f5b0056 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![],