1
+ use std:: collections:: BTreeSet ;
1
2
use std:: path:: Path ;
2
3
3
- use object:: { self , Object , ObjectSymbol , SymbolIterator } ;
4
+ use object:: { self , Object , ObjectSymbol } ;
4
5
5
6
/// Given an [`object::File`], find the exported dynamic symbol names via
6
7
/// [`object::Object::exports`]. This does not distinguish between which section the symbols appear
@@ -14,47 +15,180 @@ pub fn exported_dynamic_symbol_names<'file>(file: &'file object::File<'file>) ->
14
15
. collect ( )
15
16
}
16
17
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`.
18
24
///
19
25
/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
20
26
/// 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.
21
33
#[ 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
23
35
where
24
36
P : AsRef < Path > ,
25
- F : FnOnce ( & mut SymbolIterator < ' _ , ' _ > ) -> R ,
37
+ S : AsRef < str > ,
26
38
{
27
39
let path = path. as_ref ( ) ;
28
40
let blob = crate :: fs:: read ( path) ;
29
- let f = object:: File :: parse ( & * blob)
41
+ let obj = object:: File :: parse ( & * blob)
30
42
. 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
33
53
}
34
54
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`.
36
57
///
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.
39
61
///
40
62
/// Panics if `path` is not a valid object file readable by the current user or if `path` cannot be
41
63
/// parsed as a recognized object file.
64
+ ///
65
+ /// # Platform-specific behavior
66
+ ///
67
+ /// See [`object_contains_any_symbol_substring`].
42
68
#[ 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 ;
56
83
}
57
84
}
58
- false
59
- } )
85
+ }
86
+ false
87
+ }
88
+
89
+ #[ derive( Debug , PartialEq ) ]
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 , PartialEq ) ]
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 > (
166
+ path : P ,
167
+ candidate_symbols : & [ S ] ,
168
+ ) -> ContainsAllSymbolsOutcome < ' _ >
169
+ where
170
+ P : AsRef < Path > ,
171
+ S : AsRef < str > ,
172
+ {
173
+ let path = path. as_ref ( ) ;
174
+ let blob = crate :: fs:: read ( path) ;
175
+ let obj = object:: File :: parse ( & * blob)
176
+ . unwrap_or_else ( |e| panic ! ( "failed to parse `{}`: {e}" , path. display( ) ) ) ;
177
+ let candidate_symbols = candidate_symbols. iter ( ) . map ( |s| s. as_ref ( ) ) ;
178
+ let mut unmatched_symbols = BTreeSet :: from_iter ( candidate_symbols) ;
179
+ unmatched_symbols. retain ( |unmatched_symbol| {
180
+ for sym in obj. symbols ( ) {
181
+ if sym. name_bytes ( ) . unwrap ( ) == unmatched_symbol. as_bytes ( ) {
182
+ return false ;
183
+ }
184
+ }
185
+
186
+ true
187
+ } ) ;
188
+
189
+ if unmatched_symbols. is_empty ( ) {
190
+ ContainsAllSymbolsOutcome :: Ok
191
+ } else {
192
+ ContainsAllSymbolsOutcome :: MissingSymbols ( unmatched_symbols)
193
+ }
60
194
}
0 commit comments