From d2b519d5cd43236e8f23a5ca05b8e6e31346d241 Mon Sep 17 00:00:00 2001 From: pommicket Date: Tue, 8 Nov 2022 13:14:22 -0500 Subject: tests --- Cargo.toml | 3 ++ src/lib.rs | 2 ++ src/linker.rs | 4 +-- src/main.rs | 4 +-- tests/asm.c | 0 tests/basic.c | 5 +-- tests/cpp-input.txt | 5 +++ tests/tests.rs | 98 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 8 files changed, 114 insertions(+), 7 deletions(-) create mode 100644 src/lib.rs create mode 100644 tests/asm.c create mode 100644 tests/cpp-input.txt create mode 100644 tests/tests.rs diff --git a/Cargo.toml b/Cargo.toml index 0435f91..b308025 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,6 +3,9 @@ name = "tinyld" version = "0.1.0" edition = "2021" +[lib] +doctest = false + [dependencies] clap = { version = "4.0", features = ["derive"] } termcolor = "1.1" diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..e11d058 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,2 @@ +mod elf; +pub mod linker; diff --git a/src/linker.rs b/src/linker.rs index 8065abd..b1ee0a8 100644 --- a/src/linker.rs +++ b/src/linker.rs @@ -13,7 +13,7 @@ Position-independent code is NOT supported, and makes executables larger anyways. Make sure you compile with `-fno-pic` or equivalent. Example usage: -``` +```ignore let mut linker = Linker::new(); linker.add_input("main.o")?; linker.add_input("libc.so.6")?; @@ -50,7 +50,7 @@ This ensures that all destructors are called for local objects in main. - If you want a small executable, it's best not to use the STL. - For some reason, `std::cout` and `std::cin` don't work. If you can figure out why, please let me know. You can get around this with something like -``` +```cpp std::ofstream cout("/dev/stdout"); std::ifstream cin("/dev/stdin"); ``` diff --git a/src/main.rs b/src/main.rs index 775138a..926b1e4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,5 @@ /* @TODO: -- automated tests - what happens when a symbol has two definitions? can this happen with multiple c++ files which use the same template? - disable "warning: relocation XXX not in a data/text section" for .rel.eh_frame + maybe others - these warnings are being generated in two places. do they need to be? @@ -16,8 +15,7 @@ use clap::Parser; #[cfg(target_endian = "big")] compile_error! {"WHY do you have a big endian machine???? it's the 21st century, buddy. this program won't work fuck you"} -mod elf; -pub mod linker; +use tinyld::linker; #[derive(Parser, Debug)] struct Args { diff --git a/tests/asm.c b/tests/asm.c new file mode 100644 index 0000000..e69de29 diff --git a/tests/basic.c b/tests/basic.c index b8a6d61..6abdf20 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -3,8 +3,9 @@ int my_number; void entry() { - my_number = 137; - printf("%d\n",my_number); + volatile int *p = &my_number; + *p = 137; + printf("%d\n",*p); exit(0); } diff --git a/tests/cpp-input.txt b/tests/cpp-input.txt new file mode 100644 index 0000000..95dd95e --- /dev/null +++ b/tests/cpp-input.txt @@ -0,0 +1,5 @@ +65 +212 +0 +233 +7 diff --git a/tests/tests.rs b/tests/tests.rs new file mode 100644 index 0000000..9e23641 --- /dev/null +++ b/tests/tests.rs @@ -0,0 +1,98 @@ +#[cfg(test)] +mod tests { + use std::process::{Command, Output}; + use std::fs::File; + use tinyld::linker::{Linker, LinkWarning}; + + fn panic_warning_handler(warning: LinkWarning) { + eprintln!("warning: {warning}"); + panic!("this should not generate a warning"); + } + + fn test_linker<'a>() -> Linker<'a> { + let mut linker = Linker::new(); + linker.set_warning_handler(panic_warning_handler); + linker + } + + fn file(s: &str) -> String { + format!("./tests/{s}") + } + + fn add(linker: &mut Linker, src: &str, is_local: bool) { + let f = file(src); + let s = if is_local { &f } else { src }; + linker.add_input(s).expect(&format!("failed to add {s}")); + } + + fn link(linker: &Linker, out: &str, entry: &str) { + linker.link_to_file(&file(out), entry).expect("failed to link"); + } + + fn run_with_stdin(name: &str, stdin: Option<&str>) -> Output { + let mut command = Command::new(&file(name)); + if let Some(s) = stdin { + let file = File::open(&file(s)).expect("stdin file does not exist"); + command.stdin(file); + } + + let output = command + .output() + .expect("failed to run output executable"); + assert!(output.status.success()); + assert!(output.stderr.is_empty()); + output + } + + fn run(name: &str) -> std::process::Output { + run_with_stdin(name, None) + } + + #[test] + fn tiny_c() { + let mut linker = test_linker(); + add(&mut linker, "tiny.c", true); + link(&linker, "test.out", "entry"); + let output = run("tiny.out"); + assert!(output.stdout.is_empty()); + } + + #[test] + fn basic_c() { + let mut linker = test_linker(); + add(&mut linker, "basic.c", true); + add(&mut linker, "libc.so.6", false); + link(&linker, "basic.out", "entry"); + let output = run("basic.out"); + assert_eq!(output.stdout, b"137\n"); + } + + #[test] + fn dylib_c() { + let status = Command::new("gcc") + .args(&["-m32", "-fPIC", "-shared", &file("dylib.c"), "-o", &file("dylib.so")]) + .status() + .expect("failed to create dylib.so"); + assert!(status.success()); + + + let mut linker = test_linker(); + add(&mut linker, "dylib-test.c", true); + add(&mut linker, "dylib.so", true); + add(&mut linker, "libc.so.6", false); + link(&linker, "dylib-test.out", "entry"); + let output = run("dylib-test.out"); + assert_eq!(output.stdout, b"7\n8\n"); + } + + #[test] + fn cpp() { + let mut linker = test_linker(); + add(&mut linker, "cpp.cpp", true); + add(&mut linker, "libc.so.6", false); + add(&mut linker, "libstdc++.so.6", false); + link(&linker, "cpp.out", "main"); + let output = run_with_stdin("cpp.out", Some("cpp-input.txt")); + assert_eq!(output.stdout, b"0\n7\n65\n212\n233\n"); + } +} -- cgit v1.2.3