@@ -9,8 +9,8 @@ use std::process::{Command, Stdio};
9
9
10
10
use object:: read:: archive:: ArchiveFile ;
11
11
use object:: {
12
- File as ObjFile , Object , ObjectSection , ObjectSymbol , Result as ObjResult , Symbol , SymbolKind ,
13
- SymbolScope ,
12
+ BinaryFormat , File as ObjFile , Object , ObjectSection , ObjectSymbol , Result as ObjResult ,
13
+ SectionFlags , Symbol , SymbolKind , SymbolScope , elf ,
14
14
} ;
15
15
use serde_json:: Value ;
16
16
@@ -77,6 +77,7 @@ fn check_paths<P: AsRef<Path>>(paths: &[P]) {
77
77
78
78
verify_no_duplicates ( & archive) ;
79
79
verify_core_symbols ( & archive) ;
80
+ verify_no_exec_stack ( & archive) ;
80
81
}
81
82
}
82
83
@@ -299,6 +300,66 @@ fn verify_core_symbols(archive: &BinFile) {
299
300
println ! ( " success: no undefined references to core found" ) ;
300
301
}
301
302
303
+ /// Check that all object files contain a section named `.note.GNU-stack`, indicating a
304
+ /// nonexecutable stack.
305
+ ///
306
+ /// Paraphrased from <https://www.man7.org/linux/man-pages/man1/ld.1.html>:
307
+ ///
308
+ /// - A `.note.GNU-stack` section with the exe flag means this needs an executable stack
309
+ /// - A `.note.GNU-stack` section without the exe flag means there is no executable stack needed
310
+ /// - Without the section, behavior is target-specific and on some targets means an executable
311
+ /// stack is required.
312
+ ///
313
+ /// Now says
314
+ /// deprecated <https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=0d38576a34ec64a1b4500c9277a8e9d0f07e6774>.
315
+ fn verify_no_exec_stack ( archive : & BinFile ) {
316
+ let mut problem_objfiles = Vec :: new ( ) ;
317
+
318
+ archive. for_each_object ( |obj, obj_path| {
319
+ if obj_requires_exe_stack ( & obj) {
320
+ problem_objfiles. push ( obj_path. to_owned ( ) ) ;
321
+ }
322
+ } ) ;
323
+
324
+ if !problem_objfiles. is_empty ( ) {
325
+ panic ! ( "the following archive members require an executable stack: {problem_objfiles:#?}" ) ;
326
+ }
327
+
328
+ println ! ( " success: no writeable-executable sections found" ) ;
329
+ }
330
+
331
+ fn obj_requires_exe_stack ( obj : & ObjFile ) -> bool {
332
+ // Files other than elf likely do not use the same convention.
333
+ if obj. format ( ) != BinaryFormat :: Elf {
334
+ return false ;
335
+ }
336
+
337
+ let mut has_exe_sections = false ;
338
+ for sec in obj. sections ( ) {
339
+ let SectionFlags :: Elf { sh_flags } = sec. flags ( ) else {
340
+ unreachable ! ( "only elf files are being checked" ) ;
341
+ } ;
342
+
343
+ let exe = ( sh_flags & elf:: SHF_EXECINSTR as u64 ) != 0 ;
344
+
345
+ // If the magic section is present, its exe bit tells us whether or not the object
346
+ // file requires an executable stack.
347
+ if sec. name ( ) . unwrap_or_default ( ) == ".note.GNU-stack" {
348
+ return exe;
349
+ }
350
+
351
+ // Otherwise, just keep track of whether or not we have exeuctable sections
352
+ has_exe_sections |= exe;
353
+ }
354
+
355
+ // Ignore object files that have no executable sections, like rmeta
356
+ if !has_exe_sections {
357
+ return false ;
358
+ }
359
+
360
+ true
361
+ }
362
+
302
363
/// Thin wrapper for owning data used by `object`.
303
364
struct BinFile {
304
365
path : PathBuf ,
@@ -360,3 +421,43 @@ impl BinFile {
360
421
} ) ;
361
422
}
362
423
}
424
+
425
+ /// Check with a binary that has no `.note.GNU-stack` section, indicating platform-default stack
426
+ /// writeability.
427
+ #[ test]
428
+ fn check_no_gnu_stack_obj ( ) {
429
+ // Should be supported on all Unix platforms
430
+ let p = env ! ( "NO_GNU_STACK_OBJ" ) ;
431
+ let f = fs:: read ( p) . unwrap ( ) ;
432
+ let obj = ObjFile :: parse ( f. as_slice ( ) ) . unwrap ( ) ;
433
+ dbg ! (
434
+ obj. format( ) ,
435
+ obj. architecture( ) ,
436
+ obj. sub_architecture( ) ,
437
+ obj. is_64( )
438
+ ) ;
439
+ let has_exe_stack = obj_requires_exe_stack ( & obj) ;
440
+
441
+ let obj_target = env ! ( "OBJ_TARGET" ) ;
442
+ if obj_target. contains ( "-windows-" ) || obj_target. contains ( "-apple-" ) {
443
+ // Non-ELF targets don't have executable stacks marked in the same way
444
+ assert ! ( !has_exe_stack) ;
445
+ } else {
446
+ assert ! ( has_exe_stack) ;
447
+ }
448
+ }
449
+
450
+ #[ test]
451
+ #[ cfg_attr( not( target_env = "gnu" ) , ignore = "requires a gnu toolchain to build" ) ]
452
+ fn check_obj ( ) {
453
+ let p = option_env ! ( "HAS_EXE_STACK_OBJ" ) . expect ( "has_exe_stack.o not present" ) ;
454
+ let f = fs:: read ( p) . unwrap ( ) ;
455
+ let obj = ObjFile :: parse ( f. as_slice ( ) ) . unwrap ( ) ;
456
+ dbg ! (
457
+ obj. format( ) ,
458
+ obj. architecture( ) ,
459
+ obj. sub_architecture( ) ,
460
+ obj. is_64( )
461
+ ) ;
462
+ assert ! ( obj_requires_exe_stack( & obj) ) ;
463
+ }
0 commit comments