How to Avoid Multiple Definition Linking Error

Avoid multiple definition linker error when not using the redefined symbols

You may have a one variant of the problem or a different variant,
depending on facts whose relevance you haven't yet considered. Or possibly you have a mixture of both, so I'll walk through a solution to each variant.

You should be familiar with the nature of static libraries and how they are consumed in linkage,
as summarised here

The Superflous Globals Symbols Variant

Here are a couple of source files and a header file:

one.cpp

#include <onetwo.h>

int clash = 1;

int get_one()
{
return clash;
}

two.cpp

#include <onetwo.h>

int get_two()
{
return 2;
}

onetwo.h

#pragma once

extern int get_one();
extern int get_two();

These have been built into a static library libonetwo.a

$ g++ -Wall -Wextra -pedantic -I. -c one.cpp two.cpp
$ ar rcs libonetwo.a one.o two.o

whose intended API is defined in onetwo.h

Simarily, some other source files and a header have been
built into a static libary libfourfive.a whose intended API
is defined in fourfive.h

four.cpp

#include <fourfive.h>

int clash = 4;

int get_four()
{
return clash;
}

five.cpp

#include <fourfive.h>

int get_five()
{
return 5;
}

fourfive.h

#pragma once

extern int get_four();
extern int get_five();

And here's the source of a program that depends on both libraries:

prog.cpp

#include <onetwo.h>
#include <fourfive.h>

int main()
{
return get_one() + get_four();
}

which we try to build like so:

