about summary refs log tree commit diff
path: root/compiler/rustc_codegen_ssa/src
diff options
context:
space:
mode:
authorDaniil Belov <70999565+BelovDV@users.noreply.github.com>2022-08-24 13:10:40 +0300
committerDaniil Belov <70999565+BelovDV@users.noreply.github.com>2022-09-12 16:45:03 +0300
commitffa83596fe216c19e9d58c8318786edd07d23d5f (patch)
treec4c1947168a229b527608a92095f38498a4755a2 /compiler/rustc_codegen_ssa/src
parent56e7678ca97e9740f7d09206f767d5bb676917f7 (diff)
downloadrust-ffa83596fe216c19e9d58c8318786edd07d23d5f.tar.gz
rust-ffa83596fe216c19e9d58c8318786edd07d23d5f.zip
change rlib format to discern native dependencies
Diffstat (limited to 'compiler/rustc_codegen_ssa/src')
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs72
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs98
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs4
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs2
4 files changed, 130 insertions, 46 deletions
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index 0d2aa483d3d..bad58d0a8a0 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -1,44 +1,16 @@
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::memmap::Mmap;
 use rustc_session::cstore::DllImport;
 use rustc_session::Session;
+use rustc_span::symbol::Symbol;
 
+use object::read::archive::ArchiveFile;
+
+use std::fmt::Display;
+use std::fs::File;
 use std::io;
 use std::path::{Path, PathBuf};
 
-pub(super) fn find_library(
-    name: &str,
-    verbatim: bool,
-    search_paths: &[PathBuf],
-    sess: &Session,
-) -> PathBuf {
-    // On Windows, static libraries sometimes show up as libfoo.a and other
-    // times show up as foo.lib
-    let oslibname = if verbatim {
-        name.to_string()
-    } else {
-        format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix)
-    };
-    let unixlibname = format!("lib{}.a", name);
-
-    for path in search_paths {
-        debug!("looking for {} inside {:?}", name, path);
-        let test = path.join(&oslibname);
-        if test.exists() {
-            return test;
-        }
-        if oslibname != unixlibname {
-            let test = path.join(&unixlibname);
-            if test.exists() {
-                return test;
-            }
-        }
-    }
-    sess.fatal(&format!(
-        "could not find native static library `{}`, \
-                         perhaps an -L flag is missing?",
-        name
-    ));
-}
-
 pub trait ArchiveBuilderBuilder {
     fn new_archive_builder<'a>(&self, sess: &'a Session) -> Box<dyn ArchiveBuilder<'a> + 'a>;
 
@@ -54,6 +26,36 @@ pub trait ArchiveBuilderBuilder {
         dll_imports: &[DllImport],
         tmpdir: &Path,
     ) -> PathBuf;
+
+    fn extract_bundled_libs(
+        &self,
+        rlib: &Path,
+        outdir: &Path,
+        bundled_lib_file_names: &FxHashSet<Symbol>,
+    ) -> Result<(), String> {
+        let message = |msg: &str, e: &dyn Display| format!("{} '{}': {}", msg, &rlib.display(), e);
+        let archive_map = unsafe {
+            Mmap::map(File::open(rlib).map_err(|e| message("failed to open file", &e))?)
+                .map_err(|e| message("failed to mmap file", &e))?
+        };
+        let archive = ArchiveFile::parse(&*archive_map)
+            .map_err(|e| message("failed to parse archive", &e))?;
+
+        for entry in archive.members() {
+            let entry = entry.map_err(|e| message("failed to read entry", &e))?;
+            let data = entry
+                .data(&*archive_map)
+                .map_err(|e| message("failed to get data from archive member", &e))?;
+            let name = std::str::from_utf8(entry.name())
+                .map_err(|e| message("failed to convert name", &e))?;
+            if !bundled_lib_file_names.contains(&Symbol::intern(name)) {
+                continue; // We need to extract only native libraries.
+            }
+            std::fs::write(&outdir.join(&name), data)
+                .map_err(|e| message("failed to write file", &e))?;
+        }
+        Ok(())
+    }
 }
 
 pub trait ArchiveBuilder<'a> {
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index a3fed88cc4b..61c5cff0d43 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1,11 +1,13 @@
 use rustc_arena::TypedArena;
 use rustc_ast::CRATE_NODE_ID;
+use rustc_data_structures::fx::FxHashSet;
 use rustc_data_structures::fx::FxIndexMap;
 use rustc_data_structures::memmap::Mmap;
 use rustc_data_structures::temp_dir::MaybeTempDir;
 use rustc_errors::{ErrorGuaranteed, Handler};
 use rustc_fs_util::fix_windows_verbatim_for_gcc;
 use rustc_hir::def_id::CrateNum;
