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