$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(four.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(one.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status

encountering a name-collision for the symbol clash, because it is globally defined in two of the
object files that the linkage requires, one.o and four.o:

$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z7get_twov
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 10 entries:
9: 0000000000000000 15 FUNC GLOBAL DEFAULT 1 _Z8get_fivev

The problem symbol clash is not referenced in our own code, prog.(cpp|o). You wondered:

Is there a way to tell the linker "complain for multiple definitions only if the symbol is used"?

No there isn't, but that's immaterial. one.o would not have been extracted from libonetwo.a
and linked into the program if the linker didn't need it to resolve some symbol. It needed it
to resolve get_one. Likewise it only linked four.o because it's needed to resolve get_four.
So the colliding definitions of clash are in the linkage. And although prog.o doesn't use clash,
it does use get_one, which uses clash and which intends to use the defintion of clash in one.o.
Likewise prog.o uses get_four, which uses clash and intends to use the different definition in four.o.

Even if clash was unused by each libary as well as the program, the fact that it is defined
in multiple object files that must be linked into the program means that the program will contain
multiple definitions of it, and only --allow-multiple-definitions will allow that.

In that light you'll also see that:

Or alternatively [is there a way to] tell it, "from lib ABC ignore symbol XYZ".

in general won't fly. If we could tell the linker to ignore (say) the definition of clash
in four.o and resolve the symbol everywhere to the definition in one.o (the only other candidate) then get_four() would return 1 instead of 4 in our program. That
is in fact the effect of --allow-multiple-definitions, since it causes the first definition
in the linkage to be used.

By inspection of the source code of libonetwo.a (or libfourfive.a) we can fairly confidently spot the root
cause of the problem. The symbol clash has been left with external linkage where
it only needed internal linkage, since it isn't declared in the associated
header file and is referenced nowhere in the libary except
in the file where it's defined. The offending source files should have written:

one_good.cpp

#include <onetwo.h>

namespace {
int clash = 1;
}

int get_one()
{
return clash;
}

four_good.cpp

#include <fourfive.h>

namespace {
int clash = 4;
}

int get_four()
{
return clash;
}

and all would be good:

$ g++ -Wall -Wextra -pedantic -I. -c one_good.cpp four_good.cpp
$ readelf -s one_good.o four_good.o | egrep '(File|Symbol|OBJECT|FUNC)'
File: one_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: four_good.o
Symbol table '.symtab' contains 11 entries:
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 _ZN12_GLOBAL__N_15clashE
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z8get_fourv

$ g++ -o prog prog.o one_good.o four_good.o
$./prog; echo $?
5

Since re-writing the source code like that is not a option, we have to modify the object files to the
same effect. The tool for this is objcopy.

$ objcopy --localize-symbol=clash libonetwo.a libonetwo_good.a

This command has the same effect as running:

$ objcopy --localize-symbol=clash orig.o fixed.o

on each of the object files libonetwo(orig.o) to output a fixed object file fixed.o,
and archiving all the fixed.o files in a new static library libonetwo_good.a. And the
effect of --localize-symbol=clash, on each object file, is to change the linkage of
the symbol clash, if defined, from external (GLOBAL) to internal (LOCAL):

$ readelf -s libonetwo_good.a | egrep '(File|Symbol|OBJECT|FUNC)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT LOCAL DEFAULT 3 clash
10: 0000000000000000 16 FUNC GLOBAL DEFAULT 1 _Z7get_onev
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 10 entries:

Now the linker cannot see the LOCAL definition of clash in libonetwo_good.a(one.o).

That's sufficient to head off the multiple definition error, but since libfourfive.a has
the same defect, we'll fix it too:

$ objcopy --localize-symbol=clash libfourfive.a libfourfive_good.a

And then we can relink prog successfully, using the fixed libraries.

$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5

The Global Symbols Deadlock Variant

In this scenario, the sources and headers for libonetwo.a are:

one.cpp

#include <onetwo.h>
#include "priv_onetwo.h"

int inc_one()
{
return inc(clash);
}

two.cpp

#include <onetwo.h>
#include "priv_onetwo.h"

int inc_two()
{
return inc(clash + 1);
}

priv_onetwo.cpp

#include "priv_onetwo.h"

int clash = 1;

int inc(int i)
{
return i + 1;
}

priv_onetwo.h

#pragma once

extern int clash;
extern int inc(int);

onetwo.h

#pragma once

extern int inc_one();
extern int inc_two();

And for libfourfive.a they are:

four.cpp

#include <fourfive.h>
#include "priv_fourfive.h"

int dec_four()
{
return dec(clash);
}

five.cpp

#include <fourfive.h>
#include "priv_fourfive.h"

int dec_five()
{
return dec(clash + 1);
}

priv_fourfive.cpp

#include "priv_fourfive.h"

int clash = 4;

int dec(int i)
{
return i - 1;
}

priv_fourfive.h

#pragma once

extern int clash;
extern int dec(int);

fourfive.h

#pragma once

extern int dec_four();
extern int dec_five();

Each of these libraries is built with some common internals defined
in a source file - (priv_onetwo.cpp|priv_fourfive.cpp)
- and these internals are globally declared for building the library through a private header
- (priv_onetwo.h|priv_fourfive.h) - that is not distributed with the library.
They are undocumented symbols but nevertheless exposed to the linker.

Now there are two files in each library make that undefined (UND) references
to the global symbol clash, which is defined in another file:

$ readelf -s libonetwo.a libfourfive.a | egrep '(File|Symbol|OBJECT|FUNC|clash)'
File: libonetwo.a(one.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z7inc_onev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(two.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z7inc_twov
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libonetwo.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3inci
File: libfourfive.a(four.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 23 FUNC GLOBAL DEFAULT 1 _Z8dec_fourv
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(five.o)
Symbol table '.symtab' contains 13 entries:
9: 0000000000000000 26 FUNC GLOBAL DEFAULT 1 _Z8dec_fivev
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash
File: libfourfive.a(priv_fourfive.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash
10: 0000000000000000 19 FUNC GLOBAL DEFAULT 1 _Z3deci

Our program source this time is:

prog.cpp

#include <onetwo.h>
#include <fourfive.h>

int main()
{
return inc_one() + dec_four();
}

and:

$ g++ -Wall -Wextra -pedantic -I. -c prog.cpp
$ g++ -o prog prog.o -L. -lonetwo -lfourfive
/usr/bin/ld: ./libfourfive.a(priv_fourfive.o):(.data+0x0): multiple definition of `clash'; ./libonetwo.a(priv_onetwo.o):(.data+0x0): first defined here
collect2: error: ld returned 1 exit status

once again clash is multiply defined. To resolve inc_one in main, the
linker needed one.o, which obliged it to resolve inc, which made it need
priv_onetwo.o, which contains the first definition of clash. To resolve dec_four in main, the
linker needed four.o, which obliged it to resolve dec, which made it need
priv_fourfive.o, which contains a rival definition of clash.

In this scenario, it isn't a coding error in either library that clash has external linkage.
It needs to have external linkage. Localizing the definition of clash with objcopy in either of
libonetwo.a(priv_onetwo.o) or libfourfive.a(priv_fourfive.o) will not work. If we do
that the linkage will succeed but output a bugged program, because the linker will resolve
clash to the one surviving GLOBAL definition from the other object file: then dec_four()
will return 0 instead of 3 in the program, dec_five() will return 1 not 4 ; or else inc_one() will return 5 and inc_two() will return 6.
And if we localize both definitions then no definition of clash will be found in the
linkage of prog to satisfy the references in one.o or four.o, and it will fail for undefined reference to clash

This time objcopy comes to the rescue again, but with a different option1:

$ objcopy --redefine-sym clash=clash_onetwo libonetwo.a libonetwo_good.a

The effect of this command is to create a new static library libonetwo_good.a,
containing new object files that are pairwise the same as those in libonetwo.a,
except that the symbol clash has been everywhere replaced with clash_onetwo:

$ readelf -s libonetwo_good.a | egrep '(File|Symbol|clash)'
File: libonetwo_good.a(one.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(two.o)
Symbol table '.symtab' contains 13 entries:
10: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND clash_onetwo
File: libonetwo_good.a(priv_onetwo.o)
Symbol table '.symtab' contains 11 entries:
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 2 clash_onetwo

We'll do the corresponding thing with libfourfive.a:

$ objcopy --redefine-sym clash=clash_fourfive libfourfive.a libfourfive_good.a

Now we're good to go once more:

$ g++ -o prog prog.o -L. -lonetwo_good -lfourfive_good
$ ./prog; echo $?
5

Of the two solutions, use the fix for The Superflous Globals Symbols Variant if
superflous globals is what you've got, although the fix for the The Global Symbols Deadlock Variant
would also work. It is never desirable to tamper with object files
between compilation and linkage; it can only be unavoidable or the lesser of evils. But
if you're going to tamper with them, localizing a global symbol that should never have been global
is a more transparent tampering than changing the name of a symbol to one that
has no origin in source code.


[1] Don't forget that if you want to use objcopy with any option argument that
is a symbol in a C++ object file, you have to use the mangled name of the
C++ identifier than maps to the symbol. In this demo code it happens that the mangled name of the
C++ identifier clash is also clash. But if, e.g. the fully qualified identfier had
been onetwo::clash, its mangled name would be _ZN6onetwo5clashE, as reported by
nm or readelf. Conversely of course if you wished to use objcopy to change _ZN6onetwo5clashE in
an object file to a symbol that will demangle as onetwo::klash, then that symbol will
be _ZN6onetwo5klashE.

Multiple definition of ... linker error

Don't define variables in headers. Put declarations in header and definitions in one of the .c files.

In config.h

extern const char *names[];

In some .c file:

const char *names[] = { 
"brian", "stefan", "steve" };

If you put a definition of a global variable in a header file, then this definition will go to every .c file that includes this header, and you will get multiple definition error because a varible may be declared multiple times but can be defined only once.

Also, one more thing you can do if you have to define your variables inside of a header file you can use the static keyword.

static const char *names[] = {
"brian", "stefan", "steve" };

This way variable names will be defined only once in your entire program and can be accessed multiple number of times.

Preventing multiple definition in C++

#pragma once and include guards can only prevent multiple definitions in a single translation unit (a .cpp file). The individual units know nothing about each other, and so cannot possibly remove the multiple definition. This is why when the linker links the object files, it sees the multiple definitions still.

To solve this issue, change common.hpp to:

#pragma once

void some_function();

This tells the compiler that there is some code for some_function.
Then, add a file common.cpp that contains the code for the common.hpp header:

#include "common.hpp"
#include <iostream>

void some_function() {
std::cout << "something" << std::endl;
}

Finally, change the g++ command to:

g++ main.cpp common.cpp Thing.cpp -o main

How do I avoid the multiple definition of ... Error in this case

The problem is here:

#ifndef BOARDCOMPUTER_H
#define BOARDCOMPUTER_H

#include <Arduino.h>
#include <TFT_eSPI.h>

TFT_eSPI disp = TFT_eSPI();
...
#endif

You define disp in a header file. Defining variables in header files is plain wrong, it cannot work (except if you include the .h file in a single .c file, but then the .h file is pretty pointless anyway) and it leads to these "multiple definition" issues at link time.

This happens here:

  • you include Boardcomputer.h in foo.c, therefore foo.o will contain a variable disp. The code compile fine.
  • you include Boardcomputer.h in bar.c, therefore bar.o will contain a variable disp. The code compile fine.
  • you link foo.o and bar.o in order to produce an executable, and the linker sees two variables disp, one in bar.o and one in foo.o, hence the multiple definition error

You want this:

Boardcomputer.h

#ifndef BOARDCOMPUTER_H
#define BOARDCOMPUTER_H

#include <Arduino.h>
#include <TFT_eSPI.h>

extern TFT_eSPI disp; // declare it here

#endif

Boardcomputer.c

#include "Boardcomputer.h"
...
TFT_eSPI disp = TFT_eSPI(); // define it here
...

Linker error: Multiple definition of a function

Put the definition of random() in a .cpp source file.

Use include guards in your .h header files:

#ifndef RANDOM_H
#define RANDOM_H
extern int random();
...
...
#endif

How to avoid multiple definition error for global constants?

The solution to this is to have a separate declaration and definition...

Header (*.h; sorry, I don't know WinAPI type names, adapt as necessary):

extern const char szClassName[];

Implementation (*.c or *.cpp)

const char szClassName[] = "hello, world"

You're seeing the problem because a new symbol szClassName is being declared each time one of your *.c or *.cpp files includes the header (even with the include guards!); and that makes the linker confused (see below).

Do note that this will make sizeof(szClassName) not work anymore.

Further explanation:

After preprocessing, the compiler is basically seeing this:

  • file "a.c": const char someSymbol[] = <some text, don't care what right now>;
  • file "b.c": const char someSymbol[] = <some text, don't care if it's the same>;
  • file "c.c": const char someSymbol[] = <some text, ditto>;

When the linker is linking the object files (say, "a.obj", "b.obj" and "c.obj"), it sees the same symbol being defined with a new value (at least as far as the linker is concerned) --- and thus it fails with an error.

Understanding what causes this multiple definition error

One-Definition-Rule defines rules for two scopes, i.e. translation unit scope and program scope.

The following rule with translation unit scope states that the same translation unit must not comprise two different definitions of the same function:

Only one definition of any variable, function, class type, enumeration
type, or template is allowed in any one translation unit (some of
these may have multiple declarations, but only one definition is
allowed).

So, if you have two different .cpp-files, than you have two different translation units, and each of them may have their own definition of hello(); ODR is not violated in the scope of a translation unit.

The following rule with program scope defines that an odr-used function must be defined exactly once in the program:

One and only one definition of every non-inline function or variable
that is odr-used (see below) is required to appear in the entire
program (including any standard and user-defined libraries). The
compiler is not required to diagnose this violation, but the behavior
of the program that violates it is undefined.

The definition of odr-used informally states that for every function that is called or which's address is taken must be defined in the program:

Informally, an object is odr-used if its address is taken, or a
reference is bound to it, and a function is odr-used if a function
call to it is made or its address is taken
. If an object or a
function is odr-used, its definition must exist somewhere in the
program; a violation of that is a link-time error.

So, if more than one .cpp-file exposes an implementation of hello(), and if this function is called or referenced, then ODR from program scope is clearly violated.

If the respective function is not odr-used (i.e. called or referenced), ODR should - to my understanding - not be violated;

If a compiler complains about duplicate symbols, than this is because the program violates linkage rules (please confer also SO answer concerning "If I don't odr-use a variable"). C++11 §3.5[basic.link]/9 states:

Two names that are the same and that are declared in different scopes
shall denote the same variable, function, type, enumerator, template
or namespace if

  • both names have external linkage or else both names have internal linkage and are declared in the same translation unit; and ...

To avoid this, make sure that at most one implementation of hello() is exposed, and make all others static or use an unnamed namespace.

In the C programming language, static is used with global variables and functions to set their scope to the containing file, i.e. it does not expose this implementation and name clashes with other binaries are avoided.

So a reasonable suggestion would be: Make function definitions, that are solely used within a translation unit, visible only to this translation unit; and define functions that are exposed within a namespace or class in order to avoid unintended or unforeseeable name clashes / duplicate symbol problems in the linker.



Related Topics



Leave a reply



Submit