formatic/
object_builder.rs

1use std::{collections::HashMap, fs::OpenOptions};
2
3use object::{
4    write::{Relocation, SectionId, StandardSection, Symbol, SymbolId, SymbolSection},
5    RelocationEncoding, RelocationFlags, RelocationKind, SymbolFlags, SymbolKind, SymbolScope,
6};
7
8use crate::{Decl, Link, ObjectError, Scope};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11/// Enum which specifies the binary format
12///
13/// E.g: Coff for Windows
14pub enum BinFormat {
15    Elf,
16    Coff,
17    Macho,
18}
19
20impl BinFormat {
21    /// Function which returns the native binary format
22    ///
23    /// For any unknown os it returns elf
24    pub fn host() -> BinFormat {
25        if cfg!(target_os = "windows") {
26            BinFormat::Coff
27        } else if cfg!(target_os = "macos") {
28            BinFormat::Macho
29        } else {
30            BinFormat::Elf
31        }
32    }
33}
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
36/// Enum which specifies the architecture
37pub enum Arch {
38    X86_64,
39    Arm,
40    Riscv32,
41    Riscv64,
42    Wasm32,
43    Wasm64,
44    Unknown,
45}
46
47impl Arch {
48    /// Returns the native architecture
49    pub fn host() -> Arch {
50        if cfg!(target_arch = "x86_64") {
51            Arch::X86_64
52        } else if cfg!(target_arch = "arm") {
53            Arch::Arm
54        } else if cfg!(target_arch = "riscv32") {
55            Arch::Riscv32
56        } else if cfg!(target_arch = "riscv64") {
57            Arch::Riscv64
58        } else if cfg!(target_arch = "wasm32") {
59            Arch::Wasm32
60        } else if cfg!(target_arch = "wasm64") {
61            Arch::Wasm32
62        } else {
63            Arch::Unknown
64        }
65    }
66}
67
68#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
69/// Enum which specifies the endiannes
70pub enum Endian {
71    Litte,
72    Big,
73}
74
75impl Endian {
76    /// Returns the native endian
77    pub fn host() -> Endian {
78        if cfg!(target_endian = "big") {
79            Endian::Big
80        } else {
81            Endian::Litte
82        }
83    }
84}
85
86#[derive(Debug, Clone)]
87/// A struct for building object files
88pub struct ObjectBuilder {
89    decls: Vec<(String, Decl)>,
90    sym: HashMap<String, Vec<u8>>,
91    links: Vec<Link>,
92
93    outpath: String,
94}
95
96impl ObjectBuilder {
97    //// Returns empty instance of self
98    pub fn new(path: &str) -> Self {
99        Self {
100            decls: vec![],
101            sym: HashMap::new(),
102            links: vec![],
103
104            outpath: path.into(),
105        }
106    }
107
108    /// Adds a list of decls
109    pub fn decls(&mut self, decls: Vec<(&str, Decl)>) {
110        for decl in decls {
111            let decl = (decl.0.into(), decl.1);
112            self.decls.push(decl);
113        }
114    }
115
116    /// Adds a decl
117    pub fn add_decl(&mut self, name: &str, decl: Decl) {
118        self.decls.push((name.into(), decl));
119    }
120
121    /// Defines a symbol
122    pub fn define(&mut self, sym: &str, data: Vec<u8>) {
123        self.sym.insert(sym.into(), data);
124    }
125
126    /// Adds an link to the object file
127    pub fn link(&mut self, link: Link) {
128        self.links.push(link);
129    }
130
131    /// Writes all internaly saved symbols etc. to a object file
132    ///
133    /// Args:
134    ///  * `format`   - specifes the binary format of the object file
135    ///  * `arch`     - specifes the architecture of the object file
136    ///  * `endian`   - specifes the endian of the object file
137    pub fn write(
138        &mut self,
139        format: BinFormat,
140        arch: Arch,
141        endian: Endian,
142    ) -> Result<(), Box<dyn std::error::Error>> {
143        let file = OpenOptions::new()
144            .create(true)
145            .write(true)
146            .open(self.outpath.clone().to_owned())?;
147
148        let obj_format = match format {
149            BinFormat::Elf => object::BinaryFormat::Elf,
150            BinFormat::Coff => object::BinaryFormat::Coff,
151            BinFormat::Macho => object::BinaryFormat::MachO,
152        };
153
154        let obj_arch = match arch {
155            Arch::X86_64 => object::Architecture::X86_64,
156            Arch::Arm => object::Architecture::Arm,
157            Arch::Riscv32 => object::Architecture::Riscv32,
158            Arch::Riscv64 => object::Architecture::Riscv64,
159            Arch::Wasm32 => object::Architecture::Wasm32,
160            Arch::Wasm64 => object::Architecture::Wasm64,
161            Arch::Unknown => object::Architecture::Unknown,
162        };
163
164        let obj_endian = match endian {
165            Endian::Litte => object::Endianness::Little,
166            Endian::Big => object::Endianness::Big,
167        };
168
169        let mut obj = object::write::Object::new(obj_format, obj_arch, obj_endian);
170
171        obj.add_file_symbol(self.outpath.to_owned().into_bytes());
172
173        let mut ids: HashMap<String, SymbolId> = HashMap::new();
174        let mut funcs: HashMap<String, ((SectionId, u64), SymbolId)> = HashMap::new();
175
176        for decl in self.decls.iter() {
177            let name = &decl.0;
178            let decl = &decl.1;
179
180            // get type
181            match decl {
182                Decl::Data(s) => match s {
183                    Scope::Import => {
184                        ids.insert(
185                            name.to_string(),
186                            obj.add_symbol(Symbol {
187                                name: name.as_bytes().into(),
188                                value: 0,
189                                size: 0,
190                                kind: SymbolKind::Data,
191                                scope: SymbolScope::Dynamic,
192                                weak: false,
193                                section: SymbolSection::Undefined,
194                                flags: SymbolFlags::None,
195                            }),
196                        );
197                    }
198                    _ => {
199                        let dat_opt = self.sym.get(&name.clone());
200
201                        if dat_opt.is_none() {
202                            return Err(Box::from(ObjectError::DeclWithoutSymbol));
203                        }
204
205                        let data = dat_opt.unwrap();
206
207                        let scope;
208                        if s.to_owned() == Scope::Export {
209                            scope = SymbolScope::Linkage
210                        } else {
211                            scope = SymbolScope::Compilation
212                        }
213
214                        let (section, offset) = obj.add_subsection(
215                            StandardSection::Data,
216                            name.as_bytes().into(),
217                            data,
218                            16,
219                        );
220                        let symbol = obj.add_symbol(Symbol {
221                            name: name.as_bytes().into(),
222                            value: offset,
223                            size: data.len() as u64,
224                            kind: SymbolKind::Data,
225                            scope: scope,
226                            weak: false,
227                            section: SymbolSection::Section(section),
228                            flags: SymbolFlags::None,
229                        });
230
231                        funcs.insert(name.into(), ((section, offset), symbol));
232                    }
233                },
234
235                Decl::Function(s) => match s {
236                    Scope::Import => {
237                        ids.insert(
238                            name.to_string(),
239                            obj.add_symbol(Symbol {
240                                name: name.as_bytes().into(),
241                                value: 0,
242                                size: 0,
243                                kind: SymbolKind::Text,
244                                scope: SymbolScope::Dynamic,
245                                weak: false,
246                                section: SymbolSection::Undefined,
247                                flags: SymbolFlags::None,
248                            }),
249                        );
250                    }
251                    _ => {
252                        let dat_opt = self.sym.get(&name.clone());
253
254                        if dat_opt.is_none() {
255                            return Err(Box::from(ObjectError::DeclWithoutSymbol));
256                        }
257
258                        let scope;
259                        if s.to_owned() == Scope::Export {
260                            scope = SymbolScope::Linkage
261                        } else {
262                            scope = SymbolScope::Compilation
263                        }
264
265                        let data = dat_opt.unwrap();
266
267                        let (section, offset) = obj.add_subsection(
268                            StandardSection::Text,
269                            name.as_bytes().into(),
270                            data,
271                            16,
272                        );
273                        let symbol = obj.add_symbol(Symbol {
274                            name: name.as_bytes().into(),
275                            value: offset,
276                            size: data.len() as u64,
277                            kind: SymbolKind::Text,
278                            scope: scope,
279                            weak: false,
280                            section: SymbolSection::Section(section),
281                            flags: SymbolFlags::None,
282                        });
283
284                        funcs.insert(name.into(), ((section, offset), symbol));
285                    }
286                },
287            }
288        }
289
290        for link in self.links.iter() {
291            let link = link.to_owned();
292
293            let func_opt = funcs.get(&link.from);
294            if func_opt.is_none() {
295                return Err(Box::from(ObjectError::UnknownFunction(
296                    link.from.to_owned(),
297                )));
298            }
299            let func = func_opt.unwrap();
300
301            let id = func.0 .0;
302            let off = func.0 .1;
303
304            let sym;
305
306            if funcs.contains_key(&link.to) {
307                sym = Some(funcs.get(&link.to).unwrap().1);
308            } else if ids.contains_key(&link.to) {
309                sym = Some(ids.get(&link.to).unwrap().to_owned());
310            } else {
311                return Err(Box::from(ObjectError::UnknownTargetSymbol(
312                    link.to.to_owned(),
313                )));
314            }
315
316            obj.add_relocation(
317                id,
318                Relocation {
319                    offset: off + link.at as u64,
320                    symbol: sym.unwrap(),
321                    addend: -4,
322                    flags: RelocationFlags::Generic {
323                        kind: RelocationKind::PltRelative,
324                        encoding: RelocationEncoding::X86Branch,
325                        size: 32,
326                    },
327                },
328            )?;
329        }
330
331        obj.write_stream(file)?;
332
333        Ok(())
334    }
335}