使用 clang 从命令行链接 .dylib 库
Linking with .dylib library from the command line using clang
我正在尝试使用库编写 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
您应该检查环境变量 DYLD_LIBRARY_PATH
。它应该包含 libcsfml-graphics.2.4.dylib
的路径。另一种解决方案可能是将此路径添加到您的 PATH
变量。
您可以使用 otool -L
检查二进制文件的路径并使用 install_name_tool -change
修改它们。不过,理想情况下,您应该使用 -R
when linking/compiling.
构建二进制文件以包含其依赖项的正确 rpath
似乎在使用 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 种:
- 通过绝对路径或相对路径(后者相对于您的工作目录);
- by @executable_path,扩展到作为依赖树根的可执行文件的路径(如果你有递归依赖);
- by @loader_path,扩展到搜索依赖的可执行文件或库的路径,即它是依赖树中直接父级的路径;
- @rpath,它依次被二进制文件中的每个 rpath 替换,这是最灵活的方式,因为您可以通过多个 rpath 在多个目录中进行搜索。
现在的问题是如何确保正确引用依赖项。正如@mattmilten 指出的那样,有两种方法:
- 进行构建 system/build script/manual 构建命令确保引用正确;
- 使用 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
)。如果它是第 3 方库,您可以使用 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
检查现有的 rpaths(只需查看 LC_RPATH 部分)。
我正在尝试使用库编写 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
您应该检查环境变量 DYLD_LIBRARY_PATH
。它应该包含 libcsfml-graphics.2.4.dylib
的路径。另一种解决方案可能是将此路径添加到您的 PATH
变量。
您可以使用 otool -L
检查二进制文件的路径并使用 install_name_tool -change
修改它们。不过,理想情况下,您应该使用 -R
when linking/compiling.
似乎在使用 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 种:
- 通过绝对路径或相对路径(后者相对于您的工作目录);
- by @executable_path,扩展到作为依赖树根的可执行文件的路径(如果你有递归依赖);
- by @loader_path,扩展到搜索依赖的可执行文件或库的路径,即它是依赖树中直接父级的路径;
- @rpath,它依次被二进制文件中的每个 rpath 替换,这是最灵活的方式,因为您可以通过多个 rpath 在多个目录中进行搜索。
现在的问题是如何确保正确引用依赖项。正如@mattmilten 指出的那样,有两种方法:
- 进行构建 system/build script/manual 构建命令确保引用正确;
- 使用 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
)。如果它是第 3 方库,您可以使用 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
检查现有的 rpaths(只需查看 LC_RPATH 部分)。