Why does the order in which libraries are linked sometimes cause errors in GCC?
(See the history on this answer to get the more elaborate text, but I now think it's easier for the reader to see real command lines).
Common files shared by all below commands
// a depends on b, b depends on d
$ cat a.cpp
extern int a;
int main() {
return a;
}
$ cat b.cpp
extern int b;
int a = b;
$ cat d.cpp
int b;
Linking to static libraries
$ g++ -c b.cpp -o b.o
$ ar cr libb.a b.o
$ g++ -c d.cpp -o d.o
$ ar cr libd.a d.o
$ g++ -L. -ld -lb a.cpp # wrong order
$ g++ -L. -lb -ld a.cpp # wrong order
$ g++ a.cpp -L. -ld -lb # wrong order
$ g++ a.cpp -L. -lb -ld # right order
The linker searches from left to right, and notes unresolved symbols as it goes. If a library resolves the symbol, it takes the object files of that library to resolve the symbol (b.o out of libb.a in this case).
Dependencies of static libraries against each other work the same - the library that needs symbols must be first, then the library that resolves the symbol.
If a static library depends on another library, but the other library again depends on the former library, there is a cycle. You can resolve this by enclosing the cyclically dependent libraries by -(
and -)
, such as -( -la -lb -)
(you may need to escape the parens, such as -\(
and -\)
). The linker then searches those enclosed lib multiple times to ensure cycling dependencies are resolved. Alternatively, you can specify the libraries multiple times, so each is before one another: -la -lb -la
.
Linking to dynamic libraries
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -L. -ld -o libb.so # specifies its dependency!
$ g++ -L. -lb a.cpp # wrong order (works on some distributions)
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong order
$ g++ -Wl,--as-needed a.cpp -L. -lb # right order
It's the same here - the libraries must follow the object files of the program. The difference here compared with static libraries is that you need not care about the dependencies of the libraries against each other, because dynamic libraries sort out their dependencies themselves.
Some recent distributions apparently default to using the --as-needed
linker flag, which enforces that the program's object files come before the dynamic libraries. If that flag is passed, the linker will not link to libraries that are not actually needed by the executable (and it detects this from left to right). My recent archlinux distribution doesn't use this flag by default, so it didn't give an error for not following the correct order.
It is not correct to omit the dependency of b.so
against d.so
when creating the former. You will be required to specify the library when linking a
then, but a
doesn't really need the integer b
itself, so it should not be made to care about b
's own dependencies.
Here is an example of the implications if you miss specifying the dependencies for libb.so
$ export LD_LIBRARY_PATH=. # not needed if libs go to /usr/lib etc
$ g++ -fpic -shared d.cpp -o libd.so
$ g++ -fpic -shared b.cpp -o libb.so # wrong (but links)
$ g++ -L. -lb a.cpp # wrong, as above
$ g++ -Wl,--as-needed -L. -lb a.cpp # wrong, as above
$ g++ a.cpp -L. -lb # wrong, missing libd.so
$ g++ a.cpp -L. -ld -lb # wrong order (works on some distributions)
$ g++ -Wl,--as-needed a.cpp -L. -ld -lb # wrong order (like static libs)
$ g++ -Wl,--as-needed a.cpp -L. -lb -ld # "right"
If you now look into what dependencies the binary has, you note the binary itself depends also on libd
, not just libb
as it should. The binary will need to be relinked if libb
later depends on another library, if you do it this way. And if someone else loads libb
using dlopen
at runtime (think of loading plugins dynamically), the call will fail as well. So the "right"
really should be a wrong
as well.
lorder
+ tsort
do. But sometimes there is no order, if you have cyclic references. Then you just have to cycle through the libraries list until everything is resolved. –
Peg --start-group
and --end-group
options using gcc, you must precede with -Wl,<option>
as in -Wl,--start-group <archives> -Wl,--end-group
. It is mentioned in the manual page which is referenced, but a quick mention here is helpful too. –
Considered --as-needed
(the default in ubuntu IIRC) and the libraries are not linked to everything they depend on (--allow-shlib-undefined
), but that's probably too rare for a general introduction. –
Garlan -l
"leaves the compiler open to choose the dynamic libA.so and libB.so variant, if available" is still correct. From the GCC manual: The linker searches a standard list of directories for the library, which is actually a file named lib<library>.a. The linker then uses this file as if it had been specified precisely by name. –
Ethelyn -( ... -)
group). The "quirk", when linking ELF, is that dynamic libraries do appear to be arbitrarily rescanned, without grouping, so incorrect ordering isn't punished; for non-ELF, (e.g. Windows-PE), wrongly ordered DLLs are punished. –
Apodosis ld
component of GUN binutils; it's a "quirk" w.r.t. this required ld
order that the design of ELF is such that it doesn't care anyway, so "correct" order w.r.t. ld
documented behaviour isn't enforced. This causes complacency about ld-correct ordering among developers who primarily target ELF; I've lost count of the number of times project porting from Linux to MS-Windows has resulted in questions on MinGW-Users ML, because "broken" link order prevents DLL linking. –
Apodosis info ld Invocation
-> Options
, para '-l NAMESPEC
": "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." There is no suggestion that this applies differently to static and dynamically linked libraries. –
Apodosis gcc -lshared-foo bar.o -o bar
, which will result in undefined reference errors on non-ELF platforms such as MS-Windows, if shared-foo.dll is needed. Surely, it is better to encourage usage such as gcc bar.o -lshared-foo -o bar
, which should work equally well on both ELF and non-ELF platforms. –
Apodosis gcc bar.o -lshared-foo -o bar
style, (which is consistent with the required ordering specified in ld
documentation), it may just save them from being severely berated, when they complain for the gadzillionth time that gcc -lshared-foo bar.o -o bar
doesn't work on MS-Windows; just because it fortuitously works on Linux doesn't make it automatically "right": it isn't best practice, nor even good practice. –
Apodosis gcc -lshared-foo bar.o
is incorrect if bar.o depends on shared-foo. There are no dependencies between the shared libraries according to my answer (you can mix the -lsharedfoo
anyway you like), but there may be dependencies of the .o or .a files to a shared library. Then you have to put the bar.o
file before. Not doing that even breaks on ELF systems on recent distributions (with --as-needed
). –
Peg libd.a
has a file scope c++ object that needs to be initialized before libb.a
. Listing libd.a
first should ensure the static initializer runs first, but that leads into the wrong order category (though it is the right order for static initialization). –
Gauntlett The GNU ld linker is a so-called smart linker. It will keep track of the functions used by preceding static libraries, permanently tossing out those functions that are not used from its lookup tables. The result is that if you link a static library too early, then the functions in that library are no longer available to static libraries later on the link line.
The typical UNIX linker works from left to right, so put all your dependent libraries on the left, and the ones that satisfy those dependencies on the right of the link line. You may find that some libraries depend on others while at the same time other libraries depend on them. This is where it gets complicated. When it comes to circular references, fix your code!
Here's an example to make it clear how things work with GCC when static libraries are involved. So let's assume we have the following scenario:
myprog.o
- containingmain()
function, dependent onlibmysqlclient
libmysqlclient
- static, for the sake of the example (you'd prefer the shared library, of course, as thelibmysqlclient
is huge); in/usr/local/lib
; and dependent on stuff fromlibz
libz
(dynamic)
How do we link this? (Note: examples from compiling on Cygwin using gcc 4.3.4)
gcc -L/usr/local/lib -lmysqlclient myprog.o
# undefined reference to `_mysql_init'
# myprog depends on libmysqlclient
# so myprog has to come earlier on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# we have to link with libz, too
gcc myprog.o -lz -L/usr/local/lib -lmysqlclient
# undefined reference to `_uncompress'
# libz is needed by libmysqlclient
# so it has to appear *after* it on the command line
gcc myprog.o -L/usr/local/lib -lmysqlclient -lz
# this works
If you add -Wl,--start-group
to the linker flags it does not care which order they're in or if there are circular dependencies.
On Qt this means adding:
QMAKE_LFLAGS += -Wl,--start-group
Saves loads of time messing about and it doesn't seem to slow down linking much (which takes far less time than compilation anyway).
/usr/bin/ld: missing --end-group; added as last command line option
–
Fickle --end-group
. –
Gallant Another alternative would be to specify the list of libraries twice:
gcc prog.o libA.a libB.a libA.a libB.a -o prog.x
Doing this, you don't have to bother with the right sequence since the reference will be resolved in the second block.
A quick tip that tripped me up: if you're invoking the linker as "gcc" or "g++", then using "--start-group" and "--end-group" won't pass those options through to the linker -- nor will it flag an error. It will just fail the link with undefined symbols if you had the library order wrong.
You need to write them as "-Wl,--start-group" etc. to tell GCC to pass the argument through to the linker.
You may can use -Xlinker option.
g++ -o foobar -Xlinker -start-group -Xlinker libA.a -Xlinker libB.a -Xlinker libC.a -Xlinker -end-group
is ALMOST equal to
g++ -o foobar -Xlinker -start-group -Xlinker libC.a -Xlinker libB.a -Xlinker libA.a -Xlinker -end-group
Careful !
- The order within a group is important ! Here's an example: a debug library has a debug routine, but the non-debug library has a weak version of the same. You must put the debug library FIRST in the group or you will resolve to the non-debug version.
- You need to precede each library in the group list with -Xlinker
I have seen this a lot, some of our modules link in excess of a 100 libraries of our code plus system & 3rd party libs.
Depending on different linkers HP/Intel/GCC/SUN/SGI/IBM/etc you can get unresolved functions/variables etc, on some platforms you have to list libraries twice.
For the most part we use structured hierarchy of libraries, core, platform, different layers of abstraction, but for some systems you still have to play with the order in the link command.
Once you hit upon a solution document it so the next developer does not have to work it out again.
My old lecturer used to say, "high cohesion & low coupling", it’s still true today.
Link order certainly does matter, at least on some platforms. I have seen crashes for applications linked with libraries in wrong order (where wrong means A linked before B but B depends on A).
ld : linker daemon has been around since 1990's and it's not a program we spend a lot of time with because the automake/cmake makefile recipes are pretty easy to obtain most behaviour.
It has a LDFLAGS option known as --start-group or -(
-( archives -)
--start-group archives --end-group
This allows you to search more than once for symbols while recursing through the specific archives looking for symbols.
This is from the linker manual:
--start-group archives --end-group
The archives should be a list of archive files. They may be either explicit file names, or `-l' options. The specified archives are searched repeatedly until no new undefined references are created. Normally, an archive is searched only once in the order that it is specified on the command line. If a symbol in that archive is needed to resolve an undefined symbol referred to by an object in an archive that appears later on the command line, the linker would not be able to resolve that reference. By grouping the archives, they all be searched repeatedly until all possible references are resolved. Using this option has a significant performance cost. It is best to use it only when there are unavoidable circular references between two or more archives.
The linker daemon is like a spaghetti colander, a bunch of symbols arrive at the colander walls and poke through the holes, and the linker must track all symbols and connect together all the symbols by address linkage. If it misses some, the program will crash.
Here's what you need to know about static versus dynamic libraries When linking symbols for static libraries, ld starts from left to right and MAKES ONE PASS through all symbol tables. If you want to resolve dependency loops then you can add the same archive into the linker archive objects list more than once.
When dealing with dynamic libraries, all symbols must be resolved by the dynamic libraries so nothing gets missed.
Therefore, the one time behaviour will fail is when some symbols are static and the dynamic symbols will not be around during the single pass.
The answer then, is place the static libraries TO THE LEFT of the dynamic libraries in the make recipe.
It matters where you put your files for a linker to resolve.
It is a general rule that you put files where symbol
is referenced BEFORE file where symbol
is defined.
To understand the reason of error you have to understand how linker works:
All files passed to linker processed from left to right
Linker collects undefined symbols from files to the pool of undefined symbols
If object from archive doesn’t resolve any symbols from pool of undefined symbols, then it dropped
In a case:
gcc -o libfunc2.so -shared -fPIC -lfunc1 func2.o
We put archive library where symbol show1()
is defined before object file func2.o
where it is referenced. So libfunc1.a
doesn't resolve any symbols and is dropped.
And from GCC -l library option:
It makes a difference where in the command you write this option; the linker searches and processes libraries and object files in the order they are specified.
Thus, foo.o -lz bar.o
searches library z
after file foo.o
but before bar.o
. If bar.o
refers to functions in library z
, those functions may not be loaded.
© 2022 - 2024 — McMap. All rights reserved.
gcc
changed to more-strict behavior (relatively) recently. – Shipyard