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