这个26KB的可执行文件中存储了什么?

15

使用-O3编译此代码:

#include <iostream>
int main(){std::cout<<"Hello World"<<std::endl;}

编译结果为长度为 25,890 字节的文件。(使用 GCC 4.8.1 编译)

编译器不能只存储两个对 write(STDOUT_FILENO, ???, strlen(???)); 的调用,存储 write 函数的内容,存储字符串,然后将其写入磁盘吗?根据我的估计,这应该会生成一个长度小于 1,024 字节的可执行文件。

在汇编中编译一个hello world程序会生成一个长度为 17 字节的文件:https://stackoverflow.com/questions/284797/hello-world-in-less-than-17-bytes ,这意味着实际代码只有5个字节长。(字符串是 Hello World\0)

除了实际的 main 函数和它调用的函数之外,这个可执行文件还存储了什么?

注意:此问题也适用于 MSVC。


编辑:
很多用户指出 iostream 是罪魁祸首,因此我测试了这个假设,并以相同的参数编译了这个程序:

int main( ) {
}

并获得了23,815字节,假设已被证明是错误的。


8
你知道为什么它那么大吗?#include <iostream> - Cory Kramer
4
即使不使用 -O3 编译,它也不应该链接所有函数,不应该链接它不使用的函数。 - LyingOnTheSky
4
将可执行文件精简到5.6k。 - Karoly Horvath
2
@PaulR 我认为有些人可以轻松回答这个问题。阅读并理解 25,860 字节太多了。 - LyingOnTheSky
2
一些有帮助的问题:https://dev59.com/4Wsz5IYBdhLWcg3wVGLJ - PiotrNycz
显示剩余17条评论
8个回答

15

编译器默认生成一个完整符合PE标准的可执行文件。假设是一个发布版本,你发布的这段简单代码可能包括以下内容:

  • 加载程序需要的所有PE头和表(例如IAT),这也意味着必须满足对齐要求
  • CRT库初始化代码
  • 调试信息(即使是发布版本,你也需要手动删除这些信息)

如果编译器是MSVC,还会有额外的包含:

你发的链接中包含了一个非常小的汇编“hello world”程序,但为了在Windows环境下正常运行,至少需要将完整有效的PE结构提供给加载程序(抛开所有可能导致该代码无法运行的低级问题)。

假设加载程序已经正确地“设置好”了进程将要运行的代码位置,那么只有在这一点上,你才能将其映射到一个PE节中并进行操作。

jmp small_hello_world_entry_point

实际执行代码。

参考文献: PE格式

最后注意事项: UPX 和类似的压缩工具也被用于减小可执行文件的文件大小。


你对于“一个非常小的汇编语言‘hello world’程序”的看法是正确的,它无法运行。(与16位无关) - LyingOnTheSky
你的假设显然是错误的,问题中多次提到GCC是编译器。 - Ben Voigt
没错。我选错了编译器。但是我写的很多考虑仍然适用。我会删除与 MSVC 特定的那些。 - Marco A.
3
@MarcoA。我认为MSVC的部分内容对其他人也有帮助,他们可能也可以适用于GCC。(您可以标记这些内容,并在答案结尾解释它们可能仅适用于MSVC) - LyingOnTheSky
1
@MarcoA。crinkler应该比UPX更好,因为它替换了链接器本身,因此能够实现更小的可执行文件。您可能还想提到有几种裸机编程语言,它们自动产生最小的可执行文件大小(PowerBASIC/FreeBASIC等,Pascal的方言,...),而不像crinkler/MEW SE/UPX那样压缩节。 - turbo

9

C++不是汇编语言,就像C一样,它带有许多基础设施。除了必须与C abi兼容的开销之外,C++还有自己的许多变体,并且还必须具备提供语言的许多保证所需的所有拆卸和清除代码。

这些大部分由库提供,但其中一些必须在可执行文件本身中,以便处理无法加载共享库的情况。

在Linux/BSD下,我们可以使用objdump -dsl反汇编可执行文件。我使用以下代码:

int main() {}

并使用以下方式编译:

g++ -Wall -O3 -g0 test.cpp -o test.exe

生成的可执行文件?
6922 bytes

然后我用更少的冗余编译:

g++ -Wall -O3 -g0 test.cpp -o test.exe -nostdlib
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400150

基本上:main 是我们 C++ 代码的门面入口点,程序实际上从 _start 开始运行。
可执行文件大小?
1454 bytes

以下是objdump对这两个的描述:
g++ -Wall -O3 -g0 test.cpp -o test.exe
objdump -test.exe

test.exe:     file format elf64-x86-64

Contents of section .interp:
 400200 2f6c6962 36342f6c 642d6c69 6e75782d  /lib64/ld-linux-
 400210 7838362d 36342e73 6f2e3200           x86-64.so.2.    
Contents of section .note.ABI-tag:
 40021c 04000000 10000000 01000000 474e5500  ............GNU.
 40022c 00000000 02000000 06000000 12000000  ................
Contents of section .note.gnu.build-id:
 40023c 04000000 14000000 03000000 474e5500  ............GNU.
 40024c a0f55c7d 671f9eb2 93078fd3 0f52581a  ..\}g........RX.
 40025c 544829b2                             TH).            
Contents of section .hash:
 400260 03000000 06000000 02000000 05000000  ................
 400270 00000000 00000000 00000000 01000000  ................
 400280 00000000 03000000 04000000           ............    
