@@ -58,6 +58,7 @@ fn main() {
5858 "The binaries will not be checked for executable stacks. Used for embedded targets which \
5959 don't set `.note.GNU-stack` since there is no protection.",
6060 ) ;
61+ opts. optflag ( "" , "no-visibility" , "Don't check visibility." ) ;
6162
6263 let print_usage_and_exit = |code : i32 | -> ! {
6364 eprintln ! ( "{}" , opts. usage( USAGE ) ) ;
@@ -74,6 +75,7 @@ fn main() {
7475 }
7576
7677 let no_os_target = m. opt_present ( "no-os" ) ;
78+ let check_visibility = !m. opt_present ( "no-visibility" ) ;
7779 let free_args = m. free . iter ( ) . map ( String :: as_str) . collect :: < Vec < _ > > ( ) ;
7880 for arg in & free_args {
7981 assert ! (
@@ -85,18 +87,18 @@ fn main() {
8587 if m. opt_present ( "build-and-check" ) {
8688 let target = m. opt_str ( "target" ) . unwrap_or ( env ! ( "HOST" ) . to_string ( ) ) ;
8789 let paths = exec_cargo_with_args ( & target, & free_args) ;
88- check_paths ( & paths, no_os_target) ;
90+ check_paths ( & paths, no_os_target, check_visibility ) ;
8991 } else if m. opt_present ( "check" ) {
9092 if free_args. is_empty ( ) {
9193 print_usage_and_exit ( 1 ) ;
9294 }
93- check_paths ( & free_args, no_os_target) ;
95+ check_paths ( & free_args, no_os_target, check_visibility ) ;
9496 } else {
9597 print_usage_and_exit ( 1 ) ;
9698 }
9799}
98100
99- fn check_paths < P : AsRef < Path > > ( paths : & [ P ] , no_os_target : bool ) {
101+ fn check_paths < P : AsRef < Path > > ( paths : & [ P ] , no_os_target : bool , check_visibility : bool ) {
100102 for path in paths {
101103 let path = path. as_ref ( ) ;
102104 println ! ( "Checking {}" , path. display( ) ) ;
@@ -105,6 +107,9 @@ fn check_paths<P: AsRef<Path>>(paths: &[P], no_os_target: bool) {
105107 verify_no_duplicates ( & archive) ;
106108 verify_core_symbols ( & archive) ;
107109 verify_no_exec_stack ( & archive, no_os_target) ;
110+ if check_visibility {
111+ verify_hidden_visibility ( & archive) ;
112+ }
108113 }
109114}
110115
@@ -329,6 +334,38 @@ fn verify_core_symbols(archive: &BinFile) {
329334 println ! ( " success: no undefined references to core found" ) ;
330335}
331336
337+ /// Check for symbols with default visibility.
338+ fn verify_hidden_visibility ( archive : & BinFile ) {
339+ let mut visible = Vec :: new ( ) ;
340+ let mut found_any = false ;
341+
342+ archive. for_each_symbol ( |symbol, obj, member| {
343+ // Only check defined globals.
344+ if !symbol. is_global ( ) || symbol. is_undefined ( ) {
345+ return ;
346+ }
347+
348+ let sym = SymInfo :: new ( & symbol, obj, member) ;
349+ if sym. scope == SymbolScope :: Dynamic {
350+ visible. push ( sym) ;
351+ }
352+
353+ found_any = true
354+ } ) ;
355+
356+ if archive. has_symbol_tables ( ) {
357+ assert ! ( found_any, "no symbols found" ) ;
358+ }
359+
360+ if !visible. is_empty ( ) {
361+ visible. sort_unstable_by ( |a, b| a. name . cmp ( & b. name ) ) ;
362+ let num = visible. len ( ) ;
363+ panic ! ( "found {num:#?} visible symbols: {visible:#?}" ) ;
364+ }
365+
366+ println ! ( " success: no visible symbols found" ) ;
367+ }
368+
332369/// Reasons a binary is considered to have an executable stack.
333370enum ExeStack {
334371 MissingGnuStackSec ,
0 commit comments