How to statically link all libraries except glibc using make/gcc? - gcc

I am trying to compile a program to have all libraries statically inside the final binary, but I still want glibc to be dynamically linked. If I use "-static" it compiles ALL libraries statically, including glibc. Basically I need a -static parameter together with something like -exclude=glibc
Would be awesome with both an example when using "make" as well as an example with pure "gcc". Running "ldd" on the final binary should show only glibc dynamically linked.

You can link a subset of libraries statically using -Bstatic and -Bdynamic. On the GCC command line, this looks like this (for linking statically against PCRE, just as an example):
-Wl,-Bstatic -lpcre -Wl,-Bdynamic
Note that -lanl, -ldl, -lm, -lmvec, -lnsl, -lpthread, -lresolv, -lrt, -lutil are all part of glibc and must therefore come after the -Wl,-Bdynamic (so that they are linked dynamically). For -lcrypt, this depends on the distribution.

What you ask can be done on some systems, approximately, but not with GCC's -static option. That option has global effect on linking:
On systems that support dynamic linking, this overrides -pie and prevents linking with the shared libraries. On other systems, this option has no effect.
(GCC 9.2 manual)
To have the wanted level of control over linking, you need to pass flags through to the linker. You can do that with GCC's -Wl option. If you are using GCC then you are presumably also using the GNU linker, and on build targets that support both static and dynamic linking, it has a variety of mechanisms for mixing them. In particular, the GNU linker's -Bstatic flag and its counterpart -Bdynamic flag each take effect only for libraries named after them on the command line, up to the next such flag. That is, they allow you to switch back and forth between designating libraries for static linking and for dynamic linking.
Example:
This C program requires the math library to be linked, which is not automatic with GCC:
link_test.c:
#include <stdio.h>
#include <math.h>
int main(void) {
printf("The square root of 2 is approximately %f\n", sqrt(2.0));
}
This gcc command will cause the-lm to be linked statically, but libc to be linked dynamically:
gcc -o link_test link_test.c -Wl,-Bstatic -lm -Wl,-Bdynamic
Any number of addional -l options, library names, and object file names could be put between the -Wl,-Bstatic and -Wl,-Bdynamic options along with -lm; all such objects will be linked statically. Although libc is not explicitly linked (GCC does not require that), leaving the link type toggled to "dynamic" at the end of the explicit argument list does, for me, cause libc to be linked dynamically:
$ ldd link_test
linux-vdso.so.1 => (0x00007ffe185af000)
libc.so.6 => /lib64/libc.so.6 (0x00002b775f059000)
/lib64/ld-linux-x86-64.so.2 (0x00002b775ee35000)
(Observe that libm does not appear in the dynamic library listing, unlike when -Wl,-Bstatic is not used, but libc does.)
Note that your objective that "Running 'ldd' on the final binary should show only glibc dynamically linked" is not necessarily viable, as the above ldd output demonstrates. If your executable is dynamically linked at all, then in addition to any dynamic libraries it will have the dynamic loader linked in, and possibly also platform-specific pseudo-libraries such as linux-vdso.so.1.
You ask for a makefile example, but that's like asking just "write me a program". Nothing about this is make-specific, and there are innumerable ways to incorporate the above approach into a makefile. But since you asked, this is one of the simplest possible variations:
Makefile
link_test: link_test.c
gcc -o $# $< -Wl,-Bstatic -lm -Wl,-Bdynamic

Related

Prefer static libraries but fallback to shared if not found

GCC (or the linker) prefers dynamic libraries over static ones by default. That means if I have
libfoo.so
libfoo.a
libbar.a
and if I am building another library libmylib.so as follows:
gcc -shared -o mylib.so ... -lfoo -lbar
the linker will link to libfoo.so (by preference) and libbar.a since that is the only one available.
One can force linking of static libraries by specifying -static (or -Wl,-Bstatic) instead.
gcc -shared -o mylib.so ... -static -lfoo -lbar
This all works fine and will link to static versions of both libs.
However if I do not have the static libs it will not fallback to the shared libs. That is if I only have
libfoo.so
libbar.a
the linking will fail because of missing libfoo.a instead of falling back to libfoo.so since it is the only one available.
Is there anyway to force this behavior where static libs are preferred but shared libs are picked if there is no static one that matches? (without having to explicitly specify -Wl,-Bdynamic manually for every such case)
Is there anyway to force this behavior where static libs are preferred but shared libs are picked if there is no static one that matches?
Not that I know of.
This is quite an unusual requirement. Usually you want to link everything dynamically, except a few libraries (which you know in advance) which you want to link in statically.
For a solution to that problem, see this answer.

How to prevent GLIBC symbols from being exported from an .so?

