"undefined reference" when linking against a static library - c++

g++ (Ubuntu/Linaro 4.4.4-14ubuntu5) 4.4.5
I have the following static library called sdpAPI.a. I am having problems trying to link it with my test application. Just wondering if I am doing something wrong. The static library has been built with g++;
My directory is as follows:
/projects/unit_test/main.c
/projects/unit_test/sdp/inc/sdpAPH.h
/projects/unit_test/sdp/lib/sdpAPI.a
My source code is this:
#include <stdio.h>
#include "sdpAPI.h"
int main(void)
{
printf("----- TEST SDP ------\n");
try {
sdpSessionDescription sdp;
sdp.clear();
}
catch(...) {
printf("----- TEST FAILED --------\n");
return 0;
}
printf("------ TEST SUCCESSFULL ------\n");
return 0;
}
And my Makefile is this:
OBJECT_FILES = main.o
CC = g++
CFLAGS = -Wall -Wextra -Wunreachable-code -ggdb -O0
TARGET = sdp_demo
INC_PATH = -I sdp/inc
LIB_PATH = -L sdp/lib/sdpAPI.a
$(TARGET): $(OBJECT_FILES)
$(CC) $(CFLAGS) $(INC_PATH) $(LIB_PATH) $(OBJECT_FILES) -o $(TARGET)
main.o: main.c
$(CC) $(CFLAGS) $(INC_PATH) $(LIB_PATH) -c main.c
clean:
rm -f $(TARGET) $(OBJECT_FILES) *~
These are the linker errors I am getting:
undefined reference to `sdpSessionDescription::sdpSessionDescription()'
undefined reference to `sdpSessionDescription::clear()'
undefined reference to `sdpSessionDescription::~sdpSessionDescription()'
undefined reference to `sdpSessionDescription::~sdpSessionDescription()'
Many thanks for any suggestions,

-L specifies the library path, not a specific library. You probably want -L sdp/lib -l sdpAPI to specify both the path and the library name.
Although it will try to prefix and postfix your library name with lib and either .a or .sl (or similar).
So you may also need to rename your library to libsdpAPI.a as per the gcc manpage:
-l xyz
The linker searches a standard list of directories for the library, which is actually a file named libxyz.a.
Also keep in mind that the order of things in the command line matters. By doing $(CC) $(CFLAGS) $(INC_PATH) $(LIB_PATH) $(OBJECT_FILES) -o $(TARGET) (libraries before objects), there are no unresolved symbols at the point where you list the library, so nothing will be brought in from that library.
Then, when you finally bring in the objects (with their unresolved symbols), they stay unresolved because there are no libraries listed after that.
You should usually do libraries after objects:
$(CC) $(CFLAGS) $(INC_PATH) $(OBJECT_FILES) $(LIB_PATH) -o $(TARGET)
to ensure all unresolved symbols are known before checking the libraries.
This won't catch all problems (such as co-dependent libraries which can be fixed using other means) but it will ensure all unresolved symbols in the object files are known about before looking at the libraries.
From the same section of the man page quoted above:
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 z, those functions may not be loaded.

-L is used to specify a library path :
-Ldir Add directory dir to the list of directories to be searched for -l.
-l is what you need to specify which library to link against :
-l library Search the library named library when linking.
You probably need -L sdp/lib/ -l sdpAPI

How exactly the different options especially -l and -static worked confused me for a long time. Finally did a man gcc to get more details that I wasn't able to find online. Hope this helps someone else too
-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.)
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
z, those functions may not be loaded.
The linker searches a standard list of directories for the library,
which is actually a file named liblibrary.a. The linker then uses
this file as if it had been specified precisely by name.
The directories searched include several standard system
directories plus any that you specify with -L.
Normally the files found this way are library files---archive files
whose members are object files. The linker handles an archive file
by scanning through it for members which define symbols that have
so far been referenced but not defined. But if the file that is
found is an ordinary object file, it is linked in the usual
fashion. 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.
-static
On systems that support dynamic linking, this prevents linking with
the shared libraries. On other systems, this option has no effect.
This option will not work on Mac OS X unless all libraries
(including libgcc.a) have also been compiled with -static. Since
neither a static version of libSystem.dylib nor crt0.o are
provided, this option is not useful to most people.
-Ldir
Add directory dir to the list of directories to be searched for -l.

