blob: b55f96fcd3c5eeb2ba44ceb6bce1aeed4c0fd17d [file] [log] [blame]
//! Defines `DataContext`.
use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc};
use cranelift_codegen::entity::PrimaryMap;
use cranelift_codegen::ir;
use std::borrow::ToOwned;
use std::boxed::Box;
use std::string::String;
use std::vec::Vec;
use crate::module::ModuleReloc;
use crate::ModuleRelocTarget;
/// This specifies how data is to be initialized.
#[derive(Clone, PartialEq, Eq, Debug)]
#[cfg_attr(
feature = "enable-serde",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
pub enum Init {
/// This indicates that no initialization has been specified yet.
Uninitialized,
/// Initialize the data with all zeros.
Zeros {
/// The size of the data.
size: usize,
},
/// Initialize the data with the specified contents.
Bytes {
/// The contents, which also implies the size of the data.
contents: Box<[u8]>,
},
}
impl Init {
/// Return the size of the data to be initialized.
pub fn size(&self) -> usize {
match *self {
Self::Uninitialized => panic!("data size not initialized yet"),
Self::Zeros { size } => size,
Self::Bytes { ref contents } => contents.len(),
}
}
}
/// A description of a data object.
#[derive(Clone, Debug)]
#[cfg_attr(
feature = "enable-serde",
derive(serde_derive::Serialize, serde_derive::Deserialize)
)]
pub struct DataDescription {
/// How the data should be initialized.
pub init: Init,
/// External function declarations.
pub function_decls: PrimaryMap<ir::FuncRef, ModuleRelocTarget>,
/// External data object declarations.
pub data_decls: PrimaryMap<ir::GlobalValue, ModuleRelocTarget>,
/// Function addresses to write at specified offsets.
pub function_relocs: Vec<(CodeOffset, ir::FuncRef)>,
/// Data addresses to write at specified offsets.
pub data_relocs: Vec<(CodeOffset, ir::GlobalValue, Addend)>,
/// Object file section
pub custom_segment_section: Option<(String, String)>,
/// Alignment in bytes. `None` means that the default alignment of the respective module should
/// be used.
pub align: Option<u64>,
}
impl DataDescription {
/// Allocate a new `DataDescription`.
pub fn new() -> Self {
Self {
init: Init::Uninitialized,
function_decls: PrimaryMap::new(),
data_decls: PrimaryMap::new(),
function_relocs: vec![],
data_relocs: vec![],
custom_segment_section: None,
align: None,
}
}
/// Clear all data structures in this `DataDescription`.
pub fn clear(&mut self) {
self.init = Init::Uninitialized;
self.function_decls.clear();
self.data_decls.clear();
self.function_relocs.clear();
self.data_relocs.clear();
self.custom_segment_section = None;
self.align = None;
}
/// Define a zero-initialized object with the given size.
pub fn define_zeroinit(&mut self, size: usize) {
debug_assert_eq!(self.init, Init::Uninitialized);
self.init = Init::Zeros { size };
}
/// Define an object initialized with the given contents.
///
/// TODO: Can we avoid a Box here?
pub fn define(&mut self, contents: Box<[u8]>) {
debug_assert_eq!(self.init, Init::Uninitialized);
self.init = Init::Bytes { contents };
}
/// Override the segment/section for data, only supported on Object backend
pub fn set_segment_section(&mut self, seg: &str, sec: &str) {
self.custom_segment_section = Some((seg.to_owned(), sec.to_owned()))
}
/// Set the alignment for data. The alignment must be a power of two.
pub fn set_align(&mut self, align: u64) {
assert!(align.is_power_of_two());
self.align = Some(align);
}
/// Declare an external function import.
///
/// Users of the `Module` API generally should call
/// `Module::declare_func_in_data` instead, as it takes care of generating
/// the appropriate `ExternalName`.
pub fn import_function(&mut self, name: ModuleRelocTarget) -> ir::FuncRef {
self.function_decls.push(name)
}
/// Declares a global value import.
///
/// TODO: Rename to import_data?
///
/// Users of the `Module` API generally should call
/// `Module::declare_data_in_data` instead, as it takes care of generating
/// the appropriate `ExternalName`.
pub fn import_global_value(&mut self, name: ModuleRelocTarget) -> ir::GlobalValue {
self.data_decls.push(name)
}
/// Write the address of `func` into the data at offset `offset`.
pub fn write_function_addr(&mut self, offset: CodeOffset, func: ir::FuncRef) {
self.function_relocs.push((offset, func))
}
/// Write the address of `data` into the data at offset `offset`.
pub fn write_data_addr(&mut self, offset: CodeOffset, data: ir::GlobalValue, addend: Addend) {
self.data_relocs.push((offset, data, addend))
}
/// An iterator over all relocations of the data object.
pub fn all_relocs<'a>(
&'a self,
pointer_reloc: Reloc,
) -> impl Iterator<Item = ModuleReloc> + 'a {
let func_relocs = self
.function_relocs
.iter()
.map(move |&(offset, id)| ModuleReloc {
kind: pointer_reloc,
offset,
name: self.function_decls[id].clone(),
addend: 0,
});
let data_relocs = self
.data_relocs
.iter()
.map(move |&(offset, id, addend)| ModuleReloc {
kind: pointer_reloc,
offset,
name: self.data_decls[id].clone(),
addend,
});
func_relocs.chain(data_relocs)
}
}
#[cfg(test)]
mod tests {
use crate::ModuleRelocTarget;
use super::{DataDescription, Init};
#[test]
fn basic_data_context() {
let mut data = DataDescription::new();
assert_eq!(data.init, Init::Uninitialized);
assert!(data.function_decls.is_empty());
assert!(data.data_decls.is_empty());
assert!(data.function_relocs.is_empty());
assert!(data.data_relocs.is_empty());
data.define_zeroinit(256);
let _func_a = data.import_function(ModuleRelocTarget::user(0, 0));
let func_b = data.import_function(ModuleRelocTarget::user(0, 1));
let func_c = data.import_function(ModuleRelocTarget::user(0, 2));
let _data_a = data.import_global_value(ModuleRelocTarget::user(0, 3));
let data_b = data.import_global_value(ModuleRelocTarget::user(0, 4));
data.write_function_addr(8, func_b);
data.write_function_addr(16, func_c);
data.write_data_addr(32, data_b, 27);
assert_eq!(data.init, Init::Zeros { size: 256 });
assert_eq!(data.function_decls.len(), 3);
assert_eq!(data.data_decls.len(), 2);
assert_eq!(data.function_relocs.len(), 2);
assert_eq!(data.data_relocs.len(), 1);
data.clear();
assert_eq!(data.init, Init::Uninitialized);
assert!(data.function_decls.is_empty());
assert!(data.data_decls.is_empty());
assert!(data.function_relocs.is_empty());
assert!(data.data_relocs.is_empty());
let contents = vec![33, 34, 35, 36];
let contents_clone = contents.clone();
data.define(contents.into_boxed_slice());
assert_eq!(
data.init,
Init::Bytes {
contents: contents_clone.into_boxed_slice()
}
);
assert_eq!(data.function_decls.len(), 0);
assert_eq!(data.data_decls.len(), 0);
assert_eq!(data.function_relocs.len(), 0);
assert_eq!(data.data_relocs.len(), 0);
}
}