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

8
操作系统是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位于相同的目录中,但我事先不知道任何关于它们的信息。每个这样的额外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个回答

6
阅读dyld源代码(搜索@executable_path),我可以肯定地说答案是否定的。@executable_path将被替换为主可执行文件路径,该路径存储在dyld模块中作为全局字符串。
是的,你的怀疑是正确的,dyld在启动时读取和保存其环境变量,因此您无法在运行时更改它们(您可以搜索我链接的同一源文件以获取DYLD_LIBRARY_PATH)。您可以拥有一个存根应用程序来设置环境变量,然后启动真正的应用程序。 dyld在这里没有提供太多解决方案,它并不真正设计让您链接任意私有第三方库。

2
如果您正在维护OtherApp,可以使用@loader_path而不是@executable_path来定位依赖项:@loader_path始终解析为需要加载库的模块(即库或可执行文件)的路径,因此总是找到递归依赖项。
这在Mac Os 10.5及以上版本中可用。 详细信息请参见“man dyld”。
另一个选项是在主库之前打开依赖项。

我不维护OtherApp(如果是的话,我会直接将DLL包含在MyApp中)。在加载主库之前使用dlopen加载依赖项是行不通的:从dyld的角度来看,@executable_path/bar.dylib/Application/OtherApp.app/Contents/MacOS/bar.dylib不是同一个文件(这里没有“SONAME”的概念;搜索DLL的路径作为DLL名称),因此dyld拒绝使用手动加载的bar.dylib(我已经尝试过)。 - Thomas Pornin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接