I recently encountered a crash issue when I linked two shared libraries (both made by myself) together. I eventually found it was because of one source file duplicated between the two files. In that source file a global std::vector was defined (in fact a static member of a class), and it ended up with being freed twice -- one by each library.
I then wrote some test code to verify my thought. Here in a header I declare a class and a global object of this class:
#ifndef SHARED_HEADER_H_
#define SHARED_HEADER_H_
#include <iostream>
struct Data {
Data(void) {std::cout << "Constructor" << std::endl;}
~Data(void) {std::cout << "Destructor" << std::endl;}
int FuncDefinedByLib(void) const;
};
extern const Data data;
#endif
The FuncDefinedByLib function is left undefined.
I then created two libraries, libA and libB, both include this header.
libA looks like this
const Data data;
int Data::FuncDefinedByLib(void) const {return 1;}
void PrintA(void) {
std::cout << "LibB:" << &data << " "
<< (void*)&Data::FuncDefinedByLib << " "
<< data.FuncDefinedByLib() << std::endl;
}
It defines the global data object, the FuncDefinedByLib function, and a function PrintA that prints the address of the data object, the address of FuncDefinedByLib, and the return value of FuncDefinedByLib.
libB is almost same as libA except the name PrintA is changed to PrintB and FuncDefinedByLib returns 2 instead of 1.
Then I create a program that links to both of the libraries and calls PrintA and PrintB. Before encountering the crash issue I thought both libraries would create their own versions of class Data. However, the actual output
Constructor
Constructor
LibB:0x7efceaac0079 0x7efcea8bed60 1
LibB:0x7efceaac0079 0x7efcea8bed60 1
Destructor
Destructor
Indicates that both libraries use only one version of class Data and only one version of const Data data even if the class and the object are defined differently, which is from libA (I understand it is because libA is linked first). And the double destruction clearly explains my crash problem.
So here are my questions
How does this happen? I understand the main code linking against the two libraries may only link to the first symbol it sees. But a shared library should has been linked internally when it is created (or it is not? I really have no much knowledge of shared library), how can they know there is a twin class in other libraries and link to that when after they have been created on their own?
I know having duplicate code between shared libraries is generally a bad practice. But is there a condition that by satisfying it duplication between libraries is safe? Or is there a systematic way to make my code duplicabale without risk? Or it is never safe and should always be strictly prohibited? I don't want to always split another shared library just to share a tiny piece of code.
This behavior looks magical. Does anyone utilize this behavior to do some good magical things?