The smallest ELF executable (2021)

An exploration into creating the smallest possible 64-bit ELF executable for modern Linux by stripping away metadata and optimizing assembly instructions.
Quick edit: While I work on a proper update, I'll note here that several posters on reddit and hackernews have pointed out ways to bring the total size of the program down to 105 bytes while still printing the full "Hello, world!" Much credit goes to Josh Triplett, who produced the 105-byte version which you can find here. I fully plan to include this in an updated version of the page!
Many years ago, I came across this famous article, which I largely credit changing the trajectory of my career. At the time, I was an intern working on a the build system for a fairly large Java code base, so I was particularly susceptible to an article attempting to do the polar opposite of "enterprise Java:" strip away all but the most essential components required to define a valid Linux program.
In short, the article walks through the creation of a 45 byte (!) Linux binary. While the resulting binary is arguably not an entirely "valid" ELF file, it was at least one that Linux could run. Perhaps unfortunately, Linux has gotten more strict about ELF loading since the article's original publication, and the migration of many systems to 64-bit CPUs has rendered the older 32-bit ELF binary less relevant.
Like the article I take for inspiration, I set out to create the smallest ELF file that runs on modern Linux (kernel 5.14 at the time of writing). I will use the nasm assembler, since it is easy to install and remains one of the best x86 assemblers available.
ELF files are used everywhere in Linux and typically contain an ELF header, a program header table, and a section header table. The ELF format has remarkably few hard requirements on where the various pieces of metadata appear in the file, apart from the fact that the top-level ELF header must appear at the beginning.
My initial version was a "proper" ELF file of 383 bytes, containing a list of sections including .text and .shstrtab. By removing section information (setting the number of section headers to 0), the size dropped to 173 bytes. While utilities like objdump now have trouble finding the code, the binary still runs perfectly. The next step involves shrinking the x86 bytecode itself by replacing instructions with shorter equivalents.
Source: Hacker News












