News for hackers. Stuff that matters.
Re:Show HN: Tiny 1k Rust binary on Windows by pornel on Tuesday December 01, 2020 @12:51PM (#25267664)
Note that a typical "Hello World" executable written in C does not contain any code capable of actually printing anything. It links to libc shipped with the operating system. That is about 30MB of code that C executables get for free.

You can link libc statically and dead-code-eliminate everything except print. That's what Rust does too, but Rust has fancier machinery for formatting and handling of panics with backtraces. These bits don't get dead-code-eliminated as thoroughly in Rust (probably because the print could theoretically fail).

Re:Show HN: Tiny 1k Rust binary on Windows by colejohnson66 on Tuesday December 01, 2020 @03:47PM (#25269675)
Rant:

And below libc is probably X or Wayland which tells the kernel where to put pixels. And below that is the kernel and graphics driver which figures out how to do that and communicates it to the GPU. And below that is the actual GPU which figures out how to modulate an array of pixels into an HDMI signal. When do we stop quibbling about the size of binaries? It’s pointless to point out that small code relies on other abstractions. No one complains about tiny demo scene programs using tons of other stuff built into the OS. So why here?

Re:Show HN: Tiny 1k Rust binary on Windows by naikrovek on Tuesday December 01, 2020 @11:10PM (#25273156)
Your understanding of computers does not seem correct at all... But maybe it's my understanding that wrong. I don't know.

C64 demos and 8088 DOS demos use very little OS infrastructure. Obviously things like JavaScript demos do.

The challenges presented by a particular piece of hardware or software in the demo scene are chosen for different reasons.

Sometimes, the demo author wants to show how well they can use the existing OS infrastructure. Sometimes they want to show what can be done without it. In both cases, small sizes are impressive.

Re:Show HN: Tiny 1k Rust binary on Windows by pornel on Wednesday December 02, 2020 @06:27AM (#25275435)
I'm not complaining that a Hello World exe doesn't come with its own operating system.

It's just worth knowing when you compare exe sizes that C doesn't have an order of magnitude more efficient code generation. It just had a >30-year head start to get its standard library shipped with every operating system. Other languages typically don't have that advantage, and either have to reuse libc, or take a hit from statically linking their own libstd.

Re:Show HN: Tiny 1k Rust binary on Windows by adkadskhj on Wednesday December 02, 2020 @01:57PM (#25279957)
The entire exercise is an attempt to compare apples to apples.

In your example, both the C bin and Rust bin are using X, GPU, etcetc - so they warrant no distinction. So what is different? What is included in the Rust binary that isn't in the C? Because that's what is being tested in these types of comparisons.

This difference may be because some language included inefficient instructions - has bloated includes, etc. Or it could be because you're comparing Apples to Oranges, eg one ships with libc in the binary, and one dynamically links to it.

Re:Show HN: Tiny 1k Rust binary on Windows by colejohnson66 on Wednesday December 02, 2020 @03:01PM (#25280840)
I’m not attempting to compare apples and oranges. I’m ranting about how almost every time someone posts something about tiny programs (like 100 line JS programs or wherever), someone has to chime in about how they’re able to do something so small because they’re using all these abstractions (code) below them and, as such, it’s not an achievement.

Again: it was just a rant.

Re:Show HN: Tiny 1k Rust binary on Windows by adkadskhj on Wednesday December 02, 2020 @05:22PM (#25282529)
Specifically that your rant is here because the Parent comment mentioned Libc - but this is because the Rust version isn't using that - but the C lib is.

So it's Apples to Oranges because they're fundamentally doing different things, and yet we're comparing their Line Count. That's not only reasonable, it's necessary if you want an Apples to Apples comparison.

That's why we're not concerned about the code to render with GPU or some other junk - but we are fundamentally talking about two programs that do different things - but then comparing their line count.

Your rant may be valid in other cases, but it seems super off base here. Your rant has, imo, nothing to do with what's happening in people pointing out the discrepancy.

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Monday November 30, 2020 @03:26PM (#25258515)
This is my attempt at making the smallest exe with rust for windows using the msvc toolchain. If y'all have any pointers how to get it smaller let me know :)
Re:Show HN: Tiny 1k Rust binary on Windows by pantalaimon on Tuesday December 01, 2020 @07:16AM (#25264521)
I'm disappointed. In C I can get a 1k Hello Word binary simply be writing

    int main(void) {
        puts("Hello World!");
        return 0;
    }
The Rust version is a mess [0]

[0] https://github.com/mcountryman/min-sized-rust-windows/blob/m...

Re:Show HN: Tiny 1k Rust binary on Windows by slezyr on Tuesday December 01, 2020 @08:13AM (#25264798)
You're comparing C+StdLib vs. Rust without StdLib. Disable stdlib for C version and use the following program:

#include <windows.h>

int _start() {

    const char str[] = "Hello world!\n";
    HANDLE stdout = GetStdHandle(STD_OUTPUT_HANDLE);
    DWORD written;

    WriteFile(stdout, str, sizeof(str), &written, NULL);
    ExitProcess(12);

    return 0;
}

https://stackoverflow.com/a/42536990

Which is similar to what the Rust program does. The result should be significantly less than 1k

Re:Show HN: Tiny 1k Rust binary on Windows by ChrisSD on Tuesday December 01, 2020 @11:18AM (#25266511)
I tried your program with MSVC using the following command:

    cl small2.c /MD /link /entry:_start /subsystem:CONSOLE kernel32.lib
It's 5kb.
Re:Show HN: Tiny 1k Rust binary on Windows by slezyr on Tuesday December 01, 2020 @02:45PM (#25269021)
If I remember correctly you need a /nodefaultlib flag to disable stdlibs

https://docs.microsoft.com/en-us/cpp/build/reference/nodefau...

Re:Show HN: Tiny 1k Rust binary on Windows by ChrisSD on Tuesday December 01, 2020 @03:31PM (#25269504)
Ok, I've just tried that but I also needed to add `/force` otherwise it complained about missing symbols. So the full compile line is:

    cl small2.c /link /force /nodefaultlib /entry:_start /subsystem:CONSOLE kernel32.lib
This gets it down to 3kb.
Re:Show HN: Tiny 1k Rust binary on Windows by zamadatix on Tuesday December 01, 2020 @10:09AM (#25265805)
I think the thing being measured by GP is "easiest way to get to the 1kb mentioned in the title" not "most complicated way to get as small as possible".
Re:Show HN: Tiny 1k Rust binary on Windows by ChrisSD on Tuesday December 01, 2020 @11:03AM (#25266338)
Have you actually tried this on MSVC? This is my program. I created a file called `small.c`

    #include <stdio.h>
    
    int main(void) {
        puts("Hello World!");
        return 0;
    }

I then compiled it using this command:

   cl small.c /MD
The result? 9kb! That's 9 times the size.
Re:Show HN: Tiny 1k Rust binary on Windows by m1el on Tuesday December 01, 2020 @08:13AM (#25264802)
Can you, though?

Compiling that source on Windows using visual studio produces a 98K executable.

Re:Show HN: Tiny 1k Rust binary on Windows by userbinator on Tuesday December 01, 2020 @09:36AM (#25265491)
You just need to add some compiler options; /MD should get the size down quite a bit already, then decrease the file alignment and merge sections together:

    /MD /link /align:4096 /filealign:512 /merge:.rdata=.text /merge:.data=.text
That produced a 1.5K binary, which with a custom stub should reach 1K.
Re:Show HN: Tiny 1k Rust binary on Windows by simlevesque on Tuesday December 01, 2020 @09:43AM (#25265561)
So the point is moot, you can't have a simple 1k C example.
Re:Show HN: Tiny 1k Rust binary on Windows by ChrisSD on Tuesday December 01, 2020 @11:10AM (#25266416)
Have you actually tried this? Merging .data into .text doesn't work. The binary doesn't run and there's this link warning:

> LINK : warning LNK4254: section '.data' (C0000040) merged into '.text' (60000020) with different attributes

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @10:57AM (#25266250)
In order to get that to 1Kb you would probably have to drop the `.idata` section and resolve syscalls from the PEB like I did.
Re:Show HN: Tiny 1k Rust binary on Windows by mythz on Tuesday December 01, 2020 @09:38AM (#25265505)
The smallest executable gcc (on Ubuntu) compiles that to is 8296 bytes (tried every -O optimization).
Re:Show HN: Tiny 1k Rust binary on Windows by nitrogen on Tuesday December 01, 2020 @04:29PM (#25270177)
Is that stripped or unstripped?
Re:Show HN: Tiny 1k Rust binary on Windows by pantalaimon on Tuesday December 01, 2020 @11:50AM (#25266890)
Hm I only looked at the output of size which gives

   text    data     bss     dec     hex filename
   1566     600       8    2174     87e hello
(no optimisations)
Re:Show HN: Tiny 1k Rust binary on Windows by renewiltord on Tuesday December 01, 2020 @12:31PM (#25267431)
Actually, the equivalent Rust code to this is just the following:

    println!("Hello world!");
Exactly that text. Compile it and it comes in at exactly 13.6% of the C program you have that compiles to 1k.
Re:Show HN: Tiny 1k Rust binary on Windows by KwanEsq on Tuesday December 01, 2020 @08:02AM (#25264731)
I notice you moved from asm! to llvm_asm! [0]. Since the commit message doesn't explain why, would you be able to do so here? (Purely for my own curiosity)

[0] https://github.com/mcountryman/min-sized-rust-windows/commit...

Re:Show HN: Tiny 1k Rust binary on Windows by cdirkx on Tuesday December 01, 2020 @08:44AM (#25265023)
They are the same: asm! was replaced by llvm_asm! because it currently only supports llvm asm and the name might suggest otherwise [1]. A backend independant syntax for asm! was recently specified [2], but that will take some time to implement.

[1] https://github.com/rust-lang/rfcs/pull/2843

[2] https://github.com/rust-lang/rfcs/pull/2873

Re:Show HN: Tiny 1k Rust binary on Windows by steveklabnik on Tuesday December 01, 2020 @09:09AM (#25265226)
They are not the same, and the asm! version is already implemented; the RFC came with an implementation.

That first PR moved the old asm to llvm_asm, and then the new asm was built after a period of time so folks could migrate their code to llvm_asm, as a temporary measure. This commit happened after that transition so this has to be from the new asm back to the transition version.

Re:Show HN: Tiny 1k Rust binary on Windows by cdirkx on Tuesday December 01, 2020 @09:24AM (#25265388)
Oops, my bad, I was working with an outdated understanding of the situation. Thanks for correcting me.
Re:Show HN: Tiny 1k Rust binary on Windows by steveklabnik on Tuesday December 01, 2020 @10:27AM (#25265954)
No problem :) It was a feature that had no changes for a long time, and now has had a bunch of improvements and is finally on a path to stable. It can be hard to keep up with things sometimes.
Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @08:52AM (#25265084)
I couldn't get it working and was familiar with the `llvm_asm!` syntax. Although, I may have to go back and switch it around again.
Re:Show HN: Tiny 1k Rust binary on Windows by winter_blue on Tuesday December 01, 2020 @08:20AM (#25264857)
Zig actually does a good job of cutting final output bytes automatically, even with standard library use.

I think (?) its compiler sort-of implements a more advanced version of Unix strip.

Andrew Kelley could probably chime in more on it.

Re:Show HN: Tiny 1k Rust binary on Windows by acqq on Tuesday December 01, 2020 @09:05AM (#25265191)
It's very easy and fast to try: using 0.7.0+51d7c14ce from https://ziglang.org/download/ and:

   const std = @import("std");

   pub fn main() void {
      std.debug.print("Hello, world!\n", .{});
   }
----------

zig build-exe hello.zig -O ReleaseSmall --strip --single-threaded -target x86_64-windows

Resulting hello.exe is 3072 bytes. From these 2560 are zero bytes. The only system calls from the resulting binary are to ExitProcess, GetLastError and WriteFile from KERNEL32.dll Additionally, changing the release optimizations:

zig build-exe hello.zig -O ReleaseFast --single-threaded --strip -target x86_64-windows

produces hello.exe of 2560 bytes, 1849 from which are zeroes.

Note all: winter_blue has right to mention Zig. It's about what's provided out of the box in that new language and the provided optimizations are really effective.

Edit: asking mcountryman to explain: "the compiled rust exe has 480 non-zero bytes" -- I think you mean not just "compiled" but "hand-optimized writing hundreds of lines of the rust source code and not using standard library"? https://github.com/mcountryman/min-sized-rust-windows/blob/m... https://github.com/mcountryman/min-sized-rust-windows/blob/m...

Edit2: Please note that zig doesn't eventually link to printf and libc there, but links and resolves the std calls from the source to the calls to KERNEL32.dll.

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @10:43AM (#25266114)
Ah, yeah, I wasn't trying to shrug off the usefulness of zig, sorry. I think when it comes to compile size zig does it a bit better due to it's smaller std although, when compiling rust with just a link to libc and a printf call I believe you get an exe ~2048b when optimizing for size so I'm not certain how well that scales.

The 100 line hack I used was to noop out the `.idata` section which link.exe refused to merge/shrink, that only saved me 1Kb in the end.

Looking at the section info of the zig exe it looks like there is some black magic going on with it's alignment so I may have to dig deeper to see what zig is doing here. Also thank you both for pointing zig out, I haven't heard of it until now and it may come in handy for something else I'm planning on making :)

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @04:05PM (#25269885)
@Edit2 I saw that, and that's pretty cool in regards to how the zig std is a wrapper rather than a rewrite. Although, if we were optimizing for size in this case it might be more optimal to have only one entry in the import table :D
Re:Show HN: Tiny 1k Rust binary on Windows by acqq on Tuesday December 01, 2020 @05:26PM (#25270863)
Maybe "zig std is a wrapper rather than a rewrite" was the slip and what's written is the opposite of the idea? I wouldn't call zig's std "a wrapper" in this case where it implements the functionality without the C standard library? Had it called something in the C library, then I'd call it a wrapper.
Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Wednesday December 02, 2020 @08:30AM (#25276150)
Yeah my choice of words wasn't great. It seams as if the zig std acts as a macro rather than a wrapper. That could just be the aggressive optimization though
Re:Show HN: Tiny 1k Rust binary on Windows by acqq on Wednesday December 02, 2020 @09:36AM (#25276674)
What name would you use after seeing the implementation of the functions called?

std/debug.zig:

    pub fn print(comptime fmt: []const u8, args: anytype) void {
        const held = stderr_mutex.acquire();
        defer held.release();
        const stderr = io.getStdErr().writer();
        nosuspend stderr.print(fmt, args) catch return;
    }

io/writer.zig:

    pub fn print(self: Self, comptime format: []const u8, args: anytype) Error!void {
        return std.fmt.format(self, format, args);
    } 
std/fmt.zig:

    pub fn format(
        writer: anytype,
        comptime fmt: []const u8,
        args: anytype,
    ) !void {
       ...
It's all based on the language features and the actual implementations of zig's standard library code, and at least I don't see anything "wrapped" there, and nothing what C++, C or asm programmers understand as "a macro".
Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Wednesday December 02, 2020 @03:51PM (#25281443)
Very well optimized inlined functions?

I didn't actually check the zig std source. I was referring to what I was seeing in radare2 :)

Re:Show HN: Tiny 1k Rust binary on Windows by acqq on Wednesday December 02, 2020 @04:16PM (#25281768)
In zig there's a concept of "comptime" -- note that fmt parameter is marked as such. It's more than just inlining.

https://ziglang.org/documentation/master/#comptime

See an example calling firstNPrimes. "When we compile this program, Zig generates the constants with the answer pre-computed." "Note that we did not have to do anything special with the syntax of these functions. For example, we could call the sum function as is with a slice of numbers whose length and values were only known at run-time."

Then see "Case Study: printf in Zig"

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Wednesday December 02, 2020 @06:31PM (#25283266)
I'm a little short on time so I didn't read through all of it but, comptime generics sounds similar to how rust compiles generics and comptime parameters are similar to const fns although zig appears to have more control over what is deamed "compile time".
Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @09:36AM (#25265493)
Just for fun, the compiled rust exe has 480 non-zero bytes, the zig exe had 512 bytes.
Re:Show HN: Tiny 1k Rust binary on Windows by AndyKelley on Wednesday December 02, 2020 @04:36PM (#25281969)
related, this fun blog post by mandejan: https://blog.mandejan.nl/posts/smallest-echo.html
Re:Show HN: Tiny 1k Rust binary on Windows by mytailorisrich on Tuesday December 01, 2020 @05:50AM (#25264137)
On Windows the smallest exe can be achieved by writing in assembly and compiling as .com executable.

These literally just contain the compiled assembly and are usually used on, for example, the 4k demoscene.

1k with rust is certainly a nice squeeze.

Re:Show HN: Tiny 1k Rust binary on Windows by jariRG on Tuesday December 01, 2020 @06:49AM (#25264407)
The size limited demos are use techniques like in the OP, scrapping the runtime, no std, filling the unused exe header sections with own data, quantizing floating point values, etc. And/or exe packers like Crinkler and kkrunchy. COM's are no longer used.
Re:Show HN: Tiny 1k Rust binary on Windows by poizan42 on Tuesday December 01, 2020 @05:52AM (#25264149)
That's a DOS executable though, and won't run on 64-bit Windows.
Re:Show HN: Tiny 1k Rust binary on Windows by mytailorisrich on Tuesday December 01, 2020 @06:18AM (#25264280)
Not out of the box but with something like vDos it should.
Re:Show HN: Tiny 1k Rust binary on Windows by weinzierl on Tuesday December 01, 2020 @06:25AM (#25264310)
Regular com programs won't run, but I'm waiting for the day the vulnerability is discovered that let's you run a specially crafted binary because of left over code from the old com launcher. Is the com extension still recognized in 64-bit windows?
Re:Show HN: Tiny 1k Rust binary on Windows by poizan42 on Tuesday December 01, 2020 @06:38AM (#25264368)
Considering that COM programs are headerless I can't imagine any way a specially crafted binary could do anything since there is no parsing going on. The "COM loader" just hands it off to NTVDM which does not exist on 64-bit Windows (there are 3rd-party implementations though)

And yes, the com extension is still recognized and will pop up a dialog telling you "This app can't run on your PC".

But actually .com and .exe files are handled equivalently, so you can just rename an .exe (i.e. PE executable) to .com and it will run - that doesn't make it a COM executable though...

Re:Show HN: Tiny 1k Rust binary on Windows by zinekeller on Tuesday December 01, 2020 @06:31AM (#25264346)
No, like really impossible on a hardware level (most 16-bit instructions are hidden on hardware level on entering 64-bit mode) and on a software level (NTVDM is separate from Windows NT from day one (since it only works on x86 hardware and not on DEC Alpha for example) and it was an optional component since Vista but only on an x86 installation though).
Re:Show HN: Tiny 1k Rust binary on Windows by userbinator on Tuesday December 01, 2020 @09:40AM (#25265531)
These literally just contain the compiled assembly and are usually used on, for example, the 4k demoscene.

There are plenty of 1k demos too, on Windows and otherwise:

http://www.pouet.net/prodlist.php?type%5B%5D=1k

Re:Show HN: Tiny 1k Rust binary on Windows by anonunivgrad on Tuesday December 01, 2020 @11:50AM (#25266892)
If you're willing to rely on undocumented behavior, you can always skip kernel32 and use ntdll.dll directly. Or you could go even further and direct invoke the right syscalls. But either way, you'd be relying on behavior that Microsoft can change without notice.
Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @03:53PM (#25269752)
I thought of that but couldn't figure out how to get the syscalls for my system and the assembly around it. Being on win10, kernel32 links to api-console-... and I haven't been able to find it on my system. A few online downloads had it but, they don't export `GetStdHandle` and `WriteFile`. I thought maybe there was dynamic export generation going on because they did have those names as strings.

Edit: Also I believe ntdll doesn't export `GetStdHandle` anymore.

Re:Show HN: Tiny 1k Rust binary on Windows by ChrisSD on Tuesday December 01, 2020 @12:51PM (#25267661)
The Win32 loader will always load kernel32 no matter what. You don't even need it in your import table. It's just a matter of finding where it's loaded and calling into the right functions.
Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @04:00PM (#25269838)
Between a 40b strcmp method (If I had/if it was more widely supported I could use sse4.2 strcmp) and ~74b of resolving said exports a couple syscalls sounds pretty nice provided the context switch isn't more expensive than resolving the imports.
Re:Show HN: Tiny 1k Rust binary on Windows by ChrisSD on Tuesday December 01, 2020 @04:51PM (#25270453)
Well if you really want to you can get the output handle from the PEB[0]. You can then call NtWriteFile[1] using syscall `0x0008` (Windows 10 x64 only)[2].

[0]: https://processhacker.sourceforge.io/doc/struct___r_t_l___u_...

[1]: https://docs.microsoft.com/en-us/windows-hardware/drivers/dd...

[2]: https://j00ru.vexillium.org/syscalls/nt/64/

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Wednesday December 02, 2020 @04:31PM (#25281912)
I completely forgot stdout handle is in the PEB! Thanks! After learning how the windows x64 syscall calling convention works I got it working on win10.

https://i.imgur.com/ErHU7Kr.png https://github.com/mcountryman/min-sized-rust-windows/commit...

Re:Show HN: Tiny 1k Rust binary on Windows by ziml77 on Tuesday December 01, 2020 @09:38AM (#25265507)
I know making tiny Windows executables like this is done just for fun, but I'm curious if there's technically a performance loss from merging sections. IIRC the sections are split up and 4k aligned because they have to be page aligned in memory anyway.
Re:Show HN: Tiny 1k Rust binary on Windows by stephen82 on Tuesday December 01, 2020 @05:53AM (#25264152)
I have found this https://github.com/HugoPlatzer/tiny-rust-binaries.

Can you test it and see how it behaves with msvc toolchain?

Re:Show HN: Tiny 1k Rust binary on Windows by quietbritishjim on Tuesday December 01, 2020 @06:10AM (#25264227)
That contains a lot of platform-specific build instructions for Linux (and for the ELF file format used on Linux but not Windows), so it's never going to work with the msvc toolchain.
Re:Show HN: Tiny 1k Rust binary on Windows by stephen82 on Tuesday December 01, 2020 @07:06AM (#25264485)
For some reason, I saw ELF as EXE! hahaha ^_^

My bad everyone, my bad!

Re:Show HN: Tiny 1k Rust binary on Windows by mcountryman on Tuesday December 01, 2020 @08:47AM (#25265048)
I linked these guys here https://github.com/pts/pts-tinype. They managed to do something similar with the PE headers, and I might have to try that next although I'm doubtful win64 exes are permitted to run if they are >1Kb