From f4816def2d249ebf6068cb02e4c18ae25ee07a7b Mon Sep 17 00:00:00 2001
From: Daniil Belov <belov.dv@phystech.edu>
Date: Tue, 19 Jul 2022 12:00:28 +0300
Subject: [PATCH] Lib kind `-l link-arg` - arbitrary link argument like `-C
 link-arg`, but respecting relative order to other `-l` options, unstable

---
 compiler/rustc_codegen_ssa/src/back/link.rs   | 38 ++++++++++++++-----
 compiler/rustc_session/src/config.rs          | 17 ++++++++-
 compiler/rustc_session/src/utils.rs           |  5 ++-
 .../pass-linker-flags-from-dep/Makefile       | 10 +++++
 .../pass-linker-flags-from-dep/main.rs        |  3 ++
 .../native_dep_1.rs                           |  1 +
 .../native_dep_2.rs                           |  1 +
 .../pass-linker-flags-from-dep/rust_dep.rs    |  9 +++++
 src/test/run-make/pass-linker-flags/Makefile  |  4 ++
 src/test/run-make/pass-linker-flags/rs.rs     |  1 +
 src/test/ui/manual/manual-link-bad-kind.rs    |  2 +-
 .../ui/manual/manual-link-bad-kind.stderr     |  2 +-
 .../ui/manual/manual-link-unsupported-kind.rs |  2 +-
 .../manual-link-unsupported-kind.stderr       |  2 +-
 .../native-library-link-flags/empty-kind-1.rs |  2 +-
 .../empty-kind-1.stderr                       |  2 +-
 .../native-library-link-flags/empty-kind-2.rs |  2 +-
 .../empty-kind-2.stderr                       |  2 +-
 .../link-arg-error.rs                         |  4 ++
 .../link-arg-error.stderr                     |  2 +
 .../link-arg-from-rs.rs                       |  8 ++++
 .../link-arg-from-rs.stderr                   | 16 ++++++++
 22 files changed, 115 insertions(+), 20 deletions(-)
 create mode 100644 src/test/run-make/pass-linker-flags-from-dep/Makefile
 create mode 100644 src/test/run-make/pass-linker-flags-from-dep/main.rs
 create mode 100644 src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs
 create mode 100644 src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs
 create mode 100644 src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs
 create mode 100644 src/test/run-make/pass-linker-flags/Makefile
 create mode 100644 src/test/run-make/pass-linker-flags/rs.rs
 create mode 100644 src/test/ui/native-library-link-flags/link-arg-error.rs
 create mode 100644 src/test/ui/native-library-link-flags/link-arg-error.stderr
 create mode 100644 src/test/ui/native-library-link-flags/link-arg-from-rs.rs
 create mode 100644 src/test/ui/native-library-link-flags/link-arg-from-rs.stderr

diff --git a/compiler/rustc_codegen_ssa/src/back/link.rs b/compiler/rustc_codegen_ssa/src/back/link.rs
index 878a670cba3ef..663a6f3a27756 100644
--- a/compiler/rustc_codegen_ssa/src/back/link.rs
+++ b/compiler/rustc_codegen_ssa/src/back/link.rs
@@ -336,6 +336,7 @@ fn link_rlib<'a, B: ArchiveBuilder<'a>>(
             | NativeLibKind::Dylib { .. }
             | NativeLibKind::Framework { .. }
             | NativeLibKind::RawDylib
+            | NativeLibKind::LinkArg
             | NativeLibKind::Unspecified => continue,
         }
         if let Some(name) = lib.name {
@@ -1287,6 +1288,7 @@ fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLib]) {
                 }
                 // These are included, no need to print them
                 NativeLibKind::Static { bundle: None | Some(true), .. }
+                | NativeLibKind::LinkArg
                 | NativeLibKind::RawDylib => None,
             }
         })
@@ -2225,6 +2227,9 @@ fn add_local_native_libraries(
                 // FIXME(#58713): Proper handling for raw dylibs.
                 bug!("raw_dylib feature not yet implemented");
             }
+            NativeLibKind::LinkArg => {
+                cmd.arg(name);
+            }
         }
     }
 }
@@ -2362,19 +2367,34 @@ fn add_upstream_rust_crates<'a, B: ArchiveBuilder<'a>>(
                             (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) {
+                        match lib.kind {
+                            NativeLibKind::Static {
+                                bundle: Some(false),
+                                whole_archive: Some(true),
+                            } => {
                                 cmd.link_whole_staticlib(
                                     name,
-                                    verbatim,
+                                    lib.verbatim.unwrap_or(false),
                                     search_path.get_or_init(|| archive_search_paths(sess)),
                                 );
-                            } else {
-                                cmd.link_staticlib(name, verbatim);
                             }
+                            NativeLibKind::Static {
+                                bundle: Some(false),
+                                whole_archive: Some(false) | None,
+                            } => {
+                                cmd.link_staticlib(name, lib.verbatim.unwrap_or(false));
+                            }
+                            NativeLibKind::LinkArg => {
+                                cmd.arg(name);
+                            }
+                            NativeLibKind::Dylib { .. }
+                            | NativeLibKind::Framework { .. }
+                            | NativeLibKind::Unspecified
+                            | NativeLibKind::RawDylib => {}
+                            NativeLibKind::Static {
+                                bundle: Some(true) | None,
+                                whole_archive: _,
+                            } => {}
                         }
                     }
                 }
@@ -2565,7 +2585,7 @@ fn add_upstream_native_libraries(
                 // already included them in add_local_native_libraries and
                 // add_upstream_rust_crates
                 NativeLibKind::Static { .. } => {}
-                NativeLibKind::RawDylib => {}
+                NativeLibKind::RawDylib | NativeLibKind::LinkArg => {}
             }
         }
     }
diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs
index 55307b9cebb70..0c3322a795377 100644
--- a/compiler/rustc_session/src/config.rs
+++ b/compiler/rustc_session/src/config.rs
@@ -1942,9 +1942,22 @@ fn parse_native_lib_kind(
         "static" => NativeLibKind::Static { bundle: None, whole_archive: None },
         "dylib" => NativeLibKind::Dylib { as_needed: None },
         "framework" => NativeLibKind::Framework { as_needed: None },
+        "link-arg" => {
+            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!("library kind `link-arg` is unstable{why}"))
+            }
+            NativeLibKind::LinkArg
+        }
         _ => early_error(
             error_format,
-            &format!("unknown library kind `{kind}`, expected one of: static, dylib, framework"),
+            &format!(
+                "unknown library kind `{kind}`, expected one of: static, dylib, framework, link-arg"
+            ),
         ),
     };
     match modifiers {
@@ -2043,7 +2056,7 @@ fn parse_libs(matches: &getopts::Matches, error_format: ErrorOutputType) -> Vec<
         .into_iter()
         .map(|s| {
             // Parse string of the form "[KIND[:MODIFIERS]=]lib[:new_name]",
-            // where KIND is one of "dylib", "framework", "static" and
+            // where KIND is one of "dylib", "framework", "static", "link-arg" 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.
diff --git a/compiler/rustc_session/src/utils.rs b/compiler/rustc_session/src/utils.rs
index bda7b314308a5..9a4f6f9f9ef0c 100644
--- a/compiler/rustc_session/src/utils.rs
+++ b/compiler/rustc_session/src/utils.rs
@@ -34,6 +34,9 @@ pub enum NativeLibKind {
         /// Whether the framework will be linked only if it satisfies some undefined symbols
         as_needed: Option<bool>,
     },
+    /// Argument which is passed to linker, relative order with libraries and other arguments
+    /// is preserved
+    LinkArg,
     /// The library kind wasn't specified, `Dylib` is currently used as a default.
     Unspecified,
 }
@@ -47,7 +50,7 @@ impl NativeLibKind {
             NativeLibKind::Dylib { as_needed } | NativeLibKind::Framework { as_needed } => {
                 as_needed.is_some()
             }
-            NativeLibKind::RawDylib | NativeLibKind::Unspecified => false,
+            NativeLibKind::RawDylib | NativeLibKind::Unspecified | NativeLibKind::LinkArg => false,
         }
     }
 }