Contents of section .dynsym:
 400290 00000000 00000000 00000000 00000000  ................
 4002a0 00000000 00000000 10000000 20000000  ............ ...
 4002b0 00000000 00000000 00000000 00000000  ................
 4002c0 1f000000 20000000 00000000 00000000  .... ...........
 4002d0 00000000 00000000 8b000000 12000000  ................
 4002e0 00000000 00000000 00000000 00000000  ................
 4002f0 33000000 20000000 00000000 00000000  3... ...........
 400300 00000000 00000000 4f000000 20000000  ........O... ...
 400310 00000000 00000000 00000000 00000000  ................
Contents of section .dynstr:
 400320 006c6962 73746463 2b2b2e73 6f2e3600  .libstdc++.so.6.
 400330 5f5f676d 6f6e5f73 74617274 5f5f005f  __gmon_start__._
 400340 4a765f52 65676973 74657243 6c617373  Jv_RegisterClass
 400350 6573005f 49544d5f 64657265 67697374  es._ITM_deregist
 400360 6572544d 436c6f6e 65546162 6c65005f  erTMCloneTable._
 400370 49544d5f 72656769 73746572 544d436c  ITM_registerTMCl
 400380 6f6e6554 61626c65 006c6962 6d2e736f  oneTable.libm.so
 400390 2e36006c 69626763 635f732e 736f2e31  .6.libgcc_s.so.1
 4003a0 006c6962 632e736f 2e36005f 5f6c6962  .libc.so.6.__lib
 4003b0 635f7374 6172745f 6d61696e 00474c49  c_start_main.GLI
 4003c0 42435f32 2e322e35 00                 BC_2.2.5.       
Contents of section .gnu.version:
 4003ca 00000000 00000200 00000000           ............    
Contents of section .gnu.version_r:
 4003d8 01000100 81000000 10000000 00000000  ................
 4003e8 751a6909 00000200 9d000000 00000000  u.i.............
Contents of section .rela.dyn:
 4003f8 50096000 00000000 06000000 01000000  P.`.............
 400408 00000000 00000000                    ........        
Contents of section .rela.plt:
 400410 70096000 00000000 07000000 03000000  p.`.............
 400420 00000000 00000000                    ........        
