Merge pull request #2390 from bjorn3/more_simplejit_refactors

More SimpleJIT refactorings
This commit is contained in:
Pat Hickey
2020-11-11 11:16:04 -08:00
committed by GitHub
21 changed files with 263 additions and 446 deletions

View File

@@ -74,9 +74,6 @@ impl<'a> MemoryCodeSink<'a> {
/// A trait for receiving relocations for code that is emitted directly into memory. /// A trait for receiving relocations for code that is emitted directly into memory.
pub trait RelocSink { pub trait RelocSink {
/// Add a relocation referencing a block at the current offset.
fn reloc_block(&mut self, _: CodeOffset, _: Reloc, _: CodeOffset);
/// Add a relocation referencing an external symbol at the current offset. /// Add a relocation referencing an external symbol at the current offset.
fn reloc_external( fn reloc_external(
&mut self, &mut self,
@@ -138,11 +135,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
self.write(x); self.write(x);
} }
fn reloc_block(&mut self, rel: Reloc, block_offset: CodeOffset) {
let ofs = self.offset();
self.relocs.reloc_block(ofs, rel, block_offset);
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
srcloc: SourceLoc, srcloc: SourceLoc,
@@ -204,7 +196,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
pub struct NullRelocSink {} pub struct NullRelocSink {}
impl RelocSink for NullRelocSink { impl RelocSink for NullRelocSink {
fn reloc_block(&mut self, _: CodeOffset, _: Reloc, _: CodeOffset) {}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
_: CodeOffset, _: CodeOffset,

View File

@@ -140,9 +140,6 @@ pub trait CodeSink {
/// Add 8 bytes to the code section. /// Add 8 bytes to the code section.
fn put8(&mut self, _: u64); fn put8(&mut self, _: u64);
/// Add a relocation referencing a block at the current offset.
fn reloc_block(&mut self, _: Reloc, _: CodeOffset);
/// Add a relocation referencing an external symbol plus the addend at the current offset. /// Add a relocation referencing an external symbol plus the addend at the current offset.
fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend); fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend);

View File

@@ -59,8 +59,6 @@ impl CodeSink for TestCodeSink {
} }
} }
fn reloc_block(&mut self, _rel: Reloc, _block_offset: CodeOffset) {}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
_srcloc: SourceLoc, _srcloc: SourceLoc,

View File

@@ -72,10 +72,6 @@ impl binemit::CodeSink for TextSink {
self.offset += 8; self.offset += 8;
} }
fn reloc_block(&mut self, reloc: binemit::Reloc, block_offset: binemit::CodeOffset) {
write!(self.text, "{}({}) ", reloc, block_offset).unwrap();
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
_srcloc: ir::SourceLoc, _srcloc: ir::SourceLoc,

View File

@@ -109,7 +109,6 @@ impl binemit::CodeSink for SizeSink {
self.offset += 8; self.offset += 8;
} }
fn reloc_block(&mut self, _reloc: binemit::Reloc, _block_offset: binemit::CodeOffset) {}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
_srcloc: ir::SourceLoc, _srcloc: ir::SourceLoc,

View File

@@ -106,7 +106,6 @@ impl binemit::CodeSink for RodataSink {
} }
} }
fn reloc_block(&mut self, _reloc: binemit::Reloc, _block_offset: binemit::CodeOffset) {}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
_: ir::SourceLoc, _: ir::SourceLoc,

View File

@@ -79,7 +79,6 @@ impl CodeSink for TestStackMapsSink {
self.offset += 8; self.offset += 8;
} }
fn reloc_block(&mut self, _: Reloc, _: CodeOffset) {}
fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend) {} fn reloc_external(&mut self, _: SourceLoc, _: Reloc, _: &ExternalName, _: Addend) {}
fn reloc_constant(&mut self, _: Reloc, _: ConstantOffset) {} fn reloc_constant(&mut self, _: Reloc, _: ConstantOffset) {}
fn reloc_jt(&mut self, _: Reloc, _: JumpTable) {} fn reloc_jt(&mut self, _: Reloc, _: JumpTable) {}

View File

