Nic*_*ult 5 c macos linker shared-libraries clang
我正在尝试将我的库编译为 Mac 上的共享库。你可以在这个 PR上找到我的尝试。
这是我正在使用的命令行和详细结果:
$ gcc -v -dynamiclib -fPIC -o .../luos_engine/.pio/build/native_lib/libluos_engine.dylib .../luos_engine/.pio/build/native_lib/libdf9/libluos_engine.a
Apple clang version 15.0.0 (clang-1500.1.0.2.5)
Target: x86_64-apple-darwin23.2.0
Thread model: posix
InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/ld" -demangle -lto_library /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/libLTO.dylib -dynamic -dylib -arch x86_64 -platform_version macos 14.0.0 14.2 -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -o .../luos_engine/.pio/build/native_lib/libluos_engine.dylib -L/usr/local/lib .../luos_engine/.pio/build/native_lib/libdf9/libluos_engine.a -lSystem /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/clang/15.0.0/lib/darwin/libclang_rt.osx.a
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,编译似乎可以正常工作,但是当我尝试获取库上的符号时,我什么也没有:
$ nm .pio/build/native_lib/libws_luos_engine.dylib
.pio/build/native_lib/libluos_engine.dylib: no symbols
Run Code Online (Sandbox Code Playgroud)
为什么我没有任何符号?
对此有一个简单的解释和一个简单的解决方案。
说明。
您不成功的构建命令:
$ clang -v -dynamiclib -fPIC -o \
.../luos_engine/.pio/build/native_lib/libluos_engine.dylib \
.../luos_engine/.pio/build/native_lib/libdf9/libluos_engine.a
Run Code Online (Sandbox Code Playgroud)
是一个例子:
$ clang ... -dynamiclib ... -o /path/to/libfoo.dylib /path/to/libfoo.a
Run Code Online (Sandbox Code Playgroud)
我替换了clang你的位置gcc,因为你gcc是 的别名clang。输出是动态库,唯一的输入是静态库。您只是想制作静态库的动态版本,但libfoo.dylib.
您在评论中提出的对比成功构建命令:
$ clang -v -dynamiclib -fPIC -arch x86_64 \
-o .pio/build/native_lib/libluos_engine.dylib \
.pio/build/native_lib/libdf9/luos_engine/stats.o
Run Code Online (Sandbox Code Playgroud)
是一个例子:
$ clang ... -dynamiclib ... -o /path/to/libfoo.dylib /path/to/obj.o
Run Code Online (Sandbox Code Playgroud)
即输出是动态库,唯一的输入是目标文件。您只想从目标文件创建动态库,并且该目标文件定义的符号适当地出现在动态库中定义。
您的帖子将不成功的命令描述为“编译”。同样,在您对成功命令的评论中,您将其描述为“编译”。在这两种情况下,都没有输入源文件,也没有进行编译:这些都是纯粹的链接命令1。编译器前端委托给系统静态链接器,如不成功命令的ld详细 ( ) 输出所示:-v
"/Applications/Xcode.app/[...cut...]/usr/bin/ld" -demangle -lto_library \
[...more arguments...]
Run Code Online (Sandbox Code Playgroud)
正如 yourgcc是 的别名一样clang, yourld是 MacOS 系统链接器的别名ld64。
成功和失败之间的区别取决于链接器使用显式输入到链接的目标文件的方式与使用静态库的默认方式之间的差异:
如果obj.o输入显式目标文件,链接器无条件地将其链接到输出文件(程序或动态库),合并对未定义符号的所有新引用以及来自obj.o.
如果libfoo.a输入静态库,它是目标文件的存档obj0.o,...,objN.o。对于每个sym已链接且被引用但未定义的符号,链接器依次检查obj0.o,...objN.o来查找第一个提供 的定义的符号sym。如果定义首先由 提供objK.o,则将objK从存档中复制出来并链接,就像显式命名的目标文件一样。libfoo.a然后,链接器从头开始重新考虑,并继续下去,直到libfoo.a没有产生新的符号定义。然后链接器继续传递libfoo.a并且不再考虑它,除非它稍后在输入序列中重复出现。
有鉴于此,请考虑一下成功案例中会发生什么:
$ clang ... -dynamiclib ... -o /path/to/libfoo.dylib /path/to/obj.o
Run Code Online (Sandbox Code Playgroud)
当链接器到达 时obj.o,即是第一个也是最后一个输入文件。到目前为止,还没有任何内容链接到libfoo.dylib,因此有 0 个未定义的符号引用需要解析。然而,obj.o是无条件链接的,至少带有一个符号,要么已定义,要么已引用但未定义。没有更多的输入文件,因此链接完成,并且 . 中至少有一个符号libfoo.dylib。
接下来考虑不成功的情况下会发生什么:
$ clang ... -dynamiclib ... -o /path/to/libfoo.dylib /path/to/libfoo.a
Run Code Online (Sandbox Code Playgroud)
当链接器到达 时libfoo.a,即是第一个和最后一个输入文件;到目前为止,还没有任何内容被链接到libfoo.dylib,因此有 0 个未定义的符号引用需要解析。由于libfoo.a是静态库,因此只会搜索已链接的未解析引用的定义。没有,所以不会搜索。没有更多的输入文件,因此链接完成,并且 中仍然没有符号libfoo.dylib。
解决方案。
想要将静态库中的所有目标文件链接到链接的二进制文件是一个相当常见的目标,这可能有充分的理由。链接器提供了覆盖其默认行为并实现此目的的选项。
对于 GNU/Linux 链接器,相关链接器选项是--whole-archive。当这个选项出现时,它适用于所有后续的静态库,直到出现反补贴选项--no-whole-archive。
对于 MacOS 链接器,相关链接器选项是-force_load <somelib.a>和-all_load。_force_load仅应用下一个参数,该参数应解析为某个静态库的名称。-all_load适用于链接中任何位置的所有静态库。
GCC 或 clang 前端都无法识别这些链接器选项。-Wl,对于这两种情况,您必须通过在链接器特定选项前加上前缀(考虑使用链接器选项, )来指示要传递给链接器的特定于链接器的选项。因此,如果您的链接器是 GNU/Linux ,无论您是通过或ld调用它,您都将替换:gccclang
/path/to/libfoo.a
Run Code Online (Sandbox Code Playgroud)
和:
-Wl,--whole-archive /path/to/libfoo.a -Wl,--no-whole-archive
# Do not neglect `--no-whole-archive` even the end of your commandline!
# - there are going to be invisible default libraries following!
Run Code Online (Sandbox Code Playgroud)
如果链接器(确实)是 MacOS ld64,那么您可以将其替换为:
-Wl,-force_load /path/to/libfoo.a
Run Code Online (Sandbox Code Playgroud)
问题和解决方案的演示
我没有像你这样的 MacOS Xcode 环境,但我在 Linux 上安装了 MacOSX14.2 SDK,另外:
$ clang --version
Ubuntu clang version 16.0.6 (15)
Run Code Online (Sandbox Code Playgroud)
它支持 x86_64-apple-darwin 目标,以及 LLVM 链接器驱动程序:
$ lld --version
lld is a generic driver.
Invoke ld.lld (Unix), ld64.lld (macOS), lld-link (Windows), wasm-ld (WebAssembly)
Run Code Online (Sandbox Code Playgroud)
支持ld64仿真,以及 Linux 托管的binutils目标 MacOSmach-o二进制文件。这些就可以完成工作。
一个源文件:
$ cat foo.c
int foo(void)
{
return 42;
}
Run Code Online (Sandbox Code Playgroud)
编译为mach.o目标文件:
$ clang -c -fPIC --target=x86_64-apple-darwin foo.c
$ file foo.o
foo.o: Mach-O 64-bit x86_64 object, flags:<|SUBSECTIONS_VIA_SYMBOLS>
Run Code Online (Sandbox Code Playgroud)
制作静态库:
$ ../mach-o-binutils/ar rcs libfoo.a foo.o
Run Code Online (Sandbox Code Playgroud)
只包含foo.o并且其符号表仅包含定义foo
$ ../mach-o-binutils/nm libfoo.a
foo.o:
0000000000000000 T _foo
Run Code Online (Sandbox Code Playgroud)
libfoo.dylib现在从自身做一个绝对最小的foo.o:
$ clang -dynamiclib -o libfoo.dylib --target=x86_64-apple-darwin \
-fuse-ld=lld -nostartfiles -nodefaultlibs --sysroot=../MacOSX14.2 foo.o
Run Code Online (Sandbox Code Playgroud)
这里是:
$ file libfoo.dylib
libfoo.dylib: Mach-O 64-bit x86_64 dynamically linked shared library, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|NO_REEXPORTED_DYLIBS>
Run Code Online (Sandbox Code Playgroud)
这是它的符号:
$ ../mach-o-binutils/nm libfoo.dylib
00000000000002d0 T _foo
Run Code Online (Sandbox Code Playgroud)
foo,只不过foo, 和foo被定义为 ( T)。
libfoo.a接下来用代替做同样的事情foo.o:
$ clang -dynamiclib -o libfoo.dylib --target=x86_64-apple-darwin \
-fuse-ld=lld -nostartfiles -nodefaultlibs --sysroot=../MacOSX14.2 libfoo.a
Run Code Online (Sandbox Code Playgroud)
没有抱怨,但是:
$ ../mach-o-binutils/nm libfoo.dylib
../mach-o-binutils/nm: libfoo.dylib: no symbols
Run Code Online (Sandbox Code Playgroud)
这是这种联系的正确和适当的结果,尽管不是期望的结果。要是我们-force_load libfoo.a:
$ clang -dynamiclib -o libfoo.dylib --target=x86_64-apple-darwin \
-fuse-ld=lld -nostartfiles -nodefaultlibs --sysroot=../MacOSX14.2 -Wl,-force_load libfoo.a
Run Code Online (Sandbox Code Playgroud)
我们得到了想要的结果:
$ ../mach-o-binutils/nm libfoo.dylib
00000000000002d0 T _foo
Run Code Online (Sandbox Code Playgroud)
-fPIC。这是一个编译器选项,指示编译器发出能够在动态库中链接的位置无关的目标代码,并且在链接时必须已经完成或尚未完成。如果传递-fPIC给gccorclang链接命令,它是无害的并被忽略。因此,如果静态库中的目标文件.pio/build/native_lib/libdf9/libluos_engine.a
未编译-fPIC,则将它们中的任何或全部链接到动态库中将-fPIC不会执行任何操作,并且会导致链接重定位错误。他们可能就是这样编译的。在过去十年的大部分时间里,GCC 和 Clang 的大多数打包版本都已-fPIC默认编译,您需要指定-fno-pic禁用它;但指定-fPIC它的重要性仍然是一种谨慎的形式。