Skip to main content

cranelift_module/
data_context.rs

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