Contents of section .init:
 400428 4883ec08 e85b0000 00e86a01 0000e845  H....[....j....E
 400438 02000048 83c408c3                    ...H....        
Contents of section .plt:
 400440 ff351a05 2000ff25 1c052000 0f1f4000  .5.. ..%.. ...@.
 400450 ff251a05 20006800 000000e9 e0ffffff  .%.. .h.........
Contents of section .text:
 400460 31ed4989 d15e4889 e24883e4 f0505449  1.I..^H..H...PTI
 400470 c7c0e005 400048c7 c1f00540 0048c7c7  ....@.H....@.H..
 400480 d0054000 e8c7ffff fff49090 4883ec08  ..@.........H...
 400490 488b05b9 04200048 85c07402 ffd04883  H.... .H..t...H.
 4004a0 c408c390 90909090 90909090 90909090  ................
 4004b0 90909090 90909090 90909090 90909090  ................
 4004c0 b88f0960 00482d88 09600048 83f80e76  ...`.H-..`.H...v
 4004d0 17b80000 00004885 c0740dbf 88096000  ......H..t....`.
 4004e0 ffe0660f 1f440000 f3c3660f 1f440000  ..f..D....f..D..
 4004f0 be880960 004881ee 88096000 48c1fe03  ...`.H....`.H...
 400500 4889f048 c1e83f48 01c648d1 fe7411b8  H..H..?H..H..t..
 400510 00000000 4885c074 07bf8809 6000ffe0  ....H..t....`...
 400520 f3c36666 6666662e 0f1f8400 00000000  ..fffff.........
 400530 803d5104 20000075 5f5553bb 80076000  .=Q. ..u_US...`.
 400540 4881eb78 07600048 83ec0848 8b053e04  H..x.`.H...H..>.
 400550 200048c1 fb034883 eb01488d 6c241048   .H...H...H.l$.H
 400560 39d87322 0f1f4000 4883c001 4889051d  9.s"..@.H...H...
 400570 042000ff 14c57807 6000488b 050f0420  . ....x.`.H.... 
 400580 004839d8 72e2e835 ffffffc6 05f60320  .H9.r..5....... 
 400590 00014883 c4085b5d f3c3660f 1f440000  ..H...[]..f..D..
 4005a0 bf880760 0048833f 007505e9 40ffffff  ...`.H.?.u..@...
 4005b0 b8000000 004885c0 74f15548 89e5ffd0  .....H..t.UH....
 4005c0 5de92aff ffff9090 90909090 90909090  ].*.............
 4005d0 31c0c390 90909090 90909090 90909090  1...............
 4005e0 f3c36666 6666662e 0f1f8400 00000000  ..fffff.........
 4005f0 48896c24 d84c8964 24e0488d 2d630120  H.l$.L.d$.H.-c. 
 400600 004c8d25 5c012000 4c896c24 e84c8974  .L.%\. .L.l$.L.t
 400610 24f04c89 7c24f848 895c24d0 4883ec38  $.L.|$.H.\$.H..8
 400620 4c29e541 89fd4989 f648c1fd 034989d7  L).A..I..H...I..
 400630 e8f3fdff ff4885ed 741c31db 0f1f4000  .....H..t.1...@.
 400640 4c89fa4c 89f64489 ef41ff14 dc4883c3  L..L..D..A...H..
 400650 014839eb 72ea488b 5c240848 8b6c2410  .H9.r.H.\$.H.l$.
 400660 4c8b6424 184c8b6c 24204c8b 7424284c  L.d$.L.l$ L.t$(L
 400670 8b7c2430 4883c438 c3909090 90909090  .|$0H..8........
 400680 554889e5 53bb6807 60004883 ec08488b  UH..S.h.`.H...H.
 400690 05d30020 004883f8 ff74140f 1f440000  ... .H...t...D..
 4006a0 4883eb08 ffd0488b 034883f8 ff75f148  H.....H..H...u.H
 4006b0 83c4085b 5dc39090                    ...[]...        
Contents of section .fini:
 4006b8 4883ec08 e86ffeff ff4883c4 08c3      H....o...H....  
Contents of section .rodata:
 4006c8 01000200                             ....            
Contents of section .eh_frame_hdr:
 4006cc 011b033b 20000000 03000000 04ffffff  ...; ...........
 4006dc 3c000000 14ffffff 54000000 24ffffff  <.......T...$...
 4006ec 6c000000                             l...            
Contents of section .eh_frame:
 4006f0 14000000 00000000 017a5200 01781001  .........zR..x..
 400700 1b0c0708 90010000 14000000 1c000000  ................
 400710 c0feffff 03000000 00000000 00000000  ................
 400720 14000000 34000000 b8feffff 02000000  ....4...........
 400730 00000000 00000000 24000000 4c000000  ........$...L...
 400740 b0feffff 89000000 00518c05 86065f0e  .........Q...._.
 400750 4083078f 028e038d 0402580e 08000000  @.........X.....
 400760 00000000                             ....            
Contents of section .ctors:
 600768 ffffffff ffffffff 00000000 00000000  ................
Contents of section .dtors:
 600778 ffffffff ffffffff 00000000 00000000  ................
Contents of section .jcr:
 600788 00000000 00000000                    ........        
Contents of section .dynamic:
 600790 01000000 00000000 01000000 00000000  ................
 6007a0 01000000 00000000 69000000 00000000  ........i.......
 6007b0 01000000 00000000 73000000 00000000  ........s.......
 6007c0 01000000 00000000 81000000 00000000  ................
 6007d0 0c000000 00000000 28044000 00000000  ........(.@.....
 6007e0 0d000000 00000000 b8064000 00000000  ..........@.....
 6007f0 04000000 00000000 60024000 00000000  ........`.@.....
 600800 05000000 00000000 20034000 00000000  ........ .@.....
 600810 06000000 00000000 90024000 00000000  ..........@.....
 600820 0a000000 00000000 a9000000 00000000  ................
 600830 0b000000 00000000 18000000 00000000  ................
 600840 15000000 00000000 00000000 00000000  ................
 600850 03000000 00000000 58096000 00000000  ........X.`.....
 600860 02000000 00000000 18000000 00000000  ................
 600870 14000000 00000000 07000000 00000000  ................
 600880 17000000 00000000 10044000 00000000  ..........@.....
 600890 07000000 00000000 f8034000 00000000  ..........@.....
 6008a0 08000000 00000000 18000000 00000000  ................
 6008b0 09000000 00000000 18000000 00000000  ................
 6008c0 feffff6f 00000000 d8034000 00000000  ...o......@.....
 6008d0 ffffff6f 00000000 01000000 00000000  ...o............
 6008e0 f0ffff6f 00000000 ca034000 00000000  ...o......@.....
 6008f0 00000000 00000000 00000000 00000000  ................
 600900 00000000 00000000 00000000 00000000  ................
 600910 00000000 00000000 00000000 00000000  ................
 600920 00000000 00000000 00000000 00000000  ................
 600930 00000000 00000000 00000000 00000000  ................
 600940 00000000 00000000 00000000 00000000  ................
Contents of section .got:
 600950 00000000 00000000                    ........        
Contents of section .got.plt:
 600958 90076000 00000000 00000000 00000000  ..`.............
 600968 00000000 00000000 56044000 00000000  ........V.@.....
Contents of section .data:
 600978 00000000 00000000 00000000 00000000  ................
Contents of section .comment:
 0000 4743433a 2028474e 55292034 2e342e37  GCC: (GNU) 4.4.7
 0010 20323031 32303331 33202852 65642048   20120313 (Red H
 0020 61742034 2e342e37 2d313129 00474343  at 4.4.7-11).GCC
 0030 3a202847 4e552920 342e392e 782d676f  : (GNU) 4.9.x-go
 0040 6f676c65 20323031 35303132 33202870  ogle 20150123 (p
 0050 72657265 6c656173 652900             rerelease).     

Disassembly of section .init:

0000000000400428 <_init>:
_init():
  400428:   48 83 ec 08             sub    $0x8,%rsp
  40042c:   e8 5b 00 00 00          callq  40048c <call_gmon_start>
  400431:   e8 6a 01 00 00          callq  4005a0 <frame_dummy>
  400436:   e8 45 02 00 00          callq  400680 <__do_global_ctors_aux>
  40043b:   48 83 c4 08             add    $0x8,%rsp
  40043f:   c3                      retq   

Disassembly of section .plt:

0000000000400440 <__libc_start_main@plt-0x10>:
  400440:   ff 35 1a 05 20 00       pushq  0x20051a(%rip)        # 600960 <_GLOBAL_OFFSET_TABLE_+0x8>
  400446:   ff 25 1c 05 20 00       jmpq   *0x20051c(%rip)        # 600968 <_GLOBAL_OFFSET_TABLE_+0x10>
  40044c:   0f 1f 40 00             nopl   0x0(%rax)

0000000000400450 <__libc_start_main@plt>:
  400450:   ff 25 1a 05 20 00       jmpq   *0x20051a(%rip)        # 600970 <_GLOBAL_OFFSET_TABLE_+0x18>
  400456:   68 00 00 00 00          pushq  $0x0
  40045b:   e9 e0 ff ff ff          jmpq   400440 <_init+0x18>

Disassembly of section .text:

0000000000400460 <_start>:
_start():
  400460:   31 ed                   xor    %ebp,%ebp
  400462:   49 89 d1                mov    %rdx,%r9
  400465:   5e                      pop    %rsi
  400466:   48 89 e2                mov    %rsp,%rdx
  400469:   48 83 e4 f0             and    $0xfffffffffffffff0,%rsp
  40046d:   50                      push   %rax
  40046e:   54                      push   %rsp
  40046f:   49 c7 c0 e0 05 40 00    mov    $0x4005e0,%r8
  400476:   48 c7 c1 f0 05 40 00    mov    $0x4005f0,%rcx
  40047d:   48 c7 c7 d0 05 40 00    mov    $0x4005d0,%rdi
  400484:   e8 c7 ff ff ff          callq  400450 <__libc_start_main@plt>
  400489:   f4                      hlt    
  40048a:   90                      nop
  40048b:   90                      nop

000000000040048c <call_gmon_start>:
call_gmon_start():
  40048c:   48 83 ec 08             sub    $0x8,%rsp
  400490:   48 8b 05 b9 04 20 00    mov    0x2004b9(%rip),%rax        # 600950 <_DYNAMIC+0x1c0>
  400497:   48 85 c0                test   %rax,%rax
  40049a:   74 02                   je     40049e <call_gmon_start+0x12>
  40049c:   ff d0                   callq  *%rax
  40049e:   48 83 c4 08             add    $0x8,%rsp
  4004a2:   c3                      retq   
  4004a3:   90                      nop
  4004a4:   90                      nop
  4004a5:   90                      nop
  4004a6:   90                      nop
  4004a7:   90                      nop
  4004a8:   90                      nop
  4004a9:   90                      nop
  4004aa:   90                      nop
  4004ab:   90                      nop
  4004ac:   90                      nop
  4004ad:   90                      nop
  4004ae:   90                      nop
  4004af:   90                      nop
  4004b0:   90                      nop
  4004b1:   90                      nop
  4004b2:   90                      nop
  4004b3:   90                      nop
  4004b4:   90                      nop
  4004b5:   90                      nop
  4004b6:   90                      nop
  4004b7:   90                      nop
  4004b8:   90                      nop
  4004b9:   90                      nop
  4004ba:   90                      nop
  4004bb:   90                      nop
  4004bc:   90                      nop
  4004bd:   90                      nop
  4004be:   90                      nop
  4004bf:   90                      nop

00000000004004c0 <deregister_tm_clones>:
deregister_tm_clones():
  4004c0:   b8 8f 09 60 00          mov    $0x60098f,%eax
  4004c5:   48 2d 88 09 60 00       sub    $0x600988,%rax
  4004cb:   48 83 f8 0e             cmp    $0xe,%rax
  4004cf:   76 17                   jbe    4004e8 <deregister_tm_clones+0x28>
  4004d1:   b8 00 00 00 00          mov    $0x0,%eax
  4004d6:   48 85 c0                test   %rax,%rax
  4004d9:   74 0d                   je     4004e8 <deregister_tm_clones+0x28>
  4004db:   bf 88 09 60 00          mov    $0x600988,%edi
  4004e0:   ff e0                   jmpq   *%rax
  4004e2:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)
  4004e8:   f3 c3                   repz retq 
  4004ea:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

