除了原始机器指令之外,可执行文件中还有什么?

问题描述

@H_404_0@我正在寻求了解低级计算。我注意到编译后的二进制文件比我认为的要大得多。因此,我尝试构建没有任何stdlib代码的最小的c程序,如下所示:

void _start()
{
    while(1) {};
}
@H_404_0@ gcc -nostdlib -o minimal minimal.c

@H_404_0@当我弄清二进制文件时,它确实向我显示了我所期望的,即,这三行汇编中的确切代码

$ objdump -d minimal

minimal:     file format elf64-x86-64


disassembly of section .text:

0000000000001000 <_start>:
    1000:   55                      push   %rbp
    1001:   48 89 e5                mov    %rsp,%rbp
    1004:   eb fe                   jmp    1004 <_start+0x4>
@H_404_0@但是我实际的可执行文件仍然是13856字节。是什么,这使它如此之大?该文件中还有什么?操作系统是否需要超过这6个字节的机器代码

@H_404_0@ 编辑#1: size输出是:

$ size -A minimal
minimal  :
section              size    addr
.interp                28     680
.note.gnu.build-id     36     708
.gnu.hash              28     744
.dynsym                24     776
.dynstr                 1     800
.text                   6    4096
.eh_frame_hdr          20    8192
.eh_frame              52    8216
.dynamic              208   16176
.comment               18       0
Total                 421

解决方法

有许多不同的可执行文件格式。 .com,.exe,.elf,.coff,a.out等。它们理想地包含机器代码和其他部分(.text(代码)、. data,.bss,.rodata以及其他可能的名称,名称取决于工具链) )以及它们包含的调试信息。注意您的反汇编如何显示标签_start?这是一个字符串以及其他信息,以便能够将该字符串连接到地址以进行调试。 objdump的输出还显示您正在使用elf文件,可以轻松查找文件格式,并且可以琐碎地编写自己的程序来解析该文件,或者尝试使用readelf和其他工具查看其中的内容(高水平而不是原始的。

在通常情况下(并非总是如此,但认为是pc),在将程序加载到ram中然后运行的操作系统上,因此您首先要拥有该操作系统支持的文件格式,他们没有理由支持不止一个,但他们可能会支持。它取决于操作系统/系统设计,但是操作系统可以设计为不仅加载代码,而且还加载/初始化数据(.data,.bss)。在启动时说一个mcu,您需要将数据嵌入到二进制blob中,应用程序本身会将数据从闪存中复制到ram中,但是并不一定要在os中进行,但是为了做到这一点,您需要一种文件格式可以区分部分,目标位置和大小。这意味着文件中需要额外的字节来定义它以及文件格式。

二进制文件在可以输入C生成的代码之前会包含引导程序代码,具体取决于系统,具体取决于C库(计算机上可以使用多个C语言库,并且引导程序通常特定于C库不是目标,也不是操作系统,而不是编译器),因此文件的某个百分比是引导程序代码,当您的主程序很小时,很多文件也很麻烦。

例如,您可以使用strip通过消除一些符号和其他不必要的项目来使文件变小,例如文件大小应该变小,但是objdump反汇编将没有标签,对于x86,可变长度的指令集最多很难反汇编,因此变得更加困难,因此带有或不带有标签的输出可能无法反映实际的指令,但是如果没有标签,则gnu反汇编程序不会在标签处重置自身,并且会使输出更糟。

,

现代编译器和链接器并没有真正针对在完整规模的平台上生成超小代码而进行优化。不是因为工作困难,而是因为通常没有必要。不一定需要编译器或链接器添加其他代码(尽管可能会添加),但是它不会尝试将数据和代码打包到尽可能小的空间。

就您而言,我注意到您正在使用动态链接,即使实际上没有任何链接。使用“ -static”将节省大约8kB。 “ -s”(带)将消除更多的作用。

我不知道gcc是否有可能制作真正最小的ELF可执行文件。在您的情况下,应该大约为400个字节,几乎所有字节都是各种ELF标头,节表等。

我不知道是否允许我链接自己的网站(我敢肯定,如果没有的话,我会让别人说对了),但是我有一篇文章介绍了如何通过从头开始以二进制形式构建小型ELF可执行文件:

http://kevinboone.me/elfdemo.html

,

如果使用clang 10.0lld 10.0并去除不必要的部分,则可以将64位静态链接的可执行文件的大小减小到800字节以下。

$ cat minimal.c
void _start(void)
{
    int i = 0;

    while (i < 11) {
       i++;
    }

    asm( "int $0x80" :: "a"(1),"b"(i) );
}

$ clang -static -nostdlib -flto -fuse-ld=lld -o minimal minimal.c
$ ls -l minimal
-rwxrwxr-x 1 fpm fpm 1376 Sep  4 17:38 minimal

$ readelf --string-dump .comment minimal
String dump of section '.comment':
  [     0]  Linker: LLD 10.0.0
  [    13]  clang version 10.0.0 (Fedora 10.0.0-2.fc32)

$ readelf -W --section-headers minimal
There are 9 section headers,starting at offset 0x320:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .note.gnu.build-id NOTE            0000000000200190 000190 000018 00   A  0   0  4
  [ 2] .eh_frame_hdr     PROGBITS        00000000002001a8 0001a8 000014 00   A  0   0  4
  [ 3] .eh_frame         PROGBITS        00000000002001c0 0001c0 00003c 00   A  0   0  8
  [ 4] .text             PROGBITS        0000000000201200 000200 00002a 00  AX  0   0 16
  [ 5] .comment          PROGBITS        0000000000000000 00022a 000040 01  MS  0   0  1
  [ 6] .symtab           SYMTAB          0000000000000000 000270 000048 18      8   2  8
  [ 7] .shstrtab         STRTAB          0000000000000000 0002b8 000055 00      0   0  1
  [ 8] .strtab           STRTAB          0000000000000000 00030d 000012 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)

$ strip -R .eh_frame_hdr -R .eh_frame minimal
$ strip -R .comment -R .note.gnu.build-id minimal
strip: minimal: warning: empty loadable segment detected at vaddr=0x200000,is this intentional?

$ readelf -W --section-headers minimal
There are 3 section headers,starting at offset 0x240:

Section Headers:
  [Nr] Name              Type            Address          Off    Size   ES Flg Lk Inf Al
  [ 0]                   NULL            0000000000000000 000000 000000 00      0   0  0
  [ 1] .text             PROGBITS        0000000000201200 000200 00002a 00  AX  0   0 16
  [ 2] .shstrtab         STRTAB          0000000000000000 00022a 000011 00      0   0  1
Key to Flags:
  W (write),p (processor specific)

$ ll minimal
-rwxrwxr-x 1 fpm fpm 768 Sep  4 17:45 minimal