除了原始机器指令之外,可执行文件中还有什么?
What is in an executable besides the raw machine instructions?
我正在寻求了解低级计算。我注意到我编译的二进制文件比我认为的要大得多。所以我尝试构建没有任何标准库代码的最小可能的 c 程序,如下所示:
void _start()
{
while(1) {};
}
gcc -nostdlib -o minimal minimal.c
当我反汇编二进制文件时,它准确地显示了我所期望的,即三行汇编中的确切代码。
$ 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>
但我的实际可执行文件的大小仍然是 13856 字节。是什么,让它变得如此之大?该文件中还有什么? OS 是否需要超过这 6 字节的机器码?
编辑#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 和 pos其他人,名称取决于工具链)加上它们包含调试信息。请注意您的反汇编是如何显示标签 _start 的?这是一个字符串和其他信息,以便能够将该字符串连接到地址以进行调试。 objdump 的输出也显示你正在使用一个 elf 文件,你可以很容易地查看文件格式,并且可以自己编写程序来解析文件,或者尝试使用 readelf 和其他工具来查看其中的内容(高水平不是原始的)。
在通常(不总是,但想想 pc)程序被加载到 ram 中然后 运行 的操作系统上,因此您希望首先和 foremost 一个文件操作系统支持的格式,它们没有理由支持多种格式,但它们可能会支持。它 os/system 依赖于设计,但 os 可能设计为不仅加载代码,而且 load/initialize 数据(.data、.bss)。启动时说一个 mcu 你需要将数据嵌入到二进制 blob 中,应用程序本身将数据从闪存复制到 ram,但是在 os 中不一定需要,但为了做到这一点你需要一种可以区分部分、目标位置和大小的文件格式。这意味着文件中有额外的字节来定义它和文件格式。
一个二进制文件包含 bootstrap 代码,然后才能进入 C 生成的代码,这取决于系统,取决于 C 库(multiple/many C 库可以在计算机上使用并且 bootstrap 通常特定于 C 库,而不是目标,也不是操作系统,不是编译器的东西),所以文件的一部分是 bootstrap 代码,当你的主程序非常小的时候很多文件大小都是开销。
例如,您可以使用 strip 删除一些符号和其他 non-essential 项目来缩小文件,这样文件大小应该会变小,但 objdump 反汇编将没有标签,对于在 x86 的情况下,一个充其量难以反汇编的可变长度指令集变得更加困难,因此带或不带标签的输出可能无法反映实际指令,但如果没有标签,gnu 反汇编器不会在标签和会使输出更差。
现代编译器和 linker 并没有真正针对在 full-scale 平台上生成 ultra-small 代码进行优化。不是因为工作难,而是因为通常没有必要。编译器或 linker 不一定会添加额外的代码(尽管它可能会),而是它不会努力将您的数据和代码打包成尽可能小的 space。
在您的情况下,我注意到您使用的是动态 linking,尽管实际上没有任何 linked。使用“-static”将减少大约 8kB。 "-s" (strip) 会去掉更多一点。
我不知道是否有可能用 gcc 制作一个真正最小的 ELF executable。在您的情况下,这应该是大约 400 个字节,几乎所有这些都是各种 ELF headers、部分 table 等
我不知道我是否可以 link 我自己的网站(如果不允许,我相信有人会纠正我的错误),但我有一篇关于制作小型 ELF 执行程序的文章table 通过从头开始构建二进制文件:
如果您使用 clang 10.0
和 lld 10.0
并删除不必要的部分,您可以将 64 位静态链接可执行文件的大小减少到 800 字节以下。
$ cat minimal.c
void _start(void)
{
int i = 0;
while (i < 11) {
i++;
}
asm( "int [=10=]x80" :: "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), 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)
$ ll minimal
-rwxrwxr-x 1 fpm fpm 768 Sep 4 17:45 minimal
我正在寻求了解低级计算。我注意到我编译的二进制文件比我认为的要大得多。所以我尝试构建没有任何标准库代码的最小可能的 c 程序,如下所示:
void _start()
{
while(1) {};
}
gcc -nostdlib -o minimal minimal.c
当我反汇编二进制文件时,它准确地显示了我所期望的,即三行汇编中的确切代码。
$ 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>
但我的实际可执行文件的大小仍然是 13856 字节。是什么,让它变得如此之大?该文件中还有什么? OS 是否需要超过这 6 字节的机器码?
编辑#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 和 pos其他人,名称取决于工具链)加上它们包含调试信息。请注意您的反汇编是如何显示标签 _start 的?这是一个字符串和其他信息,以便能够将该字符串连接到地址以进行调试。 objdump 的输出也显示你正在使用一个 elf 文件,你可以很容易地查看文件格式,并且可以自己编写程序来解析文件,或者尝试使用 readelf 和其他工具来查看其中的内容(高水平不是原始的)。
在通常(不总是,但想想 pc)程序被加载到 ram 中然后 运行 的操作系统上,因此您希望首先和 foremost 一个文件操作系统支持的格式,它们没有理由支持多种格式,但它们可能会支持。它 os/system 依赖于设计,但 os 可能设计为不仅加载代码,而且 load/initialize 数据(.data、.bss)。启动时说一个 mcu 你需要将数据嵌入到二进制 blob 中,应用程序本身将数据从闪存复制到 ram,但是在 os 中不一定需要,但为了做到这一点你需要一种可以区分部分、目标位置和大小的文件格式。这意味着文件中有额外的字节来定义它和文件格式。
一个二进制文件包含 bootstrap 代码,然后才能进入 C 生成的代码,这取决于系统,取决于 C 库(multiple/many C 库可以在计算机上使用并且 bootstrap 通常特定于 C 库,而不是目标,也不是操作系统,不是编译器的东西),所以文件的一部分是 bootstrap 代码,当你的主程序非常小的时候很多文件大小都是开销。
例如,您可以使用 strip 删除一些符号和其他 non-essential 项目来缩小文件,这样文件大小应该会变小,但 objdump 反汇编将没有标签,对于在 x86 的情况下,一个充其量难以反汇编的可变长度指令集变得更加困难,因此带或不带标签的输出可能无法反映实际指令,但如果没有标签,gnu 反汇编器不会在标签和会使输出更差。
现代编译器和 linker 并没有真正针对在 full-scale 平台上生成 ultra-small 代码进行优化。不是因为工作难,而是因为通常没有必要。编译器或 linker 不一定会添加额外的代码(尽管它可能会),而是它不会努力将您的数据和代码打包成尽可能小的 space。
在您的情况下,我注意到您使用的是动态 linking,尽管实际上没有任何 linked。使用“-static”将减少大约 8kB。 "-s" (strip) 会去掉更多一点。
我不知道是否有可能用 gcc 制作一个真正最小的 ELF executable。在您的情况下,这应该是大约 400 个字节,几乎所有这些都是各种 ELF headers、部分 table 等
我不知道我是否可以 link 我自己的网站(如果不允许,我相信有人会纠正我的错误),但我有一篇关于制作小型 ELF 执行程序的文章table 通过从头开始构建二进制文件:
如果您使用 clang 10.0
和 lld 10.0
并删除不必要的部分,您可以将 64 位静态链接可执行文件的大小减少到 800 字节以下。
$ cat minimal.c
void _start(void)
{
int i = 0;
while (i < 11) {
i++;
}
asm( "int [=10=]x80" :: "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), 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)
$ ll minimal
-rwxrwxr-x 1 fpm fpm 768 Sep 4 17:45 minimal