00000000004004f0 <register_tm_clones>:
register_tm_clones():
  4004f0:   be 88 09 60 00          mov    $0x600988,%esi
  4004f5:   48 81 ee 88 09 60 00    sub    $0x600988,%rsi
  4004fc:   48 c1 fe 03             sar    $0x3,%rsi
  400500:   48 89 f0                mov    %rsi,%rax
  400503:   48 c1 e8 3f             shr    $0x3f,%rax
  400507:   48 01 c6                add    %rax,%rsi
  40050a:   48 d1 fe                sar    %rsi
  40050d:   74 11                   je     400520 <register_tm_clones+0x30>
  40050f:   b8 00 00 00 00          mov    $0x0,%eax
  400514:   48 85 c0                test   %rax,%rax
  400517:   74 07                   je     400520 <register_tm_clones+0x30>
  400519:   bf 88 09 60 00          mov    $0x600988,%edi
  40051e:   ff e0                   jmpq   *%rax
  400520:   f3 c3                   repz retq 
  400522:   66 66 66 66 66 2e 0f    data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
  400529:   1f 84 00 00 00 00 00 

0000000000400530 <__do_global_dtors_aux>:
__do_global_dtors_aux():
  400530:   80 3d 51 04 20 00 00    cmpb   $0x0,0x200451(%rip)        # 600988 <__bss_start>
  400537:   75 5f                   jne    400598 <__do_global_dtors_aux+0x68>
  400539:   55                      push   %rbp
  40053a:   53                      push   %rbx
  40053b:   bb 80 07 60 00          mov    $0x600780,%ebx
  400540:   48 81 eb 78 07 60 00    sub    $0x600778,%rbx
  400547:   48 83 ec 08             sub    $0x8,%rsp
  40054b:   48 8b 05 3e 04 20 00    mov    0x20043e(%rip),%rax        # 600990 <dtor_idx.6648>
  400552:   48 c1 fb 03             sar    $0x3,%rbx
  400556:   48 83 eb 01             sub    $0x1,%rbx
  40055a:   48 8d 6c 24 10          lea    0x10(%rsp),%rbp
  40055f:   48 39 d8                cmp    %rbx,%rax
  400562:   73 22                   jae    400586 <__do_global_dtors_aux+0x56>
  400564:   0f 1f 40 00             nopl   0x0(%rax)
  400568:   48 83 c0 01             add    $0x1,%rax
  40056c:   48 89 05 1d 04 20 00    mov    %rax,0x20041d(%rip)        # 600990 <dtor_idx.6648>
  400573:   ff 14 c5 78 07 60 00    callq  *0x600778(,%rax,8)
  40057a:   48 8b 05 0f 04 20 00    mov    0x20040f(%rip),%rax        # 600990 <dtor_idx.6648>
  400581:   48 39 d8                cmp    %rbx,%rax
  400584:   72 e2                   jb     400568 <__do_global_dtors_aux+0x38>
  400586:   e8 35 ff ff ff          callq  4004c0 <deregister_tm_clones>
  40058b:   c6 05 f6 03 20 00 01    movb   $0x1,0x2003f6(%rip)        # 600988 <__bss_start>
  400592:   48 83 c4 08             add    $0x8,%rsp
  400596:   5b                      pop    %rbx
  400597:   5d                      pop    %rbp
  400598:   f3 c3                   repz retq 
  40059a:   66 0f 1f 44 00 00       nopw   0x0(%rax,%rax,1)