Three flags you need to know:
-Ldir
-lLIB
-static
Since you want to link with static library, you need the third flag. Otherwise, you will end up with linking with a dynamic library.

Related

C++ Creating a dynamic library using source, static archives and other dynamic libraries

In my use case, I have YAML-CPP, SQLite3, and my 'data.cpp' file that I want to all be combined into the same dynamic library, 'libdata.so'.
I first compiled yaml-cpp (as an archive):
mkdir -p "build"
cd "build"
cmake ..
make -j5
to get 'libyaml-cpp.a'.
I then compile sqlite3:
gcc -c -o libsqlite3.a sqlite3.c -lpthread -ldl
to get 'libsqlite3.a'. I know that this a C-based file, and there are differences between it and C++, but I've read that it shouldn't make too much difference here. I also know that I'm using -lpthread -ldl which is for dynamic loading, but I'm not sure how to get around it.
My question is: Can I compile my 'data.cpp' file with YAMP-CPP and SQLite3 such that they all exist in the same 'libdata.so' output file (where the linker will use the YAML-CPP and SQLite3 functions contained in 'libdata.so' when they're called by 'data.cpp')?
I have tried:
g++ -c -fPIC -o libdata.so \
-Wl,--whole-archive libsqlite3.a \
-Wl,--whole-archive libyaml-cpp.a \
-ldl -lpthread \
data.cpp
(for the sake of the snippet, all files reside in the same directory)
UPDATE
I added the suggestion from botje to the line and it helped in part. After more research, I found a few more pieces that progressed further:
gcc -DSQLITE_OMIT_LOAD_EXTENSION -c -fPIC -lpthread -o libsqlite3.a sqlite3.c
mkdir -p "build"
cd "build"
env CFLAGS='-fPIC' CXXFLAGS='-fPIC' cmake ..
make -j$(CORES)
cd ..
cp "build/libyaml-cpp.a" ./
g++ -shared -fPIC -o libdata.so \
-L./ \
-Wl,-Bdynamic data.cpp \
-Wl,-Bstatic -lsqlite3 -lyaml-cpp \
-Wl,-Bdynamic -lpthread
g++ -L./ -ldata -o tester tester.cpp
The library now compiles, however, when I try to link against it with 'tester.cpp', I get the error:
/usr/bin/ld: libdata.so: undefined reference to YAML::detail...
I'm guessing this may be a flag ordering issue, but I'm not sure what order it should be then. Placing the flags for SQLite3 and YAML-CPP before the data.cpp argument fails to compile the shared library.
After some more research, here's the method that worked for me (with extra verbosity):
# Compile SQLite3:
# - Disable the plugin loader (removes the libdl dependency)
# - Compile only (-c)
# - Use Position Independent Code (-fPIC)
# - Add the PThread library
# - After compilation, archive object (for completeness)
gcc -DSQLITE_OMIT_LOAD_EXTENSION -c -fPIC -pthread -o sqlite3.o sqlite3.c
# Compile YAML-CPP
# - Create (and enter) a build directory
# - Run CMAKE with -fPIC enabled
# - Run MAKE
# - Exit and copy archive from build directory
mkdir -p "build"
cd "build"
env CFLAGS='-fPIC' CXXFLAGS='-fPIC' cmake ..
make -j$(CORES)
cd ..
cp build/libyaml-cpp.a libyaml-cpp.a
# Compile Shared Library
# - Ensure shared (-shared) (also prevents looking for a 'main')
# - Use Position Independent Code (-fPIC)
# - Use current directory for locating libraries
# - Set target CPP file
# - STATICALLY link from SQLite3 and YAML-CPP archives
# - DYNAMICALLY link from PThread library (used by SQLite3 for thread-safe access)
g++ -shared -fPIC -o libdata.so data.cpp \
-L./ \
-Wl,-Bstatic -l:sqlite3.o -lyaml-cpp \
-Wl,-Bdynamic -pthread
# Compile Test Program
# - Specify current directory for includes and libraries
# - Link dynamically to 'libdata.so'
g++ -I./ -L./ -ldata -o tester tester.cpp
The last issue I encountered ended up being a missing include directory for YAML-CPP.
A couple of notes for credit:
#Botje: For pointing out that I need -shared and not -c in the compilation of a shared library. (libdata.so)
#Maxim Egorushkin: For linking to a very useful document on the matter.
One thing to note as well, is that when linking against a C library in a C++ program, you may need to use 'extern "C"' (as elaborated in the linked page). This is especially important when using the SQLite3 library.
Note that linking .a files into .so is rather unusual. People do that, but for wrong reasons.
When you link a .so, provide individual .o files compiled with -fPIC. Don't pack those .o files into .a first, that doesn't make much sense.
Why? Because .a file is merely a bunch of .o files. There is no point in making a .a file from a bunch of .o files just to turn that then into .so file.
To make a static library one builds .o files and packs them into .a. In fact, static library is a wrong name, technically, .a file is an archive (of .o files). Archives cannot link to other libraries they need because .o file cannot carry dependencies, neither can .a files.
To make a shared library one builds .o files with -fPIC option and links them into .so, along with any required libraries (static or shared). This is the .so file that carries dependency information on other .so files, .a archives are linked in.
When you build a .a that means you trade sharing code (in form of .so) for maximum execution efficiency (in the form of linking parts of .a into your executable directly). That means you build .o files without -fPIC option (it introduces extra access overhead) and bundle them into .a. Note, that .a file cannot refer to other libraries it needs (unlike .so), it is just a bunch of .o files. Static library .a is almost just a form to refer to multiple .o file. For local builds you should use thin archives that don't copy .o into .a rather refer to .o.
Also note, that when you link .a archive, only those .o files from the archive get linked into your executable (or shared library) that resolve currently unresolved symbols (unless --whole-archive). That means, if you have a global/namespace scope object with a constructor and link that into .so then it links in everything from the supplied object files and your global object constructor runs as expected. However, if you link in .a, the linker only pulls in those symbols/object-files that resolve currently undefined symbols, so that if your global object isn't referred to (possibly indirectly) from a file with main function, it won't be linked in and its constructor won't run.
For your purpose of building one .so from multiple 3rd-party libraries, you should compile those libraries' object files with -fPIC but not link them into .a. Then you link all those .o files into one .so file with all the libraries required by those comprising .o (either statically or dynamically).
With regards to -lpthread this is sadly a very common misconception perpetuated by POSIX standard wording being out of date.
In the old days there were two implementations of Pthreads API on Linux (and probably other systems): LinuxThreads and NPTL. POSIX standard merely says that if you want POSIX-compliant behaviour then link NPTL, not LinuxThreads and that is what that -lpthread linker option for. They fail to explain this reasoning or remove that sentence because it is woefully out of date.
Nowadays, modern Linux, and probably other systems, provide only the POSIX-compliant version. Hence, that -lpthread flag is obsolete, serves no purpose and isn't sufficient to build correct multi-threaded programs.
When you build multi-threaded programs you need to follow the documentation of your compiler. gcc and clang require using -pthread flag for both compiling and linking.

Using a shared library in another shared library

I am creating a shared library from a class from an example I got here C++ Dynamic Shared Library on Linux. I would like to call another shared library from the shared library created and then use it in the main program. So I have the myclass.so library and I want to call another library say anotherclass.so from the myclass.so library and then use this myclass.so library in the main program. Any idea on how I can do this please.
There is more than one way in which multiple shared libraries may be added to
the linkage of a program, if you are building all the libraries, and the program,
yourself.
The elementary way is simply to explicitly add all of the libraries to the
the linkage of the program, and this is the usual way if you are building only the
program and linking libraries built by some other party.
If an object file foo.o in your linkage depends on a library libA.so, then
foo.o should precede libA.so in the linkage sequence. Likewise if libA.so
depends on libB.so then libA.so should precede libB.so. Here's an illustration.
We'll make a shared library libsquare.so from the files:
square.h
#ifndef SQUARE_H
#define SQUARE_H
double square(double d);
#endif
and
square.cpp
#include <square.h>
#include <cmath>
double square(double d)
{
return pow(d,2);
}
Notice that the function square calls pow, which is declared in the
Standard header <cmath> and defined in the math library, libm.
Compile the source file square.cpp to a position-independent object file
square.o:
$ g++ -Wall -fPIC -I. -c square.cpp
Then link square.o into a shared library libsquare.so:
$ g++ -shared -o libsquare.so square.o
Next we'll make another shared library libcube.so from these files:
cube.h
#ifndef CUBE_H
#define CUBE_H
double cube(double d);
#endif
and
cube.cpp
#include <cube.h>
#include <square.h>
double cube(double d)
{
return square(d) * d;
}
See that the function cube calls square, so libcube.so is going to
depend on libsquare.so. Build the library as before:
$ g++ -Wall -fPIC -I. -c cube.cpp
$ g++ -shared -o libcube.so cube.o
We haven't bothered to link libsquare with libcube, even though libcube
depends on libsquare, and even though we could have, since we're building libcube.
For that matter, we didn't bother to link libm with libsquare. By default the
linker will let us link a shared library containing undefined references, and it
is perfectly normal. It won't let us link a program with undefined references.
Finally let's make a program, using these libraries, from this file:
main.cpp
#include <cube.h>
#include <iostream>
int main()
{
std::cout << cube(3) << std::endl;
return 0;
}
First, compile that source file to main.o:
$ g++ -Wall -I. -c main.cpp
Then link main.o with all three required libraries, making sure to list
the linker inputs in dependency order: main.o, libcube.so, libsquare.so, libm.so:
$ g++ -o prog main.o -L. -lcube -lsquare -lm
libm is a system library so there's no need to tell the linker where to look for
it. But libcube and libsquare aren't, so we need to tell the linker to look for
them in the current directory (.), because that's where they are. -L. does that.
We've successfully linked ./prog, but:
$ ./prog
./prog: error while loading shared libraries: libcube.so: cannot open shared object file: No such file or directory
It doesn't run. That's because the runtime loader doesn't know where to find libcube.so (or libsquare.so, though it didn't get that far).
Normally, when we build shared libraries we then install them in one of the loader's default
search directories (the same ones as the linker's default search directories), where they're available to any program, so this wouldn't happen. But I'm not
going to install these toy libraries on my system, so as a workaround I'll prompt the loader where to look
for them by setting the LD_LIBRARY_PATH in my shell.
$ export LD_LIBRARY_PATH=.
$ ./prog
27
Good. 3 cubed = 27.
Another and better way to link a program with shared libraries that aren't located
in standard system library directories is to link the program using the linker's
-rpath=DIR option. This will write some information into the executable to tell
the loader that it should search for required shared libraries in DIR before it tries
the default places.
Let's relink ./prog that way (first deleting the LD_LIBRARY_PATH from the shell so that it's not effective any more):
$ unset LD_LIBRARY_PATH
$ g++ -o prog main.o -L. -lcube -lsquare -lm -Wl,-rpath=.
And rerun:
$ ./prog
27
To use -rpath with g++, prefix it with -Wl, because it's an option for linker, ld,
that the g++ frontend doesn't recognise: -Wl tells g++ just to pass the
option straight through to ld.
I would like to add some points to the response of #Mike.
As you do not link libcube library with libsquare you are creating a sort of "incomplete library". When I say incomplete, I meant that when you link your application you must link it with both libcube and libsquare even though it does not use any symbol directly from libsquare.
It is better to link libcube directly with libsquare. This link will create the library with a NEEDED entry like:
readelf -d libcube.so
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libsquare.so]
Then when you link your application you can do:
g++ -o prog main.o -L. -lcube
Although, this will not link because the linker tries to locate the NEEDED library libsquare. You must precise its path by adding -Wl,-rpath-link=. to the linking command:
g++ -o prog main.o -L. -lcube -Wl,-rpath-link=.
Note: For runtime, you must still set LD_LIBRARY_PATH or link with rpath as mentioned by #Mike.
In your library if you are using any other shared library so simply your library user is also dependent on that library. While creating library you can use -l so the linker have notion for shared library and it will link when required.
But when you deliver your library as its dependent on some other library you need to export that too along with your and provide some environment variable or linker flag to load it from specified path (Your exported package). That will not lead any discrepancy other wise if its some standard library function user might get definition from his system's some other library and will lead in disastrous situation.
Simply use the library like you'd use it in any other application. You don't have to link to anotherclass.so, just to myclass.so.
However, you will have to make both libraries (myclass.so and anotherclass.so) available for your later application's runtime. If one of them is missing you'll get runtime errors just like it is with any other application.

Link only needed symbols when compiling an executable with a Shared Library

I'm working on a heavy project that has a lot of static libraries that are interdependent. Furthermore some symbols are redundant between some libraries, with different implementations. My goal is to make the project work with shared libraries.
I tried to compile an executable with one of my shared libs, and I get undefined symbols errors on functions that my executable isn't using. After some research I understood that the dynamic linker works in very different ways than the static linker. If I understood right, when linking a shared library, all symbols need to be resolved as the whole library is loaded in the memory.
A simple workaround would be to add all the dependencies of my libraries when compiling the executable. But they're so full of dependencies that this sometimes means adding 10+ libraries to the command line, and this would be for something like a hundred executable.
So far I tried using -Wl,--as-needed, -Wl,--unresolved-symbols=ignore-in-shared-libs, and opening the shared object with dlopen to get the function I want with dlsym. But all of these methods fail at one point or another.
My question is: Are you forced to resolve every undefined symbol of a dynamic library when linking it against an executable ?
Details of dynamic linking and the kinds of objects involved vary across environments and toolchains. On Linux, where you say you are, and on Solaris, and several other UNIX-y platforms, you are looking at ELF objects and semantics.
So far I tried using -Wl,--as-needed,
-Wl,--unresolved-symbols=ignore-in-shared-libs,
These both have their full effect at (static) link time. The first tells the linker that the libraries following it on the command line should be linked in only if they resolve at least one as-yet undefined symbol. The latter tells the linker to not worry about resolving symbols in shared libraries included in the link. That has nothing to do with the behavior of the dynamic linker when you run the program.
and opening the shared object with dlopen to get the function I want with dlsym.
dlopen instructs the dynamic linker to link in a shared object at runtime that was not specified in the binary as a required shared library. Its behavior at that point can be modulated by the flags passed to dlopen, but the options available are not more than can be specified at link time. There is little reason to use dlopen when you actually know at link time what libraries you need.
Are you forced to resolve every undefined symbol of a dynamic library
when linking it against an executable ?
Focusing on ELF and the GNU toolchain, no. -Wl,--unresolved-symbols=ignore-in-shared-libs serves precisely the purpose of avoiding that. But as you've discovered, that comes with caveats.
In the first place, in every shared object, every symbol referring to data needs to be resolved at runtime by the dynamic linker, no matter how you linked the various shared objects, including the main program. This is primarily an operational consideration -- the dynamic linker has no way to defer resolving symbols referring to objects because it has no good way to trap attempts to access them.
On the other hand, it is possible to defer resolution of symbols referring to functions until their first use. In fact, this is the GNU linker's default, but you can reaffirm this by passing -Wl,-z,lazy to gcc when linking. Note well, however, that this sets a property of the object being linked, so you should ensure that every shared object is built with that link option (but ordinarily they are because, again, that's the default).
Additionally, you should be aware that the dynamic linker's behavior can be influenced by environment variables. In particular, lazy binding will be disabled if the dynamic linker finds LD_BIND_NOW set to a nonempty string in the runtime environment.
A simple workaround would be to add all the dependencies of my
libraries when compiling the executable. But they're so full of
dependencies that this sometimes means adding 10+ libraries to the
command line, and this would be for something like a hundred
executable.
And what's the big deal with that, really? Surely you have a well-factored Makefile (or several) to help you, so it shouldn't be a big deal to ensure that all the libraries are linked. Right?
But you should also consider refactoring your libraries, especially if "interdependent" means there are loops in the dependency graph. Dynamic linking is different from static linking, as you've discovered, and the differences are sometimes more subtle than those you're presently struggling with. Although it is not a hard rule, I urge you to avoid creating situations where the shared objects used by one process contain among them multiple definitions of the same external symbol, especially if that symbol is actually used.
Update
The above discussion focuses on linking shared libraries to an executable, but there is another important consideration: how the libraries themselves are linked. Each ELF object, whether executable or shared library, carries its own list of needed shared libraries. The dynamic linker will recursively include all of these in the list of shared libraries to be loaded (immediately) at program startup, notwithstanding its behavior with respect to lazy binding of symbols referring to functions.
Therefore, if you want an executable not to require a given shared library X, then not only that executable itself but also every shared library it does rely upon must avoid expressing a dependency on X. If some of the shared libs require X when used in conjunction with other programs, then that puts the onus on you to link in all the needed libraries when building those programs (otherwise, you can arrange to link only direct dependencies). You can tell the GNU linker to build shared libraries this way by passing it the --allow-shlib-undefined flag.
Here is a complete proof of concept:
main.c
int mul(int, int);
int main(void) {
return mul(2, 3);
}
mul.c
int add(int, int);
int mul(int x, int y) {
return x * y;
}
int mul2(int x, int y) {
return add(x, y) * add(x, -y);
}
Makefile
CC = gcc
LD = gcc
CFLAGS = -g -O2 -fPIC -DPIC
LDFLAGS = -Wl,--unresolved-symbols=ignore-in-shared-libs
SHLIB_LDFLAGS = -shared -Wl,--allow-shlib-undefined
all: main
main: main.o libmul.so
$(LD) $(CFLAGS) $(LDFLAGS) -o $# $^
libmul.so: mul.o
$(LD) $(CFLAGS) $(SHLIB_LDFLAGS) -o $# $^
clean:
rm -f main main.o libmul.so mul.o
Demo
$ make
gcc -g -O2 -fPIC -DPIC -c -o main.o main.c
gcc -g -O2 -fPIC -DPIC -c -o mul.o mul.c
gcc -g -O2 -fPIC -DPIC -shared -Wl,--allow-shlib-undefined -o libmul.so mul.o
gcc -g -O2 -fPIC -DPIC -Wl,--unresolved-symbols=ignore-in-shared-libs -o main main.o libmul.so
$ LD_LIBRARY_PATH=$(pwd) ./main
$ echo $?
6
$
Note that the -zlazy linker option discussed in comments is omitted, as it's the default.

