The executable takes 33KB in C, 75KB in nim.
But in all seriousness, my example is quite cherrypicked, since nobody will actually statically link glibc. And even if they did, one can make use of link-time optimization to remove lots of patches of unused code. Note that this is the same strategy one would employ to debloat their Rust binaries. (Use LTO, don't aggressively inline code, etc.)
Did you statically link Glibc...? Or is this with a non-GNU libc?
Either way, it probably is true that this 2.2MiB number would be smaller on, say, Debian 5. And much smaller on PDP Unix.
ARM64 assembly program (hw.s):
//
// Assembler program to print "Hello World!"
// to stdout.
//
// X0-X2 - parameters to linux function services
// X16 - linux function number
//
.global _start // Provide program starting address to linker
.align 2
// Setup the parameters to print hello world
// and then call Linux to do it.
_start: mov X0, #1 // 1 = StdOut
adr X1, helloworld // string to print
mov X2, #13 // length of our string
mov X16, #4 // MacOS write system call
svc 0 // Call linux to output the string
// Setup the parameters to exit the program
// and then call Linux to do it.
mov X0, #0 // Use 0 return code
mov X16, #1 // Service command code 1 terminates this program
svc 0 // Call MacOS to terminate the program
helloworld: .ascii "Hello World!\n"
Assembling and linking commands: as -o hw.o hw.s &&
ld -macos_version_min 14.0.0 -o hw hw.o -lSystem -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX15.2.sdk -e _start -arch arm64
Resulting file sizes: -rwxr-xr-x 1 <uid> <gid> 16K Jun 18 21:23 hw
-rw-r--r-- 1 <uid> <gid> 440B Jun 18 21:23 hw.o
-rw-r--r-- 1 <uid> <gid> 862B Jun 18 21:21 hw.s
0 - https://smist08.wordpress.com/2021/01/08/apple-m1-assembly-l...Rust binaries also dynamically link to and rely on this runtime.
For Rust code talking to Rust libraries (such as `std`), it's a totally different challenge, similar to what you face in C++. You can compile your pure Rust app in a way to divide it up into DLLs. The problem is that the polymorphism in Rust means these DLLs must all be built together. Calling a polymorphic function means code has to be generated for it. The only way around this is for your Rust library to speak the C ABI, which bloats code as building your C-style API surface will resolve all the polymorphized functions/structs, but at least gets you swappable dynamic linking.
just to have everything in one file? how to show how to do it with nix?
because it seem simpler to have a separate C++ file, and a simple shell script or makefile to compile it.
e.g. although I could figure out roughly what the .nix file does, many more people would know plain unix shell than nix.
and where is $out defined in the .nix file?
$out is a magic variable in nix that means the output of the derivation - the directory that nix moves to its final destination
/tmp$ gcc -O3 test.c -o test
/tmp$ ldd test
linux-vdso.so.1 (0x00007f3d9fbfe000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f3d9f9e8000)
/lib64/ld-linux-x86-64.so.2 (0x00007f3d9fc00000)
/tmp$ gcc -static -O3 test.c -o test
/tmp$ ldd test
not a dynamic executable
> /tmp$ gcc -static -O3 test.c -o test /tmp$ ldd test not a dynamic executable
yes, that last line above means it's a statically linked executable.
yes, i had a doubt about what the GP said, about their nix way being the only way to create a statically linked executable.
but I didn't remember all the details, because it's been a while since I worked with C in depth (moved to Java, Ruby, Python, etc.)(though I did a lot of that earlier, even in pre-Linux years), so I didn't say anything else. thanks, Josh Triplett for clarifying.
but one thing I do remember, is that static linking was the only option in the beginning, at least on Unix, and dynamic linking came only some time later.
when I started working on UNIX and C, there was no dynamic linking at all, IIRC.
https://en.m.wikipedia.org/wiki/Static_library
("dynamic linking" topic in above page links to the below page in Wikipedia: )
musl, by contrast, doesn't support NSS at all, only /etc/hosts and DNS servers listed in /etc/resolv.conf, so whether you statically or dynamically link musl, you just won't have any support for (for instance) mDNS, or dynamic users, or local containers, or various other bits of name resolution users may expect to Just Work.
then, nix to nix, i say.
let those who want to love it, love it.
for me: nein, nyet, non, nada, nako, nahi ... :)
that's "no" in german, russki ;), french, spanish, marathi, hindi.
adios, etc. ...
from a Google search:
>Overview Lake Titicaca, straddling the border between Peru and Bolivia in the Andes Mountains, is one of South America's largest lakes and the world’s highest navigable body of water. Said to be the birthplace of the Incas, it’s home to numerous ruins. Its waters are famously still and brightly reflective. Around it is Titicaca National Reserve, sheltering rare aquatic wildlife such as giant frogs.
:)
Most shells dynamically link to a runtime your OS provides "for free". The 4.3 MiB binary in question is bundling the Rust runtime and its dependencies.
For reference, a statically-compiled C++ "Hello, World" is 2.2 MiB after stripping.