I have the following C program:
#include <windows.h>
void __cdecl mainCRTStartup() {
DWORD bw;
HANDLE hfile = GetStdHandle(STD_OUTPUT_HANDLE);
WriteFile(hfile, "Hello, World!\r\n", 15, &bw, 0);
ExitProcess(0); /* Needed for successful (0) exit. */
}
I compile it with GCC 4.8.2, using the following command line:
i686-w64-mingw32-gcc -s -Os -fno-ident -fno-stack-protector -fomit-frame-pointer \
-fno-unwind-tables -fno-asynchronous-unwind-tables -falign-functions=1 \
-mpreferred-stack-boundary=2 -falign-jumps=1 -falign-loops=1 -mconsole \
-nostdlib -nodefaultlibs -nostartfiles -o h.exe h.c -lkernel32
The generated .exe file is 2048 bytes long. How can I make it smaller using MinGW, preferably at most 1024 bytes, or (even better) at most 512 bytes?
I'd prefer a solution without writing assembly code, but I'm also interested in assembly solutions.
I've tried -Wl,-N
to decrease the number of sections (segments), but that caused a segfault when running the .exe in Wine.
This article suggests that 480 bytes is possible. It uses the following settings:
#pragma comment(linker, "/FILEALIGN:16")
#pragma comment(linker, "/ALIGN:16")// Merge sections
#pragma comment(linker, "/MERGE:.rdata=.data")
#pragma comment(linker, "/MERGE:.text=.data")
#pragma comment(linker, "/MERGE:.reloc=.data")
#pragma optimize("gsy", on)
Unfortunately these #pragma
s don't work with MinGW GCC. Are there equivalents?
In here I was able to find the GCC flags
-Wl,--section-alignment,16,--file-alignment,16
which bring down the .exe size to 752 bytes. The .exe seems to work in Wine.
By modifying the linker script I was able to merge .data
and .rdata
, and go down to 736 bytes. I'm using these GCC flags in addition to those above: -Wl,--section-alignment,16,--file-alignment,16,-T,tinygccpe.scr
.
I'm still looking for the MinGW equivalent of /MERGE
.
This question is similar, but it doesn't attempt to go below 9000 bytes.
I'm also looking for a strip
tool (the strip
command in MinGW doesn't reduce the .exe size any further) which can remove the DOS stub (which is between offsets 0x40 and 0x80, it contains This program cannot be run in DOS mode.
, we could save 64 bytes). This code can remove it, but it also breaks all the absolute offsets in the .exe. Unfortunately the linker ld
in MinGW isn't able to remove the DOS stub, it's hardcoded in the file bfd/peXXigen.c
, just above the NT_SIGNATURE
.
Is it possible to strip more headers from the .exe, i.e. headers which the loader doesn't use?
Gcc distributions such as mingw provide a gcc executable which can be used to compile files in any language the distribution supports. They also provide other compiler drivers, which in the case of mingw are executables such as g++, c++, g77, etc.
"I compiled a c file on my Linux machine and named it test.exe, is it possible to run that file on windows?" - It is absolutely possible to compile a Windows executable on Linux.
MinGW ("Minimalist GNU for Windows"), formerly mingw32, is a free and open source software development environment to create Microsoft Windows applications.
This question has extensive online literature, starting from about 1995.
Each version of 32-bit and 64-bit Windows has a different set of rules on what header values they accept in PE .exe executables. For example, Windows 7 accepts .exe files with 0 sections, section alignment 4, file alignment 4, and other versions of Windows (e.g. Windows XP and the most recent Windows 10 in 2020) reject these files.
It's possible to create working .exe files smaller than 2048 bytes though. Example:
Here is why it is unlikely that a portable Win32 PE .exe hello-world shorter than 584 bytes will be released:
The .exe files smaller than 268 bytes work only on Windows versions earlier than Windows XP, and they don't work on 64-bit Windows systems.
Related literature:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With