Multiple definition of inline functions when linking static libs
Asked Answered
R

1

13

I have a C++ program that I compile with mingw (gcc for Windows). Using the TDM release of mingw which includes gcc 4.4.1. The executable links to two static library (.a) files: On of them is a third-party library written in C; the other is a C++ library, written by me, that uses the C library provides my own C++ API on top.

An (in my view, excessive) portion of the C library's functionality is implemented in inline functions. You can't avoid including the inline functions when you use the C library's API, but when I try to link it all together, I'm getting link errors saying there is a multiple definition of all of the inline functions - both ones I have called in my C++ wrapper library and ones which I have not, basically anything defined inline in the headers has gotten a function created for it in both the C library and the C++ library.

It doesn't cause multiple definition errors when the include files are used multiple times in different .c or .cpp files in the same project; the problem is just that it generates one definition per library.

How/why is the compiler generating functions and symbols for these inline functions in both libraries? How can I force it to stop generating them in my code? Is there a tool I can run to strip the duplicate functions from the .a file, or a way to make the linker ignore multiple definitions?

(FYI, the third party library does include #ifdef __cplusplus and extern "C" guards in all its headers; anyway if that were the problem, it would not cause a multiple definition of symbol, it would cause the opposite problem because the symbol would be undefined or at least different.)

Notably, the link errors do NOT occur if I link to the third party C library's DLL; however then I get strange runtime failures that seem to have to do with my code having its own version of functions it should be calling from the DLL. (As if the compiler is creating local versions of functions I didn't ask for.)

Similar versions of this question have been asked before, however, I didn't find the answer to my situation in any of these:

The answer to this question was that the poster was multiply defining variables, my problem is multiple definition of inline functions: Repeated Multiple Definition Errors from including same header in multiple cpps

This was an MSVC program, but I'm using mingw; also, the poster's problem in this question was the definition of a C++ class constructor outside of the class body in a header, while my problem is with C functions that are inline: Static Lib Multiple Definition Problem

This fool renamed all his C code as C++ files and his C code wasn't C++-safe: Multiple definition of lots of std:: functions when linking

This one was just wanting to know why violating the one definition rule was not an error: unpredictable behavior of Inline functions with different definitions

Reisinger answered 7/2, 2010 at 17:47 Comment(5)
Notice that C99 inline semantics are different than the one of C++: In C, if one of the inline function declarations is explicitly specified extern, it creates an external definition - which is not an inline definition anymore. These external definitions cannot appear multiple times in the program. In C++ an explicit extern on such a function has no effect. You are best off with doing static inline in C.Indebted
Normally inline function definitions are marked as 'weak' symbols and the linker is supposed to remove all duplicates itself.Oversoul
Thanks Johannes: using preprocessor definitions, the C library's header files declare these functions "inline" if in the C library's .c files, and "extern inline" when they're in my project. But I'm not sure why they did it that way. The C library does have some code that uses the address of a function as a unique key value. As a C++ programmer I view that as bad practice and I really pray they didn't use the address of an inline function that way. I feel there's almost enough information among these comments to put into an answer, although I still can't compile the program.Reisinger
If it uses the address of the function as a key somewhere and relies on the same address across TUs, you can't use static inline anymore. You could use -fgnu89-inline and use extern inline even in C mode, though (see gcc.gnu.org/onlinedocs/gcc/Inline.html . If you compile without -std=c99, it may even be the mode in effect anyway already, i think).Indebted
I added the "C" tag to this question and (because one cannot have 6 tag) removed the "multiple-definition-error", which seems to be covered by "linker-error" too.Indebted
I
17

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");
}
Indebted answered 7/2, 2010 at 19:43 Comment(4)
This is a very good answer. Unfortunately, I still haven't been able to apply the knowledge to successfully compile my program - attempts to change the extern inlines to static inlines make it fail with the error that it is mixing static and nonstatic definitions of the same function. Perhaps I can use macros in my own code to change the names of the functions where I include the headers, but there are approximately 100 of these things in the headers...Reisinger
For further information, this discussion indicates the compiler optimization level can affect the issue: lkml.indiana.edu/hypermail/linux/kernel/0408.0/1787.htmlReisinger
I found something similar when building with MinGW gcc, and discovered that the MinGW headers appear to be incompatible with the -std=gnu99 option. This caused a lot of multiply-defined symbols when I attempted to link, as definitions for the functions were placed in each object file built in this way.Alurta
...so I found that using -fgnu89-inline was a suitable workaround for our case, as we needed the gnu99 scoping on for-loop counter variables. (ugh)Alurta

© 2022 - 2024 — McMap. All rights reserved.