86

Most of the sources online state that you can statically link glibc, but discourage from doing so; e.g. centos package repo:

The glibc-static package contains the C library static libraries for -static linking. You don't need these, unless you link statically, which is highly discouraged.

These sources rarely (or never) say why that would be a bad idea.

genpfault
  • 51,148
  • 11
  • 85
  • 139
pzelasko
  • 2,082
  • 1
  • 16
  • 24
  • 10
    Ironic that CentOS's package repo is so outdated that it sometimes forces developers to link statically. – nada Aug 13 '19 at 15:01

3 Answers3

102

The reasons given in other answers are correct, but they are not the most important reason.

The most important reason why glibc should not be statically linked, is that it makes extensive internal use of dlopen, to load NSS (Name Service Switch) modules and iconv conversions. The modules themselves refer to C library functions. If the main program is dynamically linked with the C library, that's no problem. But if the main program is statically linked with the C library, dlopen has to go load a second copy of the C library to satisfy the modules' load requirements.

This means your "statically linked" program still needs a copy of libc.so.6 to be present on the file system, plus the NSS or iconv or whatever modules themselves, plus other dynamic libraries that the modules might need, like ld-linux.so.2, libresolv.so.2, etc. This is not what people usually want when they statically link programs.

It also means the statically linked program has two copies of the C library in its address space, and they might fight over whose stdout buffer is to be used, who gets to call sbrk with a nonzero argument, that sort of thing. There is a bunch of defensive logic inside glibc to try to make this work, but it's never been guaranteed to work.

You might think your program doesn't need to worry about this because it doesn't ever call getaddrinfo or iconv, but locale support uses iconv internally, which means any stdio.h function might trigger a call to dlopen, and you don't control this, the user's environment variable settings do.

And if your program does call iconv, for example, then things get even worse, especially when a “statically linked” executable is built on one distro, and then copied to another. The iconv modules are sometimes located in different places on different distros, so an executable that was built, say, on a Red Hat distro may fail to run properly on a Debian one, which is exactly the opposite of what people want from statically linked executables.

Sergei Tachenov
  • 24,345
  • 8
  • 57
  • 73
zwol
  • 135,547
  • 38
  • 252
  • 361
  • 15
    Note that the need for a second copy of glibc is a design decision. If the static glibc library had linked in NSS and iconv statically, it wouldn't have been necessary. The downside of course would be that you could only use those NSS modules and iconv conversions that were linked in statically, but that's pretty obvious from the definition of static linking. – MSalters Aug 13 '19 at 13:52
  • 9
    @MSalters There has been some discussion recently on the glibc development list about doing just that. This design decision was made in the 1990s, and there's a strong argument that we don't _need_ quite so much flexibility in character encoding for terminal output anymore, especially not in the kinds of programs that people want to link statically. NSS flexibility is still important but there are alternative ways to handle that (e.g. `nscd`). – zwol Aug 13 '19 at 14:01
  • 1
    This is not *quite* right; I discovered that staticly linked C programs using `stdio.h` work with no libraries in /lib. The program I had to do this with was lilo. – Joshua Aug 13 '19 at 20:13
  • @Joshua Did you try it with LANG set to a locale that requires use of an unusual character set? – zwol Aug 13 '19 at 20:21
  • 2
    @zwol: It just switches to LANG=C if it can't load the library. This behavior is necessary for early boot. – Joshua Aug 13 '19 at 20:23
  • 3
    @Joshua then it sounds like it will load the library, and another copy of glibc, if it can. – user253751 Aug 13 '19 at 22:58
  • All the symbols that are statically linked from libc should override the symbols from any dynamically linked glibc in any uses from other dlopened dynamic libraries. So if you can ensure you get all the symbols needed by these libraries in your executable, it should not even end up dynamically linking a second copy. Of course if you miss any (easy to do), you'll get the extra copy. – Chris Dodd Mar 02 '22 at 21:50
  • @ChrisDodd If I remember correctly, the plugin's DT_NEEDED entry for libc.so.6 means the dynamic loader will load libc.so.6 _even if_ all of its undefined symbols are satisfied by the executable. – zwol Mar 02 '22 at 22:19
