about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_passes/src/feature_gate.rs8
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs103
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs11
-rw-r--r--compiler/rustc_feature/src/accepted.rs4
-rw-r--r--compiler/rustc_feature/src/active.rs4
-rw-r--r--compiler/rustc_llvm/src/lib.rs2
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs42
-rw-r--r--compiler/rustc_session/src/config.rs63
-rw-r--r--compiler/rustc_session/src/cstore.rs6
-rw-r--r--compiler/rustc_session/src/utils.rs20
10 files changed, 185 insertions, 78 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index 5b6147c7223..a778d8c5470 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -387,13 +387,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
         if attr.has_name(sym::link) {
             for nested_meta in attr.meta_item_list().unwrap_or_default() {
                 if nested_meta.has_name(sym::modifiers) {
-                    gate_feature_post!(
-                        self,
-                        native_link_modifiers,
-                        nested_meta.span(),
-                        "native link modifiers are experimental"
-                    );
-
                     if let Some(modifiers) = nested_meta.value_str() {
                         for modifier in modifiers.as_str().split(',') {
                             if let Some(modifier) = modifier.strip_prefix(&['+', '-']) {
@@ -412,7 +405,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                                 gate_modifier!(
                                     "bundle" => native_link_modifiers_bundle
                                     "verbatim" => native_link_modifiers_verbatim
-                                    "whole-archive" => native_link_modifiers_whole_archive
                                     "as-needed" => native_link_modifiers_as_needed
                                 );
                             }
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index dbbbbd5ecc0..548ae0e411d 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -1844,7 +1844,7 @@ fn linker_with_args<'a, B: ArchiveBuilder<'a>>(
     // This change is somewhat breaking in practice due to local static libraries being linked
     // as whole-archive (#85144), so removing whole-archive may be a pre-requisite.
     if sess.opts.debugging_opts.link_native_libraries {
-        add_local_native_libraries(cmd, sess, codegen_results);
+        add_local_native_libraries(cmd, sess, codegen_results, crate_type);
     }
 
     // Upstream rust libraries and their nobundle static libraries
@@ -2016,6 +2016,16 @@ fn add_order_independent_options(
     add_rpath_args(cmd, sess, codegen_results, out_filename);
 }
 
+// A dylib may reexport symbols from the linked rlib or native static library.
+// Even if some symbol is reexported it's still not necessarily counted as used and may be
+// dropped, at least with `ld`-like ELF linkers. So we have to link some rlibs and static
+// libraries as whole-archive to avoid losing reexported symbols.
+// FIXME: Find a way to mark reexported symbols as used and avoid this use of whole-archive.
+fn default_to_whole_archive(sess: &Session, crate_type: CrateType, cmd: &dyn Linker) -> bool {
+    crate_type == CrateType::Dylib
+        && !(sess.target.limit_rdylib_exports && cmd.exported_symbol_means_used_symbol())
+}
+
 /// # Native library linking
 ///
 /// User-supplied library search paths (-L on the command line). These are the same paths used to
@@ -2029,6 +2039,7 @@ fn add_local_native_libraries(
     cmd: &mut dyn Linker,
     sess: &Session,
     codegen_results: &CodegenResults,
+    crate_type: CrateType,
 ) {
     let filesearch = sess.target_filesearch(PathKind::All);
     for search_path in filesearch.search_paths() {
@@ -2046,14 +2057,18 @@ fn add_local_native_libraries(
         codegen_results.crate_info.used_libraries.iter().filter(|l| relevant_lib(sess, l));
 
     let search_path = OnceCell::new();
-    let mut last = (NativeLibKind::Unspecified, None);
+    let mut last = (None, NativeLibKind::Unspecified, None);
     for lib in relevant_libs {
         let Some(name) = lib.name else {
             continue;
         };
 
         // Skip if this library is the same as the last.
-        last = if (lib.kind, lib.name) == last { continue } else { (lib.kind, lib.name) };
+        last = if (lib.name, lib.kind, lib.verbatim) == last {
+            continue;
+        } else {
+            (lib.name, lib.kind, lib.verbatim)
+        };
 
         let verbatim = lib.verbatim.unwrap_or(false);
         match lib.kind {
@@ -2064,15 +2079,19 @@ fn add_local_native_libraries(
             NativeLibKind::Framework { as_needed } => {
                 cmd.link_framework(name, as_needed.unwrap_or(true))
             }
-            NativeLibKind::Static { bundle: None | Some(true), .. }
-            | NativeLibKind::Static { whole_archive: Some(true), .. } => {
-                cmd.link_whole_staticlib(
-                    name,
-                    verbatim,
-                    &search_path.get_or_init(|| archive_search_paths(sess)),
-                );
+            NativeLibKind::Static { whole_archive, .. } => {
+                if whole_archive == Some(true)
+                    || (whole_archive == None && default_to_whole_archive(sess, crate_type, cmd))
+                {
+                    cmd.link_whole_staticlib(
+                        name,
+                        verbatim,
+                        &search_path.get_or_init(|| archive_search_paths(sess)),
+                    );
+                } else {
+                    cmd.link_staticlib(name, verbatim)
+                }
             }
-            NativeLibKind::Static { .. } => cmd.link_staticlib(name, verbatim),
             NativeLibKind::RawDylib => {
                 // FIXME(#58713): Proper handling for raw dylibs.
                 bug!("raw_dylib feature not yet implemented");
@@ -2197,34 +2216,37 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
                 // external build system already has the native dependencies defined, and it
                 // will provide them to the linker itself.
                 if sess.opts.debugging_opts.link_native_libraries {
-                    let mut last = None;
+                    let mut last = (None, NativeLibKind::Unspecified, None);
                     for lib in &codegen_results.crate_info.native_libraries[&cnum] {
+                        let Some(name) = lib.name else {
+                            continue;
+                        };
                         if !relevant_lib(sess, lib) {
-                            // Skip libraries if they are disabled by `#[link(cfg=...)]`
                             continue;
                         }
 
                         // Skip if this library is the same as the last.
-                        if last == lib.name {
+                        last = if (lib.name, lib.kind, lib.verbatim) == last {
                             continue;
-                        }
-
-                        if let Some(static_lib_name) = lib.name {
-                            if let NativeLibKind::Static { bundle: Some(false), whole_archive } =
-                                lib.kind
+                        } else {
+                            (lib.name, lib.kind, lib.verbatim)
+                        };
+
+                        if let NativeLibKind::Static { bundle: Some(false), whole_archive } =
+                            lib.kind
+                        {
+                            let verbatim = lib.verbatim.unwrap_or(false);
+                            if whole_archive == Some(true)
+                                || (whole_archive == None
+                                    && default_to_whole_archive(sess, crate_type, cmd))
                             {
-                                let verbatim = lib.verbatim.unwrap_or(false);
-                                if whole_archive == Some(true) {
-                                    cmd.link_whole_staticlib(
-                                        static_lib_name,
-                                        verbatim,
-                                        search_path.get_or_init(|| archive_search_paths(sess)),
-                                    );
-                                } else {
-                                    cmd.link_staticlib(static_lib_name, verbatim);
-                                }
-
-                                last = lib.name;
+                                cmd.link_whole_staticlib(
+                                    name,
+                                    verbatim,
+                                    search_path.get_or_init(|| archive_search_paths(sess)),
+                                );
+                            } else {
+                                cmd.link_staticlib(name, verbatim);
                             }
                         }
                     }
@@ -2282,15 +2304,10 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
         let cratepath = &src.rlib.as_ref().unwrap().0;
 
         let mut link_upstream = |path: &Path| {
-            // If we're creating a dylib, then we need to include the
-            // whole of each object in our archive into that artifact. This is
-            // because a `dylib` can be reused as an intermediate artifact.
-            //
-            // Note, though, that we don't want to include the whole of a
-            // compiler-builtins crate (e.g., compiler-rt) because it'll get
-            // repeatedly linked anyway.
+            // We don't want to include the whole compiler-builtins crate (e.g., compiler-rt)
+            // regardless of the default because it'll get repeatedly linked anyway.
             let path = fix_windows_verbatim_for_gcc(path);
-            if crate_type == CrateType::Dylib
+            if default_to_whole_archive(sess, crate_type, cmd)
                 && codegen_results.crate_info.compiler_builtins != Some(cnum)
             {
                 cmd.link_whole_rlib(&path);
@@ -2401,7 +2418,7 @@ fn add_upstream_native_libraries(
     sess: &Session,
     codegen_results: &CodegenResults,
 ) {
-    let mut last = (NativeLibKind::Unspecified, None);
+    let mut last = (None, NativeLibKind::Unspecified, None);
     for &cnum in &codegen_results.crate_info.used_crates {
         for lib in codegen_results.crate_info.native_libraries[&cnum].iter() {
             let Some(name) = lib.name else {
@@ -2412,7 +2429,11 @@ fn add_upstream_native_libraries(
             }
 
             // Skip if this library is the same as the last.
-            last = if (lib.kind, lib.name) == last { continue } else { (lib.kind, lib.name) };
+            last = if (lib.name, lib.kind, lib.verbatim) == last {
+                continue;
+            } else {
+                (lib.name, lib.kind, lib.verbatim)
+            };
 
             let verbatim = lib.verbatim.unwrap_or(false);
             match lib.kind {
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index a838787381d..3a66bfafaf3 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -186,6 +186,9 @@ pub trait Linker {
     fn no_crt_objects(&mut self);
     fn no_default_libraries(&mut self);
     fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
+    fn exported_symbol_means_used_symbol(&self) -> bool {
+        true
+    }
     fn subsystem(&mut self, subsystem: &str);
     fn group_start(&mut self);
     fn group_end(&mut self);
@@ -724,6 +727,10 @@ impl<'a> Linker for GccLinker<'a> {
         }
     }
 
+    fn exported_symbol_means_used_symbol(&self) -> bool {
+        self.sess.target.is_like_windows || self.sess.target.is_like_osx
+    }
+
     fn subsystem(&mut self, subsystem: &str) {
         self.linker_arg("--subsystem");
         self.linker_arg(&subsystem);
@@ -1471,6 +1478,10 @@ impl<'a> Linker for L4Bender<'a> {
         return;
     }
 
+    fn exported_symbol_means_used_symbol(&self) -> bool {
+        false
+    }
+
     fn subsystem(&mut self, subsystem: &str) {
         self.cmd.arg(&format!("--subsystem {}", subsystem));
     }
diff --git a/compiler/rustc_feature/src/accepted.rs b/compiler/rustc_feature/src/accepted.rs
index ace0c9df58d..e37251c9c24 100644
--- a/compiler/rustc_feature/src/accepted.rs
+++ b/compiler/rustc_feature/src/accepted.rs
@@ -215,6 +215,10 @@ declare_features! (
     /// Allows patterns with concurrent by-move and by-ref bindings.
     /// For example, you can write `Foo(a, ref b)` where `a` is by-move and `b` is by-ref.
     (accepted, move_ref_pattern, "1.49.0", Some(68354), None),
+    /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]`
+    (accepted, native_link_modifiers, "1.61.0", Some(81490), None),
+    /// Allows specifying the whole-archive link modifier
+    (accepted, native_link_modifiers_whole_archive, "1.61.0", Some(81490), None),
     /// Allows using `#![no_std]`.
     (accepted, no_std, "1.6.0", None, None),
     /// Allows defining identifiers beyond ASCII.
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index feef7295254..90b35b5a83f 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -446,16 +446,12 @@ declare_features! (
     (active, must_not_suspend, "1.57.0", Some(83310), None),
     /// Allows using `#[naked]` on functions.
     (active, naked_functions, "1.9.0", Some(32408), None),
-    /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]`
-    (active, native_link_modifiers, "1.53.0", Some(81490), None),
     /// Allows specifying the as-needed link modifier
     (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None),
     /// Allows specifying the bundle link modifier
     (active, native_link_modifiers_bundle, "1.53.0", Some(81490), None),
     /// Allows specifying the verbatim link modifier
     (active, native_link_modifiers_verbatim, "1.53.0", Some(81490), None),
-    /// Allows specifying the whole-archive link modifier
-    (active, native_link_modifiers_whole_archive, "1.53.0", Some(81490), None),
     /// Allow negative trait implementations.
     (active, negative_impls, "1.44.0", Some(68318), None),
     /// Allows the `!` type. Does not imply 'exhaustive_patterns' (below) any more.
diff --git a/compiler/rustc_llvm/src/lib.rs b/compiler/rustc_llvm/src/lib.rs
index 8476c2bfcc4..0324ac3641e 100644
--- a/compiler/rustc_llvm/src/lib.rs
+++ b/compiler/rustc_llvm/src/lib.rs
@@ -1,5 +1,5 @@
 #![feature(nll)]
-#![feature(native_link_modifiers)]
+#![cfg_attr(bootstrap, feature(native_link_modifiers))]
 #![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
 
 // NOTE: This crate only exists to allow linking on mingw targets.
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index c3a76112391..1cbfb0bd554 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -125,13 +125,18 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
 
             // Do this outside the above loop so we don't depend on modifiers coming
             // after kinds
-            if let Some(item) = items.iter().find(|item| item.has_name(sym::modifiers)) {
+            let mut modifiers_count = 0;
+            for item in items.iter().filter(|item| item.has_name(sym::modifiers)) {
                 if let Some(modifiers) = item.value_str() {
+                    modifiers_count += 1;
                     let span = item.name_value_literal_span().unwrap();
+                    let mut has_duplicate_modifiers = false;
                     for modifier in modifiers.as_str().split(',') {
                         let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
                             Some(m) => (m, modifier.starts_with('+')),
                             None => {
+                                // Note: this error also excludes the case with empty modifier
+                                // string, like `modifiers = ""`.
                                 sess.span_err(
                                     span,
                                     "invalid linking modifier syntax, expected '+' or '-' prefix \
@@ -143,6 +148,9 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
 
                         match (modifier, &mut lib.kind) {
                             ("bundle", NativeLibKind::Static { bundle, .. }) => {
+                                if bundle.is_some() {
+                                    has_duplicate_modifiers = true;
+                                }
                                 *bundle = Some(value);
                             }
                             ("bundle", _) => {
@@ -153,9 +161,17 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
                                 );
                             }
 
-                            ("verbatim", _) => lib.verbatim = Some(value),
+                            ("verbatim", _) => {
+                                if lib.verbatim.is_some() {
+                                    has_duplicate_modifiers = true;
+                                }
+                                lib.verbatim = Some(value);
+                            }
 
                             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+                                if whole_archive.is_some() {
+                                    has_duplicate_modifiers = true;
+                                }
                                 *whole_archive = Some(value);
                             }
                             ("whole-archive", _) => {
@@ -168,6 +184,9 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
 
                             ("as-needed", NativeLibKind::Dylib { as_needed })
                             | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+                                if as_needed.is_some() {
+                                    has_duplicate_modifiers = true;
+                                }
                                 *as_needed = Some(value);
                             }
                             ("as-needed", _) => {
@@ -190,12 +209,22 @@ impl<'tcx> ItemLikeVisitor<'tcx> for Collector<'tcx> {
                             }
                         }
                     }
+                    if has_duplicate_modifiers {
+                        let msg =
+                            "same modifier is used multiple times in a single `modifiers` argument";
+                        sess.span_err(item.span(), msg);
+                    }
                 } else {
                     let msg = "must be of the form `#[link(modifiers = \"...\")]`";
                     sess.span_err(item.span(), msg);
                 }
             }
 
+            if modifiers_count > 1 {
+                let msg = "multiple `modifiers` arguments in a single `#[link]` attribute";
+                sess.span_err(m.span, msg);
+            }
+
             // In general we require #[link(name = "...")] but we allow
             // #[link(wasm_import_module = "...")] without the `name`.
             let requires_name = kind_specified || lib.wasm_import_module.is_none();
@@ -349,6 +378,15 @@ impl Collector<'_> {
                 .drain_filter(|lib| {
                     if let Some(lib_name) = lib.name {
                         if lib_name.as_str() == passed_lib.name {
+                            // FIXME: This whole logic is questionable, whether modifiers are
+                            // involved or not, library reordering and kind overriding without
+                            // explicit `:rename` in particular.
+                            if lib.has_modifiers() || passed_lib.has_modifiers() {
+                                self.tcx.sess.span_err(
+                                    self.tcx.def_span(lib.foreign_module.unwrap()),
+                                    "overriding linking modifiers from command line is not supported"
+                                );
+                            }
                             if passed_lib.kind != NativeLibKind::Unspecified {
                                 lib.kind = passed_lib.kind;
                             }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 44a2e2bdc85..856436e44db 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1948,9 +1948,6 @@ fn parse_native_lib_kind(
     kind: &str,
     error_format: ErrorOutputType,
 ) -> (NativeLibKind, Option<bool>) {
-    let is_nightly = nightly_options::match_is_nightly_build(matches);
-    let enable_unstable = nightly_options::is_unstable_enabled(matches);
-
     let (kind, modifiers) = match kind.split_once(':') {
         None => (kind, None),
         Some((kind, modifiers)) => (kind, Some(modifiers)),
@@ -1972,7 +1969,7 @@ fn parse_native_lib_kind(
                     "linking modifier can't be used with library kind `static-nobundle`",
                 )
             }
-            if !is_nightly {
+            if !nightly_options::match_is_nightly_build(matches) {
                 early_error(
                     error_format,
                     "library kind `static-nobundle` are currently unstable and only accepted on \
@@ -1988,23 +1985,7 @@ fn parse_native_lib_kind(
     };
     match modifiers {
         None => (kind, None),
-        Some(modifiers) => {
-            if !is_nightly {
-                early_error(
-                    error_format,
-                    "linking modifiers are currently unstable and only accepted on \
-                the nightly compiler",
-                );
-            }
-            if !enable_unstable {
-                early_error(
-                    error_format,
-                    "linking modifiers are currently unstable, \
-                the `-Z unstable-options` flag must also be passed to use it",
-                )
-            }
-            parse_native_lib_modifiers(kind, modifiers, error_format)
-        }
+        Some(modifiers) => parse_native_lib_modifiers(kind, modifiers, error_format, matches),
     }
 }
 
@@ -2012,7 +1993,23 @@ fn parse_native_lib_modifiers(
     mut kind: NativeLibKind,
     modifiers: &str,
     error_format: ErrorOutputType,
+    matches: &getopts::Matches,
 ) -> (NativeLibKind, Option<bool>) {
+    let report_unstable_modifier = |modifier| {
+        if !nightly_options::is_unstable_enabled(matches) {
+            let why = if nightly_options::match_is_nightly_build(matches) {
+                " and only accepted on the nightly compiler"
+            } else {
+                ", the `-Z unstable-options` flag must also be passed to use it"
+            };
+            early_error(
+                error_format,
+                &format!("{modifier} linking modifier is currently unstable{why}"),
+            )
+        }
+    };
+
+    let mut has_duplicate_modifiers = false;
     let mut verbatim = None;
     for modifier in modifiers.split(',') {
         let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
@@ -2026,6 +2023,10 @@ fn parse_native_lib_modifiers(
 
         match (modifier, &mut kind) {
             ("bundle", NativeLibKind::Static { bundle, .. }) => {
+                report_unstable_modifier(modifier);
+                if bundle.is_some() {
+                    has_duplicate_modifiers = true;
+                }
                 *bundle = Some(value);
             }
             ("bundle", _) => early_error(
@@ -2034,9 +2035,18 @@ fn parse_native_lib_modifiers(
                     `static` linking kind",
             ),
 
-            ("verbatim", _) => verbatim = Some(value),
+            ("verbatim", _) => {
+                report_unstable_modifier(modifier);
+                if verbatim.is_some() {
+                    has_duplicate_modifiers = true;
+                }
+                verbatim = Some(value);
+            }
 
             ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+                if whole_archive.is_some() {
+                    has_duplicate_modifiers = true;
+                }
                 *whole_archive = Some(value);
             }
             ("whole-archive", _) => early_error(
@@ -2047,6 +2057,10 @@ fn parse_native_lib_modifiers(
 
             ("as-needed", NativeLibKind::Dylib { as_needed })
             | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+                report_unstable_modifier(modifier);
+                if as_needed.is_some() {
+                    has_duplicate_modifiers = true;
+                }
                 *as_needed = Some(value);
             }
             ("as-needed", _) => early_error(
@@ -2055,6 +2069,8 @@ fn parse_native_lib_modifiers(
                     `dylib` and `framework` linking kinds",
             ),
 
+            // Note: this error also excludes the case with empty modifier
+            // string, like `modifiers = ""`.
             _ => early_error(
                 error_format,
                 &format!(
@@ -2064,6 +2080,9 @@ fn parse_native_lib_modifiers(
             ),
         }
     }
+    if has_duplicate_modifiers {
+        report_unstable_modifier("duplicating")
+    }
 
     (kind, verbatim)
 }
diff --git a/compiler/rustc_session/src/cstore.rs b/compiler/rustc_session/src/cstore.rs
index 281fc887633..c1fd3c7c61b 100644
--- a/compiler/rustc_session/src/cstore.rs
+++ b/compiler/rustc_session/src/cstore.rs
@@ -75,6 +75,12 @@ pub struct NativeLib {
     pub dll_imports: Vec<DllImport>,
 }
 
+impl NativeLib {
+    pub fn has_modifiers(&self) -> bool {
+        self.verbatim.is_some() || self.kind.has_modifiers()
+    }
+}
+
 #[derive(Clone, Debug, Encodable, Decodable, HashStable_Generic)]
 pub struct DllImport {
     pub name: Symbol,
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index a33f94013d2..6a8775bd10b 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -43,6 +43,20 @@ pub enum NativeLibKind {
     Unspecified,
 }
 
+impl NativeLibKind {
+    pub fn has_modifiers(&self) -> bool {
+        match self {
+            NativeLibKind::Static { bundle, whole_archive } => {
+                bundle.is_some() || whole_archive.is_some()
+            }
+            NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => {
+                as_needed.is_some()
+            }
+            NativeLibKind::RawDylib | NativeLibKind::Unspecified => false,
+        }
+    }
+}
+
 rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind);
 
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
@@ -53,6 +67,12 @@ pub struct NativeLib {
     pub verbatim: Option<bool>,
 }
 
+impl NativeLib {
+    pub fn has_modifiers(&self) -> bool {
+        self.verbatim.is_some() || self.kind.has_modifiers()
+    }
+}
+
 rustc_data_structures::impl_stable_hash_via_hash!(NativeLib);
 
 /// A path that has been canonicalized along with its original, non-canonicalized form