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