簡體   English   中英

動態加載的庫和共享的全局符號

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM