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
相关产品推荐
相关产品推荐

