1+ use std:: collections:: BTreeSet ;
12use std:: path:: Path ;
23
34use 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
2335where
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