覆盖用dlopen()加载的DLL中的@executable_path

时间:2011-03-22 23:12:49

标签: macos dynamic-linking dlopen executable-path

操作系统是MacOS X,特别是PowerPC G4上的10.5(Leopard),但我在运行10.6的x86上遇到了同样的问题。

我正在编写一个动态加载DLL的应用程序。 DLL(我们称之为foo.dylib)是位于硬盘上其他位置的另一个应用程序的一部分;我的应用程序以编程方式找到foo.dylib(确切的安置可能会改变,可能是用户通过运行的应用程序本身的GUI指定DLL路径)。例如,假设我的应用程序位于目录/Application/MyApp.app/Contents/MacOS中,而foo.dylib恰好位于/Application/OtherApp.app/Contents/MacOS中。 DLL加载使用dlopen()

现在,事实证明foo.dylib本身需要一堆其他DLL,它们位于同一目录中,但我事先并不知道。每个此类额外DLL都在foo.dylib中注册,其路径为@executable_path/bar.dylib@executable_path的语义是它应该被找到当前进程可执行文件的目录替换。这适用于OtherApp,不适合我:当我打开foo.dylib时,它会尝试加载bar.dylib,并在/Application/MyApp.app/Contents/MacOS/bar.dylib中找到它,这不是正确的目录。

解决方法是将DYLD_FALLBACK_LIBRARY_PATH环境变量设置为/Application/OtherApp.app/Contents/MacOS,但必须在启动我的应用程序之前完成(该环境变量仅由动态读取一次)链接器;使用setenv()putenv()以编程方式更改其值无效。这与动态发现foo.dylib文件的位置不兼容。

是否有一种编程方式来覆盖@executable_path的效果?

2 个答案:

答案 0 :(得分:6)

通过阅读dyld source(搜索@executable_path),我会说答案是明确的“不”。 @executable_path替换为主可执行路径,该路径作为全局字符串存储在dyld模块中。

是的,您的怀疑是正确的,dyld在启动时读取并保存其环境变量,因此您无法动态更改它们(您可以搜索我为DYLD_LIBRARY_PATH链接的相同源文件)。您可以拥有一个存根应用程序来设置环境变量,然后启动您的实际应用程序。 dyld在这里没有提供很多解决方案,它并不是真的旨在让你链接到任意私有第三方库。

答案 1 :(得分:2)

如果您维护OtherApp,则可以使用@loader_path而不是@executable_path来查找依赖项:@loader_path始终解析为需要加载库的模块(即库或可执行文件)的路径,因此始终可以找到递归依赖项。

从Mac Os 10.5开始提供 有关详细信息,请参阅“man dyld”。

另一个选项是dlopen在主库之前的依赖关系。