How to compile a static library in Linux?
I have a question: How to compile a static library in Linux with gcc , i.e. I need to compile my source code into a file named out.a. Is it sufficient to simply compile with the command gcc -o out.a out.c ? I’m not quite familiar with gcc , hope anyone can give me a hand.
3 Answers 3
-c means to create an intermediary object file, rather than an executable.
This creates the static library. r means to insert with replacement, c means to create a new archive, and s means to write an index. As always, see the man page for more info.
Here a full makefile example:
TARGET = prog $(TARGET): main.o lib.a gcc $^ -o $@ main.o: main.c gcc -c $< -o $@ lib.a: lib1.o lib2.o ar rcs $@ $^ lib1.o: lib1.c lib1.h gcc -c -o $@ $< lib2.o: lib2.c lib2.h gcc -c -o $@ $< clean: rm -f *.o *.a $(TARGET)
- target: prerequisites - the rule head
- $@ - means the target
- $^ - means all prerequisites
- $ < - means just the first prerequisite
- ar - a Linux tool to create, modify, and extract from archives see the man pages for further information. The options in this case mean:
- r - replace files existing inside the archive
- c - create a archive if not already existent
- s - create an object-file index into the archive
To conclude: The static library under Linux is nothing more than a archive of object files.
main.c using the lib
#include #include "lib.h" int main ( void )
lib.h the libs main header
#ifndef LIB_H_INCLUDED #define LIB_H_INCLUDED #include "lib1.h" #include "lib2.h" #endif
lib1.c first lib source
#include "lib1.h" #include void fun1 ( int x )
lib1.h the corresponding header
#ifndef LIB1_H_INCLUDED #define LIB1_H_INCLUDED #ifdef __cplusplus extern “C” < #endif void fun1 ( int x ); #ifdef __cplusplus >#endif #endif /* LIB1_H_INCLUDED */
lib2.c second lib source
#include "lib2.h" #include void fun2 ( int x )
lib2.h the corresponding header
#ifndef LIB2_H_INCLUDED #define LIB2_H_INCLUDED #ifdef __cplusplus extern “C” < #endif void fun2 ( int x ); #ifdef __cplusplus >#endif #endif /* LIB2_H_INCLUDED */
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).
8 Answers 8
gcc -lsome_dynamic_lib code.c some_static_lib.a
Link libraries after object files — especially static libraries. In ancient and modern versions of the link environment (I'm not sure of the status quo for modestly old versions as of November 2010), listing the static library before the code.c file guarantees that the symbols in it will be ignored unless there happens to be a main() function in one of the library object files.
@jb by default, gcc links dynamically. When you use -lsome_dynamic_lib it gets linked dynamically as expected. But, when gcc is given a static library explicitly, it will always try to link it statically. There are, however, some tricky details about the order in which symbols get resolved; I'm not quite sure how that works. I've learned that, when in doubt, try rearranging the order of library flags 🙂
You could also use ld option -Bdynamic
gcc -static -lstatic1 -lstatic2 -Wl,-Bdynamic -ldynamic1 -ldynamic2
All libraries after it (including system ones linked by gcc automatically) will be linked dynamically.
-Wl,-Bdynamic requires GNU ld, so this solution doesn't work on systems where gcc uses the system ld (e.g. Mac OS X).
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 .
This works well for me, while using -static somewhere in the command fails (I assume it tries to link more things statically than only the libraries I want).
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.
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:
- Constructing the link command line with out including -static.
- Iterate over the dynamic link options.
- Accumulate library paths, i.e. those options of the form -L in a variable
- For each dynamic link option, i.e. those of the form -l , run the command gcc -print-file-name=lib.a and capture the output.
- 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 ]. . . " 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 "$" | 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
mostlyStatic gcc -o test test.c -ldl -lpthread
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"
mostlyStatic --exclude dl gcc -o test test.c -ldl -lpthread
gcc -o test test.c -ldl "/usr/lib/gcc/x86_64-linux-gnu/4.7/../../../x86_64-linux-gnu/libpthread.a"
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
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.