This blog post gives a short executable file size comparison when the same statically linked, i386 ELF executable was compiled with various small (tiny) libc implementations for Linux.
TL;DR diet libc is producing the smallest executables.
Compiler used: GCC 4.6.3 in Ubuntu Precise.
libc implementations used:
- uClibc 0.9.30.1; compilation script
- diet libc 0.33; compilation script
- musl 0.9.15; compilation script
- dynamic: eglibc 2.15-0ubuntu10.5, dynamically linked executable; compilation script
- static: eglibc 2.15-0ubuntu10.5, statically linked executable; compilation script
All file sizes are the size of statically linked, Linux i386 ELF, stripped executable, except for source file (where it is the size of a .c source file) and dynamic (where it is the size of a dynamic executable of the same kind).
The source file size reducing compiler flags and tricks in this blog post were used. The programs used dynamic memory allocation (malloc(3), free(3), realloc(3)), system call I/O (e.g. read(2) and write(2)), but none of the printf*(3) functions or stdio.
Compilation results for clang_trampoline.c:
- source file: 37889 bytes
- diet libc: 15176 bytes
- dynamic: 17644 bytes
- musl: 22420 bytes
- uClibc: 22580 bytes
- static: 709120 bytes
Compliation results for xstatic.c:
- source file: 30410 bytes
- diet libc: 12316 bytes
- dynamic: 13516 bytes
- musl: 18992 bytes
- uClibc: 19412 bytes
- static: 705024 bytes
Interesting observation: the diet libc version is smaller than the dynamic version. That's because linking against dynamic shared libraries has its own overhead (e.g. symbol table, PLT) in the executable.