Multiple Definition of Inline Functions When Linking Static Libs

Multiple definition of inline functions when linking static libs

First you have to understand the C99 inline model - perhaps there is something wrong with your headers. There are two kind of definitions for inline functions with external (non-static) linkage

  • External definition
    This definition of a function can only appear once in the whole program, in a designated TU. It provides an exported function that can be used from other TUs.

  • Inline definition
    These appear in every TU where declared as a separate definition. The definitions do not need to be identical to one another or to the external definition. If used internal in a library, they can do omit checking on function arguments that would otherwise be done in the external definition.

Each definition of the function has its own local static variables, because their local declarations have no linkage (they are not shared like in C++). A definition of a non-static inline function will be an inline definition if

  • Every function declaration in a TU includes the specifier inline, and
  • No function declaration in a TU includes the specifier extern.

Otherwise, the definition that must appear in that TU (because inline functions must be defined in the same TU where declared) is an external definition. In a call to an inline function it's unspecified whether the external or the inline definition is used. However, because the function defined in all cases is still the same (because it has external linkage), the address of it compares equal in all cases, no matter how many inline definitions appear. So if you take the function's address, it's likely the compiler resolves to the external definition (especially if optimizations are disabled).

An example that demonstrates a wrong use of inline, because it includes an external definition of a function twice in two TUs, causing a multiple definition error

// included into two TUs
void f(void); // no inline specifier
inline void f(void) { }

The following program is dangerous, because the compiler is free to use the external definition, but the program does not provide one

// main.c, only TU of the program
inline void g(void) {
printf("inline definition\n");
}

int main(void) {
g(); // could use external definition!
}

I have made some test cases using GCC that demonstrate the mechanism further:

main.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
printf("inline def main.c\n");
}

// defined in TU of second inline definition
void g(void);

// defined in TU of external definition
void h(void);

int main(void) {
// unspecified whether external definition is used!
f();
g();
h();

// will probably use external definition. But since we won't compare
// the address taken, the compiler can still use the inline definition.
// To prevent it, i tried and succeeded using "volatile".
void (*volatile fp)() = &f;
fp();
return 0;
}

main1.c

#include <stdio.h>

inline void f(void);

// inline definition of 'f'
inline void f(void) {
printf("inline def main1.c\n");
}

void g(void) {
f();
}

main2.c

#include <stdio.h>

// external definition!
extern inline void f(void);

inline void f(void) {
printf("external def\n");
}

void h(void) {
f(); // calls external def
}

Now, the program outputs what we expected!

$ gcc -std=c99 -O2 main.c main1.c main2.c
inline def main.c
inline def main1.c
external def
external def

Looking at the symbol table, we will see that the symbol of an inline definition is not exported (from main1.o), while an external definition is exported (from main2.o).


Now, if your static libraries each have an external definition of their inline functions (as they should), they will naturally conflict with each other. The solution is to make the inline functions static or just to rename them. These will always provide external definitions (so they are full fledged definitions), but they are not exported because they have internal linkage, thus not conflicting

static inline void f(void) {
printf("i'm unique in every TU\n");
}

Multiple definition within static library

A library is just a collection of object-files containing exported symbols.
It may contain any number of duplicates (just like real book libraries may contain many books with the same title). There is no linking involved.

When linking, generally speaking, the linker only looks at libraries in case there are unresolved symbols.
When looking for those, it might find one and if it does it will look no further for that symbol.

There may arise a conflict when it resolves another unresolved symbol found in another object file that contains a definition of an earlier found symbol; now it will generate an error of duplicate symbols.

GCC multiple definition errors when linking with external library

These header files are a bit old/dated. The usage of extern inline has changed with newer compilers. extern inline was the preferred way for many years. It worked fine on gcc but clang needed static inline

Now, even gcc wants static inline even with -O2 or you get what you're seeing. Since you're recompiling from source, you may have to edit the .h and change all of them to static.

I have boilerplate for my code that was #define craigs_inline extern inline and now I've switched it to #define craigs_inline static inline to keep the peace.

Note that I didn't investigate a compiler -foption_whatever that might obviate the need for this. If you find you one, please send me a comment as I'd be interested to know.

Why doesn't this create multiple defined symbols in this static library?

The key fact that was added to this question is the fact that the two modules with the duplicate symbol were linked into a static library.

A static library works very different than a shared library. When linking, the linker searches static libraries for referenced symbols. The first module that defines the referenced symbol gets linked to produce the final executable. The fact that another module defines the same symbol is irrelevant. If the other module with the duplicate symbol does not define any other symbol that the executable needs it does not get linked into the executable.

If you want to see a duplicate error with static libraries:

Call foo() and bar() from your main().

Define foo() and baz() in the 1st module in the static library.

Define bar() and baz() in the 2nd module in the static library.

Because both modules contain symbols referenced from main(), they are forced into getting linked with the executable, resulting in a duplicate symbol error due to baz().

what could cause multiple definition error again after I declare the function inline?

inline or not, once the header gets copied in at least two files, you cannot link the files anymore.

The only way you can safely implement a function in a header is to use static. This way, each copy of the function would be invisible to the other ones.

Note that there is no restriction to using them together, so you can safely write:

static inline bool testab(int a, int b)
{
return a>b;
}

Edit: More details

inline tells the compiler that you think the function is small enough to be inlined. That is you tell the compiler that you don't think the extra space for inlining the function matters much as opposed to the (slight) (possible) performance gain for it. Most compilers however are intelligent enough to decide that on their own and with your keyword, they would just tend a bit more to inline, and not necessarily always listen to you. This depends on the compiler, of course. Some compilers may completely ignore the keyword.

static on the other hand, means that whatever scope a static variable is defined, it will be invisible outside it. If you have a static variable in a function, it would be invisible outside it. If you have a static variable in a file (that is, a static global variable), it will be invisible outside it, which means the symbol is not there after compiling for the linker to see and get confused. That is why, if you have written a library in which there are global variables or functions that are not supposed to be visible outside the library, you should declare them as static.

Edit 2: Correction

Apparently, according to this answer, inline functions should not have its identifiers exported for the linker. Nonetheless, one can attach static to it either way just to make it more clear. Also apparently, some compilers export the identifier anyway, so in those cases static is indeed necessary.

linking error : multiple definition of static variable

So, this is a common mistake of misunderstanding how the header guards work.

Header guards save multiple declarations for one compilation unit, but not from errors during linking. One compilation unit implies a single cpp file.

E.g. apple.cpp includes apple.h and grapes.h, and apple.h in turn includes grapes.h. Then header guards will prevent the inclusion of the file grapes.h again during compilation.

But when the process of compilation is over, and the linker is doing its job of linking the files together, then in that case it sees two memory locations for the same static variables, since the header file was included in a separate translation unit, say apple2.cpp to which its trying to link, thus causing the multiple definition error.

The only way to resolve it is to move the definition of the static variable to a cpp file.

Avoid compiling definition of inline function multiple times

C++ doesn’t have the notion of an inline function that must be emitted in one translation unit and which therefore certainly need not be emitted anywhere else. (It doesn’t have the notion of emitting object code at all, but the point is that there’s no syntax that says “I promise this definition is ODR-identical to the others except that it and only it bears this marker.” so that compilers could do that.)

However, the behavior you want is the obvious way of implementing C++20 modules: because the definition of an inline function in a module is known to be the only definition, it can and should be emitted once in case several importing translation units need an out-of-line copy of it. (Inlining is still possible because the definition is made available in a compiler-internal form as part of building the module.) Bear in mind that member functions defined in a class in a module are not automatically inline, although constexpr still implies it.

Another ugly workaround is to make non-inline wrappers to be used outside of constant evaluation, although this could get unwieldy if there were multiple levels of constexpr functions that might also be used at runtime.

Multiply defined linker error using inlined functions

inline is just a suggestion, not a command. However, in general compilers are smart enough to do the right thing ( and Green Hills has a good reputation in so far as optimizations go ).

Make the function 'static inline', which will prevent the compiler from making the symbol exportable. That should fix your multiple definition link errors... the linker is complaining that the same function is exported from several source modules.

How can reordering the linked libraries fix multiple definitions error?

To understand why, read this (earlier) post or this (nicer) one.

To make a concrete example:

  • suppose main.o defines main(), fn(), and references a() and b().
  • libA.a contains a.o which defines a()
  • libB.a contains b.o which defines b(), and also a1.o which defines a() and fn().

Now, if you link with gcc main.o -lA -lB, the link will succeed (a.o from libA.a and b.o from libB.a will be selected into the link, and no symbol conflicts will arise. Notably, a1.o from libB.a will not be selected into the link).

But if you link with gcc main.o -lB -lA, then fn() will be multiply defined (because both a1.o and b.o from libB.a will be selected into the link, but the definition of fn() in a1.o will be in conflict with the definition of fn() in main.o).



Related Topics



Leave a reply



Submit