00000000004005a0 <frame_dummy>:
frame_dummy():
  4005a0:   bf 88 07 60 00          mov    $0x600788,%edi
  4005a5:   48 83 3f 00             cmpq   $0x0,(%rdi)
  4005a9:   75 05                   jne    4005b0 <frame_dummy+0x10>
  4005ab:   e9 40 ff ff ff          jmpq   4004f0 <register_tm_clones>
  4005b0:   b8 00 00 00 00          mov    $0x0,%eax
  4005b5:   48 85 c0                test   %rax,%rax
  4005b8:   74 f1                   je     4005ab <frame_dummy+0xb>
  4005ba:   55                      push   %rbp
  4005bb:   48 89 e5                mov    %rsp,%rbp
  4005be:   ff d0                   callq  *%rax
  4005c0:   5d                      pop    %rbp
  4005c1:   e9 2a ff ff ff          jmpq   4004f0 <register_tm_clones>
  4005c6:   90                      nop
  4005c7:   90                      nop
  4005c8:   90                      nop
  4005c9:   90                      nop
  4005ca:   90                      nop
  4005cb:   90                      nop
  4005cc:   90                      nop
  4005cd:   90                      nop
  4005ce:   90                      nop
  4005cf:   90                      nop

00000000004005d0 <main>:
main():
  4005d0:   31 c0                   xor    %eax,%eax
  4005d2:   c3                      retq   
  4005d3:   90                      nop
  4005d4:   90                      nop
  4005d5:   90                      nop
  4005d6:   90                      nop
  4005d7:   90                      nop
  4005d8:   90                      nop
  4005d9:   90                      nop
  4005da:   90                      nop
  4005db:   90                      nop
  4005dc:   90                      nop
  4005dd:   90                      nop
  4005de:   90                      nop
  4005df:   90                      nop

00000000004005e0 <__libc_csu_fini>:
__libc_csu_fini():
  4005e0:   f3 c3                   repz retq 
  4005e2:   66 66 66 66 66 2e 0f    data32 data32 data32 data32 nopw %cs:0x0(%rax,%rax,1)
  4005e9:   1f 84 00 00 00 00 00 

00000000004005f0 <__libc_csu_init>:
__libc_csu_init():
  4005f0:   48 89 6c 24 d8          mov    %rbp,-0x28(%rsp)
  4005f5:   4c 89 64 24 e0          mov    %r12,-0x20(%rsp)
  4005fa:   48 8d 2d 63 01 20 00    lea    0x200163(%rip),%rbp        # 600764 <__init_array_end>
  400601:   4c 8d 25 5c 01 20 00    lea    0x20015c(%rip),%r12        # 600764 <__init_array_end>
  400608:   4c 89 6c 24 e8          mov    %r13,-0x18(%rsp)
  40060d:   4c 89 74 24 f0          mov    %r14,-0x10(%rsp)
  400612:   4c 89 7c 24 f8          mov    %r15,-0x8(%rsp)
  400617:   48 89 5c 24 d0          mov    %rbx,-0x30(%rsp)
  40061c:   48 83 ec 38             sub    $0x38,%rsp
  400620:   4c 29 e5                sub    %r12,%rbp
  400623:   41 89 fd                mov    %edi,%r13d
  400626:   49 89 f6                mov    %rsi,%r14
  400629:   48 c1 fd 03             sar    $0x3,%rbp
  40062d:   49 89 d7                mov    %rdx,%r15
  400630:   e8 f3 fd ff ff          callq  400428 <_init>
  400635:   48 85 ed                test   %rbp,%rbp
  400638:   74 1c                   je     400656 <__libc_csu_init+0x66>
  40063a:   31 db                   xor    %ebx,%ebx
  40063c:   0f 1f 40 00             nopl   0x0(%rax)
  400640:   4c 89 fa                mov    %r15,%rdx
  400643:   4c 89 f6                mov    %r14,%rsi
  400646:   44 89 ef                mov    %r13d,%edi
  400649:   41 ff 14 dc             callq  *(%r12,%rbx,8)
  40064d:   48 83 c3 01             add    $0x1,%rbx
  400651:   48 39 eb                cmp    %rbp,%rbx
  400654:   72 ea                   jb     400640 <__libc_csu_init+0x50>
  400656:   48 8b 5c 24 08          mov    0x8(%rsp),%rbx
  40065b:   48 8b 6c 24 10          mov    0x10(%rsp),%rbp
  400660:   4c 8b 64 24 18          mov    0x18(%rsp),%r12
  400665:   4c 8b 6c 24 20          mov    0x20(%rsp),%r13
  40066a:   4c 8b 74 24 28          mov    0x28(%rsp),%r14
  40066f:   4c 8b 7c 24 30          mov    0x30(%rsp),%r15
  400674:   48 83 c4 38             add    $0x38,%rsp
  400678:   c3                      retq   
  400679:   90                      nop
  40067a:   90                      nop
  40067b:   90                      nop
  40067c:   90                      nop
  40067d:   90                      nop
  40067e:   90                      nop
  40067f:   90                      nop

