Skip to content

Commit b9137f9

Browse files
committed
Adjust run-make-support::symbols helpers
Massage the `symbols` helpers to fill out {match all, match any} x {substring match, exact match}: | | Substring match | Exact match | |-----------|----------------------------------------|-------------------------------| | Match any | `object_contains_any_symbol_substring` | `object_contains_any_symbol` | | Match all | `object_contains_all_symbol_substring` | `object_contains_all_symbols` | As part of this, rename `any_symbol_contains` to `object_contains_any_symbol_substring` for accuracy.
1 parent 9535fee commit b9137f9

File tree

1 file changed

+155
-24
lines changed

1 file changed

+155
-24
lines changed
Lines changed: 155 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use std::collections::BTreeSet;
12
use std::path::Path;
23

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

17-
/// Iterate through the symbols in an object file. See [`object::Object::symbols`].
18+
/// Check an object file's symbols for any matching **substrings**. That is, if an object file
19+
/// contains a symbol named `hello_world`, it will be matched against a provided `substrings` of
20+
/// `["hello", "bar"]`.
21+
///
22+
/// Returns `true` if **any** of the symbols found in the object file at `path` contain a
23+
/// **substring** listed in `substrings`.
1824
///
1925
/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
2026
/// parsed as a recognized object file.
27+
///
28+
/// # Platform-specific behavior
29+
///
30+
/// On Windows MSVC, the binary (e.g. `main.exe`) does not contain the symbols, but in the separate
31+
/// PDB file instead. Furthermore, you will need to use [`crate::llvm::llvm_pdbutil`] as `object`
32+
/// crate does not handle PDB files.
2133
#[track_caller]
22-
pub fn with_symbol_iter<P, F, R>(path: P, func: F) -> R
34+
pub fn object_contains_any_symbol_substring<P, S>(path: P, substrings: &[S]) -> bool
2335
where
2436
P: AsRef<Path>,
25-
F: FnOnce(&mut SymbolIterator<'_, '_>) -> R,
37+
S: AsRef<str>,
2638
{
2739
let path = path.as_ref();
2840
let blob = crate::fs::read(path);
29-
let f = object::File::parse(&*blob)
41+
let obj = object::File::parse(&*blob)
3042
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
31-
let mut iter = f.symbols();
32-
func(&mut iter)
43+
let substrings = substrings.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
44+
for sym in obj.symbols() {
45+
for substring in &substrings {
46+
if sym.name_bytes().unwrap().windows(substring.len()).any(|x| x == substring.as_bytes())
47+
{
48+
return true;
49+
}
50+
}
51+
}
52+
false
3353
}
3454

