What's the difference between `-rpath-link` and `-L`? - linker

The man for gold states:
-L DIR, --library-path DIR
Add directory to search path
--rpath-link DIR
Add DIR to link time shared library search path
The man for bfd ld makes it sort of sound like -rpath-link is used for recursively included sos.
ld.lld doesn't even list it as an argument.
Could somebody clarify this situation for me?

Here is a demo, for GNU ld, of the difference between -L and -rpath-link -
and for good measure, the difference between -rpath-link and -rpath.
foo.c
#include <stdio.h>
void foo(void)
{
puts(__func__);
}
bar.c
#include <stdio.h>
void bar(void)
{
puts(__func__);
}
foobar.c
extern void foo(void);
extern void bar(void);
void foobar(void)
{
foo();
bar();
}
main.c
extern void foobar(void);
int main(void)
{
foobar();
return 0;
}
Make two shared libraries, libfoo.so and libbar.so:
$ gcc -c -Wall -fPIC foo.c bar.c
$ gcc -shared -o libfoo.so foo.o
$ gcc -shared -o libbar.so bar.o
Make a third shared library, libfoobar.so that depends on the first two;
$ gcc -c -Wall -fPIC foobar.c
$ gcc -shared -o libfoobar.so foobar.o -lfoo -lbar
/usr/bin/ld: cannot find -lfoo
/usr/bin/ld: cannot find -lbar
collect2: error: ld returned 1 exit status
Oops. The linker doesn't know where to look to resolve -lfoo or -lbar.
The -L option fixes that.
$ gcc -shared -o libfoobar.so foobar.o -L. -lfoo -lbar
The -Ldir option tells the linker that dir is one of the directories to
search for libraries that resolve the -lname options it is given. It searches
the -L directories first, in their commandline order; then it searches its
configured default directories, in their configured order.
Now make a program that depends on libfoobar.so:
$ gcc -c -Wall main.c
$ gcc -o prog main.o -L. -lfoobar
/usr/bin/ld: warning: libfoo.so, needed by ./libfoobar.so, not found (try using -rpath or -rpath-link)
/usr/bin/ld: warning: libbar.so, needed by ./libfoobar.so, not found (try using -rpath or -rpath-link)
./libfoobar.so: undefined reference to `bar'
./libfoobar.so: undefined reference to `foo'
collect2: error: ld returned 1 exit status
Oops again. The linker detects the dynamic dependencies requested by libfoobar.so
but can't satisfy them. Let's resist its advice - try using -rpath or -rpath-link -
for a bit and see what we can do with -L and -l:
$ gcc -o prog main.o -L. -lfoobar -lfoo -lbar
So far so good. But:
$ ./prog
./prog: error while loading shared libraries: libfoobar.so: cannot open shared object file: No such file or directory
at runtime, the loader can't find libfoobar.so.
What about the linker's advice then? With -rpath-link, we can do:
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath-link=$(pwd)
and that linkage also succeeds. ($(pwd) means "Print Working Directory" and just "copies" the current path.)
The -rpath-link=dir option tells the linker that when it encounters an input file that
requests dynamic dependencies - like libfoobar.so - it should search directory dir to
resolve them. So we don't need to specify those dependencies with -lfoo -lbar and don't
even need to know what they are. What they are is information already written in the
dynamic section of libfoobar.so:-
$ readelf -d libfoobar.so
Dynamic section at offset 0xdf8 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libfoo.so]
0x0000000000000001 (NEEDED) Shared library: [libbar.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
...
...
We just need to know a directory where they can be found, whatever they are.
But does that give us a runnable prog?
$ ./prog
./prog: error while loading shared libraries: libfoobar.so: cannot open shared object file: No such file or directory
No. Same as story as before. That's because -rpath-link=dir gives the linker the information
that the loader would need to resolve some of the dynamic dependencies of prog
at runtime - assuming it remained true at runtime - but it doesn't write that information into the dynamic section of prog.
It just lets the linkage succeed, without our needing to spell out all the recursive dynamic
dependencies of the linkage with -l options.
At runtime, libfoo.so, libbar.so - and indeed libfoobar.so -
might well not be where they are now - $(pwd) - but the loader might be able to locate them
by other means: through the ldconfig cache or a setting
of the LD_LIBRARY_PATH environment variable, e.g:
$ export LD_LIBRARY_PATH=.; ./prog
foo
bar
rpath=dir provides the linker with the same information as rpath-link=dir
and instructs the linker to bake that information into the dynamic section of
the output file. Let's try that:
$ export LD_LIBRARY_PATH=
$ gcc -o prog main.o -L. -lfoobar -Wl,-rpath=$(pwd)
$ ./prog
foo
bar
All good. Because now, prog contains the information that $(pwd) is a runtime search
path for shared libraries that it depends on, as we can see:
$ readelf -d prog
Dynamic section at offset 0xe08 contains 26 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libfoobar.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000f (RPATH) Library rpath: [/home/imk/develop/so/scrap]
... ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
That search path will be tried after the directories listed in LD_LIBRARY_PATH, if any are set, and before the system defaults - the ldconfig-ed directories, plus /lib and /usr/lib.

The --rpath-link option is used by bfd ld to add to the search path used for finding DT_NEEDED shared libraries when doing link-time symbol resolution. It's basically telling the linker what to use as the runtime search path when attempting to mimic what the dynamic linker would do when resolving symbols (as set by --rpath options or the LD_LIBRARY_PATH environment variable).
Gold does not follow DT_NEEDED entries when resolving symbols in shared libraries, so the --rpath-link option is ignored. This was a deliberate design decision; indirect dependencies do not need to be present or in their runtime locations during the link process.

Related

Why the sequence of the parameters passes to the `gcc` influence the output of `readelf -d` for the built shared library?

Given these:
bar.cpp:
int foo();
int bar()
{
return foo();
}
foo.cpp:
int foo()
{
return 42;
}
The libfoo.so is built by gcc for foo.cpp,i.e. gcc -shared -o libfoo.so -fPIC foo.c
As it's all known that readelf -d could be used to show the dependency of a specific shared library.
$ gcc -shared -o libbar2.so -fPIC bar.c -lfoo -L.
$ gcc -shared -o libbar.so -lfoo -L. -fPIC bar.c
$ readelf -d libbar2.so | grep -i needed
0x0000000000000001 (NEEDED) Shared library: [libfoo.so]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
$ readelf -d libbar.so | grep -i needed
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
Why the sequence of the parameters passes to the gcc influence the output of readelf -d for the built shared library?
All these tests are on Ubuntu16.04 with gcc 5.4.0.
Update:
$ ls -l libbar*
-rwxrwxr-x 1 joy joy 8000 Oct 4 23:16 libbar2.so
-rwxrwxr-x 1 joy joy 8000 Oct 4 23:16 libbar.so
$ sum -r libbar*
00265 8 libbar2.so
56181 8 libbar.so
The linking process is sequential and the order in which you specify the files is important. The file are treated in the order they are given. See this extract from the ld manual:
Some of the command-line options to ld may be specified at any point
in the command line. However, options which
refer to files, such as -l or -T, cause the file to be read at the point at which the option appears in the command
line, relative to the object files and other file options.
When you try to link a shared library into another one, the linker will lookup if there is any undefined reference that requires something from the library in all the files considered UP TO NOW(hence in your second example, there is no files prior to the libfoo library ) , and if there is none, the library is left aside, and the linking continue with the remaining files.
Here you also have a behaviour that may be surprising: it is possible (by default) to create shared libraries that still have undefined references (that means they are not self contained). That is what happen in your second example(libbar.so). If you want to avoid this behaviour to be sure you are not in this case you can add the -Wl,-no-undefined option (see https://stackoverflow.com/a/2356393/4871988).
If you add this option the second case will raise an error at link time.
EDIT: I found this other extract in the ld manual that explain this behaviour:
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 also applies to shared libraries

Can a dynamic library depend on a static library in C and vice-versa?

I am trying to understand static libraries and shared objects in C. I am trying to understand whether one type of library can depend on other type.
Consider a scenario:
libA.so has a function foo_A_dyn():
libA.so ---> foo_A_dyn()
foo_A_dyn() uses a function foo_B_static() which is defined in libB.a which is a static library.
libB.a ---> foo_B_static()
I have built my libraries in the following way:
gcc -c foo_B.c -o foo_B.o
ar -cvq libB.a foo_B.o
gcc -fPIC -c foo_A.c -o foo_A.o
gcc -shared libA.so foo_A.o -I.
gcc main.c -lA -lB -L. -I. -o EXE
Note: main.c makes call to foo_A_dyn() and does NOT call foo_B_static() directly.
And now when I am trying to build my executable EXE, I am getting the error "undefined reference to foo_B_static".
I think the error seems genuine but I am not able to decode the rationale behind this and put it to words.
Can someone please help?
From gcc link options:
-llibrary
-l library
...
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.
Try:
gcc main.c -lB -lA -L. -I. -o EXE
Here's what the linker is doing. When we link our executable ('EXE' above) it has some symbols (functions and other things) that are unresolved. It will look down the list of libraries that follow in sequential order, trying to resolve unresolved symbols. Along the way, it finds that some of the symbols are provided by libB.so, so it notes that they are now resolved by this library. While going through libB.so it finds some symbols which are unresolved and it tries to resolve them by looking up the library that follows.
When we are ordering the libraries like:
gcc main.c -lA -lB -L. -I. -o EXE
Linker is not able to lookup for the definition of symbols used in libB into libA. Reason could be that backward reference is not available.
I have also figured out that:
shared object can depend on a static archive,
a static archive can depend on a shared object, and
one static archive can depend on another static archive
Please let me know if I have erred somewhere.

what's difference between --start-group and --whole-archive in ld

To be honest, I think this question should be easy by staring at man ld. However, by reading manpage and read the code done by others, I find people use them interchangeably or at the same time when they think there might be an issue with the order of the libraries passed to the linker.
I'm wondering what's the difference between these two options and what's the best practice when using them.
Thanks!
Related links:
What are the --start-group and --end-group command line options?
At the time of writing, the Stackoverflow tag wiki on static libraries
tells us:
A static library is an archive of object files. Used as a linker input, the linker extracts the object files it needs to carry on the linkage.
The needed object files are those that provide the linker with definitions for symbols that it finds are used, without definition, in other input files.
The needed object files, and no others, are extracted from the archive and input to the linkage exactly as if they were individual input files in the
linkage command and the static library was not mentioned at all.
...
A linker will normally support an option (GNU ld: --whole-archive, MS link: /WHOLEARCHIVE) to override the
default processing of static libraries and instead link all the contained object files, whether they are needed or not.
A static library contributes nothing to a linkage except the object files that are extracted from it, which may be vary in different linkages.
It is to be contrasted with a shared library, another kind of file altogether with a very different role in linkage.
That should make it clear what --whole-archive does. The scope of --whole-archive continues until the end of
the linker commandline or until --no-whole-archive appears1.
By default, the linker will examine a static library only at each point where that library occurs in the commandline sequence of linker inputs.
It will not go backwards to rexamine a static library for the sake of resolving symbol references it discovers in later inputs.
The --start-group ... --end-group pair of options changes that default behaviour. It directs the linker to examine the static libraries
mentioned in ... repeatedly, in that order, for as long doing so yields any new resolutions of new symbol references. --start-group ... --end-group has no effect on
the linker's default selection of object files from the static libraries in .... It will extract and link only the object files that it needs, unless --whole-archive is also
in effect.
Summing that up:-
--start-group lib0.a ... libN.a --end-group tells the linker: Keep searching through lib0.a ... libN.a for object files you need until you don't find any more.
--whole-archive lib0.a ... libN.a --no-whole-archive tells the linker: Forget about what you need. Just link all the object files in all of lib0.a ... libN.a.
You can see then that any linkage you can make succeed with --start-group lib0.a ... libN.a --end-group will also succeed with --whole-archive lib0.a ... libN.a --no-whole-archive,
because the latter will link all of the necessary object files and all the unnecessary ones, without bothering to tell the difference.
But the converse isn't true. Here is a trivial example:
x.c
#include <stdio.h>
void x(void)
{
puts(__func__);
}
y.c
#include <stdio.h>
void y(void)
{
puts(__func__);
}
main.c
extern void x(void);
int main(void)
{
x();
return 0;
}
Compile all source files:
$ gcc -Wall -c x.c y.c main.c
Make a static library, archiving x.o and y.o:
ar rcs libxy.a x.o y.o
Attempt to link a program with main.o and libxy.a in the wrong order:
$ gcc -o prog libxy.a main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `x'
collect2: error: ld returned 1 exit status
That failed because the reference to x in main.o was only discovered
by the linker too late to find the definition of x in libxy.a(x.o). It reached libxy.a
first and found no object files that it needed. At that point, it hadn't yet linked
any object files at all into the program, so there were 0 symbol references it needed to resolve. Having
considered libxy.a and found no use for it, it does not consider it again.
The correct linkage, of course, is:
$ gcc -o prog main.o libxy.a
but if you don't realise that you simply have the linkage order back-to-front, you can
get the linkage to succeed with --whole-archive:
$ gcc -o prog -Wl,--whole-archive libxy.a -Wl,--no-whole-archive main.o
$ ./prog
x
Obviously, you can't get it to succeed with
$ gcc -o prog -Wl,--start-group libxy.a -Wl,--end-group main.o
main.o: In function `main':
main.c:(.text+0x5): undefined reference to `x'
collect2: error: ld returned 1 exit status
because that is no different from:
$ gcc -o prog libxy.a main.o
Now here's an example of a linkage that fails with the default behaviour, but can be
made to succeed with --start-group ... --end-group.
a.c
#include <stdio.h>
void a(void)
{
puts(__func__);
}
b.c
#include <stdio.h>
void b(void)
{
puts(__func__);
}
ab.c
extern void b(void);
void ab(void)
{
b();
}
ba.c
extern void a(void);
void ba(void)
{
a();
}
abba.c
extern void ab(void);
extern void ba(void);
void abba(void)
{
ab();
ba();
}
main2.c
extern void abba(void);
int main(void)
{
abba();
return 0;
}
Compile all sources:-
$ gcc -Wall a.c b.c ab.c ba.c abba.c main2.c
Then make the following static libaries:
$ ar rcs libbab.a ba.o b.o x.o
$ ar rcs libaba.a ab.o a.o y.o
$ ar rcs libabba.a abba.o
(Notice that those old object files x.o and y.o are archived here again).
Here, libabba.a is dependent on both libbab.a and libaba.a. Specifically,
libabba.a(abba.o) makes a reference to ab, which is defined in libaba.a(ab.o);
and it also makes a reference to ba, which is defined in libbab.a(ba.o).
So in the linkage order, libabba.a must occur before either libbab.a and libaba.a
And libbab.a is dependent on libaba.a. Specifically, libbab.a(ba.o) makes a
reference to a, which is defined in libaba(a.o).
But libaba.a is also dependent on libbab.a. libaba(ab.o) makes a reference
to b, which is defined in libbab(b.o). There is a circular dependency between
libbab.a and libaba.a. So whichever one of them we place first in a default linkage, it
will fail with undefined reference errors. Either this way:
$ gcc -o prog2 main2.o libabba.a libaba.a libbab.a
libbab.a(ba.o): In function `ba':
ba.c:(.text+0x5): undefined reference to `a'
collect2: error: ld returned 1 exit status
Or that way:
$ gcc -o prog2 main2.o libabba.a libbab.a libaba.a
libaba.a(ab.o): In function `ab':
ab.c:(.text+0x5): undefined reference to `b'
collect2: error: ld returned 1 exit status
A circular dependency is a problem to which --start-group ... --end-group is the solution:
$ gcc -o prog2 main2.o libabba.a -Wl,--start-group libbab.a libaba.a -Wl,--end-group
$ ./prog2
b
a
Therefore --whole-archive ... --no-whole-archive is also a solution:
$ gcc -o prog2 main2.o libabba.a -Wl,--whole-archive libbab.a libaba.a -Wl,--no-whole-archive
$ ./prog2
b
a
But it is a bad solution. Let's trace which of our object files are actually linked
into the program in each case.
With --start-group ... --end-group:
$ gcc -o prog2 main2.o libabba.a -Wl,--start-group libbab.a libaba.a -Wl,--end-group -Wl,--trace
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libaba.a)ab.o
(libaba.a)a.o
(libbab.a)b.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
they are:
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libaba.a)ab.o
(libaba.a)a.o
(libbab.a)b.o
which are exactly the ones that are needed in the program.
With --whole-archive ... --no-whole-archive:
$ gcc -o prog2 main2.o libabba.a -Wl,--whole-archive libbab.a libaba.a -Wl,--no-whole-archive -Wl,-trace
/usr/bin/x86_64-linux-gnu-ld: mode elf_x86_64
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/Scrt1.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crti.o
/usr/lib/gcc/x86_64-linux-gnu/7/crtbeginS.o
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libbab.a)b.o
(libbab.a)x.o
(libaba.a)ab.o
(libaba.a)a.o
(libaba.a)y.o
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/lib/x86_64-linux-gnu/libc.so.6
(/usr/lib/x86_64-linux-gnu/libc_nonshared.a)elf-init.oS
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
/lib/x86_64-linux-gnu/ld-linux-x86-64.so.2
libgcc_s.so.1 (/usr/lib/gcc/x86_64-linux-gnu/7/libgcc_s.so.1)
/usr/lib/gcc/x86_64-linux-gnu/7/crtendS.o
/usr/lib/gcc/x86_64-linux-gnu/7/../../../x86_64-linux-gnu/crtn.o
they are:
main2.o
(libabba.a)abba.o
(libbab.a)ba.o
(libbab.a)b.o
(libbab.a)x.o
(libaba.a)ab.o
(libaba.a)a.o
(libaba.a)y.o
The same as before, plus:
(libbab.a)x.o
(libaba.a)y.o
which are dead code (and there could have been more of it, without limit). The redundant functions x() and y() are defined in the image:
$ nm prog2 | egrep 'T (x|y)'
000000000000067a T x
00000000000006ac T y
Takeaway
Use default linkage, putting your inputs in dependency order, if you can.
Use --start-group ... --end-group to surmount circular dependencies between
libraries, when you can't fix the libraries. Be aware that linkage speed will take a hit.
Use --whole-archive ... --no-whole-archive only if you actually need to link
all the object files in all the static libraries in .... Otherwise, do one of
the previous two things.
[1] And be aware that the linker's commandline, when the linker is invoked
by GCC, is actually much longer than the linkage options you explicitly pass in
the GCC commandline, with boiler-plate options silently appended. Therefore
always close --whole-archive with --no-whole-archive, and close --start-group
with --end-group.

undefined symbols when linking one .so with other

a.so defines function A(), b.so defines function B() and calls A() that is defined in a.so. b.so is linked to a.so as below
gcc -fPIC -shared B.c -o libb.so -la
Now I created a binary which is calling B() that is defined in b.so
gcc mybin.c -o mybin -lb
gcc is checking for every symbol in b.so and throws error saying A() is not defined.
gcc mybin.c -o mybin -lb -la
The above works but I had to link mybin with a.so even though it is not directly related to a.so. My requirement is that if b.so is properly linked with a.so, linking mybin with just b.so should work.
Is this possible ??
When linking the executable the linker wants to know that the libraries needed by libb.so exist so it can check they resolve any undefined references in libb.so
The linker looks for the required libraries in the usual places, so if that wouldn't find liba.so then you can tell the linker where to look by using LD_LIBRARY_PATH as shown by another answer, or by using the linker option that exists specifically for this purpose, -rpath-link
gcc mybin.c -o mybin -lb -Wl,-rpath-link=.
(The -Wl, prefix is how you tell GCC to pass an option to the linker, which is needed for linker options that GCC doesn't know about directly.)
Alternatively, you can tell the linker to allow undefined symbols in libb.so in which case it doesn't try to find liba.so to resolve the reference to A(), and trusts that you'll link correctly and ensure the library is available at run-time. That is done with the --allow-shlib-undefined option:
gcc mybin.c -o mybin -lb -Wl,--allow-shlib-undefined
However, it's generally better to let the linker check that all symbols are defined, and tell it how to find the required libraries, as it finds real problems sooner.
The -rpath-link option only tells the linker where to look for additional shared libraries, it has no visible effect on the linked executable (i.e. it doesn't record the path to the library in the executable anywhere). Another alternative is to create libb.so so that it contains the knowledge of how to find liba.so embedded within it. That is done with the -rpath linker option, e.g. if liba.so is in /some/path you could do:
gcc -fPIC -shared B.c -o libb.so -la -Wl,-rpath=/some/path
This puts a DT_RPATH tag in libb.so and the linker will use that to find its dependencies:
readelf -d libb.so | fgrep RPATH
0x000000000000000f (RPATH) Library rpath: [/some/path]
Now you can link the executable without needing any knowledge of liba.so, the linker will see that libb.so needs liba.so and will use the RPATH to find it:
gcc mybin.c -o mybin -lb
This works fine if the path /some/path/liba.so is fixed, but it might not be true that the library is found in the same location during development and after your executable is deployed. In that case you can still use -rpath-link for the executable to tell the linker where to look for it during linking, and rely on the RPATH to find it at run-time. Or you can use the special string $ORIGIN in the RPATH which the dynamic linker expands to the location of the object containing the RPATH, e.g. if liba.so and libb.so are always in the same directory, you can link libb.so like this (note the quotes to prevent the shell expanding $ORIGIN):
gcc -fPIC -shared B.c -o libb.so -la '-Wl,-rpath=$ORIGIN'
readelf -d libb.so | fgrep RPATH
0x000000000000000f (RPATH) Library rpath: [$ORIGIN]
The missing bit was the LD_LIBRARY_PATH:
export LD_LIBRARY_PATH=.
gcc -fPIC -shared a.c -o liba.so
gcc -fPIC -shared b.c -o libb.so -la
gcc mybin.c -o mybin -lb -L.
The reason -L does not work seems it that this flag it used to « Add directory dir to the list of directories to be searched for -l » , see the GCC doc. And libba.so is not specified in a -l.
Here are the steps:
gcc -fPIC -shared a.c -o liba.so
gcc -fPIC -shared b.c -o libb.so
gcc mybin.c -o mybin -lb -la -L. # -L to look for the directory to load .so files
Then make sure you do:
export LD_LIBRARY_PATH=<your_directory>
You need to have liba.so and libb.so mentioned in gcc mybin.c -o mybin -lb -la -L. as the function A() needs to be loaded at runtime. b.so IS NOT LINKED with a.so. Linking happens that runtime in shared library. They are compiled and any reference to functions or extern variables are not resolved.
If you want to include b.so but not a.so then compile the b as static library thereby it loads a into its binary. And then when you compile mybin you will only need b.lib

Will my linker link objects from unneeded source files?

When compiling a project, if my source files include a file with no used functions, will the unneeded object file be included in the compiler's output?
e.g.
foo.c
int main() {return 0;}
bar.c
void unusedFunction {;}
compiler execution:
gcc foo.c bar.c -o output
Would the output file be any smaller if I had omitted bar.c from the compiler command?
Most linkers will link unneeded files unless you tell them not to. There are flags for this.
Suppose you link with two unnneeded files: an object file and a library:
gcc main.o unneeded.o -lunneeded
With GNU Binutils or Gold, the flags are --gc-sections for unneeded symbols / object files, and --as-needed for libraries. These are linker flags, however, so they must be prefixed with -Wl,. Note that the order of these flags is important—flags only apply to libraries and object files which appear after the flags on the command line, so the flags must be specified first.
gcc -Wl,--as-needed -Wl,--gc-sections main.o unneeded.o -lunneeded
On OS X, there is a different linker so the flags are different. The -dead_strip flag removes unneeded symbols / object files, and the -dead_strip_dylibs flag removes unneeded libraries.
gcc -Wl,-dead_strip -Wl,-dead_strip_dylibs main.o unneeded.o -luneeded
Example
$ cat main.c
int main() { }
$ cat unneeded.c
void unneeded() { }
$ gcc -c main.c
$ gcc -c unneeded.c
If we link normally, we get everything...
$ gcc main.o unneeded.o -lz
$ nm a.out | grep unneeded
0000000000400574 T unneeded
$ readelf -d a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libz.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
With the right flags, we get just what we need...
$ gcc -Wl,--as-needed -Wl,--gc-sections main.o unneeded.o -lz
$ nm a.out | grep unneeded
$ readelf -d a.out | grep NEEDED
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
Every source or object file referenced in the compilation command will be included in the final executable.
To include only what's needed, build a static library from your objects, and refer to it when linking, rather than to individual objects. Static libraries were invented just for this purpose.
Use something like Scons (http://www.scons.org/) or Gradle (http://www.gradle.org/) to figure out the dependencies and will then just link the appropriate bits (assuming you are using static linking)
Dynamic linking is another matter — you get the lot as other programs may need the additional stuff.
But as to the command line given it will link it in. Why add it in the first place if it is not required?

Resources