在 macOS 上,.so 和 .dylib 有什么区别?

298

.dylib是macOS上的动态库扩展名,但我从来没有清楚地知道何时不能/不应该使用传统的unix .so共享对象。

我有一些问题:

  • 在概念层面上,.so和.dylib之间的主要区别是什么?
  • 何时可以/应该使用其中之一?
  • 编译技巧和提示(例如,替代gcc -shared -fPIC的方法,因为在osx上无法使用)
4个回答

287
Mac OS X使用的Mach-O目标文件格式用于可执行文件和库,区分共享库和动态加载模块。 使用“otool -hv some_file”查看“some_file”的文件类型。
Mach-O共享库具有文件类型“MH_DYLIB”,并带有扩展名“.dylib”。 可以使用通常的静态链接器标志(例如“-lfoo”)连接到它们。可以通过将“-dynamiclib”标志传递给编译器来创建它们。(默认情况下,“-fPIC”无需指定。)
可加载的模块在Mach-O中称为“bundles”。它们具有文件类型“MH_BUNDLE”。它们可以携带任何扩展名;苹果建议使用扩展名“。bundle”,但大多数移植软件使用扩展名“.so”以提高兼容性。通常,您将使用bundles作为扩展应用程序的插件;在这种情况下,bundle将链接到应用程序二进制文件以访问应用程序的导出API。可以通过向编译器传递“-bundle”标志来创建它们。
dylibs和bundles都可以使用dl API(例如dlopen,dlclose)进行动态加载。不可能像使用共享库一样链接到bundles。但是,可能会将bundle链接到实际的共享库;在加载bundle时,这些库将自动加载。

历史上,两者之间的差异更为重大。在Mac OS X 10.0中,没有办法动态加载库。一组dyld API(例如NSCreateObjectFileImageFromFileNSLinkModule)在10.1中被引入以加载和卸载捆绑包,但它们无法用于dylibs。在10.3中添加了一个能够与捆绑包配合使用的兼容性库;在10.4中,被重写为dyld的本地部分,并添加了对加载dylibs的支持(但不支持卸载)。最后,10.5增加了对使用dlclose关闭dylibs的支持,并弃用了dyld API。

在像Linux这样的ELF系统上,两者使用相同的文件格式;任何共享代码均可用作库并进行动态加载。

最后,请注意,在Mac OS X中,“bundle”还可以指具有标准结构的目录,其中包含可执行代码及其使用的资源。存在一些概念上的重叠(特别是与“可加载捆绑包”(如插件)有关的内容,通常包含以Mach-O捆绑包形式的可执行代码),但不应与上述讨论的Mach-O捆绑包混淆。

其他参考资料:


2
谢谢您提供这么详细的评论 :) 我理解得对吗,如果我从另一个包中加载一个包(即路径为应用程序->包A->包B),那么包B将无法看到包A中的任何符号?如果是这样,是否有任何方法可以解决这个问题?我认为我刚刚遇到了它:https://dev59.com/uFHTa4cB1Zd3GeqPVuzW - Mikhail Edoshin
6
-dynamiclib 是 GCC 的一个标志。它会让编译器传递 -dylib 给 ld。 - Miles
Mac OSX上ld命令的man手册更新URL:http://www.manpages.info/macosx/ld.1.html。 - netpoetica

29
在Mac OS X上,.dylib和.so之间的区别在于它们的编译方式。对于.so文件,您需要使用-shared选项,而对于.dylib文件,您需要使用-dynamiclib选项。.so和.dylib都可以作为动态库文件互换,并且都具有DYLIB或BUNDLE类型。以下是不同文件的读取结果。
libtriangle.dylib:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00       DYLIB    17       1368   NOUNDEFS DYLDLINK TWOLEVEL NO_REEXPORTED_DYLIBS



libtriangle.so:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00       DYLIB    17       1256   NOUNDEFS DYLDLINK TWOLEVEL NO_REEXPORTED_DYLIBS

triangle.so:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
MH_MAGIC_64  X86_64        ALL  0x00      BUNDLE    16       1696   NOUNDEFS DYLDLINK TWOLEVEL

在Mac OS X上,两者等效的原因是为了向后兼容编译成.so文件类型的其他UNIX操作系统程序。
编译注意事项:无论您编译.so文件还是.dylib文件,在链接步骤中都需要将正确的路径插入到动态库中。您可以通过添加-install_name和文件路径到链接命令来实现此目的。如果您不这样做,您将遇到本帖子中所见到的问题:Mac Dynamic Library Craziness (May be Fortran Only)

我该如何使 ./configure 生成 .dylib 文件而不是 .so 束文件? ./configure --enable-shared 无法完成此任务。 - Admia
根据我的经验,大多数Mac上的配置文件都会构建一个.so文件或静态库文件,因为这些配置文件使用标准的Unix/Linux文件名。 - Zachary Kraus
这是否意味着一个.so文件,无论是在Mac还是Linux上构建的,都可以在Mac和Linux上使用? - KcFnMi

23

在使用cmake在OSX上编写天真的代码时,我刚刚观察到了一个现象:

cmake ... -DBUILD_SHARED_LIBS=OFF ...

创建.so文件

cmake ... -DBUILD_SHARED_LIBS=ON ...

创建.dynlib文件。

也许这能帮助任何人。


或者在使用 ./configure 时加上 --with-shared。一个常用的库,比如 libncurses,在 Mac OS X 上默认情况下不会构建 .dynlib 文件,需要这个选项。 - hexalys

21

文件.so不是UNIX的共享库文件扩展名。

它只是一个常见的扩展名。

请查看ArnaudRecipes sharedlib页面第3b行。

基本上,.dylib是Mac用来表示共享库的文件扩展名。


9
@ninefingers。正确。但是除非非常明确,否则一些工具将使用默认值。例如,当使用-l<lib>标志(实际标志可能因编译器而异)时,编译器将使用特定于平台的共享库扩展名。 - Martin York

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