5
votes

I'm trying to figure out how to shrink the sizes of EXE files compiled under the newest QT SDK (4.8.2) (mingw/g++ based). I was working on a vanilla c++ console app that has a simple loop and only #includes iostream, when I noticed that the exe's it generated are about 465kb; way bigger than they should be! Commenting out all the stream stuff brings it down to the expected 5kb range (although the remaining code would be mostly dead). This doesn't seem right at all, especially since another, full project I'm working on has a QGLwidget, windowing, a dozen data structures and ~3000 statements and only clocks in at about 126Kb. Is there some setting or flag I'm missing? Here's the .pro, while the cpp is trivial and Qt-free (basically getline and cout with a half dozen char swaps):

TEMPLATE = app
CONFIG += console
CONFIG -= app_bundle
CONFIG -= qt
SOURCES += main.cpp
QMAKE_CXXFLAGS_RELEASE += -O2
QMAKE_CXXFLAGS_RELEASE += -Os

I've tried a few other configurations, and it's definitely compiling in release mode (debug is >3Mb), but I can't figure out why it's so bloated.

I've also looked at the PE header, and I see that it's importing some functions from libgcc_s_dw2-1.dll and mingwm10.dll, and it'd be nice if I could eliminate those dependencies altogether as well, especially since neither one should be required anyway. I can make the libgcc one go away(at the expense of 17kb of exe size) by adding QMAKE_LFLAGS_RELEASE += -static to the .pro, but the mingwm10.dll stays either way, calling a single function.

Based on the overall bloating, and all the useless framework stuff that the compiler is trying to sneak in (networking, at least). I'm guessing it's just a matter of a couple settings that are askew, particularly with some of the default compiler flags like -DQT_LARGEFILE_SUPPORT or -mthreads. Here is the compile output (bullets added for emphasis):

  • 14:04:00: Running steps for project conTest...
  • 14:04:00: Configuration unchanged, skipping qmake step.
  • 14:04:01: Starting: "C:\QtSDK\mingw\bin\mingw32-make.exe"
  • C:/QtSDK/mingw/bin/mingw32-make -f Makefile.Release
  • mingw32-make[1]: Entering directory `C:/Documents and Settings/Administrator/My Documents/QT/conTest'
  • g++ -c -O2 -O2 -Os -frtti -fexceptions -mthreads -Wall -DUNICODE -DQT_LARGEFILE_SUPPORT -I"c:\QtSDK\Desktop\Qt\4.8.1\mingw\mkspecs\win32-g++" -o release\main.o main.cpp g++ -Wl,-s -Wl,-subsystem,console -mthreads -o release\conTest.exe release/main.o
  • mingw32-make[1]: Leaving directory `C:/Documents and Settings/Administrator/My Documents/QT/conTest'
  • 14:04:10: The process "C:\QtSDK\mingw\bin\mingw32-make.exe" exited normally.
2
That -frtti flag is likely playing some part, though it's hard to say how much. This question has some details on the costs associated with it. The accepted answer estimates a 5% to 10% executable size penalty, but I'd imagine a table with info for all of Qt's classes would be a bit bigger.Xavier Holt
@Xavier-Holt I disabled rtti, threads, and exceptions. Although the object file shrank by 20% to 1.8k, the exe stays at 464kb. I'm gonna look through the docs and see if it has something to do with the way qt handles console apps. I suspect that Qt might be using it's own implementation of a console instead of using the native cmd.exe one, which would kinda make sense from a 'portability' perspective. The bloat is a bit bigger than the size of cmd.exe which would support this hypothesis.Ghost2
I poked around a bit more, just to see. This question has older versions of mingw statically linking libstdc++, which adds a lot of bits. There's also the GNU section stripping trick: Compile with g++ -fdata-sections -ffunction-sections and link with g++ -Wl,-s,--gc-sections (all your static libraries need to be compiled with -f***-sections for the full effect). Should reduce exe size even further, though this is more an optimization, and seems unlikely to be at the heart of your problem...Xavier Holt
I checked out the link, and adding "QMAKE_CXXFLAGS_RELEASE += -lstdc++_s" didn't affect the size, although it did eliminate the mingwm10.dll dependency. I tried the other options too, but nada. I even tried changing the compiler dropdown to 'Mingw as a GCC for windows targets' but nothing.Ghost2
And the most important question - why is that a problem?d33tah

2 Answers

3
votes

One of the problems with using mingw is that the w32 version of binutils doesn't support dead code stripping (it removes the parts of the libraries that you don't actually use.) In order to bring down the sizes of my executables, I had to patch and build binutils from source using the patches here:

http://sourceware.org/bugzilla/show_bug.cgi?id=11539

It helped. But for it to work, you will need to rebuild everything using:

-fdata-sections -ffunction-sections

in the compile flags of everything (including GCC, Qt, other libraries and your own application) and:

-Wl,--gc-sections

in the link flags of your application only. It was worth it for me, as previously my executables would weigh in at about 20MB and now that's been halved and they're about 10MB. This includes all libraries (I link statically), including Qt, SDL, and various media libraries (like Vorbis, mpg123, FLAC and others.)

Although I imagine that if you build on Windows, it's not gonna be easy to do all that. I used Linux to build w32 cross-compile versions of everything, which is way easier.

2
votes

I would say to add in the -s command. That command strips the compiled binary file of all debuging symbols. Typically when you're compiler compiles something, it leaves the names of classes and function in the .exe file as a lookup. Those symbols will include all included headers. So you're .exe file will have symbols from iostream, including functions and classes.

MinGW has nm.exe which can be executed from cmd to list all the symbols in a file. Along with the strip.exe to strip already created files of their symbols.