@@ -7,9 +7,10 @@ use std::io::{BufRead, BufReader};
77use std:: path:: { Path , PathBuf } ;
88use std:: process:: { Command , Stdio } ;
99
10- use object:: read:: archive:: { ArchiveFile , ArchiveMember } ;
10+ use object:: read:: archive:: ArchiveFile ;
1111use object:: {
12- File as ObjFile , Object , ObjectSection , ObjectSymbol , Symbol , SymbolKind , SymbolScope ,
12+ File as ObjFile , Object , ObjectSection , ObjectSymbol , Result as ObjResult , Symbol , SymbolKind ,
13+ SymbolScope ,
1314} ;
1415use serde_json:: Value ;
1516
@@ -24,6 +25,11 @@ Cargo will get invoked with `CARGO_ARGS` and the specified target. All output
2425`compiler_builtins*.rlib` files will be checked.
2526
2627If TARGET is not specified, the host target is used.
28+
29+ check PATHS ...
30+
31+ Run the same checks on the given set of paths, without invoking Cargo. Paths
32+ may be either archives or object files.
2733" ;
2834
2935fn main ( ) {
@@ -33,12 +39,14 @@ fn main() {
3339
3440 match & args_ref[ 1 ..] {
3541 [ "build-and-check" , target, "--" , args @ ..] if !args. is_empty ( ) => {
36- check_cargo_args ( args) ;
3742 run_build_and_check ( target, args) ;
3843 }
3944 [ "build-and-check" , "--" , args @ ..] if !args. is_empty ( ) => {
40- check_cargo_args ( args) ;
41- run_build_and_check ( & host_target ( ) , args) ;
45+ let target = & host_target ( ) ;
46+ run_build_and_check ( target, args) ;
47+ }
48+ [ "check" , paths @ ..] if !paths. is_empty ( ) => {
49+ check_paths ( paths) ;
4250 }
4351 _ => {
4452 println ! ( "{USAGE}" ) ;
@@ -47,22 +55,25 @@ fn main() {
4755 }
4856}
4957
50- /// Make sure `-- target` isn't passed to avoid confusion (since it should be proivded only once,
51- /// positionally).
52- fn check_cargo_args ( args : & [ & str ] ) {
58+ fn run_build_and_check ( target : & str , args : & [ & str ] ) {
59+ // Make sure `--target` isn't passed to avoid confusion (since it should be
60+ // proivded only once, positionally).
5361 for arg in args {
5462 assert ! (
5563 !arg. contains( "--target" ) ,
5664 "target must be passed positionally. {USAGE}"
5765 ) ;
5866 }
59- }
6067
61- fn run_build_and_check ( target : & str , args : & [ & str ] ) {
6268 let paths = exec_cargo_with_args ( target, args) ;
69+ check_paths ( & paths) ;
70+ }
71+
72+ fn check_paths < P : AsRef < Path > > ( paths : & [ P ] ) {
6373 for path in paths {
74+ let path = path. as_ref ( ) ;
6475 println ! ( "Checking {}" , path. display( ) ) ;
65- let archive = Archive :: from_path ( & path) ;
76+ let archive = BinFile :: from_path ( path) ;
6677
6778 verify_no_duplicates ( & archive) ;
6879 verify_core_symbols ( & archive) ;
@@ -165,7 +176,7 @@ struct SymInfo {
165176}
166177
167178impl SymInfo {
168- fn new ( sym : & Symbol , obj : & ObjFile , member : & ArchiveMember ) -> Self {
179+ fn new ( sym : & Symbol , obj : & ObjFile , obj_path : & str ) -> Self {
169180 // Include the section name if possible. Fall back to the `Section` debug impl if not.
170181 let section = sym. section ( ) ;
171182 let section_name = sym
@@ -187,7 +198,7 @@ impl SymInfo {
187198 is_weak : sym. is_weak ( ) ,
188199 is_common : sym. is_common ( ) ,
189200 address : sym. address ( ) ,
190- object : String :: from_utf8_lossy ( member . name ( ) ) . into_owned ( ) ,
201+ object : obj_path . to_owned ( ) ,
191202 }
192203 }
193204}
@@ -197,7 +208,7 @@ impl SymInfo {
197208/// Note that this will also locate cases where a symbol is weakly defined in more than one place.
198209/// Technically there are no linker errors that will come from this, but it keeps our binary more
199210/// straightforward and saves some distribution size.
200- fn verify_no_duplicates ( archive : & Archive ) {
211+ fn verify_no_duplicates ( archive : & BinFile ) {
201212 let mut syms = BTreeMap :: < String , SymInfo > :: new ( ) ;
202213 let mut dups = Vec :: new ( ) ;
203214 let mut found_any = false ;
@@ -254,7 +265,7 @@ fn verify_no_duplicates(archive: &Archive) {
254265}
255266
256267/// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined.
257- fn verify_core_symbols ( archive : & Archive ) {
268+ fn verify_core_symbols ( archive : & BinFile ) {
258269 let mut defined = BTreeSet :: new ( ) ;
259270 let mut undefined = Vec :: new ( ) ;
260271 let mut has_symbols = false ;
@@ -289,39 +300,63 @@ fn verify_core_symbols(archive: &Archive) {
289300}
290301
291302/// Thin wrapper for owning data used by `object`.
292- struct Archive {
303+ struct BinFile {
304+ path : PathBuf ,
293305 data : Vec < u8 > ,
294306}
295307
296- impl Archive {
308+ impl BinFile {
297309 fn from_path ( path : & Path ) -> Self {
298310 Self {
311+ path : path. to_owned ( ) ,
299312 data : fs:: read ( path) . expect ( "reading file failed" ) ,
300313 }
301314 }
302315
303- fn file ( & self ) -> ArchiveFile < ' _ > {
304- ArchiveFile :: parse ( self . data . as_slice ( ) ) . expect ( "archive parse failed" )
316+ fn as_archive_file ( & self ) -> ObjResult < ArchiveFile < ' _ > > {
317+ ArchiveFile :: parse ( self . data . as_slice ( ) )
305318 }
306319
307- /// For a given archive, do something with each object file.
308- fn for_each_object ( & self , mut f : impl FnMut ( ObjFile , & ArchiveMember ) ) {
309- let archive = self . file ( ) ;
310-
311- for member in archive. members ( ) {
312- let member = member. expect ( "failed to access member" ) ;
313- let obj_data = member
314- . data ( self . data . as_slice ( ) )
315- . expect ( "failed to access object" ) ;
316- let obj = ObjFile :: parse ( obj_data) . expect ( "failed to parse object" ) ;
317- f ( obj, & member) ;
320+ fn as_obj_file ( & self ) -> ObjResult < ObjFile < ' _ > > {
321+ ObjFile :: parse ( self . data . as_slice ( ) )
322+ }
323+
324+ /// For a given archive, do something with each object file. For an object file, do
325+ /// something once.
326+ fn for_each_object ( & self , mut f : impl FnMut ( ObjFile , & str ) ) {
327+ // Try as an archive first.
328+ let as_archive = self . as_archive_file ( ) ;
329+ if let Ok ( archive) = as_archive {
330+ for member in archive. members ( ) {
331+ let member = member. expect ( "failed to access member" ) ;
332+ let obj_data = member
333+ . data ( self . data . as_slice ( ) )
334+ . expect ( "failed to access object" ) ;
335+ let obj = ObjFile :: parse ( obj_data) . expect ( "failed to parse object" ) ;
336+ f ( obj, & String :: from_utf8_lossy ( member. name ( ) ) ) ;
337+ }
338+
339+ return ;
318340 }
341+
342+ // Fall back to parsing as an object file.
343+ let as_obj = self . as_obj_file ( ) ;
344+ if let Ok ( obj) = as_obj {
345+ f ( obj, & self . path . to_string_lossy ( ) ) ;
346+ return ;
347+ }
348+
349+ panic ! (
350+ "failed to parse as either archive or object file: {:?}, {:?}" ,
351+ as_archive. unwrap_err( ) ,
352+ as_obj. unwrap_err( ) ,
353+ ) ;
319354 }
320355
321- /// For a given archive, do something with each symbol.
322- fn for_each_symbol ( & self , mut f : impl FnMut ( Symbol , & ObjFile , & ArchiveMember ) ) {
323- self . for_each_object ( |obj, member | {
324- obj. symbols ( ) . for_each ( |sym| f ( sym, & obj, member ) ) ;
356+ /// D something with each symbol in an archive or object file .
357+ fn for_each_symbol ( & self , mut f : impl FnMut ( Symbol , & ObjFile , & str ) ) {
358+ self . for_each_object ( |obj, obj_path | {
359+ obj. symbols ( ) . for_each ( |sym| f ( sym, & obj, obj_path ) ) ;
325360 } ) ;
326361 }
327362}
0 commit comments