Hello! First of all, let me link this: [GitHub - vishen/go-x64-executable: Generate ELF Linux 64-bit (x86-64) executable manually] repo which is a program written in the Go programming language that manually creates the smallest ELF executable (for linux). So, the program just creates an executable that writes a message to STDOUT and then exits. The size of the executable is `232` bytes!!!! The "tiny" executable only had the ".data" and ".text" segments so it is truly the smallest possible program.
Ok, it's known that the absolute minimal is not what we want in practical productive level so we would expect compilers to produce something less minimal and agree on a standard. So, how much bigger you would expect the equivalent executable to be? 3 times bigger? 5 times bigger? Well, let's see! I used the following code:
Code:
const char* msg = "Hello, world!\n";
void _start() {
asm(
"syscall"
:: "a" (1), "D" (1), "S" (msg), "d" (14)
);
asm(
"syscall"
:: "a" (60), "D" (0)
);
}
I compiled using "GCC" and "TCC" to have a comparison between different compilers. The commands I used to compile are the following:
GCC:
Code:
gcc -Ofast test.c -o test_gcc -nostdlib
TCC:
Code:
tcc test.c -o test_tcc -nostdlib
The results I got were pretty shocking for me! I used `du` with the 'b' flag to get the result in bytes. The results were the following:
```
14K test_gcc
5,1K test_tcc
232 go-x64-executable/tiny-x64
```
What's going on here????? The executable produced with GCC is 60.3 (!!!!) times bigger than the smallest possible program! That's crazy!!! The program produced with TCC is 20 times bigger. This is still a huge difference but funny enough, compared to GCC, this is not as crazy.
So again, what's going on here? What's all that data that are generated by the compilers? This may be very specific but still, I thought, I'll take my shot and ask in case anyone knows.
For comparison (and out of curiosity of course), I also tried to create an executable in assembly using "GAS" to compile and "GOLD" to link. The code is the following:
Code:
.global _start
.data
msg: .string "Hello, world!\n"
.text
_start:
mov $1, %eax
mov $1, %edi
mov $msg, %rsi
mov $14, %edx
syscall
mov $60, %eax
mov $0, %edi
syscall
and the command used to compile is:
Code:
as test.asm -o test.o && ld.gold test.o -o test_as && ./test_as
The size of the final executable is 1016 bytes. This is way more logical! Of course, in the case of using an assembler and linker, I would also expect the ability to only include the segments that you are writing (which I wasn't able to find how to do in the "man" pages) but that's another topic for another time and another place!