我想在 Docker 容器上构建一个非常简单的命令行客户端应用程序,以向客户提供该应用程序。该应用程序使用 PEAK(制造该适配器的公司)的 CAN 转 USB 适配器。 PEAK 提供了一个库 (libpcanbasic),应用程序使用该库来访问 CAN 总线。
为了构建 libpcanbasic 库,必须安装适配器的驱动程序。我使用分阶段的 docker 容器,首先在其中构建 libpcanbasic.so 库。然后我使用第二阶段来拥有一个带有更新的 GCC (gcc:12.1.0-bullseye) 的容器。
不,我在链接我的应用程序时遇到了麻烦。 libpcanbasic.so 依赖于不属于第二阶段容器的 libc 版本/风格:
# readelf -d /usr/lib/libpcanbasic.so
Dynamic section at offset 0x189b8 contains 25 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libc.musl-aarch64.so.1]
0x000000000000000e (SONAME) Library soname: [libpcanbasic.so]
0x000000007ffffffd (AUXILIARY) Auxiliary library: [visibility=hidden]
0x000000000000000c (INIT) 0x3f38
0x000000000000000d (FINI) 0x121f0
0x0000000000000019 (INIT_ARRAY) 0x28998
0x000000000000001b (INIT_ARRAYSZ) 16 (bytes)
0x000000000000001a (FINI_ARRAY) 0x289a8
0x000000000000001c (FINI_ARRAYSZ) 16 (bytes)
0x000000006ffffef5 (GNU_HASH) 0x190
0x0000000000000005 (STRTAB) 0x1638
0x0000000000000006 (SYMTAB) 0x558
0x000000000000000a (STRSZ) 2719 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000003 (PLTGOT) 0x28b88
0x0000000000000002 (PLTRELSZ) 3096 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x3320
0x0000000000000007 (RELA) 0x20d8
0x0000000000000008 (RELASZ) 4680 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x0000000000000018 (BIND_NOW)
0x000000006ffffffb (FLAGS_1) Flags: NOW
0x000000006ffffff9 (RELACOUNT) 185
0x0000000000000000 (NULL) 0x0
我可以将该 libc 文件 (libc.musl-aarch64.so.1) 复制到/lib
第二阶段容器中,然后链接就可以了:
# readelf -d ./build/client/bootloader_client
Dynamic section at offset 0x14da8 contains 29 entries:
Tag Type Name/Value
0x0000000000000001 (NEEDED) Shared library: [libpcanbasic.so]
0x0000000000000001 (NEEDED) Shared library: [libstdc++.so.6]
0x0000000000000001 (NEEDED) Shared library: [libm.so.6]
0x0000000000000001 (NEEDED) Shared library: [libgcc_s.so.1]
0x0000000000000001 (NEEDED) Shared library: [libc.so.6]
0x000000000000000c (INIT) 0x402d98
0x000000000000000d (FINI) 0x40e670
0x0000000000000019 (INIT_ARRAY) 0x424d78
0x000000000000001b (INIT_ARRAYSZ) 16 (bytes)
0x000000000000001a (FINI_ARRAY) 0x424d88
0x000000000000001c (FINI_ARRAYSZ) 8 (bytes)
0x0000000000000004 (HASH) 0x400278
0x000000006ffffef5 (GNU_HASH) 0x4005d0
0x0000000000000005 (STRTAB) 0x4010d0
0x0000000000000006 (SYMTAB) 0x400620
0x000000000000000a (STRSZ) 4274 (bytes)
0x000000000000000b (SYMENT) 24 (bytes)
0x0000000000000015 (DEBUG) 0x0
0x0000000000000003 (PLTGOT) 0x424fe8
0x0000000000000002 (PLTRELSZ) 2448 (bytes)
0x0000000000000014 (PLTREL) RELA
0x0000000000000017 (JMPREL) 0x402408
0x0000000000000007 (RELA) 0x402318
0x0000000000000008 (RELASZ) 240 (bytes)
0x0000000000000009 (RELAENT) 24 (bytes)
0x000000006ffffffe (VERNEED) 0x402268
0x000000006fffffff (VERNEEDNUM) 3
0x000000006ffffff0 (VERSYM) 0x402182
0x0000000000000000 (NULL) 0x0
但我想,当我尝试在其他地方执行生成的二进制文件时,我会遇到问题。
为什么应用程序和共享对象(libpcanbasic.so)不只依赖于libc.so
?为什么是非常具体的版本?这可以改变吗?而且,如果我想为客户提供二进制文件,最佳实践是什么?
最好的问候,托斯顿
答案1
你的库最终不会“仅仅依赖于libc.so
”,因为它是使用穆斯勒libc(我猜你的第一阶段使用基于 Alpine 的容器映像)。您的应用程序不依赖于libc.so
任何一个,而取决于libc.so.6
哪个GNU C 库(它是在 Debian 上构建的,默认使用 GNU C 库)。
由于您习惯于容器构建,因此向客户提供二进制文件的最佳实践是使用与您想要支持的目标相对应的容器映像来构建它们。由于据我所知,您的库没有任何特定的依赖项,因此您可以通过使用旧的基于 GNU C 库的发行版构建它来简化您的维护负担(构建一个可在具有相同库版本的任何发行版上使用的库)稍后),如果您想支持基于 musl 的目标,则可以使用基于 musl 的发行版。
如果您继续进行多阶段构建,您至少应该确保所有阶段都基于相同的映像(或兼容的映像)。