The interesting contrast here is how things are done on Windows.
The reason most C++ programs ran on all Windows computers (and with such a tiny file size) is Windows shipped with the common libraries (C++ runtime) required to run the programs.
When Borland came out with Delphi they wanted Microsoft to include some of their own common libraries to reduce the footprint of the executable -- but Microsoft said no, meaning Delphi's executables had to have everything linked into it statically, resulting in files that were an order of magnitude bigger. Aside from the stigma from larger file sizes in an era (the 90s) when this mattered, they arguably came out ahead in this department as Delphi by default avoided "DLL hell" (until MS solved it more or less definitively with Windows 2000) and would run anywhere, on anything, with no additional dependencies.
Is it actually practical to statically compile & link large parts of a modern C / C++ based software environment? The last time I tried to do that with a nontrivial C program and GCC it felt like an ultimately unsuccessful snipe hunt.
Incidentally, cross-compiling static binaries is a hugely productive feature of Go.
Static linking is practically impossible on modern Unix because of bullet point 4 in nopaste7's linked page.
> all kinds of features in the libc (locale (through iconv), NSS, IDN, ...) require dynamic linking (...)
These are all features which have been shoehorned into libc on Linux, but only work when it's a shared library because they rely on shared state. I would argue a cleaner way of implementing them would have been as services, but alas, you have what you have.
It's not shared state - dynamic libraries don't share any more state between processes than static libraries do.
Those features use dynamic linking so that they can load only the necessary implementations at runtime - eg if you aren't using LDAP name lookups, then the NSS won't load the library that does LDAP name lookups.
Shared libraries on Linux only share read-only text and data, which should not be application-visible.
If you want to share state between processes, the library must explicitly set up a shared mapping - but of course a statically linked library can do this just as well.
Yes, I know. My point was related to the terminology re: static, dynamic, shared, etc.
A statically-linked library won't share any state with other users of the library. It's statically-linked into the binary. State will be shared only with the instances of the same binary.
It is irrelevant whether the library is statically-linked into the binary or not - a statically-linked library can share state with instances of that library linked into other binaries just as easily as a dynamically-linked library can. In both cases the library must explicitly set up some shared state.
But of course you could still easily mmap() a file to achieve the same thing (even from the shared library .init functions).
I can understand that the shared section might be abused by goofy programmers ("Hey guys! We don't need to open a file for storage, we can just build it into the DDL!") ... but is there a reason you think it's a bad idea otherwise?
There's only one minor problem; DLL hell. As long as none of your applications break when you update their shared dependencies, everything's fine. The package management system generally takes care of this in modern distributions, and the package maintainers usually do enough coordination to avoid (much) breakage.
But if you're trying to produce commercial shippable software (what used to be called shrinkwrap), you're not going to fit in as easily with a package management system. Your binaries will probably need updating when the shared libraries update; that's a major headache. And this is one of the reasons why there hasn't been much of a commercial market in software for Linux. Everything generally works much more smoothly if your only dependencies are glibc and a handful of other bits.
I was actually surprised that the VDSO [1] page was missing from that. For those unfamiliar... the Linux kernel selects the best system call implementation for the current system and exports a special page to user-space. It also provides a few specific additional routines (generally related to getting the current time without having to jump in and out of the kernel).
I'm not advocating for this approach, but it's there and can have a big impact.
As a fun exercise, I just checked how much impact this has on my VM right now...
timetest.c:
#include <stdio.h>
#include <time.h>
int main(void) {
for (int i = 0; i < 1000000000; i++) {
time(NULL);
}
}
gcc timetest.c -o timetest -std=c99 && time ./timetest
real 0m3.589s
user 0m3.588s
sys 0m0.000s
gcc timetest.c -o timetest -std=c99 -static && time ./timetest
Of course there are exceptions: some people like to link in openssl libraries statically; that removes another attack vector - like swapping an openssl shared library with something that returns a constants for all random numbers;
On the other hand that is also a bit silly; if the box is owned then all else is peanuts;
The suckless.org people (creators of dwm) are working on a fully static Linux distro[1]. I'm quite interested to see how it turns out, but at this point it's starting to look like vaporware.
TL;DR: Good luck! At least until recently it was virtually impossible to actually do this - Autopackage used to have a list of things that prevented making decently portable binary packages for Linux. I believe they resorted to shipping a modified linker to get things to work.
Unfortunately there is a lot of hostility to making binary distribution easier on Linux because of the "you should be using shared libraries and why would you ever want to distribute binaries when you can just distribute the source code?" attitude that prevails.
Having said that, GCC did finally add a real option to link statically (i.e. not `-static`) a few years ago. And I believe the linker finally supports symbol versioning.
The reason most C++ programs ran on all Windows computers (and with such a tiny file size) is Windows shipped with the common libraries (C++ runtime) required to run the programs.
When Borland came out with Delphi they wanted Microsoft to include some of their own common libraries to reduce the footprint of the executable -- but Microsoft said no, meaning Delphi's executables had to have everything linked into it statically, resulting in files that were an order of magnitude bigger. Aside from the stigma from larger file sizes in an era (the 90s) when this mattered, they arguably came out ahead in this department as Delphi by default avoided "DLL hell" (until MS solved it more or less definitively with Windows 2000) and would run anywhere, on anything, with no additional dependencies.