
151-byte static Linux binary in Rust - adito
http://mainisusuallyafunction.blogspot.com/2015/01/151-byte-static-linux-binary-in-rust.html
======
Alupis
reminds me a bit of the "Creating Really Teensy ELF Executables" writeup[1],
which got down to 45 bytes.

But honestly... does it really matter how small your executable is? I'd care a
lot more about performance characteristics than binary size.

[1]
[http://www.muppetlabs.com/~breadbox/software/tiny/teensy.htm...](http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html)

~~~
dbaupp
Cache Rules Everything Around Me, and bloated executables generally put more
stress on the instruction cache, resulting in poorer performance.

Of course, this is just an approximate general rule, and extremely small
binaries (and generally any "pointless" experimentation) like this are mainly
interesting from a learning point of view (or for small embedded
microcontrollers), e.g. understanding syscalls and program start-up, and, for
the write-up you link, the ELF format. (Not everything has to be inherently
useful for an actual task.)

~~~
semi-extrinsic
I'm going to have to steal your opening quote, for times when I have to keep
it real*8. Source?

~~~
dbaupp
No idea where I first heard it; I have a feeling it was on IRC from the author
of the article, coincidentally.

------
swetland
This is cute, but is there a way to actually create static binaries with the
standard tooling and not jumping through hoops?

Hello World in Rust with tip of tree (as of yesterday) is a ~500K binary that
depends on glibc and friends.

Switching from println!() to std::io...write_str() actually made it larger!

~~~
steveklabnik
You can't static link glibc, so our static binaries still have a dynamic
dependency on it.

~~~
swetland
Is there a _process_ for making a fully static binary (say if I were to
substitute an alternate libc, or provide my own low level wrappers for
syscalls, or whatnot)?

~~~
steveklabnik
Yes, that's totally possible. You can write a kernel in Rust if you want.

~~~
tdrd
...is it documented anywhere? where?

~~~
steveklabnik
[http://doc.rust-lang.org/book/unsafe.html#avoiding-the-
stand...](http://doc.rust-lang.org/book/unsafe.html#avoiding-the-standard-
library) is the closest we have, currently. [https://github.com/rust-
lang/rust/wiki/Operating-system-deve...](https://github.com/rust-
lang/rust/wiki/Operating-system-development) has a bunch of stuff, and
[https://github.com/charliesome/rustboot](https://github.com/charliesome/rustboot)
and its forks are examples that I know of.

~~~
swetland
The example minimal do-nothing program there is much smaller (8k), but still
results in a dynamic binary requiring glibc.

The only unresolved symbol is: __libc_start_main@@GLIBC_2.2.5

------
yazaddaruvala
I'd actually love to ask Linus' on his opinion of the practical uses of Rust
in the Linux kernel. Does he think it could have a place, if not, why? Can
those issues be addressed, etc?

Even if that means him dismissing me/my question entirely lol.

~~~
0xFFC
Rust seems very promising language , but why on earth should any one use rust
in kernel development ? as far as I see this link is just calling write system
call from rust program (which is very good) , but don't have anything to do
with linus or kernel internal. and if you mean some thing like scripting in
kernel ( i think openbsd ddne it with lua ) there is more viable and simpler
choice than rust , like lua.

~~~
jnbiche
> but why on earth should any one use rust in kernel development

Because that's explicitly one of the things Rust was developed for.

Here's a whole list of kernels and other OS projects that have been created
using Rust:

[https://github.com/rust-lang/rust/wiki/Operating-system-
deve...](https://github.com/rust-lang/rust/wiki/Operating-system-development)

------
userbinator
That main() can still be improved... try this:

    
    
        push 1
        pop eax
        mov edi, eax
        mov esi, 400008h
        push 7
        pop edx
        syscall
        push 3ch
        pop eax
        xor edi, edi
        syscall
    

It should be 10 bytes shorter.

~~~
ekimekim
Yes, but the point was that it could be done in rust, using rust's language
features (type safety and so forth). Making your main inline assembly isn't
really using the language very much.

~~~
omega_rythm
You really should have a look at the rust code this guy wrote. I would not
call that last optimization type safe.

~~~
the_mitsuhiko
How so? The only unsafe part is the transmute which only saves 7 bytes anyways
:P

~~~
exprL
Even that is still type safe, just not portable. After all, every part of the
memory is a collection of bytes (that is, a slice of u8) – whether you can
access them is another story.

------
starmole
Wow. This is seriously impressive. Rust looks like it actually might be
capable as a systems programming tool to match C. I have to learn more about
it now! :)

~~~
0xFFC
dude its great , but not that good, at least not yet .

~~~
zxcdw
Care to elaborate why do you think so?

~~~
berkut
Well for one: at least currently, there's a much better chance of having the
c/c++ runtimes on a system than the Rust one I imagine.

~~~
steveklabnik
You don't need a Rust runtime installed, you just need a glibc.

~~~
berkut
Even if you don't statically link Rust's runtime?

If so I can see that working for very simple code, but what about wanting to
use Tasks/Threads or do I/O. Is that converted to native system calls at
compile time?

~~~
andolanra
After a series of recent changes, Rust's runtime has been radically reduced.
I/O goes through system calls, and the threading system is just native OS
threads. There's a very small amount of support needed for the "Rust runtime",
including things like stack guards, but it's much smaller than it was.

The RFC describing the changes is [^1] and the actual commit that finalized
them is [^2].

[^1]: [https://github.com/rust-lang/rfcs/pull/230](https://github.com/rust-
lang/rfcs/pull/230)

[^2]: [https://github.com/rust-
lang/rust/commit/0efafac398ff7f28c5f...](https://github.com/rust-
lang/rust/commit/0efafac398ff7f28c5f0fe756c15b9008b3e0534)

------
tdicola
That is really impressive. Now for comedy, someone find the size of a similar
program in Go.

~~~
Alupis
> Now for comedy, someone find the size of a similar program in Go

As far as I know, Go statically links the runtime into the binary (including
the garbage collector, the scheduling system and more)... so for a small
program like this sample, it will appear to be abnormally bloated. If it were
a larger, more complex example, then the binary size wouldn't change as
drastically (and on a surface level might appear to be more justifiable).

~~~
erglkjahlkh
It is also bad in following dependencies, and links the whole libraries. The
result is like spaghetti, import on library and half of the standard libraries
come with it. Some optimizations have been already done to prevent that, but
the work is going to be still in progress for several versions.

~~~
giovannibajo1
On the other hand, rust has no standard library ("batteries") by explicit
choice, so once the ecosystem will evolve, there would likely be multiple json
or http libraries (just to name "low-level" libraries with widespread usage),
and you might end up linking a few of them in a binary as indirect dependency.

~~~
masklinn
> On the other hand, rust has no standard library

? [http://doc.rust-lang.org/std/](http://doc.rust-lang.org/std/)

> there would likely be multiple json or http libraries (just to name "low-
> level" libraries with widespread usage)

The standard library used to build json in, that was actually split out to a
standalone libraries for various reasons: [https://github.com/rust-lang/rustc-
serialize](https://github.com/rust-lang/rustc-serialize)

> and you might end up linking a few of them in a binary as indirect
> dependency.

That's not a counter-argument to erglkjahlkh's comment. In fact it's pretty
much irrelevant:

* Go works at a library level, when you import fmt you don't import encoding/json, and it's not loaded

* The issue he outlines is mostly that Go does little to no dead code elimination, when a library is imported all of its content (and the content of its dependencies) will end up in the final library, even if you only access a small utility function

~~~
pstuart
> Go does little to no dead code elimination

Yet. With the rewrite of the compiler it is safe to assume that there will be
many optimizations to come.

~~~
masklinn
Sadly no: one of the primary (if not the primary) goal of Go's developers is
compilation speed, and with Go being intrinsically slower than C the post-
rewrite available budget for possible optimisations (whatever the acceptance
threshold they define) will be lower rather than higher.

~~~
pstuart
Perhaps I'm naive, but I don't see why they couldn't have the option of
something like -O3 for those that wanted it.

~~~
masklinn
Because they don't want to.

------
CyberShadow
I did something similar in D/Win32:

[http://forum.dlang.org/post/qcbicxrtmjmwiljsyhdf@forum.dlang...](http://forum.dlang.org/post/qcbicxrtmjmwiljsyhdf@forum.dlang.org)

The PE format is rather bulkier than ELF, though.

------
Keyframe
I just had a look. Installed nightly Rust on OSX and ran through it a hello
world program - 307K! HW in C with gcc 4.2.1 (LLVM) makes 8.7K.

~~~
epdtry
Rust produces statically linked binaries by default, while GCC and Clang
default to dynamic linking. For a better comparison, pass `-static` to
GCC/Clang, or pass `-C prefer-dynamic` to rustc.

On Linux, with dynamic linking:

    
    
      rustc: 8656 bytes
      clang: 6960 bytes
      gcc:   6688 bytes

~~~
swetland
Is it possible to make actual static binaries in rust? By default it compiles
in the rust libraries, but depends on glibc and friends (and is not actually a
static binary) -- at least with tip of tree rust from yesterday.

~~~
Rusky
You can drop the glibc dependency pretty easily. One way (used for a lot of
toy kernels that have been written in Rust) is to add the #![no_std] attribute
and then explicitly pull in any static dependencies you still use (like
libcore).

