1use object::write::{Object, Relocation, StandardSection, Symbol, SymbolSection};
28use object::{
29 Architecture, BinaryFormat, Endianness, RelocationFlags, SectionKind, SymbolFlags, SymbolKind,
30 SymbolScope, elf,
31};
32use rucc_target::{Arch, Os, TargetInfo};
33
34use crate::section::{Reference, Text};
35
36const ALIGN: u64 = 16;
38
39#[derive(Debug, Clone, PartialEq, Eq)]
41pub enum Error {
42 Format {
44 triple: String,
46 },
47 Refused {
49 why: String,
51 },
52}
53
54impl std::fmt::Display for Error {
55 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56 match self {
57 Error::Format { triple } => {
58 write!(f, "there is no object writer for {triple} in this compiler yet")
59 }
60 Error::Refused { why } => {
61 write!(f, "the object writer refused what it was given: {why}")
62 }
63 }
64 }
65}
66
67impl std::error::Error for Error {}
68
69pub fn write(text: &Text, target: &TargetInfo) -> Result<Vec<u8>, Error> {
76 if target.triple.arch != Arch::X86_64 || target.triple.os == Os::Darwin {
77 return Err(Error::Format { triple: target.triple.to_string() });
78 }
79 let mut obj = Object::new(BinaryFormat::Elf, Architecture::X86_64, Endianness::Little);
80 let section = obj.section_id(StandardSection::Text);
81 obj.append_section_data(section, &text.bytes, ALIGN);
82
83 let mut symbols = std::collections::BTreeMap::new();
86 for func in &text.funcs {
87 let id = obj.add_symbol(Symbol {
88 name: func.name.clone().into_bytes(),
89 value: func.start as u64,
90 size: func.len as u64,
91 kind: SymbolKind::Text,
92 scope: SymbolScope::Linkage,
97 weak: false,
98 section: SymbolSection::Section(section),
99 flags: SymbolFlags::None,
100 });
101 symbols.insert(func.name.clone(), id);
102 }
103 for reloc in &text.relocs {
104 if symbols.contains_key(&reloc.symbol) {
105 continue;
106 }
107 let id = obj.add_symbol(Symbol {
108 name: reloc.symbol.clone().into_bytes(),
109 value: 0,
110 size: 0,
111 kind: SymbolKind::Unknown,
115 scope: SymbolScope::Dynamic,
116 weak: false,
117 section: SymbolSection::Undefined,
118 flags: SymbolFlags::None,
119 });
120 symbols.insert(reloc.symbol.clone(), id);
121 }
122
123 for reloc in &text.relocs {
124 let symbol = symbols[&reloc.symbol];
125 obj.add_relocation(
126 section,
127 Relocation {
128 offset: reloc.at as u64,
129 symbol,
130 addend: reloc.addend,
131 flags: RelocationFlags::Elf { r_type: r_type(reloc.kind) },
132 },
133 )
134 .map_err(|why| Error::Refused { why: why.to_string() })?;
135 }
136
137 obj.add_section(Vec::new(), b".note.GNU-stack".to_vec(), SectionKind::Metadata);
140
141 obj.write().map_err(|why| Error::Refused { why: why.to_string() })
142}
143
144fn r_type(reference: Reference) -> elf::RelocationType {
151 match reference {
152 Reference::Call => elf::R_X86_64_PLT32,
153 Reference::Data => elf::R_X86_64_PC32,
154 }
155}
156
157#[cfg(test)]
158mod tests {
159 use super::*;
160
161 use object::read::{Object as _, ObjectSection as _, ObjectSymbol as _};
162 use rucc_target::{Env, Triple};
163
164 use crate::section::{Extent, Reloc};
165
166 fn target() -> TargetInfo {
168 TargetInfo::new(Triple::new(Arch::X86_64, Os::Linux, Env::Gnu))
169 }
170
171 fn calling(name: &str) -> Text {
173 Text {
174 bytes: vec![0xe8, 0, 0, 0, 0, 0xc3],
175 funcs: vec![Extent { name: "f".to_owned(), start: 0, len: 6 }],
176 relocs: vec![Reloc {
177 at: 1,
178 symbol: name.to_owned(),
179 kind: Reference::Call,
180 addend: -4,
181 }],
182 }
183 }
184
185 #[test]
186 fn the_bytes_come_back_out_of_the_section_they_went_into() {
187 let text = calling("puts");
188 let bytes = write(&text, &target()).expect("an object");
189 let file = object::File::parse(&bytes[..]).expect("a readable object");
190 let section = file.section_by_name(".text").expect("a text section");
191 assert_eq!(section.data().expect("the bytes"), &text.bytes[..]);
192 }
193
194 #[test]
195 fn a_function_is_a_symbol_that_says_where_it_is_and_how_long_it_is() {
196 let mut text = calling("puts");
197 text.funcs.push(Extent { name: "g".to_owned(), start: 16, len: 1 });
198 text.bytes.resize(17, 0x90);
199 let bytes = write(&text, &target()).expect("an object");
200 let file = object::File::parse(&bytes[..]).expect("a readable object");
201 let g = file.symbols().find(|s| s.name() == Ok("g")).expect("the second function");
202 assert_eq!(g.address(), 16);
203 assert_eq!(g.size(), 1);
204 assert_eq!(g.kind(), SymbolKind::Text);
205 assert!(g.is_global(), "a function is global until the machine IR can say otherwise");
206 }
207
208 #[test]
209 fn a_name_this_file_does_not_define_is_left_for_the_linker_to_find() {
210 let bytes = write(&calling("puts"), &target()).expect("an object");
211 let file = object::File::parse(&bytes[..]).expect("a readable object");
212 let puts = file.symbols().find(|s| s.name() == Ok("puts")).expect("the callee");
213 assert!(puts.is_undefined(), "the file does not define it and must not claim to");
214 }
215
216 #[test]
217 fn a_call_asks_for_the_relocation_a_stub_may_answer_and_a_load_asks_for_the_one_that_may_not() {
218 for (reference, wanted) in
219 [(Reference::Call, elf::R_X86_64_PLT32), (Reference::Data, elf::R_X86_64_PC32)]
220 {
221 let mut text = calling("puts");
222 text.relocs[0].kind = reference;
223 let bytes = write(&text, &target()).expect("an object");
224 let file = object::File::parse(&bytes[..]).expect("a readable object");
225 let section = file.section_by_name(".text").expect("a text section");
226 let (offset, reloc) = section.relocations().next().expect("one relocation");
227 assert_eq!(offset, 1);
228 assert_eq!(reloc.addend(), -4);
229 assert_eq!(reloc.flags(), RelocationFlags::Elf { r_type: wanted });
230 }
231 }
232
233 #[test]
234 fn a_name_wanted_twice_is_one_symbol_rather_than_two() {
235 let mut text = calling("puts");
236 text.relocs.push(Reloc {
237 at: 1,
238 symbol: "puts".to_owned(),
239 kind: Reference::Call,
240 addend: -4,
241 });
242 let bytes = write(&text, &target()).expect("an object");
243 let file = object::File::parse(&bytes[..]).expect("a readable object");
244 assert_eq!(file.symbols().filter(|s| s.name() == Ok("puts")).count(), 1);
245 }
246
247 #[test]
248 fn a_function_that_is_also_called_is_not_a_second_symbol() {
249 let text = calling("f");
250 let bytes = write(&text, &target()).expect("an object");
251 let file = object::File::parse(&bytes[..]).expect("a readable object");
252 let mut found = file.symbols().filter(|s| s.name() == Ok("f"));
253 let f = found.next().expect("the function");
254 assert!(!f.is_undefined(), "the file defines it");
255 assert!(found.next().is_none(), "and defines it once");
256 }
257
258 #[test]
259 fn the_marker_that_says_the_stack_is_not_executable_is_written() {
260 let bytes = write(&calling("puts"), &target()).expect("an object");
261 let file = object::File::parse(&bytes[..]).expect("a readable object");
262 let note = file.section_by_name(".note.GNU-stack").expect("the marker");
263 assert!(note.data().expect("no bytes").is_empty());
264 }
265
266 #[test]
267 fn a_platform_this_does_not_write_is_said_so_rather_than_written_as_elf() {
268 let text = calling("puts");
269 for triple in [
270 Triple::new(Arch::Aarch64, Os::Linux, Env::Gnu),
271 Triple::new(Arch::X86_64, Os::Darwin, Env::Gnu),
272 ] {
273 let error = write(&text, &TargetInfo::new(triple)).expect_err("no writer");
274 assert!(matches!(error, Error::Format { .. }), "{error:?}");
275 }
276 }
277}