HOWTO: 4k intros in GNU/Linux

A short tutorial by Timo Wiren (glaze/biomassa^wAMMA)

Revision history
Revision 1.0.5 2019-09-08 Used LZMA instead of 7z, fixed some stuff to work on modern systems.
Revision 1.0.4 2008-02-25 Used 7z instead of gzip, -O1 instead of -Os and a few other things.
Revision 1.0.3 2006-09-24 Now using /tmp directory, little additions here and there. Marq: My last name is not Wigren ;-)
Revision 1.0.2 2006-04-28 Cleanup.
Revision 1.0.1 2005-08-14 Added a new URL and basic tips. Cleanup.

Contents

1. About the author and this howto
2. Basic size optimization
3. Compressing the object code
4. Example code
5. Links to external websites

1. About the author and this howto

I'm a graphics programmer working in the games industry in Finland. I have been programming since 1998 and using GNU/Linux since 2001. I mostly develop graphics code, 2D and 3D, and use C and C++. I've been interested in demoscene since 1999, but I have released only few crappy half-done productions.

This howto does not cover all areas of size optimization. It mostly covers the usage of standard tools to produce smaller code. 32-bit OS, GCC, the C language and SDL 1.2 is used in this howto.

2. Basic size optimization

These are pretty basic things, but it doesn't hurt to mention them here:

3. Compressing the object code

We can compress the binary and add decompression script in front of it. To compress, use gzip --best -f program or (better) xz -c6 --format=lzma program >program.xz
Save the following decompression script to a file unpack.header:
a=/tmp/I;tail -n+2 $0|unxz>$a;chmod +x $a;$a;rm $a;exit
Now you can put the decompression script to the beginning of file and your program after it:

cat unpack.header program.gz > program

Remember to make the resulting file executable: chmod +x program.

Of course, there are more things you can do. LibC inserts standard and bloaty code by default, so rename main() to _start(), and put the following inline assembly code to the end of main function:

asm ( \
"movl $1,%eax\n" \
"xor %ebx,%ebx\n" \
"int $128\n" \
);

This ends the program without LibC code. Now you must compile it like this:

gcc -Os -fomit-frame-pointer -c program.c
ld -dynamic-linker /lib/ld-linux.so.2 program.o /usr/lib/i386-linux-gnu/libSDL.so -o program

Now you should be able to do 4k intros and the rest is up to smart algorithms and reuse of code and data. Of course there are many other small tricks that were not covered in this howto, but I'll add them when I remember those or find new ones.

4. Example code

XOR texture scroller (s.c):

/* XOR texture scroller begins */
#include <SDL/SDL.h>

void _start() {
    int* v, x, y, w = 320, h = 240, i = 0;

    SDL_Surface* b;
    SDL_Event e;

    b = SDL_SetVideoMode(w, h, 32, SDL_HWSURFACE | SDL_DOUBLEBUF | SDL_HWACCEL);    
    v = b->pixels;
  
    do {
        SDL_PollEvent(&e);

        for (y = 0; y < h; ++y) 
            for (x = 0; x < w; ++x) 
                v[y * w + x] = (x+i) ^ y;
    
        SDL_Flip(b);
        ++i;
    } while (e.type != SDL_KEYDOWN);
  
    SDL_Quit();

    asm ( \
      "movl $1,%eax\n" \
      "xor %ebx,%ebx\n" \
      "int $128\n" \
    );
}
/* XOR texture scroller ends */

I was able to make the scroller binary size 694 bytes. I also have programmed a simple ray tracer under 2k.

4.1 Example compilation and compression script:

gcc -O1 -ffast-math -fomit-frame-pointer -fno-plt -fno-stack-protector -c main.c
ld -dynamic-linker /lib/ld-linux.so.2 main.o /usr/lib/i386-linux-gnu/libSDL.so -o main
strip -s -R .comment -R .gnu.version main
./sstrip -z main
xz -c6 --format=lzma main >main.xz
cat unpack.header main.xz > main
chmod a+x main
rm main.xz
wc -c main

5. Links to external websites

in4k, information about 4k coding on various platforms.
A Whirlwind Tutorial on Creating Really Teensy ELF Executables for Linux
Kickers of ELF, contains sstrip.
GC Masher, a tool that automagically tests gcc options for size optimization.

Valid XHTML 1.0 Strict