两个带有共享静态库的 macOS dylib:为什么要共享全局变量?

问题描述 投票:0回答:0

令我惊讶的是,当编译恰好共享一个静态库的两个单独的dylib时,该静态库中定义的全局变量似乎是共享的。 这篇SO文章似乎表明每个动态库都会将其全局变量分开,但在上述情况下,下面包含的测试代码证明这不是真的。我正在寻求确认这在 macOS(可能还有 Linux)上是可以预期的。

鉴于这种情况:

  • 静态库“Foo”有一个名为“Bar”的全局;这是一个初始化为 123 的 int。
  • dylib“AAA”链接到“Foo”
  • dylib“BBB”链接到“Foo”
  • 应用程序“MyApp”链接到动态库 AAA 和 BBB。
  • 应用程序调用 dylib“AAA”中修改 Bar 的函数,将其设置为 111
  • 应用程序调用打印 Bar 的 dylib“AAA”中的函数
  • 应用程序调用打印 Bar 的 dylib“BBB”中的函数

我预计当“AAA”打印“Bar”时它会是 111,而当“BBB”打印 bar 时它仍然是 123。相反,当“BBB”打印“Bar”时,它是 111,表明 - 从MyApp 的观点——只有一个共享的“Bar”实例。

我的怀疑是,由于“Bar”同时被“AAA”和“BBB”暴露,当你动态链接两个dylib时,两个“wins”之一,因为名称完全相同,链接器无法区分两个。

这个怀疑似乎可以通过在Xcode的“Other C++ Flags”中设置“-fvisibility=hidden”标志得到证实。如果我为动态库“AAA”和“BBB”执行此操作,那么这两个全局变量似乎是不同的。我预计这是因为'visibility=hidden'隐藏了“Bar”的两个副本,从而解决了上一段中描述的冲突。

有人可以证实我对此的理解吗?

--- 示例代码 ---

静态库CGlobalTest有一个C++类,如下所示。该类在函数内部声明了一个全局,在.cpp 文件中声明了一个类全局和静态全局。函数 GetGlobal() 根据 GlobalType 参数返回对其中之一的引用。

CGlobalTest.cpp:

class CGlobalTest
{
public:
    CGlobalTest() { }
    
    static int& GetFunctionGlobal()
                {
                    static int sFunctionGlobal = 123;
                    return sFunctionGlobal;
                }
    
    static int& GetClassGlobal()
                {
                    return sClassGlobal;
                }
    
    static int& GetFileGlobal();
    
    static int& GetGlobal(
                    GlobalType  inType)
                {
                    switch (inType) {
                    case kFunctionGlobal:
                        return GetFunctionGlobal();
                        break;
                    case kClassGlobal:
                        return GetClassGlobal();
                        break;
                    case kFileGlobal:
                        return GetFileGlobal();
                        break;
                    }
                }
    
    static int  sClassGlobal;
};

CGlobalTest.h

#include "static_lib.h"

int CGlobalTest::sClassGlobal = 456;

int sFileGlobal = 789;

int&
CGlobalTest::GetFileGlobal()
{
    return sFileGlobal;
}

然后我有两个使用 CGlobalTest 静态库的动态库,称为 global_test_dynamic_1 和 global_test_dynamic_2。 1 和 2 的代码基本相同,所以我只包括第一个。

dynamic_lib_1.cpp:

#include "dynamic_lib_1.h"
#include "static_lib.h"
#include "stdio.h"

const char*
GlobalTypeToString(
    GlobalType  inType)
{
    const char* type = "";
    switch (inType) {
    case kFunctionGlobal:
        type = "Function Global";
        break;
    case kClassGlobal:
        type = "Class Global";
        break;
    case kFileGlobal:
        type = "File Global";
        break;
    }
    
    return type;
}

void dynamic_lib_1_set_global(enum GlobalType inType, int value)
{
    int& global = CGlobalTest::GetGlobal((GlobalType) inType);
    global = value;
    printf("Dynamic Lib 1: Set %s: %d (%p)\n", GlobalTypeToString(inType), global, &global);
}

void dynamic_lib_1_print_global(enum GlobalType inType)
{
    const int& global = CGlobalTest::GetGlobal((GlobalType) inType);
    printf("Dynamic Lib 1: %s = %d (%p)\n", GlobalTypeToString(inType), global, &global);
}

dynamic_lib_1.h

#ifdef __cplusplus
#define EXPORT extern "C" __attribute__((visibility("default")))
#else
#define EXPORT
#endif

#include "global_type.h"

EXPORT void dynamic_lib_1_set_global(enum GlobalType inType, int value);
EXPORT void dynamic_lib_1_print_global(enum GlobalType inType);

最后,有一个链接到两个dylib的应用程序。

#include "dynamic_lib_1.h"
#include "dynamic_lib_2.h"
#include "global_type.h"

#include <assert.h>
#include <dlfcn.h>
#include <stdio.h>
#include <unistd.h>

typedef void (*print_func)(enum GlobalType inType);
typedef void (*set_func)(enum GlobalType inType, int value);

int main()
{
    printf("App is starting up...\n");

    // LOAD DYNAMIC LIBRARY 1

    void* handle1 = dlopen("libglobal_test_dynamic_1.dylib", RTLD_NOW);
    assert(handle1 != NULL);

    print_func d1_print = (print_func) dlsym(handle1, "dynamic_lib_1_print_global");
    assert(d1_print != NULL);
    
    set_func d1_set = (set_func) dlsym(handle1, "dynamic_lib_1_set_global");
    assert(d1_set != NULL);
    
    // LOAD DYNAMIC LIBRARY 2

    void* handle2 = dlopen("libglobal_test_dynamic_2.dylib", RTLD_NOW);
    assert(handle1 != NULL);

    print_func d2_print = (print_func) dlsym(handle2, "dynamic_lib_2_print_global");
    assert(d2_print != NULL);
    
    set_func d2_set = (set_func) dlsym(handle2, "dynamic_lib_2_set_global");
    assert(d2_set != NULL);
    
    enum GlobalType type;
    
    printf("**************************************************\n");
    printf("** FUNCTION GLOBAL\n");
    printf("**************************************************\n");
    
    type = kFunctionGlobal;
    
    (d1_print)(type);
    (d2_print)(type);
    
    printf("** SET D1 TO 111 - THEN PRINT FROM D2\n");
    d1_set(type, 111);
    d1_print(type);
    d2_print(type);

    printf("** SET D2 TO 222 - THEN PRINT FROM D1\n");
    d2_set(type, 222);
    d2_print(type);
    d1_print(type);

    printf("**************************************************\n");
    printf("** CLASS GLOBAL\n");
    printf("**************************************************\n");
    
    type = kClassGlobal;
    
    (d1_print)(type);
    (d2_print)(type);
    
    printf("** SET D1 TO 111 - THEN PRINT FROM D2\n");
    d1_set(type, 111);
    d1_print(type);
    d2_print(type);

    printf("** SET D2 TO 222 - THEN PRINT FROM D1\n");
    d2_set(type, 222);
    d2_print(type);
    d1_print(type);

    printf("**************************************************\n");
    printf("** FILE GLOBAL\n");
    printf("**************************************************\n");
    
    type = kFileGlobal;
    
    (d1_print)(type);
    (d2_print)(type);
    
    printf("** SET D1 TO 111 - THEN PRINT FROM D2\n");
    d1_set(type, 111);
    d1_print(type);
    d2_print(type);

    printf("** SET D2 TO 222 - THEN PRINT FROM D1\n");
    d2_set(type, 222);
    d2_print(type);
    d1_print(type);

    return 0;
}
macos global dylib
© www.soinside.com 2019 - 2024. All rights reserved.