Skip to content

Add Rust version #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[target.x86_64-unknown-linux-gnu]
linker = "x86_64-linux-gnu-gcc"
rustflags = [
"-Clink-args=-nostartfiles",
"-Clink-args=-Wl,-n,-N,--no-dynamic-linker,--build-id=none",
"-Crelocation-model=static"
]

[target.aarch64-unknown-linux-gnu]
linker = "aarch64-linux-gnu-gcc"
rustflags = [
"-Clink-args=-nostartfiles",
"-Clink-args=-Wl,-n,-N,--no-dynamic-linker,--build-id=none",
"-Crelocation-model=static"
]
32 changes: 10 additions & 22 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,18 @@ jobs:
- name: Install dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y make nasm binutils binutils-aarch64-linux-gnu
sudo apt-get install -y curl make nasm qemu-user-static \
gcc-x86-64-linux-gnu binutils-x86-64-linux-gnu \
gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu
curl https://sh.rustup.rs -sSf | sh -s -- -y

- name: Build via Makefile
run: make ci_build
- name: Build and Test
run: |
. "$HOME/.cargo/env"
make -s ci_tests

- name: Public artifact
uses: actions/upload-artifact@v1
- name: Publish artifact
uses: actions/upload-artifact@v4
with:
name: Build Artifact
path: out/

test:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout Repo
uses: actions/checkout@v2

- name: Install dependencies
run: |
sudo apt-get update -y
sudo apt-get install -y make nasm binutils binutils-aarch64-linux-gnu
sudo wget https://github.com/multiarch/qemu-user-static/releases/download/v7.1.0-2/qemu-aarch64-static -O /usr/sbin/qemu-aarch64-static
sudo chmod +x /usr/sbin/qemu-aarch64-static

- name: Run Tests
run: make ci_tests
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.idea
out/
target/
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[workspace]
resolver = "2"
members = ["rs-nap"]

[profile.release]
strip = true
opt-level = "z"
codegen-units = 1
panic = "abort"
lto = true
15 changes: 3 additions & 12 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,12 @@ ci_build:
x86_64-linux-gnu-ld -m elf_x86_64 -z noseparate-code -z noexecstack --strip-all -o out/nap nap.o && rm nap.o
aarch64-linux-gnu-as nap-aarch64.s -o nap-aarch64.o
aarch64-linux-gnu-ld -z noseparate-code -z noexecstack --strip-all -o out/nap-aarch64 nap-aarch64.o && rm nap-aarch64.o
cargo build --release --target-dir target --target x86_64-unknown-linux-gnu && cp target/x86_64-unknown-linux-gnu/release/rs-nap out/rs-nap-x86_64
cargo build --release --target-dir target --target aarch64-unknown-linux-gnu && cp target/aarch64-unknown-linux-gnu/release/rs-nap out/rs-nap-aarch64
ls -la out/

ci_tests: ci_build
if [ $$(arch) = "x86_64" ]; then \
echo "[x86_64] Testing 1s nap"; \
timeout 3s out/nap 1 2>&1 > /dev/null; \
echo "[x86_64] Testing 10s default/bad input nap"; \
timeout 12s out/nap bad_arg ; if [ $$? = "1" ]; then true; else false; fi; \
else \
echo "x86_64 testing not available on aarch64 platform"; \
fi
echo "[aarch64] Testing 1s nap"
timeout 3s qemu-aarch64-static out/nap-aarch64 1 2>&1 > /dev/null
echo "[aarch64] Testing 10s default/bad input nap"
timeout 12s qemu-aarch64-static out/nap-aarch64 bad_arg ; if [ $$? = "1" ]; then true; else false; fi
@bash run-tests.sh

tests: install
if [ $$(arch) = "x86_64" ]; then \
Expand Down
7 changes: 7 additions & 0 deletions rs-nap/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions rs-nap/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
[package]
name = "rs-nap"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
60 changes: 60 additions & 0 deletions rs-nap/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Rust rewrite

## Goals

### Feature parity

- [x] Maintain roughly the same binary size
- [x] Zero requirements other than the linux kernel
- [x] Increased readability over assembly
- [x] Shared code base for all architectures

### Architecture support

- [x] x86_64 implementation
- [x] aarch64 implementation

#### Architecture specific implementation notes

For each new architecture:

1. Add a file to the `support` subfolder which implements
- The startup code (`_start`) to get arguments from the stack
- and the following linux kernel service calls
- `sys_exit` to call `exit`
- `sys_write` to call `write` to stdout 1
- and `sys_sleep`
2. Add a platform specific entry to `support.rs` in the form of a `cfg_attr`
3. Add a platform target to `.cargo/config.toml`
4. Add a toolchain target to `rust-toolchain.toml`
5. Update this `README.md` with the new platform build and run commands
6. Update any CI/CD (future) to build the binary for the target platform

## Run on a native processor

```sh
# Build
cargo build --release
# Display file size
ls -lah ./target/release/rs-nap
# Run for 3 seconds
./target/release/rs-nap 3
```

## Run on an emulated processor

