static linking only some libraries

How can I statically link only a some specific libraries to my binary when linking with GCC?

gcc ... -static ... tries to statically link all the linked libraries, but I haven't got the static version of some of them (eg: libX11).

167342 次浏览

From the manpage of ld (this does not work with gcc), referring to the --static option:

You may use this option multiple times on the command line: it affects library searching for -l options which follow it.

One solution is to put your dynamic dependencies before the --static option on the command line.

Another possibility is to not use --static, but instead provide the full filename/path of the static object file (i.e. not using -l option) for statically linking in of a specific library. Example:

# echo "int main() {}" > test.cpp
# c++ test.cpp /usr/lib/libX11.a
# ldd a.out
linux-vdso.so.1 =>  (0x00007fff385cc000)
libstdc++.so.6 => /usr/lib/libstdc++.so.6 (0x00007f9a5b233000)
libm.so.6 => /lib/libm.so.6 (0x00007f9a5afb0000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x00007f9a5ad99000)
libc.so.6 => /lib/libc.so.6 (0x00007f9a5aa46000)
/lib64/ld-linux-x86-64.so.2 (0x00007f9a5b53f000)

As you can see in the example, libX11 is not in the list of dynamically-linked libraries, as it was linked statically.

Beware: An .so file is always linked dynamically, even when specified with a full filename/path.

Some loaders (linkers) provide switches for turning dynamic loading on and off. If GCC is running on such a system (Solaris - and possibly others), then you can use the relevant option.

If you know which libraries you want to link statically, you can simply specify the static library file in the link line - by full path.

gcc -lsome_dynamic_lib code.c some_static_lib.a

You could also use ld option -Bdynamic

gcc <objectfiles> -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2

All libraries after it (including system ones linked by gcc automatically) will be linked dynamically.

The problem as I understand it is as follows. You have several libraries, some static, some dynamic and some both static and dynamic. gcc's default behavior is to link "mostly dynamic". That is, gcc links to dynamic libraries when possible but otherwise falls back to static libraries. When you use the -static option to gcc the behavior is to only link static libraries and exit with an error if no static library can be found, even if there is an appropriate dynamic library.

Another option, which I have on several occasions wished gcc had, is what I call -mostly-static and is essentially the opposite of -dynamic (the default). -mostly-static would, if it existed, prefer to link against static libraries but would fall back to dynamic libraries.

This option does not exist but it can be emulated with the following algorithm:

  1. Constructing the link command line with out including -static.

  2. Iterate over the dynamic link options.

  3. Accumulate library paths, i.e. those options of the form -L<lib_dir> in a variable <lib_path>

  4. For each dynamic link option, i.e. those of the form -l<lib_name>, run the command gcc <lib_path> -print-file-name=lib<lib_name>.a and capture the output.

  5. If the command prints something other than what you passed, it will be the full path to the static library. Replace the dynamic library option with the full path to the static library.

Rinse and repeat until you've processed the entire link command line. Optionally the script can also take a list of library names to exclude from static linking.

The following bash script seems to do the trick:

#!/bin/bash


if [ $# -eq 0 ]; then
echo "Usage: $0 [--exclude <lib_name>]. . . <link_command>"
fi


exclude=()
lib_path=()


while [ $# -ne 0 ]; do
case "$1" in
-L*)
if [ "$1" == -L ]; then
shift
LPATH="-L$1"
else
LPATH="$1"
fi


lib_path+=("$LPATH")
echo -n "\"$LPATH\" "
;;


-l*)
NAME="$(echo $1 | sed 's/-l\(.*\)/\1/')"


if echo "${exclude[@]}" | grep " $NAME " >/dev/null; then
echo -n "$1 "
else
LIB="$(gcc $lib_path -print-file-name=lib"$NAME".a)"
if [ "$LIB" == lib"$NAME".a ]; then
echo -n "$1 "
else
echo -n "\"$LIB\" "
fi
fi
;;


--exclude)
shift
exclude+=(" $1 ")
;;


*) echo -n "$1 "
esac


shift
done


echo

For example:

mostlyStatic gcc -o test test.c -ldl -lpthread

on my system returns:

gcc -o test test.c "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libdl.a" "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"

or with an exclusion:

mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread

I then get:

gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
gcc objectfiles -o program -Wl,-Bstatic -ls1 -ls2 -Wl,-Bdynamic -ld1 -ld2

you can also use: -static-libgcc -static-libstdc++ flags for gcc libraries

keep in mind that if libs1.so and libs1.a both exists, the linker will pick libs1.so if it's before -Wl,-Bstatic or after -Wl,-Bdynamic. Don't forget to pass -L/libs1-library-location/ before calling -ls1.

There is also -l:libstatic1.a (minus l colon) variant of -l option in gcc which can be used to link static library (Thanks to https://stackoverflow.com/a/20728782). Is it documented? Not in the official documentation of gcc (which is not exact for shared libs too): https://gcc.gnu.org/onlinedocs/gcc/Link-Options.html

-llibrary
-l library

Search the library named library when linking. (The second alternative with the library as a separate argument is only for POSIX compliance and is not recommended.) ... The only difference between using an -l option and specifying a file name is that -l surrounds library with ‘lib’ and ‘.a’ and searches several directories.

The binutils ld doc describes it. The -lname option will do search for libname.so then for libname.a adding lib prefix and .so (if enabled at the moment) or .a suffix. But -l:name option will only search exactly for the name specified: https://sourceware.org/binutils/docs/ld/Options.html

-l namespec
--library=namespec

Add the archive or object file specified by namespec to the list of files to link. This option may be used any number of times. If namespec is of the form :filename, ld will search the library path for a file called filename, otherwise it will search the library path for a file called libnamespec.a.

On systems which support shared libraries, ld may also search for files other than libnamespec.a. Specifically, on ELF and SunOS systems, ld will search a directory for a library called libnamespec.so before searching for one called libnamespec.a. (By convention, a .so extension indicates a shared library.) Note that this behavior does not apply to :filename, which always specifies a file called filename.

The linker will search an archive only once, at the location where it is specified on the command line. If the archive defines a symbol which was undefined in some object which appeared before the archive on the command line, the linker will include the appropriate file(s) from the archive. However, an undefined symbol in an object appearing later on the command line will not cause the linker to search the archive again.

See the -( option for a way to force the linker to search archives multiple times.

You may list the same archive multiple times on the command line.

This type of archive searching is standard for Unix linkers. However, if you are using ld on AIX, note that it is different from the behaviour of the AIX linker.

The variant -l:namespec is documented since 2.18 version of binutils (2007): https://sourceware.org/binutils/docs-2.18/ld/Options.html

to link dynamic and static library within one line, you must put static libs after dynamic libs and object files, like this:

gcc -lssl main.o -lFooLib -o main

otherwise, it will not work. it does take me sometime to figure it out.