6

I am normally writing in C for sizecoding demo-making competition (64kb) but considering moving to C++.

Under MinGW g++ i have some trouble with the .exe-size. (Before using executable-packers, i have to get it down to <100 kb.).

I have looked at this: How to reduce the size of executable produced by MinGW g++ compiler? however i am already using MinGW/g++ 4.8.1 and -s -Os ... see below (and for 4.8.1 too: unrecognized option '-shared-libstdc++' and cannot find -lstdc++_s).

This little helloworld has only 10 kb (which is ok):

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

However when i add:

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

it becomes 193 kb

And when i add:

#include <iostream>

then it becomes 756 kb.

I am using these flags:

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

There has to be some way to only link what i use. What am i missing?

Optional 1: Is it possible to get -shared-libstdc++ or -lstdc++_s working in MinGW/g++ 4.8.1?

Optional 2: When I try -nostdlib and replace main with WinMain:

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

I get no compiler warnings but a runtime crash, it works fine when compiling as C though. (optional because I don't want you/me to spend time debugging crt-startup, a compiler-linker-flag to trim libraries would be more helpful)

Community
  • 1
  • 1
ben
  • 452
  • 3
  • 10
  • 1
    is `strip` command available for you? – billz Aug 24 '13 at 01:39
  • the unix-world command? no. – ben Aug 24 '13 at 01:45
  • MinGW should have this command, have you tried it? – billz Aug 24 '13 at 01:48
  • 2
    i just downloaded the windows-version of strip from mingw binutils but it did not change the exe size at all using `strip b.exe` or `strip -s b.exe`. Do i have to specify some additional arguments? – ben Aug 24 '13 at 01:52
  • These don't do anything: `-ffunction-sections -fdata-sections -fvisibility-inlines-hidden` Oh, and why on Earth would you care? Why would anyone care about the binary size on Windows? Also, have you compared to Linux GCC and MSVC? – rubenvb Aug 26 '13 at 17:20
  • @rubenvb: I wouldn't precisely say that they do "nothing". Maybe one could say "nothing useful", but surely not "nothing". `-ffunction-sections` greatly increases executable size. A section for every function places an extra string into the executable for the section's name, and on top of that sections have both alignment and minimum size constraints. – Damon Aug 26 '13 at 17:25
  • @Damon: You're absolutely right, and they also *save* size when linked with `--gc-sections`, but not on Windows. So as far as the question is concerned, they do nothing ;-). – rubenvb Aug 26 '13 at 17:29
  • @rubenvb Why i care: it is for sizecoding ie. demo-making competition. – ben Aug 26 '13 at 18:12
  • @Damon removing any of these options did not change the exe-size, they are in my script because of another project – ben Aug 26 '13 at 18:15
  • Then use GCC on Linux? – rubenvb Aug 26 '13 at 20:10
  • @rubenvb The demos are expected to run on windows. – ben Aug 27 '13 at 05:59
  • Mingw always produces bigger executables, I don't think it can be fixed (the only thing you can do is to run strip). Give up. If you want smaller executables and faster compilation, use microsoft compiler. The only problem is that microsoft compiler is less standard-compliant and has its own minor quirks. – SigTerm Aug 27 '13 at 13:58

4 Answers4

6

Those extra bytes are "pulled in" by standard library calls - the high level ones tend to bring everything below them along, memory allocation, the exceptions they use and so on. The easiest thing to start with is to minimize what you use. Basing a hello world on putchar() might give you a good point of comparison. I'll focus on statically linked programs as that's what I know, rubenvb's answer appears to cover shared libraries well anyway.

Features like new, delete, pure virtual functions etc. pull in bits of the library too and many dependencies underneath. A quick overview on how to replace those is here: http://ptspts.blogspot.com.au/2010/12/how-to-write-c-program-without-libstdc.html - if you get really extreme, you could find a version of malloc treated in the same way.

More recently with C++11 you'll run into __cxa_guard_acquire and __cxa_guard_release if you do this:

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

For that, use the -fno-threadsafe-statics flag if your compiler supports it.

If that doesn't quite get you far enough you can link with the flag -Map=filename.map on linux versions of ld to generate a "map" file. The first section of the file that outputs lists each section that was pulled in, along with the one that required it.*

*The map file would also reveal that function-sections does nothing to the standard library as it's already been compiled without that flag, apart from that

2

There is no way you can get rid of the extra code MinGW GCC compiled executables need to function. You might try a MinGW-w64 toolchain, which approaches the CRT differently, but it might be worse than plain MinGW in terms of "Hello world" size.

I strongly suggest using MSVC if binary size is that important, as it relies on its own CRT code built in to the OS. Which I may carefully call "cheating" in some way :-).

About the DLL libstdc++, of course it's possible, you just need a decent toolchain ;-). See here. Note that this is also "cheating", because I doubt the contest will ignore the size of the runtime libraries. This is why codesize contests are stupid. There is always another layer of the runtime you're ignoring. Until you hit bare metal kernel or assembly, which is not the point of said contest.

rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • I don't know if you looked into this or not but I found that the compiled binary is much smaller in msvc than mingw (97kb vs 643kb). Note that msvc *is* linking its stdlib and crt statically. [Test demo here](http://ideone.com/i9L8Hx). If you check the import directory of cl's compiled binary, you'll see that it only contains `kernel32.dll` which confirms that the crt and stdlib is statically linked into the binary. – greatwolf Sep 27 '14 at 12:03
  • OTOH, mingw's binary imports `msvcrt.dll`, `libwinpthread-1.dll` as well as `kernel32.dll`. – greatwolf Sep 27 '14 at 12:05
  • @greatwolf you can link winpthread in statically by using the `-static` link option. Then the binary depends on ntdll.dll, msvcrt.dll and kernel32.dll. Yes it will be a half megabyte bigger, but since when is that kind of size important for real-world applications? If the size of a Hello World application is so important to you, please use MSVC. Yes MSVC is better than GCC on Windows in removing dead code, and using OS-provided functionality. – rubenvb Sep 27 '14 at 12:24
1

It's a shot in the dark, but try -fwhole-program option when linking. Also -flto when compiling and linking may be helpful

Michał Walenciak
  • 4,257
  • 4
  • 33
  • 61
  • Ok, tried them both. together and separately because the documentation says so ( http://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#Optimize-Options ). But exe-size remains **exactly** the same. thanks though! – ben Aug 26 '13 at 17:35
1

I tried to run your code under QT 5.1.0 MinGW 4.8 32 bit

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


std::string asdf;

    return 0;
}

In release mode exe size is 34 kb, in debug mode exe size is 92 kb

tas
  • 46
  • 1
  • that is much better than my gcc 4.8.1, but it is still way way too large. thanks though! – ben Aug 27 '13 at 14:30