Skip to content

Adjust run-make-support::symbols helpers #143837

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
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
179 changes: 155 additions & 24 deletions src/tools/run-make-support/src/symbols.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use std::collections::BTreeSet;
use std::path::Path;

use object::{self, Object, ObjectSymbol, SymbolIterator};
Expand All @@ -14,47 +15,177 @@ pub fn exported_dynamic_symbol_names<'file>(file: &'file object::File<'file>) ->
.collect()
}

/// Iterate through the symbols in an object file. See [`object::Object::symbols`].
/// Check an object file's symbols for any matching **substrings**. That is, if an object file
/// contains a symbol named `hello_world`, it will be matched against a provided `substrings` of
/// `["hello", "bar"]`.
///
/// 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 or if `path` cannot be
/// parsed as a recognized object file.
///
/// # Platform-specific behavior
///
/// On Windows MSVC, the binary (e.g. `main.exe`) does not contain the symbols, but in the separate
/// PDB file instead. Furthermore, you will need to use [`crate::llvm::llvm_pdbutil`] as `object`
/// crate does not handle PDB files.
#[track_caller]
pub fn with_symbol_iter<P, F, R>(path: P, func: F) -> R
pub fn object_contains_any_symbol_substring<P, S>(path: P, substrings: &[S]) -> bool
where
P: AsRef<Path>,
F: FnOnce(&mut SymbolIterator<'_, '_>) -> R,
S: AsRef<str>,
{
let path = path.as_ref();
let blob = crate::fs::read(path);
let f = object::File::parse(&*blob)
let obj = object::File::parse(&*blob)
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
let mut iter = f.symbols();
func(&mut iter)
let substrings = substrings.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
for sym in obj.symbols() {
for substring in &substrings {
if sym.name_bytes().unwrap().windows(substring.len()).any(|x| x == substring.as_bytes())
{
return true;
}
}
}
false
}

/// Check an object file's symbols for substrings.
/// Check an object file's symbols for any exact matches against those provided in
/// `candidate_symbols`.
///
/// 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 an **exact
/// match** against those listed in `candidate_symbols`. Take care to account for (1) platform
/// differences and (2) calling convention and symbol decorations differences.
///
/// 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.
///
/// # Platform-specific behavior
///
/// See [`object_contains_any_symbol_substring`].
#[track_caller]
pub fn any_symbol_contains(path: impl AsRef<Path>, substrings: &[&str]) -> bool {
with_symbol_iter(path, |syms| {
for sym in syms {
for substring in substrings {
if sym
.name_bytes()
.unwrap()
.windows(substring.len())
.any(|x| x == substring.as_bytes())
{
eprintln!("{:?} contains {}", sym, substring);
return true;
}
pub fn object_contains_any_symbol<P, S>(path: P, candidate_symbols: &[S]) -> bool
where
P: AsRef<Path>,
S: AsRef<str>,
{
let path = path.as_ref();
let blob = crate::fs::read(path);
let obj = object::File::parse(&*blob)
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
for sym in obj.symbols() {
for candidate_symbol in &candidate_symbols {
if sym.name_bytes().unwrap() == candidate_symbol.as_bytes() {
return true;
}
}
false
})
}
false
}

#[derive(Debug)]
pub enum ContainsAllSymbolSubstringsOutcome<'a> {
Ok,
MissingSymbolSubstrings(BTreeSet<&'a str>),
}

/// Check an object file's symbols for presence of all of provided **substrings**. That is, if an
/// object file contains symbols `["hello", "goodbye", "world"]`, it will be matched against a list
/// of `substrings` of `["he", "go"]`. In this case, `he` is a substring of `hello`, and `go` is a
/// substring of `goodbye`, so each of `substrings` was found.
///
/// Returns `true` if **all** `substrings` were present in the names of symbols for the given object
/// file (as substrings of symbol names).
///
/// 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.
///
/// # Platform-specific behavior
///
/// See [`object_contains_any_symbol_substring`].
#[track_caller]
pub fn object_contains_all_symbol_substring<'s, P, S>(
path: P,
substrings: &'s [S],
) -> ContainsAllSymbolSubstringsOutcome<'s>
where
P: AsRef<Path>,
S: AsRef<str>,
{
let path = path.as_ref();
let blob = crate::fs::read(path);
let obj = object::File::parse(&*blob)
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
let substrings = substrings.iter().map(|s| s.as_ref());
let mut unmatched_symbol_substrings = BTreeSet::from_iter(substrings);
unmatched_symbol_substrings.retain(|unmatched_symbol_substring| {
for sym in obj.symbols() {
if sym
.name_bytes()
.unwrap()
.windows(unmatched_symbol_substring.len())
.any(|x| x == unmatched_symbol_substring.as_bytes())
{
return false;
}
}

true
});

if unmatched_symbol_substrings.is_empty() {
ContainsAllSymbolSubstringsOutcome::Ok
} else {
ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(unmatched_symbol_substrings)
}
}

#[derive(Debug)]
pub enum ContainsAllSymbolsOutcome<'a> {
Ok,
MissingSymbols(BTreeSet<&'a str>),
}

/// Check an object file contains all symbols provided in `candidate_symbols`.
///
/// Returns `true` if **all** of the symbols in `candidate_symbols` are found within the object file
/// at `path` by **exact match**. Take care to account for (1) platform differences and (2) calling
/// convention and symbol decorations differences.
///
/// 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.
///
/// # Platform-specific behavior
///
/// See [`object_contains_any_symbol_substring`].
#[track_caller]
pub fn object_contains_all_symbols<P, S>(path: P, candidate_symbols: &[S]) -> bool
where
P: AsRef<Path>,
S: AsRef<str>,
{
let path = path.as_ref();
let blob = crate::fs::read(path);
let obj = object::File::parse(&*blob)
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref());
let mut unmatched_symbols = BTreeSet::from_iter(candidate_symbols);
unmatched_symbols.retain(|unmatched_symbol| {
for sym in obj.symbols() {
if sym.name_bytes().unwrap() == unmatched_symbol.bytes() {
return false;
}
}

true
});

if unmatched_symbols.is_empty() {
ContainsAllSymbolsOutcome::Ok
} else {
ContainsAllSymbolsOutcome::MissingSymbols(unmatched_symbols)
}
}
4 changes: 2 additions & 2 deletions tests/run-make/fmt-write-bloat/rmake.rs
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Obviously still broken)

Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
use run_make_support::artifact_names::bin_name;
use run_make_support::env::no_debug_assertions;
use run_make_support::rustc;
use run_make_support::symbols::any_symbol_contains;
use run_make_support::symbols::object_contains_any_symbol_substring;

fn main() {
rustc().input("main.rs").opt().run();
Expand All @@ -31,5 +31,5 @@ fn main() {
// otherwise, add them to the list of symbols to deny.
panic_syms.extend_from_slice(&["panicking", "panic_fmt", "pad_integral", "Display"]);
}
assert!(!any_symbol_contains(bin_name("main"), &panic_syms));
assert!(!object_contains_any_symbol_substring(bin_name("main"), &panic_syms));
}
4 changes: 2 additions & 2 deletions tests/run-make/used/rmake.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,9 @@
// https://rust-lang.github.io/rfcs/2386-used.html

use run_make_support::rustc;
use run_make_support::symbols::any_symbol_contains;
use run_make_support::symbols::object_contains_any_symbol_substring;

fn main() {
rustc().opt_level("3").emit("obj").input("used.rs").run();
assert!(any_symbol_contains("used.o", &["FOO"]));
assert!(object_contains_any_symbol_substring("used.o", &["FOO"]));
}
Loading