• Stars
    star
    765
  • Rank 59,372 (Top 2 %)
  • Language
    Rust
  • License
    Other
  • Created about 1 year ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Rust programs written entirely in Rust

Eyra

Rust programs written entirely in Rust

Github Actions CI Status zulip chat crates.io page docs.rs docs

Eyra is a package that supports building Rust programs implemented entirely in Rust.

It uses Origin for program and thread startup and shutdown, and c-gull for ABI-compatible libc function implementations. It currently works on Nightly Rust on Linux on x86-64, x86, aarch64, and riscv64.

Quick start

Running a Rust program under Eyra needs two steps. First, a Cargo.toml dependency, which we can add with:

cargo add eyra --rename=std

And, a build.rs file to add -nostartfiles to the link flags to disable the host startup code, so that Eyra can provide its own. build.rs:

fn main() {
    println!("cargo:rustc-link-arg=-nostartfiles");
}

With that, cargo build, cargo run, cargo test (with Nightly) and so on will work normally with any *-unknown-linux-gnu* target.

Under the covers, it's using Origin to start and stop the program, c-ward to handle libc calls from std, and rustix to do the printing, so it's completely implemented in Rust.

Examples

For an example of the above steps, check out this hello world example.

Other examples include

Why?

Why use Eyra?

  • It fixes Rust's set_var unsoundness issue. The environment-variable implementation leaks memory internally (it is optional, but enabled by default), so setenv etc. are thread-safe.

  • Whole-program LTO, including the libc. This sometimes produces smaller static binaries, and sometimes produces faster code (though on the other hand, sometimes it doesn't, though on the first hand, there are still low-hanging fruit, so consider trying it and filing issues).

    For even more code-size reductions, see the techniques in the hello-world-small example.

  • Support for compiling programs with alternate calling conventions, using Eyra and -Zbuild-std to build a program completely from source.

  • Fully static linking that supports the platform NSS/DNS config. "Is such a thing even possible?", "Yes it is."

  • Or, bring your own reason! Be creative and do your own thing, and tell us about it!

Why not use Eyra?

  • It's not as mature as the major libc implementations.

  • It's not as complete as the major libc implementations. It can run most Rust code, and some popular C libraries, but still lacks a lot of things used by typical C code.

  • It currently depends on Rust Nightly and only runs on Linux, and currently only on x86-64, x86, aarch64, and riscv64.

  • It can't currently run under Miri because Miri doesn't currently recognize syscalls made from assembly code. That said, Eyra does strive to adhere to strict provenance and to avoid undefined behavior throughout, so if Miri were to gain support for such syscalls, Eyra should be well-positioned.

  • No support for dynamic linking.

It might seem like "memory safety" might be a reason to use Eyra, and Eyra does have a lot of code written in safe Rust, so it does benefit some from Rust's memory safety. However, Eyra also has a lot of unsafe code (it's unavoidable for implementing a libc). Until this code has been more throughly proven, it's not realistic to consider it more safe than mature C code.

Fully static linking

Eyra executables don't depend on any dynamic libraries, however by default they do still depend on a dynamic linker (eg. "/lib64/ld-linux-x86-64.so.2").

For fully static linking, there are currently two options:

  • Build with RUSTFLAGS=-C target-feature=+crt-static -C relocation-model=static. This disables Position-Independent Executable (PIE) mode, which is straightforward, however it loses the security benefits of Address-Space Layout Randomization (ASLR).

  • Build with RUSTFLAGS=-C target-feature=+crt-static and enable the experimental-relocate feature. This allows PIE mode and ASLR to work, however it does so by enabling an experimental implementation of relocations. This code seems to be working in practice so far, however it involves Rust code patching itself as it runs, which is outside of any Rust semantics.

Optional logging

Eyra has a log feature to enable Rust log tracing of program and thread startup and shutdown, and an env_logger feature to install env_logger as the logger, which can be enabled in Cargo.toml:

[dependencies]
std = { package = "eyra", version = "<current-version>", features = ["log", "env_logger"] }

With this, and setting the RUST_LOG environment variable to "trace", the hello world program output like this:

[TRACE origin::program] Program started
[TRACE origin::thread] Main Thread[51383] initialized
[TRACE origin::program] Calling `.init_array`-registered function `0x55e86306bb80(1, 0x7ffd0f76aad8, 0x7ffd0f76aae8)`
[TRACE origin::program] Calling `origin_main(1, 0x7ffd0f76aad8, 0x7ffd0f76aae8)`
Hello, world!
[TRACE origin::program] `origin_main` returned `0`
[TRACE origin::thread] Thread[51383] calling `at_thread_exit`-registered function
[TRACE origin::thread] Thread[51383] calling `at_thread_exit`-registered function
[TRACE origin::program] Program exiting with status `0`

Compatibility with -Zbuild-std

Eyra works with -Zbuild-std, however the --rename=std trick used above doesn't work, so it's necessary to instead use this cargo add invocation:

cargo add eyra

and to also add this line to the program's main.rs file:

extern crate eyra;

to ensure that the Eyra libraries are linked in.

Reducing code size

Eyra can be used with the techniques in min-sized-rust to produce very small statically-linked binaries. Check out the hello-world-small example.

Relationship to Mustang

Eyra is similar to Mustang and uses the same underlying code, but instead of using a custom target and -Z build-std, Eyra just needs users to add -nostartfiles to their link line, such as via build.rs in the example.

Like Mustang, Eyra currently runs on Nightly Rust on Linux on x86-64, x86, aarch64, and riscv64. It aims to support all Linux versions supported by Rust, though at this time it's only tested on relatively recent versions. It's complete enough to run:

Compiling C programs

Eyra can also be compiled into a libc.a that can be used to compile C programs; see the eyra-c repository.

Design philosophy

Eyra and the libraries it uses have some design goals.

Normal Rust, all the way down

Sometimes in libc implementation code, there's a temptation to say "it's ok if some things are technically Undefined Behavior, because this is Low Level Code and We Know What We're Doing".

Origin, c-scape, c-gull, rustix, and the others strive to resist this temptation, and follow the Rust rules, including strict provenance, I/O safety, and all the rest, all the way down to the syscalls.

It's just normal Rust code, as far down as we can go in userspace, and when we eventually do have to switch to inline asm, we do as little of it as we can.

Currently there is only one known place where this goal is not achieved. In a "static PIE" executable (eg. built with RUSTFLAGS="-C target-feature=+crt-static"), the dynamic linker isn't used, so the executable has to handle all its relocations itself. However, that means storing to memory locations that wouldn't otherwise be considered mutable. Origin's code for doing this is currently disabled by default, and can be enabled with the "experimental-relocate" cargo feature.

C compatibility as a layer on top of Rust, not vice versa

Eyra is built on a collection of Rust crates with idiomatic Rust APIs, and two crates, c-scape and c-gull, which are relatively thin layers on top that implement the libc-compatible C ABI.

It's sometimes more work to write the code as separate layers like this, but it has the advantage of clearly separating out the unsafe associated with things like C pointers and strings in libc APIs from the essential unsafe needed to implement things like system calls, thread primitives, and other features. And it means that Rust programs that don't want to go through the C compatibility layer can use the underlying crates directly.

More Repositories

1

mustang

Rust programs written entirely in Rust
Rust
828
star
2

wasm-reference-manual

WebAssembly Reference Manual
564
star
3

c-ward

An implementation of libc written in Rust
Rust
199
star
4

origin

Program startup and thread support written in Rust
Rust
166
star
5

io-lifetimes

A low-level I/O ownership and borrowing library
Rust
94
star
6

hello-world-vs-io-errors

A repository about "Hello, World" programs and I/O errors
62
star
7

hello-wasi-http

Rust
57
star
8

nameless

Full-service command-line parsing
Rust
52
star
9

is-terminal

Test whether a given stream is a terminal
Rust
49
star
10

linux-raw-sys

Generated bindings for Linux's userspace API
Rust
39
star
11

llvm2cranelift

LLVM IR to Cranelift IR translator
Rust
30
star
12

origin-studio

An alternative `std`-like implementation built on origin
Rust
24
star
13

io-streams

Unbuffered and unlocked I/O streams
Rust
24
star
14

world-of-wasi-demo

A demo for my talk The World of WASI
Rust
21
star
15

hello-embedded

Hello Embedded!
Rust
21
star
16

io-extras

Non-owning unsafe I/O
Rust
20
star
17

ambient-authority

Ambient Authority
Rust
19
star
18

duplex

The Duplex trait: interactive streams
Rust
17
star
19

atomic-dbg

Atomic `dbg`/`eprintln`/`eprint` macros
Rust
17
star
20

eyra-c

Support for compiling C programs with Eyra
Rust
13
star
21

socketpair

Cross-platform socketpair functionality
Rust
10
star
22

office-hours

Open office hours
10
star
23

wasm-collection

A collection of miscellaenous valid wasm modules
8
star
24

mir2cranelift

Rust MIR to Cranelift IL translator (inactive; soon to be replaced)
Rust
8
star
25

rustix-futex-sync

Linux futex-based synchronization
Rust
8
star
26

utf8-io

Traits and types for UTF-8 I/O
Rust
6
star
27

io-arrays

Random-access I/O
Rust
6
star
28

basic-text

Basic Text strings and I/O streams
Rust
5
star
29

rustix-openpty

Rust
5
star
30

open-ambient

Open files and directories with constant paths
Rust
4
star
31

winx

A collection of miscellaneous Windows API utilities
Rust
4
star
32

linux-mount-api-documentation

Formatted copies of the Linux mount API manual page drafts
3
star
33

rustix-linux-procfs

Utilities for opening Linux procfs files and directories
Rust
3
star
34

terminal-io

Utilities for reading and writing on terminals
Rust
3
star
35

rustix-is-terminal

Test whether a given stream is a terminal, using rustix
Rust
2
star
36

dir-view

Views of cap-std's Dir
Rust
2
star
37

rustix-testing

Extra testing for rustix
Rust
2
star
38

cap-std-testing

Extra testing for cap-std
Rust
2
star
39

rustix-bench

Rust
2
star
40

char-device

Character Device I/O
Rust
2
star
41

nuttx-sys

Raw bindings to NuttX APIs
Rust
2
star
42

wasi-filesystem.wit

1
star
43

wasi-preview2

WASI Preview2 Proposal
1
star
44

intercomm

experiments in passing handles to std::process::Command
Rust
1
star
45

wasi-filesystem-presentation

1
star
46

codeexpr

Rust
1
star
47

windows-hello

Rust
1
star
48

wasi-random-presentation

Rust
1
star
49

layered-io

I/O traits extending Read and Write
Rust
1
star
50

annotated-types

An experiment which may turn into a WASI proposal
1
star
51

wasi-common-test

Rust
1
star
52

import-export-demo

Rust
1
star
53

sunfishcode.github.io

HTML
1
star
54

wasi-wiki

A Wiki for WASI designs
1
star