Linker does not emit multiple definition error when same symbol coexists in object file and static library
Asked Answered
E

1

11

Given a compiled function with signature void some_func() in a static library, and another one with the same void some_func() signature in a object file one expects that when you link them together a "multiple definition" error should have occured.

But this is not the case. As far as i've observed the linkers (tested with GCC and MSVC toolchains) choose the implementation residing in the object file without emitting any error or warning.

Given the following POC:

somelib.h

#ifndef _SOMELIB_H_
#define _SOMELIB_H_

void some_func();

#endif /* _SOMELIB_H_ */

somelib.c

#include "somelib.h"
#include <stdio.h>

void some_func()
{
    printf("some_func in library\n");
}

troublingheader.h

#ifndef _TROUBLING_HEADER_H_
#define _TROUBLING_HEADER_H_

void some_func();

#endif /* _TROUBLING_HEADER_H_ */

troublingsource.c

#include "troublingheader.h"
#include <stdio.h>

void some_func()
{
    printf("Troubling func\n");
}

main.c

#include <stdio.h>
#include "somelib.h"

int main()
{
    some_func();
}

And a simple Makefile to assist building (create tmp folder first):

tmp/wat.exe: tmp/libsomelib.a tmp/main.c.o tmp/troublingsource.c.o
    gcc -static -static-libgcc -Ltmp -otmp/wat.exe tmp/main.c.o tmp/troublingsource.c.o -lsomelib

tmp/main.c.o:
    gcc -Wall -Wextra -c -g -O0 src/main.c -o tmp/main.c.o

tmp/troublingsource.c.o:
    gcc -Wall -Wextra -c -g -O0 src/troublingsource.c -o tmp/troublingsource.c.o

tmp/somelib.o:
    gcc -Wall -Wextra -c -g -O0 src/somelib.c -o tmp/somelib.c.o

tmp/libsomelib.a: tmp/somelib.o
    ar rcs tmp/libsomelib.a tmp/somelib.c.o

When running the final executable, the contents "Troubling func" are shown as above described.

Can someone explain me why this happens?

Estival answered 27/5, 2016 at 18:29 Comment(7)
Exactly what is your basis for expecting an error in the case you describe? that's not necessarily what I would expect.Saleratus
Why not? I see two identical symbols in two different translation units, how does the linker decide which is the right one?Estival
Libraries are only searched if needed. If your code defines a function, there's no need to search the library for that function.Anaplasty
Standard behavior for UNIX linkers is to consider each object file and library in the order specified on the command line. Symbols in any given file must be resolved either within the same file or by a file linked later.Saleratus
Moreover, duplicate symbols in the linked executable are not a problem, if the symbols are even retained at all. The point of linking is to associate each function and object reference with an address in the program's address space, and there is no inherent reason why two functions with the same name cannot be present at different addresses.Saleratus
@JohnBollinger the answer @Anaplasty makes some sense, but what you are saying is contradicting with the fact that if you directly link two object files with the same symbol inside we get "multiple definition of `some_func'" kind of errorsEstival
note: this behaviour is permitted by the C Standard (multiple definitions in different units are undefined behaviour, no diagnostic required)Acquaint
R
16

In the case of the GNU linker ld, invoked by GCC, what you have observed is explained by the following points.

  • An object file in the linkage sequence is added to the linkage unconditionally, whether it contains symbols that the program needs or not.

  • A static library is an archive of object files together with a "table of contents" that the linker can inspect.

  • By default, an object file within a static library in the linkage sequence is not added to the linkage unconditionally. The linker searches a static library only to find definitions of symbols that it has observed to be referenced, but not defined, by object files already added to the linkage. An object file is extracted from the library and added to the linkage only if it provides definitions of such symbols. It is added only if the linker already needs some of the definitions that it provides.

For the avoidance of confusion, we'll change the output message:

"some_func in library\n"

to:

"some_func in somelib.o"

