拥有整个标准库或任何其他库已经编译并准备链接以形成可执行文件是一个很好的特性,它使编译更快,但据我所知,整个库是链接的,即使只使用其中的几个函数。
因此,例如在我的机器上,下面的代码在编译为目标代码时是1.6 kB,但是当我将其链接到标准库时,它几乎变成了17 kB。
#include <stdio.h>
int main(void)
{
printf("Hello worldn");
}
是否有其他的方法来重新编译标准库(或任何其他库)的部分,这是为了使程序更节省空间所必需的?
对不起,如果这个问题已经问过了,我谷歌了一下,但没有找到任何答案。
如果我们转储生成的ELF可执行文件的内容,我们将看到没有C运行时库的部分因为GLIBC库是动态地链接在中。(例如来自libc.so.6
).
$ gcc -Os -s a.c
$ ls -la a.out
-rwxrwxrwx 1 user user 14408 Feb 18 12:56 a.out
$ ldd a.out
linux-vdso.so.1 (0x00007ffd157f8000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007feb0a33f000)
/lib64/ld-linux-x86-64.so.2 (0x00007feb0a518000)
$ objdump -T a.out
a.out: file format elf64-x86-64
DYNAMIC SYMBOL TABLE:
0000000000000000 w D *UND* 0000000000000000 _ITM_deregisterTMCloneTable
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 puts
0000000000000000 DF *UND* 0000000000000000 GLIBC_2.2.5 __libc_start_main
0000000000000000 w D *UND* 0000000000000000 __gmon_start__
0000000000000000 w D *UND* 0000000000000000 _ITM_registerTMCloneTable
0000000000000000 w DF *UND* 0000000000000000 GLIBC_2.2.5 __cxa_finalize
我们还注意到GCC将没有占位符的printf
优化为puts
(对于文件大小而言,这无关紧要)。
查看"inside"对于ELF,我们可以转储它的部分:
$ readelf -SW a.out
There are 28 section headers, starting at offset 0x3148:
Section Headers:
[Nr] Name Type Address Off Size ES Flg Lk Inf Al
[ 0] NULL 0000000000000000 000000 000000 00 0 0 0
[ 1] .interp PROGBITS 00000000000002a8 0002a8 00001c 00 A 0 0 1
[ 2] .note.ABI-tag NOTE 00000000000002c4 0002c4 000020 00 A 0 0 4
[ 3] .note.gnu.build-id NOTE 00000000000002e4 0002e4 000024 00 A 0 0 4
[ 4] .gnu.hash GNU_HASH 0000000000000308 000308 000024 00 A 5 0 8
[ 5] .dynsym DYNSYM 0000000000000330 000330 0000a8 18 A 6 1 8
[ 6] .dynstr STRTAB 00000000000003d8 0003d8 000082 00 A 0 0 1
[ 7] .gnu.version VERSYM 000000000000045a 00045a 00000e 02 A 5 0 2
[ 8] .gnu.version_r VERNEED 0000000000000468 000468 000020 00 A 6 1 8
[ 9] .rela.dyn RELA 0000000000000488 000488 0000c0 18 A 5 0 8
[10] .rela.plt RELA 0000000000000548 000548 000018 18 AI 5 23 8
[11] .init PROGBITS 0000000000001000 001000 000017 00 AX 0 0 4
[12] .plt PROGBITS 0000000000001020 001020 000020 10 AX 0 0 16
[13] .plt.got PROGBITS 0000000000001040 001040 000008 08 AX 0 0 8
[14] .text PROGBITS 0000000000001050 001050 000171 00 AX 0 0 16
[15] .fini PROGBITS 00000000000011c4 0011c4 000009 00 AX 0 0 4
[16] .rodata PROGBITS 0000000000002000 002000 000010 00 A 0 0 4
[17] .eh_frame_hdr PROGBITS 0000000000002010 002010 00003c 00 A 0 0 4
[18] .eh_frame PROGBITS 0000000000002050 002050 000100 00 A 0 0 8
[19] .init_array INIT_ARRAY 0000000000003de8 002de8 000008 08 WA 0 0 8
[20] .fini_array FINI_ARRAY 0000000000003df0 002df0 000008 08 WA 0 0 8
[21] .dynamic DYNAMIC 0000000000003df8 002df8 0001e0 10 WA 6 0 8
[22] .got PROGBITS 0000000000003fd8 002fd8 000028 08 WA 0 0 8
[23] .got.plt PROGBITS 0000000000004000 003000 000020 08 WA 0 0 8
[24] .data PROGBITS 0000000000004020 003020 000010 00 WA 0 0 8
[25] .bss NOBITS 0000000000004030 003030 000008 00 WA 0 0 1
[26] .comment PROGBITS 0000000000000000 003030 00001c 01 MS 0 0 1
[27] .shstrtab STRTAB 0000000000000000 00304c 0000f7 00 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
l (large), p (processor specific)
所以~14 kB几乎是目前最小的ELF可执行文件大小,因为PIC, relro, eh_frame和其他由GNU链接器生成的部分。
您可以通过关闭relro(这会降低一点安全性)来减小大小。
$ gcc -Os -s -z norelro -Wl,--gc-sections a.c
$ $ ls -la a.out
-rwxrwxrwx 1 user user 10960 Feb 18 13:06 a.out