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