+use rustc_metadata::find_native_static_library;
 use rustc_metadata::fs::{emit_metadata, METADATA_FILENAME};
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::SymbolExportKind;
@@ -24,7 +26,7 @@ use rustc_target::spec::crt_objects::{CrtObjects, LinkSelfContainedDefault};
 use rustc_target::spec::{LinkOutputKind, LinkerFlavor, LldFlavor, SplitDebuginfo};
 use rustc_target::spec::{PanicStrategy, RelocModel, RelroLevel, SanitizerSet, Target};
 
-use super::archive::{find_library, ArchiveBuilder, ArchiveBuilderBuilder};
+use super::archive::{ArchiveBuilder, ArchiveBuilderBuilder};
 use super::command::Command;
 use super::linker::{self, Linker};
 use super::metadata::{create_rmeta_file, MetadataPosition};
@@ -307,6 +309,9 @@ fn link_rlib<'a>(
         }
     }
 
+    // Used if packed_bundled_libs flag enabled.
+    let mut packed_bundled_libs = Vec::new();
+
     // Note that in this loop we are ignoring the value of `lib.cfg`. That is,
     // we may not be configured to actually include a static library if we're
     // adding it here. That's because later when we consume this rlib we'll
@@ -326,6 +331,8 @@ fn link_rlib<'a>(
     for lib in codegen_results.crate_info.used_libraries.iter() {
         match lib.kind {
             NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
+                if flavor == RlibFlavor::Normal && sess.opts.unstable_opts.packed_bundled_libs => {}
+            NativeLibKind::Static { bundle: None | Some(true), whole_archive: Some(true) }
                 if flavor == RlibFlavor::Normal =>
             {
                 // Don't allow mixing +bundle with +whole_archive since an rlib may contain
@@ -348,7 +355,16 @@ fn link_rlib<'a>(
         }
         if let Some(name) = lib.name {
             let location =
-                find_library(name.as_str(), lib.verbatim.unwrap_or(false), &lib_search_paths, sess);
+                find_native_static_library(name.as_str(), lib.verbatim, &lib_search_paths, sess);
+            if sess.opts.unstable_opts.packed_bundled_libs && flavor == RlibFlavor::Normal {
+                packed_bundled_libs.push(find_native_static_library(
+                    lib.filename.unwrap().as_str(),
+                    Some(true),
+                    &lib_search_paths,
+                    sess,
+                ));
+                continue;
+            }
             ab.add_archive(&location, Box::new(|_| false)).unwrap_or_else(|e| {
                 sess.fatal(&format!(
                     "failed to add native library {}: {}",
@@ -403,6 +419,12 @@ fn link_rlib<'a>(
         ab.add_file(&trailing_metadata);
     }
 
+    // Add all bundled static native library dependencies.
+    // Archives added to the end of .rlib archive, see comment above for the reason.
+    for lib in packed_bundled_libs {
+        ab.add_file(&lib)
+    }
+
     return Ok(ab);
 }
 
@@ -2350,7 +2372,15 @@ fn add_upstream_rust_crates<'a>(
         let src = &codegen_results.crate_info.used_crate_source[&cnum];
         match data[cnum.as_usize() - 1] {
             _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
-                add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
+                add_static_crate(
+                    cmd,
+                    sess,
+                    archive_builder_builder,
+                    codegen_results,
+                    tmpdir,
+                    cnum,
+                    &Default::default(),
+                );
             }
             // compiler-builtins are always placed last to ensure that they're
             // linked correctly.
@@ -2360,7 +2390,23 @@ fn add_upstream_rust_crates<'a>(
             }
             Linkage::NotLinked | Linkage::IncludedFromDylib => {}
             Linkage::Static => {
-                add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
+                let bundled_libs = if sess.opts.unstable_opts.packed_bundled_libs {
+                    codegen_results.crate_info.native_libraries[&cnum]
+                        .iter()
+                        .filter_map(|lib| lib.filename)
+                        .collect::<FxHashSet<_>>()
+                } else {
+                    Default::default()
+                };
+                add_static_crate(
+                    cmd,
+                    sess,
+                    archive_builder_builder,
+                    codegen_results,
+                    tmpdir,
+                    cnum,
+                    &bundled_libs,
+                );
 
                 // Link static native libs with "-bundle" modifier only if the crate they originate from
                 // is being linked statically to the current crate.  If it's linked dynamically
@@ -2371,6 +2417,14 @@ fn add_upstream_rust_crates<'a>(
                 // external build system already has the native dependencies defined, and it
                 // will provide them to the linker itself.
                 if sess.opts.unstable_opts.link_native_libraries {
+                    if sess.opts.unstable_opts.packed_bundled_libs {
+                        // If rlib contains native libs as archives, unpack them to tmpdir.
+                        let rlib = &src.rlib.as_ref().unwrap().0;
+                        archive_builder_builder
+                            .extract_bundled_libs(rlib, tmpdir, &bundled_libs)
+                            .unwrap_or_else(|e| sess.fatal(e));
+                    }
+
                     let mut last = (None, NativeLibKind::Unspecified, None);
                     for lib in &codegen_results.crate_info.native_libraries[&cnum] {
                         let Some(name) = lib.name else {
@@ -2420,10 +2474,17 @@ fn add_upstream_rust_crates<'a>(
                             | NativeLibKind::Framework { .. }
                             | NativeLibKind::Unspecified
                             | NativeLibKind::RawDylib => {}
-                            NativeLibKind::Static {
-                                bundle: Some(true) | None,
-                                whole_archive: _,
-                            } => {}
+                            NativeLibKind::Static { bundle: Some(true) | None, whole_archive } => {
+                                if sess.opts.unstable_opts.packed_bundled_libs {
+                                    // If rlib contains native libs as archives, they are unpacked to tmpdir.
+                                    let path = tmpdir.join(lib.filename.unwrap().as_str());
+                                    if whole_archive == Some(true) {
+                                        cmd.link_whole_rlib(&path);
+                                    } else {
+                                        cmd.link_rlib(&path);
+                                    }
+                                }
+                            }
                         }
                     }
                 }
@@ -2438,7 +2499,15 @@ fn add_upstream_rust_crates<'a>(
     // was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
     // is used)
     if let Some(cnum) = compiler_builtins {
-        add_static_crate(cmd, sess, archive_builder_builder, codegen_results, tmpdir, cnum);
+        add_static_crate(
+            cmd,
+            sess,
+            archive_builder_builder,
+            codegen_results,
+            tmpdir,
+            cnum,
+            &Default::default(),
+        );
     }
 
     // Converts a library file-stem into a cc -l argument
@@ -2471,6 +2540,7 @@ fn add_upstream_rust_crates<'a>(
         codegen_results: &CodegenResults,
         tmpdir: &Path,
         cnum: CrateNum,
+        bundled_lib_file_names: &FxHashSet<Symbol>,
     ) {
         let src = &codegen_results.crate_info.used_crate_source[&cnum];
         let cratepath = &src.rlib.as_ref().unwrap().0;
@@ -2499,6 +2569,7 @@ fn add_upstream_rust_crates<'a>(
         let dst = tmpdir.join(cratepath.file_name().unwrap());
         let name = cratepath.file_name().unwrap().to_str().unwrap();
         let name = &name[3..name.len() - 5]; // chop off lib/.rlib
+        let bundled_lib_file_names = bundled_lib_file_names.clone();
 
         sess.prof.generic_activity_with_arg("link_altering_rlib", name).run(|| {
             let canonical_name = name.replace('-', "_");
@@ -2532,6 +2603,15 @@ fn add_upstream_rust_crates<'a>(
                     let skip_because_lto =
                         upstream_rust_objects_already_included && is_rust_object && is_builtins;
 
+                    // We skip native libraries because:
+                    // 1. This native libraries won't be used from the generated rlib,
+                    //    so we can throw them away to avoid the copying work.
+                    // 2. We can't allow it to be a single remaining entry in archive
+                    //    as some linkers may complain on that.
+                    if bundled_lib_file_names.contains(&Symbol::intern(f)) {
+                        return true;
+                    }
+
                     if skip_because_cfg_say_so || skip_because_lto {
                         return true;
                     }
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 949a356fce1..e505543b27c 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -1,4 +1,3 @@
-use super::archive;
 use super::command::Command;
 use super::symbol_export;
 use rustc_span::symbol::sym;
@@ -11,6 +10,7 @@ use std::path::{Path, PathBuf};
 use std::{env, mem, str};
 
 use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
+use rustc_metadata::find_native_static_library;
 use rustc_middle::middle::dependency_format::Linkage;
 use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
 use rustc_middle::ty::TyCtxt;
@@ -514,7 +514,7 @@ impl<'a> Linker for GccLinker<'a> {
             // -force_load is the macOS equivalent of --whole-archive, but it
             // involves passing the full path to the library to link.
             self.linker_arg("-force_load");
-            let lib = archive::find_library(lib, verbatim, search_path, &self.sess);
+            let lib = find_native_static_library(lib, Some(verbatim), search_path, &self.sess);
             self.linker_arg(&lib);
         }
     }
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index 52da7abcac5..4ea75dba471 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -112,6 +112,7 @@ bitflags::bitflags! {
 pub struct NativeLib {
     pub kind: NativeLibKind,
     pub name: Option<Symbol>,
+    pub filename: Option<Symbol>,
     pub cfg: Option<ast::MetaItem>,
     pub verbatim: Option<bool>,
     pub dll_imports: Vec<cstore::DllImport>,
@@ -121,6 +122,7 @@ impl From<&cstore::NativeLib> for NativeLib {
     fn from(lib: &cstore::NativeLib) -> Self {
         NativeLib {
             kind: lib.kind,
+            filename: lib.filename,
             name: lib.name,
             cfg: lib.cfg.clone(),
             verbatim: lib.verbatim,