From d433448d6fb87df87e1c8ff555bce6566a1162a7 Mon Sep 17 00:00:00 2001 From: Folkert Date: Sun, 28 Aug 2022 13:17:50 +0200 Subject: [PATCH 1/2] don't regenerate the dynhost file if it is unchanged --- crates/linker/src/lib.rs | 58 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 55 insertions(+), 3 deletions(-) diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index c8a8853a1d..035c2b8ec4 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -136,10 +136,62 @@ fn generate_dynamic_lib( } } - let bytes = crate::generate_dylib::generate(target, &custom_names) - .unwrap_or_else(|e| internal_error!("{}", e)); + if !dynhost_is_up_to_date(target, dummy_lib_path, &custom_names) { + let bytes = crate::generate_dylib::generate(target, &custom_names) + .unwrap_or_else(|e| internal_error!("{}", e)); - std::fs::write(dummy_lib_path, &bytes).unwrap_or_else(|e| internal_error!("{}", e)) + std::fs::write(dummy_lib_path, &bytes).unwrap_or_else(|e| internal_error!("{}", e)) + } +} + +fn object_matches_target<'a>(target: &Triple, object: &object::File<'a, &'a [u8]>) -> bool { + use target_lexicon::{Architecture as TLA, OperatingSystem as TLO}; + + match target.architecture { + TLA::X86_64 => { + if object.architecture() != object::Architecture::X86_64 { + return false; + } + + let actual = object.format(); + + let expected = match target.operating_system { + TLO::Linux => object::BinaryFormat::Elf, + other => internal_error!("unexpected target {:?}", other), + }; + + actual == expected + } + TLA::Aarch64(_) => object.architecture() == object::Architecture::Aarch64, + _ => false, + } +} + +fn dynhost_is_up_to_date(target: &Triple, dummy_lib_path: &Path, custom_names: &[String]) -> bool { + if !std::path::Path::exists(dummy_lib_path) { + return false; + } + + let exec_file = fs::File::open(dummy_lib_path).unwrap_or_else(|e| internal_error!("{}", e)); + let exec_mmap = unsafe { Mmap::map(&exec_file).unwrap_or_else(|e| internal_error!("{}", e)) }; + let exec_data = &*exec_mmap; + + let object = object::File::parse(exec_data).unwrap(); + + // the user may have been cross-compiling. + // The dynhost on disk must match our current target + if !object_matches_target(target, &object) { + return false; + } + + // we made this dynhost file. For the file to be the same as what we'd generate, + // we need all symbols to be there and in the correct order + let dynamic_symbols: Vec<_> = object.exports().unwrap(); + + let it1 = dynamic_symbols.iter().map(|e| e.name()); + let it2 = custom_names.iter().map(|s| s.as_bytes()); + + it1.eq(it2) } fn is_roc_symbol(sym: &object::Symbol) -> bool { From 3e78395237fbed815e5eee491b3879b0e7c1ed6c Mon Sep 17 00:00:00 2001 From: Folkert Date: Tue, 30 Aug 2022 12:32:19 +0200 Subject: [PATCH 2/2] changes after review --- crates/linker/src/lib.rs | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/crates/linker/src/lib.rs b/crates/linker/src/lib.rs index 035c2b8ec4..bd9356150c 100644 --- a/crates/linker/src/lib.rs +++ b/crates/linker/src/lib.rs @@ -136,7 +136,7 @@ fn generate_dynamic_lib( } } - if !dynhost_is_up_to_date(target, dummy_lib_path, &custom_names) { + if !dummy_lib_is_up_to_date(target, dummy_lib_path, &custom_names) { let bytes = crate::generate_dylib::generate(target, &custom_names) .unwrap_or_else(|e| internal_error!("{}", e)); @@ -153,21 +153,27 @@ fn object_matches_target<'a>(target: &Triple, object: &object::File<'a, &'a [u8] return false; } - let actual = object.format(); - - let expected = match target.operating_system { + let target_format = match target.operating_system { TLO::Linux => object::BinaryFormat::Elf, - other => internal_error!("unexpected target {:?}", other), + TLO::Windows => object::BinaryFormat::Pe, + _ => todo!("surgical linker does not support target {:?}", target), }; - actual == expected + object.format() == target_format } TLA::Aarch64(_) => object.architecture() == object::Architecture::Aarch64, - _ => false, + _ => todo!("surgical linker does not support target {:?}", target), } } -fn dynhost_is_up_to_date(target: &Triple, dummy_lib_path: &Path, custom_names: &[String]) -> bool { +/// Checks whether the dummy `.dll/.so` is up to date, in other words that it exports exactly the +/// symbols that it is supposed to export, and is built for the right target. If this is the case, +/// we can skip rebuildingthe dummy lib. +fn dummy_lib_is_up_to_date( + target: &Triple, + dummy_lib_path: &Path, + custom_names: &[String], +) -> bool { if !std::path::Path::exists(dummy_lib_path) { return false; }