1use alloc::collections::BTreeMap;
12use alloc::string::String;
13use alloc::vec::Vec;
14
15use crate::error::LinkError;
16use crate::image::{Image, OutputSection};
17use crate::object::{Object, Width};
18
19#[derive(Clone, PartialEq, Eq, Debug, Default)]
48pub struct Linker {
49 base_address: u64,
50 entry: Option<String>,
51}
52
53impl Linker {
54 #[must_use]
57 pub fn new() -> Self {
58 Self::default()
59 }
60
61 #[must_use]
79 pub const fn base_address(mut self, address: u64) -> Self {
80 self.base_address = address;
81 self
82 }
83
84 #[must_use]
102 pub fn entry(mut self, symbol: impl Into<String>) -> Self {
103 self.entry = Some(symbol.into());
104 self
105 }
106
107 pub fn link(&self, objects: &[Object]) -> Result<Image, LinkError> {
138 let (mut sections, contrib) = merge_sections(objects)?;
139 place_sections(&mut sections, self.base_address)?;
140 let symbols = resolve_symbols(objects, §ions, &contrib)?;
141 patch_relocations(objects, &mut sections, &symbols, &contrib)?;
142 let entry = resolve_entry(self.entry.as_deref(), &symbols)?;
143
144 Ok(Image {
145 sections,
146 symbols,
147 entry,
148 })
149 }
150}
151
152type SectionIndex<'a> = BTreeMap<&'a str, usize>;
155
156type Contributions<'a> = Vec<BTreeMap<&'a str, u64>>;
159
160fn merge_sections(
164 objects: &[Object],
165) -> Result<(Vec<OutputSection>, Contributions<'_>), LinkError> {
166 let mut index: SectionIndex<'_> = BTreeMap::new();
167 let mut sections: Vec<OutputSection> = Vec::new();
168 let mut contrib: Contributions<'_> = Vec::with_capacity(objects.len());
169
170 for object in objects {
171 let mut object_contrib: BTreeMap<&str, u64> = BTreeMap::new();
172 for section in &object.sections {
173 let name = section.name.as_str();
174 let slot = match index.get(name) {
175 Some(&i) => i,
176 None => {
177 let i = sections.len();
178 index.insert(name, i);
179 sections.push(OutputSection {
180 name: section.name.clone(),
181 address: 0,
182 data: Vec::new(),
183 });
184 i
185 }
186 };
187 let base = sections[slot].data.len() as u64;
188 sections[slot].data.extend_from_slice(§ion.data);
189 object_contrib.insert(name, base);
190 }
191 contrib.push(object_contrib);
192 }
193
194 Ok((sections, contrib))
195}
196
197fn place_sections(sections: &mut [OutputSection], base_address: u64) -> Result<(), LinkError> {
199 let mut cursor = base_address;
200 for section in sections.iter_mut() {
201 section.address = cursor;
202 cursor = cursor
203 .checked_add(section.data.len() as u64)
204 .ok_or(LinkError::LayoutOverflow)?;
205 }
206 Ok(())
207}
208
209fn resolve_symbols(
212 objects: &[Object],
213 sections: &[OutputSection],
214 contrib: &Contributions<'_>,
215) -> Result<BTreeMap<String, u64>, LinkError> {
216 let index = section_index(sections);
217 let mut symbols: BTreeMap<String, u64> = BTreeMap::new();
218
219 for (object, object_contrib) in objects.iter().zip(contrib) {
220 for symbol in &object.symbols {
221 let address = symbol_address(
222 object,
223 object_contrib,
224 &index,
225 sections,
226 &symbol.section,
227 symbol.offset,
228 )?;
229 if symbols.insert(symbol.name.clone(), address).is_some() {
230 return Err(LinkError::DuplicateSymbol {
231 name: symbol.name.clone(),
232 });
233 }
234 }
235 }
236
237 Ok(symbols)
238}
239
240fn patch_relocations(
243 objects: &[Object],
244 sections: &mut [OutputSection],
245 symbols: &BTreeMap<String, u64>,
246 contrib: &Contributions<'_>,
247) -> Result<(), LinkError> {
248 let index: BTreeMap<String, usize> = sections
250 .iter()
251 .enumerate()
252 .map(|(i, section)| (section.name.clone(), i))
253 .collect();
254
255 for (object, object_contrib) in objects.iter().zip(contrib) {
256 for relocation in &object.relocations {
257 let base = section_base(object, object_contrib, &relocation.section)?;
258 let slot = index
259 .get(relocation.section.as_str())
260 .copied()
261 .ok_or_else(|| LinkError::InvalidSection {
262 object: object.name.clone(),
263 section: relocation.section.clone(),
264 })?;
265
266 let section_len = object
268 .section_data(&relocation.section)
269 .map_or(0, <[u8]>::len) as u64;
270 let end = relocation
271 .offset
272 .checked_add(relocation.width.bytes() as u64)
273 .filter(|&end| end <= section_len)
274 .ok_or_else(|| LinkError::RelocationOutOfRange {
275 object: object.name.clone(),
276 section: relocation.section.clone(),
277 offset: relocation.offset,
278 })?;
279
280 let target = symbols
281 .get(relocation.target.as_str())
282 .copied()
283 .ok_or_else(|| LinkError::UndefinedSymbol {
284 name: relocation.target.clone(),
285 object: object.name.clone(),
286 })?;
287
288 let value = i128::from(target) + i128::from(relocation.addend);
289 if value < 0 || value > relocation.width.max_value() {
290 return Err(LinkError::RelocationOverflow {
291 target: relocation.target.clone(),
292 width: relocation.width,
293 });
294 }
295
296 let start = (base + relocation.offset) as usize;
299 let bytes = &mut sections[slot].data[start..(base + end) as usize];
300 match relocation.width {
301 Width::U32 => bytes.copy_from_slice(&(value as u32).to_le_bytes()),
302 Width::U64 => bytes.copy_from_slice(&(value as u64).to_le_bytes()),
303 }
304 }
305 }
306
307 Ok(())
308}
309
310fn resolve_entry(
312 entry: Option<&str>,
313 symbols: &BTreeMap<String, u64>,
314) -> Result<Option<u64>, LinkError> {
315 match entry {
316 Some(name) => symbols
317 .get(name)
318 .copied()
319 .map(Some)
320 .ok_or_else(|| LinkError::UndefinedEntry { name: name.into() }),
321 None => Ok(None),
322 }
323}
324
325fn section_index(sections: &[OutputSection]) -> SectionIndex<'_> {
327 sections
328 .iter()
329 .enumerate()
330 .map(|(i, section)| (section.name.as_str(), i))
331 .collect()
332}
333
334fn section_base(
337 object: &Object,
338 object_contrib: &BTreeMap<&str, u64>,
339 section: &str,
340) -> Result<u64, LinkError> {
341 object_contrib
342 .get(section)
343 .copied()
344 .ok_or_else(|| LinkError::InvalidSection {
345 object: object.name.clone(),
346 section: section.into(),
347 })
348}
349
350fn symbol_address(
353 object: &Object,
354 object_contrib: &BTreeMap<&str, u64>,
355 index: &SectionIndex<'_>,
356 sections: &[OutputSection],
357 section: &str,
358 offset: u64,
359) -> Result<u64, LinkError> {
360 let base = section_base(object, object_contrib, section)?;
361 let slot = index
362 .get(section)
363 .copied()
364 .ok_or_else(|| LinkError::InvalidSection {
365 object: object.name.clone(),
366 section: section.into(),
367 })?;
368 sections[slot]
369 .address
370 .checked_add(base)
371 .and_then(|a| a.checked_add(offset))
372 .ok_or(LinkError::LayoutOverflow)
373}
374
375pub fn link(objects: &[Object]) -> Result<Image, LinkError> {
405 Linker::new().link(objects)
406}
407
408#[cfg(test)]
409#[allow(
410 clippy::unwrap_used,
411 reason = "tests build known-valid links, so they cannot fail"
412)]
413mod tests {
414 use super::{Linker, link};
415 use crate::error::LinkError;
416 use crate::object::{Object, Width};
417
418 #[test]
419 fn test_same_named_sections_merge_in_object_order() {
420 let mut a = Object::new("a");
421 a.section(".text", [1, 2]);
422 let mut b = Object::new("b");
423 b.section(".text", [3, 4, 5]);
424
425 let image = link(&[a, b]).unwrap();
426 assert_eq!(image.section(".text").unwrap().data(), &[1, 2, 3, 4, 5]);
427 }
428
429 #[test]
430 fn test_sections_are_placed_end_to_end_from_the_base() {
431 let mut obj = Object::new("o");
432 obj.section(".text", [0u8; 4]);
433 obj.section(".data", [0u8; 8]);
434
435 let image = Linker::new().base_address(0x1000).link(&[obj]).unwrap();
436 assert_eq!(image.section(".text").unwrap().address(), 0x1000);
437 assert_eq!(image.section(".data").unwrap().address(), 0x1004);
438 }
439
440 #[test]
441 fn test_symbol_resolves_to_section_plus_contribution_plus_offset() {
442 let mut a = Object::new("a");
443 a.section(".text", [0u8; 4]);
444 a.define("a_fn", ".text", 2);
445 let mut b = Object::new("b");
446 b.section(".text", [0u8; 4]);
447 b.define("b_fn", ".text", 1);
448
449 let image = Linker::new().base_address(0x10).link(&[a, b]).unwrap();
450 assert_eq!(image.symbol("a_fn"), Some(0x12));
451 assert_eq!(image.symbol("b_fn"), Some(0x10 + 4 + 1));
452 }
453
454 #[test]
455 fn test_relocation_is_patched_with_the_target_address() {
456 let mut code = Object::new("code");
457 code.section(".text", [0u8; 8]);
458 code.define("target", ".text", 4);
459
460 let mut data = Object::new("data");
461 data.section(".data", [0u8; 4]);
462 data.relocate(".data", 0, "target", Width::U32, 0);
463
464 let image = Linker::new()
465 .base_address(0x100)
466 .link(&[code, data])
467 .unwrap();
468 let slot = image.section(".data").unwrap().data();
469 assert_eq!(u32::from_le_bytes(slot.try_into().unwrap()), 0x104);
470 }
471
472 #[test]
473 fn test_relocation_addend_is_added_to_the_address() {
474 let mut obj = Object::new("o");
475 obj.section(".text", [0u8; 8]);
476 obj.define("base", ".text", 0);
477 obj.section(".data", [0u8; 8]);
478 obj.relocate(".data", 0, "base", Width::U64, 16);
479
480 let image = Linker::new().base_address(0x1000).link(&[obj]).unwrap();
481 let slot = image.section(".data").unwrap().data();
482 assert_eq!(u64::from_le_bytes(slot.try_into().unwrap()), 0x1010);
484 }
485
486 #[test]
487 fn test_duplicate_symbol_is_rejected() {
488 let mut a = Object::new("a");
489 a.section(".text", [0u8; 1]);
490 a.define("dup", ".text", 0);
491 let mut b = Object::new("b");
492 b.section(".text", [0u8; 1]);
493 b.define("dup", ".text", 0);
494
495 assert_eq!(
496 link(&[a, b]),
497 Err(LinkError::DuplicateSymbol { name: "dup".into() })
498 );
499 }
500
501 #[test]
502 fn test_undefined_relocation_target_is_rejected() {
503 let mut obj = Object::new("o");
504 obj.section(".data", [0u8; 8]);
505 obj.relocate(".data", 0, "nowhere", Width::U64, 0);
506
507 assert_eq!(
508 link(&[obj]),
509 Err(LinkError::UndefinedSymbol {
510 name: "nowhere".into(),
511 object: "o".into(),
512 })
513 );
514 }
515
516 #[test]
517 fn test_undefined_entry_is_rejected() {
518 let mut obj = Object::new("o");
519 obj.section(".text", [0u8; 1]);
520
521 assert_eq!(
522 Linker::new().entry("_start").link(&[obj]),
523 Err(LinkError::UndefinedEntry {
524 name: "_start".into()
525 })
526 );
527 }
528
529 #[test]
530 fn test_symbol_in_a_missing_section_is_rejected() {
531 let mut obj = Object::new("o");
532 obj.section(".text", [0u8; 4]);
533 obj.define("ghost", ".rodata", 0); assert_eq!(
536 link(&[obj]),
537 Err(LinkError::InvalidSection {
538 object: "o".into(),
539 section: ".rodata".into(),
540 })
541 );
542 }
543
544 #[test]
545 fn test_relocation_past_the_section_end_is_rejected() {
546 let mut obj = Object::new("o");
547 obj.section(".text", [0u8; 4]);
548 obj.define("t", ".text", 0);
549 obj.section(".data", [0u8; 4]);
550 obj.relocate(".data", 1, "t", Width::U64, 0); assert_eq!(
553 link(&[obj]),
554 Err(LinkError::RelocationOutOfRange {
555 object: "o".into(),
556 section: ".data".into(),
557 offset: 1,
558 })
559 );
560 }
561
562 #[test]
563 fn test_address_too_large_for_width_is_rejected() {
564 let mut obj = Object::new("o");
565 obj.section(".text", [0u8; 4]);
566 obj.define("t", ".text", 0);
567 obj.section(".data", [0u8; 4]);
568 obj.relocate(".data", 0, "t", Width::U32, 0);
569
570 assert_eq!(
572 Linker::new().base_address(0x1_0000_0000).link(&[obj]),
573 Err(LinkError::RelocationOverflow {
574 target: "t".into(),
575 width: Width::U32,
576 })
577 );
578 }
579
580 #[test]
581 fn test_negative_addend_below_zero_is_rejected() {
582 let mut obj = Object::new("o");
583 obj.section(".text", [0u8; 4]);
584 obj.define("t", ".text", 0); obj.section(".data", [0u8; 8]);
586 obj.relocate(".data", 0, "t", Width::U64, -1); assert_eq!(
589 link(&[obj]),
590 Err(LinkError::RelocationOverflow {
591 target: "t".into(),
592 width: Width::U64,
593 })
594 );
595 }
596
597 #[test]
598 fn test_negative_addend_within_range_resolves() {
599 let mut obj = Object::new("o");
600 obj.section(".text", [0u8; 8]);
601 obj.define("t", ".text", 4); obj.section(".data", [0u8; 8]);
603 obj.relocate(".data", 0, "t", Width::U64, -4);
604
605 let image = Linker::new().base_address(0x100).link(&[obj]).unwrap();
606 let slot = image.section(".data").unwrap().data();
607 assert_eq!(u64::from_le_bytes(slot.try_into().unwrap()), 0x100);
608 }
609
610 #[test]
611 fn test_link_is_deterministic() {
612 let build = || {
613 let mut a = Object::new("a");
614 a.section(".text", [1, 2, 3]);
615 a.define("a", ".text", 0);
616 let mut b = Object::new("b");
617 b.section(".data", [4, 5]);
618 b.define("b", ".data", 1);
619 [a, b]
620 };
621 assert_eq!(link(&build()).unwrap(), link(&build()).unwrap());
622 }
623}