Skip to content

tests: Port symbol-mangling-hashed to rmake.rs #135768

New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Merged
merged 5 commits into from
Feb 1, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/tools/run-make-support/src/external_deps/rustc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}"));
Expand Down
10 changes: 7 additions & 3 deletions src/tools/run-make-support/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down Expand Up @@ -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::{
Expand All @@ -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;
34 changes: 25 additions & 9 deletions src/tools/run-make-support/src/symbols.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<P, F, R>(path: P, func: F) -> R
where
P: AsRef<Path>,
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<Path>, substrings: &[&str]) -> bool {
with_symbol_iter(path, |syms| {
for sym in syms {
Expand Down
6 changes: 6 additions & 0 deletions src/tools/run-make-support/src/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
1 change: 0 additions & 1 deletion src/tools/tidy/src/allowed_run_make_makefiles.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1 @@
run-make/split-debuginfo/Makefile
run-make/symbol-mangling-hashed/Makefile
48 changes: 0 additions & 48 deletions tests/run-make/symbol-mangling-hashed/Makefile

This file was deleted.

9 changes: 0 additions & 9 deletions tests/run-make/symbol-mangling-hashed/b_bin.rs

This file was deleted.

9 changes: 0 additions & 9 deletions tests/run-make/symbol-mangling-hashed/b_dylib.rs

This file was deleted.

9 changes: 9 additions & 0 deletions tests/run-make/symbol-mangling-hashed/default_bin.rs
Original file line number Diff line number Diff line change
@@ -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();
}
9 changes: 9 additions & 0 deletions tests/run-make/symbol-mangling-hashed/default_dylib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![crate_type = "dylib"]

extern crate hashed_dylib;
extern crate hashed_rlib;

pub fn ddhello() {
hashed_rlib::hrhello();
hashed_dylib::hdhello();
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
#![crate_type = "dylib"]
pub fn hello() {
pub fn hdhello() {
println!("hello dylib");
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
#![crate_type = "rlib"]

pub fn hello() {
pub fn hrhello() {
println!("hello rlib");
}
108 changes: 108 additions & 0 deletions tests/run-make/symbol-mangling-hashed/rmake.rs
Original file line number Diff line number Diff line change
@@ -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:
//! <https://github.com/rust-lang/compiler-team/issues/705>.
//! - Implementation PR: <https://github.com/rust-lang/rust/pull/118636>.
//! - PE format: <https://learn.microsoft.com/en-us/windows/win32/debug/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);
}
}
Loading