```sh
# Install cross compiler and emulation layer

# Debian-based distros
sudo apt -y install gcc-aarch64-linux-gnu binutils-aarch64-linux-gnu qemu-user-static
```

```sh
# Build
cargo build --target aarch64-unknown-linux-gnu --release
# Display file size
ls -lah target/aarch64-unknown-linux-gnu/release/rs-nap
# Run for 3 seconds
qemu-aarch64-static target/aarch64-unknown-linux-gnu/release/rs-nap 3
```
53 changes: 53 additions & 0 deletions rs-nap/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
#![no_std]
#![no_main]
#![feature(naked_functions)]

mod support;
use support::*;
use core::slice::from_raw_parts as mkslice;

#[no_mangle]
pub unsafe fn nap(args: &[*const u8]) -> ! {
let mut sleep_time:usize = 10;
let mut good_input:bool = false;

if args.len() > 1 {
(sleep_time, good_input) = get_sleep_time(args[1]);
}

sleep(sleep_time, good_input);

print_str(b"Done!\n");
sys_exit(0)
}

pub unsafe fn sleep(mut sleep_time: usize, good_input: bool) {
if good_input == false {
sleep_time = 10;
print_str(b"Bad input. ");
}

print_str(b"Sleeping for ");
print_num(sleep_time);
print_str(b" seconds...\n");

sys_sleep(sleep_time);
}

unsafe fn get_sleep_time(arg: *const u8) -> (usize, bool) {
let (seconds,_) = from_radix_10(mkslice(arg, strlen(arg)));
(seconds, seconds > 0 && seconds < 1000000000)
}

#[no_mangle]
unsafe fn get_args(stack_top: *const u8) {
let argc = *(stack_top as *const usize);
let argv = stack_top.add(8) as *const *const u8;
let args = mkslice(argv, argc as usize);
nap(args)
}

#[panic_handler]
unsafe fn my_panic(_info: &core::panic::PanicInfo) -> ! {
sys_exit(255)
}
8 changes: 8 additions & 0 deletions rs-nap/src/support.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#[cfg_attr(all(target_arch="x86_64", target_os="linux"), path="support/x86_64.rs")]
#[cfg_attr(all(target_arch="aarch64", target_os="linux"), path="support/aarch64.rs")]
mod support;
pub use support::*;

#[path="support/noarch.rs"]
mod noarch;
pub use noarch::*;
47 changes: 47 additions & 0 deletions rs-nap/src/support/aarch64.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
use core::arch::{asm,naked_asm};

mod interop;
use interop::timespec;

#[no_mangle]
#[naked]
unsafe extern "C" fn _start() {
// Move the stack pointer before it gets clobbered
naked_asm!(
"mov fp, sp",
"mov x0, fp",
"bl get_args"
)
}

pub unsafe fn sys_exit(exit_code:usize) -> ! {
asm!("svc 0",
in("w8") 93,
in("x0") exit_code,
options(nostack, noreturn)
)
}

pub unsafe fn sys_write(buffer: *const u8, count: usize) {
asm!("svc #0",
inout("x0") 1 => _,
inout("x1") buffer => _,
inout("x2") count => _,
inout("x8") 64 => _,
options(nostack)
)
}

pub unsafe fn sys_sleep(seconds: usize) {
let sleep_time = timespec {
tv_sec: seconds as isize,
tv_nsec: 0
};

asm!("svc 0",
in("x0") &sleep_time,
in("x1") 0,
in("x8") 101,
options(nostack, preserves_flags)
)
}
5 changes: 5 additions & 0 deletions rs-nap/src/support/interop.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#[repr(C)]
pub struct timespec {
pub tv_sec: isize,
pub tv_nsec: isize,
}
65 changes: 65 additions & 0 deletions rs-nap/src/support/noarch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
use crate::support::sys_write;

pub unsafe fn strlen(mut s: *const u8) -> usize {
let mut count = 0;
while *s != b'\0' {
count += 1;
s = s.add(1);
}
count
}

pub fn print_str(s: &[u8]) {
unsafe {
sys_write(s.as_ptr(), s.len());
}
}

pub fn print_num(n: usize) {
if n > 9 {
print_num(n / 10);
}
let c = b'0' + (n % 10) as u8;
print_str(&[c]);
}

fn nth(n: u8) -> usize
{
let mut i:usize = 0;
for _ in 0..n {
i = i + 1;
}
i
}

pub fn ascii_to_digit(character: u8) -> Option<usize> {
match character {
b'0' => Some(nth(0)),
b'1' => Some(nth(1)),
b'2' => Some(nth(2)),
b'3' => Some(nth(3)),
b'4' => Some(nth(4)),
b'5' => Some(nth(5)),
b'6' => Some(nth(6)),
b'7' => Some(nth(7)),
b'8' => Some(nth(8)),
b'9' => Some(nth(9)),
_ => None,
}
}

pub fn from_radix_10(text: &[u8]) -> (usize, usize) {
let mut index:usize = 0;
let mut number:usize = 0;
while index != text.len() {
if let Some(digit) = ascii_to_digit(text[index]) {
number *= nth(10);
number += digit;
index += 1;
} else {
break;
}
}
(number, index)
}

Loading