gpt4 book ai didi

c++ - 为什么 RTLD_DEEPBIND 和 RTLD_LOCAL 不能防止静态类成员符号的冲突

转载 作者:行者123 更新时间:2023-12-05 03:31:20 25 4
gpt4 key购买 nike

我正在尝试为应用程序编写一个简单的插件系统,并希望防止插件相互踩踏符号,但是 RTLD_DEEPBIND 和 RTLD_LOCAL 似乎不足以解决静态类成员的问题在不同的插件中具有相同的名称。

我写了一个精简的例子来说明我的意思。我像这样编译并运行它:

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

第二个插件的输出文件内容表明它重用了第一个插件的类。

我怎样才能避免这种情况?

编辑:我用 clang 编译了插件(不仅仅是插件),尽管所有 RTLD_DEEPBIND 东西都在 main.cpp 中,它仍然工作,它仍然是用 g++ 编译的。当使用 gcc 10.3 或 11.1 编译插件时,即使我尝试了 -Bsymbolic,它也不起作用。这是错误吗?

如果我在用 clang 编译/链接的 DSO 上运行 readelf,我会看到这两行:

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

使用 gcc 我得到:

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

在 BIND 列下使用 WEAK 而不是 UNIQUE。

dumb-plugin.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 :

#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 将静态内联数据成员(以及类模板的静态数据成员,内联与否,以及内联函数中的静态变量,也许还有其他东西)作为全局唯一symbols(ELF 格式的 GNU 扩展)。根据设计,每个进程只有一个具有给定名称的此类符号。

clang 实现诸如普通弱符号之类的东西。当使用 RTLD_LOCAL 和 RTLD_DEEPBIND 时,它们不会发生冲突。

有几种方法可以避免冲突,但所有方法都需要插件编写者采取行动。 IMO 的最佳方式是使用 hidden symbol visibility by default , 只打开意味着 dlsymd 的符号。

关于c++ - 为什么 RTLD_DEEPBIND 和 RTLD_LOCAL 不能防止静态类成员符号的冲突,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/70660488/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com