@@ -373,11 +373,41 @@ fn align_to(value: u64, align: u64) -> u64 { |
| 373 | 373 | |
| 374 | 374 | #[cfg(test)] |
| 375 | 375 | mod tests { |
| 376 | + use std::path::PathBuf; |
| 377 | + |
| 376 | 378 | use super::*; |
| 379 | + use crate::atom::{Atom, AtomFlags, AtomSection, AtomTable}; |
| 380 | + use crate::resolve::{AtomId, InputId, SymbolTable}; |
| 377 | 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 | 411 | #[test] |
| 382 | 412 | fn segment_command_preserves_reserved_section_fields() { |
| 383 | 413 | let layout = Layout { |
@@ -446,4 +476,87 @@ mod tests { |
| 446 | 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 | } |