1use object::write::{Object as Writer, Relocation, StandardSection, Symbol, SymbolSection};
29use object::{
30 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionKind, SymbolFlags, SymbolKind,
31 SymbolScope, elf,
32};
33use rucc_target::{Arch, Os, TargetInfo};
34
35use crate::section::{Binding, Data, Object, Place, Reference, Reloc, Text};
36
37const ALIGN: u64 = 16;
39
40#[derive(Debug, Clone, PartialEq, Eq)]
42pub enum Error {
43 Format {
45 triple: String,
47 },
48 Refused {
50 why: String,
52 },
53}
54
55impl std::fmt::Display for Error {
56 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
57 match self {
58 Error::Format { triple } => {
59 write!(f, "there is no object writer for {triple} in this compiler yet")
60 }
61 Error::Refused { why } => {
62 write!(f, "the object writer refused what it was given: {why}")
63 }
64 }
65 }
66}
67
68impl std::error::Error for Error {}
69
70pub fn write(text: &Text, data: &Data, target: &TargetInfo) -> Result<Vec<u8>, Error> {
77 if target.triple.arch != Arch::X86_64 || target.triple.os == Os::Darwin {
78 return Err(Error::Format { triple: target.triple.to_string() });
79 }
80 let mut obj = Writer::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
81 let section = obj.section_id(StandardSection::Text);
82 obj.append_section_data(section, &text.bytes, ALIGN);
83
84 let mut symbols = std::collections::BTreeMap::new();
88 for func in &text.funcs {
89 let id = obj.add_symbol(Symbol {
90 name: func.name.clone().into_bytes(),
91 value: func.start as u64,
92 size: func.len as u64,
93 kind: SymbolKind::Text,
94 scope: SymbolScope::Linkage,
99 weak: false,
100 section: SymbolSection::Section(section),
101 flags: SymbolFlags::None,
102 });
103 symbols.insert(func.name.clone(), id);
104 }
105
106 let mut placed = Vec::with_capacity(data.objects.len());
111 for object in &data.objects {
112 let (section, offset) = put(&mut obj, object);
113 let id = obj.add_symbol(Symbol {
114 name: object.name.clone().into_bytes(),
115 value: if object.place == Place::Merged { object.align } else { offset },
118 size: object.size,
119 kind: SymbolKind::Data,
120 scope: scope_of(object.binding),
121 weak: object.binding == Binding::Weak,
122 section,
123 flags: SymbolFlags::None,
124 });
125 symbols.insert(object.name.clone(), id);
126 placed.push((section.id(), offset));
127 }
128
129 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
130 for reloc in wanted {
131 if symbols.contains_key(&reloc.symbol) {
132 continue;
133 }
134 let id = obj.add_symbol(Symbol {
135 name: reloc.symbol.clone().into_bytes(),
136 value: 0,
137 size: 0,
138 kind: SymbolKind::Unknown,
142 scope: SymbolScope::Dynamic,
143 weak: false,
144 section: SymbolSection::Undefined,
145 flags: SymbolFlags::None,
146 });
147 symbols.insert(reloc.symbol.clone(), id);
148 }
149
150 for reloc in &text.relocs {
151 add(&mut obj, section, 0, reloc, &symbols)?;
152 }
153 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
154 let Some(section) = section else { continue };
155 for reloc in &object.relocs {
156 add(&mut obj, section, offset, reloc, &symbols)?;
157 }
158 }
159
160 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
163
164 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
165}
166
167fn put(obj: &mut Writer<'_>, object: &Object) -> (SymbolSection, u64) {
174 let section = match &object.place {
175 Place::Written => obj.section_id(StandardSection::Data),
176 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
177 Place::Zero => obj.section_id(StandardSection::UninitializedData),
178 Place::Merged => return (SymbolSection::Common, 0),
179 Place::Named(name) => {
183 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
184 }
185 };
186 let offset = if object.place == Place::Zero {
187 obj.append_section_bss(section, object.size, object.align)
188 } else {
189 obj.append_section_data(section, &object.bytes, object.align)
190 };
191 (SymbolSection::Section(section), offset)
192}
193
194fn add(
196 obj: &mut Writer<'_>,
197 section: object::write::SectionId,
198 offset: u64,
199 reloc: &Reloc,
200 symbols: &std::collections::BTreeMap<String, object::write::SymbolId>,
201) -> Result<(), Error> {
202 let r_type = r_type(reloc.kind)
203 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
204 obj.add_relocation(
205 section,
206 Relocation {
207 offset: offset + reloc.at as u64,
208 symbol: symbols[&reloc.symbol],
209 addend: reloc.addend,
210 flags: RelocationFlags::Elf { r_type },
211 },
212 )
213 .map_err(|why| Error::Refused { why: why.to_string() })
214}
215
216fn scope_of(binding: Binding) -> SymbolScope {
218 match binding {
219 Binding::Local => SymbolScope::Compilation,
220 Binding::Global | Binding::Weak => SymbolScope::Linkage,
224 }
225}
226
227fn r_type(reference: Reference) -> Option<elf::RelocationType> {
235 Some(match reference {
236 Reference::Call => elf::R_X86_64_PLT32,
237 Reference::Data => elf::R_X86_64_PC32,
238 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
239 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
240 Reference::Address { .. } => return None,
241 })
242}
243
244#[cfg(test)]
245mod tests {
246 use super::*;
247
248 use object::read::elf::Sym as _;
249 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
250 use rucc_target::{Env, Triple};
251
252 use crate::section::{Extent, Reloc};
253
254 fn target() -> TargetInfo {
256 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
257 }
258
259 fn calling(name: &str) -> Text {
261 Text {
262 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
263 funcs: vec![Extent { name: "f".to_owned(), start: 0, len: 6 }],
264 relocs: vec![Reloc {
265 at: 1,
266 symbol: name.to_owned(),
267 kind: Reference::Call,
268 addend: -4,
269 }],
270 }
271 }
272
273 #[test]
274 fn the_bytes_come_back_out_of_the_section_they_went_into() {
275 let text = calling("puts");
276 let bytes = write(&text, &Data::default(), &target()).expect("an object");
277 let file = object::File::parse(&bytes[..]).expect("a readable object");
278 let section = file.section_by_name(".text").expect("a text section");
279 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
280 }
281
282 #[test]
283 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
284 let mut text = calling("puts");
285 text.funcs.push(Extent { name: "g".to_owned(), start: 16, len: 1 });
286 text.bytes.resize(17, 0x90);
287 let bytes = write(&text, &Data::default(), &target()).expect("an object");
288 let file = object::File::parse(&bytes[..]).expect("a readable object");
289 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
290 assert_eq!(g.address(), 16);
291 assert_eq!(g.size(), 1);
292 assert_eq!(g.kind(), SymbolKind::Text);
293 assert!(g.is_global(), "a function is global until the machine IR can say otherwise");
294 }
295
296 #[test]
297 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
298 let bytes = write(&calling("puts"), &Data::default(), &target()).expect("an object");
299 let file = object::File::parse(&bytes[..]).expect("a readable object");
300 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
301 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
302 }
303
304 #[test]
305 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
306 for (reference, wanted) in
307 [(Reference::Call, elf::R_X86_64_PLT32), (Reference::Data, elf::R_X86_64_PC32)]
308 {
309 let mut text = calling("puts");
310 text.relocs[0].kind = reference;
311 let bytes = write(&text, &Data::default(), &target()).expect("an object");
312 let file = object::File::parse(&bytes[..]).expect("a readable object");
313 let section = file.section_by_name(".text").expect("a text section");
314 let (offset, reloc) = section.relocations().next().expect("one relocation");
315 assert_eq!(offset, 1);
316 assert_eq!(reloc.addend(), -4);
317 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
318 }
319 }
320
321 #[test]
322 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
323 let mut text = calling("puts");
324 text.relocs.push(Reloc {
325 at: 1,
326 symbol: "puts".to_owned(),
327 kind: Reference::Call,
328 addend: -4,
329 });
330 let bytes = write(&text, &Data::default(), &target()).expect("an object");
331 let file = object::File::parse(&bytes[..]).expect("a readable object");
332 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
333 }
334
335 #[test]
336 fn a_function_that_is_also_called_is_not_a_second_symbol() {
337 let text = calling("f");
338 let bytes = write(&text, &Data::default(), &target()).expect("an object");
339 let file = object::File::parse(&bytes[..]).expect("a readable object");
340 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
341 let f = found.next().expect("the function");
342 assert!(!f.is_undefined(), "the file defines it");
343 assert!(found.next().is_none(), "and defines it once");
344 }
345
346 #[test]
347 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
348 let bytes = write(&calling("puts"), &Data::default(), &target()).expect("an object");
349 let file = object::File::parse(&bytes[..]).expect("a readable object");
350 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
351 assert!(note.data().expect("no bytes").is_empty());
352 }
353
354 fn variable(name: &str, place: Place) -> Object {
356 Object {
357 name: name.to_owned(),
358 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
359 size: 4,
360 align: 4,
361 place,
362 binding: Binding::Global,
363 relocs: Vec::new(),
364 }
365 }
366
367 fn holding(object: Object) -> Vec<u8> {
369 let data = Data { objects: vec![object] };
370 write(&Text::default(), &data, &target()).expect("an object")
371 }
372
373 #[test]
374 fn what_a_variable_is_decides_which_section_it_goes_in() {
375 for (place, wanted) in [
376 (Place::Written, ".data"),
377 (Place::ReadOnly, ".rodata"),
378 (Place::Zero, ".bss"),
379 (Place::Named(".init_array".to_owned()), ".init_array"),
380 ] {
381 let bytes = holding(variable("x", place.clone()));
382 let file = object::File::parse(&bytes[..]).expect("a readable object");
383 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
384 assert_eq!(section.size(), 4, "{place:?}");
385 let carried = section.data().expect("the bytes").len();
388 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
389 }
390 }
391
392 #[test]
393 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
394 let mut data = Data { objects: vec![variable("first", Place::Written)] };
395 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
396 let bytes = write(&Text::default(), &data, &target()).expect("an object");
397 let file = object::File::parse(&bytes[..]).expect("a readable object");
398 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
399 assert_eq!(second.kind(), SymbolKind::Data);
400 assert_eq!(second.size(), 4);
401 assert_eq!(second.address(), 16);
405 }
406
407 #[test]
408 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
409 for (binding, global, weak) in [
410 (Binding::Global, true, false),
411 (Binding::Local, false, false),
412 (Binding::Weak, true, true),
413 ] {
414 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
415 let file = object::File::parse(&bytes[..]).expect("a readable object");
416 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
417 assert_eq!(x.is_global(), global, "{binding:?}");
418 assert_eq!(x.is_weak(), weak, "{binding:?}");
419 }
420 }
421
422 #[test]
423 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
424 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
425 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
426 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
427 assert!(x.is_common(), "the linker merges every definition of this name into one");
428 assert_eq!(x.size(), 4);
429 assert_eq!(x.address(), 0);
433 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
434 }
435
436 #[test]
437 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
438 let object = Object {
439 bytes: vec![0; 8],
440 size: 8,
441 align: 8,
442 relocs: vec![Reloc {
443 at: 0,
444 symbol: "y".to_owned(),
445 kind: Reference::Address { bytes: 8 },
446 addend: 16,
447 }],
448 ..variable("p", Place::Written)
449 };
450 let bytes = holding(object);
451 let file = object::File::parse(&bytes[..]).expect("a readable object");
452 let section = file.section_by_name(".data").expect("a data section");
453 let (offset, reloc) = section.relocations().next().expect("one relocation");
454 assert_eq!(offset, 0);
455 assert_eq!(reloc.addend(), 16);
456 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
457 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
458 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
459 }
460
461 #[test]
463 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
464 let mut data = Data { objects: vec![variable("first", Place::Written)] };
465 data.objects.push(Object {
466 bytes: vec![0; 16],
467 size: 16,
468 align: 8,
469 relocs: vec![Reloc {
470 at: 8,
471 symbol: "y".to_owned(),
472 kind: Reference::Address { bytes: 8 },
473 addend: 0,
474 }],
475 ..variable("second", Place::Written)
476 });
477 let bytes = write(&Text::default(), &data, &target()).expect("an object");
478 let file = object::File::parse(&bytes[..]).expect("a readable object");
479 let section = file.section_by_name(".data").expect("a data section");
480 let (offset, _) = section.relocations().next().expect("one relocation");
481 assert_eq!(offset, 16);
484 }
485
486 #[test]
487 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
488 let text = calling("puts");
489 for triple in [
490 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
491 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
492 ] {
493 let error =
494 write(&text, &Data::default(), &TargetInfo::new(triple)).expect_err("no writer");
495 assert!(matches!(error, Error::Format { .. }), "{error:?}");
496 }
497 }
498}