@@ -7,8 +7,12 @@ use std::io::{BufRead, BufReader};
7
7
use std:: path:: { Path , PathBuf } ;
8
8
use std:: process:: { Command , Stdio } ;
9
9
10
+ use object:: elf:: SHF_EXECINSTR ;
10
11
use 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
+ } ;
12
16
use serde_json:: Value ;
13
17
14
18
const CHECK_LIBRARIES : & [ & str ] = & [ "compiler_builtins" , "builtins_test_intrinsics" ] ;
@@ -32,8 +36,11 @@ fn main() {
32
36
let paths = exec_cargo_with_args ( rest) ;
33
37
for path in paths {
34
38
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) ;
37
44
}
38
45
}
39
46
_ => {
@@ -133,12 +140,12 @@ impl SymInfo {
133
140
/// Note that this will also locate cases where a symbol is weakly defined in more than one place.
134
141
/// Technically there are no linker errors that will come from this, but it keeps our binary more
135
142
/// straightforward and saves some distribution size.
136
- fn verify_no_duplicates ( path : & Path ) {
143
+ fn verify_no_duplicates ( archive : & Archive ) {
137
144
let mut syms = BTreeMap :: < String , SymInfo > :: new ( ) ;
138
145
let mut dups = Vec :: new ( ) ;
139
146
let mut found_any = false ;
140
147
141
- for_each_symbol ( path , |symbol, member| {
148
+ archive . for_each_symbol ( |symbol, member| {
142
149
// Only check defined globals
143
150
if !symbol. is_global ( ) || symbol. is_undefined ( ) {
144
151
return ;
@@ -185,12 +192,12 @@ fn verify_no_duplicates(path: &Path) {
185
192
}
186
193
187
194
/// 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 ) {
189
196
let mut defined = BTreeSet :: new ( ) ;
190
197
let mut undefined = Vec :: new ( ) ;
191
198
let mut has_symbols = false ;
192
199
193
- for_each_symbol ( path , |symbol, member| {
200
+ archive . for_each_symbol ( |symbol, member| {
194
201
has_symbols = true ;
195
202
196
203
// Find only symbols from `core`
@@ -219,14 +226,84 @@ fn verify_core_symbols(path: &Path) {
219
226
println ! ( " success: no undefined references to core found" ) ;
220
227
}
221
228
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
+ } ) ;
231
308
}
232
309
}
0 commit comments