gpt4 book ai didi

c++ - 在多个共享库中静态库成员的多重初始化

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:05:26 32 4
gpt4 key购买 nike

请注意,这不是 Multiple instances of singleton across shared libraries on Linux 的拷贝因为添加 -rdynamic 标志不会改变问题中描述的内容。

我有一个静态 C++ 库,它有一些静态初始化代码(用于需要在未命名的命名空间中调用构造函数的变量)。然后我有 2 个(或更多)共享库在内部使用该静态库。

任何同时使用这两个共享库的可执行文件都会调用静态库的初始化代码两次,从而导致 folly::Singleton 或 gflags 全局变量行为异常。这是预期的行为吗?这是动态链接器的错误/弱点吗?

编辑:请注意,在 macOS 上它只初始化一次。

示例:

静态.h

int get();

静态.cpp

static int y = 0;
struct C { C() : x(y++) {}; int x; };
namespace { C c; }
int get() { return c.x; }

共享.h

int shared_get();

共享.cpp

#include "static.h"
int shared_get() { return get(); }

共享2.h

int shared2_get();

共享2.cpp

#include "static.h"
int shared2_get() { return get(); }

主要.cpp

#include "shared.h"
#include "shared2.h"
#include <iostream>
int main() {
std::cout << shared_get() << " " << shared2_get() << std::endl;
return 0;
}

编译:

g++ -fPIC -g -c  -o static.o static.cpp
ar rcs libstatic.a static.o
g++ -g -fPIC -shared -o libshared.so shared.cpp ./libstatic.a
g++ -g -fPIC -shared -o libshared2.so shared2.cpp ./libstatic.a
g++ main.cpp ./libshared.so ./libshared2.so

运行:

LD_LIBRARY_PATH=. ./a.out

结果是“1 1”,而我期望它是“0 0”。

查看带有 nm 的符号,libshared.solibshared2.so 都包含:

t _GLOBAL__sub_I_static.cpp

仅将静态库链接到可执行文件解决了该行为,但没有解释两个共享库如何在不干扰的情况下在内部使用静态库。完成这里是如何获得“0 0”:

编译:

g++ -fPIC -g -c  -o static.o static.cpp
ar rcs libstatic.a static.o
g++ -g -fPIC -shared -o libshared.so shared.cpp
g++ -g -fPIC -shared -o libshared2.so shared2.cpp
g++ main.cpp ./libshared.so ./libshared2.so ./libstatic.a

最佳答案

为了进一步简化问题,您得到的结果等同于拥有

静态.cpp:

static int y = 0;
struct C { C() : x(y++) {}; int x; };
static C c;
int get() { return c.x; }

共享1.cpp:

#include "static.h"
#include "static.cpp"
int shared1_get() { return get(); }

共享2.cpp:

#include "static.h"
#include "static.cpp"
int shared2_get() { return get(); }

(IOW,2 个 DSO 中的每一个都将包含所有静态库的代码,因此我们可以跳过静态库部分以使示例更简单一些。)

在你的情况下

static C c; 

本质上是对 struct C 的构造函数的每个 DSO 调用,在您的情况下(结构上没有隐藏属性)被导出。

因为你有两个带有 static C c; 的库,c 的构造函数将在每个 DSO 中被调用一次,并且因为构造函数符号是导出的(默认) ,其中一个构造函数将获胜(连同其相关联的 int get();)并将其静态变量一起拖动。

本质上,将使用 static C c; 中的一个,并将构造两次。这听起来可能很奇怪,但就共享库而言,您应该考虑使用 C,而不是 C++,并将 static C c; 视为

/*C*/
static int y;
static struct C c;
__attribute__((constructor))
void C__ctor(struct C *this) /*exported function*/
{
C.x=y++;
}

要解决此问题,您可以将整个 C 结构标记为隐藏,这样属性也适用于构造函数

struct __attribute__((visibility("hidden")))  C { C() : x(y++) {}; int x; };

或者更好的是,使用 -fvisibility=hidden 编译并应用 visibility("default")明确的属性。

下面是一个可执行(shell 脚本)示例:

#!/bin/sh -eu
echo 'int get();' > static.h
cat > static.cpp <<EOF
static int y = 0;
#if 1 /*toggle to play with the visibility attribute*/
#define MAYBE_HIDDEN __attribute__((visibility("hidden")))
#else
#define MAYBE_HIDDEN
#endif
struct MAYBE_HIDDEN C { C() : x(y++) {}; int x; };
static C c;
int get() { return c.x; }
EOF
cat > shared.h <<EOF
int shared_get();
EOF

cat > shared.cpp <<EOF
#include "static.h"
#include "static.cpp"
int shared_get() { return get(); }
EOF

cat > shared2.h <<EOF
int shared2_get();
EOF

cat > shared2.cpp <<EOF
#include "static.h"
#include "static.cpp"
int shared2_get() { return get(); }
EOF
cat > main.cpp <<EOF
#include "shared.h"
#include "shared2.h"
#include <iostream>
int main() {
std::cout << shared_get() << " " << shared2_get() << std::endl;
return 0;
}
EOF
g++ -fPIC -g -c -o static.o static.cpp
#ar rcs libstatic.a static.o
g++ -g -fPIC -shared -o libshared.so shared.cpp #./libstatic.a
g++ -g -fPIC -shared -o libshared2.so shared2.cpp #./libstatic.a
g++ main.cpp ./libshared.so ./libshared2.so
./a.out

我已经跳过了静态库,直接包含了 C++ 代码,但您也可以恢复它——它不会改变结果。

如果你在没有隐藏属性的情况下编译,你可能想尝试运行nm -D libshared.so libshared1.so。您应该到达那里的 _ZN1CC1Ev_ZN1CC2Ev 符号(或者不是,如果您应用了隐藏属性)应该是导出的构造函数。

关于c++ - 在多个共享库中静态库成员的多重初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51217747/

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