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