将整个标准库链接到每个 C/C++ 文件 space 是否有效?

Is linking the entire standard library to every C/C++ file space efficient?

让整个标准库或任何其他库已经编译并准备好 linked 以形成可执行文件是一个很好的功能,它使编译速度更快,但据我所知,整个即使只使用了其中的几个函数,库也是 linked。

因此,例如在我的机器上,以下代码编译为目标代码时为 1.6 kB,但当我 link 将其编译为标准库时,它几乎变为 17 kB。

#include <stdio.h>

int main(void)
{
    printf("Hello world\n");
}

是否有任何其他方法可以仅重新编译标准库(或任何其他库)中必要的部分以使程序更 space 高效?

抱歉,如果这个问题已经被问过了,我用谷歌搜索了它,但找不到任何答案。

如果我们转储生成的 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(文件大小无关紧要)。

要查看 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)

由于 PIC、relro、eh_frame 和 GNU 链接器生成的其他部分,目前 ~14 kB 几乎是最小的 ELF 可执行文件大小。

您可以通过关闭 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