动态加载的库和共享的全局符号

phl*_*psy 9 c++ linux g++ cmake shared-libraries

由于我在动态加载的库中观察到了全局变量的一些奇怪行为,因此我编写了以下测试.

首先,我们需要一个静态链接的库:标题 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
Run Code Online (Sandbox Code Playgroud)

和来源 test.cpp

#include "base.hpp"

test global_test = test(1);
Run Code Online (Sandbox Code Playgroud)

然后我写了一个动态加载的库: library.cpp

#include "base.hpp"

extern "C" {
  test* get_global_test() { return &global_test; }
}
Run Code Online (Sandbox Code Playgroud)

和加载此库的客户端程序: 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;
}
Run Code Online (Sandbox Code Playgroud)

现在我用.编译静态加载的库

g++ -Wall -g -c base.cpp
ar rcs libbase.a base.o
Run Code Online (Sandbox Code Playgroud)

动态加载的库

g++ -Wall -g -fPIC -shared library.cpp libbase.a -o liblibrary.so
Run Code Online (Sandbox Code Playgroud)

和客户

g++ -Wall -g -ldl client.cpp libbase.a -o client 
Run Code Online (Sandbox Code Playgroud)

现在我观察:客户端和动态加载的库拥有不同版本的变量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)
Run Code Online (Sandbox Code Playgroud)

分析创建的makefiles我发现cmake构建了客户端

g++ -Wall -g -ldl -rdynamic client.cpp libbase.a -o client
Run Code Online (Sandbox Code Playgroud)

这最终会产生稍微不同但却致命的行为:global_test客户端和动态加载的库是相同的,但在程序结束时会被销毁两次.

我是以错误的方式使用cmake吗?是否有可能客户端和动态加载的库使用相同global_test但没有这种双重破坏问题?

Tom*_*mek 1

我的第一个问题是,是否有任何特殊原因导致您静态和动态(通过 dlopen)链接相同的代码?

对于您的问题: -rdynamic 将从您的程序中导出符号,并且可能发生的情况是动态链接器将对全局变量的所有引用解析为它在符号表中遇到的第一个符号。是哪一个我也不知道。

编辑:鉴于您的目的,我会以这种方式链接您的程序:

g++ -Wall -g -ldl client.cpp -llibrary -L. -o client
Run Code Online (Sandbox Code Playgroud)

您可能需要修复订单。