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