我的主应用程序静态链接到具有函数 ABC 的静态库 A,而我的动态库 xyz.dylib 也静态链接到具有相同函数 ABC 的同一个静态库 A。函数 ABC 使用全局定义的变量。
现在,当主应用程序在运行时使用 dlopen 加载 xyz.dylib 时。初始化程序在我调用 ABC 函数的地方被调用。此函数 ABC 并使用来自主应用程序地址空间的全局变量。
在 Osx 上,内联 dylib 链接器的函数将使用第一个使用的函数。因此,例如,如果一个内联函数首先在您的主可执行文件中使用,然后在加载的 dylib 中使用,它将使用主可执行文件中的那个。
这通常很好,除非您的内联引用了全局符号,在这种情况下,如果您的 dylib 和可执行文件的全局变量,您现在正在使用一个。
同样,这通常很好,因为始终使用相同的版本。
当您有 2 个内联函数引用了可执行文件和 dylib 中的全局变量时,就会出现问题,并且一个函数首先在可执行文件中使用,另一个函数首先在 dylib 中使用。然后你有一个不匹配的对。例如:
class MagicAlloc
{
void* Alloc() { return gAlloc.get(); }
void Free( void* v ) { gAlloc.free( v ); }
static RealAllocator gAlloc;
};
假设您在可执行文件中调用 MagicAlloc::Alloc,然后在 dylib 中调用它,现在对于两者中的所有分配,您将在可执行文件中使用 gAlloc。然后第一次调用 MagicAlloc::Free 发生在 dylib 中。然后,您将尝试从 dylib 中释放全局变量中二进制文件中分配的某些内容。
有两种解决方案:
不要使用内联来引用全局/静态。将全局结构和函数定义移动到同一个翻译单元(目标文件)中。将全局变量标记为“静态”,这样它们甚至在 TLU 之外都不可见。现在您的函数将在链接步骤中静态解析,并绑定到正确的全局。
隐藏可执行文件中除插件 api 之外的所有符号。正常链接,但在链接二进制文件本身时,将以下内容传递给链接器:
-Wl,-exported_symbols_list,export_file
其中 export file 是应导出的链接符号列表。例如,您至少需要在该文件中有“_main”。现在,当您的 dylib 运行时,它将无法动态链接到错误的内联,因为它们不会在动态符号表中。第二种解决方案也更安全,因为恶意插件无法轻松访问全局变量。