use crate::traps::{ObjectTrapSink, ObjectTrapSite};
use cranelift_codegen::binemit::{
Addend, CodeOffset, NullStackmapSink, NullTrapSink, Reloc, RelocSink,
};
use cranelift_codegen::entity::SecondaryMap;
use cranelift_codegen::isa::TargetIsa;
use cranelift_codegen::{self, binemit, ir};
use cranelift_module::{
Backend, DataContext, DataDescription, DataId, FuncId, Init, Linkage, ModuleNamespace,
ModuleResult,
};
use object::write::{
Object, Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection,
};
use object::{RelocationEncoding, RelocationKind, SymbolFlags, SymbolKind, SymbolScope};
use std::collections::HashMap;
use target_lexicon::PointerWidth;
#[derive(Debug)]
pub enum ObjectTrapCollection {
Disabled,
Enabled,
}
pub struct ObjectBuilder {
isa: Box<dyn TargetIsa>,
name: String,
collect_traps: ObjectTrapCollection,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
function_alignment: u64,
}
impl ObjectBuilder {
pub fn new(
isa: Box<dyn TargetIsa>,
name: String,
collect_traps: ObjectTrapCollection,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
) -> ModuleResult<Self> {
Ok(Self {
isa,
name,
collect_traps,
libcall_names,
function_alignment: 1,
})
}
pub fn function_alignment(&mut self, alignment: u64) -> &mut Self {
self.function_alignment = alignment;
self
}
}
pub struct ObjectBackend {
isa: Box<dyn TargetIsa>,
object: Object,
functions: SecondaryMap<FuncId, Option<SymbolId>>,
data_objects: SecondaryMap<DataId, Option<SymbolId>>,
traps: SecondaryMap<FuncId, Vec<ObjectTrapSite>>,
libcalls: HashMap<ir::LibCall, SymbolId>,
libcall_names: Box<dyn Fn(ir::LibCall) -> String>,
collect_traps: ObjectTrapCollection,
function_alignment: u64,
}
impl Backend for ObjectBackend {
type Builder = ObjectBuilder;
type CompiledFunction = ObjectCompiledFunction;
type CompiledData = ObjectCompiledData;
type FinalizedFunction = ();
type FinalizedData = ();
type Product = ObjectProduct;
fn new(builder: ObjectBuilder) -> Self {
let triple = builder.isa.triple();
let mut object = Object::new(triple.binary_format, triple.architecture);
object.add_file_symbol(builder.name.as_bytes().to_vec());
Self {
isa: builder.isa,
object,
functions: SecondaryMap::new(),
data_objects: SecondaryMap::new(),
traps: SecondaryMap::new(),
libcalls: HashMap::new(),
libcall_names: builder.libcall_names,
collect_traps: builder.collect_traps,
function_alignment: builder.function_alignment,
}
}
fn isa(&self) -> &dyn TargetIsa {
&*self.isa
}
fn declare_function(&mut self, id: FuncId, name: &str, linkage: Linkage) {
let (scope, weak) = translate_linkage(linkage);
if let Some(function) = self.functions[id] {
let symbol = self.object.symbol_mut(function);
symbol.scope = scope;
symbol.weak = weak;
} else {
let symbol_id = self.object.add_symbol(Symbol {
name: name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope,
weak,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
self.functions[id] = Some(symbol_id);
}
}
fn declare_data(
&mut self,
id: DataId,
name: &str,
linkage: Linkage,
_writable: bool,
_align: Option<u8>,
) {
let (scope, weak) = translate_linkage(linkage);
if let Some(data) = self.data_objects[id] {
let symbol = self.object.symbol_mut(data);
symbol.scope = scope;
symbol.weak = weak;
} else {
let symbol_id = self.object.add_symbol(Symbol {
name: name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Data,
scope,
weak,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
self.data_objects[id] = Some(symbol_id);
}
}
fn define_function(
&mut self,
func_id: FuncId,
_name: &str,
ctx: &cranelift_codegen::Context,
_namespace: &ModuleNamespace<Self>,
code_size: u32,
) -> ModuleResult<ObjectCompiledFunction> {
let mut code: Vec<u8> = vec![0; code_size as usize];
let mut reloc_sink = ObjectRelocSink::default();
let mut trap_sink = ObjectTrapSink::default();
let mut stackmap_sink = NullStackmapSink {};
if let ObjectTrapCollection::Enabled = self.collect_traps {
unsafe {
ctx.emit_to_memory(
&*self.isa,
code.as_mut_ptr(),
&mut reloc_sink,
&mut trap_sink,
&mut stackmap_sink,
)
};
} else {
let mut trap_sink = NullTrapSink {};
unsafe {
ctx.emit_to_memory(
&*self.isa,
code.as_mut_ptr(),
&mut reloc_sink,
&mut trap_sink,
&mut stackmap_sink,
)
};
}
let symbol = self.functions[func_id].unwrap();
let section = self.object.section_id(StandardSection::Text);
let offset = self
.object
.add_symbol_data(symbol, section, &code, self.function_alignment);
self.traps[func_id] = trap_sink.sites;
Ok(ObjectCompiledFunction {
offset,
size: code_size,
section,
relocs: reloc_sink.relocs,
})
}
fn define_data(
&mut self,
data_id: DataId,
_name: &str,
writable: bool,
align: Option<u8>,
data_ctx: &DataContext,
_namespace: &ModuleNamespace<Self>,
) -> ModuleResult<ObjectCompiledData> {
let &DataDescription {
ref init,
ref function_decls,
ref data_decls,
ref function_relocs,
ref data_relocs,
} = data_ctx.description();
let size = init.size();
let mut data = Vec::with_capacity(size);
match *init {
Init::Uninitialized => {
panic!("data is not initialized yet");
}
Init::Zeros { .. } => {
data.resize(size, 0);
}
Init::Bytes { ref contents } => {
data.extend_from_slice(contents);
}
}
let reloc_size = match self.isa.triple().pointer_width().unwrap() {
PointerWidth::U16 => 16,
PointerWidth::U32 => 32,
PointerWidth::U64 => 64,
};
let mut relocs = Vec::new();
for &(offset, id) in function_relocs {
relocs.push(RelocRecord {
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(RelocRecord {
offset,
name: data_decls[id].clone(),
kind: RelocationKind::Absolute,
encoding: RelocationEncoding::Generic,
size: reloc_size,
addend,
});
}
let symbol = self.data_objects[data_id].unwrap();
let section = self.object.section_id(if writable {
StandardSection::Data
} else if relocs.is_empty() {
StandardSection::ReadOnlyData
} else {
StandardSection::ReadOnlyDataWithRel
});
let offset =
self.object
.add_symbol_data(symbol, section, &data, u64::from(align.unwrap_or(1)));
Ok(ObjectCompiledData {
offset,
section,
relocs,
})
}
fn write_data_funcaddr(
&mut self,
_data: &mut ObjectCompiledData,
_offset: usize,
_what: ir::FuncRef,
) {
unimplemented!()
}
fn write_data_dataaddr(
&mut self,
_data: &mut ObjectCompiledData,
_offset: usize,
_what: ir::GlobalValue,
_usize: binemit::Addend,
) {
unimplemented!()
}
fn finalize_function(
&mut self,
_id: FuncId,
func: &ObjectCompiledFunction,
namespace: &ModuleNamespace<Self>,
) {
for &RelocRecord {
offset,
ref name,
kind,
encoding,
size,
addend,
} in &func.relocs
{
let offset = func.offset + u64::from(offset);
let symbol = self.get_symbol(namespace, name);
self.object
.add_relocation(
func.section,
Relocation {
offset,
size,
kind,
encoding,
symbol,
addend,
},
)
.unwrap();
}
}
fn get_finalized_function(&self, _func: &ObjectCompiledFunction) {
}
fn finalize_data(
&mut self,
_id: DataId,
data: &ObjectCompiledData,
namespace: &ModuleNamespace<Self>,
) {
for &RelocRecord {
offset,
ref name,
kind,
encoding,
size,
addend,
} in &data.relocs
{
let offset = data.offset + u64::from(offset);
let symbol = self.get_symbol(namespace, name);
self.object
.add_relocation(
data.section,
Relocation {
offset,
size,
kind,
encoding,
symbol,
addend,
},
)
.unwrap();
}
}
fn get_finalized_data(&self, _data: &ObjectCompiledData) {
}
fn publish(&mut self) {
}
fn finish(self) -> ObjectProduct {
ObjectProduct {
object: self.object,
functions: self.functions,
data_objects: self.data_objects,
traps: self.traps,
}
}
}
impl ObjectBackend {
fn get_symbol(
&mut self,
namespace: &ModuleNamespace<Self>,
name: &ir::ExternalName,
) -> SymbolId {
match *name {
ir::ExternalName::User { .. } => {
if namespace.is_function(name) {
let id = namespace.get_function_id(name);
self.functions[id].unwrap()
} else {
let id = namespace.get_data_id(name);
self.data_objects[id].unwrap()
}
}
ir::ExternalName::LibCall(ref libcall) => {
let name = (self.libcall_names)(*libcall);
if let Some(symbol) = self.object.symbol_id(name.as_bytes()) {
symbol
} else if let Some(symbol) = self.libcalls.get(libcall) {
*symbol
} else {
let symbol = self.object.add_symbol(Symbol {
name: name.as_bytes().to_vec(),
value: 0,
size: 0,
kind: SymbolKind::Text,
scope: SymbolScope::Unknown,
weak: false,
section: SymbolSection::Undefined,
flags: SymbolFlags::None,
});
self.libcalls.insert(*libcall, symbol);
symbol
}
}
_ => panic!("invalid ExternalName {}", name),
}
}
}
fn translate_linkage(linkage: Linkage) -> (SymbolScope, bool) {
let scope = match linkage {
Linkage::Import => SymbolScope::Unknown,
Linkage::Local => SymbolScope::Compilation,
Linkage::Export | Linkage::Preemptible => SymbolScope::Dynamic,
};
let weak = linkage == Linkage::Preemptible;
(scope, weak)
}
#[derive(Clone)]
pub struct ObjectCompiledFunction {
offset: u64,
size: u32,
section: SectionId,
relocs: Vec<RelocRecord>,
}
#[derive(Clone)]
pub struct ObjectCompiledData {
offset: u64,
section: SectionId,
relocs: Vec<RelocRecord>,
}
pub struct ObjectProduct {
pub object: Object,
pub functions: SecondaryMap<FuncId, Option<SymbolId>>,
pub data_objects: SecondaryMap<DataId, Option<SymbolId>>,
pub traps: SecondaryMap<FuncId, Vec<ObjectTrapSite>>,
}
impl ObjectProduct {
#[inline]
pub fn function_symbol(&self, id: FuncId) -> SymbolId {
self.functions[id].unwrap()
}
#[inline]
pub fn data_symbol(&self, id: DataId) -> SymbolId {
self.data_objects[id].unwrap()
}
#[inline]
pub fn emit(self) -> Result<Vec<u8>, String> {
self.object.write()
}
}
#[derive(Clone)]
struct RelocRecord {
offset: CodeOffset,
name: ir::ExternalName,
kind: RelocationKind,
encoding: RelocationEncoding,
size: u8,
addend: Addend,
}
#[derive(Default)]
struct ObjectRelocSink {
relocs: Vec<RelocRecord>,
}
impl RelocSink for ObjectRelocSink {
fn reloc_ebb(&mut self, _offset: CodeOffset, _reloc: Reloc, _ebb_offset: CodeOffset) {
unimplemented!();
}
fn reloc_external(
&mut self,
offset: CodeOffset,
reloc: Reloc,
name: &ir::ExternalName,
addend: Addend,
) {
let (kind, encoding, size) = match 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),
Reloc::X86CallPLTRel4 => (
RelocationKind::PltRelative,
RelocationEncoding::X86Branch,
32,
),
Reloc::X86GOTPCRel4 => (RelocationKind::GotRelative, RelocationEncoding::Generic, 32),
_ => unimplemented!(),
};
self.relocs.push(RelocRecord {
offset,
name: name.clone(),
kind,
encoding,
size,
addend,
});
}
fn reloc_jt(&mut self, _offset: CodeOffset, reloc: Reloc, _jt: ir::JumpTable) {
match reloc {
Reloc::X86PCRelRodata4 => {
}
_ => {
panic!("Unhandled reloc");
}
}
}
fn reloc_constant(&mut self, _offset: CodeOffset, reloc: Reloc, _jt: ir::ConstantOffset) {
match reloc {
Reloc::X86PCRelRodata4 => {
}
_ => {
panic!("Unhandled reloc");
}
}
}
}