在多个共享库中时,静态库成员的多个初始化

时间:2018-07-06 21:20:51

标签: c++ compilation linker shared-libraries static-initialization

请注意,这不是Multiple instances of singleton across shared libraries on Linux的副本,因为添加 # Show user what edits were successfuly made. print('{} was successfully updated with the following changes:'.format(os.path.basename(self.file))) # Loop through keys in edits for n in edits: # Loop through entries of current key if isinstance(edits[n], dict): # Print the current key print(n) for m in edits[n]: # Print current entry of current key print(m, ':', edits[n][m]) elif isinstance(edits[n], list): # Print the current key followed by the elements print(n) for m in edits[n]: # Print current entry of current key print('- ', [m]) else: # scalar print('{}: {}'.format(n, edits[n])) 标志不会改变问题中所描述的内容。

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

使用两个共享库的任何可执行文件都会调用静态库的初始化代码两次,从而使诸如test.yaml was successfully updated with the following changes: A: 7 test.yaml was successfully updated with the following changes: A: Q: PQ 或gflags全局变量的行为异常。 这是预期的行为吗?这是动态链接程序的错误/弱点吗?

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

示例:

static.h

-rdynamic

static.cpp

folly::Singleton

shared.h

int get();

shared.cpp

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

shared2.h

int shared_get();

shared2.cpp

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

main.cpp

int shared2_get();

编译:

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

运行:

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

当我希望它是“ 0 0”时,结果是“ 1 1”。

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 nm看符号都包含:

libshared.so

仅将静态库链接到可执行文件可以解决此问题,但没有说明两个共享库如何在内部使用静态库而不会产生干扰。完成操作是如何获取“ 0 0”:

编译:

libshared2.so

1 个答案:

答案 0 :(得分:1)

为进一步简化该问题,您获得的结果与拥有

static.cpp:

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

shared1.cpp:

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

shared2.cpp:

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

(IOW,两个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符号(或者,如果您已应用hidden属性,则不应该)应该是导出的构造函数。