为什么我可以用 GCC 制作的最小编译 exe 是 67KB?
Why is smallest compiled exe I can make with GCC is 67KB?
我想制作一个非常小的编译后的 exe,它是用 C 编写的。但我能设法得到的最小的是 67KB。我正在使用 MinGW。
我试过不使用任何头文件,并且编译没有错误:
//no header
void main() {
write(1, "Hello world!", 12);
}
如果我构建 运行 GCC 没有显示错误,但它也是 67KB。
我刚刚在 x86_64 Linux 中尝试过这个,在这个级别上它可能与 MinGW 没有太大区别,尽管你永远不知道。
基本上,问题在于,即使没有从 C 库中提取任何内容,除非它被引用,CRT "startfiles" 确实引用了一小部分内容,这些内容又引用了一些其他内容,并且"Hello world" 结果看起来很糟糕。这不是一个值得解决的问题,因为所有实际程序无论如何都会引用这些核心功能。
启动文件的源代码可用,而且很小,如果您愿意,编译器允许您覆盖标准文件,因此优化它们并不是什么大不了的事。它们是用汇编代码编写的,但您可以通过简单地删除行来删除大部分无关的垃圾。
但是,有一个技巧可以将启动文件完全从等式中删除:
#include <unistd.h>
void _start (void) {
write(1,"Hello world!", 12);
_exit(0);
}
编译:gcc -nostartfiles t.c -s -static
这有效(偶然,见下文),并给我一个 1792 字节的文件大小。
为了比较,您的原始代码使用相同的编译器给出了 738624 字节,当我删除 -static
时它下降到 4400 字节,但那是作弊! (我的代码实际上变得 更大 没有 -static
,因为动态链接器元数据超过了 write
和 _exit
的代码)。
偶然部分,程序现在没有初始化堆栈指针。同样,对于所有其他全局状态,启动文件通常会处理。碰巧,在 x86_64 Linux 上,这不是一个致命的问题(只是不要在生产中这样做,对吧?)但是,当我用 -m32
尝试时,我得到了write
.
中的分段错误
可以通过为这些东西添加您自己的初始化来解决这个问题,但是代码将不再是 可移植的(它已经不是绝对可移植的了)。或者,直接调用 write 系统调用。
我想制作一个非常小的编译后的 exe,它是用 C 编写的。但我能设法得到的最小的是 67KB。我正在使用 MinGW。 我试过不使用任何头文件,并且编译没有错误:
//no header
void main() {
write(1, "Hello world!", 12);
}
如果我构建 运行 GCC 没有显示错误,但它也是 67KB。
我刚刚在 x86_64 Linux 中尝试过这个,在这个级别上它可能与 MinGW 没有太大区别,尽管你永远不知道。
基本上,问题在于,即使没有从 C 库中提取任何内容,除非它被引用,CRT "startfiles" 确实引用了一小部分内容,这些内容又引用了一些其他内容,并且"Hello world" 结果看起来很糟糕。这不是一个值得解决的问题,因为所有实际程序无论如何都会引用这些核心功能。
启动文件的源代码可用,而且很小,如果您愿意,编译器允许您覆盖标准文件,因此优化它们并不是什么大不了的事。它们是用汇编代码编写的,但您可以通过简单地删除行来删除大部分无关的垃圾。
但是,有一个技巧可以将启动文件完全从等式中删除:
#include <unistd.h>
void _start (void) {
write(1,"Hello world!", 12);
_exit(0);
}
编译:gcc -nostartfiles t.c -s -static
这有效(偶然,见下文),并给我一个 1792 字节的文件大小。
为了比较,您的原始代码使用相同的编译器给出了 738624 字节,当我删除 -static
时它下降到 4400 字节,但那是作弊! (我的代码实际上变得 更大 没有 -static
,因为动态链接器元数据超过了 write
和 _exit
的代码)。
偶然部分,程序现在没有初始化堆栈指针。同样,对于所有其他全局状态,启动文件通常会处理。碰巧,在 x86_64 Linux 上,这不是一个致命的问题(只是不要在生产中这样做,对吧?)但是,当我用 -m32
尝试时,我得到了write
.
可以通过为这些东西添加您自己的初始化来解决这个问题,但是代码将不再是 可移植的(它已经不是绝对可移植的了)。或者,直接调用 write 系统调用。