35-
/// Check an object file's symbols for substrings.
55+
/// Check an object file's symbols for any exact matches against those provided in
56+
/// `candidate_symbols`.
3657
///
37-
/// Returns `true` if any of the symbols found in the object file at `path` contain a substring
38-
/// listed in `substrings`.
58+
/// Returns `true` if **any** of the symbols found in the object file at `path` contain an **exact
59+
/// match** against those listed in `candidate_symbols`. Take care to account for (1) platform
60+
/// differences and (2) calling convention and symbol decorations differences.
3961
///
4062
/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
4163
/// parsed as a recognized object file.
64+
///
65+
/// # Platform-specific behavior
66+
///
67+
/// See [`object_contains_any_symbol_substring`].
4268
#[track_caller]
43-
pub fn any_symbol_contains(path: impl AsRef<Path>, substrings: &[&str]) -> bool {
44-
with_symbol_iter(path, |syms| {
45-
for sym in syms {
46-
for substring in substrings {
47-
if sym
48-
.name_bytes()
49-
.unwrap()
50-
.windows(substring.len())
51-
.any(|x| x == substring.as_bytes())
52-
{
53-
eprintln!("{:?} contains {}", sym, substring);
54-
return true;
55-
}
69+
pub fn object_contains_any_symbol<P, S>(path: P, candidate_symbols: &[S]) -> bool
70+
where
71+
P: AsRef<Path>,
72+
S: AsRef<str>,
73+
{
74+
let path = path.as_ref();
75+
let blob = crate::fs::read(path);
76+
let obj = object::File::parse(&*blob)
77+
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
78+
let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref()).collect::<Vec<_>>();
79+
for sym in obj.symbols() {
80+
for candidate_symbol in &candidate_symbols {
81+
if sym.name_bytes().unwrap() == candidate_symbol.as_bytes() {
82+
return true;
5683
}
5784
}
58-
false
59-
})
85+
}
86+
false
87+
}
88+
89+
#[derive(Debug)]
90+
pub enum ContainsAllSymbolSubstringsOutcome<'a> {
91+
Ok,
92+
MissingSymbolSubstrings(BTreeSet<&'a str>),
93+
}
94+
95+
/// Check an object file's symbols for presence of all of provided **substrings**. That is, if an
96+
/// object file contains symbols `["hello", "goodbye", "world"]`, it will be matched against a list
97+
/// of `substrings` of `["he", "go"]`. In this case, `he` is a substring of `hello`, and `go` is a
98+
/// substring of `goodbye`, so each of `substrings` was found.
99+
///
100+
/// Returns `true` if **all** `substrings` were present in the names of symbols for the given object
101+
/// file (as substrings of symbol names).
102+
///
103+
/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
104+
/// parsed as a recognized object file.
105+
///
106+
/// # Platform-specific behavior
107+
///
108+
/// See [`object_contains_any_symbol_substring`].
109+
#[track_caller]
110+
pub fn object_contains_all_symbol_substring<'s, P, S>(
111+
path: P,
112+
substrings: &'s [S],
113+
) -> ContainsAllSymbolSubstringsOutcome<'s>
114+
where
115+
P: AsRef<Path>,
116+
S: AsRef<str>,
117+
{
118+
let path = path.as_ref();
119+
let blob = crate::fs::read(path);
120+
let obj = object::File::parse(&*blob)
121+
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
122+
let substrings = substrings.iter().map(|s| s.as_ref());
123+
let mut unmatched_symbol_substrings = BTreeSet::from_iter(substrings);
124+
unmatched_symbol_substrings.retain(|unmatched_symbol_substring| {
125+
for sym in obj.symbols() {
126+
if sym
127+
.name_bytes()
128+
.unwrap()
129+
.windows(unmatched_symbol_substring.len())
130+
.any(|x| x == unmatched_symbol_substring.as_bytes())
131+
{
132+
return false;
133+
}
134+
}
135+
136+
true
137+
});
138+
139+
if unmatched_symbol_substrings.is_empty() {
140+
ContainsAllSymbolSubstringsOutcome::Ok
141+
} else {
142+
ContainsAllSymbolSubstringsOutcome::MissingSymbolSubstrings(unmatched_symbol_substrings)
143+
}
144+
}
145+
146+
#[derive(Debug)]
147+
pub enum ContainsAllSymbolsOutcome<'a> {
148+
Ok,
149+
MissingSymbols(BTreeSet<&'a str>),
150+
}
151+
152+
/// Check an object file contains all symbols provided in `candidate_symbols`.
153+
///
154+
/// Returns `true` if **all** of the symbols in `candidate_symbols` are found within the object file
155+
/// at `path` by **exact match**. Take care to account for (1) platform differences and (2) calling
156+
/// convention and symbol decorations differences.
157+
///
158+
/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
159+
/// parsed as a recognized object file.
160+
///
161+
/// # Platform-specific behavior
162+
///
163+
/// See [`object_contains_any_symbol_substring`].
164+
#[track_caller]
165+
pub fn object_contains_all_symbols<P, S>(path: P, candidate_symbols: &[S]) -> bool
166+
where
167+
P: AsRef<Path>,
168+
S: AsRef<str>,
169+
{
170+
let path = path.as_ref();
171+
let blob = crate::fs::read(path);
172+
let obj = object::File::parse(&*blob)
173+
.unwrap_or_else(|e| panic!("failed to parse `{}`: {e}", path.display()));
174+
let candidate_symbols = candidate_symbols.iter().map(|s| s.as_ref());
175+
let mut unmatched_symbols = BTreeSet::from_iter(candidate_symbols);
176+
unmatched_symbols.retain(|unmatched_symbol| {
177+
for sym in obj.symbols() {
178+
if sym.name_bytes().unwrap() == unmatched_symbol.bytes() {
179+
return false;
180+
}
181+
}
182+
183+
true
184+
});
185+
186+
if unmatched_symbols.is_empty() {
187+
ContainsAllSymbolsOutcome::Ok
188+
} else {
189+
ContainsAllSymbolsOutcome::MissingSymbols(unmatched_symbols)
190+
}
60191
}

0 commit comments

Comments
 (0)