【问题标题】:C++ executable size using MinGW使用 MinGW 的 C++ 可执行文件大小
【发布时间】:2013-08-27 04:02:45
【问题描述】:

我通常用 C 编写大小编码演示制作比赛 (64kb),但考虑转向 C++。

MinGW g++ 下,我在 .exe 大小方面遇到了一些问题。 (在使用可执行打包程序之前,我必须将其降低到

我看过这个: How to reduce the size of executable produced by MinGW g++ compiler? 但是我已经在使用 MinGW/g++ 4.8.1 和 -s -Os ...见下文(对于 4.8.1 也是:unrecognized option '-shared-libstdc++'cannot find -lstdc++_s)。

这个小小的helloworld只有10 kb(没关系):

#include "windows.h"
int main() {
    MessageBoxA(0, "test", "test", 0);
    return 0;
}

但是当我添加:

#include <string>
...
std::string asdf;

变成 193 kb

当我添加:

#include <iostream>

然后它变成 756 kb。

我正在使用这些标志:

-std=c++11
-Wall
-s       (or -Wl,-s)
-Os
-DNDEBUG
-fno-exceptions
-fno-rtti
(note: removed those with no effect)

必须有某种方法只链接我使用的东西。我错过了什么?

可选 1: 是否可以让 -shared-libstdc++ 或 -lstdc++_s 在 MinGW/g++ 4.8.1 中工作?

可选2:当我尝试-nostdlib并将main替换为WinMain时:

    #include "windows.h"
    int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
        LPSTR lpCmdLine, int nCmdShow) {
        MessageBoxA(0, "test", "test", 0);
        return 0;
    }

我没有收到编译器警告,但运行时崩溃,但在编译为 C 时它工作正常。 (可选,因为我不希望你/我花时间调试 crt-startup,编译器链接器标志来修剪库会更有帮助)

【问题讨论】:

  • 您可以使用strip 命令吗?
  • unix-world 命令?没有。
  • MinGW应该有这个命令,你试过了吗?
  • 我刚刚从 mingw binutils 下载了 windows 版本的 strip,但使用 strip b.exestrip -s b.exe 并没有改变 exe 大小。我必须指定一些额外的参数吗?
  • 这些没有任何作用:-ffunction-sections -fdata-sections -fvisibility-inlines-hidden 哦,你到底为什么要关心?为什么有人会关心 Windows 上的二进制大小?另外,你有和 Linux GCC 和 MSVC 比较过吗?

标签: c++ windows mingw executable


【解决方案1】:

这是在黑暗中拍摄,但在链接时尝试 -fwhole-program 选项。编译和链接时 -flto 也可能会有所帮助

【讨论】:

  • 好的,两个都试了。一起和分开,因为文档是这样说的(gcc.gnu.org/onlinedocs/gcc/…)。但 exe 大小保持完全相同相同。不过谢谢!
【解决方案2】:

您无法摆脱 MinGW GCC 编译的可执行文件需要运行的额外代码。您可以尝试使用 MinGW-w64 工具链,它以不同的方式处理 CRT,但就“Hello world”大小而言,它可能比普通的 MinGW 更差。

如果二进制大小如此重要,我强烈建议使用 MSVC,因为它依赖于操作系统内置的自己的 CRT 代码。我可能会以某种方式小心地将其称为“作弊”:-)。

关于 DLL libstdc++,当然有可能,你只需要一个像样的工具链 ;-)。见here。请注意,这也是“作弊”,因为我怀疑比赛会忽略运行时库的大小。这就是为什么代码大小竞赛是愚蠢的。您总是忽略运行时的另一层。直到你遇到裸机内核或程序集,这不是所述比赛的重点。

【讨论】:

  • 我不知道你是否看过这个,但我发现在 msvc 中编译的二进制文件比 mingw 小得多(97kb vs 643kb)。请注意,msvc 正在 静态链接其 stdlib 和 crt。 Test demo here。如果您检查 cl 编译后的二进制文件的导入目录,您会看到它只包含 kernel32.dll,这证实了 crt 和 stdlib 已静态链接到二进制文件中。
  • OTOH,mingw 的二进制导入 msvcrt.dlllibwinpthread-1.dll 以及 kernel32.dll
  • @greatwolf 您可以使用-static 链接选项静态链接winpthread。然后二进制依赖于ntdll.dll、msvcrt.dll和kernel32.dll。是的,它会大半兆字节,但是从什么时候开始,这种大小对于现实世界的应用程序很重要?如果 Hello World 应用程序的大小对您来说非常重要,请使用 MSVC。是的,MSVC 在删除死代码和使用操作系统提供的功能方面优于 Windows 上的 GCC。
【解决方案3】:

我尝试在 QT 5.1.0 MinGW 4.8 32 位下运行您的代码

#include "windows.h"
#include <string>
#include <iostream>
int main() {
    MessageBoxA(0, "test", "test", 0);


std::string asdf;

    return 0;
}

在发布模式下,exe 大小为 34 kb, 在调试模式下,exe 大小为 92 kb

【讨论】:

  • 这比我的 gcc 4.8.1 好得多,但它仍然太大了。不过谢谢!
【解决方案4】:

那些额外的字节被标准库调用“拉入” - 高级字节倾向于将所有内容放在它们之下,内存分配,它们使用的异常等等。最简单的开始是尽量减少你使用的东西。基于 putchar() 的 hello world 可能会给您一个很好的比较点。我将专注于静态链接程序,因为这就是我所知道的,rubenvb 的答案似乎很好地涵盖了共享库。

new、delete、纯虚函数等特性也引入了库的一些部分以及下面的许多依赖项。关于如何替换它们的快速概述在这里:http://ptspts.blogspot.com.au/2010/12/how-to-write-c-program-without-libstdc.html - 如果你真的很极端,你可以找到一个以相同方式处理的 malloc 版本。

最近使用 C++11,如果您这样做,您将遇到 __cxa_guard_acquire 和 __cxa_guard_release:

int foo() {
    static int bar = 1; //thread safe in C++11, requires inbuilt functions
}

为此,如果您的编译器支持,请使用 -fno-threadsafe-statics 标志。

如果这还不足以让您走得足够远,您可以在 ld 的 linux 版本上与标志 -Map=filename.map 链接以生成“地图”文件。输出文件的第一部分列出了被拉入的每个部分,以及需要它的部分。*

*映射文件也会显示函数部分对标准库没有任何作用,因为它已经在没有那个标志的情况下被编译,除了那个

【讨论】:

  • 我也在手机上写这篇文章,所以如果你有耐心的话,我会编辑链接、格式和用户名等内容。
  • 迄今为止的最佳答案。博文的链接非常有趣。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-06
  • 1970-01-01
  • 2018-05-25
相关资源
最近更新 更多