1 //! Defines `DataContext`. 2 3 use cranelift_codegen::binemit::{Addend, CodeOffset, Reloc}; 4 use cranelift_codegen::entity::PrimaryMap; 5 use cranelift_codegen::ir::{self, SourceLoc}; 6 use cranelift_codegen::MachReloc; 7 use std::borrow::ToOwned; 8 use std::boxed::Box; 9 use std::string::String; 10 use std::vec::Vec; 11 12 /// This specifies how data is to be initialized. 13 #[derive(Clone, PartialEq, Eq, Debug)] 14 pub enum Init { 15 /// This indicates that no initialization has been specified yet. 16 Uninitialized, 17 /// Initialize the data with all zeros. 18 Zeros { 19 /// The size of the data. 20 size: usize, 21 }, 22 /// Initialize the data with the specified contents. 23 Bytes { 24 /// The contents, which also implies the size of the data. 25 contents: Box<[u8]>, 26 }, 27 } 28 29 impl Init { 30 /// Return the size of the data to be initialized. 31 pub fn size(&self) -> usize { 32 match *self { 33 Self::Uninitialized => panic!("data size not initialized yet"), 34 Self::Zeros { size } => size, 35 Self::Bytes { ref contents } => contents.len(), 36 } 37 } 38 } 39 40 /// A description of a data object. 41 #[derive(Clone)] 42 pub struct DataDescription { 43 /// How the data should be initialized. 44 pub init: Init, 45 /// External function declarations. 46 pub function_decls: PrimaryMap<ir::FuncRef, ir::ExternalName>, 47 /// External data object declarations. 48 pub data_decls: PrimaryMap<ir::GlobalValue, ir::ExternalName>, 49 /// Function addresses to write at specified offsets. 50 pub function_relocs: Vec<(CodeOffset, ir::FuncRef)>, 51 /// Data addresses to write at specified offsets. 52 pub data_relocs: Vec<(CodeOffset, ir::GlobalValue, Addend)>, 53 /// Object file section 54 pub custom_segment_section: Option<(String, String)>, 55 /// Alignment in bytes. `None` means that the default alignment of the respective module should 56 /// be used. 57 pub align: Option<u64>, 58 } 59 60 impl DataDescription { 61 /// An iterator over all relocations of the data object. 62 pub fn all_relocs<'a>(&'a self, pointer_reloc: Reloc) -> impl Iterator<Item = MachReloc> + 'a { 63 let func_relocs = self 64 .function_relocs 65 .iter() 66 .map(move |&(offset, id)| MachReloc { 67 kind: pointer_reloc, 68 offset, 69 srcloc: SourceLoc::default(), 70 name: self.function_decls[id].clone(), 71 addend: 0, 72 }); 73 let data_relocs = self 74 .data_relocs 75 .iter() 76 .map(move |&(offset, id, addend)| MachReloc { 77 kind: pointer_reloc, 78 offset, 79 srcloc: SourceLoc::default(), 80 name: self.data_decls[id].clone(), 81 addend, 82 }); 83 func_relocs.chain(data_relocs) 84 } 85 } 86 87 /// This is to data objects what cranelift_codegen::Context is to functions. 88 pub struct DataContext { 89 description: DataDescription, 90 } 91 92 impl DataContext { 93 /// Allocate a new context. 94 pub fn new() -> Self { 95 Self { 96 description: DataDescription { 97 init: Init::Uninitialized, 98 function_decls: PrimaryMap::new(), 99 data_decls: PrimaryMap::new(), 100 function_relocs: vec![], 101 data_relocs: vec![], 102 custom_segment_section: None, 103 align: None, 104 }, 105 } 106 } 107 108 /// Clear all data structures in this context. 109 pub fn clear(&mut self) { 110 self.description.init = Init::Uninitialized; 111 self.description.function_decls.clear(); 112 self.description.data_decls.clear(); 113 self.description.function_relocs.clear(); 114 self.description.data_relocs.clear(); 115 self.description.custom_segment_section = None; 116 self.description.align = None; 117 } 118 119 /// Define a zero-initialized object with the given size. 120 pub fn define_zeroinit(&mut self, size: usize) { 121 debug_assert_eq!(self.description.init, Init::Uninitialized); 122 self.description.init = Init::Zeros { size }; 123 } 124 125 /// Define an object initialized with the given contents. 126 /// 127 /// TODO: Can we avoid a Box here? 128 pub fn define(&mut self, contents: Box<[u8]>) { 129 debug_assert_eq!(self.description.init, Init::Uninitialized); 130 self.description.init = Init::Bytes { contents }; 131 } 132 133 /// Override the segment/section for data, only supported on Object backend 134 pub fn set_segment_section(&mut self, seg: &str, sec: &str) { 135 self.description.custom_segment_section = Some((seg.to_owned(), sec.to_owned())) 136 } 137 138 /// Set the alignment for data. The alignment must be a power of two. 139 pub fn set_align(&mut self, align: u64) { 140 assert!(align.is_power_of_two()); 141 self.description.align = Some(align); 142 } 143 144 /// Declare an external function import. 145 /// 146 /// Users of the `Module` API generally should call 147 /// `Module::declare_func_in_data` instead, as it takes care of generating 148 /// the appropriate `ExternalName`. 149 pub fn import_function(&mut self, name: ir::ExternalName) -> ir::FuncRef { 150 self.description.function_decls.push(name) 151 } 152 153 /// Declares a global value import. 154 /// 155 /// TODO: Rename to import_data? 156 /// 157 /// Users of the `Module` API generally should call 158 /// `Module::declare_data_in_data` instead, as it takes care of generating 159 /// the appropriate `ExternalName`. 160 pub fn import_global_value(&mut self, name: ir::ExternalName) -> ir::GlobalValue { 161 self.description.data_decls.push(name) 162 } 163 164 /// Write the address of `func` into the data at offset `offset`. 165 pub fn write_function_addr(&mut self, offset: CodeOffset, func: ir::FuncRef) { 166 self.description.function_relocs.push((offset, func)) 167 } 168 169 /// Write the address of `data` into the data at offset `offset`. 170 pub fn write_data_addr(&mut self, offset: CodeOffset, data: ir::GlobalValue, addend: Addend) { 171 self.description.data_relocs.push((offset, data, addend)) 172 } 173 174 /// Reference the initializer data. 175 pub fn description(&self) -> &DataDescription { 176 debug_assert!( 177 self.description.init != Init::Uninitialized, 178 "data must be initialized first" 179 ); 180 &self.description 181 } 182 } 183 184 #[cfg(test)] 185 mod tests { 186 use super::{DataContext, Init}; 187 use cranelift_codegen::ir; 188 189 #[test] 190 fn basic_data_context() { 191 let mut data_ctx = DataContext::new(); 192 { 193 let description = &data_ctx.description; 194 assert_eq!(description.init, Init::Uninitialized); 195 assert!(description.function_decls.is_empty()); 196 assert!(description.data_decls.is_empty()); 197 assert!(description.function_relocs.is_empty()); 198 assert!(description.data_relocs.is_empty()); 199 } 200 201 data_ctx.define_zeroinit(256); 202 203 let _func_a = data_ctx.import_function(ir::ExternalName::user(0, 0)); 204 let func_b = data_ctx.import_function(ir::ExternalName::user(0, 1)); 205 let func_c = data_ctx.import_function(ir::ExternalName::user(1, 0)); 206 let _data_a = data_ctx.import_global_value(ir::ExternalName::user(2, 2)); 207 let data_b = data_ctx.import_global_value(ir::ExternalName::user(2, 3)); 208 209 data_ctx.write_function_addr(8, func_b); 210 data_ctx.write_function_addr(16, func_c); 211 data_ctx.write_data_addr(32, data_b, 27); 212 213 { 214 let description = data_ctx.description(); 215 assert_eq!(description.init, Init::Zeros { size: 256 }); 216 assert_eq!(description.function_decls.len(), 3); 217 assert_eq!(description.data_decls.len(), 2); 218 assert_eq!(description.function_relocs.len(), 2); 219 assert_eq!(description.data_relocs.len(), 1); 220 } 221 222 data_ctx.clear(); 223 { 224 let description = &data_ctx.description; 225 assert_eq!(description.init, Init::Uninitialized); 226 assert!(description.function_decls.is_empty()); 227 assert!(description.data_decls.is_empty()); 228 assert!(description.function_relocs.is_empty()); 229 assert!(description.data_relocs.is_empty()); 230 } 231 232 let contents = vec![33, 34, 35, 36]; 233 let contents_clone = contents.clone(); 234 data_ctx.define(contents.into_boxed_slice()); 235 { 236 let description = data_ctx.description(); 237 assert_eq!( 238 description.init, 239 Init::Bytes { 240 contents: contents_clone.into_boxed_slice() 241 } 242 ); 243 assert_eq!(description.function_decls.len(), 0); 244 assert_eq!(description.data_decls.len(), 0); 245 assert_eq!(description.function_relocs.len(), 0); 246 assert_eq!(description.data_relocs.len(), 0); 247 } 248 } 249 } 250