Skip to content

Upgrade to rustc 1.30-nightly (2018-08-30) #2

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 21 commits into
base: master
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
3 changes: 3 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[build]
rustflags = ["-C", "relocation-model=static"]
incremental = false
20 changes: 20 additions & 0 deletions .cirrus.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
task:
name: Linux amd64 nightly
container:
image: rustlang/rust:nightly
cpu: 1
memory: 1
cargo_cache:
folder: $HOME/.cargo/registry
fingerprint_script: cat Cargo.lock 2> /dev/null || true
setup_script:
- apt-get update
- apt-get install -yqq nasm bsdmainutils
build_script:
- ./build.sh
test_script:
- ./tinyrust
- test "$(./tinyrust)" == 'Hello!'
- test "$(wc -c < tinyrust)" -eq 137
before_cache_script:
- rm -rf $HOME/.cargo/registry/index
15 changes: 12 additions & 3 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
# cargo
/target

# pre-cargo
/syscall.rs
/libtinyrust.rlib

# rlib extraction and other intermediate artefacts
/*.o
/*.rmeta
/payload
/payload.bin
/tinyrust
/tinyrust.ll
/tinyrust.o
/libtinyrust.rlib

# final executable
/tinyrust
7 changes: 0 additions & 7 deletions COPYRIGHT

This file was deleted.

16 changes: 16 additions & 0 deletions Cargo.lock

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

30 changes: 30 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "tiny-rust-executable"
version = "0.5.1"
authors = [
"Keegan McAllister <mcallister.keegan@gmail.com>",
"Melvin Kicchi <melvin@likes.pounc.es>",
"Hiroki Noda <kubo39@gmail.com>",
"Torbjørn Birch Moltu <t.b.moltu@lyse.net>"
]
license = "Apache-2.0 OR MIT"
edition = "2015"

[lib]
name = "tinyrust"
path = "tinyrust.rs"

[profile.dev]
opt-level = "z"
overflow-checks = false
lto = false
panic = "abort"

[profile.release]
opt-level = "z"
overflow-checks = false
lto = false # enabling LTO breaks objdump and requires other flags to ld
panic = "abort"

[dependencies]
sc = "0.2.3"
2 changes: 0 additions & 2 deletions LICENSE-MIT
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
Copyright (c) 2014 The syscall.rs Project Developers

Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Expand Down
117 changes: 77 additions & 40 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,61 +1,98 @@
# Using Rust to make a 151-byte static AMD64 Linux binary
# Using Rust to make a 137-byte static AMD64 Linux binary

`elf.s` contains a custom ELF header, but no instructions. All of the machine
code comes out of `rustc`. (Although all of the instructions that survive
optimization *went in* as inline assembly.)
[![Build Status](https://api.cirrus-ci.com/github/tormol/tiny-rust-executable.svg)](https://cirrus-ci.com/github/tormol/tiny-rust-executable)

```
$ ./build.sh
Tested on rustc 1.0.0-dev (44a287e6e 2015-01-08 17:03:40 -0800)
You have rustc 1.0.0-dev (44a287e6e 2015-01-08 17:03:40 -0800)
`elf.s` contains a custom ELF header, but no instructions.
All of the machine code comes out of `rustc`.
(While most of the operations originate from inline assembly, LLVM replaces the instructions with more compact ones!)

Cloning into 'syscall.rs'...
Compiling syscall v0.1.0 (file:///home/keegan/tiny-rust-demo/syscall.rs)
This project is built by running the `./build.sh` script instead of `cargo build`: Making the binary this small requires post-processing steps which cargo doesn't support.
Requires nightly Rust because it uses inline assembly (through the `sc` crate) to make direct system calls.

+ rustc tinyrust.rs -O -C no-stack-check -C relocation-model=static -L syscall.rs/target
+ ar x libtinyrust.rlib tinyrust.o
+ objdump -dr tinyrust.o
[Blog post about this demo](http://mainisusuallyafunction.blogspot.com/2015/01/151-byte-static-linux-binary-in-rust.html), by Keegan McAllister who originally created it.

tinyrust.o: file format elf64-x86-64
## Example

```sh
$ ./build.sh
Tested on rustc 1.85.0 (4d91de4e4 2025-02-17)
You have rustc 1.85.0 (4d91de4e4 2025-02-17)

+ cargo build --release --verbose
Updating crates.io index
Locking 1 package to latest compatible version
Downloaded sc v0.2.7
Downloaded 1 crate (39.6KiB) in 0.88s
Compiling sc v0.2.7
Running `/home/tbm/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/rustc --crate-name sc --edition=2015 /home/tbm/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/sc-0.2.7/src/lib.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=137 --crate-type lib --emit=dep-info,metadata,link -C opt-level=z -C panic=abort -C embed-bitcode=no --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=de0ba5d176876c9c -C extra-filename=-e50ec66b37f4e2cb --out-dir /home/tbm/p/rust/tiny-rust-demo/target/release/deps -C strip=debuginfo -L dependency=/home/tbm/p/rust/tiny-rust-demo/target/release/deps --cap-lints allow -C relocation-model=static`
Compiling tiny-rust-executable v0.5.1 (/home/tbm/p/rust/tiny-rust-demo)
Running `/home/tbm/.rustup/toolchains/stable-x86_64-unknown-linux-gnu/bin/rustc --crate-name tinyrust --edition=2015 tinyrust.rs --error-format=json --json=diagnostic-rendered-ansi,artifacts,future-incompat --diagnostic-width=137 --crate-type lib --emit=dep-info,metadata,link -C opt-level=z -C panic=abort -C embed-bitcode=no --check-cfg 'cfg(docsrs,test)' --check-cfg 'cfg(feature, values())' -C metadata=76aa331090f20463 -C extra-filename=-f769982e789cd78c --out-dir /home/tbm/p/rust/tiny-rust-demo/target/release/deps -C strip=debuginfo -L dependency=/home/tbm/p/rust/tiny-rust-demo/target/release/deps --extern sc=/home/tbm/p/rust/tiny-rust-demo/target/release/deps/libsc-e50ec66b37f4e2cb.rmeta -C relocation-model=static`
Finished `release` profile [optimized] target(s) in 0.06s
++ ar t target/release/libtinyrust.rlib
++ grep '.o$'
+ OBJECT=tinyrust-f769982e789cd78c.tinyrust.35d04aaf99cb8b97-cgu.0.rcgu.o
+ ar x target/release/libtinyrust.rlib tinyrust-f769982e789cd78c.tinyrust.35d04aaf99cb8b97-cgu.0.rcgu.o
+ objdump -dr tinyrust-f769982e789cd78c.tinyrust.35d04aaf99cb8b97-cgu.0.rcgu.o

tinyrust-f769982e789cd78c.tinyrust.35d04aaf99cb8b97-cgu.0.rcgu.o: file format elf64-x86-64


Disassembly of section .text.main:

0000000000000000 <main>:
0: b8 01 00 00 00 mov $0x1,%eax
5: bf 01 00 00 00 mov $0x1,%edi
a: be 08 00 40 00 mov $0x400008,%esi
f: ba 07 00 00 00 mov $0x7,%edx
14: 0f 05 syscall
16: b8 3c 00 00 00 mov $0x3c,%eax
1b: 31 ff xor %edi,%edi
1d: 0f 05 syscall
0: 6a 01 push $0x1
2: 58 pop %rax
3: 6a 07 push $0x7
5: 5a pop %rdx
6: be 08 00 40 00 mov $0x400008,%esi
b: 48 89 c7 mov %rax,%rdi
e: 0f 05 syscall
10: 6a 3c push $0x3c
12: 58 pop %rax
13: 31 ff xor %edi,%edi
15: 0f 05 syscall
17: 0f 0b ud2
+ echo

+ ld --gc-sections -e main -T script.ld -o payload tinyrust.o
+ ld --gc-sections -e main -T script.ld -o payload tinyrust-f769982e789cd78c.tinyrust.35d04aaf99cb8b97-cgu.0.rcgu.o
+ objcopy -j combined -O binary payload payload.bin
++ nm -f posix payload
++ nm --format=posix payload
++ grep '^main '
++ awk '{print $3}'
+ ENTRY=0000000000400078
+ set -x
+ nasm -f bin -o tinyrust -D entry=0x0000000000400078 elf.s
+ ENTRY=400070
+ nasm -f bin -o tinyrust -D entry=0x400070 elf.s
+ chmod +x tinyrust
+ hd tinyrust
00000000 7f 45 4c 46 02 01 01 00 48 65 6c 6c 6f 21 0a 00 |.ELF....Hello!..|
00000010 02 00 3e 00 01 00 00 00 78 00 40 00 00 00 00 00 |..>.....x.@.....|
00000020 40 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |@...............|
00000030 00 00 00 00 40 00 38 00 01 00 00 00 00 00 00 00 |....@.8.........|
00000040 01 00 00 00 07 00 00 00 00 00 00 00 00 00 00 00 |................|
00000050 00 00 40 00 00 00 00 00 00 00 40 00 00 00 00 00 |..@.......@.....|
00000060 97 00 00 00 00 00 00 00 97 00 00 00 00 00 00 00 |................|
00000070 00 10 00 00 00 00 00 00 b8 01 00 00 00 bf 01 00 |................|
00000080 00 00 be 08 00 40 00 ba 07 00 00 00 0f 05 b8 3c |.....@.........<|
00000090 00 00 00 31 ff 0f 05 |...1...|
00000097
+ hexdump -C tinyrust
00000000 7f 45 4c 46 02 01 01 09 48 65 6c 6c 6f 21 0a 00 |.ELF....Hello!..|
00000010 02 00 3e 00 01 00 00 00 70 00 40 00 00 00 00 00 |..>.....p.@.....|
00000020 38 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |8...............|
00000030 00 00 00 00 38 00 38 00 01 00 00 00 07 00 00 00 |....8.8.........|
00000040 00 00 00 00 00 00 00 00 00 00 40 00 00 00 00 00 |..........@.....|
00000050 00 00 40 00 00 00 00 00 89 00 00 00 00 00 00 00 |..@.............|
00000060 89 00 00 00 00 00 00 00 00 10 00 00 00 00 00 00 |................|
00000070 6a 01 58 6a 07 5a be 08 00 40 00 48 89 c7 0f 05 |j.Xj.Z...@.H....|
00000080 6a 3c 58 31 ff 0f 05 0f 0b |j<X1.....|
00000089
+ wc -c tinyrust
151 tinyrust
137 tinyrust

$ ./tinyrust
Hello!
```

## License

Licensed under either of

* Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

## See Also

* [A 99-byte x86 (32-bit) Go executable](https://github.com/xaionaro-go/tinyhelloworld)
28 changes: 11 additions & 17 deletions build.sh
Original file line number Diff line number Diff line change
@@ -1,38 +1,32 @@
#!/bin/bash
#!/usr/bin/env bash

set -e

for d in rustc git cargo ar ld objcopy nasm; do
for d in rustc cargo ar ld objcopy nasm hexdump; do
which $d >/dev/null || (echo "Can't find $d, needed to build"; exit 1)
done

printf "Tested on rustc 1.1.0-dev (435622028 2015-05-04)\nYou have "
printf "Tested on rustc 1.85.0 (4d91de4e4 2025-02-17)\nYou have "
rustc --version
echo

if [ ! -d syscall.rs ]; then
git clone https://github.com/kmcallister/syscall.rs
(cd syscall.rs && cargo build --release)
echo
fi

set -x

rustc tinyrust.rs \
-O -C no-stack-check -C relocation-model=static \
-L syscall.rs/target/release
cargo build --release --verbose

ar x libtinyrust.rlib tinyrust.o
# tinyrust.tinyrust.3a1fbbbh-cgu.0.rcgu.o
OBJECT=$(ar t target/release/libtinyrust.rlib | grep '.o$')
ar x target/release/libtinyrust.rlib "$OBJECT"

objdump -dr tinyrust.o
objdump -dr "$OBJECT"
echo

ld --gc-sections -e main -T script.ld -o payload tinyrust.o
ld --gc-sections -e main -T script.ld -o payload "$OBJECT"
objcopy -j combined -O binary payload payload.bin

ENTRY=$(nm -f posix payload | grep '^main ' | awk '{print $3}')
ENTRY=$(nm --format=posix payload | grep '^main ' | awk '{print $3}')
nasm -f bin -o tinyrust -D entry=0x$ENTRY elf.s

chmod +x tinyrust
hd tinyrust
hexdump -C tinyrust
wc -c tinyrust
6 changes: 2 additions & 4 deletions elf.s
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

ehdr:
db 0x7f, "ELF" ; magic
db 2, 1, 1, 0 ; 64-bits, little endian, version 1
db 2, 1, 1, 9 ; 64-bits, little endian, version 9(FreeBSD, linux loader ignores this)

; This padding is a perfect place to put a string constant!
db "Hello!", 0x0A, 0
Expand All @@ -20,13 +20,11 @@ ehdr:
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 1 ; e_phnum
dw 0, 0, 0 ; e_sh*

ehdrsize equ $ - ehdr

phdr:
dd 1 ; p_type = loadable program segment
dd 1 ; p_type = loadable program segment & (e_phnum + e_sh*)
dd 7 ; p_flags = rwx
dq 0 ; p_offset
dq $$, $$ ; p_vaddr, p_paddr
Expand Down
4 changes: 2 additions & 2 deletions script.ld
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
SECTIONS {
. = 0x400078;
. = 0x400070;

combined . : AT(0x400078) ALIGN(1) SUBALIGN(1) {
combined . : AT(0x400070) ALIGN(1) SUBALIGN(1) {
*(.text*)
*(.data*)
*(.rodata*)
Expand Down
16 changes: 5 additions & 11 deletions tinyrust.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
#![crate_type="rlib"]
#![feature(core)]
#![no_std]

#[macro_use] extern crate syscall;
#[macro_use] extern crate sc;

use std::{mem, raw, intrinsics};
use core::{hint,slice};

fn exit(n: usize) -> ! {
unsafe {
syscall!(EXIT, n);
intrinsics::unreachable()
hint::unreachable_unchecked()
}
}

Expand All @@ -22,12 +21,7 @@ fn write(fd: usize, buf: &[u8]) {
pub fn main() {
// Make a Rust value representing the string constant we stashed
// in the ELF file header.
let message: &'static [u8] = unsafe {
mem::transmute(raw::Slice {
data: 0x00400008 as *const u8,
len: 7,
})
};
let message = unsafe{ slice::from_raw_parts(0x00400008 as *const u8, 7) };

write(1, message);
exit(0);
Expand Down