为什么我可以用GCC制作的最小编译的exe是67KB?

Ádá*_*zay 5 c gcc mingw compiler-optimization

我想做一个很小的编译后的exe,它是用C编写的。但是我能设法得到的最小文件是67KB。我正在使用MinGW。我尝试不使用任何头文件,并且编译时没有错误:

//no header
void main() {
 write(1, "Hello world!", 12);
}
Run Code Online (Sandbox Code Playgroud)

如果我生成并运行此命令,GCC不会显示任何错误,但它也是67KB。

ams*_*ams 5

我刚刚在 x86_64 Linux 中尝试过这个,虽然你永远不知道,这可能与这个级别的 MinGW 没有太大区别。

基本上,问题在于,即使没有从 C 库中提取任何内容,除非它被引用,CRT“启动文件”确实引用了一小部分内容,这些内容又引用了其他一些内容,而“Hello world”最终会查找坏的。这不是一个值得解决的问题,因为所有真实的程序无论如何都会引用这些核心函数。

启动文件的源是可用的,而且非常小,如果您愿意,编译器允许您覆盖标准文件,因此优化它们并不是一件大事。它们是用汇编代码编写的,但是您可以通过简单地删除行来删除大部分无关的垃圾。

但是,有一个技巧可以将起始文件完全排除在等式之外:

#include <unistd.h>

void _start (void) {
  write(1,"Hello world!", 12);
  _exit(0);
}
Run Code Online (Sandbox Code Playgroud)

编译: gcc -nostartfiles t.c -s -static

哪个有效(偶然,见下文),并给我一个1792 bytes的文件大小。

为了进行比较,您的原始代码使用相同的编译器提供 738624 字节,当我删除时会下降到 4400 字节-static,但那是作弊!(我的代码实际上在没有 的情况下变得更大-static,因为动态链接器元数据超过了write和 的代码_exit)。

偶然的一部分,是该程序现在已经没有堆栈指针初始化。同样,对于所有其他全局状态,启动文件通常会处理。碰巧的是,在 x86_64 Linux 上,这不是一个致命的问题(只是不要在生产中这样做,对吗?)但是,当我尝试使用它时,-m32我在write.

这个问题可以通过添加自己的初始化的东西是固定的,但随后的代码将不再作为便携式(不是绝对便携的话)。或者,直接调用 write 系统调用。