0000000000400680 <__do_global_ctors_aux>:
__do_global_ctors_aux():
  400680:   55                      push   %rbp
  400681:   48 89 e5                mov    %rsp,%rbp
  400684:   53                      push   %rbx
  400685:   bb 68 07 60 00          mov    $0x600768,%ebx
  40068a:   48 83 ec 08             sub    $0x8,%rsp
  40068e:   48 8b 05 d3 00 20 00    mov    0x2000d3(%rip),%rax        # 600768 <__CTOR_LIST__>
  400695:   48 83 f8 ff             cmp    $0xffffffffffffffff,%rax
  400699:   74 14                   je     4006af <__do_global_ctors_aux+0x2f>
  40069b:   0f 1f 44 00 00          nopl   0x0(%rax,%rax,1)
  4006a0:   48 83 eb 08             sub    $0x8,%rbx
  4006a4:   ff d0                   callq  *%rax
  4006a6:   48 8b 03                mov    (%rbx),%rax
  4006a9:   48 83 f8 ff             cmp    $0xffffffffffffffff,%rax
  4006ad:   75 f1                   jne    4006a0 <__do_global_ctors_aux+0x20>
  4006af:   48 83 c4 08             add    $0x8,%rsp
  4006b3:   5b                      pop    %rbx
  4006b4:   5d                      pop    %rbp
  4006b5:   c3                      retq   
  4006b6:   90                      nop
  4006b7:   90                      nop

Disassembly of section .fini:

00000000004006b8 <_fini>:
_fini():
  4006b8:   48 83 ec 08             sub    $0x8,%rsp
  4006bc:   e8 6f fe ff ff          callq  400530 <__do_global_dtors_aux>
  4006c1:   48 83 c4 08             add    $0x8,%rsp
  4006c5:   c3                      retq   

以及更小的文件:

g++ -Wall -O3 -g0 test.cpp -o test.exe -nostdlib
/usr/bin/ld: warning: cannot find entry symbol _start; defaulting to 0000000000400150

test.exe:     file format elf64-x86-64

