Fix linking dynamic libraries in Rust binaries

An earlier commit, 646a62e, changed how Rust links against native
dependencies. Rather than a `-l...` argument to instruct rustc to
link against the specified library, GN uses `-Clink-arg=` to bypass
rustc and directly instruct the linker to link the native library.

This has a few implications: normally, Rust uses the `-Bstatic`
and `-Bdynamic` linker arguments to tell the linker whether or not
to link dynamic binaries. These flags take effect for the rest
of the arguments specified (or until another one of the flags changes
the mode). rustc has logic to ensure that these flags are emitted
correctly, switching the mode as necessary when it outputs linker
arguments to link objects of each type.

Bypassing rustc via `-Clink-arg` prevents this logic from being used,
and so it may be the case that, when rustc is building up the linker
arguments, it's already emitted a `-Bstatic` flag, causing the linker
arguments to be in the wrong mode when we emit the arguments to
link the native libraries. Specicially, we cannot pass a dynamic object
when the linker is in static-only mode.

This commit addresses this issue by emitting a `-Bdynamic` linker
argument before emitting the `-Clink-arg` arguments for the native
libraries, ensuring that the linker is always in a mode that allows
linking dynamic libraries.

Change-Id: I79c1f285e661dc7e4638b1374b718fbbc4f31049
Reviewed-on: https://gn-review.googlesource.com/c/gn/+/12000
Reviewed-by: Tyler Mandry <tmandry@google.com>
Commit-Queue: Tyler Mandry <tmandry@google.com>
diff --git a/src/gn/ninja_rust_binary_target_writer.cc b/src/gn/ninja_rust_binary_target_writer.cc
index 73cc9e8..afdf5d9 100644
--- a/src/gn/ninja_rust_binary_target_writer.cc
+++ b/src/gn/ninja_rust_binary_target_writer.cc
@@ -316,6 +316,12 @@
     out_ << " -Lnative=";
     path_output_.WriteDir(out_, nonrustdep_dir, PathOutput::DIR_NO_LAST_SLASH);
   }
+  // Before outputting any libraries to link, ensure the linker is in a mode
+  // that allows dynamic linking, as rustc may have previously put it into
+  // static-only mode.
+  if (nonrustdeps.size() > 0) {
+    out_ << " -Clink-arg=-Bdynamic";
+  }
   for (const auto& nonrustdep : nonrustdeps) {
     out_ << " -Clink-arg=";
     path_output_.WriteFile(out_, nonrustdep);
diff --git a/src/gn/ninja_rust_binary_target_writer_unittest.cc b/src/gn/ninja_rust_binary_target_writer_unittest.cc
index d130abb..8d3f07e 100644
--- a/src/gn/ninja_rust_binary_target_writer_unittest.cc
+++ b/src/gn/ninja_rust_binary_target_writer_unittest.cc
@@ -448,7 +448,7 @@
         "  externs = --extern mylib=obj/bar/libmylib.rlib\n"
         "  rustdeps = -Ldependency=obj/bar "
         "-Lnative=obj/baz -Lnative=obj/foo -Lnative=. "
-        "-Clink-arg=obj/baz/sourceset.csourceset.o "
+        "-Clink-arg=-Bdynamic -Clink-arg=obj/baz/sourceset.csourceset.o "
         "-Clink-arg=obj/foo/libstatic.a -Clink-arg=./libshared.so "
         "-Clink-arg=./libshared_with_toc.so\n"
         "  ldflags =\n"
@@ -488,7 +488,8 @@
         "build ./foo_bar: rust_bin ../../foo/main.rs | ../../foo/source.rs "
         "../../foo/main.rs obj/foo/libstatic.a\n"
         "  externs =\n"
-        "  rustdeps = -Lnative=obj/foo -Clink-arg=obj/foo/libstatic.a\n"
+        "  rustdeps = -Lnative=obj/foo -Clink-arg=-Bdynamic "
+        "-Clink-arg=obj/foo/libstatic.a\n"
         "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();
@@ -527,7 +528,8 @@
         "../../baz/lib.rs "
         "obj/foo/libstatic.a\n"
         "  externs =\n"
-        "  rustdeps = -Lnative=obj/foo -Clink-arg=obj/foo/libstatic.a\n"
+        "  rustdeps = -Lnative=obj/foo -Clink-arg=-Bdynamic "
+        "-Clink-arg=obj/foo/libstatic.a\n"
         "  ldflags =\n"
         "  sources = ../../baz/lib.rs\n";
     std::string out_str = out.str();
@@ -981,7 +983,7 @@
         "../../foo/main.rs obj/bar/libmylib.so\n"
         "  externs =\n"
         "  rustdeps = -Ldependency=obj/bar -Lnative=obj/bar "
-        "-Clink-arg=obj/bar/libmylib.so\n"
+        "-Clink-arg=-Bdynamic -Clink-arg=obj/bar/libmylib.so\n"
         "  ldflags =\n"
         "  sources = ../../foo/source.rs ../../foo/main.rs\n";
     std::string out_str = out.str();