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