【发布时间】:2021-05-28 20:24:12
【问题描述】:
我正在尝试创建一个不会在运行时加载库的静态应用程序。具体来说(为了概念验证),我编写了一个调用gethostbyname 的短程序:
#include <netdb.h>
#include <unistd.h>
#include <cstdlib>
#include <iostream>
#include <string>
using namespace std;
int main() {
string hostname(100, 0);
const int hostname_length = gethostname(
const_cast<char *>(hostname.data()),
hostname.capacity()
);
hostname.resize(hostname.size());
cout << "Hostname: " << hostname << endl;
const struct hostent *host_entry = gethostbyname(hostname.c_str());
cout << "Hostname 2: " << host_entry->h_name << endl;
getchar(); // Keep the program alive so I can look at /proc/#/maps
}
使用标准glibc 和-static 编译时,会显示一条警告消息(如下所示)并加载多个库,例如libresolv、libnss_files、libnss_dns 等。
/usr/bin/ld: /tmp/cc1EWL0m.o: in function `main':
main.cpp:(.text+0xe2): warning: Using 'gethostbyname' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
为了防止这种情况,我通过以下方式从源代码编译glibc 2.33:
../glibc/configure --enable-static-pie --enable-static-nss --prefix=/opt/glibc --disable-nscd
make
sudo make install
然后,按照here 介绍的技术,我编译了我的程序:
g++ -o custom main.cpp -nostdlib -nostartfiles -static /opt/glibc/lib/crt1.o /opt/glibc/lib/crti.o `gcc --print-file-name=crtbegin.o` -L/opt/glibc/lib -Wl,--start-group -lstdc++ -lgcc -lgcc_eh -lc -Wl,--end-group `gcc --print-file-name=crtend.o` /opt/glibc/lib/crtn.o
果然关于在运行时加载库的警告消失了。但是运行cat /proc/$(pgrep custom)/maps,我可以看到库仍在加载:
cat /proc/`pgrep custom`/maps
00400000-00401000 r--p 00000000 08:10 962 /home/dan/projects/l/custom_glibc/example/custom
00401000-00565000 r-xp 00001000 08:10 962 /home/dan/projects/l/custom_glibc/example/custom
00565000-005bd000 r--p 00165000 08:10 962 /home/dan/projects/l/custom_glibc/example/custom
005bd000-005c7000 r--p 001bc000 08:10 962 /home/dan/projects/l/custom_glibc/example/custom
005c7000-005ca000 rw-p 001c6000 08:10 962 /home/dan/projects/l/custom_glibc/example/custom
005ca000-005cf000 rw-p 00000000 00:00 0
019df000-01a01000 rw-p 00000000 00:00 0 [heap]
7f3e9f5cf000-7f3e9f6cf000 rw-p 00000000 00:00 0
7f3e9f6cf000-7f3e9f6d0000 r--p 00000000 08:10 337811 /opt/glibc/lib/ld-2.33.so
7f3e9f6d0000-7f3e9f6f4000 r-xp 00001000 08:10 337811 /opt/glibc/lib/ld-2.33.so
7f3e9f6f4000-7f3e9f6fd000 r--p 00025000 08:10 337811 /opt/glibc/lib/ld-2.33.so
7f3e9f6fd000-7f3e9f6fe000 ---p 0002e000 08:10 337811 /opt/glibc/lib/ld-2.33.so
7f3e9f6fe000-7f3e9f700000 r--p 0002e000 08:10 337811 /opt/glibc/lib/ld-2.33.so
7f3e9f700000-7f3e9f702000 rw-p 00030000 08:10 337811 /opt/glibc/lib/ld-2.33.so
7f3e9f702000-7f3e9f728000 r--p 00000000 08:10 330240 /opt/glibc/lib/libc-2.33.so
7f3e9f728000-7f3e9f869000 r-xp 00026000 08:10 330240 /opt/glibc/lib/libc-2.33.so
7f3e9f869000-7f3e9f8b5000 r--p 00167000 08:10 330240 /opt/glibc/lib/libc-2.33.so
7f3e9f8b5000-7f3e9f8b6000 ---p 001b3000 08:10 330240 /opt/glibc/lib/libc-2.33.so
7f3e9f8b6000-7f3e9f8b9000 r--p 001b3000 08:10 330240 /opt/glibc/lib/libc-2.33.so
7f3e9f8b9000-7f3e9f8bc000 rw-p 001b6000 08:10 330240 /opt/glibc/lib/libc-2.33.so
7f3e9f8bc000-7f3e9f8c0000 rw-p 00000000 00:00 0
7f3e9f8c0000-7f3e9f8c3000 r--p 00000000 08:10 337766 /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8c3000-7f3e9f8c9000 r-xp 00003000 08:10 337766 /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8c9000-7f3e9f8cb000 r--p 00009000 08:10 337766 /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8cb000-7f3e9f8cc000 r--p 0000a000 08:10 337766 /opt/glibc/lib/libnss_files-2.33.so
7f3e9f8cc000-7f3e9f8cd000 rw-p 0000b000 08:10 337766 /opt/glibc/lib/libnss_files-2.33.so
7ffcb4dd2000-7ffcb4df4000 rw-p 00000000 00:00 0 [stack]
7ffcb4df9000-7ffcb4dfc000 r--p 00000000 00:00 0 [vvar]
7ffcb4dfc000-7ffcb4dfe000 r-xp 00000000 00:00 0 [vdso]
当尝试添加-lnss_files 以对其进行静态链接时,我发现我的自定义glibc 安装中没有libnss_files.a。我能够在我构建glibc 的位置找到libnss_files_pic.a,但即使将其复制到glibc 安装并链接它也无济于事。
我想知道是否有一种方法可以防止在运行时加载库,除非转移到另一个 libc 实现...
【问题讨论】:
-
gethostbyname使用 nsswitch.conf(5) 在运行时需要不同的行为。您可以修补 musl.libc.org 以满足您的需求 -
一般来说,您不应该需要构建自己的 glibc 来构建(全)静态可执行文件,但也有例外。特别是,任何为模块或扩展提供插件式模型的东西几乎肯定会使用动态链接来做到这一点,这将需要一些共享库组件。 Glibc 的名称解析器就是这样做的一个组件。
-
@BasileStarynkevitch 所以如果我理解正确,即使
musl也不能以一种在运行时不打补丁就不会加载库的方式构建?如果是这样,为什么要修补musl而不是glibc? -
我不太了解
musl libc。请下载并研究其源代码。你也可以试试dietlibc。但是你真的应该解释(在几段书面英语中)为什么你试图创建一个不会在运行时加载库的静态应用程序。 -
在这种情况下,必须避免使用@DanArad、
gethostbyname()和其他解析器函数,或者您需要替代 C 库。如果您选择后者,那么您需要研究如何配置替代库的名称解析器,因为这可能与 glibc 的解析器的配置互操作,也可能不互操作。