Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is smallest compiled exe I can make with GCC is 67KB?

I'd like to make a very small compiled exe, which was written in C. But the smallest I can managed to get is 67KB. I'm using MinGW. I've tried not to use any header file, and this compiles with no error:

//no header
void main() {
 write(1, "Hello world!", 12);
}

GCC shows no error if I build and run this, but it's also 67KB.

like image 298
Ádám Bozzay Avatar asked Nov 08 '15 18:11

Ádám Bozzay


People also ask

What determines the size of an executable?

Generally, the size of your executable is not greatly based on how many variable constants you have. Based on the different OS that you are talking about, these are some aspects that can affect your executable size: Resources & libraries statically linked into the executable.

What is the default executable name generated by GCC?

By default, gcc assumes that you want to create an executable program called a.exe.

What is GCC executable?

It 'translates' the programming languages to machine language. Or to put it in another way, it converts our source code to executable instruction file for computers. GCC stands for “GNU Compiler Collection”. GCC is an integrated distribution of compilers for several major programming languages.


1 Answers

I just tried this in x86_64 Linux, which probably isn't much different to MinGW at this level, although you never know.

Basically, the problem is that, even though nothing gets pulled in from the C library unless it's referenced, the CRT "startfiles" do reference a small selection of things, which in turn reference some other things, and "Hello world" ends up looking bad. This is not a problem worth fixing because all real programs would reference those core functions anyway.

The source for the start files is available, and quite small, and the compiler allows you to override the standard ones if you choose to, so optimizing them is not a massive deal. They're written in assembler code, but you can probably remove most of the extraneous garbage by simply deleting lines.

But, there's a hack for cutting the start-files out of the equation altogether:

#include <unistd.h>

void _start (void) {
  write(1,"Hello world!", 12);
  _exit(0);
}

Compile: gcc -nostartfiles t.c -s -static

Which works (by chance, see below), and gives me a file size of 1792 bytes.

For comparison, your original codes gives 738624 bytes, with the same compiler, which drops to 4400 bytes when I remove -static, but then that's cheating! (My code actually gets larger without -static, because the dynamic linker meta-data outweighs to code of write and _exit).

The by chance part, is that the program now has no stack pointer initialized. Likewise for all other global state the start-files usually take care of. As it happens, on x86_64 Linux, this isn't a fatal problem (just don't do it in production, right?) However, when I tried it with -m32 I get a segmentation fault inside write.

The problem can be fixed by adding your own initialization for that stuff, but then the code would no longer be as portable (it isn't absolutely portable already). Alternatively, call the write system call directly.

like image 145
ams Avatar answered Sep 26 '22 08:09

ams