@@ -1,6 +1,6 @@
//! Defines `DataContext`. //! Defines `DataContext`.
use cranelift_codegen::binemit::{Addend, CodeOffset}; use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc};
use cranelift_codegen::entity::PrimaryMap; use cranelift_codegen::entity::PrimaryMap;
use cranelift_codegen::ir; use cranelift_codegen::ir;
use std::borrow::ToOwned; use std::borrow::ToOwned;
@@ -8,6 +8,8 @@ use std::boxed::Box;
use std::string::String; use std::string::String;
use std::vec::Vec; use std::vec::Vec;
use crate::RelocRecord;
/// This specifies how data is to be initialized. /// This specifies how data is to be initialized.
#[derive(PartialEq, Eq, Debug)] #[derive(PartialEq, Eq, Debug)]
pub enum Init { pub enum Init {
@@ -55,6 +57,34 @@ pub struct DataDescription {
pub align: Option<u64>, pub align: Option<u64>,
} }
impl DataDescription {
/// An iterator over all relocations of the data object.
pub fn all_relocs<'a>(
&'a self,
pointer_reloc: Reloc,
) -> impl Iterator<Item = RelocRecord> + 'a {
let func_relocs = self
.function_relocs
.iter()
.map(move |&(offset, id)| RelocRecord {
reloc: pointer_reloc,
offset,
name: self.function_decls[id].clone(),
addend: 0,
});
let data_relocs = self
.data_relocs
.iter()
.map(move |&(offset, id, addend)| RelocRecord {
reloc: pointer_reloc,
offset,
name: self.data_decls[id].clone(),
addend,
});
func_relocs.chain(data_relocs)
}
}
/// This is to data objects what cranelift_codegen::Context is to functions. /// This is to data objects what cranelift_codegen::Context is to functions.
pub struct DataContext { pub struct DataContext {
description: DataDescription, description: DataDescription,

View File

@@ -29,6 +29,18 @@ impl From<FuncId> for ir::ExternalName {
} }
} }
impl FuncId {
/// Get the `FuncId` for the function named by `name`.
pub fn from_name(name: &ir::ExternalName) -> FuncId {
if let ir::ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 0);
FuncId::from_u32(index)
} else {
panic!("unexpected ExternalName kind {}", name)
}
}
}
/// A data object identifier for use in the `Module` interface. /// A data object identifier for use in the `Module` interface.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)] #[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct DataId(u32); pub struct DataId(u32);
@@ -44,6 +56,18 @@ impl From<DataId> for ir::ExternalName {
} }
} }
impl DataId {
/// Get the `DataId` for the data object named by `name`.
pub fn from_name(name: &ir::ExternalName) -> DataId {
if let ir::ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 1);
DataId::from_u32(index)
} else {
panic!("unexpected ExternalName kind {}", name)
}
}
}
/// Linkage refers to where an entity is defined and who can see it. /// Linkage refers to where an entity is defined and who can see it.
#[derive(Copy, Clone, Debug, PartialEq, Eq)] #[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Linkage { pub enum Linkage {
@@ -162,9 +186,6 @@ pub enum ModuleError {
/// Indicates an identifier was defined, but was declared as an import /// Indicates an identifier was defined, but was declared as an import
#[error("Invalid to define identifier declared as an import: {0}")] #[error("Invalid to define identifier declared as an import: {0}")]
InvalidImportDefinition(String), InvalidImportDefinition(String),
/// Indicates a too-long function was defined
#[error("Function {0} exceeds the maximum function size")]
FunctionTooLarge(String),
/// Wraps a `cranelift-codegen` error /// Wraps a `cranelift-codegen` error
#[error("Compilation error: {0}")] #[error("Compilation error: {0}")]
Compilation(#[from] CodegenError), Compilation(#[from] CodegenError),
@@ -217,21 +238,10 @@ impl ModuleDeclarations {
self.functions.iter() self.functions.iter()
} }
/// Get the `FuncId` for the function named by `name`. /// Return whether `name` names a function, rather than a data object.
pub fn get_function_id(&self, name: &ir::ExternalName) -> FuncId { pub fn is_function(name: &ir::ExternalName) -> bool {
if let ir::ExternalName::User { namespace, index } = *name { if let ir::ExternalName::User { namespace, .. } = *name {
debug_assert_eq!(namespace, 0); namespace == 0
FuncId::from_u32(index)
} else {
panic!("unexpected ExternalName kind {}", name)
}
}
/// Get the `DataId` for the data object named by `name`.
pub fn get_data_id(&self, name: &ir::ExternalName) -> DataId {
if let ir::ExternalName::User { namespace, index } = *name {
debug_assert_eq!(namespace, 1);
DataId::from_u32(index)
} else { } else {
panic!("unexpected ExternalName kind {}", name) panic!("unexpected ExternalName kind {}", name)
} }
@@ -252,15 +262,6 @@ impl ModuleDeclarations {
&self.data_objects[data_id] &self.data_objects[data_id]
} }
/// Return whether `name` names a function, rather than a data object.
pub fn is_function(&self, name: &ir::ExternalName) -> bool {
if let ir::ExternalName::User { namespace, .. } = *name {
namespace == 0
} else {
panic!("unexpected ExternalName kind {}", name)
}
}
/// Declare a function in this module. /// Declare a function in this module.
pub fn declare_function( pub fn declare_function(
&mut self, &mut self,

View File

@@ -1,12 +1,13 @@
//! Defines `ObjectModule`. //! Defines `ObjectModule`.
use anyhow::anyhow; use anyhow::anyhow;
use cranelift_codegen::binemit::{
Addend, CodeInfo, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink,
};
use cranelift_codegen::entity::SecondaryMap; use cranelift_codegen::entity::SecondaryMap;
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{self, ir}; use cranelift_codegen::{self, ir};
use cranelift_codegen::{
binemit::{Addend, CodeInfo, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink},
CodegenError,
};
use cranelift_module::{ use cranelift_module::{
DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction, DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleDeclarations, ModuleError, ModuleResult, RelocRecord, ModuleDeclarations, ModuleError, ModuleResult, RelocRecord,
@@ -268,17 +269,16 @@ impl Module for ObjectModule {
relocs: &[RelocRecord], relocs: &[RelocRecord],
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {} with bytes", func_id); info!("defining function {} with bytes", func_id);
let total_size: u32 = match bytes.len().try_into() {
Ok(total_size) => total_size,
_ => Err(CodegenError::CodeTooLarge)?,
};
let decl = self.declarations.get_function_decl(func_id); let decl = self.declarations.get_function_decl(func_id);
if !decl.linkage.is_definable() { if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone())); return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
} }
let total_size: u32 = match bytes.len().try_into() {
Ok(total_size) => total_size,
_ => Err(ModuleError::FunctionTooLarge(decl.name.clone()))?,
};
let &mut (symbol, ref mut defined) = self.functions[func_id].as_mut().unwrap(); let &mut (symbol, ref mut defined) = self.functions[func_id].as_mut().unwrap();
if *defined { if *defined {
return Err(ModuleError::DuplicateDefinition(decl.name.clone())); return Err(ModuleError::DuplicateDefinition(decl.name.clone()));
@@ -305,7 +305,10 @@ impl Module for ObjectModule {
}; };
if !relocs.is_empty() { if !relocs.is_empty() {
let relocs = self.process_relocs(relocs); let relocs = relocs
.iter()
.map(|record| self.process_reloc(record))
.collect();
self.relocs.push(SymbolRelocs { self.relocs.push(SymbolRelocs {
section, section,
offset, offset,
@@ -330,40 +333,24 @@ impl Module for ObjectModule {
let &DataDescription { let &DataDescription {
ref init, ref init,
ref function_decls, function_decls: _,
ref data_decls, data_decls: _,
ref function_relocs, function_relocs: _,
ref data_relocs, data_relocs: _,
ref custom_segment_section, ref custom_segment_section,
align, align,
} = data_ctx.description(); } = data_ctx.description();
let reloc_size = match self.isa.triple().pointer_width().unwrap() { let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
PointerWidth::U16 => 16, PointerWidth::U16 => unimplemented!("16bit pointers"),
PointerWidth::U32 => 32, PointerWidth::U32 => Reloc::Abs4,
PointerWidth::U64 => 64, PointerWidth::U64 => Reloc::Abs8,
}; };
let mut relocs = Vec::new(); let relocs = data_ctx
for &(offset, id) in function_relocs { .description()
relocs.push(ObjectRelocRecord { .all_relocs(pointer_reloc)
offset, .map(|record| self.process_reloc(&record))
name: function_decls[id].clone(), .collect::<Vec<_>>();
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: reloc_size,
addend: 0,
});
}
for &(offset, id, addend) in data_relocs {
relocs.push(ObjectRelocRecord {
offset,
name: data_decls[id].clone(),
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: reloc_size,
addend,
});
}
let section = if custom_segment_section.is_none() { let section = if custom_segment_section.is_none() {
let section_kind = if let Init::Zeros { .. } = *init { let section_kind = if let Init::Zeros { .. } = *init {
@@ -477,11 +464,11 @@ impl ObjectModule {
fn get_symbol(&mut self, name: &ir::ExternalName) -> SymbolId { fn get_symbol(&mut self, name: &ir::ExternalName) -> SymbolId {
match *name { match *name {
ir::ExternalName::User { .. } => { ir::ExternalName::User { .. } => {
if self.declarations.is_function(name) { if ModuleDeclarations::is_function(name) {
let id = self.declarations.get_function_id(name); let id = FuncId::from_name(name);
self.functions[id].unwrap().0 self.functions[id].unwrap().0
} else { } else {
let id = self.declarations.get_data_id(name); let id = DataId::from_name(name);
self.data_objects[id].unwrap().0 self.data_objects[id].unwrap().0
} }
} }
@@ -510,18 +497,13 @@ impl ObjectModule {
} }
} }
fn process_relocs(&self, relocs: &[RelocRecord]) -> Vec<ObjectRelocRecord> { fn process_reloc(&self, record: &RelocRecord) -> ObjectRelocRecord {
relocs
.iter()
.map(|record| {
let mut addend = record.addend; let mut addend = record.addend;
let (kind, encoding, size) = match record.reloc { let (kind, encoding, size) = match record.reloc {
Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32), Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64), Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32), Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
Reloc::X86CallPCRel4 => { Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32),
(RelocationKind::Relative, RelocationEncoding::X86Branch, 32)
}
// TODO: Get Cranelift to tell us when we can use // TODO: Get Cranelift to tell us when we can use
// R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX. // R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
Reloc::X86CallPLTRel4 => ( Reloc::X86CallPLTRel4 => (
@@ -529,9 +511,7 @@ impl ObjectModule {
RelocationEncoding::X86Branch, RelocationEncoding::X86Branch,
32, 32,
), ),
Reloc::X86GOTPCRel4 => { Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
(RelocationKind::GotRelative, RelocationEncoding::Generic, 32)
}
Reloc::ElfX86_64TlsGd => { Reloc::ElfX86_64TlsGd => {
assert_eq!( assert_eq!(
self.object.format(), self.object.format(),
@@ -571,8 +551,6 @@ impl ObjectModule {
size, size,
addend, addend,
} }
})
.collect()
} }
} }
@@ -644,10 +622,6 @@ struct ObjectRelocSink {
} }
impl RelocSink for ObjectRelocSink { impl RelocSink for ObjectRelocSink {
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
unimplemented!();
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
offset: CodeOffset, offset: CodeOffset,

View File

@@ -70,10 +70,10 @@ fn main() {
module.clear_context(&mut ctx); module.clear_context(&mut ctx);
// Perform linking. // Perform linking.
let product = module.finish(); module.finalize_definitions();
// Get a raw pointer to the generated code. // Get a raw pointer to the generated code.
let code_b = product.lookup_func(func_b); let code_b = module.get_finalized_function(func_b);
// Cast it to a rust function pointer type. // Cast it to a rust function pointer type.
let ptr_b = unsafe { mem::transmute::<_, fn() -> u32>(code_b) }; let ptr_b = unsafe { mem::transmute::<_, fn() -> u32>(code_b) };

View File

@@ -1,16 +1,17 @@
//! Defines `SimpleJITModule`. //! Defines `SimpleJITModule`.
use crate::memory::Memory; use crate::{compiled_blob::CompiledBlob, memory::Memory};
use cranelift_codegen::binemit::{
Addend, CodeInfo, CodeOffset, Reloc, RelocSink, StackMap, StackMapSink, TrapSink,
};
use cranelift_codegen::isa::TargetIsa; use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings::Configurable; use cranelift_codegen::settings::Configurable;
use cranelift_codegen::{self, ir, settings}; use cranelift_codegen::{self, ir, settings};
use cranelift_codegen::{
binemit::{self, Addend, CodeInfo, CodeOffset, Reloc, RelocSink, TrapSink},
CodegenError,
};
use cranelift_entity::SecondaryMap; use cranelift_entity::SecondaryMap;
use cranelift_module::{ use cranelift_module::{
DataContext, DataDescription, DataId, FuncId, FuncOrDataId, Init, Linkage, Module, DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleCompiledFunction, ModuleDeclarations, ModuleError, ModuleResult, RelocRecord, ModuleDeclarations, ModuleError, ModuleResult, RelocRecord,
}; };
use cranelift_native; use cranelift_native;
#[cfg(not(windows))] #[cfg(not(windows))]
@@ -128,26 +129,12 @@ pub struct SimpleJITModule {
libcall_names: Box<dyn Fn(ir::LibCall) -> String>, libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
memory: MemoryHandle, memory: MemoryHandle,
declarations: ModuleDeclarations, declarations: ModuleDeclarations,
functions: SecondaryMap<FuncId, Option<CompiledBlob>>, compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
data_objects: SecondaryMap<DataId, Option<CompiledBlob>>, compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
functions_to_finalize: Vec<FuncId>, functions_to_finalize: Vec<FuncId>,
data_objects_to_finalize: Vec<DataId>, data_objects_to_finalize: Vec<DataId>,
} }
struct StackMapRecord {
#[allow(dead_code)]
offset: CodeOffset,
#[allow(dead_code)]
stack_map: StackMap,
}
#[derive(Clone)]
struct CompiledBlob {
ptr: *mut u8,
size: usize,
relocs: Vec<RelocRecord>,
}
/// A handle to allow freeing memory allocated by the `Module`. /// A handle to allow freeing memory allocated by the `Module`.
struct MemoryHandle { struct MemoryHandle {
code: Memory, code: Memory,
@@ -155,16 +142,7 @@ struct MemoryHandle {
writable: Memory, writable: Memory,
} }
/// A `SimpleJITProduct` allows looking up the addresses of all functions and data objects impl SimpleJITModule {
/// defined in the original module.
pub struct SimpleJITProduct {
memory: MemoryHandle,
declarations: ModuleDeclarations,
functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
}
impl SimpleJITProduct {
/// Free memory allocated for code and data segments of compiled functions. /// Free memory allocated for code and data segments of compiled functions.
/// ///
/// # Safety /// # Safety
@@ -179,29 +157,6 @@ impl SimpleJITProduct {
self.memory.writable.free_memory(); self.memory.writable.free_memory();
} }
/// Get the `FuncOrDataId` associated with the given name.
pub fn func_or_data_for_func(&self, name: &str) -> Option<FuncOrDataId> {
self.declarations.get_name(name)
}
/// Return the address of a function.
pub fn lookup_func(&self, func_id: FuncId) -> *const u8 {
self.functions[func_id]
.as_ref()
.unwrap_or_else(|| panic!("{} is not defined", func_id))
.ptr
}
/// Return the address and size of a data object.
pub fn lookup_data(&self, data_id: DataId) -> (*const u8, usize) {
let data = self.data_objects[data_id]
.as_ref()
.unwrap_or_else(|| panic!("{} is not defined", data_id));
(data.ptr, data.size)
}
}
impl SimpleJITModule {
fn lookup_symbol(&self, name: &str) -> Option<*const u8> { fn lookup_symbol(&self, name: &str) -> Option<*const u8> {
self.symbols self.symbols
.get(name) .get(name)
@@ -212,9 +167,9 @@ impl SimpleJITModule {
fn get_definition(&self, name: &ir::ExternalName) -> *const u8 { fn get_definition(&self, name: &ir::ExternalName) -> *const u8 {
match *name { match *name {
ir::ExternalName::User { .. } => { ir::ExternalName::User { .. } => {
let (name, linkage) = if self.declarations.is_function(name) { let (name, linkage) = if ModuleDeclarations::is_function(name) {
let func_id = self.declarations.get_function_id(name); let func_id = FuncId::from_name(name);
match &self.functions[func_id] { match &self.compiled_functions[func_id] {
Some(compiled) => return compiled.ptr, Some(compiled) => return compiled.ptr,
None => { None => {
let decl = self.declarations.get_function_decl(func_id); let decl = self.declarations.get_function_decl(func_id);
@@ -222,8 +177,8 @@ impl SimpleJITModule {
} }
} }
} else { } else {
let data_id = self.declarations.get_data_id(name); let data_id = DataId::from_name(name);
match &self.data_objects[data_id] { match &self.compiled_data_objects[data_id] {
Some(compiled) => return compiled.ptr, Some(compiled) => return compiled.ptr,
None => { None => {
let decl = self.declarations.get_data_decl(data_id); let decl = self.declarations.get_data_decl(data_id);
@@ -250,7 +205,7 @@ impl SimpleJITModule {
/// Returns the address of a finalized function. /// Returns the address of a finalized function.
pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 { pub fn get_finalized_function(&self, func_id: FuncId) -> *const u8 {
let info = &self.functions[func_id]; let info = &self.compiled_functions[func_id];
debug_assert!( debug_assert!(
!self.functions_to_finalize.iter().any(|x| *x == func_id), !self.functions_to_finalize.iter().any(|x| *x == func_id),
"function not yet finalized" "function not yet finalized"
@@ -262,7 +217,7 @@ impl SimpleJITModule {
/// Returns the address and size of a finalized data object. /// Returns the address and size of a finalized data object.
pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) { pub fn get_finalized_data(&self, data_id: DataId) -> (*const u8, usize) {
let info = &self.data_objects[data_id]; let info = &self.compiled_data_objects[data_id];
debug_assert!( debug_assert!(
!self.data_objects_to_finalize.iter().any(|x| *x == data_id), !self.data_objects_to_finalize.iter().any(|x| *x == data_id),
"data object not yet finalized" "data object not yet finalized"
@@ -291,95 +246,6 @@ impl SimpleJITModule {
} }
} }
fn finalize_function(&mut self, id: FuncId) {
use std::ptr::write_unaligned;
let func = self.functions[id]
.as_ref()
.expect("function must be compiled before it can be finalized");
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &func.relocs
{
debug_assert!((offset as usize) < func.size);
let at = unsafe { func.ptr.offset(offset as isize) };
let base = self.get_definition(name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
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)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32;
#[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!(),
}
}
}
fn finalize_data(&mut self, id: DataId) {
use std::ptr::write_unaligned;
let data = self.data_objects[id]
.as_ref()
.expect("data object must be compiled before it can be finalized");
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} in &data.relocs
{
debug_assert!((offset as usize) < data.size);
let at = unsafe { data.ptr.offset(offset as isize) };
let base = self.get_definition(name);
// TODO: Handle overflow.
let what = unsafe { base.offset(addend as isize) };
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)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4
| Reloc::X86CallPCRel4
| Reloc::X86GOTPCRel4
| Reloc::X86CallPLTRel4 => panic!("unexpected text relocation in data"),
_ => unimplemented!(),
}
}
}
/// Finalize all functions and data objects that are defined but not yet finalized. /// Finalize all functions and data objects that are defined but not yet finalized.
/// All symbols referenced in their bodies that are declared as needing a definition /// All symbols referenced in their bodies that are declared as needing a definition
/// must be defined by this point. /// must be defined by this point.
@@ -390,12 +256,18 @@ impl SimpleJITModule {
for func in std::mem::take(&mut self.functions_to_finalize) { for func in std::mem::take(&mut self.functions_to_finalize) {
let decl = self.declarations.get_function_decl(func); let decl = self.declarations.get_function_decl(func);
debug_assert!(decl.linkage.is_definable()); debug_assert!(decl.linkage.is_definable());
self.finalize_function(func); 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));
} }
for data in std::mem::take(&mut self.data_objects_to_finalize) { for data in std::mem::take(&mut self.data_objects_to_finalize) {
let decl = self.declarations.get_data_decl(data); let decl = self.declarations.get_data_decl(data);
debug_assert!(decl.linkage.is_definable()); debug_assert!(decl.linkage.is_definable());
self.finalize_data(data); 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));
} }
// Now that we're done patching, prepare the memory for execution! // Now that we're done patching, prepare the memory for execution!
@@ -417,8 +289,8 @@ impl SimpleJITModule {
libcall_names: builder.libcall_names, libcall_names: builder.libcall_names,
memory, memory,
declarations: ModuleDeclarations::default(), declarations: ModuleDeclarations::default(),
functions: SecondaryMap::new(), compiled_functions: SecondaryMap::new(),
data_objects: SecondaryMap::new(), compiled_data_objects: SecondaryMap::new(),
functions_to_finalize: Vec::new(), functions_to_finalize: Vec::new(),
data_objects_to_finalize: Vec::new(), data_objects_to_finalize: Vec::new(),
} }
@@ -480,11 +352,10 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone())); return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
} }
if !self.functions[id].is_none() { if !self.compiled_functions[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned())); return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
} }
self.functions_to_finalize.push(id);
let size = code_size as usize; let size = code_size as usize;
let ptr = self let ptr = self
.memory .memory
@@ -492,10 +363,8 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT) .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc."); .expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, &decl.name);
let mut reloc_sink = SimpleJITRelocSink::default(); let mut reloc_sink = SimpleJITRelocSink::default();
let mut stack_map_sink = SimpleJITStackMapSink::default(); let mut stack_map_sink = binemit::NullStackMapSink {};
unsafe { unsafe {
ctx.emit_to_memory( ctx.emit_to_memory(
&*self.isa, &*self.isa,
@@ -506,11 +375,13 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
) )
}; };
self.functions[id] = Some(CompiledBlob { self.record_function_for_perf(ptr, size, &decl.name);
self.compiled_functions[id] = Some(CompiledBlob {
ptr, ptr,
size, size,
relocs: reloc_sink.relocs, relocs: reloc_sink.relocs,
}); });
self.functions_to_finalize.push(id);
Ok(ModuleCompiledFunction { size: code_size }) Ok(ModuleCompiledFunction { size: code_size })
} }
@@ -521,21 +392,21 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
bytes: &[u8], bytes: &[u8],
relocs: &[RelocRecord], relocs: &[RelocRecord],
) -> ModuleResult<ModuleCompiledFunction> { ) -> ModuleResult<ModuleCompiledFunction> {
info!("defining function {} with bytes", id);
let total_size: u32 = match bytes.len().try_into() {
Ok(total_size) => total_size,
_ => Err(CodegenError::CodeTooLarge)?,
};
let decl = self.declarations.get_function_decl(id); let decl = self.declarations.get_function_decl(id);
if !decl.linkage.is_definable() { if !decl.linkage.is_definable() {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone())); return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
} }
let total_size: u32 = match bytes.len().try_into() { if !self.compiled_functions[id].is_none() {
Ok(total_size) => total_size,
_ => Err(ModuleError::FunctionTooLarge(decl.name.clone()))?,
};
if !self.functions[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned())); return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
} }
self.functions_to_finalize.push(id);
let size = bytes.len(); let size = bytes.len();
let ptr = self let ptr = self
.memory .memory
@@ -543,17 +414,17 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT) .allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc."); .expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, &decl.name);
unsafe { unsafe {
ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size); ptr::copy_nonoverlapping(bytes.as_ptr(), ptr, size);
} }
self.functions[id] = Some(CompiledBlob { self.record_function_for_perf(ptr, size, &decl.name);
self.compiled_functions[id] = Some(CompiledBlob {
ptr, ptr,
size, size,
relocs: relocs.to_vec(), relocs: relocs.to_vec(),
}); });
self.functions_to_finalize.push(id);
Ok(ModuleCompiledFunction { size: total_size }) Ok(ModuleCompiledFunction { size: total_size })
} }
@@ -564,20 +435,18 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
return Err(ModuleError::InvalidImportDefinition(decl.name.clone())); return Err(ModuleError::InvalidImportDefinition(decl.name.clone()));
} }
if !self.data_objects[id].is_none() { if !self.compiled_data_objects[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned())); return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
} }
assert!(!decl.tls, "SimpleJIT doesn't yet support TLS"); assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
self.data_objects_to_finalize.push(id);
let &DataDescription { let &DataDescription {
ref init, ref init,
ref function_decls, function_decls: _,
ref data_decls, data_decls: _,
ref function_relocs, function_relocs: _,
ref data_relocs, data_relocs: _,
custom_segment_section: _, custom_segment_section: _,
align, align,
} = data.description(); } = data.description();
@@ -608,55 +477,23 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
} }
} }
let reloc = match self.isa.triple().pointer_width().unwrap() { let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
PointerWidth::U16 => panic!(), PointerWidth::U16 => panic!(),
PointerWidth::U32 => Reloc::Abs4, PointerWidth::U32 => Reloc::Abs4,
PointerWidth::U64 => Reloc::Abs8, PointerWidth::U64 => Reloc::Abs8,
}; };
let mut relocs = Vec::new(); let relocs = data
for &(offset, id) in function_relocs { .description()
relocs.push(RelocRecord { .all_relocs(pointer_reloc)
reloc, .collect::<Vec<_>>();
offset,
name: function_decls[id].clone(),
addend: 0,
});
}
for &(offset, id, addend) in data_relocs {
relocs.push(RelocRecord {
reloc,
offset,
name: data_decls[id].clone(),
addend,
});
}
self.data_objects[id] = Some(CompiledBlob { ptr, size, relocs }); self.compiled_data_objects[id] = Some(CompiledBlob { ptr, size, relocs });
self.data_objects_to_finalize.push(id);
Ok(()) Ok(())
} }
} }
impl SimpleJITModule {
/// SimpleJIT emits code and data into memory as it processes them. This
/// method performs no additional processing, but returns a handle which
/// allows freeing the allocated memory. Otherwise said memory is leaked
/// to enable safe handling of the resulting pointers.
///
/// This method does not need to be called when access to the memory
/// handle is not required.
pub fn finish(mut self) -> SimpleJITProduct {
self.finalize_definitions();
SimpleJITProduct {
memory: self.memory,
declarations: self.declarations,
functions: self.functions,
data_objects: self.data_objects,
}
}
}
#[cfg(not(windows))] #[cfg(not(windows))]
fn lookup_with_dlsym(name: &str) -> Option<*const u8> { fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
let c_str = CString::new(name).unwrap(); let c_str = CString::new(name).unwrap();
@@ -702,10 +539,6 @@ struct SimpleJITRelocSink {
} }
impl RelocSink for SimpleJITRelocSink { impl RelocSink for SimpleJITRelocSink {
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
unimplemented!();
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
offset: CodeOffset, offset: CodeOffset,
@@ -746,14 +579,3 @@ impl RelocSink for SimpleJITRelocSink {
} }
} }
} }
#[derive(Default)]
struct SimpleJITStackMapSink {
stack_maps: Vec<StackMapRecord>,
}
impl StackMapSink for SimpleJITStackMapSink {
fn add_stack_map(&mut self, offset: CodeOffset, stack_map: StackMap) {
self.stack_maps.push(StackMapRecord { offset, stack_map });
}
}

