I have doubt is it possible if I built lib1.so
using source file common.cpp
and lib2.so
using same source file common.cpp
again. Now I want to build my application APP
using this two library ,
My question are
foo
is class in common.cpp
. foo_v1
is object of foo in lib1.so and foo_v2
is object of foo in lib2.so. Now during bulid of APP
what would happen? Also is it possible to create object of foo in APP
application ?Naturally one would suggest you consider building the common functionality shared
by lib1.so
and lib2.so
into a distinct shared library, libcommon.so
.
But if you want nevertheless to statically link the common functionality
identically1
into both lib1.so
and lib2.so
, you can link these two shared libraries with
your program. The linker will have no problem with that. Here is an
illustration:
common.h
#ifndef COMMON_H
#define COMMON_H
#include <string>
struct common
{
void print1(std::string const & s) const;
void print2(std::string const & s) const;
static unsigned count;
};
common.cpp
#include <iostream>
#include "common.h"
unsigned common::count = 0;
void common::print1(std::string const & s) const
{
std::cout << s << ". (count = " << count++ << ")" << std::endl;
}
void common::print2(std::string const & s) const
{
std::cout << s << ". (count = " << count++ << ")" << std::endl;
}
foo.h
#ifndef FOO_H
#define FOO_H
#include "common.h"
struct foo
{
void i_am() const;
private:
common _c;
};
#endif
foo.cpp
#include "foo.h"
void foo::i_am() const
{
_c.print1(__PRETTY_FUNCTION__);
}
bar.h
#ifndef BAR_H
#define BAR_H
#include "common.h"
struct bar
{
void i_am() const;
private:
common _c;
};
#endif
bar.cpp
#include "bar.h"
void bar::i_am() const
{
_c.print2(__PRETTY_FUNCTION__);
}
Now we'll make two shared libraries, libfoo.so
and libbar.so
. The
source files we need are foo.cpp
, bar.cpp
and common.cpp
. First
compile them all to PIC (Position Independent Code
object files:
$ g++ -Wall -Wextra -fPIC -c foo.cpp bar.cpp common.cpp
And here are the object files we just made:
$ ls *.o
bar.o common.o foo.o
Now link libfoo.so
using foo.o
and common.o
:
$ g++ -shared -o libfoo.so foo.o common.o
Then link libbar.so
using bar.o
and (again) common.o
$ g++ -shared -o libbar.so bar.o common.o
We can see that common::...
symbols are defined and exported by libfoo.so
:
$ nm -DC libfoo.so | grep common
0000000000202094 B common::count
0000000000000e7e T common::print1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
0000000000000efa T common::print2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
(T
means defined in the code section, B
means defined in the uinitialized data section). And exactly the same is true about libbar.so
$ nm -DC libbar.so | grep common
0000000000202094 B common::count
0000000000000e7e T common::print1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
0000000000000efa T common::print2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
Now we'll make a program linked with these libraries:
main.cpp
#include "foo.h"
#include "bar.h"
int main()
{
foo f;
bar b;
common c;
f.i_am();
b.i_am();
c.print1(__PRETTY_FUNCTION__);
return 0;
}
It calls foo
; it calls bar
,
and it calls common::print1
.
$ g++ -Wall -Wextra -c main.cpp
$ g++ -o prog main.o -L. -lfoo -lbar -Wl,-rpath=$PWD
It runs like:
$ ./prog
void foo::i_am() const. (count = 0)
void bar::i_am() const. (count = 1)
int main(). (count = 2)
Which is just fine. You might perhaps have worried that two copies of the static class variable
common::count
would end up in the program - one from libfoo.so
and another from libbar.so
,
and that foo
would increment one copy and bar
would increment the other. But that didn't happen.
How did the linker resolve the common::...
symbols? Well to see that we need to find their mangled forms,
as the linker sees them:
$ nm common.o | grep common
0000000000000140 t _GLOBAL__sub_I_common.cpp
0000000000000000 B _ZN6common5countE
0000000000000000 T _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
000000000000007c T _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
There they all are and we can tell which one is which with c++filt
:
$ c++filt _ZN6common5countE
common::count
$ c++filt _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
common::print1(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
$ c++filt _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
common::print2(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const
Now we can re-do the linkage of prog
, this time asking the linker to tell us the names of the
input files in which these common::...
symbols were defined or referenced. This diagnostic
linkage is a bit of a mouthful, so I'll \
-split it:
$ g++ -o prog main.o -L. -lfoo -lbar -Wl,-rpath=$PWD \
-Wl,-trace-symbol=_ZN6common5countE \
-Wl,-trace-symbol=_ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE \
-Wl,-trace-symbol=_ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
main.o: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: definition of _ZN6common5countE
./libfoo.so: definition of _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: definition of _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: reference to _ZN6common5countE
./libbar.so: reference to _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
So the linker tells us that it linked the definition of common::count
from ./libfoo.so
. Likewise the
definition of common::print1
. Likewise the definition of common::print2
. It linked all the
common::...
symbol definitions from libfoo.so
.
It tells us that the reference(s) to common::print1
in main.o
was resolved to the definition in libfoo.so
. Likewise
the reference(s) to common::count
in libbar.so
. Likewise the reference(s) to common::print1
and
common::print2
in libbar.so
. All the common::...
symbol references in the program were resolved to the
definitions provided by libfoo.so
.
So there were no multiple definition errors, and there is no uncertainty about which "copies" or "versions" of the common::...
symbols are used
by the program: it just uses the definitions from libfoo.so
.
Why? Simply because libfoo.so
was the first library in the linkage that provided definitions
for the common::...
symbols. If we relink prog
with the order of -lfoo
and -lbar
reversed:
$ g++ -o prog main.o -L. -lbar -lfoo -Wl,-rpath=$PWD \
-Wl,-trace-symbol=_ZN6common5countE \
-Wl,-trace-symbol=_ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE \
-Wl,-trace-symbol=_ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
main.o: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: definition of _ZN6common5countE
./libbar.so: definition of _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libbar.so: definition of _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: reference to _ZN6common5countE
./libfoo.so: reference to _ZNK6common6print2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
./libfoo.so: reference to _ZNK6common6print1ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE
then we get exactly the opposite answers. All the common::...
symbol references in the program
are now resolved to the definitions provided by libbar.so
. Because libbar.so
provided them first.
There is still no uncertainty, and it makes no difference to the program, because both libfoo.so
and libbar.so
linked the common::...
definitions from the same object file, common.o
.
The linker does not try to find multiple definitions of symbols. Once it has found a definition of a symbol S, in an input object file or shared library, it binds references to S to the definition it has found and is done with resolving S. It does not care if a shared library it finds later can provide another definition of S, the same or different, even if that later shared library resolves symbols other than S.
The only way in which you can cause a multiple definition error is by compelling the linker
to statically link multiple definitions, i.e. compel it to physically merge into the output binary
two object files obj1.o
and obj2.o
both of which contain a definition S.
If you do that, the competing static definitions have exactly the same status, and only
one definition can be used by the program, so the linker has to fail you. But it does not need to take any notice of a
dynamic symbol definition of S provided by a shared library if it has already resolved S, and it does not do so.
lib1
and lib2
with different preprocessor, compiler or linkage options, you can sabotage the "common" functionality to an arbitary extent.