11//! Tool used by CI to inspect compiler-builtins archives and help ensure we won't run into any
22//! linking errors.
33
4+ #![ allow( unused) ] // TODO
5+
46use std:: collections:: { BTreeMap , BTreeSet } ;
57use std:: fs;
68use std:: io:: { BufRead , BufReader } ;
79use std:: path:: { Path , PathBuf } ;
810use std:: process:: { Command , Stdio } ;
911
1012use object:: read:: archive:: ArchiveFile ;
13+ use object:: read:: elf:: SectionHeader ;
1114use object:: {
12- BinaryFormat , File as ObjFile , Object , ObjectSection , ObjectSymbol , Result as ObjResult ,
13- SectionFlags , Symbol , SymbolKind , SymbolScope , elf,
15+ Architecture , BinaryFormat , Bytes , Endianness , File as ObjFile , LittleEndian , Object ,
16+ ObjectSection , ObjectSymbol , Result as ObjResult , SectionFlags , SectionKind , Symbol ,
17+ SymbolKind , SymbolScope , elf,
1418} ;
1519use serde_json:: Value ;
1620
@@ -45,6 +49,9 @@ fn main() {
4549 let target = & host_target ( ) ;
4650 run_build_and_check ( target, args) ;
4751 }
52+ [ "check" , "--ignore-exe-stack" , paths @ ..] if !paths. is_empty ( ) => {
53+ check_paths ( paths) ;
54+ }
4855 [ "check" , paths @ ..] if !paths. is_empty ( ) => {
4956 check_paths ( paths) ;
5057 }
@@ -75,8 +82,8 @@ fn check_paths<P: AsRef<Path>>(paths: &[P]) {
7582 println ! ( "Checking {}" , path. display( ) ) ;
7683 let archive = BinFile :: from_path ( path) ;
7784
78- verify_no_duplicates ( & archive) ;
79- verify_core_symbols ( & archive) ;
85+ // verify_no_duplicates(&archive);
86+ // verify_core_symbols(&archive);
8087 verify_no_exec_stack ( & archive) ;
8188 }
8289}
@@ -300,18 +307,7 @@ fn verify_core_symbols(archive: &BinFile) {
300307 println ! ( " success: no undefined references to core found" ) ;
301308}
302309
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>.
310+ /// Ensure that the object/archive will not require an executable stack.
315311fn verify_no_exec_stack ( archive : & BinFile ) {
316312 let mut problem_objfiles = Vec :: new ( ) ;
317313
@@ -322,42 +318,91 @@ fn verify_no_exec_stack(archive: &BinFile) {
322318 } ) ;
323319
324320 if !problem_objfiles. is_empty ( ) {
325- panic ! ( "the following archive members require an executable stack: {problem_objfiles:#?}" ) ;
321+ panic ! ( "the following object files require an executable stack: {problem_objfiles:#?}" ) ;
326322 }
327323
328- println ! ( " success: no writeable- executable sections found" ) ;
324+ println ! ( " success: no writeable+ executable sections found" ) ;
329325}
330326
327+ /// True if the section/flag combination indicates that the object file should be linked with an
328+ /// executable stack.
329+ ///
330+ /// Paraphrased from <https://www.man7.org/linux/man-pages/man1/ld.1.html>:
331+ ///
332+ /// - A `.note.GNU-stack` section with the exe flag means this needs an executable stack
333+ /// - A `.note.GNU-stack` section without the exe flag means there is no executable stack needed
334+ /// - Without the section, behavior is target-specific and on some targets means an executable
335+ /// stack is required.
336+ ///
337+ /// If any object files meet the requirements for an executable stack, any final binary that links
338+ /// it will have a program header with a `PT_GNU_STACK` section, which will be marked `RWE` rather
339+ /// than the desired `RW`. (We don't check final binaries).
340+ ///
341+ /// Per [1], it is now deprecated behavior for a missing `.note.GNU-stack` section to imply an
342+ /// executable stack. However, we shouldn't assume that tooling has caught up to this.
343+ ///
344+ /// [1]: https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=0d38576a34ec64a1b4500c9277a8e9d0f07e6774>
331345fn obj_requires_exe_stack ( obj : & ObjFile ) -> bool {
332- // Files other than elf likely do not use the same convention.
346+ // Files other than elf do not use the same convention.
333347 if obj. format ( ) != BinaryFormat :: Elf {
334348 return false ;
335349 }
336350
351+ let mut return_immediate = None ;
337352 let mut has_exe_sections = false ;
338353 for sec in obj. sections ( ) {
339354 let SectionFlags :: Elf { sh_flags } = sec. flags ( ) else {
340355 unreachable ! ( "only elf files are being checked" ) ;
341356 } ;
342357
343- let exe = ( sh_flags & elf:: SHF_EXECINSTR as u64 ) != 0 ;
358+ if sec. kind ( ) == SectionKind :: Elf ( elf:: SHT_ARM_ATTRIBUTES ) {
359+ let data = sec. data ( ) . unwrap ( ) ;
360+ parse_arm_thing ( data) ;
361+ }
362+
363+ let is_exe = ( sh_flags & elf:: SHF_EXECINSTR as u64 ) != 0 ;
344364
345365 // If the magic section is present, its exe bit tells us whether or not the object
346366 // file requires an executable stack.
347367 if sec. name ( ) . unwrap_or_default ( ) == ".note.GNU-stack" {
348- return exe ;
368+ return_immediate = Some ( is_exe ) ;
349369 }
350370
351371 // Otherwise, just keep track of whether or not we have exeuctable sections
352- has_exe_sections |= exe;
372+ has_exe_sections |= is_exe;
373+ }
374+
375+ if let Some ( imm) = return_immediate {
376+ return imm;
353377 }
354378
355379 // Ignore object files that have no executable sections, like rmeta
356380 if !has_exe_sections {
357381 return false ;
358382 }
359383
360- true
384+ platform_default_exe_stack_required ( obj. architecture ( ) )
385+ }
386+
387+ /// Default if there is no `.note.GNU-stack` section.
388+ fn platform_default_exe_stack_required ( arch : Architecture ) -> bool {
389+ match arch {
390+ // PPC64 doesn't set `.note.GNU-stack` since GNU nested functions don't need a trampoline,
391+ // <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=21098>.
392+ Architecture :: PowerPc64 => false ,
393+ _ => true ,
394+ }
395+ }
396+
397+ fn parse_arm_thing ( data : & [ u8 ] ) {
398+ eprintln ! ( "data: {data:x?}" ) ;
399+ eprintln ! ( "data string: {:?}" , String :: from_utf8_lossy( data) ) ;
400+ eprintln ! ( "data: {:x?}" , & data[ 16 ..] ) ;
401+ // let mut rest = &data[16..];
402+ let mut b = Bytes ( data) ;
403+ b. skip ( 16 ) . unwrap ( ) ;
404+
405+ // while !rest.is_empty() {}
361406}
362407
363408/// Thin wrapper for owning data used by `object`.
@@ -448,8 +493,12 @@ fn check_no_gnu_stack_obj() {
448493}
449494
450495#[ test]
451- #[ cfg_attr( not( target_env = "gnu" ) , ignore = "requires a gnu toolchain to build" ) ]
496+ #[ cfg_attr(
497+ any( target_os = "windows" , target_vendor = "apple" ) ,
498+ ignore = "requires elf format"
499+ ) ]
452500fn check_obj ( ) {
501+ #[ expect( clippy:: option_env_unwrap, reason = "test is ignored" ) ]
453502 let p = option_env ! ( "HAS_EXE_STACK_OBJ" ) . expect ( "has_exe_stack.o not present" ) ;
454503 let f = fs:: read ( p) . unwrap ( ) ;
455504 let obj = ObjFile :: parse ( f. as_slice ( ) ) . unwrap ( ) ;
0 commit comments