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.
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.
fn reloc_external(
&mut self,
@@ -138,11 +135,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
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(
&mut self,
srcloc: SourceLoc,
@@ -204,7 +196,6 @@ impl<'a> CodeSink for MemoryCodeSink<'a> {
pub struct NullRelocSink {}
impl RelocSink for NullRelocSink {
fn reloc_block(&mut self, _: CodeOffset, _: Reloc, _: CodeOffset) {}
fn reloc_external(
&mut self,
_: CodeOffset,

View File

@@ -140,9 +140,6 @@ pub trait CodeSink {
/// Add 8 bytes to the code section.
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.
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(
&mut self,
_srcloc: SourceLoc,

View File

@@ -72,10 +72,6 @@ impl binemit::CodeSink for TextSink {
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(
&mut self,
_srcloc: ir::SourceLoc,

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
//! Defines `DataContext`.
use cranelift_codegen::binemit::{Addend, CodeOffset};
use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc};
use cranelift_codegen::entity::PrimaryMap;
use cranelift_codegen::ir;
use std::borrow::ToOwned;
@@ -8,6 +8,8 @@ use std::boxed::Box;
use std::string::String;
use std::vec::Vec;
use crate::RelocRecord;
/// This specifies how data is to be initialized.
#[derive(PartialEq, Eq, Debug)]
pub enum Init {
@@ -55,6 +57,34 @@ pub struct DataDescription {
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.
pub struct DataContext {
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.
#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
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.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Linkage {
@@ -162,9 +186,6 @@ pub enum ModuleError {
/// Indicates an identifier was defined, but was declared as an import
#[error("Invalid to define identifier declared as an import: {0}")]
InvalidImportDefinition(String),
/// Indicates a too-long function was defined
#[error("Function {0} exceeds the maximum function size")]
FunctionTooLarge(String),
/// Wraps a `cranelift-codegen` error
#[error("Compilation error: {0}")]
Compilation(#[from] CodegenError),
@@ -217,21 +238,10 @@ impl ModuleDeclarations {
self.functions.iter()
}
/// Get the `FuncId` for the function named by `name`.
pub fn get_function_id(&self, 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)
}
}
/// 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)
/// Return whether `name` names a function, rather than a data object.
pub fn is_function(name: &ir::ExternalName) -> bool {
if let ir::ExternalName::User { namespace, .. } = *name {
namespace == 0
} else {
panic!("unexpected ExternalName kind {}", name)
}
@@ -252,15 +262,6 @@ impl ModuleDeclarations {
&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.
pub fn declare_function(
&mut self,

View File

@@ -1,12 +1,13 @@
//! Defines `ObjectModule`.
use anyhow::anyhow;
use cranelift_codegen::binemit::{
Addend, CodeInfo, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink,
};
use cranelift_codegen::entity::SecondaryMap;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{self, ir};
use cranelift_codegen::{
binemit::{Addend, CodeInfo, CodeOffset, NullStackMapSink, Reloc, RelocSink, TrapSink},
CodegenError,
};
use cranelift_module::{
DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleDeclarations, ModuleError, ModuleResult, RelocRecord,
@@ -268,17 +269,16 @@ impl Module for ObjectModule {
relocs: &[RelocRecord],
) -> ModuleResult<ModuleCompiledFunction> {
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);
if !decl.linkage.is_definable() {
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();
if *defined {
return Err(ModuleError::DuplicateDefinition(decl.name.clone()));
@@ -305,7 +305,10 @@ impl Module for ObjectModule {
};
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 {
section,
offset,
@@ -330,40 +333,24 @@ impl Module for ObjectModule {
let &DataDescription {
ref init,
ref function_decls,
ref data_decls,
ref function_relocs,
ref data_relocs,
function_decls: _,
data_decls: _,
function_relocs: _,
data_relocs: _,
ref custom_segment_section,
align,
} = data_ctx.description();
let reloc_size = match self.isa.triple().pointer_width().unwrap() {
PointerWidth::U16 => 16,
PointerWidth::U32 => 32,
PointerWidth::U64 => 64,
let pointer_reloc = match self.isa.triple().pointer_width().unwrap() {
PointerWidth::U16 => unimplemented!("16bit pointers"),
PointerWidth::U32 => Reloc::Abs4,
PointerWidth::U64 => Reloc::Abs8,
};
let mut relocs = Vec::new();
for &(offset, id) in function_relocs {
relocs.push(ObjectRelocRecord {
offset,
name: function_decls[id].clone(),
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 relocs = data_ctx
.description()
.all_relocs(pointer_reloc)
.map(|record| self.process_reloc(&record))
.collect::<Vec<_>>();
let section = if custom_segment_section.is_none() {
let section_kind = if let Init::Zeros { .. } = *init {
@@ -477,11 +464,11 @@ impl ObjectModule {
fn get_symbol(&mut self, name: &ir::ExternalName) -> SymbolId {
match *name {
ir::ExternalName::User { .. } => {
if self.declarations.is_function(name) {
let id = self.declarations.get_function_id(name);
if ModuleDeclarations::is_function(name) {
let id = FuncId::from_name(name);
self.functions[id].unwrap().0
} else {
let id = self.declarations.get_data_id(name);
let id = DataId::from_name(name);
self.data_objects[id].unwrap().0
}
}
@@ -510,69 +497,60 @@ impl ObjectModule {
}
}
fn process_relocs(&self, relocs: &[RelocRecord]) -> Vec<ObjectRelocRecord> {
relocs
.iter()
.map(|record| {
let mut addend = record.addend;
let (kind, encoding, size) = match record.reloc {
Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
Reloc::X86CallPCRel4 => {
(RelocationKind::Relative, RelocationEncoding::X86Branch, 32)
}
// TODO: Get Cranelift to tell us when we can use
// R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
Reloc::X86CallPLTRel4 => (
RelocationKind::PltRelative,
RelocationEncoding::X86Branch,
32,
),
Reloc::X86GOTPCRel4 => {
(RelocationKind::GotRelative, RelocationEncoding::Generic, 32)
}
Reloc::ElfX86_64TlsGd => {
assert_eq!(
self.object.format(),
object::BinaryFormat::Elf,
"ElfX86_64TlsGd is not supported for this file format"
);
(
RelocationKind::Elf(object::elf::R_X86_64_TLSGD),
RelocationEncoding::Generic,
32,
)
}
Reloc::MachOX86_64Tlv => {
assert_eq!(
self.object.format(),
object::BinaryFormat::MachO,
"MachOX86_64Tlv is not supported for this file format"
);
addend += 4; // X86_64_RELOC_TLV has an implicit addend of -4
(
RelocationKind::MachO {
value: object::macho::X86_64_RELOC_TLV,
relative: true,
},
RelocationEncoding::Generic,
32,
)
}
// FIXME
_ => unimplemented!(),
};
ObjectRelocRecord {
offset: record.offset,
name: record.name.clone(),
kind,
encoding,
size,
addend,
}
})
.collect()
fn process_reloc(&self, record: &RelocRecord) -> ObjectRelocRecord {
let mut addend = record.addend;
let (kind, encoding, size) = match record.reloc {
Reloc::Abs4 => (RelocationKind::Absolute, RelocationEncoding::Generic, 32),
Reloc::Abs8 => (RelocationKind::Absolute, RelocationEncoding::Generic, 64),
Reloc::X86PCRel4 => (RelocationKind::Relative, RelocationEncoding::Generic, 32),
Reloc::X86CallPCRel4 => (RelocationKind::Relative, RelocationEncoding::X86Branch, 32),
// TODO: Get Cranelift to tell us when we can use
// R_X86_64_GOTPCRELX/R_X86_64_REX_GOTPCRELX.
Reloc::X86CallPLTRel4 => (
RelocationKind::PltRelative,
RelocationEncoding::X86Branch,
32,
),
Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
Reloc::ElfX86_64TlsGd => {
assert_eq!(
self.object.format(),
object::BinaryFormat::Elf,
"ElfX86_64TlsGd is not supported for this file format"
);
(
RelocationKind::Elf(object::elf::R_X86_64_TLSGD),
RelocationEncoding::Generic,
32,
)
}
Reloc::MachOX86_64Tlv => {
assert_eq!(
self.object.format(),
object::BinaryFormat::MachO,
"MachOX86_64Tlv is not supported for this file format"
);
addend += 4; // X86_64_RELOC_TLV has an implicit addend of -4
(
RelocationKind::MachO {
value: object::macho::X86_64_RELOC_TLV,
relative: true,
},
RelocationEncoding::Generic,
32,
)
}
// FIXME
_ => unimplemented!(),
};
ObjectRelocRecord {
offset: record.offset,
name: record.name.clone(),
kind,
encoding,
size,
addend,
}
}
}
@@ -644,10 +622,6 @@ struct ObjectRelocSink {
}
impl RelocSink for ObjectRelocSink {
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
unimplemented!();
}
fn reloc_external(
&mut self,
offset: CodeOffset,

View File

@@ -70,10 +70,10 @@ fn main() {
module.clear_context(&mut ctx);
// Perform linking.
let product = module.finish();
module.finalize_definitions();
// 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.
let ptr_b = unsafe { mem::transmute::<_, fn() -> u32>(code_b) };

View File

@@ -1,16 +1,17 @@
//! Defines `SimpleJITModule`.
use crate::memory::Memory;
use cranelift_codegen::binemit::{
Addend, CodeInfo, CodeOffset, Reloc, RelocSink, StackMap, StackMapSink, TrapSink,
};
use crate::{compiled_blob::CompiledBlob, memory::Memory};
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::settings::Configurable;
use cranelift_codegen::{self, ir, settings};
use cranelift_codegen::{
binemit::{self, Addend, CodeInfo, CodeOffset, Reloc, RelocSink, TrapSink},
CodegenError,
};
use cranelift_entity::SecondaryMap;
use cranelift_module::{
DataContext, DataDescription, DataId, FuncId, FuncOrDataId, Init, Linkage, Module,
ModuleCompiledFunction, ModuleDeclarations, ModuleError, ModuleResult, RelocRecord,
DataContext, DataDescription, DataId, FuncId, Init, Linkage, Module, ModuleCompiledFunction,
ModuleDeclarations, ModuleError, ModuleResult, RelocRecord,
};
use cranelift_native;
#[cfg(not(windows))]
@@ -128,26 +129,12 @@ pub struct SimpleJITModule {
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
memory: MemoryHandle,
declarations: ModuleDeclarations,
functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
compiled_functions: SecondaryMap<FuncId, Option<CompiledBlob>>,
compiled_data_objects: SecondaryMap<DataId, Option<CompiledBlob>>,
functions_to_finalize: Vec<FuncId>,
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`.
struct MemoryHandle {
code: Memory,
@@ -155,16 +142,7 @@ struct MemoryHandle {
writable: Memory,
}
/// A `SimpleJITProduct` allows looking up the addresses of all functions and data objects
/// 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 {
impl SimpleJITModule {
/// Free memory allocated for code and data segments of compiled functions.
///
/// # Safety
@@ -179,29 +157,6 @@ impl SimpleJITProduct {
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> {
self.symbols
.get(name)
@@ -212,9 +167,9 @@ impl SimpleJITModule {
fn get_definition(&self, name: &ir::ExternalName) -> *const u8 {
match *name {
ir::ExternalName::User { .. } => {
let (name, linkage) = if self.declarations.is_function(name) {
let func_id = self.declarations.get_function_id(name);
match &self.functions[func_id] {
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);
@@ -222,8 +177,8 @@ impl SimpleJITModule {
}
}
} else {
let data_id = self.declarations.get_data_id(name);
match &self.data_objects[data_id] {
let data_id = DataId::from_name(name);
match &self.compiled_data_objects[data_id] {
Some(compiled) => return compiled.ptr,
None => {
let decl = self.declarations.get_data_decl(data_id);
@@ -250,7 +205,7 @@ impl SimpleJITModule {
/// Returns the address of a finalized function.
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!(
!self.functions_to_finalize.iter().any(|x| *x == func_id),
"function not yet finalized"
@@ -262,7 +217,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.data_objects[data_id];
let info = &self.compiled_data_objects[data_id];
debug_assert!(
!self.data_objects_to_finalize.iter().any(|x| *x == data_id),
"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.
/// All symbols referenced in their bodies that are declared as needing a definition
/// must be defined by this point.
@@ -390,12 +256,18 @@ impl SimpleJITModule {
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());
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) {
let decl = self.declarations.get_data_decl(data);
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!
@@ -417,8 +289,8 @@ impl SimpleJITModule {
libcall_names: builder.libcall_names,
memory,
declarations: ModuleDeclarations::default(),
functions: SecondaryMap::new(),
data_objects: SecondaryMap::new(),
compiled_functions: SecondaryMap::new(),
compiled_data_objects: SecondaryMap::new(),
functions_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()));
}
if !self.functions[id].is_none() {
if !self.compiled_functions[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
}
self.functions_to_finalize.push(id);
let size = code_size as usize;
let ptr = self
.memory
@@ -492,10 +363,8 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, &decl.name);
let mut reloc_sink = SimpleJITRelocSink::default();
let mut stack_map_sink = SimpleJITStackMapSink::default();
let mut stack_map_sink = binemit::NullStackMapSink {};
unsafe {
ctx.emit_to_memory(
&*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,
size,
relocs: reloc_sink.relocs,
});
self.functions_to_finalize.push(id);
Ok(ModuleCompiledFunction { size: code_size })
}
@@ -521,21 +392,21 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
bytes: &[u8],
relocs: &[RelocRecord],
) -> 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);
if !decl.linkage.is_definable() {
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()))?,
};
if !self.functions[id].is_none() {
if !self.compiled_functions[id].is_none() {
return Err(ModuleError::DuplicateDefinition(decl.name.to_owned()));
}
self.functions_to_finalize.push(id);
let size = bytes.len();
let ptr = self
.memory
@@ -543,17 +414,17 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
.allocate(size, EXECUTABLE_DATA_ALIGNMENT)
.expect("TODO: handle OOM etc.");
self.record_function_for_perf(ptr, size, &decl.name);
unsafe {
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,
size,
relocs: relocs.to_vec(),
});
self.functions_to_finalize.push(id);
Ok(ModuleCompiledFunction { size: total_size })
}
@@ -564,20 +435,18 @@ impl<'simple_jit_backend> Module for SimpleJITModule {
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()));
}
assert!(!decl.tls, "SimpleJIT doesn't yet support TLS");
self.data_objects_to_finalize.push(id);
let &DataDescription {
ref init,
ref function_decls,
ref data_decls,
ref function_relocs,
ref data_relocs,
function_decls: _,
data_decls: _,
function_relocs: _,
data_relocs: _,
custom_segment_section: _,
align,
} = 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::U32 => Reloc::Abs4,
PointerWidth::U64 => Reloc::Abs8,
};
let mut relocs = Vec::new();
for &(offset, id) in function_relocs {
relocs.push(RelocRecord {
reloc,
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,
});
}
let relocs = data
.description()
.all_relocs(pointer_reloc)
.collect::<Vec<_>>();
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(())
}
}
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))]
fn lookup_with_dlsym(name: &str) -> Option<*const u8> {
let c_str = CString::new(name).unwrap();
@@ -702,10 +539,6 @@ struct SimpleJITRelocSink {
}
impl RelocSink for SimpleJITRelocSink {
fn reloc_block(&mut self, _offset: CodeOffset, _reloc: Reloc, _block_offset: CodeOffset) {
unimplemented!();
}
fn reloc_external(
&mut self,
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,
trivial_numeric_casts,
unused_extern_crates,
unstable_features
unstable_features,
unreachable_pub
)]
#![warn(unused_import_braces)]
#![cfg_attr(feature = "clippy", plugin(clippy(conf_file = "../../clippy.toml")))]
@@ -23,9 +24,10 @@
)]
mod backend;
mod compiled_blob;
mod memory;
pub use crate::backend::{SimpleJITBuilder, SimpleJITModule, SimpleJITProduct};
pub use crate::backend::{SimpleJITBuilder, SimpleJITModule};
/// Version number of this crate.
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
/// function pointers remain valid for the remainder of the
/// program's life.
pub struct Memory {
pub(crate) struct Memory {
allocations: Vec<PtrLen>,
executable: usize,
current: PtrLen,
@@ -134,7 +134,7 @@ pub struct Memory {
}
impl Memory {
pub fn new() -> Self {
pub(crate) fn new() -> Self {
Self {
allocations: Vec::new(),
executable: 0,
@@ -150,7 +150,7 @@ impl Memory {
}
/// 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");
if self.position % align != 0 {
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.
pub fn set_readable_and_executable(&mut self) {
pub(crate) fn set_readable_and_executable(&mut self) {
self.finish_current();
#[cfg(feature = "selinux-fix")]
@@ -202,7 +202,7 @@ impl Memory {
}
/// 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();
#[cfg(feature = "selinux-fix")]
@@ -232,7 +232,7 @@ impl Memory {
/// Frees all allocated memory regions that would be leaked otherwise.
/// 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();
}
}

View File

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

View File

@@ -19,22 +19,6 @@ impl 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(
&mut self,
where_: binemit::CodeOffset,

View File

@@ -114,15 +114,6 @@ struct 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(
&mut self,
offset: binemit::CodeOffset,

View File

@@ -184,14 +184,6 @@ impl 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(
&mut self,
offset: binemit::CodeOffset,

View File

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

View File

@@ -86,15 +86,6 @@ struct 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(
&mut self,
offset: binemit::CodeOffset,