我测试了两个成功退出的最小程序,第一个使用libc
,第二个不使用libc
。
:
segment .text
global main
main:
xor eax, eax
ret
构建和检查大小:
yasm -f elf64 main.s; clang -o main main.o; stat ./main
...
Size: 8552
...
第二: segment .text
global _start
_start:
mov eax, 60
xor edi, edi
syscall
构建和检查大小:
yasm -f elf64 main.s; ld -o main main.o; stat ./main
...
Size: 704
---
显然,使用main的程序体积更大。为什么链接器不从二进制文件中剥离主例程来优化大小?我指的是在C程序编译后获取目标文件的链接器。
为什么链接器不从可执行文件中剥离主例程?
因为链接器一般不会从给定的目标文件中剥离任何例程。他们根本不在那一行。
链接器不操作例程,它们操作节。一些链接器可以有限地删除未引用的部分,例如GNU BFD ld和Gold有--gc-sections
标志,但在您的示例中没有未引用的部分,因此无论如何都不会被GC。
现在,您的两个程序之间的区别在于,main
链接在C运行时启动(通常是crt0.o
),并设置argc
和argv
参数的方式main
被定义为期望它们。
你的main
不关心这个,但它需要一个非常聪明的链接器来推断。此外,您的main
确实关心返回后调用sys_exit
,因此它实际上需要crt0.o
才能正常关闭。
是argc/argv设置过程,占用了几千字节吗?
如果你使用的是GNU ld,你可以要求链接器告诉你确切的是什么使用了多少空间与链接器--print-map
参数。
在ELF平台上,您还可以使用nm main
来查看哪些符号在最终二进制文件中占用了空间,并使用ld -y <symbol> ...
来查看这些符号从哪里得到链接。