View File

@@ -0,0 +1,55 @@
use cranelift_codegen::binemit::Reloc;
use cranelift_codegen::ir::ExternalName;
use cranelift_module::RelocRecord;
#[derive(Clone)]
pub(crate) struct CompiledBlob {
pub(crate) ptr: *mut u8,
pub(crate) size: usize,
pub(crate) relocs: Vec<RelocRecord>,
}
impl CompiledBlob {
pub(crate) fn perform_relocations(&self, get_definition: impl Fn(&ExternalName) -> *const u8) {
use std::ptr::write_unaligned;
for &RelocRecord {
reloc,
offset,
ref name,
addend,
} 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) };
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)
};
}
Reloc::Abs8 => {
#[cfg_attr(feature = "cargo-clippy", allow(clippy::cast_ptr_alignment))]
unsafe {
write_unaligned(at as *mut u64, what as u64)
};
}
Reloc::X86PCRel4 | Reloc::X86CallPCRel4 => {
// TODO: Handle overflow.
let pcrel = ((what as isize) - (at as isize)) as i32;
#[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!(),
}
}
}
}

View File

@@ -4,7 +4,8 @@
missing_docs, missing_docs,
trivial_numeric_casts, trivial_numeric_casts,
unused_extern_crates, unused_extern_crates,
unstable_features unstable_features,
unreachable_pub
)] )]
#![warn(unused_import_braces)] #![warn(unused_import_braces)]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))] #![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
@@ -23,9 +24,10 @@
)] )]
mod backend; mod backend;
mod compiled_blob;
mod memory; mod memory;
pub use crate::backend::{SimpleJITBuilder, SimpleJITModule, SimpleJITProduct}; pub use crate::backend::{SimpleJITBuilder, SimpleJITModule};
/// Version number of this crate. /// Version number of this crate.
pub const VERSION: &str = env!("CARGO_PKG_VERSION"); pub const VERSION: &str = env!("CARGO_PKG_VERSION");

View File

@@ -126,7 +126,7 @@ impl Drop for PtrLen {
/// accessible memory. Memory will be leaked by default to have /// accessible memory. Memory will be leaked by default to have
/// function pointers remain valid for the remainder of the /// function pointers remain valid for the remainder of the
/// program's life. /// program's life.
pub struct Memory { pub(crate) struct Memory {
allocations: Vec<PtrLen>, allocations: Vec<PtrLen>,
executable: usize, executable: usize,
current: PtrLen, current: PtrLen,
@@ -134,7 +134,7 @@ pub struct Memory {
} }
impl Memory { impl Memory {
pub fn new() -> Self { pub(crate) fn new() -> Self {
Self { Self {
allocations: Vec::new(), allocations: Vec::new(),
executable: 0, executable: 0,
@@ -150,7 +150,7 @@ impl Memory {
} }
/// TODO: Use a proper error type. /// TODO: Use a proper error type.
pub fn allocate(&mut self, size: usize, align: u64) -> Result<*mut u8, String> { pub(crate) fn allocate(&mut self, size: usize, align: u64) -> Result<*mut u8, String> {
let align = usize::try_from(align).expect("alignment too big"); let align = usize::try_from(align).expect("alignment too big");
if self.position % align != 0 { if self.position % align != 0 {
self.position += align - self.position % align; self.position += align - self.position % align;
@@ -173,7 +173,7 @@ impl Memory {
} }
/// Set all memory allocated in this `Memory` up to now as readable and executable. /// Set all memory allocated in this `Memory` up to now as readable and executable.
pub fn set_readable_and_executable(&mut self) { pub(crate) fn set_readable_and_executable(&mut self) {
self.finish_current(); self.finish_current();
#[cfg(feature = "selinux-fix")] #[cfg(feature = "selinux-fix")]
@@ -202,7 +202,7 @@ impl Memory {
} }
/// Set all memory allocated in this `Memory` up to now as readonly. /// Set all memory allocated in this `Memory` up to now as readonly.
pub fn set_readonly(&mut self) { pub(crate) fn set_readonly(&mut self) {
self.finish_current(); self.finish_current();
#[cfg(feature = "selinux-fix")] #[cfg(feature = "selinux-fix")]
@@ -232,7 +232,7 @@ impl Memory {
/// Frees all allocated memory regions that would be leaked otherwise. /// Frees all allocated memory regions that would be leaked otherwise.
/// Likely to invalidate existing function pointers, causing unsafety. /// Likely to invalidate existing function pointers, causing unsafety.
pub unsafe fn free_memory(&mut self) { pub(crate) unsafe fn free_memory(&mut self) {
self.allocations.clear(); self.allocations.clear();
} }
} }

View File

@@ -186,5 +186,5 @@ fn libcall_function() {
.define_function(func_id, &mut ctx, &mut trap_sink) .define_function(func_id, &mut ctx, &mut trap_sink)
.unwrap(); .unwrap();
module.finish(); module.finalize_definitions();
} }

View File

@@ -19,22 +19,6 @@ impl PrintRelocs {
} }
impl binemit::RelocSink for PrintRelocs { impl binemit::RelocSink for PrintRelocs {
fn reloc_block(
&mut self,
where_: binemit::CodeOffset,
r: binemit::Reloc,
offset: binemit::CodeOffset,
) {
if self.flag_print {
writeln!(
&mut self.text,
"reloc_block: {} {} at {}",
r, offset, where_
)
.unwrap();
}
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
where_: binemit::CodeOffset, where_: binemit::CodeOffset,

View File

@@ -114,15 +114,6 @@ struct RelocSink {
} }
impl binemit::RelocSink for RelocSink { impl binemit::RelocSink for RelocSink {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("block headers not yet implemented");
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
offset: binemit::CodeOffset, offset: binemit::CodeOffset,

View File

@@ -184,14 +184,6 @@ impl TrampolineRelocSink {
} }
impl binemit::RelocSink for TrampolineRelocSink { impl binemit::RelocSink for TrampolineRelocSink {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
panic!("trampoline compilation should not produce block relocs");
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
offset: binemit::CodeOffset, offset: binemit::CodeOffset,

View File

@@ -83,10 +83,6 @@ pub fn element(elements: ElementSectionReader) -> Result<(), Error> {
struct UnimplementedRelocSink; struct UnimplementedRelocSink;
impl binemit::RelocSink for UnimplementedRelocSink { impl binemit::RelocSink for UnimplementedRelocSink {
fn reloc_block(&mut self, _: binemit::CodeOffset, _: binemit::Reloc, _: binemit::CodeOffset) {
unimplemented!()
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
_: binemit::CodeOffset, _: binemit::CodeOffset,

View File

@@ -86,15 +86,6 @@ struct RelocSink {
} }
impl binemit::RelocSink for RelocSink { impl binemit::RelocSink for RelocSink {
fn reloc_block(
&mut self,
_offset: binemit::CodeOffset,
_reloc: binemit::Reloc,
_block_offset: binemit::CodeOffset,
) {
// This should use the `offsets` field of `ir::Function`.
panic!("block headers not yet implemented");
}
fn reloc_external( fn reloc_external(
&mut self, &mut self,
offset: binemit::CodeOffset, offset: binemit::CodeOffset,