@@ -9,8 +9,8 @@ use std::process::{Command, Stdio};
99
1010use object:: read:: archive:: ArchiveFile ;
1111use 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 ,
1414} ;
1515use serde_json:: Value ;
1616
@@ -77,6 +77,7 @@ fn check_paths<P: AsRef<Path>>(paths: &[P]) {
7777
7878 verify_no_duplicates ( & archive) ;
7979 verify_core_symbols ( & archive) ;
80+ verify_no_exec_stack ( & archive) ;
8081 }
8182}
8283
@@ -299,6 +300,66 @@ fn verify_core_symbols(archive: &BinFile) {
299300 println ! ( " success: no undefined references to core found" ) ;
300301}
301302
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+
302363/// Thin wrapper for owning data used by `object`.
303364struct BinFile {
304365 path : PathBuf ,
@@ -360,3 +421,43 @@ impl BinFile {
360421 } ) ;
361422 }
362423}
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