1+ use  std:: collections:: BTreeSet ; 
12use  std:: path:: Path ; 
23
3- use  object:: { self ,  Object ,  ObjectSymbol ,   SymbolIterator } ; 
4+ use  object:: { self ,  Object ,  ObjectSymbol } ; 
45
56/// Given an [`object::File`], find the exported dynamic symbol names via 
67/// [`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>) ->
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 ,  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+     } 
60194} 
0 commit comments