Ensure dynamic library dependencies end up in the runfiles directory (#2671)

Right now the runfiles directory only contains "data" that is attached
to a rust_binary() or one of its dependencies. This is inconsistent with
cc_binary() and go_binary(), which also include an `_solib_*/` directory
containing shared library dependencies.

When doing a plain 'bazel run' against the resulting executable, this is
not a noticeable issue. The reason being that the runtime dynamic linker
will use `${execroot}/_solib_*`. However, it does become a problem when
`pkg_tar(include_runfiles = True)` is used to construct an archive of
the binary so that it can be placed in a container image.

This change extends the runfiles gathering logic to merge all
LibraryToLink.dynamic_library files in there as well.
diff --git a/rust/private/rustc.bzl b/rust/private/rustc.bzl
index 98806f1..6dbb258 100644
--- a/rust/private/rustc.bzl
+++ b/rust/private/rustc.bzl
@@ -1444,8 +1444,18 @@
 
     experimental_use_coverage_metadata_files = toolchain._experimental_use_coverage_metadata_files
 
+    dynamic_libraries = [
+        library_to_link.dynamic_library
+        for dep in getattr(ctx.attr, "deps", [])
+        if CcInfo in dep
+        for linker_input in dep[CcInfo].linking_context.linker_inputs.to_list()
+        for library_to_link in linker_input.libraries
+        if _is_dylib(library_to_link)
+    ]
     runfiles = ctx.runfiles(
-        files = getattr(ctx.files, "data", []) + ([] if experimental_use_coverage_metadata_files else coverage_runfiles),
+        files = getattr(ctx.files, "data", []) +
+                ([] if experimental_use_coverage_metadata_files else coverage_runfiles) +
+                dynamic_libraries,
         collect_data = True,
     )
     if getattr(ctx.attr, "crate", None):
diff --git a/test/cc_shared_library/BUILD.bazel b/test/cc_shared_library/BUILD.bazel
index 82e1fb1..e5145d6 100644
--- a/test/cc_shared_library/BUILD.bazel
+++ b/test/cc_shared_library/BUILD.bazel
@@ -1,5 +1,5 @@
-load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
-load("@rules_rust//rust:defs.bzl", "rust_static_library")
+load("@rules_cc//cc:defs.bzl", "cc_import", "cc_library", "cc_test")
+load("@rules_rust//rust:defs.bzl", "rust_binary", "rust_static_library")
 
 rust_static_library(
     name = "rust_lib",
@@ -28,3 +28,30 @@
     linkstatic = True,
     deps = [":c_lib"],
 )
+
+NOT_WINDOWS = select({
+    "@platforms//os:linux": [],
+    "@platforms//os:macos": [],
+    "//conditions:default": ["@platforms//:incompatible"],
+})
+
+cc_import(
+    name = "shared_import",
+    shared_library = ":shared",
+    target_compatible_with = NOT_WINDOWS,
+)
+
+rust_binary(
+    name = "linked_against_shared",
+    srcs = ["linked_against_shared.rs"],
+    edition = "2018",
+    target_compatible_with = NOT_WINDOWS,
+    deps = [":shared_import"],
+)
+
+sh_test(
+    name = "runfiles_contains_shared",
+    srcs = ["runfiles_contains_shared.sh"],
+    data = [":linked_against_shared"],
+    target_compatible_with = NOT_WINDOWS,
+)
diff --git a/test/cc_shared_library/linked_against_shared.rs b/test/cc_shared_library/linked_against_shared.rs
new file mode 100644
index 0000000..57a2490
--- /dev/null
+++ b/test/cc_shared_library/linked_against_shared.rs
@@ -0,0 +1,6 @@
+extern "C" {
+    fn foo() -> i32;
+}
+fn main() {
+    println!("{}", unsafe { foo() })
+}
diff --git a/test/cc_shared_library/runfiles_contains_shared.sh b/test/cc_shared_library/runfiles_contains_shared.sh
new file mode 100755
index 0000000..612229f
--- /dev/null
+++ b/test/cc_shared_library/runfiles_contains_shared.sh
@@ -0,0 +1,7 @@
+#!/bin/sh
+
+set -eux
+
+# Validate that the runfiles directory actually contains the shared
+# library against which the Rust binary is linked.
+test -n "$(find ${RUNFILES_DIR} -name 'libshared.*')"