227 lines
7.4 KiB
Rust
227 lines
7.4 KiB
Rust
use super::*;
|
|
use std::cell::Cell;
|
|
|
|
fn header(t: Target) -> Vec<u8> {
|
|
let mut b = vec![0; 256];
|
|
match t {
|
|
Target::WindowsAmd64 => {
|
|
b[..2].copy_from_slice(b"MZ");
|
|
b[0x3c..0x40].copy_from_slice(&64u32.to_le_bytes());
|
|
b[64..68].copy_from_slice(b"PE\0\0");
|
|
b[68..70].copy_from_slice(&0x8664u16.to_le_bytes());
|
|
b[86..88].copy_from_slice(&2u16.to_le_bytes());
|
|
b[88..90].copy_from_slice(&0x20bu16.to_le_bytes());
|
|
b[156..158].copy_from_slice(&3u16.to_le_bytes());
|
|
}
|
|
Target::MacosArm64 => {
|
|
b[..4].copy_from_slice(&[0xcf, 0xfa, 0xed, 0xfe]);
|
|
b[4..8].copy_from_slice(&0x0100000cu32.to_le_bytes());
|
|
b[12..16].copy_from_slice(&2u32.to_le_bytes());
|
|
}
|
|
Target::LinuxAmd64 | Target::LinuxArm64 => {
|
|
b[..7].copy_from_slice(b"\x7fELF\x02\x01\x01");
|
|
b[16..18].copy_from_slice(&2u16.to_le_bytes());
|
|
let cpu: u16 = if t == Target::LinuxAmd64 { 62 } else { 183 };
|
|
b[18..20].copy_from_slice(&cpu.to_le_bytes());
|
|
}
|
|
}
|
|
b.extend_from_slice(&runtime_marker(t));
|
|
b
|
|
}
|
|
struct Dir(PathBuf);
|
|
impl Dir {
|
|
fn new() -> Self {
|
|
let p = std::env::temp_dir().join(format!(
|
|
"tb-export-test-{}-{}",
|
|
std::process::id(),
|
|
NEXT.fetch_add(1, Ordering::Relaxed)
|
|
));
|
|
fs::create_dir(&p).unwrap();
|
|
Self(p)
|
|
}
|
|
}
|
|
impl Drop for Dir {
|
|
fn drop(&mut self) {
|
|
fs::remove_dir_all(&self.0).unwrap();
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn targets_versions_integrity_and_payload_bounds_are_enforced() {
|
|
let d = Dir::new();
|
|
for t in Target::ALL {
|
|
let b = header(t);
|
|
assert_eq!(native_target(&b).unwrap(), t);
|
|
let path = d.0.join(t.runtime_name());
|
|
fs::write(&path, &b).unwrap();
|
|
prepare_template(&path, t).unwrap();
|
|
assert_eq!(template(&path, t).unwrap(), b);
|
|
let other = if t == Target::LinuxAmd64 {
|
|
Target::LinuxArm64
|
|
} else {
|
|
Target::LinuxAmd64
|
|
};
|
|
assert!(template(&path, other)
|
|
.unwrap_err()
|
|
.to_string()
|
|
.contains("Architektur"));
|
|
let meta = manifest(&b, t);
|
|
for wrong in [
|
|
meta.replace("runtime=1", "runtime=2"),
|
|
meta.replace("package=0.1.0", "package=9"),
|
|
meta.replace("tbc=4", "tbc=99"),
|
|
meta.replace("checksum=", "checksum=0"),
|
|
] {
|
|
fs::write(manifest_path(&path), wrong).unwrap();
|
|
assert!(template(&path, t).is_err());
|
|
}
|
|
fs::remove_file(manifest_path(&path)).unwrap();
|
|
assert!(template(&path, t).is_err());
|
|
fs::remove_file(&path).unwrap();
|
|
assert!(template(&path, t)
|
|
.unwrap_err()
|
|
.to_string()
|
|
.contains("fehlt"));
|
|
for len in 0..64 {
|
|
assert!(native_target(&b[..len]).is_err());
|
|
}
|
|
}
|
|
assert!(Target::parse("aarch64-pc-windows-msvc").is_err());
|
|
assert!(Target::parse("x86_64-apple-darwin").is_err());
|
|
let mut foreign_abi = header(Target::LinuxAmd64);
|
|
foreign_abi[7] = 9;
|
|
assert!(native_target(&foreign_abi).is_err());
|
|
let module = tb_vm::compile_source("TEST", "PRINT 42\nEND\n").unwrap();
|
|
for t in [Target::WindowsAmd64, Target::LinuxAmd64, Target::LinuxArm64] {
|
|
let mut b = header(t);
|
|
if t == Target::WindowsAmd64 {
|
|
let mut signed = b.clone();
|
|
signed[196..200].copy_from_slice(&5u32.to_le_bytes());
|
|
signed[232] = 1;
|
|
assert!(append_payload(&mut signed, &module, t)
|
|
.unwrap_err()
|
|
.to_string()
|
|
.contains("Signierte PE"));
|
|
b[152..156].copy_from_slice(&123u32.to_le_bytes());
|
|
}
|
|
append_payload(&mut b, &module, t).unwrap();
|
|
if t == Target::WindowsAmd64 {
|
|
assert_eq!(&b[152..156], &[0; 4]);
|
|
}
|
|
assert_eq!(embedded(&b, t).unwrap().to_tbc(), module.to_tbc());
|
|
let at = b.len() - FOOTER;
|
|
for field in [8, 12, 16, 20, 24, 32, 40] {
|
|
let mut bad = b.clone();
|
|
bad[at + field] ^= 0xff;
|
|
assert!(embedded(&bad, t).is_err(), "{field}");
|
|
}
|
|
for cut in 1..=FOOTER + 1 {
|
|
assert!(embedded(&b[..b.len() - cut], t).is_err());
|
|
}
|
|
let mut bad = b.clone();
|
|
bad[300] ^= 1;
|
|
assert!(embedded(&bad, t).is_err());
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn publication_preserves_originals_on_conflict_failure_race_and_cancel() {
|
|
let d = Dir::new();
|
|
let out = d.0.join("result");
|
|
let source = d.0.join("source.bas");
|
|
fs::write(&source, b"source").unwrap();
|
|
fs::write(&out, b"old").unwrap();
|
|
assert!(publish(&out, false, &[], &|| false, |_| panic!(
|
|
"darf nicht schreiben"
|
|
))
|
|
.is_err());
|
|
assert!(publish(
|
|
&source,
|
|
true,
|
|
std::slice::from_ref(&source),
|
|
&|| false,
|
|
|_| panic!("Projektschutz")
|
|
)
|
|
.is_err());
|
|
assert!(publish(&out, true, &[], &|| false, |p| {
|
|
fs::write(p, b"new")?;
|
|
bail!("Finalisierungsfehler")
|
|
})
|
|
.is_err());
|
|
assert_eq!(fs::read(&out).unwrap(), b"old");
|
|
let cancel = Cell::new(false);
|
|
assert!(publish(&out, true, &[], &|| cancel.get(), |p| {
|
|
fs::write(p, b"new")?;
|
|
cancel.set(true);
|
|
Ok(())
|
|
})
|
|
.is_err());
|
|
assert_eq!(fs::read(&out).unwrap(), b"old");
|
|
assert!(publish(&out, true, &[], &|| true, |_| panic!("abgebrochen")).is_err());
|
|
assert!(publish(&out, true, &[], &|| false, |p| {
|
|
fs::write(p, b"new")?;
|
|
fs::write(&out, b"external")?;
|
|
Ok(())
|
|
})
|
|
.is_err());
|
|
assert_eq!(fs::read(&out).unwrap(), b"external");
|
|
fs::remove_file(&out).unwrap();
|
|
assert!(publish(&out, true, &[], &|| false, |p| {
|
|
fs::write(p, b"new")?;
|
|
fs::write(&out, b"racer")?;
|
|
Ok(())
|
|
})
|
|
.is_err());
|
|
assert_eq!(fs::read(&out).unwrap(), b"racer");
|
|
assert!(
|
|
publish(&d.0.join("missing/result"), false, &[], &|| false, |_| Ok(
|
|
()
|
|
))
|
|
.is_err()
|
|
);
|
|
publish(&out, true, &[], &|| false, |p| {
|
|
fs::write(p, b"approved")?;
|
|
Ok(())
|
|
})
|
|
.unwrap();
|
|
assert_eq!(fs::read(&out).unwrap(), b"approved");
|
|
assert_eq!(fs::read(&source).unwrap(), b"source");
|
|
assert_eq!(fs::read_dir(&d.0).unwrap().count(), 2);
|
|
#[cfg(unix)]
|
|
{
|
|
std::os::unix::fs::symlink(&source, d.0.join("alias")).unwrap();
|
|
assert!(
|
|
publish(&d.0.join("alias"), true, &[], &|| false, |_| panic!(
|
|
"Symlink"
|
|
))
|
|
.is_err()
|
|
);
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn fehlgeschlagene_native_finalisierung_erhaelt_das_ziel() {
|
|
let dir = Dir::new();
|
|
let runtime = dir.0.join("tbrt");
|
|
let out = dir.0.join("program");
|
|
fs::write(&runtime, header(Target::MacosArm64)).unwrap();
|
|
prepare_template(&runtime, Target::MacosArm64).unwrap();
|
|
fs::write(&out, b"original").unwrap();
|
|
let module = tb_vm::compile_source("TEST", "END\n").unwrap();
|
|
let error = export(
|
|
Export {
|
|
module: &module,
|
|
target: Target::MacosArm64,
|
|
template: &runtime,
|
|
output: &out,
|
|
overwrite: true,
|
|
protected: &[],
|
|
},
|
|
&|| false,
|
|
)
|
|
.unwrap_err();
|
|
assert!(error.to_string().contains("Finalisierung"), "{error:#}");
|
|
assert_eq!(fs::read(&out).unwrap(), b"original");
|
|
assert_eq!(fs::read_dir(&dir.0).unwrap().count(), 3);
|
|
}
|