If we have a function foo() which has same prototype in two different libraries with different implementation and if we include only single header file (which has declaration of function), what happens if we try to compile or execute the program at compile or runtime ?
Per your tags, you are asking about the linkage of static libraries. The linker doesn't know or care what source language(s) its input files were compiled from. The C language is immaterial to the question, but I'll use C for illustration.
Read the Stackoverflow tag Wiki about static libraries and it will explain that linking your program with a static library is exactly the same as linking your program with 0 or more of the object files that are archived in the static library - namely the 0 or more object files that the linker needs to provide definitions for otherwise unresolved symbol-references in the program.
Once the linker has found an object file p.o
file in a static library libx.a
that provides it
with a definition for some symbol foo
that the program refers to, it will link
the object file libx.a(p.o)
into the program to resolve foo
. It will not
try to find any other definition of foo
in any other object file q.o
file in any other
static library liby.a
that comes after libx.a
in the linkage.
So if there is any other object file q.o
in any other static library liby.a
that comes later in the linkage than libx.a
that also contains a definition of
foo
, that object file liby.a(q.o)
will not even be linked into the program
unless the linker needs it provide a definition of some other symbol bar
that the program refers to. Assuming that is not the case, liby.a(q.o)
might
as well not exist, for linkage purposes.
The linker will not link multiple definitions of the same symbol from libx.a(p.o)
and liby.a(q.o)
if it
does not need to. It will link the first object file, libx.a(p.o)
, that defines foo
into the program, and then it is done with defining foo
.
Here's an illustration of that:
main.c
extern void foo(void);
extern void bar(void);
int main(void)
{
foo();
bar();
return 0;
}
p.c
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
q.c
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
r.c
#include <stdio.h>
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
Function foo
is defined in p.c
and also in q.c
.
Compile all the .c
files to .o
files:
$ gcc -c main.c p.c q.c r.c
Create three static libraries, one from each of p.o
, q.o
, r.o
:
$ ar rcs libx.a p.o
$ ar rcs liby.a q.o
$ ar rcs libz.a r.o
Then link a program, inputting libx.a
before liby.a
:
$ gcc -o prog main.o libz.a libx.a liby.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: libx.a(p.o): definition of foo
The diagnostic linkage option -Wl,-trace-symbol=foo
asks the linker to tell
us the names of the files that are linked into prog
where it finds unresolved references to foo
and
also the name of the file where foo
is defined. You see that foo
is referenced in main.o
and the definition provided by libx.a(p.o)
is linked. The other definition of foo
in
liby.a(q.o)
is not linked. This linkage is exactly the same as
gcc -o prog main.o r.o p.o
which contains only the definition of foo
from p.o
, as the program
shows:
$ ./prog
foo from p.c
bar from r.c
Now relink prog
, this time with liby.a
before libx.a
:
$ gcc -o prog main.o libz.a liby.a libx.a -Wl,-trace-symbol=foo
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: liby.a(q.o): definition of foo
This time, the definition of foo
was linked from liby.a(q.o)
. This linkage is exactly the same as:
gcc -o prog main.o r.o q.o
which contains only the definition of foo
from q.o
, as the program
shows:
$ ./prog
foo from q.c
bar from r.c
The linker does not care how many definitions of foo
you offer it in
different object files in different static libraries. It only cares that
if foo
is referenced in the program, then foo
is defined, exactly once,
by the files that it links into the program.
If you compel the linker to link files into the program that contain more
than one definition of foo
, then by default and normally the linker will
give you a multiple definition error and the linkage will fail, because
there cannot be more than one definition of foo
in a program. Here's an
illustration of that:
qr.c
#include <stdio.h>
void foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
Compile that file:
$ gcc -c qr.c
Archive qr.o
in a new static library:
$ ar rcs libyz.a qr.o
The object file libyz.a(qr.o)
defines both foo
and bar
. So we can link
our program like:
$ gcc -o prog main.o libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libyz.a(qr.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): definition of bar
And it runs like:
$ ./prog
foo from qr.c
bar from qr.c
But if we attempt to link it like:
$ gcc -o prog main.o libx.a libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libx.a(p.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): in function `foo':
qr.c:(.text+0x0): multiple definition of `foo'; libx.a(p.o):p.c:(.text+0x0): first defined here
/usr/bin/ld: libyz.a(qr.o): definition of bar
collect2: error: ld returned 1 exit status
there is a multiple definition of foo
. That is because:
foo
and found the first one in libx.a(p.o)
;
so it linked that file into the program. It won't search for any other one.bar
and found the first one in libyz.a(qr.o)
;
so it linked that file into the program. It won't search for any other one.libyz.a(qr.o)
contains another definition of foo
as well as a definition
of bar
. So now two definitions of foo
have been linked, and that is an error.I said that you'll get a multiple definition error by default if you make the linker attempt to link into the program more than one file that defines a symbol.
But you can avoid that by telling the linker than the symbol is weakly defined, provided that your linker understands this concept (as the GNU and Apple linkers do).
GCC compilers support a non-standard language extension, the __attribute__
syntax
that you can use to communicate to the linker that a symbol definition is weak.
Here's an illustration of that:
qr.c (2)
#include <stdio.h>
void __attribute__((weak)) foo(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
void bar(void)
{
printf("%s %s %s\n",__func__,"from",__FILE__);
}
Recompile:
$ gcc -c qr.c
Delete libyz.a
and recreate it:
$ rm libyz.a
$ ar rcs libyz.a qr.o
Retry the linkage that just failed:
$ gcc -o prog main.o libx.a libyz.a -Wl,-trace-symbol=foo,-trace-symbol=bar
/usr/bin/ld: main.o: reference to foo
/usr/bin/ld: main.o: reference to bar
/usr/bin/ld: libx.a(p.o): definition of foo
/usr/bin/ld: libyz.a(qr.o): definition of bar
This time, no error. The definition of foo
from libx.a(p.o)
is
linked. The weak definition from libyz.a(qr.o)
is ignored. The program
runs like:
$ ./prog
foo from p.c
bar from qr.c
If a symbol definition isn't weak, it is strong. The linker's rules are:
Do not use weak symbol definitions simply to escape from multiple definition errors that take you by surprise. Such surprises mean that you don't understand your linkage. Analyse it and fix it so that you are no longer trying to make a program containing multiple definitions of the same thing.
Weak symbol definitions are usually generated by your compiler, behind the scenes, to implement features of the source language that require them ( e.g. global inline function definitions, or template instantiations in C++). Use them yourself only if you understand exactly why you want the linker to input multiple definitions of the same symbol.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With