I am linking GLIBC statically into my .so:
g++ -shared -o morpher.so -static-libstdc++ -static-libgcc -Wl,-L. -Wl,--whole-archive -l:hello.o -l:libmorpher.a -Wl,--no-whole-archive
I do this to make my binary (.so) portable across different Linux distributions and versions.
However, this has an undesirable side effect: all GLIBC symbols, including e.g. malloc() and free(), are exported from my shared library:
$ nm morpher.so | grep free
00000000000ef520 t _ZN12_GLOBAL__N_14pool4freeEPv.constprop.0
000000000008b08c t _ZN12_GLOBAL__N_14pool4freeEPv.constprop.0.cold
00000000000ef710 T _ZN9__gnu_cxx9__freeresEv
00000000000ef860 T __cxa_free_dependent_exception
00000000000ef7b0 T __cxa_free_exception
U __freelocale##GLIBC_2.2.5
U free##GLIBC_2.2.5
Adding -fvisibility=hidden has no effect: it only works on source files at compile time.
Is there a way to prevent GLIBC and libstdc++ symbols from being exported?
nm (without the -D option) shows the static symbol table (.symtab), not the dynamic symbol table (.dynsym). Only the dynamic symbol table is relevant to dynamic linking.
In your case, it appears that the symbols are not actually defined in the shared object. Rather, __freelocale and free are undefined (U) or imported. If you build your shared object on the oldest distribution you want to support, such imports do not impact cross-distribution compatibility because glibc has a compatible ABI across distributions, and later versions support symbols from older versions.
-static-libgcc is about libgcc (not glibc). A lot of libgcc is always statically linked. The shared-by-default part is mostly about the unwinder, and linking that dynamically is recommended, so that the system unwinder is used.

Mingw gcc, "-shared -static" passing together

When studying Scintilla's makefile for MinGW under windows, I noticed that it is passing -shared and -static together as LDFLAGS to gcc.
LDFLAGS=-shared -static -mwindows $(LDMINGW)
I googled, and only find some information from clang: https://reviews.llvm.org/D43811
[MinGW, CrossWindows] Allow passing -static together with -shared
In these combinations, link a DLL as usual, but pass -Bstatic instead of -Bdynamic to indicate prefering static libraries.
My question is: Would GCC do the same?
I haven't find any proof yet.
You can pass both -static and -shared in a GCC linkage. Their
combined effect is the same as you found described in your llvm link,
and this has always been the case for GCC.
-shared directs a GCC linkage to produce a shared library rather than a program,
which it achieves by passing on the option -shared to its invocation of
the linker.
-static directs a GCC linkage to ignore shared libraries when resolving
input library options -lname. By default -lname would be resolved by
searching the specified or default linker search directories for either
the shared library libname.so (on Windows, [lib]name.dll)
or the static library libname.a (on Windows also [lib]name.lib) and to prefer
the shared library if both of them are found in the same directory. -static
simply excludes all shared libraries from the search. GCC achieves this by passing the option -Bstatic
through to its invocation of the linker at a position in the generated linker
commandline that precedes all of the -lname options.
The GNU linker documentation of -Bstatic is explicit
that this option is consistent with -shared and that the effect is to produce a shared library
all of whose dependent libraries have been statically resolved.
-Bstatic
-dn
-non_shared
-static
Do not link against shared libraries. This is only meaningful on platforms for which shared libraries are supported.
The different variants of this option are for compatibility with various systems. You may use this option multiple times on the command line:
it affects library searching for -l options which follow it. This option also implies --unresolved-symbols=report-all.
This option can be used with -shared. Doing so means that a shared library is being created but that all of the library’s external references must be resolved by pulling in entries from static libraries.
(emphasis mine).
Although static linkage of shared library is in principle just a linkage restricted
in the same way as static linkage of a program, in practice it frequently encounters
a snag on Unix and Linux because all the object code linked into an ELF shared library
libname.so must be Position Independent Code,
as produced by the GCC compilation option -fPIC, whereas object files that are destined to be
archived in static libraries are customarily not compiled with -fPIC. Linkages using
-shared ... -static are thus apt to fail because necessary static libraries contain
non-PIC object files.
You do not have this worry with GCC on Windows, however, because there
is no such distinction as PIC v. non-PIC in Windows PE
object code.

Statically linking any library causes libc to fail to be linked against

