与 gcc 链接将文件大小增加到 16 KB

Linking with gcc increases file size to 16 KB

我现在在 linux。我正在编译一个超级简单的 C 程序:

#include <stdio.h>
int main()
{
    printf("Hello, world!\n");
    return 0;
}

并编译 gcc main.c -o main

经过运行ll得到文件大小,就是这样returns:

-rwxr-xr-x 1 xylight xylight  16K Nov  5 11:30 main
-rw-r--r-- 1 xylight xylight   68 Nov  5 11:23 main.c
-rw-r--r-- 1 xylight xylight 1.5K Nov  5 11:30 main.o

链接后main.o,文件大小变成16KB!我怎样才能把它变小?任何链接器选项?

我不确定这是否重复,我在这里找不到任何内容。让我知道它是否重复。

运行readelf -h main后说ELF类型是这样的:DYN (Shared object file)

有人知道我可以做些什么来缩小它吗?

我在 SO 上找不到很好的解释,所以让我 post 在这里解释一下。

首先,executable默认包含静态符号table,用于调试,运行时不加载。我们可以用 strip main 摆脱它,这将为我们节省大约 2K(在我的 Ubuntu 20 上减少到 15K)。

现在,我们可以通过 运行 readelf -SW main:

仔细查看开销
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .interp           PROGBITS        0000000000000318 000318 00001c 00   A  0   0  1
  [ 2] .note.gnu.property NOTE            0000000000000338 000338 000020 00   A  0   0  8
  [ 3] .note.gnu.build-id NOTE            0000000000000358 000358 000024 00   A  0   0  4
  [ 4] .note.ABI-tag     NOTE            000000000000037c 00037c 000020 00   A  0   0  4
  [ 5] .gnu.hash         GNU_HASH        00000000000003a0 0003a0 000024 00   A  6   0  8
  [ 6] .dynsym           DYNSYM          00000000000003c8 0003c8 0000a8 18   A  7   1  8
  [ 7] .dynstr           STRTAB          0000000000000470 000470 000082 00   A  0   0  1
  [ 8] .gnu.version      VERSYM          00000000000004f2 0004f2 00000e 02   A  6   0  2
  [ 9] .gnu.version_r    VERNEED         0000000000000500 000500 000020 00   A  7   1  8
  [10] .rela.dyn         RELA            0000000000000520 000520 0000c0 18   A  6   0  8
  [11] .rela.plt         RELA            00000000000005e0 0005e0 000018 18  AI  6  24  8
  [12] .init             PROGBITS        0000000000001000 001000 00001b 00  AX  0   0  4
  [13] .plt              PROGBITS        0000000000001020 001020 000020 10  AX  0   0 16
  [14] .plt.got          PROGBITS        0000000000001040 001040 000010 10  AX  0   0 16
  [15] .plt.sec          PROGBITS        0000000000001050 001050 000010 10  AX  0   0 16
  [16] .text             PROGBITS        0000000000001060 001060 000185 00  AX  0   0 16
  [17] .fini             PROGBITS        00000000000011e8 0011e8 00000d 00  AX  0   0  4
  [18] .rodata           PROGBITS        0000000000002000 002000 000012 00   A  0   0  4
  [19] .eh_frame_hdr     PROGBITS        0000000000002014 002014 000044 00   A  0   0  4
  [20] .eh_frame         PROGBITS        0000000000002058 002058 000108 00   A  0   0  8
  [21] .init_array       INIT_ARRAY      0000000000003db8 002db8 000008 08  WA  0   0  8
  [22] .fini_array       FINI_ARRAY      0000000000003dc0 002dc0 000008 08  WA  0   0  8
  [23] .dynamic          DYNAMIC         0000000000003dc8 002dc8 0001f0 10  WA  7   0  8
  [24] .got              PROGBITS        0000000000003fb8 002fb8 000048 08  WA  0   0  8
  [25] .data             PROGBITS        0000000000004000 003000 000010 00  WA  0   0  8
  [26] .bss              NOBITS          0000000000004010 003010 000008 00  WA  0   0  1
  [27] .comment          PROGBITS        0000000000000000 003010 00002a 01  MS  0   0  1
  [28] .shstrtab         STRTAB          0000000000000000 00303a 00010a 00      0   0  1

如您所见,前 1.5K(最多但不包括 .init)保存 ELF header 和用于加载共享库的簿记数据(所有这些 .gnu.hash , .dynsym, 等等).

接下来是 500 字节的代码(.init.plt 等,但不包括 .rodata)。请注意,代码分配从 4K 页面边界开始,因此我们浪费了 2.5K 用于填充。

然后 3.5K 被浪费在 ~1K 数据部分之前的 4K 页边界处重新对齐代码(.rodata、展开 tables 等)。 .eh_frame.init_array 之间有一个有趣的 3K 浪费,这是由于只读数据和普通数据之间的一些奇怪对齐而发生的(有关更多详细信息,请参见 this question)。

所以总而言之,只有一小部分 ELF 大小(1.5+0.5+1=3K,即 20%)被真正使用,其余的浪费在 ELF 被 mmaped 时正确对齐地址记忆。需要地址对齐,以便动态加载程序可以为内存页分配不同的权限(例如,代码页不能写入但可以执行,数据页权限相反)。