10

The program/glibc interface is standardized and documented by POSIX, the C and C++ standards, and others. For example, the fopen() function behaves per the C standard, and pthread_mutex_lock() per POSIX.

The glibc/kernel interface is not standardized. Does fopen() use open() under the hood? Or does it use openat()? Or something else? What will it use next year? You don't know.

If the glibc/kernel interface changes, a program that uses whatever changed but statically links glibc won't work any more.

15+ years ago, Solaris removed all static versions of libc for this very reason.

Static Linking - where did it go?

With Solaris 10 you can no longer build a static executable. It's not that ld(1) doesn't allow static linking, or using archives, it's just that libc.a, the archive version of libc.so.1, is no longer provided. This library provides the interfaces between user land and the kernel, and without this library it is rather hard to create any form of application.

We've been warning users against static linking for some time now, and linking against libc.a has been especially problematic. Every solaris release, or update (even some patches) has resulted in some application that was built against libc.a, failing. The problem is that libc is supposed to isolate an application from the user/kernel boundary, a boundary which can undergo changes from release to release.

If an application is built against libc.a, then any kernel interface it references is extracted from the archive and becomes a part of the application. Thus, this application can only run on a kernel that is in-sync with the kernel interfaces used. Should these interfaces change, the application is treading on shaky ground.

...

Edit:

There seems to be serious overestimation of the stability of the Linux kernel interface. See Linux kernel API changes/additions for details. To summarize:

enter image description here

Community
  • 1
  • 1
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56
  • 11
    https://yarchive.net/comp/linux/gcc_vs_kernel_stability.html: _We care about user-space interfaces to an insane degree. We go to extreme lengths to maintain even badly designed or unintentional interfaces. Breaking user programs simply isn't acceptable._ – Maxim Egorushkin Aug 13 '19 at 11:37
  • https://unix.stackexchange.com/questions/235335/why-is-there-a-linux-kernel-policy-to-never-break-user-space – Maxim Egorushkin Aug 13 '19 at 11:40
  • 2
    @MaximEgorushkin Reality is different. [The Linux ABI isn't very stable, to the point it's been mocked relatively recently](http://lkml.iu.edu/hypermail/linux/kernel/1604.0/00998.html): "It's not a secret that there are two basic ways of running a Linux distribution on your hardware. Either you use a stable distro which has quite an outdated kernel release which might not support your hardware or you run the most recent stable version but you lose stability and you are prone to regressions." – Andrew Henle Aug 13 '19 at 11:46
  • 16
    The quote you cited is about in-kernel driver APIs, not the user-space API. – Maxim Egorushkin Aug 13 '19 at 11:50
  • @MaximEgorushkin Are you really claiming the the Linux kernel user-space API is standardized to the level of POSIX, the C standard, and the C++ standard, amongst others? Really? That the Linux kernel user-space API is as stable as standard C functions listed in the C standard? Because **that** is the interface that gets coded to. – Andrew Henle Aug 13 '19 at 11:53
  • 3
    I never claimed that Linux API is standardized. Only that it is (relatively) stable. And most Linux POSIX function implementations are quite compliant. – Maxim Egorushkin Aug 13 '19 at 11:54
  • @MaximEgorushkin *I never claimed that Linux API is standardized. Only that it is (relatively) stable.* Compared to POSIX and the C standard, it's not stable at all. It can be changed whenever a small group decides unilaterally to change it. The `glibc` interface is standardized. By POSIX. By the C standard. And others. So you admit this answer is correct then? – Andrew Henle Aug 13 '19 at 11:58
  • 2
    Read my first two comments again and argue with Linus Torvalds. – Maxim Egorushkin Aug 13 '19 at 12:03
  • @MaximEgorushkin That has **absolutely nothing** to do with static linking `glibc`. You need to [read Richard Stallman about how its GNU (the **G** in `glibc`)/Linux and not just Linux.](https://en.wikipedia.org/wiki/GNU/Linux_naming_controversy) It's even got a wikipedia page. – Andrew Henle Aug 13 '19 at 12:06
  • 2
    @AndrewHenle: That whole confusion is **exactly** why it's such a problem, even 25 years later. `glibc` tries to be too much for too many cases. Microsoft is much cleaner in this respect. Windows itself has the Universal CRT, Visual Studio has its MSVCRT. – MSalters Aug 13 '19 at 13:46
  • 8
    "The glibc interface is standardized. By POSIX. By the C standard. And others." the *programming interface* is, but the *binary interface* is not! – plugwash Aug 13 '19 at 19:58
  • @plugwash And that's why statically-linking glibc has some serious drawbacks. The binary interface can change a lot easier than what you called the "programming interface". That programming interface is defined by [the C standard](https://port70.net/~nsz/c/c11/n1570.html) and [POSIX](http://pubs.opengroup.org/onlinepubs/9699919799/) among other standards. The kernel's binary interface is not standardized at all. The programming interface is tightly defined and standardized. – Andrew Henle Aug 13 '19 at 20:10
  • 1
    The kernel's binary interface is not standardised, but neither is glibc's binary interface. – plugwash Aug 13 '19 at 20:18
  • @plugwash *The kernel's binary interface is not standardised, but neither is glibc's binary interface.* [Ummm, you sure about that?](https://stackoverflow.com/questions/18133812/where-is-the-x86-64-system-v-abi-documented) The C standard says the function is `fopen( const char *file, const char *mode );` The platform ABI specifies the calling convention into the system-provided library, along with [the C standard](https://port70.net/~nsz/c/c11/n1570.html#6.2.2) and [POSIX](https://pubs.opengroup.org/onlinepubs/9699919799/). So, do you know what system call that `fopen()` call uses? – Andrew Henle Aug 13 '19 at 20:29
  • @plugwash Can you please provide the standard that Linux kernel system call interface is governed by? – Andrew Henle Aug 13 '19 at 20:29
  • 3
    Linux is committed to keeping its interface backwards compatible for this reason. – user253751 Aug 13 '19 at 23:01
  • 3
    @AndrewHenle Actually, that's an argument in favour of static linking. What if the binary interface to glibc changed, but your program didn't change to use the new binary interface? (Answer: glibc doesn't change its binary interface for this reason) – user253751 Aug 13 '19 at 23:02
  • 2
    @AndrewHenle The standard is here: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/ - unfortunately it's written in C rather than English. – user253751 Aug 13 '19 at 23:03
  • @AndrewHenle those standards are an insufficient specification of the interface for two reasons. The first is that C standard library functionality is sometimes implemented using macros. The second and far more pressing issue is symbol versioning, which effectively prevents software built against a newer glibc from running with an older glibc. – plugwash Aug 14 '19 at 13:12
  • 1
    @plugwash Are you **really** trying to claim that statically linking to glibc 2.30 is the solution to producing a binary guaranteed to run on a system with glibc 2.21? – Andrew Henle Aug 14 '19 at 13:45
-4

I can think of one good reason for allowing static linking. If someone gains access to the system, they can replace the libc.so with one that contains malware. When your application is run, it will execute the malware. If the app had been built with a static glibc, that attack vector would disappear.

tstone
  • 31
  • 4
  • 1
    OP is asking why static linking is *discouraged* but you give a reason why it *should* be used. Seems like not a very constructive answer. – Adrian Mole Aug 16 '22 at 13:21
  • 8
    If someone can replace libc.so, you've already lost. Such a user almost certainly has root privileges and can modify any file or byte of memory on the system, including patching the running kernel, etc. – Jonathan Myers Aug 30 '22 at 20:20