summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Cargo.toml3
-rw-r--r--src/lib.rs2
-rw-r--r--src/linker.rs4
-rw-r--r--src/main.rs4
-rw-r--r--tests/asm.c0
-rw-r--r--tests/basic.c5
-rw-r--r--tests/cpp-input.txt5
-rw-r--r--tests/tests.rs98
8 files changed, 114 insertions, 7 deletions
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
--- /dev/null
+++ b/tests/asm.c
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");
+ }
+}