使用clang从命令行链接.dylib库

时间:2018-10-25 04:06:52

标签: c macos clang dylib

我正在尝试使用库编写C程序。该库提供了一个include/lib/目录,分别包含头文件和.dylib文件。

我的文件与include/lib/在同一目录中。我正在尝试使用以下命令进行编译:

clang -I"./include/" -L"./lib/" -lcsfml-graphics -lcsfml-window test.c

但是,当我运行程序时,出现以下错误:

% ./a.out 
dyld: Library not loaded: @rpath/libcsfml-graphics.2.4.dylib
  Referenced from: ~/src/CSFML-2.4-osx-clang/./a.out
  Reason: image not found
zsh: abort      ./a.out

使用这些库进行编译的正确方法是什么?我宁愿在编写小型程序时简单地使用命令行,而不必设置Xcode等。

% ls -l lib/
-rwxr-xr-x@ 1  staff   50296 Mar  1  2017 libcsfml-audio.2.4.0.dylib*
lrwxr-xr-x@ 1  staff      26 Mar  1  2017 libcsfml-audio.2.4.dylib@ -> libcsfml-audio.2.4.0.dylib
lrwxr-xr-x@ 1  staff      24 Mar  1  2017 libcsfml-audio.dylib@ -> libcsfml-audio.2.4.dylib
-rwxr-xr-x@ 1  staff  163680 Mar  1  2017 libcsfml-graphics.2.4.0.dylib*
lrwxr-xr-x@ 1  staff      29 Mar  1  2017 libcsfml-graphics.2.4.dylib@ -> libcsfml-graphics.2.4.0.dylib
lrwxr-xr-x@ 1  staff      27 Mar  1  2017 libcsfml-graphics.dylib@ -> libcsfml-graphics.2.4.dylib
-rwxr-xr-x@ 1  staff   67272 Mar  1  2017 libcsfml-network.2.4.0.dylib*
lrwxr-xr-x@ 1  staff      28 Mar  1  2017 libcsfml-network.2.4.dylib@ -> libcsfml-network.2.4.0.dylib
lrwxr-xr-x@ 1  staff      26 Mar  1  2017 libcsfml-network.dylib@ -> libcsfml-network.2.4.dylib

2 个答案:

答案 0 :(得分:4)

似乎用clang -I"./include/" -L"./lib/" -lcsfml-graphics -lcsfml-window -Wl,-rpath,"@executable_path/lib" test.c修复了rpath之后,您仍然缺少SFML库。我刚刚检查了CSFML是否依赖于SFML,并且基于您的ls -l lib/清单,lib /中显然没有SFML。我的猜测是,修复rpath之后,您可能没有注意到缺少的依赖项已更改为@rpath/libsfml-graphics.2.4.dylib而不是@rpath/libcsfml-graphics.2.4.dylib。请下载SFML库并将其放入lib/。

现在,您的问题是如何从命令行在macOS上构建。您的构建步骤是正确的,因此我认为最困难的部分是dyld如何搜索依赖项,而不是它们如何与ld链接。

一个简短的理论。

二进制文件(可执行文件或动态库)有4种引用其依赖项的方式:

  • 通过绝对或相对路径(后者相对于您的工作目录);
  • 通过@executable_path扩展到可执行文件的路径,该路径是依赖关系树的根(如果您具有递归依赖项);
  • 通过@loader_path扩展到可执行文件或要搜索依赖项的库的路径,即它是依赖关系树中直接父级的路径;
  • 通过@rpath替换,它依次替换为二进制文件中找到的每个rpath,这是最灵活的方法,因为您可以通过使用多个rpath在多个目录中进行搜索。

现在的问题是如何确保正确引用依赖项。正如@mattmilten指出的那样,有两种方法:

  • 使您的构建系统/构建脚本/手动构建命令确保引用正确;
  • 使用install_name_tool纠正损坏的引用。

在构建过程中使其自动运行。

为了使第一种方法起作用,您需要确保依赖性库的标识名称是正确的。假设您要针对某个库libA.dylib链接二进制文件。 libA.dylib的标识名称是默认引用,在构建二进制文件时,链接器(ld)将使用该默认引用。您可以通过查看otool -L libA.dylib的第一行(或者在otool -l libA.dylib的LC_ID_DYLIB部分中找到它)。如果是libcsfml-graphics.2.4.dylib,它是@rpath/libcsfml-graphics.2.4.dylib,并且在链接时被传递给a.out,这就是为什么当dyld无法满足它时,您会在错误消息中看到它的原因。

设置正确的标识名是libA.dylib作者的责任。根据他们对libA.dylib放置位置的期望(使用@rpath是一个不错的选择)以及其版本控制方案(如果CSFML版本2.4.0可以替换为2.4.x而不会丢失二进制文件)进行设置兼容性)。如果您要自己构建libA.dylib,则可以使用-install_name参数(例如clang -Wl,-dylib -Wl,-install_name,@executable_path/libA.dylib -o libA.dylib liba.c)进行设置。如果它是第三方库,则可以使用install_name_tool -id @rpath/libA.dylib libA.dylib进行更改。

据我所知,标识名仅在链接期间使用,而在加载二进制文件时不使用,因此,如果您更喜欢使用install_name_tool修复不正确引用的第二种方法,则可以忽略它。

手动修复。

使用install_name_tool修复引用很简单,但很乏味。当有很多不正确的引用时,您可能需要一个脚本。您所需要做的只是以下命令(显然binary占位符应替换为实际的二进制名称):

  • install_name_tool -change @executable_path/libA.dylib @rpath/libA.dylib binary更改引用@executable_path/libA.dylib-> @rpath/libA.dylib;
  • install_name_tool -add_rpath @executable_path/lib binary@executable_path/lib添加到rpaths;
  • install_name_tool -delete_rpath @executable_path/lib binary从rpaths中删除@executable_path/lib
  • otool -l binary检查现有的rpath(只需查看LC_RPATH部分)。

答案 1 :(得分:1)

您应该检查环境变量DYLD_LIBRARY_PATH。它应包含到libcsfml-graphics.2.4.dylib的路径。另一个解决方案可能是将此路径添加到您的PATH变量中。

您可以使用otool -L检查二进制文件的路径,并使用install_name_tool -change对其进行修改。不过,理想情况下,在链接/编译时,应该使用-R构建二进制文件以包含正确的rpath到其依赖项。