diff --git a/src/test/run-make/pass-linker-flags-from-dep/Makefile b/src/test/run-make/pass-linker-flags-from-dep/Makefile
new file mode 100644
index 0000000000000..eecd4a6e60757
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags-from-dep/Makefile
@@ -0,0 +1,10 @@
+-include ../../run-make-fulldeps/tools.mk
+
+all:
+	# Build deps
+	$(RUSTC) native_dep_1.rs --crate-type=staticlib
+	$(RUSTC) native_dep_2.rs --crate-type=staticlib
+	$(RUSTC) rust_dep.rs -l static:-bundle=native_dep_1 -llink-arg=some_flag -l static:-bundle=native_dep_2 --crate-type=lib -Z unstable-options
+
+	# Check sequence of linker args
+	$(RUSTC) main.rs --extern lib=$(TMPDIR)/librust_dep.rlib --crate-type=bin --print link-args | $(CGREP) -e '-lnative_dep_1.*some_flag.*-lnative_dep_2'
diff --git a/src/test/run-make/pass-linker-flags-from-dep/main.rs b/src/test/run-make/pass-linker-flags-from-dep/main.rs
new file mode 100644
index 0000000000000..87bd7a11fe52e
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags-from-dep/main.rs
@@ -0,0 +1,3 @@
+fn main() {
+    native_lib::f();
+}
diff --git a/src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs b/src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs
new file mode 100644
index 0000000000000..fdb2d9ca68e63
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags-from-dep/native_dep_1.rs
@@ -0,0 +1 @@
+pub fn f1() {}
diff --git a/src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs b/src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs
new file mode 100644
index 0000000000000..f788b77118461
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags-from-dep/native_dep_2.rs
@@ -0,0 +1 @@
+pub fn f2() {}
diff --git a/src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs b/src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs
new file mode 100644
index 0000000000000..7f5df1139342f
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags-from-dep/rust_dep.rs
@@ -0,0 +1,9 @@
+extern "C" {
+    pub fn foo();
+}
+
+pub fn f() {
+    unsafe {
+        foo();
+    }
+}
diff --git a/src/test/run-make/pass-linker-flags/Makefile b/src/test/run-make/pass-linker-flags/Makefile
new file mode 100644
index 0000000000000..c0be5fe2413a5
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags/Makefile
@@ -0,0 +1,4 @@
+-include ../../run-make-fulldeps/tools.mk
+
+all:
+	$(RUSTC) rs.rs -l static=l1 -l link-arg=a1 -l static=l2 -l link-arg=a2 -l dylib=d1 -l link-arg=a3 --print link-args | $(CGREP) -e '-ll1.*a1.*-ll2.*a2.*-ld1.*a3'
diff --git a/src/test/run-make/pass-linker-flags/rs.rs b/src/test/run-make/pass-linker-flags/rs.rs
new file mode 100644
index 0000000000000..f328e4d9d04c3
--- /dev/null
+++ b/src/test/run-make/pass-linker-flags/rs.rs
@@ -0,0 +1 @@
+fn main() {}
diff --git a/src/test/ui/manual/manual-link-bad-kind.rs b/src/test/ui/manual/manual-link-bad-kind.rs
index d1609338db666..c50a6c034b593 100644
--- a/src/test/ui/manual/manual-link-bad-kind.rs
+++ b/src/test/ui/manual/manual-link-bad-kind.rs
@@ -1,5 +1,5 @@
 // compile-flags:-l bar=foo
-// error-pattern: unknown library kind `bar`, expected one of: static, dylib, framework
+// error-pattern: unknown library kind `bar`, expected one of: static, dylib, framework, link-arg
 
 fn main() {
 }
diff --git a/src/test/ui/manual/manual-link-bad-kind.stderr b/src/test/ui/manual/manual-link-bad-kind.stderr
index 86146956699f3..647c4c61e0212 100644
--- a/src/test/ui/manual/manual-link-bad-kind.stderr
+++ b/src/test/ui/manual/manual-link-bad-kind.stderr
@@ -1,2 +1,2 @@
-error: unknown library kind `bar`, expected one of: static, dylib, framework
+error: unknown library kind `bar`, expected one of: static, dylib, framework, link-arg
 
diff --git a/src/test/ui/manual/manual-link-unsupported-kind.rs b/src/test/ui/manual/manual-link-unsupported-kind.rs
index 7a40186d504c6..b8ec575a455ff 100644
--- a/src/test/ui/manual/manual-link-unsupported-kind.rs
+++ b/src/test/ui/manual/manual-link-unsupported-kind.rs
@@ -1,5 +1,5 @@
 // compile-flags:-l raw-dylib=foo
-// error-pattern: unknown library kind `raw-dylib`, expected one of: static, dylib, framework
+// error-pattern: unknown library kind `raw-dylib`, expected one of: static, dylib, framework, link-arg
 
 fn main() {
 }
diff --git a/src/test/ui/manual/manual-link-unsupported-kind.stderr b/src/test/ui/manual/manual-link-unsupported-kind.stderr
index 4965c0af5f241..ae4a1ec9a959b 100644
--- a/src/test/ui/manual/manual-link-unsupported-kind.stderr
+++ b/src/test/ui/manual/manual-link-unsupported-kind.stderr
@@ -1,2 +1,2 @@
-error: unknown library kind `raw-dylib`, expected one of: static, dylib, framework
+error: unknown library kind `raw-dylib`, expected one of: static, dylib, framework, link-arg
 
diff --git a/src/test/ui/native-library-link-flags/empty-kind-1.rs b/src/test/ui/native-library-link-flags/empty-kind-1.rs
index 086d8cff95770..18937856d20d3 100644
--- a/src/test/ui/native-library-link-flags/empty-kind-1.rs
+++ b/src/test/ui/native-library-link-flags/empty-kind-1.rs
@@ -1,6 +1,6 @@
 // Unspecified kind should fail with an error
 
 // compile-flags: -l =mylib
