Skip to content

Commit f51f0b6

Browse files
committed
wip
1 parent 9f44c32 commit f51f0b6

File tree

4 files changed

+106
-42
lines changed

4 files changed

+106
-42
lines changed

crates/symbol-check/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,6 @@ serde_json = "1.0.140"
1111

1212
[features]
1313
wasm = ["object/wasm"]
14+
15+
[build-dependencies]
16+
cc = "1.2.25"

crates/symbol-check/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
fn main() {
2+
let intermediates = cc::Build::new()
3+
.file("has_wx.c")
4+
.try_compile_intermediates();
5+
if let Ok(list) = intermediates {
6+
let [obj] = list.as_slice() else { panic!() };
7+
println!("cargo::rustc-env=HAS_WX_OBJ={}", obj.display());
8+
}
9+
}

crates/symbol-check/has_wx.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
void intermediate(void (*)(int, int), int);
2+
3+
int hack(int *array, int size) {
4+
void store (int index, int value) {
5+
array[index] = value;
6+
}
7+
8+
intermediate(store, size);
9+
}

crates/symbol-check/src/main.rs

Lines changed: 85 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ use std::process::{Command, Stdio};
1010
use object::elf::SHF_EXECINSTR;
1111
use object::read::archive::{ArchiveFile, ArchiveMember};
1212
use object::{
13-
Object, ObjectSection, ObjectSymbol, SectionFlags, Symbol, SymbolKind, SymbolScope,
14-
SymbolSection,
13+
File as ObjFile, Object, ObjectSection, ObjectSymbol, SectionFlags, Symbol, SymbolKind,
14+
SymbolScope, SymbolSection,
1515
};
1616
use serde_json::Value;
1717

@@ -32,16 +32,11 @@ fn main() {
3232
let args_ref = args.iter().map(String::as_str).collect::<Vec<_>>();
3333

3434
match &args_ref[1..] {
35-
["build-and-check", rest @ ..] if !rest.is_empty() => {
36-
let paths = exec_cargo_with_args(rest);
37-
for path in paths {
38-
println!("Checking {}", path.display());
39-
let archive = Archive::from_path(&path);
40-
41-
verify_no_duplicates(&archive);
42-
verify_core_symbols(&archive);
43-
verify_no_exec_stack(&archive);
44-
}
35+
["build-and-check", "--target", target, args @ ..] if !args.is_empty() => {
36+
run_build_and_check(Some(target), args);
37+
}
38+
["build-and-check", args @ ..] if !args.is_empty() => {
39+
run_build_and_check(None, args);
4540
}
4641
_ => {
4742
println!("{USAGE}");
@@ -50,12 +45,43 @@ fn main() {
5045
}
5146
}
5247

48+
fn run_build_and_check(target: Option<&str>, args: &[&str]) {
49+
let paths = exec_cargo_with_args(target, args);
50+
for path in paths {
51+
println!("Checking {}", path.display());
52+
let archive = Archive::from_path(&path);
53+
54+
verify_no_duplicates(&archive);
55+
verify_core_symbols(&archive);
56+
verify_no_exec_stack(&archive);
57+
}
58+
}
59+
60+
fn host_target() -> String {
61+
let out = Command::new("rustc")
62+
.arg("--version")
63+
.arg("--verbose")
64+
.output()
65+
.unwrap();
66+
assert!(out.status.success());
67+
let out = String::from_utf8(out.stdout).unwrap();
68+
out.lines()
69+
.find_map(|s| s.strip_prefix("host: "))
70+
.unwrap()
71+
.to_owned()
72+
}
73+
5374
/// Run `cargo build` with the provided additional arguments, collecting the list of created
5475
/// libraries.
55-
fn exec_cargo_with_args(args: &[&str]) -> Vec<PathBuf> {
76+
fn exec_cargo_with_args(target: Option<&str>, args: &[&str]) -> Vec<PathBuf> {
77+
let mut host = String::new();
78+
let target = target.unwrap_or_else(|| {
79+
host = host_target();
80+
host.as_str()
81+
});
82+
5683
let mut cmd = Command::new("cargo");
57-
cmd.arg("build")
58-
.arg("--message-format=json")
84+
cmd.args(["build", "--target", &target, "--message-format=json"])
5985
.args(args)
6086
.stdout(Stdio::piped());
6187

@@ -232,32 +258,9 @@ fn verify_no_exec_stack(archive: &Archive) {
232258
let mut problem_objfiles = Vec::new();
233259

234260
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;
261+
if obj_has_exe_stack(&obj) {
262+
problem_objfiles.push(String::from_utf8_lossy(member.name()).into_owned());
258263
}
259-
260-
problem_objfiles.push(String::from_utf8_lossy(member.name()).into_owned());
261264
});
262265

263266
if !problem_objfiles.is_empty() {
@@ -270,6 +273,35 @@ fn verify_no_exec_stack(archive: &Archive) {
270273
println!(" success: no writeable-executable sections found");
271274
}
272275

276+
fn obj_has_exe_stack(obj: &ObjFile) -> bool {
277+
// Files other than elf likely do not use the same convention.
278+
if !matches!(obj, ObjFile::Elf32(_) | ObjFile::Elf64(_)) {
279+
return false;
280+
}
281+
282+
let mut has_exe_sections = false;
283+
for sec in obj.sections() {
284+
let SectionFlags::Elf { sh_flags } = sec.flags() else {
285+
unreachable!("only elf files are being checked");
286+
};
287+
288+
let exe = (sh_flags & SHF_EXECINSTR as u64) != 0;
289+
has_exe_sections |= exe;
290+
291+
// Located a GNU-stack section, nothing else to do
292+
if sec.name().unwrap_or_default() == ".note.GNU-stack" {
293+
return false;
294+
}
295+
}
296+
297+
// Ignore object files that have no executable sections, like rmeta
298+
if !has_exe_sections {
299+
return false;
300+
}
301+
302+
true
303+
}
304+
273305
/// Thin wrapper for owning data used by `object`.
274306
struct Archive {
275307
data: Vec<u8>,
@@ -287,15 +319,15 @@ impl Archive {
287319
}
288320

289321
/// For a given archive, do something with each object file.
290-
fn for_each_object(&self, mut f: impl FnMut(object::File, &ArchiveMember)) {
322+
fn for_each_object(&self, mut f: impl FnMut(ObjFile, &ArchiveMember)) {
291323
let archive = self.file();
292324

293325
for member in archive.members() {
294326
let member = member.expect("failed to access member");
295327
let obj_data = member
296328
.data(self.data.as_slice())
297329
.expect("failed to access object");
298-
let obj = object::File::parse(obj_data).expect("failed to parse object");
330+
let obj = ObjFile::parse(obj_data).expect("failed to parse object");
299331
f(obj, &member);
300332
}
301333
}
@@ -307,3 +339,14 @@ impl Archive {
307339
});
308340
}
309341
}
342+
343+
#[test]
344+
fn check_obj() {
345+
let Some(p) = option_env!("HAS_WX_OBJ") else {
346+
return;
347+
};
348+
349+
let f = fs::read(p).unwrap();
350+
let obj = ObjFile::parse(f.as_slice()).unwrap();
351+
assert!(obj_has_exe_stack(&obj));
352+
}

0 commit comments

Comments
 (0)