I faced a situation where different order of linking librdkafka and the Pulsar C++ client does matter, because both of them include their version of LZ4 compression. The linking fails because of multiple definitions of LZ4 functions (both librdkafka and Pulsar have the same names for those functions). I checked the static libraries, but I couldn't find anything suspicious, why it works in one order and doesn't in the another. Because it is hard to provide a minimal working example with those big libraries, I tried to reproduce the same situation, and I was able to do so. I created a small project where the linking order matters.
libA.hpp:
#pragma once
void NotClashingFunctionA();
void ClashingFunction();
libA.cpp:
#include "libB.hpp"
#include <iostream>
void NotClashingFunctionA() {
std::cout << "Not clashing function A\n";
}
void ClashingFunction() {
std::cout << "Clashing function A\n";
}
libB.hpp:
#pragma once
void NotClashingFunctionB();
libB.cpp:
#include "libB.hpp"
#include <iostream>
void NotClashingFunctionB() {
std::cout << "Not clashing function B\n";
}
libBSub.hpp:
#pragma once
void ClashingFunction();
libBSub.cpp:
#include "libBSub.hpp"
#include <iostream>
void ClashingFunction() {
std::cout << "Clashing function B\n";
}
main.cpp:
#include "libA.hpp"
#include "libB.hpp"
#include "libBSub.hpp"
int main() {
NotClashingFunctionA();
NotClashingFunctionB();
ClashingFunction();
return 0;
}
CMakeLists.txt:
project(clashing)
add_library(A STATIC libA.cpp)
add_library(B STATIC libB.cpp libBSub.cpp)
add_executable(working main.cpp)
target_link_libraries(working A B)
add_executable(failing main.cpp)
target_link_libraries(failing B A)
From the logs I can clearly see that working
just links fine:
clang++ -g -rdynamic CMakeFiles/working.dir/main.cpp.o -o working libA.a libB.a
make[3]: Leaving directory 'build'
[100%] Built target working
But failing
fails to link:
clang++ -g -rdynamic CMakeFiles/failing.dir/main.cpp.o -o failing libB.a libA.a
ld: libA.a(libA.cpp.o): in function `ClashingFunction()':
libA.cpp:9: multiple definition of `ClashingFunction()'; libB.a(libBSub.cpp.o):libBSub.cpp:5: first defined here
clang-12: error: linker command failed with exit code 1 (use -v to see invocation)
I removed the common prefixes to make the logs more readable.
As you can see the only difference between the two is the linking order A
and B
. I don't know why it works in A B
order, and not B A
order.
If you cannot explain it in details, helping keywords are also very appreciated, because I have absolutely no idea why it is happening.
To understand why, read this (earlier) post or this (nicer) one.
To make a concrete example:
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
).