use object::{Object, ObjectSection};
use std::process::Command;
fn synth() -> &'static str {
env!("CARGO_BIN_EXE_synth")
}
fn fixture(name: &str) -> std::path::PathBuf {
std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../..")
.join("scripts/repro")
.join(name)
}
fn compile_text(
fixture_name: &str,
out_tag: &str,
envs: &[(&str, &str)],
extra: &[&str],
) -> Result<Vec<u8>, String> {
let path = fixture(fixture_name);
let elf = format!("/tmp/volseg543_{out_tag}.elf");
let mut args = vec![
"compile",
path.to_str().unwrap(),
"-o",
&elf,
"-b",
"arm",
"--target",
"cortex-m4",
"--all-exports",
];
args.extend_from_slice(extra);
let mut cmd = Command::new(synth());
cmd.env_remove("SYNTH_BASE_CSE");
cmd.env_remove("SYNTH_CONST_CSE");
for (k, v) in envs {
cmd.env(k, v);
}
let out = cmd.args(&args).output().expect("run synth");
if !out.status.success() {
return Err(format!(
"exit={:?} stderr={}",
out.status.code(),
String::from_utf8_lossy(&out.stderr)
));
}
let bytes = std::fs::read(&elf).expect("read elf");
let obj = object::File::parse(&*bytes).expect("parse elf");
let text = obj
.section_by_name(".text")
.expect("fixture must have a .text section")
.data()
.expect("read .text")
.to_vec();
Ok(text)
}
const FIXTURE: &str = "base_cse_branch.wat";
#[test]
fn volatile_segment_flag_accepted_543() {
compile_text(
FIXTURE,
"accept_single",
&[],
&["--volatile-segment", "0x20001000:4096"],
)
.expect("single --volatile-segment must be accepted");
compile_text(
FIXTURE,
"accept_repeat",
&[],
&[
"--volatile-segment",
"0x20001000:4096",
"--volatile-segment",
"536875008:256",
],
)
.expect("repeated --volatile-segment must be accepted");
}
#[test]
fn volatile_segment_flag_rejects_garbage_543() {
let err = compile_text(FIXTURE, "reject", &[], &["--volatile-segment", "garbage"])
.expect_err("`--volatile-segment garbage` must fail");
assert!(
err.contains("volatile-segment"),
"error should name the offending flag, got: {err}"
);
}
#[test]
fn volatile_segment_flag_is_byte_inert_543() {
const BASE_CSE_OFF: (&str, &str) = ("SYNTH_BASE_CSE", "0");
const CONST_CSE_OFF: (&str, &str) = ("SYNTH_CONST_CSE", "0");
let without = compile_text(
FIXTURE,
"inert_without",
&[BASE_CSE_OFF, CONST_CSE_OFF],
&[],
)
.expect("baseline compile (no flag) must succeed");
let with = compile_text(
FIXTURE,
"inert_with",
&[BASE_CSE_OFF, CONST_CSE_OFF],
&["--volatile-segment", "0x20001000:4096"],
)
.expect("compile with flag must succeed");
assert_eq!(
without, with,
"#543: --volatile-segment changed the emitted .text with every \
address-caching lever disabled (SYNTH_BASE_CSE=0, SYNTH_CONST_CSE=0) \
— the back-off must only fire through an active lever"
);
}