From 571ff70fa19b31ef9cab78bb75e915f18f39363c Mon Sep 17 00:00:00 2001 From: Shadow Cat Date: Fri, 12 Jun 2026 22:24:35 -0400 Subject: [PATCH] convert programs into tests --- src/arch/x86_64/mod.rs | 2 +- src/arch/x86_64/test/bin.rs | 132 --------------------------- src/arch/x86_64/test/full/linux.rs | 54 +++++++++++ src/arch/x86_64/test/full/mod.rs | 11 +++ src/arch/x86_64/test/full/util.rs | 13 +++ src/arch/x86_64/test/full/windows.rs | 48 ++++++++++ src/arch/x86_64/test/mod.rs | 7 +- src/arch/x86_64/test/reg.rs | 83 ----------------- src/backend/container/pe/mod.rs | 1 - src/main.rs | 3 +- test/{ => bin}/x86_64_test | Bin test/bin/x86_64_test.exe | Bin 0 -> 616 bytes test/x86_64_test.exe | Bin 624 -> 0 bytes 13 files changed, 129 insertions(+), 225 deletions(-) delete mode 100644 src/arch/x86_64/test/bin.rs create mode 100644 src/arch/x86_64/test/full/linux.rs create mode 100644 src/arch/x86_64/test/full/mod.rs create mode 100644 src/arch/x86_64/test/full/util.rs create mode 100644 src/arch/x86_64/test/full/windows.rs delete mode 100644 src/arch/x86_64/test/reg.rs rename test/{ => bin}/x86_64_test (100%) create mode 100755 test/bin/x86_64_test.exe delete mode 100755 test/x86_64_test.exe diff --git a/src/arch/x86_64/mod.rs b/src/arch/x86_64/mod.rs index 1fd20c2..109e0f3 100644 --- a/src/arch/x86_64/mod.rs +++ b/src/arch/x86_64/mod.rs @@ -1,6 +1,7 @@ mod compile; mod encode; mod reg; +#[cfg(test)] mod test; mod types; mod util; @@ -14,7 +15,6 @@ use crate::{ pub use compile::*; pub use encode::*; pub use reg::*; -pub use test::bin::run as bin_test; pub use types::*; use util::*; diff --git a/src/arch/x86_64/test/bin.rs b/src/arch/x86_64/test/bin.rs deleted file mode 100644 index dc18d04..0000000 --- a/src/arch/x86_64/test/bin.rs +++ /dev/null @@ -1,132 +0,0 @@ -use crate::{ - arch::x86_64::*, - backend::{Instr as BInstr, Program}, -}; -use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt, process::Command}; - -pub fn run() { - if let Err(err) = linux() { - println!("{err:?}"); - } -} - -fn linux() -> Result<(), CompilerMsg> { - let mut program = Program::::default(); - let text = b"Hello world!\n"; - let text_sym = program.ro_data("hello_en", text); - let text2 = "世界、こんにちは!\n"; - let text_sym2 = program.ro_data("hello_jp", text2); - let hello2 = program.func( - "hello2", - [BInstr::Asm(encode(|c| { - c.mov(ax, 1)?; - c.mov(di, 1)?; - c.lea(rsi, text_sym2); - c.mov(dx, text2.len() as u64)?; - c.syscall(); - c.ret(); - Ok(()) - })?)], - ); - let entry = program.func( - "main", - [BInstr::Asm(encode(|c| { - c.mov(rdi, 39)?; - c.push(rdi)?; - c.mov(ax, 1)?; - c.mov(di, 1)?; - c.lea(rsi, text_sym); - c.mov(dx, text.len() as u64)?; - c.syscall(); - c.call(hello2); - c.mov(ax, 0x3c)?; - c.pop(rdi)?; - c.syscall(); - Ok(()) - })?)], - ); - program.entry = Some(entry); - let linked = program.compile().expect("failed to compile"); - let binary = linked.to_elf(); - let path = "./test/x86_64_test"; - write(path, &binary); - println!("running..."); - let gdb = false; - let mut proc = if gdb { - let mut cmd = Command::new("gdb"); - #[rustfmt::skip] - cmd.args([ - "-q", - "-ex", "starti", - "-ex", "layout asm", - "-ex", "focus next", - "x86_64_test", - ]); - cmd - } else { - Command::new(path) - } - .spawn() - .expect("failed to run"); - let status = proc.wait().expect("failed to wait"); - if let Some(code) = status.code() { - std::process::exit(code); - } - Ok(()) -} - -fn windows() -> Result<(), CompilerMsg> { - let mut program = Program::::default(); - let [get_std_handle, write_file, exit_process] = - program.external("KERNEL32.dll", ["GetStdHandle", "WriteFile", "ExitProcess"]); - let text = b"Hello world!\n"; - let text_sym = program.ro_data("hello_en", text); - let written = program.ro_data("written", [0; 4]); - let entry = program.func( - "main", - [BInstr::Asm(encode(|c| { - c.sub(); - // stdout - c.mov(ecx, -11)?; - c.call_mem(get_std_handle); - // write - c.mov(rcx, rax)?; - c.lea(rdx, text_sym); - c.mov(r8d, text.len() as u64)?; - c.lea(r9, written); - c.mov(mem(rsp, 0x20, Width::B32), 0)?; - c.call_mem(write_file); - // exit - c.mov(ecx, 39)?; - c.call_mem(exit_process); - Ok(()) - })?)], - ); - program.entry = Some(entry); - let linked = program.compile().expect("failed to compile"); - - let binary = linked.to_pe(); - let path = "./test/x86_64_test.exe"; - write(path, &binary); - - let mut cmd = Command::new("wine"); - cmd.arg("x86_64_test"); - let mut proc = cmd.spawn().expect("failed to run"); - let status = proc.wait().expect("failed to wait"); - if let Some(code) = status.code() { - std::process::exit(code); - } - Ok(()) -} - -fn write(path: &str, binary: &[u8]) { - let mut file = OpenOptions::new() - .create(true) - .write(true) - .truncate(true) - .mode(0o750) - .open(path) - .expect("Failed to create file"); - file.write_all(binary).expect("Failed to write to file"); - file.sync_all().expect("Failed to sync file"); -} diff --git a/src/arch/x86_64/test/full/linux.rs b/src/arch/x86_64/test/full/linux.rs new file mode 100644 index 0000000..bc49257 --- /dev/null +++ b/src/arch/x86_64/test/full/linux.rs @@ -0,0 +1,54 @@ +use super::*; + +#[test] +fn hello() -> Result<(), CompilerMsg> { + let mut program = Program::::default(); + let text = b"Hello world!\n"; + let text_sym = program.ro_data("hello_en", text); + let text2 = "世界、こんにちは!\n"; + let text_sym2 = program.ro_data("hello_jp", text2); + let hello2 = program.func( + "hello2", + [Instr::Asm(encode(|c| { + c.mov(ax, 1)?; + c.mov(di, 1)?; + c.lea(rsi, text_sym2); + c.mov(dx, text2.len() as u64)?; + c.syscall(); + c.ret(); + Ok(()) + })?)], + ); + let entry = program.func( + "main", + [Instr::Asm(encode(|c| { + c.mov(rdi, 39)?; + c.push(rdi)?; + c.mov(ax, 1)?; + c.mov(di, 1)?; + c.lea(rsi, text_sym); + c.mov(dx, text.len() as u64)?; + c.syscall(); + c.call(hello2); + c.mov(ax, 0x3c)?; + c.pop(rdi)?; + c.syscall(); + Ok(()) + })?)], + ); + program.entry = Some(entry); + let linked = program.compile().expect("failed to compile"); + let binary = linked.to_elf(); + let path = "test/bin/x86_64_test"; + write(path, &binary); + println!("running..."); + let mut cmd = Command::new(path); + let output = cmd.output().expect("failed to run"); + let Some(code) = output.status.code() else { + panic!("no exit code"); + }; + let result: String = output.stdout.try_into().expect("non ascii output"); + assert_eq!(result, "Hello world!\n世界、こんにちは!\n"); + assert_eq!(code, 39); + Ok(()) +} diff --git a/src/arch/x86_64/test/full/mod.rs b/src/arch/x86_64/test/full/mod.rs new file mode 100644 index 0000000..0ad9f49 --- /dev/null +++ b/src/arch/x86_64/test/full/mod.rs @@ -0,0 +1,11 @@ +mod linux; +mod util; +mod windows; + +use crate::{ + arch::x86_64::*, + backend::{Instr, Program}, + io::CompilerMsg, +}; +use std::process::Command; +use util::*; diff --git a/src/arch/x86_64/test/full/util.rs b/src/arch/x86_64/test/full/util.rs new file mode 100644 index 0000000..b4ec7a5 --- /dev/null +++ b/src/arch/x86_64/test/full/util.rs @@ -0,0 +1,13 @@ +use std::{fs::OpenOptions, io::Write, os::unix::fs::OpenOptionsExt}; + +pub fn write(path: &str, binary: &[u8]) { + let mut file = OpenOptions::new() + .create(true) + .write(true) + .truncate(true) + .mode(0o750) + .open(path) + .expect("Failed to create file"); + file.write_all(binary).expect("Failed to write to file"); + file.sync_all().expect("Failed to sync file"); +} diff --git a/src/arch/x86_64/test/full/windows.rs b/src/arch/x86_64/test/full/windows.rs new file mode 100644 index 0000000..7c9b44c --- /dev/null +++ b/src/arch/x86_64/test/full/windows.rs @@ -0,0 +1,48 @@ +use super::*; + +#[test] +fn hello() -> Result<(), CompilerMsg> { + let mut program = Program::::default(); + let [get_std_handle, write_file, exit_process] = + program.external("KERNEL32.dll", ["GetStdHandle", "WriteFile", "ExitProcess"]); + let text = b"Hello world!\n"; + let text_sym = program.ro_data("hello_en", text); + let written = program.ro_data("written", [0; 4]); + let entry = program.func( + "main", + [Instr::Asm(encode(|c| { + c.sub(); + // stdout + c.mov(ecx, -11)?; + c.call_mem(get_std_handle); + // write + c.mov(rcx, rax)?; + c.lea(rdx, text_sym); + c.mov(r8d, text.len() as u64)?; + c.lea(r9, written); + c.mov(mem(rsp, 0x20, Width::B32), 0)?; + c.call_mem(write_file); + // exit + c.mov(ecx, 39)?; + c.call_mem(exit_process); + Ok(()) + })?)], + ); + program.entry = Some(entry); + let linked = program.compile().expect("failed to compile"); + + let binary = linked.to_pe(); + let path = "test/bin/x86_64_test.exe"; + write(path, &binary); + + let mut cmd = Command::new("wine"); + cmd.arg(path); + let output = cmd.output().expect("failed to run"); + let Some(code) = output.status.code() else { + panic!("no exit code"); + }; + let result: String = output.stdout.try_into().expect("non ascii output"); + assert_eq!(result, "Hello world!\n"); + assert_eq!(code, 39); + Ok(()) +} diff --git a/src/arch/x86_64/test/mod.rs b/src/arch/x86_64/test/mod.rs index 89572ae..3a6a91a 100644 --- a/src/arch/x86_64/test/mod.rs +++ b/src/arch/x86_64/test/mod.rs @@ -1,7 +1,2 @@ -pub mod bin; -#[cfg(test)] +mod full; mod nasm; -#[cfg(test)] -mod reg; -#[cfg(test)] -use super::*; diff --git a/src/arch/x86_64/test/reg.rs b/src/arch/x86_64/test/reg.rs deleted file mode 100644 index 3a8ac1e..0000000 --- a/src/arch/x86_64/test/reg.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::*; -macro_rules! eq { - ($expected:expr, $instr:ident $args:tt $(,)?) => { - let expected = $expected.as_ref(); - let mut code = Code::default(); - let res = code.$instr $args; - let asm = stringify!($instr $args); - if let Err(e) = res { - panic!("{asm}: failed to compile: {}", e.msg); - } - let res = &code.bytes[..]; - assert_eq!(expected, res, "{asm}: expected {expected:x?}, got {res:x?}"); - }; -} - -// used objdump on some nasm compiled assembly - -#[test] -fn mov_reg_reg() { - eq!([0x48, 0x89, 0xd8], mov(rax, rbx)); - eq!([0x89, 0xd8], mov(eax, ebx)); - eq!([0x66, 0x89, 0xd8], mov(ax, bx)); - eq!([0x88, 0xd8], mov(al, bl)); - eq!([0x88, 0xfc], mov(ah, bh)); - - eq!([0x88, 0xf8], mov(al, bh)); - eq!([0x88, 0xdc], mov(ah, bl)); - eq!([0x40, 0x88, 0xe7], mov(dil, spl)); - - eq!([0x4d, 0x89, 0xc8], mov(r8, r9)); - eq!([0x45, 0x89, 0xc8], mov(r8d, r9d)); - eq!([0x66, 0x45, 0x89, 0xc8], mov(r8w, r9w)); - eq!([0x45, 0x88, 0xc8], mov(r8b, r9b)); - - eq!([0x49, 0x89, 0xc0], mov(r8, rax)); - eq!([0x4c, 0x89, 0xc0], mov(rax, r8)); - eq!([0x4d, 0x89, 0xd1], mov(r9, r10)); - - eq!([0x4d, 0x89, 0xe0], mov(r8, r12)); - - eq!([0x89, 0xe0], mov(eax, esp)); - eq!([0x89, 0xc4], mov(esp, eax)); -} - -#[test] -fn mov_reg_imm() { - eq!( - [0x49, 0xbf, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], - mov(r15, 0x123456789abcdef0u64), - ); - eq!( - [0x49, 0xb8, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], - mov(r8, 0x123456789abcdef0u64), - ); - eq!( - [0x49, 0xb9, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], - mov(r9, 0x123456789abcdef0u64), - ); - eq!([0x41, 0xb9, 0x78, 0x56, 0x34, 0x12], mov(r9d, 0x12345678)); - eq!([0x66, 0x41, 0xb9, 0x34, 0x12], mov(r9w, 0x1234)); - eq!([0x41, 0xb1, 0x12], mov(r9b, 0x12)); - eq!([0x41, 0xb0, 0x12], mov(r8b, 0x12)); - eq!([0x41, 0xb7, 0x12], mov(r15b, 0x12)); - - eq!( - [0x48, 0xb8, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], - mov(rax, 0x123456789abcdef0u64), - ); - eq!( - [0x48, 0xbb, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], - mov(rbx, 0x123456789abcdef0u64), - ); - eq!( - [0x48, 0xbf, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12], - mov(rdi, 0x123456789abcdef0u64), - ); - eq!([0xbb, 0x78, 0x56, 0x34, 0x12], mov(ebx, 0x12345678)); - eq!([0x66, 0xbb, 0x34, 0x12], mov(bx, 0x1234)); - eq!([0xb3, 0x12], mov(bl, 0x12)); - eq!([0xb7, 0x12], mov(bh, 0x12)); - eq!([0xb4, 0x12], mov(ah, 0x12)); - eq!([0x40, 0xb7, 0x12], mov(dil, 0x12)); -} diff --git a/src/backend/container/pe/mod.rs b/src/backend/container/pe/mod.rs index ce7f136..f9f2674 100644 --- a/src/backend/container/pe/mod.rs +++ b/src/backend/container/pe/mod.rs @@ -48,7 +48,6 @@ pub fn create(program: &LinkedProgram) -> Vec { let code_start = data.pos(); data.extend(&program.code); - println!("entry: 0x{:x}", (code_start as u64 + program.entry.unwrap()) as u32); if !program.imports.is_empty() { let import_rva = import::encode(&mut data, &program.imports, code_start); diff --git a/src/main.rs b/src/main.rs index 8f3d00d..509dffd 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,4 @@ -#![feature(gen_blocks)] +#![cfg_attr(test, feature(gen_blocks))] use crate::{io::CompilerOutput, parser_ir::parse_program}; @@ -10,7 +10,6 @@ mod parser; mod parser_ir; fn main() { - return crate::arch::x86_64::bin_test(); let mut args = std::env::args(); let Some(path) = args.nth(1) else { println!("file expected"); diff --git a/test/x86_64_test b/test/bin/x86_64_test similarity index 100% rename from test/x86_64_test rename to test/bin/x86_64_test diff --git a/test/bin/x86_64_test.exe b/test/bin/x86_64_test.exe new file mode 100755 index 0000000000000000000000000000000000000000..6e01c6a34b2ebc487c57ef26422421d9d96b1e95 GIT binary patch literal 616 zcmeZ`Vjvqh0CfhqGBBjHF`}69fkBChn~8(r2a;$bP)Pw;8Aulc1BeO2EFcaLz~~Gn zm^uaqX0WgTL;;jyU_j@?MZoF~01efE=%6d9SCU#$0<;n2XOKT(G_oTU7#I>fQgd?h z70UCAa#9qzK$0HKZ!~s({SO49J3!pdgC4!2*MMBd9lSukPcJWsbKFHm0mx(cFFG4Y z?o7?y^_?55|D*3f5K=aM<_5bBzUCeji`MLTS8|kIw