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.rs39
-rw-r--r--compiler/rustc_codegen_cranelift/src/archive.rs4
-rw-r--r--compiler/rustc_codegen_llvm/src/back/archive.rs5
-rw-r--r--compiler/rustc_codegen_ssa/src/back/archive.rs16
-rw-r--r--compiler/rustc_codegen_ssa/src/back/link.rs73
-rw-r--r--compiler/rustc_codegen_ssa/src/back/linker.rs124
-rw-r--r--compiler/rustc_codegen_ssa/src/lib.rs3
-rw-r--r--compiler/rustc_data_structures/src/obligation_forest/mod.rs7
-rw-r--r--compiler/rustc_expand/src/module.rs9
-rw-r--r--compiler/rustc_feature/src/active.rs20
-rw-r--r--compiler/rustc_interface/src/tests.rs173
-rw-r--r--compiler/rustc_metadata/src/native_libs.rs122
-rw-r--r--compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs11
-rw-r--r--compiler/rustc_middle/src/middle/cstore.rs1
-rw-r--r--compiler/rustc_session/src/config.rs153
-rw-r--r--compiler/rustc_session/src/options.rs4
-rw-r--r--compiler/rustc_session/src/utils.rs33
-rw-r--r--compiler/rustc_span/src/symbol.rs6
18 files changed, 628 insertions, 175 deletions
diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs
index da516f5cb41..dc3383dae84 100644
--- a/compiler/rustc_ast_passes/src/feature_gate.rs
+++ b/compiler/rustc_ast_passes/src/feature_gate.rs
@@ -326,6 +326,45 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> {
                 );
             }
         }
+
+        // Check for unstable modifiers on `#[link(..)]` attribute
+        if self.sess.check_name(attr, 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(&['+', '-'][..]) {
+                                macro_rules! gate_modifier { ($($name:literal => $feature:ident)*) => {
+                                    $(if modifier == $name {
+                                        let msg = concat!("`#[link(modifiers=\"", $name, "\")]` is unstable");
+                                        gate_feature_post!(
+                                            self,
+                                            $feature,
+                                            nested_meta.name_value_literal_span().unwrap(),
+                                            msg
+                                        );
+                                    })*
+                                }}
+
+                                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
+                                );
+                            }
+                        }
+                    }
+                }
+            }
+        }
     }
 
     fn visit_item(&mut self, i: &'a ast::Item) {
diff --git a/compiler/rustc_codegen_cranelift/src/archive.rs b/compiler/rustc_codegen_cranelift/src/archive.rs
index 7583fc42407..fc0823302e0 100644
--- a/compiler/rustc_codegen_cranelift/src/archive.rs
+++ b/compiler/rustc_codegen_cranelift/src/archive.rs
@@ -85,8 +85,8 @@ impl<'a> ArchiveBuilder<'a> for ArArchiveBuilder<'a> {
         ));
     }
 
