Golf with an elf

I came across this page about making the smallest possible executable binary file using Linux’s ELF executable format. Supposedly with judicious use of absolutely evil hacks, you can get the file down to a scant 45 bytes — which is the size of the ELF header.

Or at least, you could on older kernels. On kryten’s slightly dusty 2.6.18 kernel, the two smallest (and evilest) versions of the program crash, but the antepenultimate one, the 64 byte one, works fine. Which is still quite impressive.

Note that I said 45 (or maybe 64) bytes is the smallest executable binary program you can execute successfully. If you switch over to a shell script, you can get quite a bit shorter. In fact, an empty file will execute successfully:

$ touch empty
$ chmod +x empty
$ ls -l empty
-rwxr-xr-x 1 paul paul 0 2008-03-17 22:32 empty
$ ./empty
$ echo $?
0

It’s the same basic functionality as /bin/true, which on kryten, weighs in at 22,120 bytes.

9 Responses

  1. For those of us following along at home…what is this? What’s being reduced to a fraction of its former self?

    What does it do in a ton less lines of code?

  2. I thought POSIX mandated that non-executable files at least have a hashbang in them. So if you want to be absolutely proper, the minimal file is:

    #!/bin/[

    which is 9 bytes. (counting the newline)

    Basically, what you’ve made isn’t a self-contained shell script but something that your shell is interpreting as a script anyway (which is probably a bashism, though I seem to recall that tcsh allows that too).

    What’s the minimal size for a.out format? (does Linux even support a.out anymore?)

  3. er, by non-executable I meant to say non-binary executable.

  4. Ryan: The program, like /bin/true, does nothing, successfully. Compare /bin/false, which does nothing, unsuccessfully.

    fluffy: Portability goes out the window when playing golf. Also, on Debian [ is in /usr/bin, not /bin. Does POSIX specify where [ has to be?

    Executing an empty file as a shell script would be a Linuxism if anything, since it’s the kernel that decides what shell, if any, needs to be invoked. I believe the shell itself ignores the content of the shebang line.

    Running the empty script is noticably slower than /bin/true or a shell script with only “#!/bin/sh” in it, so the kernel must be doing a lot of extra work to handle the empty file case. Maybe it’s something to do with binfmt_misc.

    Linux still supports a.out, at least as a module; my /boot/config has “CONFIG_BINFMT_AOUT=m”. Good luck finding an a.out to try it with, though.

  5. So if it does nothing, successfully, what is the point? Is it part of a bigger program that does do something useful? Or is it basically trying to reduce the code required to run a certain program [successfully], even if it does nothing useful?

  6. Ryan, that’s a good question, and it’s one I’ll answer in a top-level post, since a good answer deserves a bit more room than I have in a comment.

  7. The shell does not ignore the content of the shebang. In fact there were some very well-known bugs with older version of tcsh where they would attempt to execute a bash script as tcsh instead under certain circumstances.

    In order to isolate shell-specific behavior, try this code:

    int main(int argc, char *argv[])
    {
    execl(argv[1], argv[1], NULL);
    perror(“execl”);
    return 1;
    }

    and then run it with your zero-byte executable file, and see what happens. (I’m curious. I don’t have Linux handy right now, but on OSX it gives “exec format error.”

  8. Linux does the same thing: “Exec format error”. Changing the call from execl to execlp works, though. Looking up the man page for exec reveals this paragraph about execlp and execvp:

    If the header of a file isn’t recognized (the attempted execve(2)
    returned ENOEXEC), these functions will execute the shell (/bin/sh)
    with the path of the file as its first argument. (If this attempt
    fails, no further searching is done.)

    And, lo and behold, strerror(ENOEXEC) returns “Exec format error”. So the empty-file-works-as-shell-script trick should work on any POSIX system.

  9. Which means it’s relying on behavior of libc and the shell, not of the kernel. So that gets into philosophical territory as to whether that could really be considered a valid program.

Comments are closed.