-// error-pattern: unknown library kind ``, expected one of: static, dylib, framework
+// error-pattern: unknown library kind ``, expected one of: static, dylib, framework, link-arg
 
 fn main() {}
diff --git a/src/test/ui/native-library-link-flags/empty-kind-1.stderr b/src/test/ui/native-library-link-flags/empty-kind-1.stderr
index 37846c0b06f69..3e5b054933999 100644
--- a/src/test/ui/native-library-link-flags/empty-kind-1.stderr
+++ b/src/test/ui/native-library-link-flags/empty-kind-1.stderr
@@ -1,2 +1,2 @@
-error: unknown library kind ``, expected one of: static, dylib, framework
+error: unknown library kind ``, expected one of: static, dylib, framework, link-arg
 
diff --git a/src/test/ui/native-library-link-flags/empty-kind-2.rs b/src/test/ui/native-library-link-flags/empty-kind-2.rs
index 45ec8ec85e301..851eb63fcd8b8 100644
--- a/src/test/ui/native-library-link-flags/empty-kind-2.rs
+++ b/src/test/ui/native-library-link-flags/empty-kind-2.rs
@@ -1,6 +1,6 @@
 // Unspecified kind should fail with an error
 
 // compile-flags: -l :+bundle=mylib
-// error-pattern: unknown library kind ``, expected one of: static, dylib, framework
+// error-pattern: unknown library kind ``, expected one of: static, dylib, framework, link-arg
 
 fn main() {}
diff --git a/src/test/ui/native-library-link-flags/empty-kind-2.stderr b/src/test/ui/native-library-link-flags/empty-kind-2.stderr
index 37846c0b06f69..3e5b054933999 100644
--- a/src/test/ui/native-library-link-flags/empty-kind-2.stderr
+++ b/src/test/ui/native-library-link-flags/empty-kind-2.stderr
@@ -1,2 +1,2 @@
-error: unknown library kind ``, expected one of: static, dylib, framework
+error: unknown library kind ``, expected one of: static, dylib, framework, link-arg
 
diff --git a/src/test/ui/native-library-link-flags/link-arg-error.rs b/src/test/ui/native-library-link-flags/link-arg-error.rs
new file mode 100644
index 0000000000000..e041650d024f4
--- /dev/null
+++ b/src/test/ui/native-library-link-flags/link-arg-error.rs
@@ -0,0 +1,4 @@
+// compile-flags: -l link-arg:+bundle=arg -Z unstable-options
+// error-pattern: linking modifier `bundle` is only compatible with `static` linking kind
+
+fn main() {}
diff --git a/src/test/ui/native-library-link-flags/link-arg-error.stderr b/src/test/ui/native-library-link-flags/link-arg-error.stderr
new file mode 100644
index 0000000000000..e1d01e1415274
--- /dev/null
+++ b/src/test/ui/native-library-link-flags/link-arg-error.stderr
@@ -0,0 +1,2 @@
+error: linking modifier `bundle` is only compatible with `static` linking kind
+
diff --git a/src/test/ui/native-library-link-flags/link-arg-from-rs.rs b/src/test/ui/native-library-link-flags/link-arg-from-rs.rs
new file mode 100644
index 0000000000000..075e4d9e79e46
--- /dev/null
+++ b/src/test/ui/native-library-link-flags/link-arg-from-rs.rs
@@ -0,0 +1,8 @@
+// link-arg is not supposed to be usable in #[link] attributes
+
+// compile-flags:
+// error-pattern: error[E0458]: unknown link kind `link-arg`, expected one of: static, dylib, framework, raw-dylib
+
+#[link(kind = "link-arg")]
+extern "C" {}
+pub fn main() {}
diff --git a/src/test/ui/native-library-link-flags/link-arg-from-rs.stderr b/src/test/ui/native-library-link-flags/link-arg-from-rs.stderr
new file mode 100644
index 0000000000000..69a7825c0b105
--- /dev/null
+++ b/src/test/ui/native-library-link-flags/link-arg-from-rs.stderr
@@ -0,0 +1,16 @@
+error[E0458]: unknown link kind `link-arg`, expected one of: static, dylib, framework, raw-dylib
+  --> $DIR/link-arg-from-rs.rs:6:15
+   |
+LL | #[link(kind = "link-arg")]
+   |               ^^^^^^^^^^ unknown link kind
+
+error[E0459]: `#[link]` attribute requires a `name = "string"` argument
+  --> $DIR/link-arg-from-rs.rs:6:1
+   |
+LL | #[link(kind = "link-arg")]
+   | ^^^^^^^^^^^^^^^^^^^^^^^^^^ missing `name` argument
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0458, E0459.
+For more information about an error, try `rustc --explain E0458`.