diff --git a/src/tools/run-make-support/src/external_deps/rustc.rs b/src/tools/run-make-support/src/external_deps/rustc.rs
index b70db7130f677..8d99924a2d17c 100644
--- a/src/tools/run-make-support/src/external_deps/rustc.rs
+++ b/src/tools/run-make-support/src/external_deps/rustc.rs
@@ -216,6 +216,18 @@ impl Rustc {
self
}
+ /// Specify option of `-C symbol-mangling-version`.
+ pub fn symbol_mangling_version(&mut self, option: &str) -> &mut Self {
+ self.cmd.arg(format!("-Csymbol-mangling-version={option}"));
+ self
+ }
+
+ /// Specify `-C prefer-dynamic`.
+ pub fn prefer_dynamic(&mut self) -> &mut Self {
+ self.cmd.arg(format!("-Cprefer-dynamic"));
+ self
+ }
+
/// Specify error format to use
pub fn error_format(&mut self, format: &str) -> &mut Self {
self.cmd.arg(format!("--error-format={format}"));
diff --git a/src/tools/run-make-support/src/lib.rs b/src/tools/run-make-support/src/lib.rs
index 7316244b3841d..a8c9bec57fde6 100644
--- a/src/tools/run-make-support/src/lib.rs
+++ b/src/tools/run-make-support/src/lib.rs
@@ -47,7 +47,9 @@ pub use wasmparser;
// tidy-alphabetical-end
// Re-exports of external dependencies.
-pub use external_deps::{c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc};
+pub use external_deps::{
+ cargo, c_build, c_cxx_compiler, clang, htmldocck, llvm, python, rustc, rustdoc
+};
// These rely on external dependencies.
pub use c_cxx_compiler::{Cc, Gcc, cc, cxx, extra_c_flags, extra_cxx_flags, gcc};
@@ -79,7 +81,10 @@ pub use env::{env_var, env_var_os, set_current_dir};
pub use run::{cmd, run, run_fail, run_with_args};
/// Helpers for checking target information.
-pub use targets::{is_aix, is_darwin, is_msvc, is_windows, llvm_components_contain, target, uname, apple_os};
+pub use targets::{
+ apple_os, is_aix, is_darwin, is_msvc, is_windows, is_windows_gnu, llvm_components_contain,
+ target, uname,
+};
/// Helpers for building names of output artifacts that are potentially target-specific.
pub use artifact_names::{
@@ -104,4 +109,3 @@ pub use assertion_helpers::{
pub use string::{
count_regex_matches_in_files_with_extension, invalid_utf8_contains, invalid_utf8_not_contains,
};
-use crate::external_deps::cargo;
diff --git a/src/tools/run-make-support/src/symbols.rs b/src/tools/run-make-support/src/symbols.rs
index fd0c866bcc927..e4d244e14a4a1 100644
--- a/src/tools/run-make-support/src/symbols.rs
+++ b/src/tools/run-make-support/src/symbols.rs
@@ -2,28 +2,44 @@ use std::path::Path;
use object::{self, Object, ObjectSymbol, SymbolIterator};
-/// Iterate through the symbols in an object file.
-///
-/// Uses a callback because `SymbolIterator` does not own its data.
+/// Given an [`object::File`], find the exported dynamic symbol names via
+/// [`object::Object::exports`]. This does not distinguish between which section the symbols appear
+/// in.
+#[track_caller]
+pub fn exported_dynamic_symbol_names<'file>(file: &'file object::File<'file>) -> Vec<&'file str> {
+ file.exports()
+ .unwrap()
+ .into_iter()
+ .filter_map(|sym| std::str::from_utf8(sym.name()).ok())
+ .collect()
+}
+
+/// Iterate through the symbols in an object file. See [`object::Object::symbols`].
///
-/// Panics if `path` is not a valid object file readable by the current user.
+/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
+/// parsed as a recognized object file.
+#[track_caller]
pub fn with_symbol_iter
(path: P, func: F) -> R
where
P: AsRef,
F: FnOnce(&mut SymbolIterator<'_, '_>) -> R,
{
- let raw_bytes = crate::fs::read(path);
- let f = object::File::parse(raw_bytes.as_slice()).expect("unable to parse file");
+ let path = path.as_ref();
+ let blob = crate::fs::read(path);
+ let f = object::File::parse(&*blob)
+ .unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
let mut iter = f.symbols();
func(&mut iter)
}
/// Check an object file's symbols for substrings.
///
-/// Returns `true` if any of the symbols found in the object file at
-/// `path` contain a substring listed in `substrings`.
+/// Returns `true` if any of the symbols found in the object file at `path` contain a substring
+/// listed in `substrings`.
///
-/// Panics if `path` is not a valid object file readable by the current user.
+/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
+/// parsed as a recognized object file.
+#[track_caller]
pub fn any_symbol_contains(path: impl AsRef, substrings: &[&str]) -> bool {
with_symbol_iter(path, |syms| {
for sym in syms {
diff --git a/src/tools/run-make-support/src/targets.rs b/src/tools/run-make-support/src/targets.rs
index ae004fd0cbdd8..a16fca71d2eec 100644
--- a/src/tools/run-make-support/src/targets.rs
+++ b/src/tools/run-make-support/src/targets.rs
@@ -22,6 +22,12 @@ pub fn is_msvc() -> bool {
target().contains("msvc")
}
+/// Check if target is windows-gnu.
+#[must_use]
+pub fn is_windows_gnu() -> bool {
+ target().ends_with("windows-gnu")
+}
+
/// Check if target uses macOS.
#[must_use]
pub fn is_darwin() -> bool {
diff --git a/src/tools/tidy/src/allowed_run_make_makefiles.txt b/src/tools/tidy/src/allowed_run_make_makefiles.txt
index e75d3dc2147bc..45b40b17ea37f 100644
--- a/src/tools/tidy/src/allowed_run_make_makefiles.txt
+++ b/src/tools/tidy/src/allowed_run_make_makefiles.txt
@@ -1,2 +1 @@
run-make/split-debuginfo/Makefile
-run-make/symbol-mangling-hashed/Makefile
diff --git a/tests/run-make/symbol-mangling-hashed/Makefile b/tests/run-make/symbol-mangling-hashed/Makefile
deleted file mode 100644
index c95036ead9587..0000000000000
--- a/tests/run-make/symbol-mangling-hashed/Makefile
+++ /dev/null
@@ -1,48 +0,0 @@
-include ../tools.mk
-
-# ignore-cross-compile
-# only-linux
-# only-x86_64
-
-NM=nm -D
-RLIB_NAME=liba_rlib.rlib
-DYLIB_NAME=liba_dylib.so
-SO_NAME=libb_dylib.so
-BIN_NAME=b_bin
-
-ifeq ($(UNAME),Darwin)
-NM=nm -gU
-RLIB_NAME=liba_rlib.rlib
-DYLIB_NAME=liba_dylib.dylib
-SO_NAME=libb_dylib.dylib
-BIN_NAME=b_bin
-endif
-
-ifdef IS_WINDOWS
-NM=nm -g
-RLIB_NAME=liba_rlib.dll.a
-DYLIB_NAME=liba_dylib.dll
-SO_NAME=libb_dylib.dll
-BIN_NAME=b_bin.exe
-endif
-
-all:
- $(RUSTC) -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=hashed -C metadata=foo a_dylib.rs
- $(RUSTC) -C prefer-dynamic -Z unstable-options -C symbol-mangling-version=hashed -C metadata=bar a_rlib.rs
- $(RUSTC) -C prefer-dynamic -L $(TMPDIR) b_dylib.rs
- $(RUSTC) -C prefer-dynamic -L $(TMPDIR) b_bin.rs
-
- # Check hashed symbol name
-
- [ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep -c hello)" -eq "0" ]
- [ "$$($(NM) $(TMPDIR)/$(DYLIB_NAME) | grep _RNxC7a_dylib | grep -c ' T ')" -eq "2" ]
-
- [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep b_dylib | grep -c hello)" -eq "1" ]
- [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC6a_rlib | grep -c ' T ')" -eq "2" ]
- [ "$$($(NM) $(TMPDIR)/$(SO_NAME) | grep _RNxC7a_dylib | grep -c ' U ')" -eq "1" ]
-
- [ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep _RNxC6a_rlib | grep -c ' U ')" -eq "1" ]
- [ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep _RNxC7a_dylib | grep -c ' U ')" -eq "1" ]
- [ "$$($(NM) $(TMPDIR)/$(BIN_NAME) | grep b_dylib | grep hello | grep -c ' U ')" -eq "1" ]
-
- $(call RUN,$(BIN_NAME))
diff --git a/tests/run-make/symbol-mangling-hashed/b_bin.rs b/tests/run-make/symbol-mangling-hashed/b_bin.rs
deleted file mode 100644
index 8ee7fecda62a0..0000000000000
--- a/tests/run-make/symbol-mangling-hashed/b_bin.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-extern crate a_dylib;
-extern crate a_rlib;
-extern crate b_dylib;
-
-fn main() {
- a_rlib::hello();
- a_dylib::hello();
- b_dylib::hello();
-}
diff --git a/tests/run-make/symbol-mangling-hashed/b_dylib.rs b/tests/run-make/symbol-mangling-hashed/b_dylib.rs
deleted file mode 100644
index 3252c9c75c2a4..0000000000000
--- a/tests/run-make/symbol-mangling-hashed/b_dylib.rs
+++ /dev/null
@@ -1,9 +0,0 @@
-#![crate_type = "dylib"]
-
-extern crate a_dylib;
-extern crate a_rlib;
-
-pub fn hello() {
- a_rlib::hello();
- a_dylib::hello();
-}
diff --git a/tests/run-make/symbol-mangling-hashed/default_bin.rs b/tests/run-make/symbol-mangling-hashed/default_bin.rs
new file mode 100644
index 0000000000000..387596705c364
--- /dev/null
+++ b/tests/run-make/symbol-mangling-hashed/default_bin.rs
@@ -0,0 +1,9 @@
+extern crate default_dylib;
+extern crate hashed_dylib;
+extern crate hashed_rlib;
+
+fn main() {
+ hashed_rlib::hrhello();
+ hashed_dylib::hdhello();
+ default_dylib::ddhello();
+}
diff --git a/tests/run-make/symbol-mangling-hashed/default_dylib.rs b/tests/run-make/symbol-mangling-hashed/default_dylib.rs
new file mode 100644
index 0000000000000..986d1c7b74d21
--- /dev/null
+++ b/tests/run-make/symbol-mangling-hashed/default_dylib.rs
@@ -0,0 +1,9 @@
+#![crate_type = "dylib"]
+
+extern crate hashed_dylib;
+extern crate hashed_rlib;
+
+pub fn ddhello() {
+ hashed_rlib::hrhello();
+ hashed_dylib::hdhello();
+}
diff --git a/tests/run-make/symbol-mangling-hashed/a_dylib.rs b/tests/run-make/symbol-mangling-hashed/hashed_dylib.rs
similarity index 74%
rename from tests/run-make/symbol-mangling-hashed/a_dylib.rs
rename to tests/run-make/symbol-mangling-hashed/hashed_dylib.rs
index 49d65b72cacc1..fbb7cba43e050 100644
--- a/tests/run-make/symbol-mangling-hashed/a_dylib.rs
+++ b/tests/run-make/symbol-mangling-hashed/hashed_dylib.rs
@@ -1,4 +1,4 @@
#![crate_type = "dylib"]
-pub fn hello() {
+pub fn hdhello() {
println!("hello dylib");
}
diff --git a/tests/run-make/symbol-mangling-hashed/a_rlib.rs b/tests/run-make/symbol-mangling-hashed/hashed_rlib.rs
similarity index 74%
rename from tests/run-make/symbol-mangling-hashed/a_rlib.rs
rename to tests/run-make/symbol-mangling-hashed/hashed_rlib.rs
index 71e44ccc20075..048e67784d23f 100644
--- a/tests/run-make/symbol-mangling-hashed/a_rlib.rs
+++ b/tests/run-make/symbol-mangling-hashed/hashed_rlib.rs
@@ -1,5 +1,5 @@
#![crate_type = "rlib"]
-pub fn hello() {
+pub fn hrhello() {
println!("hello rlib");
}
diff --git a/tests/run-make/symbol-mangling-hashed/rmake.rs b/tests/run-make/symbol-mangling-hashed/rmake.rs
new file mode 100644
index 0000000000000..136e6b9fa3a95
--- /dev/null
+++ b/tests/run-make/symbol-mangling-hashed/rmake.rs
@@ -0,0 +1,108 @@
+// ignore-tidy-linelength
+//! Basic smoke test for the unstable option `-C symbol_mangling_version=hashed` which aims to
+//! replace full symbol mangling names based on hash digests to shorten symbol name lengths in
+//! dylibs for space savings.
+//!
+//! # References
+//!
+//! - MCP #705: Provide option to shorten symbol names by replacing them with a digest:
+//! .
+//! - Implementation PR: .
+//! - PE format: .
+
+//@ ignore-cross-compile
+
+#![deny(warnings)]
+
+use run_make_support::symbols::exported_dynamic_symbol_names;
+use run_make_support::{bin_name, cwd, dynamic_lib_name, is_darwin, object, rfs, run, rustc};
+
+macro_rules! adjust_symbol_prefix {
+ ($name:literal) => {
+ if is_darwin() { concat!("_", $name) } else { $name }
+ };
+}
+
+fn main() {
+ rustc()
+ .input("hashed_dylib.rs")
+ .prefer_dynamic()
+ .arg("-Zunstable-options")
+ .symbol_mangling_version("hashed")
+ .metadata("foo")
+ .run();
+
+ rustc()
+ .input("hashed_rlib.rs")
+ .prefer_dynamic()
+ .arg("-Zunstable-options")
+ .symbol_mangling_version("hashed")
+ .metadata("bar")
+ .run();
+
+ rustc().input("default_dylib.rs").library_search_path(cwd()).prefer_dynamic().run();
+ rustc().input("default_bin.rs").library_search_path(cwd()).prefer_dynamic().run();
+
+ {
+ // Check hashed symbol name
+
+ let dylib_filename = dynamic_lib_name("hashed_dylib");
+ println!("checking dylib `{dylib_filename}`");
+
+ let dylib_blob = rfs::read(&dylib_filename);
+ let dylib_file = object::File::parse(&*dylib_blob)
+ .unwrap_or_else(|e| panic!("failed to parse `{dylib_filename}`: {e}"));
+
+ let dynamic_symbols = exported_dynamic_symbol_names(&dylib_file);
+
+ if dynamic_symbols.iter().filter(|sym| sym.contains("hdhello")).count() != 0 {
+ eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+ panic!("expected no occurrence of `hdhello`");
+ }
+
+ let expected_prefix = adjust_symbol_prefix!("_RNxC12hashed_dylib");
+ if dynamic_symbols.iter().filter(|sym| sym.starts_with(expected_prefix)).count() != 2 {
+ eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+ panic!("expected two dynamic symbols starting with `{expected_prefix}`");
+ }
+ }
+
+ {
+ let dylib_filename = dynamic_lib_name("default_dylib");
+ println!("checking so `{dylib_filename}`");
+
+ let dylib_blob = rfs::read(&dylib_filename);
+ let dylib_file = object::File::parse(&*dylib_blob)
+ .unwrap_or_else(|e| panic!("failed to parse `{dylib_filename}`: {e}"));
+
+ let dynamic_symbols = exported_dynamic_symbol_names(&dylib_file);
+
+ if dynamic_symbols
+ .iter()
+ .filter(|sym| sym.contains("default_dylib") && sym.contains("ddhello"))
+ .count()
+ != 1
+ {
+ eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+ panic!("expected one occurrence of mangled `ddhello`");
+ }
+
+ let expected_rlib_prefix = adjust_symbol_prefix!("_RNxC11hashed_rlib");
+ if dynamic_symbols.iter().filter(|sym| sym.starts_with(expected_rlib_prefix)).count() != 2 {
+ eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+ panic!("expected two exported symbols starting with `{expected_rlib_prefix}`");
+ }
+
+ let expected_dylib_prefix = adjust_symbol_prefix!("_RNxC12hashed_dylib");
+ if dynamic_symbols.iter().any(|sym| sym.starts_with("_RNxC12hashed_dylib")) {
+ eprintln!("exported dynamic symbols: {:#?}", dynamic_symbols);
+ panic!("did not expect any symbols starting with `{expected_dylib_prefix}`");
+ }
+ }
+
+ // Check that the final binary can be run.
+ {
+ let bin_filename = bin_name("default_bin");
+ run(&bin_filename);
+ }
+}