Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In C program what happens when same function is defined in two different libraries with different implementation?

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 ?

like image 253
user3747488 Avatar asked Dec 17 '22 19:12

user3747488


1 Answers

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:

  • The linker needed a definition of 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.
  • The linker needed a definition of 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.
  • But 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:

  • At most one strong definition of a symbol can be linked.
  • If one or more weak definitions, as well as a strong definition, are input, then the one strong definition is linked and the all the weak ones are ignored.
  • If only weak definitions are input, then the linker can pick any one of them arbitrarily. (In practice, it picks whichever one if finds first).

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.

like image 157
Mike Kinghan Avatar answered Dec 28 '22 06:12

Mike Kinghan