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 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                 name: self.function_decls[id].clone(),
70                 addend: 0,
71             });
72         let data_relocs = self
73             .data_relocs
74             .iter()
75             .map(move |&(offset, id, addend)| MachReloc {
76                 kind: pointer_reloc,
77                 offset,
78                 name: self.data_decls[id].clone(),
79                 addend,
80             });
81         func_relocs.chain(data_relocs)
82     }
83 }
84 
85 /// This is to data objects what cranelift_codegen::Context is to functions.
86 pub struct DataContext {
87     description: DataDescription,
88 }
89 
90 impl DataContext {
91     /// Allocate a new context.
92     pub fn new() -> Self {
93         Self {
94             description: DataDescription {
95                 init: Init::Uninitialized,
96                 function_decls: PrimaryMap::new(),
97                 data_decls: PrimaryMap::new(),
98                 function_relocs: vec![],
99                 data_relocs: vec![],
100                 custom_segment_section: None,
101                 align: None,
102             },
103         }
104     }
105 
106     /// Clear all data structures in this context.
107     pub fn clear(&mut self) {
108         self.description.init = Init::Uninitialized;
109         self.description.function_decls.clear();
110         self.description.data_decls.clear();
111         self.description.function_relocs.clear();
112         self.description.data_relocs.clear();
113         self.description.custom_segment_section = None;
114         self.description.align = None;
115     }
116 
117     /// Define a zero-initialized object with the given size.
118     pub fn define_zeroinit(&mut self, size: usize) {
119         debug_assert_eq!(self.description.init, Init::Uninitialized);
120         self.description.init = Init::Zeros { size };
121     }
122 
123     /// Define an object initialized with the given contents.
124     ///
125     /// TODO: Can we avoid a Box here?
126     pub fn define(&mut self, contents: Box<[u8]>) {
127         debug_assert_eq!(self.description.init, Init::Uninitialized);
128         self.description.init = Init::Bytes { contents };
129     }
130 
131     /// Override the segment/section for data, only supported on Object backend
132     pub fn set_segment_section(&mut self, seg: &str, sec: &str) {
133         self.description.custom_segment_section = Some((seg.to_owned(), sec.to_owned()))
134     }
135 
136     /// Set the alignment for data. The alignment must be a power of two.
137     pub fn set_align(&mut self, align: u64) {
138         assert!(align.is_power_of_two());
139         self.description.align = Some(align);
140     }
141 
142     /// Declare an external function import.
143     ///
144     /// Users of the `Module` API generally should call
145     /// `Module::declare_func_in_data` instead, as it takes care of generating
146     /// the appropriate `ExternalName`.
147     pub fn import_function(&mut self, name: ir::ExternalName) -> ir::FuncRef {
148         self.description.function_decls.push(name)
149     }
150 
151     /// Declares a global value import.
152     ///
153     /// TODO: Rename to import_data?
154     ///
155     /// Users of the `Module` API generally should call
156     /// `Module::declare_data_in_data` instead, as it takes care of generating
157     /// the appropriate `ExternalName`.
158     pub fn import_global_value(&mut self, name: ir::ExternalName) -> ir::GlobalValue {
159         self.description.data_decls.push(name)
160     }
161 
162     /// Write the address of `func` into the data at offset `offset`.
163     pub fn write_function_addr(&mut self, offset: CodeOffset, func: ir::FuncRef) {
164         self.description.function_relocs.push((offset, func))
165     }
166 
167     /// Write the address of `data` into the data at offset `offset`.
168     pub fn write_data_addr(&mut self, offset: CodeOffset, data: ir::GlobalValue, addend: Addend) {
169         self.description.data_relocs.push((offset, data, addend))
170     }
171 
172     /// Reference the initializer data.
173     pub fn description(&self) -> &DataDescription {
174         debug_assert!(
175             self.description.init != Init::Uninitialized,
176             "data must be initialized first"
177         );
178         &self.description
179     }
180 }
181 
182 #[cfg(test)]
183 mod tests {
184     use super::{DataContext, Init};
185     use cranelift_codegen::ir;
186 
187     #[test]
188     fn basic_data_context() {
189         let mut data_ctx = DataContext::new();
190         {
191             let description = &data_ctx.description;
192             assert_eq!(description.init, Init::Uninitialized);
193             assert!(description.function_decls.is_empty());
194             assert!(description.data_decls.is_empty());
195             assert!(description.function_relocs.is_empty());
196             assert!(description.data_relocs.is_empty());
197         }
198 
199         data_ctx.define_zeroinit(256);
200 
201         let _func_a = data_ctx.import_function(ir::ExternalName::user(0, 0));
202         let func_b = data_ctx.import_function(ir::ExternalName::user(0, 1));
203         let func_c = data_ctx.import_function(ir::ExternalName::user(1, 0));
204         let _data_a = data_ctx.import_global_value(ir::ExternalName::user(2, 2));
205         let data_b = data_ctx.import_global_value(ir::ExternalName::user(2, 3));
206 
207         data_ctx.write_function_addr(8, func_b);
208         data_ctx.write_function_addr(16, func_c);
209         data_ctx.write_data_addr(32, data_b, 27);
210 
211         {
212             let description = data_ctx.description();
213             assert_eq!(description.init, Init::Zeros { size: 256 });
214             assert_eq!(description.function_decls.len(), 3);
215             assert_eq!(description.data_decls.len(), 2);
216             assert_eq!(description.function_relocs.len(), 2);
217             assert_eq!(description.data_relocs.len(), 1);
218         }
219 
220         data_ctx.clear();
221         {
222             let description = &data_ctx.description;
223             assert_eq!(description.init, Init::Uninitialized);
224             assert!(description.function_decls.is_empty());
225             assert!(description.data_decls.is_empty());
226             assert!(description.function_relocs.is_empty());
227             assert!(description.data_relocs.is_empty());
228         }
229 
230         let contents = vec![33, 34, 35, 36];
231         let contents_clone = contents.clone();
232         data_ctx.define(contents.into_boxed_slice());
233         {
234             let description = data_ctx.description();
235             assert_eq!(
236                 description.init,
237                 Init::Bytes {
238                     contents: contents_clone.into_boxed_slice()
239                 }
240             );
241             assert_eq!(description.function_decls.len(), 0);
242             assert_eq!(description.data_decls.len(), 0);
243             assert_eq!(description.function_relocs.len(), 0);
244             assert_eq!(description.data_relocs.len(), 0);
245         }
246     }
247 }
248