static linking only some libraries

GccLinkerStatic Libraries

Gcc Problem Overview


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).

Gcc Solutions


Solution 1 - Gcc

gcc -lsome_dynamic_lib code.c some_static_lib.a

Solution 2 - Gcc

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.

Solution 3 - Gcc

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.

Solution 4 - Gcc

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.

Solution 5 - Gcc

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"

Solution 6 - Gcc

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

Solution 7 - Gcc

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.

Solution 8 - Gcc

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionpeoroView Question on Stackoverflow
Solution 1 - GccŠimon TóthView Answer on Stackoverflow
Solution 2 - GccDmitry YudakovView Answer on Stackoverflow
Solution 3 - GccwgodoyView Answer on Stackoverflow
Solution 4 - GccypnosView Answer on Stackoverflow
Solution 5 - GccjcofflandView Answer on Stackoverflow
Solution 6 - GccosgxView Answer on Stackoverflow
Solution 7 - GccJonathan LefflerView Answer on Stackoverflow
Solution 8 - GccVincentView Answer on Stackoverflow