@@ -373,11 +373,41 @@ fn align_to(value: u64, align: u64) -> u64 { |
| 373 | | 373 | |
| 374 | #[cfg(test)] | 374 | #[cfg(test)] |
| 375 | mod tests { | 375 | mod tests { |
| | 376 | + use std::path::PathBuf; |
| | 377 | + |
| 376 | use super::*; | 378 | use super::*; |
| | 379 | + use crate::atom::{Atom, AtomFlags, AtomSection, AtomTable}; |
| | 380 | + use crate::resolve::{AtomId, InputId, SymbolTable}; |
| 377 | use crate::section::{ | 381 | use crate::section::{ |
| 378 | - Layout, OutputSection, OutputSectionId, OutputSegment, Prot, SectionKind, PAGE_SIZE, | 382 | + build_layout, Layout, OutputSection, OutputSectionId, OutputSegment, Prot, SectionKind, |
| | 383 | + PAGE_SIZE, |
| 379 | }; | 384 | }; |
| 380 | | 385 | |
| | 386 | + fn synth_atom( |
| | 387 | + origin: InputId, |
| | 388 | + input_offset: u32, |
| | 389 | + size: u32, |
| | 390 | + align_pow2: u8, |
| | 391 | + section: AtomSection, |
| | 392 | + data: &[u8], |
| | 393 | + ) -> Atom { |
| | 394 | + Atom { |
| | 395 | + id: AtomId(0), |
| | 396 | + origin, |
| | 397 | + input_section: 1, |
| | 398 | + section, |
| | 399 | + input_offset, |
| | 400 | + size, |
| | 401 | + align_pow2, |
| | 402 | + owner: None, |
| | 403 | + alt_entries: Vec::new(), |
| | 404 | + relocs: Vec::new(), |
| | 405 | + data: data.to_vec(), |
| | 406 | + flags: AtomFlags::NONE, |
| | 407 | + parent_of: None, |
| | 408 | + } |
| | 409 | + } |
| | 410 | + |
| 381 | #[test] | 411 | #[test] |
| 382 | fn segment_command_preserves_reserved_section_fields() { | 412 | fn segment_command_preserves_reserved_section_fields() { |
| 383 | let layout = Layout { | 413 | let layout = Layout { |
@@ -446,4 +476,87 @@ mod tests { |
| 446 | < ids.iter().position(|cmd| *cmd == LC_SOURCE_VERSION) | 476 | < ids.iter().position(|cmd| *cmd == LC_SOURCE_VERSION) |
| 447 | ); | 477 | ); |
| 448 | } | 478 | } |
| | 479 | + |
| | 480 | + #[test] |
| | 481 | + fn layout_and_writer_are_deterministic_across_hundred_runs() { |
| | 482 | + let mut baseline: Option<Vec<u8>> = None; |
| | 483 | + |
| | 484 | + for _ in 0..100 { |
| | 485 | + let mut atoms = AtomTable::new(); |
| | 486 | + atoms.push(synth_atom( |
| | 487 | + InputId(1), |
| | 488 | + 0, |
| | 489 | + 8, |
| | 490 | + 3, |
| | 491 | + AtomSection::Data, |
| | 492 | + &0x1122_3344_5566_7788u64.to_le_bytes(), |
| | 493 | + )); |
| | 494 | + atoms.push(synth_atom( |
| | 495 | + InputId(0), |
| | 496 | + 4, |
| | 497 | + 4, |
| | 498 | + 2, |
| | 499 | + AtomSection::Text, |
| | 500 | + &[0xc0, 0x03, 0x5f, 0xd6], |
| | 501 | + )); |
| | 502 | + atoms.push(synth_atom( |
| | 503 | + InputId(0), |
| | 504 | + 0, |
| | 505 | + 3, |
| | 506 | + 0, |
| | 507 | + AtomSection::CStringLiterals, |
| | 508 | + b"hi\0", |
| | 509 | + )); |
| | 510 | + atoms.push(synth_atom( |
| | 511 | + InputId(0), |
| | 512 | + 24, |
| | 513 | + 16, |
| | 514 | + 3, |
| | 515 | + AtomSection::ZeroFill, |
| | 516 | + &[], |
| | 517 | + )); |
| | 518 | + atoms.push(synth_atom( |
| | 519 | + InputId(0), |
| | 520 | + 16, |
| | 521 | + 8, |
| | 522 | + 3, |
| | 523 | + AtomSection::Data, |
| | 524 | + &0x99aa_bbcc_ddee_ff00u64.to_le_bytes(), |
| | 525 | + )); |
| | 526 | + |
| | 527 | + let layout = build_layout(OutputKind::Executable, &atoms, &SymbolTable::new()); |
| | 528 | + let opts = LinkOptions { |
| | 529 | + output: Some(PathBuf::from("deterministic-a.out")), |
| | 530 | + ..LinkOptions::default() |
| | 531 | + }; |
| | 532 | + let plan = WritePlan { |
| | 533 | + dylibs: vec![DylibCmd { |
| | 534 | + cmd: LC_LOAD_DYLIB, |
| | 535 | + name: "/usr/lib/libSystem.B.dylib".into(), |
| | 536 | + timestamp: 2, |
| | 537 | + current_version: 0, |
| | 538 | + compatibility_version: 0, |
| | 539 | + }], |
| | 540 | + rpaths: vec!["@executable_path/../lib".into()], |
| | 541 | + uuid: Some([0x5a; 16]), |
| | 542 | + source_version: Some(0), |
| | 543 | + }; |
| | 544 | + |
| | 545 | + let mut bytes = Vec::new(); |
| | 546 | + write_with_atoms_and_plan( |
| | 547 | + &layout, |
| | 548 | + &atoms, |
| | 549 | + OutputKind::Executable, |
| | 550 | + &opts, |
| | 551 | + &plan, |
| | 552 | + &mut bytes, |
| | 553 | + ) |
| | 554 | + .expect("write deterministic executable"); |
| | 555 | + |
| | 556 | + match &baseline { |
| | 557 | + Some(expected) => assert_eq!(&bytes, expected), |
| | 558 | + None => baseline = Some(bytes), |
| | 559 | + } |
| | 560 | + } |
| | 561 | + } |
| 449 | } | 562 | } |