📄 readme
字号:
Tiny ProgramsThe programs listed here are provided as exhibitions of just how compresseda Linux ELF executable can be and still function.Take a look through /usr/bin: how big is the smallest ELF executable inthere? Now try writing the smallest hello-world program you can. Can youget it under 1K? Can you get it under 100 bytes?This is precisely what I set out to do one day, and I ended up with a minorobsession over creating the smallest possible executables.The distribution comes with preassembled binaries along with the assemblysource code. If you wish to be able to rebuild them yourself, you will needto have a copy of Nasm, the excellent x86 assembler. Nasm's home page is athttp://www.web-sites.co.uk/nasm/.___________________________________________________________________________ Some Introductory Blather Which You Should Feel Free to Skip if You Want to Get to the Good StuffYour average ELF executable, even after being stripped, contains a fair bitof overhead, some of it completely superfluous. And when you rip outeverything that doesn't contribute to a program's memory image, much ofwhat remains, beside the program code itself, is information needed by thedynamic linker. This is of course very important information to have,normally -- but if the program makes its own system calls, it can dispensewith that overhead as well.Of course, there are few tools out there that will create an executablethat has none of this extra machinery. But if one is willing to define thefile entirely by hand, an executable can be created with the absoluteminimum contents, including only the information that Linux needs in orderto get it into memory and start it running.The minimum information an ELF executable file needs to have is an ELFheader and a program header table with at least one entry. (For moreinformation about these entities, consult the ELF standard. You candownload a copy of it as a Postscript document fromftp://tsx.mit.edu/pub/linux/packages/GCC/ELF.doc.tar.gz, or from theequivalent location of any repository containing GNU's gcc package forLinux. Or, you can also retrieve a transcription of the document as aflat-text file from http://www.muppetlabs.com/~breadbox/software/ELF.txt.)But, even in these two remaining structures, there are fields that aren'tused, particularly with a bare-bones executable. Furthermore, there areother fields in these structures which do serve a purpose, but which arenot, at this point in time, actually examined by the Linux OS. So, thesefields can be used, if the programmer is desperate enough, to hold otherbits of data, or even small amounts of code....Let me pause for a moment and make this perfectly clear: I emphatically donot advocate the wilfull, widespread disregard of standards. Certainly,just because the ELF standard says that a given field will hold a certainvalue does not automatically mean that Linux ought to examine that fieldand reject any ELF file with something else there. But by the same token,just because (the current version of) Linux ignores the contents of thatfield does NOT give programmers implicit permission to fill it in withanything they like. When programmers en masse adhere to existingimplementations instead of existing standards is exactly when awell-written standard becomes an obstacle instead of a tool, when futurestandards are forced to canonize ludicrous practices in the name ofbackward-compatibility, and when everything generally goes to hell. So:where these programs violate requirements of the ELF standard, do not takethem seriously. These programs have been offered up as entertainment, andperhaps as an education in existing practice. But please do not seek toemulate such behavior in other, more serious programming work.In fact, not every program here is portable among all the 2.2 kernels.Beginning with 2.2.17 (?), Linux began verifying one of fields (namelye_phentsize) in the ELF header. This field is ignored in prior 2.2 kernels,and also in the 2.4 kernel. I'm unclear as to reason for the divergence,but that is the risk one takes when ignoring standards. In order toaccommodate users who are running the pickier kernels, I have suppliedalternate versions of the offending programs. Since these are writtenspecifically for later 2.2 kernels, I have managed to keep them at theiroriginal size by taking advantage of yet another assumption not guaranteedto be portable -- namely, that all the general registers are initialized tozero on program startup. (Not only is this not guaranteed, it is untrue for2.0 kernels, where only edx is consistently cleared.)Of course, violating requirements of the ELF standard is only one way inwhich these programs achieve such compression. Much else regarding theorganization of the ELF structures in these programs is admittedlyunorthodox, but nonetheless in complete adherence to the standard. Mostimportant of all, of course, is the creative reworking of algorithms.Finally, as with any CISC assembly programming, there are also plenty ofuncommon instructions used to accomplish common tasks. (This can be anespecially fruitful avenue of exploration on the Intel x86 as so many ofthe short instructions date back to the 8086, or worse, and are usuallyconsidered to be completely obsolescent, as Intel has made no attempt tokeep their timings abreast of the rest of the instruction set. These days,one usually programs in assembly only in order to optimize the runningtime, in which case it is very important to stick with the core instructionset as much as possible. But when optimizing for space instead of speed,one suddenly finds new uses for all those obscure one- and two-byteinstructions.)The path of optimizing an ELF executable for size is described in my essay"A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux",at http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html.___________________________________________________________________________true.asmThis program returns an exit code of either zero or one, depending onwhether it is invoked with the name "true" or "false", respectively. Thisone is the runt of the litter. Its size is 45 (count them) bytes. I believethat this is the smallest it is possible for a Linux ELF executable to be.hello.asmhello-2.2.17.asmThe final version of the program that started me off on this whole pursuit:hello world. It is 59 bytes long. This may well be the densest one here.With the program header table overlaid on top of the ELF header, andprogram itself running through both of them, some of the bytes have no lessthan three completely different purposes.fgconsole.asmfgconsole-2.2.17.asmfgconsole was the smallest ELF executable in my old /usr/bin directory(2640 bytes), so I decided to see how small my own version could be. Imanaged to fit it into 72 bytes. fgconsole simply prints the number of thecurrently active virtual console. If run from somewhere other than theconsole, it returns a non-zero exit code.keepalive.asmkeepalive-2.2.17.asmHere's a program that I actually use, quite frequently in fact. It simplyruns forever, printing a bell character every five minutes or so. I keep itin the background after telnetting from another machine that times outconnections after 15 minutes of idle time. At 57 bytes, it's a bit longerthan the one-line shell script I originally used, but on the plus side itdoesn't take up an extra process in order to sleep.bf.asmBrainfuck is a very simplistic programming language, which was invented byUrban Mueller solely for the purpose of being able to create a compilerthat was less than 256 bytes in size, for the Amiga OS. (More informationabout Brainfuck can be found at http://www.muppetlabs.com/~breadbox/bf/.) Ieventually decided to take up the challenge as well, and create a Brainfuckcompiler for Linux using less than 256 bytes. The compiler works as afilter: redirect the source code to the compiler's input and the compiler'soutput to a file. (And don't forget to chmod +x the output file.) At thebeginning I didn't expect that I would actually succeed, since I thought Iwould need almost half of that just for the headers. I think my first cutwas 458 bytes in size. I was quite pleased with myself when I trimmed thatdown to less than 300 bytes, ecstatic when I finally reached the 255-bytegoal, and downright triumphant when I later got it to work in 238 bytes. Bythe time I had a 198-byte version, I was just plain dumbfounded. It's nowat 171 bytes. And though I can't quite believe it, it works just as well asthe first one did. As useless as it is, I think this one would have to bethe crown jewel of this little collection. It is quite possibly also theugliest and most opaque program I have ever written.hexdump.asmThis is your classic hexdump program. I wrote it one day in a fit of pique,after trying (and failing) to convince the standard Linux hexdump programto use the canonical hexdump formatting style. This program, and the onesthat follow, are different from the previous ones in that these adhere 100%to the requirements of the ELF standard. The reason I did this is because,unlike the previous programs, these can actually accomplish somethinguseful. I wanted to be able to keep them around and know that they wouldcontinue to work. This one is actually larger than earlier versions. Thefirst version only read from stdin, but I found myself using this programoften enough to be annoyed by this limitation, so I added command-lineparsing. As a result of these extra goals, hexdump is a whopping 232 bytes.ls.asmAfter hexdump, I came into contact with Konstantin Boldyshev, who heads theasumutils project (you can visit the asmutils home page athttp://www.linuxassembly.org/asmutils.html), and I got involved in creatingtiny programs that were actually useful. ls is my first real achievementalong these lines. It is 1017 bytes in size, and sports many of theimportant command-line options:-C columnar output; default if stdout is a tty-1 one file per output line; default if stdout is not a tty-l "long" output-F show file type suffixes-i show inode numbers-s show size of files in 1K-blocks-d don't expand directories-a show all (hidden) files-B don't show files ending in ~ (backups)-N don't replace non-graphic characters with a question mark-b show non-graphic characters as octal escape sequences-R recursively list contents of subdirectories(I'm especially proud of getting that last feature to work.)The "long" output format only displays numeric user IDs, and it displaystimestamps as an age, instead of the actual timestamp. There is no sortingcapability, and the columnar output is much less intelligent than GNU's(though it looks pretty good most of the time). Beyond that, however, itconforms pretty closely to the standard ls program we all know and love.date.asmdate was my the next creation after ls for the asmutils project. (Note thatthe programs here are slightly different from the ones in asmutils. Theasmutils programs are portable to other x86-based OSes, such as BSD. Theversions you see here are strictly for Linux, and this allows me to squeezeout a couple more bytes here and there.) This version of date doesn't haveclock-setting capabilities; it just does date output formatting. It honorsthe local time zone, if set, as well as the LANG environment variable. Italso contains its own default locale if LANG isn't set, which makes it abit large -- 1448 bytes, to be precise.factor.asmThis is an implementation of the standard Unix program. It displays theprime factors of the integers specified on the command line, or on standardinput if no arguments are given. This program is the oddball of thiscollection. Instead of simply trying to achieve the smallest possible size,I decided to shoot for real portability, for a change. This program notonly conforms to the requirements of the ELF standard, it is also the onlyprogram in this collection that actually uses functions from libc (asopposed to making its own system calls). Thus it should continue to workwith any future version of Linux, as long as new versions of libc and theELF standard remain backwards-compatible. In addition, I decided to try tobalance optimizing for both size and speed. This program takes advantage ofthe fact that floating-point division can be done in parallel with integerinstructions on all Pentium processors. As a result it is significantlyfaster than the GNU implementation of factor for Linux. Finally, I addedonline help and an appropriate error message for invalid input. Theresulting program therefore arguably stands as a completely functionalreplacement for the existing utility. Its size is 896 bytes.snake.asmBy now you're probably thinking: "Okay, the first couple of programs wereimpressive, but now it's just repetitive. Isn't there anything interestingin here?" Well, here's something to reward you for reading all the way tothe end: a game! Snake is a classic computer game -- in fact it's so oldthat you can hardly find it anymore in its original form. I originallywrote this as an Easter Egg for a contract involving text-terminalinterfaces. Now it's available to all as a tiny program. In case you'venever played it, the object of the game is to guide your snake around theplaying field, eating the food blocks that appear at random locations, anddodging the walls and your own tail. As you eat the blocks, your scoreincreases but so does your length. The program should work on anyVT100-compatible terminal (although see the program comments regarding theline-drawing character set -- not all Linux console fonts provide correctsupport for them). Plus, at 1496 bytes, it's the perfect addition to arescue floppy ... when you've just lost all your data, what's better than agame to take your mind off your troubles?
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -