You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

macOS下两个依赖同一静态库的dylib为何共享全局变量?

静态库全局变量在多动态库中的共享问题确认

我意外发现,编译两个依赖同一静态库的独立dylib时,该静态库中定义的全局变量会被共享。有文章指出每个动态库的全局变量应相互独立,但测试代码证明情况并非如此。我希望确认这在macOS(可能Linux也一样)是否属于预期行为。

测试场景

  • 静态库"Foo"包含一个名为"Bar"的全局变量,是初始值为123的int类型;
  • dylib"AAA"链接到"Foo";
  • dylib"BBB"链接到"Foo";
  • 应用"MyApp"链接到dylib AAA和BBB;
  • 应用调用dylib"AAA"中的函数修改Bar,将其设为111;
  • 应用调用dylib"AAA"中的函数打印Bar;
  • 应用调用dylib"BBB"中的函数打印Bar。

预期与实际结果

我原本预期AAA打印Bar为111,BBB打印Bar仍为123,但实际BBB打印的Bar也是111,说明从MyApp视角看,Bar只有一个共享实例。

猜测与验证

我猜测由于Bar同时暴露在AAA和BBB中,动态链接时因名称完全相同,链接器无法区分,导致其中一个实例“生效”。

在Xcode的“Other C++ Flags”中设置-fvisibility=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;
}

动态库global_test_dynamic_1

创建了两个使用CGlobalTest静态库的动态库,分别名为global_test_dynamic_1和global_test_dynamic_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;
}

内容的提问来源于stack exchange,提问作者Mark Coniglio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 13:52:00