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)]
11pub enum BinFormat {
15 Elf,
16 Coff,
17 Macho,
18}
19
20impl BinFormat {
21 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)]
36pub enum Arch {
38 X86_64,
39 Arm,
40 Riscv32,
41 Riscv64,
42 Wasm32,
43 Wasm64,
44 Unknown,
45}
46
47impl Arch {
48 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)]
69pub enum Endian {
71 Litte,
72 Big,
73}
74
75impl Endian {
76 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)]
87pub 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 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 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 pub fn add_decl(&mut self, name: &str, decl: Decl) {
118 self.decls.push((name.into(), decl));
119 }
120
121 pub fn define(&mut self, sym: &str, data: Vec<u8>) {
123 self.sym.insert(sym.into(), data);
124 }
125
126 pub fn link(&mut self, link: Link) {
128 self.links.push(link);
129 }
130
131 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 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}