Contents of section .note.gnu.build-id:
 400120 04000000 14000000 03000000 474e5500  ............GNU.
 400130 d4b1e35c 21d1f541 b81d3ac9 d62bac7a  ...\!..A..:..+.z
 400140 606b1ad4                             `k..            
Contents of section .text:
 400150 31c0c3                               1..             
Contents of section .eh_frame_hdr:
 400154 011b033b 10000000 01000000 fcffffff  ...;............
 400164 2c000000                             ,...            
Contents of section .eh_frame:
 400168 14000000 00000000 017a5200 01781001  .........zR..x..
 400178 1b0c0708 90010000 14000000 1c000000  ................
 400188 c8ffffff 03000000 00000000 00000000  ................
Contents of section .comment:
 0000 4743433a 2028474e 55292034 2e392e78  GCC: (GNU) 4.9.x
 0010 2d676f6f 676c6520 32303135 30313233  -google 20150123
 0020 20287072 6572656c 65617365 2900       (prerelease).  

Disassembly of section .text:

0000000000400150 <main>:
main():
  400150:       31 c0                   xor    %eax,%eax
  400152:       c3                      retq   

值得注意的是,这个可执行文件不起作用,它会导致段错误:为了使它工作,我们实际上需要实现_start而不是main
我们可以看到,在较大的可执行文件中,大部分是胶水代码,它处理加载动态库和准备标准库所需的更广泛环境的问题。
--- 编辑 ---
即使我们的较小的代码仍然需要包括异常处理、全局构造/析构函数支持等。它可能可以省略这些内容,如果你深入挖掘,可能会找到省去它们的方法,但一般来说,你可能不需要这样做,总是包括这些基本支持可能比让大多数新程序员绊倒在“如何强制编译器发出基本语言支持”的问题上更容易,而不是让少数新的嵌入式程序员问“我如何防止编译器发出基本语言支持?”。
还要注意编译器生成ELF格式的二进制文件,这是一个小的贡献(也许约60字节),再加上发射自己的身份证明增加了一些大小。但较小的二进制文件的大部分是语言支持(EH和CTOR/DTOR)。
使用#include<iostream>-O3 -g0进行编译会产生一个7625字节的二进制文件,如果我用-O0 -g3编译它,就会产生一个64Kb的二进制文件,其中大部分是描述STL符号的文本。

1
它并没有直接回答问题(或者根本没有回答),但我无法忽视这个很棒的结果,给你点赞。 - LyingOnTheSky
我无法复制您的26k二进制文件;使用-O0 -g3生成6kb,使用#include <iostream>-O3 -g0生成7.5Kb,使用#include-O0 -g3生成64Kb。因此,我无法准确回答您的开销是多少,但我已经回答了“除了实际的主函数和调用的函数之外,该EXE存储了什么?”这个问题——它需要使用标准库和支持语言特性(如异常处理),最后,最不重要的是,它将二进制格式化为elf二进制而不是原始可执行文件。 - kfsone

3

您的可执行文件包含C运行时库,这个库知道如何获取环境变量,设置argv向量,并在调用exit()之后但在调用_exit()之前关闭所有打开的文件。


3

许多因素可能会影响编译过程中的最终文件大小,正如其他帖子所指出的那样。

分析您的具体示例比我愿意付出的工作还要多,但是我知道许多年前有一个类似的例子,它应该可以帮助您理解一般问题,并指导您找到您寻求的具体答案。

http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html

该文档使用GCC在C(而不是C ++)中完成,查看ELF可执行文件的大小(而不是Windows EXE),但正如我所说,许多相同的问题也适用于这种情况。在这种情况下,作者只查看 return 42;

阅读完该文档后,请考虑打印到 stdout 要比仅返回数字复杂得多。另外,由于您正在使用C++和 cout <<,其中隐藏了很多您没有编写的代码,而且您无法真正知道它是如何实现的,除非查看该源代码。


抱歉,我无法完成此任务。请提供要翻译的文本。 - LyingOnTheSky

3
人们经常忽略/忘记高级语言创建的可执行文件需要引擎才能正常运行。例如,C++引擎负责以下事项:
- 堆栈管理 - 当您调用new,delete时,实际上并未访问操作系统函数 - 相反,引擎使用自己分配的堆内存 - 因此,引擎具有自己的内存管理,需要占用代码/空间 - 本地变量内存管理 - 每次调用任何函数时,所有本地变量都必须被分配和释放,在退出之前释放 - 类/模板 - 要正确处理这些,需要相当多的代码
除此之外,还必须链接使用的所有内容,例如:
- 运行时库(RTL) - 大多数现代可执行文件的MSVCPP和MSVB不会链接它们,所以我们需要在系统中安装大量的RTL才能使exe运行。但仍然必须在可执行文件中存在用到的DLL链接(请参见运行时DLL链接) - 调试信息 - 框架链接(类似于RTL,您需要将代码绑定到框架库中) - 对于高级Windows / Forms IDE,还有窗口引擎 - 包含的库和链接的对象(即使只使用<<,您也需要更多的iostream类和运算符才能使其正常工作...)
您可以将C++引擎视为操作系统内的小型操作系统。
- 在独立MCU应用程序中,它们真的是操作系统本身
可执行文件格式(如PE)占据另一些空间,代码对齐也会增加一些空间。
当你把所有这些放在一起时,那么26KB就不那么荒谬了。

3
编译器并非无所不能。 std::cout是一个流对象,具有一组数据成员用于管理缓冲区(分配、复制数据,并在流被销毁时释放)。 operator<<被实现为一个重载函数,它解释其参数,并且当提供一个字符串时,将数据复制到缓冲区中,还有一些逻辑,当缓冲区满时可能会刷新缓冲区。
实际上,std::endl是一个函数,与所有版本的流的operator<<()合作,影响流拥有的数据。具体而言,它将换行符插入流的缓冲区,然后刷新缓冲区。
刷新流的缓冲区调用其他函数,将数据从缓冲区复制到标准输出设备(例如屏幕)。
以上就是语句std::cout<<"Hello World"<<std::endl的全部内容。
此外,作为C++程序,即使在调用main()之前,必须执行一定量的代码。这包括检查程序是否使用命令行参数,创建流如std::coutstd::cerrstd::cin(还有其他),确保这些流与相关设备连接(如终端、管道等)。当main()返回时,还需要释放所有创建的流(并刷新其缓冲区)等操作。
以上所有操作都涉及调用其他功能。创建流的缓冲区意味着必须分配缓冲区,并在main()返回后释放缓冲区。
C++流的规范也涉及错误检查。例如,std::cout的缓冲区分配可能会失败(例如,如果主机系统没有足够的空闲内存)。标准输出设备可能被重定向到具有有限容量的文件中,因此写入数据可能会失败。所有这些情况都必须进行检查,并妥善处理。
所有这些东西都将包含在这个26K的可执行文件中(除非该代码在运行时库中)。
原则上,编译器可以识别程序未使用其命令行参数(因此不包括管理命令行参数的代码),只写入std::cout(因此在main()之前不需要创建所有其他流,并在main()返回后释放它们),只使用两个重载版本的operator<<()和一个流操纵器(因此链接器不需要包含流的所有其他成员函数的代码)。 它还可能认识到该语句将数据写入流并立即刷新缓冲区-从而消除std::cout的缓冲区以及管理它的所有代码。如果编译器能够读取程序员的思维(实际上很少有编译器能够做到这一点),它可能会发现实际上没有任何缓冲区是必要的,用户永远不会重定向标准输出等-并消除与所有这些内容相关联的代码和数据结构。
那么,编译器如何识别所有这些都不需要?编译器是软件,因此它们必须对其输入(例如源文件)进行某种级别的分析。消除人类可能认为不必要的所有代码的分析是显著的-因此需要时间。如果编译器不执行分析,则可能由链接器执行。无论是编译器还是链接器执行消除不必要代码的分析都无关紧要-它需要时间。潜在的时间相当长。
程序员往往不耐烦。很少有程序员会容忍一个简单的“hello world”程序的构建过程超过几秒钟(也许他们会容忍一分钟,但不会太多)。
这使得编译器供应商面临决策。他们可以让程序员设计和实现各种分析来消除不需要的代码。这将增加几周或者如果他们正在按紧迫的期限工作,则可能增加几个月来实施,验证,验证并向客户(其他开发人员)交付可用的编译器。这个编译器在编译代码时将非常缓慢。相反,供应商(及其开发人员)选择在编译器中实现较少的分析,以便他们可以实际上向将在合理时间内使用它的开发人员交付可用的编译器。这个编译器将在大多数程序员可以容忍的时间内生成可执行文件(例如,“hello world”程序在一分钟内)。即使可执行文件更大又怎样?它仍将起作用。硬件(例如驱动器)相对便宜,而开发人员的努力相对昂贵。

抱歉,我无法完成此任务。请提供要翻译的文本。 - LyingOnTheSky
我并不是在暗示 iostream 是罪魁祸首。我在描述一堆贡献者。流的实现和规范是其中之一。必须在调用 main() 之前和之后执行的代码是另一个。 - Peter

3
这是一个很老的问题,它有明确的答案。最大的问题在于要写很多小的信息和进行许多小测试,以展示PE结构的不同方面。我试图跳过细节,并基于自己多年使用的Microsoft Visual Studio描述问题的主要部分。所有其他编译器大多都是相同的,我认为只需要使用少量的编译器和链接器选项即可。
首先,建议您在main的第一行设置断点,启动调试并检查调试器的调用堆栈窗口。你会看到像这样的东西: enter image description here 因此,非常重要的第一件事是要理解的是,main不是程序中将被调用的第一个函数。程序的入口点是mainCRTStartup,它调用__tmainCRTStartup,然后调用main
CRT启动代码做了许多小事情。其中一件非常容易理解:它使用GetCommandLineW Windows API获取命令行并解析参数,然后使用参数调用main
为了减少代码大小,有两种常见方法:
  1. 使用DLL中的CRT
  2. 如果代码中真的没有使用CRT,则从EXE中移除CRT。
如果您使用“VS2013 x64 Native Tools Command Prompt”(或某些类似的命令提示符)启动cmd.exe,则非常有用。命令提示符内部将设置一些附加路径,因此您可以使用例如dumpbin.exe实用程序。
如果您使用Multi-threaded DLL (/MD)编译器选项,则将获得7K大的exe文件。 "dumpbin /imports HelloWorld.exe"将向您显示您的程序与“MSVCR120.dll”和“KERNEL32.dll”一起使用。
CRT的删除取决于您使用的c/cpp编译器版本(Visual Studio的版本)甚至取决于文件的扩展名:.c.cpp。我理解您的问题是为了理解问题而普遍提出的问题。因此,建议从最简单的情况开始,将.cpp文件重命名为.c并修改代码如下:
#include <Windows.h>

int mainCRTStartup()
{
    return 0;
}

现在可以看到

C:\Oleg\StackOverflow\HelloWorld\Release>dir HelloWorld.exe
 Volume in drive C has no label.
 Volume Serial Number is 4CF9-FADF

 Directory of C:\Oleg\StackOverflow\HelloWorld\Release

21.06.2015  12:56             3.584 HelloWorld.exe
               1 File(s)          3.584 bytes
               0 Dir(s)  16.171.196.416 bytes free

C:\Oleg\StackOverflow\HelloWorld\Release>dumpbin HelloWorld.exe
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file HelloWorld.exe

File Type: EXECUTABLE IMAGE

  Summary

        1000 .data
        1000 .rdata
        1000 .reloc
        1000 .rsrc
        1000 .text

可以添加连接器选项/MERGE:.rdata=.text来减小文件大小并移除一个部分。

C:\Oleg\StackOverflow\HelloWorld\Release>dir HelloWorld.exe
 Volume in drive C has no label.
 Volume Serial Number is 4CF9-FADF

 Directory of C:\Oleg\StackOverflow\HelloWorld\Release

21.06.2015  18:44             3.072 HelloWorld.exe
               1 File(s)          3.072 bytes
               0 Dir(s)  16.170.852.352 bytes free

C:\Oleg\StackOverflow\HelloWorld\Release>dumpbin HelloWorld.exe
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file HelloWorld.exe

File Type: EXECUTABLE IMAGE

  Summary

        1000 .data
        1000 .reloc
        1000 .rsrc
        1000 .text

为了得到“Hello World”程序,我建议修改代码如下:
#include <Windows.h>

int mainCRTStartup()
{
    LPCTSTR pszString = TEXT("Hello world");
    DWORD cbWritten;
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), pszString, lstrlen(pszString), &cbWritten, NULL);
    return 0;
}

您可以轻松验证代码是否有效并且仍然很小。

为了从.cpp文件中删除CRT,建议按照以下步骤进行操作。首先,我们将使用以下HelloWorld.cpp代码。

#include <Windows.h>
int mainCRTStartup()
{
    LPCTSTR pszString = TEXT("Hello world");
    DWORD cbWritten;
    WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), pszString, lstrlen(pszString), &cbWritten, NULL);
    return 0;
}

有些编译器和链接器选项很重要,需要进行验证并设置或删除。我在下面的图片中包含了这些设置:

enter image description here

enter image description here

enter image description here

enter image description here

最后一张屏幕显示我们如何移除与默认库的绑定,因为我们不需要它们。编译器使用类似于#pragma comment(lib, "some.lib")的指令来注入某些库的使用。通过使用选项/NODEFAULTLIB,我们可以移除这样的库,从而得到我们需要的可执行文件。

您会发现生成的HelloWorld.exe仅有3K(3.072字节),并且仅依赖于一个KERNEL32.dll:

C:\Oleg\StackOverflow\HelloWorld\Release>dumpbin /imports HelloWorld.exe
Microsoft (R) COFF/PE Dumper Version 12.00.31101.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file HelloWorld.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    KERNEL32.dll
                402000 Import Address Table
                402038 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                  60B lstrlenW
                  5E0 WriteConsoleW
                  2C0 GetStdHandle

  Summary

        1000 .idata
        1000 .reloc
        1000 .rsrc
        1000 .text

您可以从 这里 下载对应的Visual Studio 2013演示项目。需要将编译方式从默认的“Debug”切换到“Release”,并重新构建解决方案,就会得到一个名为HelloWorld.exe的可执行文件,其大小为3K。


1
这确实展示了编写具有相同语义的程序是多么困难。
如果流是good(),那么<
另外,std::cout的streambuf可能会被从它下面交换出来。编译器无法知道它实际上会去STDOUT_FILENO。它必须使用整个streambuf中间层。

抱歉,我无法理解您的请求。请提供需要翻译的英文文本。 - LyingOnTheSky

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接