-    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol) {
-        let location = find_library(name, &self.lib_search_paths, self.sess);
+    fn add_native_library(&mut self, name: rustc_span::symbol::Symbol, verbatim: bool) {
+        let location = find_library(name, verbatim, &self.lib_search_paths, self.sess);
         self.add_archive(location.clone(), |_| false).unwrap_or_else(|e| {
             panic!("failed to add native library {}: {}", location.to_string_lossy(), e);
         });
diff --git a/compiler/rustc_codegen_llvm/src/back/archive.rs b/compiler/rustc_codegen_llvm/src/back/archive.rs
index 4e7213853b0..261affe2c42 100644
--- a/compiler/rustc_codegen_llvm/src/back/archive.rs
+++ b/compiler/rustc_codegen_llvm/src/back/archive.rs
@@ -100,8 +100,9 @@ impl<'a> ArchiveBuilder<'a> for LlvmArchiveBuilder<'a> {
 
     /// Adds all of the contents of a native library to this archive. This will
     /// search in the relevant locations for a library named `name`.
-    fn add_native_library(&mut self, name: Symbol) {
-        let location = find_library(name, &self.config.lib_search_paths, self.config.sess);
+    fn add_native_library(&mut self, name: Symbol, verbatim: bool) {
+        let location =
+            find_library(name, verbatim, &self.config.lib_search_paths, self.config.sess);
         self.add_archive(&location, |_| false).unwrap_or_else(|e| {
             self.config.sess.fatal(&format!(
                 "failed to add native library {}: {}",
diff --git a/compiler/rustc_codegen_ssa/src/back/archive.rs b/compiler/rustc_codegen_ssa/src/back/archive.rs
index c477ac6462a..c197d48d4ea 100644
--- a/compiler/rustc_codegen_ssa/src/back/archive.rs
+++ b/compiler/rustc_codegen_ssa/src/back/archive.rs
@@ -4,11 +4,19 @@ use rustc_span::symbol::Symbol;
 use std::io;
 use std::path::{Path, PathBuf};
 
-pub fn find_library(name: Symbol, search_paths: &[PathBuf], sess: &Session) -> PathBuf {
+pub fn find_library(
+    name: Symbol,
+    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 =
-        format!("{}{}{}", sess.target.staticlib_prefix, name, sess.target.staticlib_suffix);
+    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 {
@@ -45,7 +53,7 @@ pub trait ArchiveBuilder<'a> {
         lto: bool,
         skip_objects: bool,
     ) -> io::Result<()>;
-    fn add_native_library(&mut self, name: Symbol);
+    fn add_native_library(&mut self, name: Symbol, verbatim: bool);
     fn update_symbols(&mut self);
 
     fn build(self);
diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index dcdd0910aa6..59f66c55572 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -329,15 +329,15 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
     // metadata of the rlib we're generating somehow.
     for lib in codegen_results.crate_info.used_libraries.iter() {
         match lib.kind {
-            NativeLibKind::StaticBundle => {}
-            NativeLibKind::StaticNoBundle
-            | NativeLibKind::Dylib
-            | NativeLibKind::Framework
+            NativeLibKind::Static { bundle: None | Some(true), .. } => {}
+            NativeLibKind::Static { bundle: Some(false), .. }
+            | NativeLibKind::Dylib { .. }
+            | NativeLibKind::Framework { .. }
             | NativeLibKind::RawDylib
             | NativeLibKind::Unspecified => continue,
         }
         if let Some(name) = lib.name {
-            ab.add_native_library(name);
+            ab.add_native_library(name, lib.verbatim.unwrap_or(false));
         }
     }
 
@@ -430,9 +430,10 @@ fn link_staticlib<'a, B: ArchiveBuilder<'a>>(
         // Clearly this is not sufficient for a general purpose feature, and
         // we'd want to read from the library's metadata to determine which
         // object files come from where and selectively skip them.
-        let skip_object_files = native_libs
-            .iter()
-            .any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib));
+        let skip_object_files = native_libs.iter().any(|lib| {
+            matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
+                && !relevant_lib(sess, lib)
+        });
         ab.add_rlib(
             path,
             &name.as_str(),
@@ -931,7 +932,7 @@ fn link_sanitizer_runtime(sess: &Session, linker: &mut dyn Linker, name: &str) {
         let path = find_sanitizer_runtime(&sess, &filename);
         let rpath = path.to_str().expect("non-utf8 component in path");
         linker.args(&["-Wl,-rpath", "-Xlinker", rpath]);
-        linker.link_dylib(Symbol::intern(&filename));
+        linker.link_dylib(Symbol::intern(&filename), false, true);
     } else {
         let filename = format!("librustc{}_rt.{}.a", channel, name);
         let path = find_sanitizer_runtime(&sess, &filename).join(&filename);
@@ -1080,21 +1081,25 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
         .filter_map(|lib| {
             let name = lib.name?;
             match lib.kind {
-                NativeLibKind::StaticNoBundle
-                | NativeLibKind::Dylib
+                NativeLibKind::Static { bundle: Some(false), .. }
+                | NativeLibKind::Dylib { .. }
                 | NativeLibKind::Unspecified => {
+                    let verbatim = lib.verbatim.unwrap_or(false);
                     if sess.target.is_like_msvc {
-                        Some(format!("{}.lib", name))
+                        Some(format!("{}{}", name, if verbatim { "" } else { ".lib" }))
+                    } else if sess.target.linker_is_gnu {
+                        Some(format!("-l{}{}", if verbatim { ":" } else { "" }, name))
                     } else {
                         Some(format!("-l{}", name))
                     }
                 }
-                NativeLibKind::Framework => {
+                NativeLibKind::Framework { .. } => {
                     // ld-only syntax, since there are no frameworks in MSVC
                     Some(format!("-framework {}", name))
                 }
                 // These are included, no need to print them
-                NativeLibKind::StaticBundle | NativeLibKind::RawDylib => None,
+                NativeLibKind::Static { bundle: None | Some(true), .. }
+                | NativeLibKind::RawDylib => None,
             }
         })
         .collect();
@@ -1812,11 +1817,20 @@ fn add_local_native_libraries(
             Some(l) => l,
             None => continue,
         };
+        let verbatim = lib.verbatim.unwrap_or(false);
         match lib.kind {
-            NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name),
-            NativeLibKind::Framework => cmd.link_framework(name),
-            NativeLibKind::StaticNoBundle => cmd.link_staticlib(name),
-            NativeLibKind::StaticBundle => cmd.link_whole_staticlib(name, &search_path),
+            NativeLibKind::Dylib { as_needed } => {
+                cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+            }
+            NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
+            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);
+            }
+            NativeLibKind::Static { .. } => cmd.link_staticlib(name, verbatim),
             NativeLibKind::RawDylib => {
                 // FIXME(#58713): Proper handling for raw dylibs.
                 bug!("raw_dylib feature not yet implemented");
@@ -2000,9 +2014,10 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
         // there's a static library that's not relevant we skip all object
         // files.
         let native_libs = &codegen_results.crate_info.native_libraries[&cnum];
-        let skip_native = native_libs
-            .iter()
-            .any(|lib| lib.kind == NativeLibKind::StaticBundle && !relevant_lib(sess, lib));
+        let skip_native = native_libs.iter().any(|lib| {
+            matches!(lib.kind, NativeLibKind::Static { bundle: None | Some(true), .. })
+                && !relevant_lib(sess, lib)
+        });
 
         if (!are_upstream_rust_objects_already_included(sess)
             || ignored_for_lto(sess, &codegen_results.crate_info, cnum))
@@ -2144,22 +2159,28 @@ fn add_upstream_native_libraries(
             if !relevant_lib(sess, &lib) {
                 continue;
             }
+            let verbatim = lib.verbatim.unwrap_or(false);
             match lib.kind {
-                NativeLibKind::Dylib | NativeLibKind::Unspecified => cmd.link_dylib(name),
-                NativeLibKind::Framework => cmd.link_framework(name),
-                NativeLibKind::StaticNoBundle => {
+                NativeLibKind::Dylib { as_needed } => {
+                    cmd.link_dylib(name, verbatim, as_needed.unwrap_or(true))
+                }
+                NativeLibKind::Unspecified => cmd.link_dylib(name, verbatim, true),
+                NativeLibKind::Framework { as_needed } => {
+                    cmd.link_framework(name, as_needed.unwrap_or(true))
+                }
+                NativeLibKind::Static { bundle: Some(false), .. } => {
                     // Link "static-nobundle" native libs only if the crate they originate from
                     // is being linked statically to the current crate.  If it's linked dynamically
                     // or is an rlib already included via some other dylib crate, the symbols from
                     // native libs will have already been included in that dylib.
                     if data[cnum.as_usize() - 1] == Linkage::Static {
-                        cmd.link_staticlib(name)
+                        cmd.link_staticlib(name, verbatim)
                     }
                 }
                 // ignore statically included native libraries here as we've
                 // already included them when we included the rust library
                 // previously
-                NativeLibKind::StaticBundle => {}
+                NativeLibKind::Static { bundle: None | Some(true), .. } => {}
                 NativeLibKind::RawDylib => {
                     // FIXME(#58713): Proper handling for raw dylibs.
                     bug!("raw_dylib feature not yet implemented");
diff --git a/compiler/rustc_codegen_ssa/src/back/linker.rs b/compiler/rustc_codegen_ssa/src/back/linker.rs
index 8ba124a1fa8..401d379b0d1 100644
--- a/compiler/rustc_codegen_ssa/src/back/linker.rs
+++ b/compiler/rustc_codegen_ssa/src/back/linker.rs
@@ -103,18 +103,19 @@ impl LinkerInfo {
 pub trait Linker {
     fn cmd(&mut self) -> &mut Command;
     fn set_output_kind(&mut self, output_kind: LinkOutputKind, out_filename: &Path);
-    fn link_dylib(&mut self, lib: Symbol);
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, as_needed: bool);
     fn link_rust_dylib(&mut self, lib: Symbol, path: &Path);
-    fn link_framework(&mut self, framework: Symbol);
-    fn link_staticlib(&mut self, lib: Symbol);
+    fn link_framework(&mut self, framework: Symbol, as_needed: bool);
+    fn link_staticlib(&mut self, lib: Symbol, verbatim: bool);
     fn link_rlib(&mut self, lib: &Path);
     fn link_whole_rlib(&mut self, lib: &Path);
-    fn link_whole_staticlib(&mut self, lib: Symbol, search_path: &[PathBuf]);
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, search_path: &[PathBuf]);
     fn include_path(&mut self, path: &Path);
     fn framework_path(&mut self, path: &Path);
     fn output_filename(&mut self, path: &Path);
     fn add_object(&mut self, path: &Path);
     fn gc_sections(&mut self, keep_metadata: bool);
+    fn no_gc_sections(&mut self);
     fn full_relro(&mut self);
     fn partial_relro(&mut self);
     fn no_relro(&mut self);
@@ -338,13 +339,32 @@ impl<'a> Linker for GccLinker<'a> {
         }
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, as_needed: bool) {
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // FIXME(81490): ld64 doesn't support these flags but macOS 11
+                // has -needed-l{} / -needed_library {}
+                // but we have no way to detect that here.
+                self.sess.warn("`as-needed` modifier not implemented yet for ld64");
+            } else if self.sess.target.linker_is_gnu {
+                self.linker_arg("--no-as-needed");
+            } else {
+                self.sess.warn("`as-needed` modifier not supported for current linker");
+            }
+        }
         self.hint_dynamic();
-        self.cmd.arg(format!("-l{}", lib));
+        self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib));
+        if !as_needed {
+            if self.sess.target.is_like_osx {
+                // See above FIXME comment
+            } else if self.sess.target.linker_is_gnu {
+                self.linker_arg("--as-needed");
+            }
+        }
     }
-    fn link_staticlib(&mut self, lib: Symbol) {
+    fn link_staticlib(&mut self, lib: Symbol, verbatim: bool) {
         self.hint_static();
-        self.cmd.arg(format!("-l{}", lib));
+        self.cmd.arg(format!("-l{}{}", if verbatim { ":" } else { "" }, lib));
     }
     fn link_rlib(&mut self, lib: &Path) {
         self.hint_static();
@@ -378,8 +398,14 @@ impl<'a> Linker for GccLinker<'a> {
         self.cmd.arg(format!("-l{}", lib));
     }
 
-    fn link_framework(&mut self, framework: Symbol) {
+    fn link_framework(&mut self, framework: Symbol, as_needed: bool) {
         self.hint_dynamic();
+        if !as_needed {
+            // FIXME(81490): ld64 as of macOS 11 supports the -needed_framework
+            // flag but we have no way to detect that here.
+            // self.cmd.arg("-needed_framework").sym_arg(framework);
+            self.sess.warn("`as-needed` modifier not implemented yet for ld64");
+        }
         self.cmd.arg("-framework").sym_arg(framework);
     }
 
@@ -389,17 +415,21 @@ impl<'a> Linker for GccLinker<'a> {
     // don't otherwise explicitly reference them. This can occur for
     // libraries which are just providing bindings, libraries with generic
     // functions, etc.
-    fn link_whole_staticlib(&mut self, lib: Symbol, search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, search_path: &[PathBuf]) {
         self.hint_static();
         let target = &self.sess.target;
         if !target.is_like_osx {
-            self.linker_arg("--whole-archive").cmd.arg(format!("-l{}", lib));
+            self.linker_arg("--whole-archive").cmd.arg(format!(
+                "-l{}{}",
+                if verbatim { ":" } else { "" },
+                lib
+            ));
             self.linker_arg("--no-whole-archive");
         } else {
             // -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, search_path, &self.sess);
+            let lib = archive::find_library(lib, verbatim, search_path, &self.sess);
             self.linker_arg(&lib);
         }
     }
@@ -443,6 +473,16 @@ impl<'a> Linker for GccLinker<'a> {
         }
     }
 
+    fn no_gc_sections(&mut self) {
+        if self.sess.target.is_like_osx {
+            self.linker_arg("-no_dead_strip");
+        } else if self.sess.target.is_like_solaris {
+            self.linker_arg("-zrecord");
+        } else {
+            self.linker_arg("--no-gc-sections");
+        }
+    }
+
     fn optimize(&mut self) {
         if !self.sess.target.linker_is_gnu {
             return;
@@ -710,8 +750,12 @@ impl<'a> Linker for MsvcLinker<'a> {
         }
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
-        self.cmd.arg(&format!("{}.lib", lib));
+    fn no_gc_sections(&mut self) {
+        self.cmd.arg("/OPT:NOREF,NOICF");
+    }
+
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, _as_needed: bool) {
+        self.cmd.arg(format!("{}{}", lib, if verbatim { "" } else { ".lib" }));
     }
 
     fn link_rust_dylib(&mut self, lib: Symbol, path: &Path) {
@@ -725,8 +769,8 @@ impl<'a> Linker for MsvcLinker<'a> {
         }
     }
 
-    fn link_staticlib(&mut self, lib: Symbol) {
-        self.cmd.arg(&format!("{}.lib", lib));
+    fn link_staticlib(&mut self, lib: Symbol, verbatim: bool) {
+        self.cmd.arg(format!("{}{}", lib, if verbatim { "" } else { ".lib" }));
     }
 
     fn full_relro(&mut self) {
@@ -764,13 +808,13 @@ impl<'a> Linker for MsvcLinker<'a> {
     fn framework_path(&mut self, _path: &Path) {
         bug!("frameworks are not supported on windows")
     }
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         bug!("frameworks are not supported on windows")
     }
 
-    fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
-        self.link_staticlib(lib);
-        self.cmd.arg(format!("/WHOLEARCHIVE:{}.lib", lib));
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, _search_path: &[PathBuf]) {
+        self.link_staticlib(lib, verbatim);
+        self.cmd.arg(format!("/WHOLEARCHIVE:{}{}", lib, if verbatim { "" } else { ".lib" }));
     }
     fn link_whole_rlib(&mut self, path: &Path) {
         self.link_rlib(path);
@@ -919,7 +963,7 @@ impl<'a> Linker for EmLinker<'a> {
         self.cmd.arg("-L").arg(path);
     }
 
-    fn link_staticlib(&mut self, lib: Symbol) {
+    fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
@@ -931,14 +975,14 @@ impl<'a> Linker for EmLinker<'a> {
         self.cmd.arg(path);
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
+    fn link_dylib(&mut self, lib: Symbol, verbatim: bool, _as_needed: bool) {
         // Emscripten always links statically
-        self.link_staticlib(lib);
+        self.link_staticlib(lib, verbatim);
     }
 
-    fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, lib: Symbol, verbatim: bool, _search_path: &[PathBuf]) {
         // not supported?
-        self.link_staticlib(lib);
+        self.link_staticlib(lib, verbatim);
     }
 
     fn link_whole_rlib(&mut self, lib: &Path) {
@@ -947,7 +991,7 @@ impl<'a> Linker for EmLinker<'a> {
     }
 
     fn link_rust_dylib(&mut self, lib: Symbol, _path: &Path) {
-        self.link_dylib(lib);
+        self.link_dylib(lib, false, true);
     }
 
     fn link_rlib(&mut self, lib: &Path) {
@@ -970,7 +1014,7 @@ impl<'a> Linker for EmLinker<'a> {
         bug!("frameworks are not supported on Emscripten")
     }
 
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         bug!("frameworks are not supported on Emscripten")
     }
 
@@ -978,6 +1022,10 @@ impl<'a> Linker for EmLinker<'a> {
         // noop
     }
 
+    fn no_gc_sections(&mut self) {
+        // noop
+    }
+
     fn optimize(&mut self) {
         // Emscripten performs own optimizations
         self.cmd.arg(match self.sess.opts.optimize {
@@ -1121,11 +1169,11 @@ impl<'a> Linker for WasmLd<'a> {
         }
     }
 
-    fn link_dylib(&mut self, lib: Symbol) {
+    fn link_dylib(&mut self, lib: Symbol, _verbatim: bool, _as_needed: bool) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
-    fn link_staticlib(&mut self, lib: Symbol) {
+    fn link_staticlib(&mut self, lib: Symbol, _verbatim: bool) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
@@ -1159,11 +1207,11 @@ impl<'a> Linker for WasmLd<'a> {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         panic!("frameworks not supported")
     }
 
-    fn link_whole_staticlib(&mut self, lib: Symbol, _search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) {
         self.cmd.arg("-l").sym_arg(lib);
     }
 
@@ -1175,6 +1223,10 @@ impl<'a> Linker for WasmLd<'a> {
         self.cmd.arg("--gc-sections");
     }
 
+    fn no_gc_sections(&mut self) {
+        self.cmd.arg("--no-gc-sections");
+    }
+
     fn optimize(&mut self) {
         self.cmd.arg(match self.sess.opts.optimize {
             OptLevel::No => "-O0",
@@ -1329,7 +1381,7 @@ impl<'a> Linker for PtxLinker<'a> {
         });
     }
 
-    fn link_dylib(&mut self, _lib: Symbol) {
+    fn link_dylib(&mut self, _lib: Symbol, _verbatim: bool, _as_needed: bool) {
         panic!("external dylibs not supported")
     }
 
@@ -1337,11 +1389,11 @@ impl<'a> Linker for PtxLinker<'a> {
         panic!("external dylibs not supported")
     }
 
-    fn link_staticlib(&mut self, _lib: Symbol) {
+    fn link_staticlib(&mut self, _lib: Symbol, _verbatim: bool) {
         panic!("staticlibs not supported")
     }
 
-    fn link_whole_staticlib(&mut self, _lib: Symbol, _search_path: &[PathBuf]) {
+    fn link_whole_staticlib(&mut self, _lib: Symbol, _verbatim: bool, _search_path: &[PathBuf]) {
         panic!("staticlibs not supported")
     }
 
@@ -1349,7 +1401,7 @@ impl<'a> Linker for PtxLinker<'a> {
         panic!("frameworks not supported")
     }
 
-    fn link_framework(&mut self, _framework: Symbol) {
+    fn link_framework(&mut self, _framework: Symbol, _as_needed: bool) {
         panic!("frameworks not supported")
     }
 
@@ -1361,6 +1413,8 @@ impl<'a> Linker for PtxLinker<'a> {
 
     fn gc_sections(&mut self, _keep_metadata: bool) {}
 
+    fn no_gc_sections(&mut self) {}
+
     fn pgo_gen(&mut self) {}
 
     fn no_crt_objects(&mut self) {}
diff --git a/compiler/rustc_codegen_ssa/src/lib.rs b/compiler/rustc_codegen_ssa/src/lib.rs
index f0f45b067b3..1b53b551901 100644
--- a/compiler/rustc_codegen_ssa/src/lib.rs
+++ b/compiler/rustc_codegen_ssa/src/lib.rs
@@ -114,11 +114,12 @@ pub struct NativeLib {
     pub kind: NativeLibKind,
     pub name: Option<Symbol>,
     pub cfg: Option<ast::MetaItem>,
+    pub verbatim: Option<bool>,
 }
 
 impl From<&cstore::NativeLib> for NativeLib {
     fn from(lib: &cstore::NativeLib) -> Self {
-        NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone() }
+        NativeLib { kind: lib.kind, name: lib.name, cfg: lib.cfg.clone(), verbatim: lib.verbatim }
     }
 }
 
diff --git a/compiler/rustc_data_structures/src/obligation_forest/mod.rs b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
index a5b2df1da5d..29d685ab530 100644
--- a/compiler/rustc_data_structures/src/obligation_forest/mod.rs
+++ b/compiler/rustc_data_structures/src/obligation_forest/mod.rs
@@ -336,12 +336,13 @@ impl<O: ForestObligation> ObligationForest<O> {
 
     // Returns Err(()) if we already know this obligation failed.
     fn register_obligation_at(&mut self, obligation: O, parent: Option<usize>) -> Result<(), ()> {
-        if self.done_cache.contains(&obligation.as_cache_key()) {
+        let cache_key = obligation.as_cache_key();
+        if self.done_cache.contains(&cache_key) {
             debug!("register_obligation_at: ignoring already done obligation: {:?}", obligation);
             return Ok(());
         }
 
-        match self.active_cache.entry(obligation.as_cache_key()) {
+        match self.active_cache.entry(cache_key.clone()) {
             Entry::Occupied(o) => {
                 let node = &mut self.nodes[*o.get()];
                 if let Some(parent_index) = parent {
@@ -365,7 +366,7 @@ impl<O: ForestObligation> ObligationForest<O> {
                     && self
                         .error_cache
                         .get(&obligation_tree_id)
-                        .map(|errors| errors.contains(&obligation.as_cache_key()))
+                        .map(|errors| errors.contains(&cache_key))
                         .unwrap_or(false);
 
                 if already_failed {
diff --git a/compiler/rustc_expand/src/module.rs b/compiler/rustc_expand/src/module.rs
index 993522d01d8..4d777049f0d 100644
--- a/compiler/rustc_expand/src/module.rs
+++ b/compiler/rustc_expand/src/module.rs
@@ -36,7 +36,7 @@ crate struct ParsedExternalMod {
 pub enum ModError<'a> {
     CircularInclusion(Vec<PathBuf>),
     ModInBlock(Option<Ident>),
-    FileNotFound(Ident, PathBuf),
+    FileNotFound(Ident, PathBuf, PathBuf),
     MultipleCandidates(Ident, PathBuf, PathBuf),
     ParserError(DiagnosticBuilder<'a>),
 }
@@ -219,7 +219,7 @@ pub fn default_submod_path<'a>(
             file_path: secondary_path,
             dir_ownership: DirOwnership::Owned { relative: None },
         }),
-        (false, false) => Err(ModError::FileNotFound(ident, default_path)),
+        (false, false) => Err(ModError::FileNotFound(ident, default_path, secondary_path)),
         (true, true) => Err(ModError::MultipleCandidates(ident, default_path, secondary_path)),
     }
 }
@@ -247,7 +247,7 @@ impl ModError<'_> {
                 }
                 err
             }
-            ModError::FileNotFound(ident, default_path) => {
+            ModError::FileNotFound(ident, default_path, secondary_path) => {
                 let mut err = struct_span_err!(
                     diag,
                     span,
@@ -256,9 +256,10 @@ impl ModError<'_> {
                     ident,
                 );
                 err.help(&format!(
-                    "to create the module `{}`, create file \"{}\"",
+                    "to create the module `{}`, create file \"{}\" or \"{}\"",
                     ident,
                     default_path.display(),
+                    secondary_path.display(),
                 ));
                 err
             }
diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs
index 4ad5f085ad0..f747f854514 100644
--- a/compiler/rustc_feature/src/active.rs
+++ b/compiler/rustc_feature/src/active.rs
@@ -656,6 +656,21 @@ declare_features! (
     /// Allows using imported `main` function
     (active, imported_main, "1.53.0", Some(28937), None),
 
+    /// Allows specifying modifiers in the link attribute: `#[link(modifiers = "...")]`
+    (active, native_link_modifiers, "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),
+
+    /// Allows specifying the as-needed link modifier
+    (active, native_link_modifiers_as_needed, "1.53.0", Some(81490), None),
+
     // -------------------------------------------------------------------------
     // feature-group-end: actual feature gates
     // -------------------------------------------------------------------------
@@ -683,6 +698,11 @@ pub const INCOMPLETE_FEATURES: &[Symbol] = &[
     sym::const_generics_defaults,
     sym::inherent_associated_types,
     sym::type_alias_impl_trait,
+    sym::native_link_modifiers,
+    sym::native_link_modifiers_bundle,
+    sym::native_link_modifiers_verbatim,
+    sym::native_link_modifiers_whole_archive,
+    sym::native_link_modifiers_as_needed,
 ];
 
 /// Some features are not allowed to be used together at the same time, if
diff --git a/compiler/rustc_interface/src/tests.rs b/compiler/rustc_interface/src/tests.rs
index d8c1a7a2682..4d87bbead41 100644
--- a/compiler/rustc_interface/src/tests.rs
+++ b/compiler/rustc_interface/src/tests.rs
@@ -12,7 +12,7 @@ use rustc_session::config::{
 };
 use rustc_session::lint::Level;
 use rustc_session::search_paths::SearchPath;
-use rustc_session::utils::{CanonicalizedPath, NativeLibKind};
+use rustc_session::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use rustc_session::{build_session, getopts, DiagnosticOutput, Session};
 use rustc_span::edition::{Edition, DEFAULT_EDITION};
 use rustc_span::symbol::sym;
@@ -303,38 +303,122 @@ fn test_native_libs_tracking_hash_different_values() {
     let mut v2 = Options::default();
     let mut v3 = Options::default();
     let mut v4 = Options::default();
+    let mut v5 = Options::default();
 
     // Reference
     v1.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     // Change label
     v2.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("X"), None, NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("X"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     // Change kind
     v3.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::StaticBundle),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     // Change new-name
     v4.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), Some(String::from("X")), NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: Some(String::from("X")),
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
+    ];
+
+    // Change verbatim
+    v5.libs = vec![
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: Some(true),
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     assert_different_hash(&v1, &v2);
     assert_different_hash(&v1, &v3);
     assert_different_hash(&v1, &v4);
+    assert_different_hash(&v1, &v5);
 }
 
 #[test]
@@ -345,21 +429,66 @@ fn test_native_libs_tracking_hash_different_order() {
 
     // Reference
     v1.libs = vec![
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::Framework),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     v2.libs = vec![
-        (String::from("b"), None, NativeLibKind::Framework),
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("c"), None, NativeLibKind::Unspecified),
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
     ];
 
     v3.libs = vec![
-        (String::from("c"), None, NativeLibKind::Unspecified),
-        (String::from("a"), None, NativeLibKind::StaticBundle),
-        (String::from("b"), None, NativeLibKind::Framework),
+        NativeLib {
+            name: String::from("c"),
+            new_name: None,
+            kind: NativeLibKind::Unspecified,
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("a"),
+            new_name: None,
+            kind: NativeLibKind::Static { bundle: None, whole_archive: None },
+            verbatim: None,
+        },
+        NativeLib {
+            name: String::from("b"),
+            new_name: None,
+            kind: NativeLibKind::Framework { as_needed: None },
+            verbatim: None,
+        },
     ];
 
     assert_same_hash(&v1, &v2);
diff --git a/compiler/rustc_metadata/src/native_libs.rs b/compiler/rustc_metadata/src/native_libs.rs
index 523e016eeb9..bc342119efb 100644
--- a/compiler/rustc_metadata/src/native_libs.rs
+++ b/compiler/rustc_metadata/src/native_libs.rs
@@ -8,8 +8,8 @@ use rustc_middle::ty::TyCtxt;
 use rustc_session::parse::feature_err;
 use rustc_session::utils::NativeLibKind;
 use rustc_session::Session;
-use rustc_span::source_map::Span;
 use rustc_span::symbol::{kw, sym, Symbol};
+use rustc_span::Span;
 use rustc_target::spec::abi::Abi;
 
 crate fn collect(tcx: TyCtxt<'_>) -> Vec<NativeLib> {
@@ -56,6 +56,7 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
                 cfg: None,
                 foreign_module: Some(it.def_id.to_def_id()),
                 wasm_import_module: None,
+                verbatim: None,
             };
             let mut kind_specified = false;
 
@@ -67,10 +68,18 @@ impl ItemLikeVisitor<'tcx> for Collector<'tcx> {
                         None => continue, // skip like historical compilers
                     };
                     lib.kind = match &*kind.as_str() {
-                        "static" => NativeLibKind::StaticBundle,
-                        "static-nobundle" => NativeLibKind::StaticNoBundle,
-                        "dylib" => NativeLibKind::Dylib,
-                        "framework" => NativeLibKind::Framework,
+                        "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
+                        "static-nobundle" => {
+                            sess.struct_span_warn(
+                                item.span(),
+                                "library kind `static-nobundle` has been superseded by specifying \
+                                modifier `-bundle` with library kind `static`",
+                            )
+                            .emit();
+                            NativeLibKind::Static { bundle: Some(false), whole_archive: None }
+                        }
+                        "dylib" => NativeLibKind::Dylib { as_needed: None },
+                        "framework" => NativeLibKind::Framework { as_needed: None },
                         "raw-dylib" => NativeLibKind::RawDylib,
                         k => {
                             struct_span_err!(sess, item.span(), E0458, "unknown kind: `{}`", k)
@@ -108,6 +117,71 @@ impl 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)) {
+                if let Some(modifiers) = item.value_str() {
+                    let span = item.name_value_literal_span().unwrap();
+                    for modifier in modifiers.as_str().split(',') {
+                        let (modifier, value) = match modifier.strip_prefix(&['+', '-'][..]) {
+                            Some(m) => (m, modifier.starts_with('+')),
+                            None => {
+                                sess.span_err(
+                                    span,
+                                    "invalid linking modifier syntax, expected '+' or '-' prefix \
+                                    before one of: bundle, verbatim, whole-archive, as-needed",
+                                );
+                                continue;
+                            }
+                        };
+
+                        match (modifier, &mut lib.kind) {
+                            ("bundle", NativeLibKind::Static { bundle, .. }) => {
+                                *bundle = Some(value);
+                            }
+                            ("bundle", _) => sess.span_err(
+                                span,
+                                "bundle linking modifier is only compatible with \
+                                `static` linking kind",
+                            ),
+
+                            ("verbatim", _) => lib.verbatim = Some(value),
+
+                            ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+                                *whole_archive = Some(value);
+                            }
+                            ("whole-archive", _) => sess.span_err(
+                                span,
+                                "whole-archive linking modifier is only compatible with \
+                                `static` linking kind",
+                            ),
+
+                            ("as-needed", NativeLibKind::Dylib { as_needed })
+                            | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+                                *as_needed = Some(value);
+                            }
+                            ("as-needed", _) => sess.span_err(
+                                span,
+                                "as-needed linking modifier is only compatible with \
+                                `dylib` and `framework` linking kinds",
+                            ),
+
+                            _ => sess.span_err(
+                                span,
+                                &format!(
+                                    "unrecognized linking modifier `{}`, expected one \
+                                    of: bundle, verbatim, whole-archive, as-needed",
+                                    modifier
+                                ),
+                            ),
+                        }
+                    }
+                } else {
+                    let msg = "must be of the form `#[link(modifiers = \"...\")]`";
+                    sess.span_err(item.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();
@@ -152,7 +226,7 @@ impl Collector<'tcx> {
             return;
         }
         let is_osx = self.tcx.sess.target.is_like_osx;
-        if lib.kind == NativeLibKind::Framework && !is_osx {
+        if matches!(lib.kind, NativeLibKind::Framework { .. }) && !is_osx {
             let msg = "native frameworks are only available on macOS targets";
             match span {
                 Some(span) => struct_span_err!(self.tcx.sess, span, E0455, "{}", msg).emit(),
@@ -168,7 +242,9 @@ impl Collector<'tcx> {
             )
             .emit();
         }
-        if lib.kind == NativeLibKind::StaticNoBundle && !self.tcx.features().static_nobundle {
+        if matches!(lib.kind, NativeLibKind::Static { bundle: Some(false), .. })
+            && !self.tcx.features().static_nobundle
+        {
             feature_err(
                 &self.tcx.sess.parse_sess,
                 sym::static_nobundle,
@@ -193,30 +269,30 @@ impl Collector<'tcx> {
     fn process_command_line(&mut self) {
         // First, check for errors
         let mut renames = FxHashSet::default();
-        for (name, new_name, _) in &self.tcx.sess.opts.libs {
-            if let Some(ref new_name) = new_name {
+        for lib in &self.tcx.sess.opts.libs {
+            if let Some(ref new_name) = lib.new_name {
                 let any_duplicate = self
                     .libs
                     .iter()
                     .filter_map(|lib| lib.name.as_ref())
-                    .any(|n| &n.as_str() == name);
+                    .any(|n| &n.as_str() == &lib.name);
                 if new_name.is_empty() {
                     self.tcx.sess.err(&format!(
                         "an empty renaming target was specified for library `{}`",
-                        name
+                        lib.name
                     ));
                 } else if !any_duplicate {
                     self.tcx.sess.err(&format!(
                         "renaming of the library `{}` was specified, \
                                                 however this crate contains no `#[link(...)]` \
                                                 attributes referencing this library.",
-                        name
+                        lib.name
                     ));
-                } else if !renames.insert(name) {
+                } else if !renames.insert(&lib.name) {
                     self.tcx.sess.err(&format!(
                         "multiple renamings were \
                                                 specified for library `{}` .",
-                        name
+                        lib.name
                     ));
                 }
             }
@@ -229,7 +305,7 @@ impl Collector<'tcx> {
         // it.  (This ensures that the linker is able to see symbols from
         // all possible dependent libraries before linking in the library
         // in question.)
-        for &(ref name, ref new_name, kind) in &self.tcx.sess.opts.libs {
+        for passed_lib in &self.tcx.sess.opts.libs {
             // If we've already added any native libraries with the same
             // name, they will be pulled out into `existing`, so that we
             // can move them to the end of the list below.
@@ -237,13 +313,14 @@ impl Collector<'tcx> {
                 .libs
                 .drain_filter(|lib| {
                     if let Some(lib_name) = lib.name {
-                        if lib_name.as_str() == *name {
-                            if kind != NativeLibKind::Unspecified {
-                                lib.kind = kind;
+                        if lib_name.as_str() == passed_lib.name {
+                            if passed_lib.kind != NativeLibKind::Unspecified {
+                                lib.kind = passed_lib.kind;
                             }
-                            if let Some(new_name) = new_name {
+                            if let Some(new_name) = &passed_lib.new_name {
                                 lib.name = Some(Symbol::intern(new_name));
                             }
+                            lib.verbatim = passed_lib.verbatim;
                             return true;
                         }
                     }
@@ -252,13 +329,14 @@ impl Collector<'tcx> {
                 .collect::<Vec<_>>();
             if existing.is_empty() {
                 // Add if not found
-                let new_name = new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
+                let new_name = passed_lib.new_name.as_ref().map(|s| &**s); // &Option<String> -> Option<&str>
                 let lib = NativeLib {
-                    name: Some(Symbol::intern(new_name.unwrap_or(name))),
-                    kind,
+                    name: Some(Symbol::intern(new_name.unwrap_or(&passed_lib.name))),
+                    kind: passed_lib.kind,
                     cfg: None,
                     foreign_module: None,
                     wasm_import_module: None,
+                    verbatim: passed_lib.verbatim,
                 };
                 self.register_native_lib(None, lib);
             } else {
diff --git a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
index 8595a70dd94..b11ad6c7ff8 100644
--- a/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
+++ b/compiler/rustc_metadata/src/rmeta/decoder/cstore_impl.rs
@@ -256,16 +256,13 @@ pub fn provide(providers: &mut Providers) {
     // resolve! Does this work? Unsure! That's what the issue is about
     *providers = Providers {
         is_dllimport_foreign_item: |tcx, id| match tcx.native_library_kind(id) {
-            Some(NativeLibKind::Dylib | NativeLibKind::RawDylib | NativeLibKind::Unspecified) => {
-                true
-            }
+            Some(
+                NativeLibKind::Dylib { .. } | NativeLibKind::RawDylib | NativeLibKind::Unspecified,
+            ) => true,
             _ => false,
         },
         is_statically_included_foreign_item: |tcx, id| {
-            matches!(
-                tcx.native_library_kind(id),
-                Some(NativeLibKind::StaticBundle | NativeLibKind::StaticNoBundle)
-            )
+            matches!(tcx.native_library_kind(id), Some(NativeLibKind::Static { .. }))
         },
         native_library_kind: |tcx, id| {
             tcx.native_libraries(id.krate)
diff --git a/compiler/rustc_middle/src/middle/cstore.rs b/compiler/rustc_middle/src/middle/cstore.rs
index 4f1ca968c30..82b9ebcc7ec 100644
--- a/compiler/rustc_middle/src/middle/cstore.rs
+++ b/compiler/rustc_middle/src/middle/cstore.rs
@@ -94,6 +94,7 @@ pub struct NativeLib {
     pub cfg: Option<ast::MetaItem>,
     pub foreign_module: Option<DefId>,
     pub wasm_import_module: Option<Symbol>,
+    pub verbatim: Option<bool>,
 }
 
 #[derive(Clone, TyEncodable, TyDecodable, HashStable, Debug)]
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 08d6f4a46fe..6956b815f19 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -5,7 +5,7 @@ pub use crate::options::*;
 
 use crate::lint;
 use crate::search_paths::SearchPath;
-use crate::utils::{CanonicalizedPath, NativeLibKind};
+use crate::utils::{CanonicalizedPath, NativeLib, NativeLibKind};
 use crate::{early_error, early_warn, Session};
 
 use rustc_data_structures::fx::FxHashSet;
@@ -1027,8 +1027,11 @@ pub fn rustc_short_optgroups() -> Vec<RustcOptGroup> {
             "",
             "Link the generated crate(s) to the specified native
                              library NAME. The optional KIND can be one of
-                             static, framework, or dylib (the default).",
-            "[KIND=]NAME",
+                             static, framework, or dylib (the default).
+                             Optional comma separated MODIFIERS (bundle|verbatim|whole-archive|as-needed)
+                             may be specified each with a prefix of either '+' to
+                             enable or '-' to disable.",
+            "[KIND[:MODIFIERS]=]NAME[:RENAME]",
         ),
         make_crate_type_option(),
         opt::opt_s("", "crate-name", "Specify the name of the crate being built", "NAME"),
@@ -1591,52 +1594,127 @@ fn select_debuginfo(
     }
 }
 
-fn parse_libs(
-    matches: &getopts::Matches,
+fn parse_native_lib_kind(kind: &str, error_format: ErrorOutputType) -> NativeLibKind {
+    match kind {
+        "dylib" => NativeLibKind::Dylib { as_needed: None },
+        "framework" => NativeLibKind::Framework { as_needed: None },
+        "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
+        "static-nobundle" => {
+            early_warn(
+                error_format,
+                "library kind `static-nobundle` has been superseded by specifying \
+                `-bundle` on library kind `static`. Try `static:-bundle`",
+            );
+            NativeLibKind::Static { bundle: Some(false), whole_archive: None }
+        }
+        s => early_error(
+            error_format,
+            &format!("unknown library kind `{}`, expected one of dylib, framework, or static", s),
+        ),
+    }
+}
+
+fn parse_native_lib_modifiers(
+    is_nightly: bool,
+    mut kind: NativeLibKind,
+    modifiers: &str,
     error_format: ErrorOutputType,
-) -> Vec<(String, Option<String>, NativeLibKind)> {
+) -> (NativeLibKind, Option<bool>) {
+    let mut verbatim = None;
+    for modifier in modifiers.split(',') {
+        let (modifier, value) = match modifier.strip_prefix(&['+', '-'][..]) {
+            Some(m) => (m, modifier.starts_with('+')),
+            None => early_error(
+                error_format,
+                "invalid linking modifier syntax, expected '+' or '-' prefix \
+                    before one of: bundle, verbatim, whole-archive, as-needed",
+            ),
+        };
+
+        if !is_nightly {
+            early_error(
+                error_format,
+                "linking modifiers are currently unstable and only accepted on \
+                the nightly compiler",
+            );
+        }
+
+        match (modifier, &mut kind) {
+            ("bundle", NativeLibKind::Static { bundle, .. }) => {
+                *bundle = Some(value);
+            }
+            ("bundle", _) => early_error(
+                error_format,
+                "bundle linking modifier is only compatible with \
+                    `static` linking kind",
+            ),
+
+            ("verbatim", _) => verbatim = Some(value),
+
+            ("whole-archive", NativeLibKind::Static { whole_archive, .. }) => {
+                *whole_archive = Some(value);
+            }
+            ("whole-archive", _) => early_error(
+                error_format,
+                "whole-archive linking modifier is only compatible with \
+                    `static` linking kind",
+            ),
+
+            ("as-needed", NativeLibKind::Dylib { as_needed })
+            | ("as-needed", NativeLibKind::Framework { as_needed }) => {
+                *as_needed = Some(value);
+            }
+            ("as-needed", _) => early_error(
+                error_format,
+                "as-needed linking modifier is only compatible with \
+                    `dylib` and `framework` linking kinds",
+            ),
+
+            _ => early_error(
+                error_format,
+                &format!(
+                    "unrecognized linking modifier `{}`, expected one \
+                    of: bundle, verbatim, whole-archive, as-needed",
+                    modifier
+                ),
+            ),
+        }
+    }
+
+    (kind, verbatim)
+}
+
+fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<NativeLib> {
+    let is_nightly = nightly_options::match_is_nightly_build(matches);
     matches
         .opt_strs("l")
         .into_iter()
         .map(|s| {
-            // Parse string of the form "[KIND=]lib[:new_name]",
-            // where KIND is one of "dylib", "framework", "static".
-            let (name, kind) = match s.split_once('=') {
-                None => (s, NativeLibKind::Unspecified),
+            // Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
+            // where KIND is one of "dylib", "framework", "static" and
+            // where MODIFIERS are  a comma separated list of supported modifiers
+            // (bundle, verbatim, whole-archive, as-needed). Each modifier is prefixed
+            // with either + or - to indicate whether it is enabled or disabled.
+            // The last value specified for a given modifier wins.
+            let (name, kind, verbatim) = match s.split_once('=') {
+                None => (s, NativeLibKind::Unspecified, None),
                 Some((kind, name)) => {
-                    let kind = match kind {
-                        "dylib" => NativeLibKind::Dylib,
-                        "framework" => NativeLibKind::Framework,
-                        "static" => NativeLibKind::StaticBundle,
-                        "static-nobundle" => NativeLibKind::StaticNoBundle,
-                        s => {
-                            early_error(
-                                error_format,
-                                &format!(
-                                    "unknown library kind `{}`, expected \
-                                     one of dylib, framework, or static",
-                                    s
-                                ),
-                            );
+                    let (kind, verbatim) = match kind.split_once(':') {
+                        None => (parse_native_lib_kind(kind, error_format), None),
+                        Some((kind, modifiers)) => {
+                            let kind = parse_native_lib_kind(kind, error_format);
+                            parse_native_lib_modifiers(is_nightly, kind, modifiers, error_format)
                         }
                     };
-                    (name.to_string(), kind)
+                    (name.to_string(), kind, verbatim)
                 }
             };
-            if kind == NativeLibKind::StaticNoBundle
-                && !nightly_options::match_is_nightly_build(matches)
-            {
-                early_error(
-                    error_format,
-                    "the library kind 'static-nobundle' is only \
-                     accepted on the nightly compiler",
-                );
-            }
+
             let (name, new_name) = match name.split_once(':') {
                 None => (name, None),
                 Some((name, new_name)) => (name.to_string(), Some(new_name.to_owned())),
             };
-            (name, new_name, kind)
+            NativeLib { name, new_name, kind, verbatim }
         })
         .collect()
 }
@@ -2316,7 +2394,7 @@ crate mod dep_tracking {
     };
     use crate::lint;
     use crate::options::WasiExecModel;
-    use crate::utils::NativeLibKind;
+    use crate::utils::{NativeLib, NativeLibKind};
     use rustc_feature::UnstableFeatures;
     use rustc_span::edition::Edition;
     use rustc_target::spec::{CodeModel, MergeFunctions, PanicStrategy, RelocModel};
@@ -2391,6 +2469,7 @@ crate mod dep_tracking {
         DebugInfo,
         UnstableFeatures,
         OutputTypes,
+        NativeLib,
         NativeLibKind,
         SanitizerSet,
         CFGuard,
@@ -2409,8 +2488,8 @@ crate mod dep_tracking {
         PathBuf,
         (PathBuf, PathBuf),
         CrateType,
+        NativeLib,
         (String, lint::Level),
-        (String, Option<String>, NativeLibKind),
         (String, u64)
     );
 
diff --git a/compiler/rustc_session/src/options.rs b/compiler/rustc_session/src/options.rs
index cd28517bfbc..7799dfd1978 100644
--- a/compiler/rustc_session/src/options.rs
+++ b/compiler/rustc_session/src/options.rs
@@ -3,7 +3,7 @@ use crate::config::*;
 use crate::early_error;
 use crate::lint;
 use crate::search_paths::SearchPath;
-use crate::utils::NativeLibKind;
+use crate::utils::NativeLib;
 
 use rustc_target::spec::{CodeModel, LinkerFlavor, MergeFunctions, PanicStrategy, SanitizerSet};
 use rustc_target::spec::{RelocModel, RelroLevel, SplitDebuginfo, TargetTriple, TlsModel};
@@ -133,7 +133,7 @@ top_level_options!(
         describe_lints: bool [UNTRACKED],
         output_types: OutputTypes [TRACKED],
         search_paths: Vec<SearchPath> [UNTRACKED],
-        libs: Vec<(String, Option<String>, NativeLibKind)> [TRACKED],
+        libs: Vec<NativeLib> [TRACKED],
         maybe_sysroot: Option<PathBuf> [UNTRACKED],
 
         target_triple: TargetTriple [TRACKED],
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index e9d597d1ba6..1a044e677a0 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -19,25 +19,42 @@ impl Session {
 
 #[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
 pub enum NativeLibKind {
-    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included
-    /// when linking a final binary, but not when archiving an rlib.
-    StaticNoBundle,
-    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC) included
-    /// when linking a final binary, but also included when archiving an rlib.
-    StaticBundle,
+    /// Static library (e.g. `libfoo.a` on Linux or `foo.lib` on Windows/MSVC)
+    Static {
+        /// Whether to bundle objects from static library into produced rlib
+        bundle: Option<bool>,
+        /// Whether to link static library without throwing any object files away
+        whole_archive: Option<bool>,
+    },
     /// Dynamic library (e.g. `libfoo.so` on Linux)
     /// or an import library corresponding to a dynamic library (e.g. `foo.lib` on Windows/MSVC).
-    Dylib,
+    Dylib {
+        /// Whether the dynamic library will be linked only if it satifies some undefined symbols
+        as_needed: Option<bool>,
+    },
     /// Dynamic library (e.g. `foo.dll` on Windows) without a corresponding import library.
     RawDylib,
     /// A macOS-specific kind of dynamic libraries.
-    Framework,
+    Framework {
+        /// Whether the framework will be linked only if it satifies some undefined symbols
+        as_needed: Option<bool>,
+    },
     /// The library kind wasn't specified, `Dylib` is currently used as a default.
     Unspecified,
 }
 
 rustc_data_structures::impl_stable_hash_via_hash!(NativeLibKind);
 
+#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Encodable, Decodable)]
+pub struct NativeLib {
+    pub name: String,
+    pub new_name: Option<String>,
+    pub kind: NativeLibKind,
+    pub verbatim: Option<bool>,
+}
+
+rustc_data_structures::impl_stable_hash_via_hash!(NativeLib);
+
 /// A path that has been canonicalized along with its original, non-canonicalized form
 #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
 pub struct CanonicalizedPath {
diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs
index b2dac10c83f..4c80b84e3d2 100644
--- a/compiler/rustc_span/src/symbol.rs
+++ b/compiler/rustc_span/src/symbol.rs
@@ -748,6 +748,7 @@ symbols! {
         minnumf64,
         mips_target_feature,
         misc,
+        modifiers,
         module,
         module_path,
         more_struct_aliases,
@@ -763,6 +764,11 @@ symbols! {
         naked,
         naked_functions,
         name,
+        native_link_modifiers,
+        native_link_modifiers_as_needed,
+        native_link_modifiers_bundle,
+        native_link_modifiers_verbatim,
+        native_link_modifiers_whole_archive,
         ne,
         nearbyintf32,
         nearbyintf64,