[英]Dynamic loaded libraries and shared global symbols
由于我在动态加载的库中观察到了全局变量的一些奇怪行为,因此我编写了以下测试。
首先,我们需要一个静态链接的库:标题test.hpp
#ifndef __BASE_HPP
#define __BASE_HPP
#include <iostream>
class test {
private:
int value;
public:
test(int value) : value(value) {
std::cout << "test::test(int) : value = " << value << std::endl;
}
~test() {
std::cout << "test::~test() : value = " << value << std::endl;
}
int get_value() const { return value; }
void set_value(int new_value) { value = new_value; }
};
extern test global_test;
#endif // __BASE_HPP
和源test.cpp
#include "base.hpp"
test global_test = test(1);
然后我写了一个动态加载的库: library.cpp
#include "base.hpp"
extern "C" {
test* get_global_test() { return &global_test; }
}
以及加载此库的客户端程序: client.cpp
#include <iostream>
#include <dlfcn.h>
#include "base.hpp"
typedef test* get_global_test_t();
int main() {
global_test.set_value(2); // global_test from libbase.a
std::cout << "client: " << global_test.get_value() << std::endl;
void* handle = dlopen("./liblibrary.so", RTLD_LAZY);
if (handle == NULL) {
std::cout << dlerror() << std::endl;
return 1;
}
get_global_test_t* get_global_test = NULL;
void* func = dlsym(handle, "get_global_test");
if (func == NULL) {
std::cout << dlerror() << std::endl;
return 1;
} else get_global_test = reinterpret_cast<get_global_test_t*>(func);
test* t = get_global_test(); // global_test from liblibrary.so
std::cout << "liblibrary.so: " << t->get_value() << std::endl;
std::cout << "client: " << global_test.get_value() << std::endl;
dlclose(handle);
return 0;
}
现在我用。编译静态加载的库
g++ -Wall -g -c base.cpp
ar rcs libbase.a base.o
动态加载的库
g++ -Wall -g -fPIC -shared library.cpp libbase.a -o liblibrary.so
和客户
g++ -Wall -g -ldl client.cpp libbase.a -o client
现在我观察:客户端和动态加载的库拥有变量global_test
的不同版本。 但在我的项目中,我正在使用cmake。 构建脚本如下所示:
CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
PROJECT(globaltest)
ADD_LIBRARY(base STATIC base.cpp)
ADD_LIBRARY(library MODULE library.cpp)
TARGET_LINK_LIBRARIES(library base)
ADD_EXECUTABLE(client client.cpp)
TARGET_LINK_LIBRARIES(client base dl)
分析创建的makefile
我发现cmake用它构建客户端
g++ -Wall -g -ldl -rdynamic client.cpp libbase.a -o client
这最终导致一个稍微不同但致命的行为:客户端的global_test
和动态加载的库是相同的,但在程序结束时将被销毁两次。
我是以错误的方式使用cmake吗? 是否可能客户端和动态加载的库使用相同的global_test
但没有这种双重破坏问题?
g++ -Wall -g -ldl -rdynamic client.cpp libbase.a -o client
CMake添加-rdynamic
选项,允许加载的库解析加载可执行文件中的符号......所以你可以看到这是你不想要的。 没有这个选项,它只是偶然错过了这个符号。
但是......你不应该做那样的事情。 您的库和可执行文件不应共享符号,除非它们确实应该共享。
始终将动态链接视为静态链接。
如果使用共享库,你必须定义要与宏观导出喜欢的东西在这里 。 请参阅那里的DLL_PUBLIC宏定义。
默认情况下,链接器不会将基本可执行文件中的全局变量('D')与共享库中的全局变量('D')组合在一起。 基本可执行文件是特殊的。 使用ld读取的那些模糊控制文件之一可能有一种模糊的方法,但我有点怀疑它。
--export-dynamic将导致a.out'D'符号可供共享库使用。
但是,请考虑这个过程。 第1步:从.o创建一个带有'U'的DSO和带有'D'的.a。 因此,链接器在DSO中包含符号。 步骤2,在其中一个.o文件中创建一个“U”,在.a和DSO中创建“D”。 它将尝试使用从左到右的规则来解决。
与函数相反,变量在任何情况下都会对模块之间的链接器造成某些困难。 更好的做法是避免跨模块边界的全局var引用,并使用函数调用。 但是,如果在基本可执行文件和共享库中放置相同的函数,那仍然会失败。
我的第一个问题是,是否有任何特定的原因,您静态和动态(通过dlopen)链接相同的代码?
对于您的问题:-rdynamic将从您的程序中导出符号,并且可能发生的情况是动态链接器将对全局变量的所有引用解析为它在符号表中遇到的第一个符号。 哪一个是我不知道的。
编辑:鉴于你的目的,我会以这种方式链接你的程序:
g++ -Wall -g -ldl client.cpp -llibrary -L. -o client
您可能需要修改订单。
我建议编译你计划链接到dinamic库的任何.a静态库,-fvisibility = hidden参数,所以:
g ++ -Wall -fvisibility = hidden -g -c base.cpp
我建议使用dlopen(... RTLD_LAZY|RTLD_GLOBAL);
合并全局符号表。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.