简体   繁体   English

为什么dlopen加载的主可执行文件和共享库共享一个命名空间静态变量的副本?

[英]Why the main executable and a shared library loaded by dlopen share one copy of a namespace static variable?

As far as I understand namespace scope static variables should have one copy in each compilation unit. 据我所知,命名空间范围静态变量应该在每个编译单元中有一个副本。 So if I have a header file like this: 所以,如果我有这样的头文件:

class BadLad {                                                                                      
public:                                                                                             
    BadLad();                                                                                       
    ~BadLad();                                                                                      
};                                                                                                  

static std::unique_ptr<int> sCount;                                                                 
static BadLad sBadLad;

and badlad.cpp 和badlad.cpp

#include "badlad.h"

BadLad::BadLad() {
    if (!sCount) {
        sCount.reset(new int(1));
        std::cout<<"BadLad, reset count, "<<*sCount<<std::endl;
    }
    else {
        ++*sCount;
        std::cout<<"BadLad, "<<*sCount<<std::endl;
    }
}

BadLad::~BadLad() {
    if (sCount && --*sCount == 0) {
        std::cout<<"~BadLad, delete "<<*sCount<<std::endl;
        delete(sCount.release());
    }
    else {
        std::cout<<"~BadLad, "<<*sCount<<std::endl;
    }
}

I expect sCount and sBadLad to be unique in each cpp file that includes badlad.h. 我希望sCount和sBadLad在包含badlad.h的每个cpp文件中都是唯一的。

However, I found it's not the case in the following experiment: 但是,我发现在以下实验中并非如此:

  • I compile badlad to a shared library libBadLad.so . 我将badlad编译为共享库libBadLad.so
  • I create another shared library libPlugin.so which links libBadLad.so , only plugin.cpp includes badlad.h , so I expect there is one copy of sCount in libPlugin.so. 我创建了另一个链接libBadLad.so共享库libPlugin.so ,只有plugin.cpp包含badlad.h ,所以我希望sCount中有一个sCount副本。
  • I create a main program that links libBadLad.so, I expect there is one copy of sCount in main. 我创建了一个链接libBadLad.so的主程序,我希望在main中有一个sCount副本。

The main program looks like this: 主程序如下所示:

#include <dlfcn.h>

int main() {
    void* dll1 = dlopen("./libplugin.so", RTLD_LAZY);
    dlclose(dll1);

    void* dll2 = dlopen("./libplugin.so", RTLD_LAZY);
    dlclose(dll2);

    return 0;
}

When executing the main program, I can see the sCount variable is first created and set to 1 before main is called, which is expected. 执行主程序时,我可以看到首先创建sCount变量,并在调用main之前将其设置为1,这是预期的。 But then after the first dlopen is called, sCount is incremented to 2, and subsequently decreased to 1 when dlclose is called. 但是在调用第一个dlopen之后, sCount增加到2,然后在调用dlclose时减少到1。 The same happens to the second dlopen/dlclose. 第二个dlopen / dlclose也是如此。

So my questions is, why there is only one copy of sCount? 所以我的问题是,为什么只有一份sCount? Why the linker doesn't keep the copies separate (which I think is what most people expect)? 为什么链接器不会将副本分开(我认为这是大多数人所期望的)? It behaves the same if I link libPlugin.so to main directly instead of dlopen. 如果我将libPlugin.so直接链接到main而不是dlopen,它的行为相同。

I'm running this on macOS with clang-4 (clang-900.0.39.2). 我正在使用clang-4(clang-900.0.39.2)在macOS上运行它。

EDIT: please see the full source code in this repo . 编辑:请参阅此repo中的完整源代码。

(Iteration 2) (迭代2)