My system is an older NAS running 2.6.32. I have found that when using -static for any subsequent library, it will also try to statically link any other library that I might need.
When I add the -Wl,-Bdynamic flag first and then explicitly name those libraries using -lc, such as "-Wl,-Bdynamic -lc -lstdc++" then it works. So what happens is that libc and others fail to be statically linked.
The static libc on the system is called /opt/lib/libc_nonshared.a.
The contents of /opt/lib/libc.so is this:
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( /lib/libc.so.6 /opt/lib/libc_nonshared.a )
The gcc version is 4.2.3. The current build command I am facing adds -dynamic at the end but this doesn't help much. When I add some static library directly using its .a name, and not using a -l flag, then there is no issue.
The problem seems to be that the dynamic library of libc came with the NAS, but the static version sits in /opt/lib.
I run:
gcc hamming.c -static -L. -L/opt/lib -l:matrix.a -o hamming
I get:
/opt/lib/gcc/arm-none-linux-gnueabi/4.2.3/../../../../arm-none-linux-gnueabi/bin/ld: cannot find -lc
collect2: ld returned 1 exit status
make: *** [hamming] Error 1
when I try to use static libc as is. Were I to perform a 'hack' to link libc_nonshared.a to libc.a, it suddenly does find it. But complains:
hamming.c:54: undefined reference to `malloc'
hamming.c:54: undefined reference to `memset'
And a zillion other errors of course. As mentioned above, /opt/libc.so contains the reference to both files (dynamic and static).
For libstdc++ only a .la file exists.
The -static linker flag does not take any argument. It is a boolean
flag that simply directs the linker to link no shared libraries, as
documented
-static
Do not link against shared libraries...
There is no need to explicitly direct the linker to link shared (dynamic)
libraries when it has a choice because that is the default bevaiour. If
you simply link, e.g.
gcc -o prog ... -lfoo ...
then the linker will link the first of libfoo.so (shared) or libfoo.a
(static) that it finds in any of the specified (-Ldir) or default
search directories, searched in commandline sequence. If it finds both
libfoo.so and libfoo.a in the same directory then it will choose
libfoo.so. Thus shared and static libraries may be freely intermixed
without any special options.
Specify -static only if you wish to link only static libraries.
If you wish to insist on linking a particular libfoo.a even when
libfoo.so is in the same directory and would be chosen by default,
use the explicit form of the -l option: -l:libfoo.a
Later
gcc hamming.c -static -L. -L/opt/lib -l:matrix.a -o hamming
This command is failing with:
ld: cannot find -lc
because the linker (ld) cannot find a static library libc.a in
any of the specified linker search directories (-L. -L/opt/lib) or
the default linker search directories. If you wish instead to link
/opt/lib/libc_nonshared.a then your command should be:
>gcc hamming.c -static -L. -L/opt/lib -l:matrix.a -lc_nonshared -o hamming
However, you have not explained why you want to link this program statically
(-static) in the first place, which is not the usual way and will require you to have installed
static versions of all libraries required for the linkage - both those
you explicitly link and the default libraries that gcc will add for C language
linkage (Standard C library, GCC runtime library).
Supposing you have a static library called (oddly) matrix.a (rather
than normally, libmatrix.a) that is located in /some/dir/, then the
normal way to compile and link your program would be:
gcc hamming.c -L/some/dir -l:matrix.a -o hamming
I suggest you start with that and deviate only as problems compel
you to.
The discovery of an /opt/lib/libc.so containing:
OUTPUT_FORMAT(elf32-littlearm)
GROUP ( /lib/libc.so.6 /opt/lib/libc_nonshared.a )
is misleading you. This is not your shared libc. A shared library
is a binary. This is a linker script, and it says that your shared libc
is in fact /lib/libc.so.6. The linker will almost certainly find and use it by default.

GCC link order changed?

I am trying to link a C++ module using GCC, essentially like this:
gcc -c hello.c
g++ -c world.cpp
gcc -ohello -lstdc++ hello.o world.o
Note that I use -lstdc++ to link the C++ module in, so that I can use gcc instead of g++. The problem is that I'm getting the error:
undefined reference to `operator new(unsigned long)'
(Assuming that world.cpp contains at least one call to new.)
This error is fixed if I put -lstdc++ at the end of the linker line, like this:
gcc -ohello hello.o world.o -lstdc++
I am aware that this question has been asked many times here, but I have a special requirement. I am not directly calling GCC. I am using a build system for a different programming language (Mercury) which is calling GCC on my behalf, and I can't easily modify the way it calls GCC (though I can specify additional libraries using the LDFLAGS environment variable). So I have two additional requirements:
I cannot use g++ to link (only gcc) -- that is why I am doing the -lstdc++ trick above rather than simply linking with g++).
I don't think that I can control the order of the linker commands -- Mercury will put the .o files on the command-line after any libraries.
I understand the basic reason why the order is important, but what is baffling me is why did this break now? I just updated to Ubuntu 11.10 / GCC 4.6.1. I have been successfully compiling this program for years using precisely the above technique (putting -lstdc++ first). Only now has this error come up. An unrelated program of mine links against OpenGL using -lgl and that too broke when I upgraded and I had to move -lgl to the end of the command-line. I'm probably going to discover that dozens of my programs no longer compile. Why did this change? Is there something wrong with my new system or is that the way it is now? Note that these are ordinary shared libraries, not statically linked.
Is there anything I can do to make GCC go back to the old way, where the order of libraries doesn't matter? Is there any other way I can convince GCC to link libstdc++ properly without moving it after the .o files on the command-line?
If Mercury puts object files after libraries, Mercury is broken. Libraries belong after object files - always. You may sometimes get away with the reverse order, but not reliably. (Static libraries must go after the object files that reference symbols in the static library. Sometimes, a linker will note the symbols defined by a shared library even when none of the symbols are used; sometimes, the linker will only note the shared library symbols if the shared library provides at least one symbol.)

Resources