g++ cannot find shared library

I apologize that this is redundant, but none of the answers available seem to be able to solve my problem. I am attempting to compile an executable using a shared object library. The shared object library is called libsession.so and is found in the same directory that I am compiling the executable. To compile and link, I use the following command
g++ test_main.cpp -o program -std=c++11 -I ../src/base -L. -lsession
Unforutanely, I get the cannot find -lsession error when linking. If I change the command to directly reference the shared library as follows
g++ test_main.cpp -o program -std=c++11 -I ../src/base libsession.so
then the executable compiles/links and all is well. Does anyone have any thoughts as to what I may be doing wrong?
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.
https://gcc.gnu.org/onlinedocs/gcc-3.0/gcc_3.html#SEC16

g++: In what order should static and dynamic libraries be linked?

Let's say we got a main executable called "my_app" and it uses several other libraries: 3 libraries are linked statically, and other 3 are linked dynamically.
In which order should they be linked against "my_app"?
But in which order should these be linked?
Let's say we got libSA (as in Static A) which depends on libSB, and libSC which depends on libSB:
libSA -> libSB -> libSC
and three dynamic libraries:libDA -> libDB -> libDC (libDA is the basic, libDC is the highest)
in which order should these be linked? the basic one first or last?
g++ ... -g libSA libSB libSC -lDA -lDB -lDC -o my_app
seems like the currect order, but is that so? what if there are dependencies between any dynamic library to a static one, or the other way?
In the static case, it doesn't really matter, because you don't actually link static libraries - all you do is pack some object files together in one archive. All you have to is compile your object files, and you can create static libraries right away.
The situation with dynamic libraries is more convoluted, there are two aspects:
A shared library works exactly the same way as static library (except for shared segments, if they are present), which means, you can just do the same - just link your shared library as soon as you have the object files. This means for example symbols from libDA will appear as undefined in libDB
You can specify the libraries to link to on the command line when linking shared objects. This has the same effect as 1., but, marks libDB as needing libDA.
The difference is that if you use the former way, you have to specify all three libraries (-lDA, -lDB, -lDC) on the command line when linking the executable. If you use the latter, you just specify -lDC and it will pull the others automatically at link time. Note that link time is just before your program runs (which means you can get different versions of symbols, even from different libraries).
This all applies to UNIX; Windows DLL work quite differently.
Edit after clarification of the question:
Quote from the ld info manual.
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.
That means:
Any static library or object that depends on other library should be placed before it in the command line. If static libraries depend on each other circularly, you can eg. use the -( command line option, or place the libraries on the command line twice (-lDA -lDB -lDA). The order of dynamic libraries doesn't matter.
This is the sort of question that's best solved by a trivial example. Really! Take 2 minutes, code up a simple example, and try it out! You'll learn something, and it's faster than asking.
For example, given files:
a1.cc
#include <stdio.h>
void a1() { printf("a1\n"); }
a2.cc
#include <stdio.h>
extern void a1();
void a2() { printf("a2\n"); a1(); }
a3.cc
#include <stdio.h>
extern void a2();
void a3() { printf("a3\n"); a2(); }
aa.cc
extern void a3();
int main()
{
a3();
}
Running:
g++ -Wall -g -c a1.cc
g++ -Wall -g -c a2.cc
g++ -Wall -g -c a3.cc
ar -r liba1.a a1.o
ar -r liba2.a a2.o
ar -r liba3.a a3.o
g++ -Wall -g aa.cc -o aa -la1 -la2 -la3 -L.
Shows:
./liba3.a(a3.o)(.text+0x14): In function `a3()':
/tmp/z/a3.C:4: undefined reference to `a2()'
Whereas:
g++ -Wall -g -c a1.C
g++ -Wall -g -c a2.C
g++ -Wall -g -c a3.C
ar -r liba1.a a1.o
ar -r liba2.a a2.o
ar -r liba3.a a3.o
g++ -Wall -g aa.C -o aa -la3 -la2 -la1 -L.
Succeeds. (Just the -la3 -la2 -la1 parameter order is changed.)
PS:
nm --demangle liba*.a
liba1.a:
a1.o:
U __gxx_personality_v0
U printf
0000000000000000 T a1()
liba2.a:
a2.o:
U __gxx_personality_v0
U printf
U a1()
0000000000000000 T a2()
liba3.a:
a3.o:
U __gxx_personality_v0
U printf
U a2()
0000000000000000 T a3()
From man nm:
If lowercase, the symbol is local; if uppercase, the symbol is global (external).
"T" The symbol is in the text (code) section.
"U" The symbol is undefined.
I worked in a project with a bunch of internal libraries that unfortunately depended on each other (and it got worse over time). We ended up "solving" this by setting up SCons to specify all libs twice when linking:
g++ ... -la1 -la2 -la3 -la1 -la2 -la3 ...
The dependencies for linking a library or executable have to be present at link-time, so you cannot link libXC before libXB is present. It doesn't matter if statically or dynamically.
Start with the most basic one, which has no (or just outside of your project) dependencies.
It's good practice to keep libraries independent of each other to avoid link order issues.