What happens in your case is very interesting and very unfortunate. 在你的情况下发生的事情是非常有趣和非常不幸的。 Let's analyze it step by step. 让我们一步一步地分析它。

  1. Your program is linked against libBadLad.so . 您的程序与libBadLad.so链接。 This shared library is thus loaded on program startup. 因此,在程序启动时加载此共享库。 Constructors of static objects are executed before main . 静态对象的构造函数在main之前执行。
  2. Your program then dlopens libplugin.so . 你的程序然后dlopens libplugin.so This shared library is then loaded, and constructors of static objects are executed. 然后加载此共享库,并执行静态对象的构造函数。
  3. What about libBadLad.so that libplugin.so is linked against? libplugin.so链接的libBadLad.so怎么样? Since the process already contains an image of libBadLad.so , this shared library is not loaded the second time . 由于该进程已包含libBadLad.so的映像, 因此第二次不会加载此共享库 libplugin.so could just as well not link against it at all. libplugin.so也可以根本不与它链接。
  4. Back to the static objects of libplugin.so . 回到libplugin.so的静态对象。 There are two of them, sCount and sBadLad . 有两个, sCountsBadLad Both are constructed, in order . 两者都是按顺序构建的
  5. sBadLad has a user-defined non-inline constructor. sBadLad具有用户定义的非内联构造函数。 It is not defined in libplugin.so , so it is resolved against the already-loaded libBadLad.so , which has this symbol defined. 它没有在libplugin.so定义 ,因此它针对已经加载的libBadLad.so ,后者定义了此符号。
  6. BadLad::BadLad from libBadLad.so is called. BadLad::BadLad 来自libBadLad.so BadLad::BadLad
  7. This constructor references a static variable sCount . 此构造函数引用静态变量sCount This resolves to sCount from libBadLad.so , not sCount from libplugin.so , because the function itself is in libBadLad.so . 这解析为sCountlibBadLad.so ,而不是sCountlibplugin.so ,因为函数本身位于libBadLad.so This is already initialised and is pointing to an int that has the value of 1. 这已经初始化,并指向一个值为1的int
  8. The count is incremented. 计数递增。
  9. Meanwhile, sCount from libplugin.so sits quietly, being initialised to nullptr . 同时, sCountlibplugin.so安静地坐在那里,被初始化为nullptr
  10. The library is unloaded and loaded again, etc. 库被卸载并再次加载等。

And the moral of the story is? 故事的寓意是什么? Static variables are evil . 静态变量是邪恶的 Avoid. 避免。

Note the C++ standard has nothing to say about any of this, as it does not deal with dynamic loading. 请注意,C ++标准没有任何关于此的内容,因为它不涉及动态加载。

However a similar effect can be reproduced without any dynaamic loaading. 然而,可以在没有任何动态装载的情况下再现类似的效果。

   // foo.cpp
   #include "badlad.h"

   // bar.cpp
   #include "badlad.h"
   int main () {}

Build and test: 构建和测试:

   # > g++ -o test foo.cpp bar.cpp badlad.cpp
   ./test
   BadLad, reset count to, 1
   BadLad, 2
   BadLad, 3
   ~BadLad, 2
   Segmentation fault

Why segmentation fault? 为什么分段错误? This is our good old static initialisation order fiasco. 这是我们旧的静态初始化命令惨败。 The moral of the story? 这个故事的主旨? Static variables are evil . 静态变量是邪恶的

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Xcode不与共享库共享静态变量 - Xcode does not share static variable with shared library 如何使用 dlopen 检查共享库是否已成功加载或未加载? - How to check shared library is loaded successfully or not loaded using dlopen? 在运行dlopen的插件之间共享的运行时链接库全局变量? - are runtime linking library globals shared among plugins loaded with dlopen? 调用@plt函数时,在dlopen / static init上共享库SIGSEGV - Shared library SIGSEGV on dlopen / static init when calling @plt function 使用dlopen加载库时带有静态存储行为的动态变量 - dynamic variable with static storage behaviour with when libs loaded using dlopen 为什么动态 linker *减去*虚拟地址以找出 memory 中加载的共享库可执行文件的位置? - Why does the dynamic linker *subtract* the virtual address to find out location of loaded shared library executable in memory? Linux可执行文件通过dlopen打开共享库在emplace_back上崩溃 - Linux executable opening shared library through dlopen crashing on emplace_back 带有带有dlopen的共享库的不同数学符号绑定,并直接链接到可执行文件(Linux) - Different math symbol bindings with shared library with dlopen and directly linked into executable (Linux) C-共享库-dlopen,dlsym - C - Shared Library - dlopen, dlsym 当动态库使用静态库的符号时,请从静态库中打开一个动态库 - dlopen a dynamic library from a static library, when the dynamic library uses symbols of the static one
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM