简体   繁体   English

为什么 RTLD_DEEPBIND 和 RTLD_LOCAL 不能防止 static class 成员符号的冲突

[英]why are RTLD_DEEPBIND and RTLD_LOCAL not preventing collision of static class member symbol

I am trying to write a simple plugin system for an application and would like to prevent plugins from stomping on each others symbols, however RTLD_DEEPBIND and RTLD_LOCAL don't seem to be enough when it comes to static class members when they happen to have the same name in different plugins.我正在尝试为应用程序编写一个简单的插件系统,并希望防止插件踩到其他符号,但是当涉及到 static class 成员时,RTLD_DEEPBIND 和 RTLD_LOCAL 似乎还不够在不同的插件中命名。

I wrote a stripped down example to show what I mean.我写了一个精简的例子来说明我的意思。 I compiled and ran it like this:我像这样编译并运行它:

g++ -c dumb-plugin.cpp -std=c++17 -fPIC
gcc -shared dumb-plugin.o -o dumb1.plugin
cp dumb1.plugin dumb2.plugin
g++ main.cpp -ldl -o main
./main

And the content of the output file for the second plugin showed that it reused the the class from the first plugin.第二个插件的 output 文件的内容显示它重用了第一个插件中的 class。

How can I avoid this?我怎样才能避免这种情况?

EDIT: I compiled the plugin with clang(not main just the plugin) and it worked despite all of the RTLD_DEEPBIND stuff being in main.cpp which was still compiled with g++.编辑:我用clang编译了插件(不仅仅是插件),尽管所有RTLD_DEEPBIND东西都在main.cpp中,它仍然用g++编译。 It didn't work when the plugin was compiled with gcc 10.3 or 11.1 even when I tried -Bsymbolic.使用 gcc 10.3 或 11.1 编译插件时,即使我尝试了 -Bsymbolic,它也不起作用。 Is this a bug?这是一个错误吗?

If I run readelf on the DSO compiled/linked with clang i see these 2 lines:如果我在使用 clang 编译/链接的 DSO 上运行 readelf,我会看到以下两行:

21: 00000000000040b0     4 OBJECT  UNIQUE DEFAULT   26 _ZN9DumbClass7co[...]_ZN9DumbClass7co[...]
25: 00000000000040b0     4 OBJECT  UNIQUE DEFAULT   26 _ZN9DumbClass7co[...]

and with gcc i get:并使用 gcc 我得到:

20: 00000000000040a8     4 OBJECT  WEAK   DEFAULT   24 _ZN9DumbClass7co[...]
27: 00000000000040a8     4 OBJECT  WEAK   DEFAULT   24 _ZN9DumbClass7co[...]

with WEAK instead of UNIQUE under the BIND column.在 BIND 列下使用 WEAK 而不是 UNIQUE。

dumb-plugin.cpp:哑插件.cpp:

#include <dlfcn.h>
#include <cstdio>
#include <string>

int global_counter = 0;
static int static_global_counter = 0;

std::string replace_slashes(const char * str) {
    std::string s;
    for (const char* c = str; *c != '\0'; c++)
        s += (*c == '/')?
            '#' : *c;
    return s;
}

void foo() {}

class DumbClass {
    public:
        static inline int counter = 0;
};

extern "C" void plugin_func() {
    static int static_local_counter = 0;

    Dl_info info;
    dladdr((void*)foo, &info);

    std::string path = "plugin_func() from: " + replace_slashes(info.dli_fname);
    auto fp = std::fopen(path.c_str(), "w");

    fprintf(fp, "static local counter: %d\n",  static_local_counter++);
    fprintf(fp, "DumbClass::counter: %d\n",    DumbClass::counter++);
    fprintf(fp, "global counter: %d\n",        global_counter++);
    fprintf(fp, "static global counter: %d\n", static_global_counter++);

    std::fclose(fp);
}

main.cpp:主.cpp:

#include <dlfcn.h>
#include <iostream>
#include <unistd.h>
#include <string.h>


int main () {
    char path1[512], path2[512];
    getcwd(path1, 512);
    strcat(path1, "/dumb1.plugin");
    getcwd(path2, 512);
    strcat(path2, "/dumb2.plugin");

    auto h1 = dlopen(path1, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);
    auto h2 = dlopen(path2, RTLD_NOW | RTLD_LOCAL | RTLD_DEEPBIND);

    auto func = (void(*)())  dlsym(h1, "plugin_func");
    func();
    func =      (void(*)())  dlsym(h2, "plugin_func");
    func();
}

gcc implements static inline data members (and also static data members of class templates, inline or not, and static variables in inline functions, and perhaps other things as well) as global unique symbols (a GNU extension to the ELF format). gcc implements static inline data members (and also static data members of class templates, inline or not, and static variables in inline functions, and perhaps other things as well) as global unique symbols (a GNU extension to the ELF format). There is only one such symbol with a given name per process, by design.根据设计,每个进程只有一个具有给定名称的此类符号。

clang implements such things as normal weak symbols. clang实现了诸如普通弱符号之类的东西。 These will not collide when RTLD_LOCAL and RTLD_DEEPBIND are used.当使用 RTLD_LOCAL 和 RTLD_DEEPBIND 时,它们不会发生冲突。

There are several ways to avoid collisions, but all of them require plugin writers to take an action.有几种方法可以避免冲突,但所有这些方法都需要插件编写者采取行动。 The best way IMO is to use hidden symbol visibility by default , only opening symbols that are meant to be dlsym d. IMO 的最佳方式是默认使用隐藏符号可见性,仅打开应为dlsym d 的符号。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM