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: SymbolScope::Linkage,
96 weak: false,
97 section: SymbolSection::Section(section),
98 flags: SymbolFlags::None,
99 });
100 symbols.insert(func.name.clone(), id);
101 }
102
103 let mut placed = Vec::with_capacity(data.objects.len());
108 for object in &data.objects {
109 let (section, offset) = put(&mut obj, object);
110 let id = obj.add_symbol(Symbol {
111 name: object.name.clone().into_bytes(),
112 value: if object.place == Place::Merged { object.align } else { offset },
115 size: object.size,
116 kind: SymbolKind::Data,
117 scope: scope_of(object.binding),
118 weak: object.binding == Binding::Weak,
119 section,
120 flags: SymbolFlags::None,
121 });
122 symbols.insert(object.name.clone(), id);
123 placed.push((section.id(), offset));
124 }
125
126 let wanted = text.relocs.iter().chain(data.objects.iter().flat_map(|object| &object.relocs));
127 for reloc in wanted {
128 if symbols.contains_key(&reloc.symbol) {
129 continue;
130 }
131 let id = obj.add_symbol(Symbol {
132 name: reloc.symbol.clone().into_bytes(),
133 value: 0,
134 size: 0,
135 kind: SymbolKind::Unknown,
139 scope: SymbolScope::Dynamic,
140 weak: false,
141 section: SymbolSection::Undefined,
142 flags: SymbolFlags::None,
143 });
144 symbols.insert(reloc.symbol.clone(), id);
145 }
146
147 for reloc in &text.relocs {
148 add(&mut obj, section, 0, reloc, &symbols)?;
149 }
150 for (object, &(section, offset)) in data.objects.iter().zip(&placed) {
151 let Some(section) = section else { continue };
152 for reloc in &object.relocs {
153 add(&mut obj, section, offset, reloc, &symbols)?;
154 }
155 }
156
157 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
160
161 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
162}
163
164fn put(obj: &mut Writer<'_>, object: &Object) -> (SymbolSection, u64) {
171 let section = match &object.place {
172 Place::Written => obj.section_id(StandardSection::Data),
173 Place::ReadOnly => obj.section_id(StandardSection::ReadOnlyData),
174 Place::Zero => obj.section_id(StandardSection::UninitializedData),
175 Place::Merged => return (SymbolSection::Common, 0),
176 Place::Named(name) => {
180 obj.add_section(Vec::new(), name.clone().into_bytes(), SectionKind::Data)
181 }
182 };
183 let offset = if object.place == Place::Zero {
184 obj.append_section_bss(section, object.size, object.align)
185 } else {
186 obj.append_section_data(section, &object.bytes, object.align)
187 };
188 (SymbolSection::Section(section), offset)
189}
190
191fn add(
193 obj: &mut Writer<'_>,
194 section: object::write::SectionId,
195 offset: u64,
196 reloc: &Reloc,
197 symbols: &std::collections::BTreeMap<String, object::write::SymbolId>,
198) -> Result<(), Error> {
199 let r_type = r_type(reloc.kind)
200 .ok_or_else(|| Error::Refused { why: format!("no relocation is {:?}", reloc.kind) })?;
201 obj.add_relocation(
202 section,
203 Relocation {
204 offset: offset + reloc.at as u64,
205 symbol: symbols[&reloc.symbol],
206 addend: reloc.addend,
207 flags: RelocationFlags::Elf { r_type },
208 },
209 )
210 .map_err(|why| Error::Refused { why: why.to_string() })
211}
212
213fn scope_of(binding: Binding) -> SymbolScope {
215 match binding {
216 Binding::Local => SymbolScope::Compilation,
217 Binding::Global | Binding::Weak => SymbolScope::Linkage,
221 }
222}
223
224fn r_type(reference: Reference) -> Option<elf::RelocationType> {
232 Some(match reference {
233 Reference::Call => elf::R_X86_64_PLT32,
234 Reference::Data => elf::R_X86_64_PC32,
235 Reference::Address { bytes: 8 } => elf::R_X86_64_64,
236 Reference::Address { bytes: 4 } => elf::R_X86_64_32,
237 Reference::Address { .. } => return None,
238 })
239}
240
241#[cfg(test)]
242mod tests {
243 use super::*;
244
245 use object::read::elf::Sym as _;
246 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
247 use rucc_target::{Env, Triple};
248
249 use crate::section::{Extent, Reloc};
250
251 fn target() -> TargetInfo {
253 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
254 }
255
256 fn calling(name: &str) -> Text {
258 Text {
259 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
260 funcs: vec![Extent { name: "f".to_owned(), start: 0, len: 6 }],
261 relocs: vec![Reloc {
262 at: 1,
263 symbol: name.to_owned(),
264 kind: Reference::Call,
265 addend: -4,
266 }],
267 ..Text::default()
268 }
269 }
270
271 #[test]
272 fn the_bytes_come_back_out_of_the_section_they_went_into() {
273 let text = calling("puts");
274 let bytes = write(&text, &Data::default(), &target()).expect("an object");
275 let file = object::File::parse(&bytes[..]).expect("a readable object");
276 let section = file.section_by_name(".text").expect("a text section");
277 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
278 }
279
280 #[test]
281 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
282 let mut text = calling("puts");
283 text.funcs.push(Extent { name: "g".to_owned(), start: 16, len: 1 });
284 text.bytes.resize(17, 0x90);
285 let bytes = write(&text, &Data::default(), &target()).expect("an object");
286 let file = object::File::parse(&bytes[..]).expect("a readable object");
287 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
288 assert_eq!(g.address(), 16);
289 assert_eq!(g.size(), 1);
290 assert_eq!(g.kind(), SymbolKind::Text);
291 assert!(g.is_global(), "a function is global until the machine IR can say otherwise");
292 }
293
294 #[test]
295 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
296 let bytes = write(&calling("puts"), &Data::default(), &target()).expect("an object");
297 let file = object::File::parse(&bytes[..]).expect("a readable object");
298 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
299 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
300 }
301
302 #[test]
303 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
304 for (reference, wanted) in
305 [(Reference::Call, elf::R_X86_64_PLT32), (Reference::Data, elf::R_X86_64_PC32)]
306 {
307 let mut text = calling("puts");
308 text.relocs[0].kind = reference;
309 let bytes = write(&text, &Data::default(), &target()).expect("an object");
310 let file = object::File::parse(&bytes[..]).expect("a readable object");
311 let section = file.section_by_name(".text").expect("a text section");
312 let (offset, reloc) = section.relocations().next().expect("one relocation");
313 assert_eq!(offset, 1);
314 assert_eq!(reloc.addend(), -4);
315 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
316 }
317 }
318
319 #[test]
320 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
321 let mut text = calling("puts");
322 text.relocs.push(Reloc {
323 at: 1,
324 symbol: "puts".to_owned(),
325 kind: Reference::Call,
326 addend: -4,
327 });
328 let bytes = write(&text, &Data::default(), &target()).expect("an object");
329 let file = object::File::parse(&bytes[..]).expect("a readable object");
330 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
331 }
332
333 #[test]
334 fn a_function_that_is_also_called_is_not_a_second_symbol() {
335 let text = calling("f");
336 let bytes = write(&text, &Data::default(), &target()).expect("an object");
337 let file = object::File::parse(&bytes[..]).expect("a readable object");
338 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
339 let f = found.next().expect("the function");
340 assert!(!f.is_undefined(), "the file defines it");
341 assert!(found.next().is_none(), "and defines it once");
342 }
343
344 #[test]
345 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
346 let bytes = write(&calling("puts"), &Data::default(), &target()).expect("an object");
347 let file = object::File::parse(&bytes[..]).expect("a readable object");
348 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
349 assert!(note.data().expect("no bytes").is_empty());
350 }
351
352 fn variable(name: &str, place: Place) -> Object {
354 Object {
355 name: name.to_owned(),
356 bytes: if place == Place::Zero { Vec::new() } else { vec![1, 0, 0, 0] },
357 size: 4,
358 align: 4,
359 place,
360 binding: Binding::Global,
361 relocs: Vec::new(),
362 }
363 }
364
365 fn holding(object: Object) -> Vec<u8> {
367 let data = Data { objects: vec![object] };
368 write(&Text::default(), &data, &target()).expect("an object")
369 }
370
371 #[test]
372 fn what_a_variable_is_decides_which_section_it_goes_in() {
373 for (place, wanted) in [
374 (Place::Written, ".data"),
375 (Place::ReadOnly, ".rodata"),
376 (Place::Zero, ".bss"),
377 (Place::Named(".init_array".to_owned()), ".init_array"),
378 ] {
379 let bytes = holding(variable("x", place.clone()));
380 let file = object::File::parse(&bytes[..]).expect("a readable object");
381 let section = file.section_by_name(wanted).unwrap_or_else(|| panic!("{place:?}"));
382 assert_eq!(section.size(), 4, "{place:?}");
383 let carried = section.data().expect("the bytes").len();
386 assert_eq!(carried, if place == Place::Zero { 0 } else { 4 }, "{place:?}");
387 }
388 }
389
390 #[test]
391 fn a_variable_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
392 let mut data = Data { objects: vec![variable("first", Place::Written)] };
393 data.objects.push(Object { align: 16, ..variable("second", Place::Written) });
394 let bytes = write(&Text::default(), &data, &target()).expect("an object");
395 let file = object::File::parse(&bytes[..]).expect("a readable object");
396 let second = file.symbols().find(|s| s.name() == Ok("second")).expect("the second one");
397 assert_eq!(second.kind(), SymbolKind::Data);
398 assert_eq!(second.size(), 4);
399 assert_eq!(second.address(), 16);
403 }
404
405 #[test]
406 fn the_linkage_a_variable_had_is_the_binding_the_symbol_gets() {
407 for (binding, global, weak) in [
408 (Binding::Global, true, false),
409 (Binding::Local, false, false),
410 (Binding::Weak, true, true),
411 ] {
412 let bytes = holding(Object { binding, ..variable("x", Place::Written) });
413 let file = object::File::parse(&bytes[..]).expect("a readable object");
414 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
415 assert_eq!(x.is_global(), global, "{binding:?}");
416 assert_eq!(x.is_weak(), weak, "{binding:?}");
417 }
418 }
419
420 #[test]
421 fn a_tentative_definition_asks_the_linker_for_space_rather_than_naming_any() {
422 let bytes = holding(Object { align: 8, ..variable("x", Place::Merged) });
423 let file = object::read::elf::ElfFile64::<Endianness>::parse(&bytes[..]).expect("readable");
424 let x = file.symbols().find(|s| s.name() == Ok("x")).expect("the variable");
425 assert!(x.is_common(), "the linker merges every definition of this name into one");
426 assert_eq!(x.size(), 4);
427 assert_eq!(x.address(), 0);
431 assert_eq!(x.elf_symbol().st_value(Endianness::Little), 8);
432 }
433
434 #[test]
435 fn an_address_in_an_image_is_the_address_and_not_a_distance_to_it() {
436 let object = Object {
437 bytes: vec![0; 8],
438 size: 8,
439 align: 8,
440 relocs: vec![Reloc {
441 at: 0,
442 symbol: "y".to_owned(),
443 kind: Reference::Address { bytes: 8 },
444 addend: 16,
445 }],
446 ..variable("p", Place::Written)
447 };
448 let bytes = holding(object);
449 let file = object::File::parse(&bytes[..]).expect("a readable object");
450 let section = file.section_by_name(".data").expect("a data section");
451 let (offset, reloc) = section.relocations().next().expect("one relocation");
452 assert_eq!(offset, 0);
453 assert_eq!(reloc.addend(), 16);
454 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: elf::R_X86_64_64 });
455 let y = file.symbols().find(|s| s.name() == Ok("y")).expect("what it points at");
456 assert!(y.is_undefined(), "nothing here defines it and the linker is being asked for it");
457 }
458
459 #[test]
461 fn a_relocation_counts_from_the_start_of_the_section_and_not_of_the_image_it_is_in() {
462 let mut data = Data { objects: vec![variable("first", Place::Written)] };
463 data.objects.push(Object {
464 bytes: vec![0; 16],
465 size: 16,
466 align: 8,
467 relocs: vec![Reloc {
468 at: 8,
469 symbol: "y".to_owned(),
470 kind: Reference::Address { bytes: 8 },
471 addend: 0,
472 }],
473 ..variable("second", Place::Written)
474 });
475 let bytes = write(&Text::default(), &data, &target()).expect("an object");
476 let file = object::File::parse(&bytes[..]).expect("a readable object");
477 let section = file.section_by_name(".data").expect("a data section");
478 let (offset, _) = section.relocations().next().expect("one relocation");
479 assert_eq!(offset, 16);
482 }
483
484 #[test]
485 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
486 let text = calling("puts");
487 for triple in [
488 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
489 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
490 ] {
491 let error =
492 write(&text, &Data::default(), &TargetInfo::new(triple)).expect_err("no writer");
493 assert!(matches!(error, Error::Format { .. }), "{error:?}");
494 }
495 }
496}