Then a demonstration of these points that explains what you've seen:

Case 1

Link troublingsource.o itself and somelib.o from a static library.

Linkage sequence: main.o, troublingsource.o, libsomelib.a

gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o main.o main.c
ar rcs libsomelib.a somelib.o
gcc -o test main.o troublingsource.o -L. -lsomelib
./test
Troubling func

Here:

  • main.o was added unconditionally to the linkage.
  • Symbol main was found defined in main.o
  • Symbol some_func was found referenced but not defined in main.o
  • troublingsource.o was added unconditionally to the linkage
  • Symbol some_func, previously referenced but not defined, was found defined in troublingsource.o.
  • No unresolved references remained. libsomelib.a was not even searched.

Case 2

Link someblib.o itself and troublingsource.o from a static library.

Linkage sequence: main.o, somelib.o, libtroublingsource.a

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o main.o main.c
ar rcs libtroublingsource.a troublingsource.o
gcc -o test main.o somelib.o -L. -ltroublingsource
./test
some_func in somelib.o

Here:

  • main.o was added unconditionally to the linkage.
  • Symbol main was found defined in main.o
  • Symbol some_func was found referenced but not defined in main.o
  • someblib.o was added unconditionally to the linkage
  • Symbol some_func, previously referenced but not defined, was found defined in somelib.o.
  • No unresolved references remained. libtroublingsource.a was not even searched.

Case 3

Link someblib.o and troublingsource.o from separate static libraries.

Linkage sequence: main.o, libsomelib.a, libtroublingsource.a

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o main.o main.c
ar rcs libsomelib.a somelib.o
ar rcs libtroublingsource.a troublingsource.o
gcc -o test main.o -L. -lsomelib -ltroublingsource
./test
some_func in somelib.o

Here:

  • main.o was added unconditionally to the linkage.
  • Symbol main was found defined in main.o
  • Symbol some_func was found referenced but not defined in main.o
  • libsomeblib.a was searched for an object file providing a definition of some_func
  • A definition of some_func was found in member somelib.o of libsomelib.a
  • somelib.o was extracted from libsomelib.a and added to the linkage.
  • No unresolved references remained. libtroublingsource.a was not even searched.

Case 4

Link someblib.o itself and troublingsource.o itself.

Linkage sequence: main.o, somelib.o, troublingsource.o

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o troublingsource.o troublingsource.c
gcc -I. -c -o main.o main.c
gcc -o test main.o somelib.o troublingsource.o
troublingsource.o: In function `some_func':
troublingsource.c:(.text+0x0): multiple definition of `some_func'
somelib.o:somelib.c:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

Here:

  • main.o was added unconditionally to the linkage.
  • Symbol main was found defined in main.o
  • Symbol some_func was found referenced but not defined in main.o
  • someblib.o was added unconditionally to the linkage
  • Symbol some_func, previously referenced but not defined, was found defined in somelib.o
  • troublingsource.o was added unconditionally to the linkage
  • Symbol some_func, allready defined in somelib.o, was found defined again in troublingbsource.o. Error.

Case 5

Link someblib.o from a static library.

Linkage sequence: libsomelib.a main.o

gcc -I. -c -o somelib.o somelib.c
gcc -I. -c -o main.o main.c
ar rcs libsomelib.a somelib.o
gcc -o test -L. -lsomelib main.o
main.o: In function `main':
main.c:(.text+0xa): undefined reference to `some_func'
collect2: error: ld returned 1 exit status

Here:

  • libsomelib.a was encountered before any object files were added to the linkage. At that point no symbols were referenced but undefined, so libsomelib.a was not even searched,
  • main.o was added unconditionally to the linkage.
  • Symbol main was found defined in main.o
  • Symbol some_func was found referenced but not defined in main.o
  • There were no further object files or libraries to link. some_func was finally an undefined reference. Error.
Revanchism answered 30/5, 2016 at 8:55 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.