use std::{
io::{stdin, Read},
os::unix::ffi::OsStrExt,
process::ExitCode,
str::FromStr,
};
use data_encoding::HEXLOWER_PERMISSIVE;
use libseccomp::ScmpArch;
use nix::{errno::Errno, sys::utsname::uname};
use syd::asm::disasm;
#[cfg(all(
not(coverage),
not(feature = "prof"),
not(target_os = "android"),
not(target_arch = "riscv64"),
target_page_size_4k,
target_pointer_width = "64"
))]
#[global_allocator]
static GLOBAL: hardened_malloc::HardenedMalloc = hardened_malloc::HardenedMalloc;
#[cfg(feature = "prof")]
#[global_allocator]
static GLOBAL: tcmalloc::TCMalloc = tcmalloc::TCMalloc;
syd::main! {
use lexopt::prelude::*;
syd::set_sigpipe_dfl()?;
let mut opt_arch = ScmpArch::Native;
let mut parser = lexopt::Parser::from_env();
while let Some(arg) = parser.next()? {
match arg {
Short('h') => {
help();
return Ok(ExitCode::SUCCESS);
}
Short('a') => {
let value = parser.value()?.parse::<String>()?;
if matches!(value.to_ascii_lowercase().as_str(), "help" | "list") {
syd::confine::print_seccomp_architectures();
return Ok(ExitCode::SUCCESS);
}
opt_arch = match ScmpArch::from_str(&format!(
"SCMP_ARCH_{}",
value.to_ascii_uppercase()
)) {
Ok(opt_arch) => opt_arch,
Err(_) => {
eprintln!("Invalid architecture `{value}', use `-a list' for a list.");
return Ok(ExitCode::FAILURE);
}
};
}
_ => return Err(arg.unexpected().into()),
}
}
if opt_arch == ScmpArch::Native {
opt_arch = determine_arch()?;
}
let mut input = Vec::with_capacity(4096);
stdin().read_to_end(&mut input)?;
let hex = input
.iter()
.take(input.len().min(16))
.all(|&b| b.is_ascii_whitespace() || b.is_ascii_hexdigit());
if hex {
let data = std::str::from_utf8(&input)?;
let data = data.split_whitespace().collect::<String>();
input = match HEXLOWER_PERMISSIVE.decode(data.as_bytes()) {
Ok(input) => input,
Err(error) => {
eprintln!("Error decoding hex: {error}");
return Ok(ExitCode::FAILURE);
}
}
}
let instructions = disasm(&input, opt_arch, 0, false, true)?;
for instruction in &instructions {
println!(
"{}",
serde_json::to_string(&instruction).or(Err(Errno::EINVAL))?
);
}
Ok(ExitCode::SUCCESS)
}
fn help() {
println!("Usage: syd-asm [-h] [-a list|x86|x86_64|aarch64...]");
println!("Disassembles raw CPU instructions from standard input.");
}
fn determine_arch() -> Result<ScmpArch, Errno> {
let uts = uname()?;
let machine = uts.machine().as_bytes();
match machine {
b"x86_64" => Ok(ScmpArch::X8664),
b"i386" | b"i686" => Ok(ScmpArch::X86),
b"armv7l" | b"arm" => Ok(ScmpArch::Arm),
b"aarch64" => Ok(ScmpArch::Aarch64),
b"loongarch64" => Ok(ScmpArch::Loongarch64),
b"m68k" => Ok(ScmpArch::M68k),
b"mips" => Ok(ScmpArch::Mips),
b"mips64" => Ok(ScmpArch::Mips64),
b"riscv64" => Ok(ScmpArch::Riscv64),
b"ppc64" => Ok(ScmpArch::Ppc64),
b"ppc64le" => Ok(ScmpArch::Ppc64Le),
b"s390x" => Ok(ScmpArch::S390X),
b"sheb" => Ok(ScmpArch::Sheb),
b"sh" => Ok(ScmpArch::Sh),
_ => Err(Errno::EINVAL),
}
}