使用 sqlite 使用程序进行静态构建

Make a static build with a program using sqlite

上下文: 我这样做是为了了解静态链接与动态链接的工作原理,请避免说它是愚蠢的或其他什么。它有兴趣,我用谷歌搜索找不到太多。

TL;DR: 转到“问题”点。

第一步:程序。我尝试了这个使用 sqlite 的小程序,它很愚蠢,但足以说明问题。

#include <stdio.h>
#include <sqlite3.h>

int main() {
   printf("Hello, World!\n");
   printf("%s\n", sqlite3_libversion()); 

   return 0;
}

建筑: 我有以下 Makefile

build:
    gcc -o test main.c -lsqlite3

静态构建: 这就是重点。我试过这个 Makefile。

build.o:
    gcc -c main.c

build.static: build.o
    gcc main.o /usr/lib/x86_64-linux-gnu/libsqlite3.a /usr/lib/x86_64-linux-gnu/libpthread.a -o static-test

为什么?

  1. 我使用二进制 libsqlite3.a 替换 libsqlite(显然)
  2. 尝试编译后,我在 pthread 上遇到了一些问题,所以我添加了 pthread.a binary

问题: 我仍然遇到问题...但我现在有问题要求 dlopen(等等)。这不是默认包含的吗?此外,它用于...动态包含新库,我该如何结束这种依赖地狱?

$ make build.static                   
gcc -c main.c
gcc main.o /usr/lib/x86_64-linux-gnu/libsqlite3.a /usr/lib/x86_64-linux-gnu/libpthread.a -o static-test
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libsqlite3.a(sqlite3.o) : dans la fonction « fts5Bm25Function » :
(.text+0x2c914) : référence indéfinie vers « log »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libsqlite3.a(sqlite3.o) : dans la fonction « unixDlError » :
(.text+0x345e3) : référence indéfinie vers « dlerror »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libsqlite3.a(sqlite3.o) : dans la fonction « unixDlClose » :
(.text+0x8358) : référence indéfinie vers « dlclose »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libsqlite3.a(sqlite3.o) : dans la fonction « unixDlSym » :
(.text+0x836b) : référence indéfinie vers « dlsym »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libsqlite3.a(sqlite3.o) : dans la fonction « unixDlOpen » :
(.text+0x837d) : référence indéfinie vers « dlopen »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libpthread.a(pthread_create.o) : dans la fonction « allocate_stack » :
/build/glibc-ZN95T4/glibc-2.31/nptl/allocatestack.c:525 : référence indéfinie vers « _dl_stack_flags »
/usr/bin/ld : /build/glibc-ZN95T4/glibc-2.31/nptl/allocatestack.c:647 : référence indéfinie vers « _dl_stack_flags »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libpthread.a(elision-lock.o) : dans la fonction « do_set_elision_enable » :
/build/glibc-ZN95T4/glibc-2.31/nptl/../sysdeps/unix/sysv/linux/x86/elision-conf.c:66 : référence indéfinie vers « _dl_x86_cpu_features »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libpthread.a(nptl-init.o) : dans la fonction « __pthread_initialize_minimal_internal » :
/build/glibc-ZN95T4/glibc-2.31/nptl/nptl-init.c:335 : référence indéfinie vers « _dl_pagesize »
/usr/bin/ld : /build/glibc-ZN95T4/glibc-2.31/nptl/nptl-init.c:344 : référence indéfinie vers « _dl_pagesize »
/usr/bin/ld : /build/glibc-ZN95T4/glibc-2.31/nptl/nptl-init.c:360 : référence indéfinie vers « _dl_init_static_tls »
/usr/bin/ld : /build/glibc-ZN95T4/glibc-2.31/nptl/nptl-init.c:362 : référence indéfinie vers « _dl_wait_lookup_done »
/usr/bin/ld : /usr/lib/x86_64-linux-gnu/libpthread.a(nptl-init.o) : dans la fonction « __pthread_get_minstack » :
/build/glibc-ZN95T4/glibc-2.31/nptl/nptl-init.c:393 : référence indéfinie vers « _dl_pagesize »
collect2: error: ld returned 1 exit status
make: *** [Makefile:8 : build.static] Erreur 1

动态库包含对其他动态库的依赖列表。如果加载了动态库,系统会自动尝试定位并加载它的依赖。

例如如果你 link 反对 libA 并且 libA 依赖于 libB,你的主要可执行文件不必 link 反对 libB 因为 libB 将在加载 libA 时自动加载。

静态库不存在这样的东西。静态库只是目标文件的存档(想想多个 .o 文件包装成一个 .tar 文件)。目标文件没有依赖项列表。当您 link 目标文件到动态库或可执行二进制文件时,您告诉 linker 还需要哪些其他库,并根据该参数创建依赖项列表。

例如

ld -shared -o xyz -l abc -l efg *.o

此调用指示 ld 针对库 abcefg 创建名为 xyz 和 link 的动态库。如果这两个是动态库,它们将被添加到 xyz 的依赖列表中。因此,加载 xyz 的任何人都必须同时加载 abcefg.

但是,目标文件尚未 linked。它们是编译源代码文件的结果,要编译源代码文件,您甚至不必指定任何库。因此目标文件没有依赖关系,目标文件的存档也没有。 link反对他们时,您需要确保满足所有礼节。

由于静态库没有依赖列表,因此没有简单的方法可以找出它们所依赖的其他库。如果在任何地方都没有记录,您只能在尝试 link 时查看缺少哪些符号,然后找到提供这些符号和 link 的库。

如果你的静态库也有一个动态库版本,那么你很幸运,因为这样你就可以检查动态库有哪些依赖项。

ldd someLibrary

objdump -p someLibrary | grep NEEDED

readelf -d someLibrary | grep NEEDED

所有三个命令都列出了直接依赖项,ldd 还列出了使用 --verbose 调用时的间接依赖项(依赖项的依赖项),以防其他命令遍历整个依赖项给自己上链。选择在您的系统上可用或可以安装以防丢失的那个。

对于真正的静态二进制文件,您需要拥有所有直接和间接依赖项的静态版本,并且 link 针对所有这些依赖项。

有趣的事实:
既然您知道静态库只是目标文件的存档,那么文件扩展名 .a 的来源或创建它们的工具为何命名为 ar.

就很明显了

ℹ️ 你应该看看 这对这个话题很有启发性。

第一个解决方案是使用 ldd 查看依赖项。然后你需要将它们添加到编译命令中,就像它们将成为 link (它们不会)。这可能有问题,因为顺序很重要。这是我的案例的解决方案:

gcc -o test main.c libsqlite3.a -lm -ldl -lpthread -static

另一种可能更可靠的解决方案是使用 pkg-config,它将库提供给 link 及其包含(在我的例子中)sqlite 的顺序:

# Gives the name of the package
pkg-config --list-all | grep sqlite
# Give dependencies... In the right order!
pkg-config --static --libs sqlite3

有了依赖关系后,只需用它们重新组合构建方法即可。