使用 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
为什么?
- 我使用二进制 libsqlite3.a 替换 libsqlite(显然)
- 尝试编译后,我在 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
针对库 abc
和 efg
创建名为 xyz
和 link 的动态库。如果这两个是动态库,它们将被添加到 xyz
的依赖列表中。因此,加载 xyz
的任何人都必须同时加载 abc
和 efg
.
但是,目标文件尚未 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
有了依赖关系后,只需用它们重新组合构建方法即可。
上下文: 我这样做是为了了解静态链接与动态链接的工作原理,请避免说它是愚蠢的或其他什么。它有兴趣,我用谷歌搜索找不到太多。
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
为什么?
- 我使用二进制 libsqlite3.a 替换 libsqlite(显然)
- 尝试编译后,我在 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
针对库 abc
和 efg
创建名为 xyz
和 link 的动态库。如果这两个是动态库,它们将被添加到 xyz
的依赖列表中。因此,加载 xyz
的任何人都必须同时加载 abc
和 efg
.
但是,目标文件尚未 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
有了依赖关系后,只需用它们重新组合构建方法即可。