@@ -7,8 +7,12 @@ use std::io::{BufRead, BufReader};
77use std:: path:: { Path , PathBuf } ;
88use std:: process:: { Command , Stdio } ;
99
10+ use object:: elf:: SHF_EXECINSTR ;
1011use object:: read:: archive:: { ArchiveFile , ArchiveMember } ;
11- use object:: { Object , ObjectSymbol , Symbol , SymbolKind , SymbolScope , SymbolSection } ;
12+ use object:: {
13+ Object , ObjectSection , ObjectSymbol , SectionFlags , Symbol , SymbolKind , SymbolScope ,
14+ SymbolSection ,
15+ } ;
1216use serde_json:: Value ;
1317
1418const CHECK_LIBRARIES : & [ & str ] = & [ "compiler_builtins" , "builtins_test_intrinsics" ] ;
@@ -32,8 +36,11 @@ fn main() {
3236 let paths = exec_cargo_with_args ( rest) ;
3337 for path in paths {
3438 println ! ( "Checking {}" , path. display( ) ) ;
35- verify_no_duplicates ( & path) ;
36- verify_core_symbols ( & path) ;
39+ let archive = Archive :: from_path ( & path) ;
40+
41+ verify_no_duplicates ( & archive) ;
42+ verify_core_symbols ( & archive) ;
43+ verify_no_exec_stack ( & archive) ;
3744 }
3845 }
3946 _ => {
@@ -133,12 +140,12 @@ impl SymInfo {
133140/// Note that this will also locate cases where a symbol is weakly defined in more than one place.
134141/// Technically there are no linker errors that will come from this, but it keeps our binary more
135142/// straightforward and saves some distribution size.
136- fn verify_no_duplicates ( path : & Path ) {
143+ fn verify_no_duplicates ( archive : & Archive ) {
137144 let mut syms = BTreeMap :: < String , SymInfo > :: new ( ) ;
138145 let mut dups = Vec :: new ( ) ;
139146 let mut found_any = false ;
140147
141- for_each_symbol ( path , |symbol, member| {
148+ archive . for_each_symbol ( |symbol, member| {
142149 // Only check defined globals
143150 if !symbol. is_global ( ) || symbol. is_undefined ( ) {
144151 return ;
@@ -185,12 +192,12 @@ fn verify_no_duplicates(path: &Path) {
185192}
186193
187194/// Ensure that there are no references to symbols from `core` that aren't also (somehow) defined.
188- fn verify_core_symbols ( path : & Path ) {
195+ fn verify_core_symbols ( archive : & Archive ) {
189196 let mut defined = BTreeSet :: new ( ) ;
190197 let mut undefined = Vec :: new ( ) ;
191198 let mut has_symbols = false ;
192199
193- for_each_symbol ( path , |symbol, member| {
200+ archive . for_each_symbol ( |symbol, member| {
194201 has_symbols = true ;
195202
196203 // Find only symbols from `core`
@@ -219,14 +226,84 @@ fn verify_core_symbols(path: &Path) {
219226 println ! ( " success: no undefined references to core found" ) ;
220227}
221228
222- /// For a given archive path, do something with each symbol.
223- fn for_each_symbol ( path : & Path , mut f : impl FnMut ( Symbol , & ArchiveMember ) ) {
224- let data = fs:: read ( path) . expect ( "reading file failed" ) ;
225- let archive = ArchiveFile :: parse ( data. as_slice ( ) ) . expect ( "archive parse failed" ) ;
226- for member in archive. members ( ) {
227- let member = member. expect ( "failed to access member" ) ;
228- let obj_data = member. data ( & * data) . expect ( "failed to access object" ) ;
229- let obj = object:: File :: parse ( obj_data) . expect ( "failed to parse object" ) ;
230- obj. symbols ( ) . for_each ( |sym| f ( sym, & member) ) ;
229+ /// Check that all object files contain a section named `.note.GNU-stack`, indicating a
230+ /// nonexecutable stack.
231+ fn verify_no_exec_stack ( archive : & Archive ) {
232+ let mut problem_objfiles = Vec :: new ( ) ;
233+
234+ 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 ;
258+ }
259+
260+ problem_objfiles. push ( String :: from_utf8_lossy ( member. name ( ) ) . into_owned ( ) ) ;
261+ } ) ;
262+
263+ if !problem_objfiles. is_empty ( ) {
264+ panic ! (
265+ "the following archive members have executable sections but no \
266+ `.note.GNU-stack` section: {problem_objfiles:#?}"
267+ ) ;
268+ }
269+
270+ println ! ( " success: no writeable-executable sections found" ) ;
271+ }
272+
273+ /// Thin wrapper for owning data used by `object`.
274+ struct Archive {
275+ data : Vec < u8 > ,
276+ }
277+
278+ impl Archive {
279+ fn from_path ( path : & Path ) -> Self {
280+ Self {
281+ data : fs:: read ( path) . expect ( "reading file failed" ) ,
282+ }
283+ }
284+
285+ fn file ( & self ) -> ArchiveFile {
286+ ArchiveFile :: parse ( self . data . as_slice ( ) ) . expect ( "archive parse failed" )
287+ }
288+
289+ /// For a given archive, do something with each object file.
290+ fn for_each_object ( & self , mut f : impl FnMut ( object:: File , & ArchiveMember ) ) {
291+ let archive = self . file ( ) ;
292+
293+ for member in archive. members ( ) {
294+ let member = member. expect ( "failed to access member" ) ;
295+ let obj_data = member
296+ . data ( self . data . as_slice ( ) )
297+ . expect ( "failed to access object" ) ;
298+ let obj = object:: File :: parse ( obj_data) . expect ( "failed to parse object" ) ;
299+ f ( obj, & member) ;
300+ }
301+ }
302+
303+ /// For a given archive, do something with each symbol.
304+ fn for_each_symbol ( & self , mut f : impl FnMut ( Symbol , & ArchiveMember ) ) {
305+ self . for_each_object ( |obj, member| {
306+ obj. symbols ( ) . for_each ( |sym| f ( sym, member) ) ;
307+ } ) ;
231308 }
232309}
0 commit comments