use std::path::PathBuf;
use std::process::Command;
mod artifact_guard;
use object::{Object, ObjectSection, ObjectSymbol, RelocationTarget};
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn repro(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join(name)
}
fn compile(fixture: &str, extra: &[&str], out: &str) -> std::process::Output {
let fx = repro(fixture);
let mut args = vec![
"compile",
fx.to_str().unwrap(),
"--target",
"cortex-m3",
"--native-pointer-abi",
"--all-exports",
"--relocatable",
"-o",
out,
];
args.extend_from_slice(extra);
Command::new(synth())
.args(&args)
.output()
.expect("run synth")
}
fn compile_read(fixture: &str, extra: &[&str], tag: &str) -> Vec<u8> {
let fx = repro(fixture);
let out = artifact_guard::unique_artifact(tag, "o");
let mut args = vec![
"compile",
fx.to_str().unwrap(),
"--target",
"cortex-m3",
"--native-pointer-abi",
"--all-exports",
"--relocatable",
"-o",
out.to_str().unwrap(),
];
args.extend_from_slice(extra);
let mut cmd = Command::new(synth());
cmd.args(&args);
artifact_guard::compile_bytes_or_panic(&mut cmd, &out, tag)
}
fn bss_size(data: &[u8]) -> u64 {
let obj = object::File::parse(data).expect("parse ELF");
obj.sections()
.find(|s| s.name() == Ok(".bss"))
.expect(".bss present")
.size()
}
fn max_wasm_data_addend(bytes: &[u8]) -> u32 {
let obj = object::File::parse(bytes).expect("parse ELF");
let text = obj.section_by_name(".text").expect(".text");
let text_data = text.data().expect("text data");
let mut best = 0u32;
for (off, reloc) in text.relocations() {
if let RelocationTarget::Symbol(sidx) = reloc.target() {
let sym = obj.symbol_by_index(sidx).expect("sym");
if sym.name() == Ok("__synth_wasm_data") {
let p = off as usize;
let word = u32::from_le_bytes(text_data[p..p + 4].try_into().unwrap());
best = best.max(word);
}
}
}
best
}
#[test]
fn bss_static_downshifts_678() {
let bytes = compile_read(
"mem678_bss.wat",
&["--shadow-stack-size", "512"],
"mem678_bss_test",
);
assert_eq!(
max_wasm_data_addend(&bytes),
576,
"static at 4160 must rebase to 4160 - (4096-512) = 576"
);
assert_eq!(bss_size(&bytes), 584);
}
#[test]
fn buffer_plus_bss_downshifts_678() {
let bytes = compile_read(
"mem678_buf.wat",
&["--shadow-stack-size", "512"],
"mem678_buf_test",
);
assert_eq!(
max_wasm_data_addend(&bytes),
616,
"bss static at 4200 must rebase to 616; the data-seg read stays in .data"
);
}
#[test]
fn straddling_static_refused_678() {
let out = compile(
"mem678_straddle.wat",
&["--shadow-stack-size", "512"],
"/tmp/mem678_straddle_test.o",
);
assert!(!out.status.success(), "straddle must be refused");
let log = String::from_utf8_lossy(&out.stderr);
assert!(
log.contains("straddles an initialized (data) segment"),
"expected the precise straddle decline; got:\n{log}"
);
}
#[test]
fn no_flag_leaves_statics_inline_678() {
let bytes = compile_read("mem678_buf.wat", &[], "mem678_buf_noflag");
assert_eq!(max_wasm_data_addend(&bytes), 4200);
}