@@ -10,8 +10,8 @@ use std::process::{Command, Stdio};
1010use object:: elf:: SHF_EXECINSTR ;
1111use object:: read:: archive:: { ArchiveFile , ArchiveMember } ;
1212use object:: {
13- Object , ObjectSection , ObjectSymbol , SectionFlags , Symbol , SymbolKind , SymbolScope ,
14- SymbolSection ,
13+ File as ObjFile , Object , ObjectSection , ObjectSymbol , SectionFlags , Symbol , SymbolKind ,
14+ SymbolScope , SymbolSection ,
1515} ;
1616use serde_json:: Value ;
1717
@@ -32,16 +32,11 @@ fn main() {
3232 let args_ref = args. iter ( ) . map ( String :: as_str) . collect :: < Vec < _ > > ( ) ;
3333
3434 match & args_ref[ 1 ..] {
35- [ "build-and-check" , rest @ ..] if !rest. is_empty ( ) => {
36- let paths = exec_cargo_with_args ( rest) ;
37- for path in paths {
38- println ! ( "Checking {}" , path. display( ) ) ;
39- let archive = Archive :: from_path ( & path) ;
40-
41- verify_no_duplicates ( & archive) ;
42- verify_core_symbols ( & archive) ;
43- verify_no_exec_stack ( & archive) ;
44- }
35+ [ "build-and-check" , "--target" , target, args @ ..] if !args. is_empty ( ) => {
36+ run_build_and_check ( Some ( target) , args) ;
37+ }
38+ [ "build-and-check" , args @ ..] if !args. is_empty ( ) => {
39+ run_build_and_check ( None , args) ;
4540 }
4641 _ => {
4742 println ! ( "{USAGE}" ) ;
@@ -50,12 +45,43 @@ fn main() {
5045 }
5146}
5247
48+ fn run_build_and_check ( target : Option < & str > , args : & [ & str ] ) {
49+ let paths = exec_cargo_with_args ( target, args) ;
50+ for path in paths {
51+ println ! ( "Checking {}" , path. display( ) ) ;
52+ let archive = Archive :: from_path ( & path) ;
53+
54+ verify_no_duplicates ( & archive) ;
55+ verify_core_symbols ( & archive) ;
56+ verify_no_exec_stack ( & archive) ;
57+ }
58+ }
59+
60+ fn host_target ( ) -> String {
61+ let out = Command :: new ( "rustc" )
62+ . arg ( "--version" )
63+ . arg ( "--verbose" )
64+ . output ( )
65+ . unwrap ( ) ;
66+ assert ! ( out. status. success( ) ) ;
67+ let out = String :: from_utf8 ( out. stdout ) . unwrap ( ) ;
68+ out. lines ( )
69+ . find_map ( |s| s. strip_prefix ( "host: " ) )
70+ . unwrap ( )
71+ . to_owned ( )
72+ }
73+
5374/// Run `cargo build` with the provided additional arguments, collecting the list of created
5475/// libraries.
55- fn exec_cargo_with_args ( args : & [ & str ] ) -> Vec < PathBuf > {
76+ fn exec_cargo_with_args ( target : Option < & str > , args : & [ & str ] ) -> Vec < PathBuf > {
77+ let mut host = String :: new ( ) ;
78+ let target = target. unwrap_or_else ( || {
79+ host = host_target ( ) ;
80+ host. as_str ( )
81+ } ) ;
82+
5683 let mut cmd = Command :: new ( "cargo" ) ;
57- cmd. arg ( "build" )
58- . arg ( "--message-format=json" )
84+ cmd. args ( [ "build" , "--target" , & target, "--message-format=json" ] )
5985 . args ( args)
6086 . stdout ( Stdio :: piped ( ) ) ;
6187
@@ -232,32 +258,9 @@ fn verify_no_exec_stack(archive: &Archive) {
232258 let mut problem_objfiles = Vec :: new ( ) ;
233259
234260 archive. for_each_object ( |obj, member| {
235- // Files other than elf likely do not use the same convention.
236- if !matches ! ( obj, object:: File :: Elf32 ( _) | object:: File :: Elf64 ( _) ) {
237- return ;
238- }
239-
240- let mut has_exe_sections = false ;
241- for sec in obj. sections ( ) {
242- let SectionFlags :: Elf { sh_flags } = sec. flags ( ) else {
243- unreachable ! ( "only elf files are being checked" ) ;
244- } ;
245-
246- let exe = ( sh_flags & SHF_EXECINSTR as u64 ) != 0 ;
247- has_exe_sections |= exe;
248-
249- // Located a GNU-stack section, nothing else to do
250- if sec. name ( ) . unwrap_or_default ( ) == ".note.GNU-stack" {
251- return ;
252- }
253- }
254-
255- // Ignore object files that have no executable sections, like rmeta
256- if !has_exe_sections {
257- return ;
261+ if obj_has_exe_stack ( & obj) {
262+ problem_objfiles. push ( String :: from_utf8_lossy ( member. name ( ) ) . into_owned ( ) ) ;
258263 }
259-
260- problem_objfiles. push ( String :: from_utf8_lossy ( member. name ( ) ) . into_owned ( ) ) ;
261264 } ) ;
262265
263266 if !problem_objfiles. is_empty ( ) {
@@ -270,6 +273,35 @@ fn verify_no_exec_stack(archive: &Archive) {
270273 println ! ( " success: no writeable-executable sections found" ) ;
271274}
272275
276+ fn obj_has_exe_stack ( obj : & ObjFile ) -> bool {
277+ // Files other than elf likely do not use the same convention.
278+ if !matches ! ( obj, ObjFile :: Elf32 ( _) | ObjFile :: Elf64 ( _) ) {
279+ return false ;
280+ }
281+
282+ let mut has_exe_sections = false ;
283+ for sec in obj. sections ( ) {
284+ let SectionFlags :: Elf { sh_flags } = sec. flags ( ) else {
285+ unreachable ! ( "only elf files are being checked" ) ;
286+ } ;
287+
288+ let exe = ( sh_flags & SHF_EXECINSTR as u64 ) != 0 ;
289+ has_exe_sections |= exe;
290+
291+ // Located a GNU-stack section, nothing else to do
292+ if sec. name ( ) . unwrap_or_default ( ) == ".note.GNU-stack" {
293+ return false ;
294+ }
295+ }
296+
297+ // Ignore object files that have no executable sections, like rmeta
298+ if !has_exe_sections {
299+ return false ;
300+ }
301+
302+ true
303+ }
304+
273305/// Thin wrapper for owning data used by `object`.
274306struct Archive {
275307 data : Vec < u8 > ,
@@ -287,15 +319,15 @@ impl Archive {
287319 }
288320
289321 /// For a given archive, do something with each object file.
290- fn for_each_object ( & self , mut f : impl FnMut ( object :: File , & ArchiveMember ) ) {
322+ fn for_each_object ( & self , mut f : impl FnMut ( ObjFile , & ArchiveMember ) ) {
291323 let archive = self . file ( ) ;
292324
293325 for member in archive. members ( ) {
294326 let member = member. expect ( "failed to access member" ) ;
295327 let obj_data = member
296328 . data ( self . data . as_slice ( ) )
297329 . expect ( "failed to access object" ) ;
298- let obj = object :: File :: parse ( obj_data) . expect ( "failed to parse object" ) ;
330+ let obj = ObjFile :: parse ( obj_data) . expect ( "failed to parse object" ) ;
299331 f ( obj, & member) ;
300332 }
301333 }
@@ -307,3 +339,14 @@ impl Archive {
307339 } ) ;
308340 }
309341}
342+
343+ #[ test]
344+ fn check_obj ( ) {
345+ let Some ( p) = option_env ! ( "HAS_WX_OBJ" ) else {
346+ return ;
347+ } ;
348+
349+ let f = fs:: read ( p) . unwrap ( ) ;
350+ let obj = ObjFile :: parse ( f. as_slice ( ) ) . unwrap ( ) ;
351+ assert ! ( obj_has_exe_stack( & obj) ) ;
352+ }
0 commit comments