静态变量在不同模块上共享价值

Static variables share value across different modules

本文关键字:共享 模块 变量 静态      更新时间:2023-10-16

第一个问题上下文:Linux X64,GCC v4.8.5。有一个应用程序加载两个共享库(让它为Module1.so和Module2.SO),这些模块具有部分相同的代码。现在一点代码:

//SomeClass.h
class SomeClass
{
public:
    static unsigned long& s_uObj2()
    {
        static unsigned long s_uObj2;
        return s_uObj2;
    };
    void Initialize();
};
//SomeClass.cpp
void SomeClass::Initialize()
{
     if (0 == s_uObj2())
     {
         //do init
     }
     s_uObj2()++; //acts as a counter
}

此代码已经很久以前编写,它的想法是防止每个模块中的someclass的双重初始化。问题:此实现以某种方式共享不同模块(在单个应用程序中)的S_UOBJ2值,这导致了以下事实:仅将初始化第一个模块。

那怎么可能?我认为应该隔离不同模块之间的地址空间?

请不要指出"静态变量如何工作"的一些一般案例定义。我真正需要的 - 分析为什么不同的模块在这种确切情况下共享单个变量的值。那是因为它是真实的项目,我无法重构以使其正常工作。

问题:此实施以某种方式共享S_UOBJ2值跨不同模块(在单个应用程序中),这导致了以下事实:

那怎么可能?我认为应该是隔离的地址空间在不同的模块之间?

这是C 标准的一个定义规则要求。该规则基本上说,所有具有相同名称的全局对象都必须在整个程序中解析为单个定义。例如,所有全局变量(包括类似于您的案例)必须解析到同一单个对象。

现在,GCC非常努力地保留所有共享库中的ODR。如果您在代码上方构建并检查其导出的符号,则可以看到它导出SomeClass::s_uObj2

$ g++ tmp.cpp -shared -fPIC && objdump -T a.out | c++filt
...
0000000000200970  w   DO .bss   0000000000000008  Base        SomeClass::s_uObj2()::s_uObj2

这意味着在启动时,动态链接器将把SomeClass::s_uObj2()::s_uObj2的所有重复副本解析为 single 对象(这是恰好已加载的第一个共享库中的副本)。

克服此问题的通常方法(如果您真的愿意放弃ODR,那是不好的,那是不好的)是避免从图书馆导出s_uObj2,即限制其可见性。

有很多方法可以做到这一点,我只列举几个:

  • -fvisibility-inlines-hidden

    编译
  • __attribute__((visibility("hidden")))附加到s_uObj2的定义

  • 将您的类声明放入

    #pragma GCC visibility push(hidden)

    ...

    #pragma GCC visibility pop

第一个是讨厌的,因为它将有效地禁用所有代码的ODR,而不仅仅是上面的摘要。后两个更细粒度。