convert programs into tests
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
mod compile;
|
mod compile;
|
||||||
mod encode;
|
mod encode;
|
||||||
mod reg;
|
mod reg;
|
||||||
|
#[cfg(test)]
|
||||||
mod test;
|
mod test;
|
||||||
mod types;
|
mod types;
|
||||||
mod util;
|
mod util;
|
||||||
@@ -14,7 +15,6 @@ use crate::{
|
|||||||
pub use compile::*;
|
pub use compile::*;
|
||||||
pub use encode::*;
|
pub use encode::*;
|
||||||
pub use reg::*;
|
pub use reg::*;
|
||||||
pub use test::bin::run as bin_test;
|
|
||||||
pub use types::*;
|
pub use types::*;
|
||||||
use util::*;
|
use util::*;
|
||||||
|
|
||||||
|
|||||||
@@ -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::<X86_64>::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::<X86_64>::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");
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello() -> Result<(), CompilerMsg> {
|
||||||
|
let mut program = Program::<X86_64>::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(())
|
||||||
|
}
|
||||||
@@ -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::*;
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hello() -> Result<(), CompilerMsg> {
|
||||||
|
let mut program = Program::<X86_64>::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(())
|
||||||
|
}
|
||||||
@@ -1,7 +1,2 @@
|
|||||||
pub mod bin;
|
mod full;
|
||||||
#[cfg(test)]
|
|
||||||
mod nasm;
|
mod nasm;
|
||||||
#[cfg(test)]
|
|
||||||
mod reg;
|
|
||||||
#[cfg(test)]
|
|
||||||
use super::*;
|
|
||||||
|
|||||||
@@ -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));
|
|
||||||
}
|
|
||||||
@@ -48,7 +48,6 @@ pub fn create(program: &LinkedProgram<u64>) -> Vec<u8> {
|
|||||||
|
|
||||||
let code_start = data.pos();
|
let code_start = data.pos();
|
||||||
data.extend(&program.code);
|
data.extend(&program.code);
|
||||||
println!("entry: 0x{:x}", (code_start as u64 + program.entry.unwrap()) as u32);
|
|
||||||
|
|
||||||
if !program.imports.is_empty() {
|
if !program.imports.is_empty() {
|
||||||
let import_rva = import::encode(&mut data, &program.imports, code_start);
|
let import_rva = import::encode(&mut data, &program.imports, code_start);
|
||||||
|
|||||||
+1
-2
@@ -1,4 +1,4 @@
|
|||||||
#![feature(gen_blocks)]
|
#![cfg_attr(test, feature(gen_blocks))]
|
||||||
|
|
||||||
use crate::{io::CompilerOutput, parser_ir::parse_program};
|
use crate::{io::CompilerOutput, parser_ir::parse_program};
|
||||||
|
|
||||||
@@ -10,7 +10,6 @@ mod parser;
|
|||||||
mod parser_ir;
|
mod parser_ir;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
return crate::arch::x86_64::bin_test();
|
|
||||||
let mut args = std::env::args();
|
let mut args = std::env::args();
|
||||||
let Some(path) = args.nth(1) else {
|
let Some(path) = args.nth(1) else {
|
||||||
println!("file expected");
|
println!("file expected");
|
||||||
|
|||||||
Executable
BIN
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user