Linux i386

Here's a small collection of standalone programs written in i386 assembly language with GAS syntax for Linux. They are written to be statically linked programs: in particular, they don't use libc either shared or statically. (In fact, none of them so far use any external libraries.)

/bin/arch (okay)
source code   (needs to link writestr.s also)
/bin/basename (okay)
source code   (needs to link writestr.s also)
/bin/cat (okay)
source code  
/bin/cmp (okay)
source code  
/bin/dirname (okay)
source code   (needs to link writestr.s also)
/bin/dmesg (okay)
source code  
/bin/echo (okay)
source code   (needs to link writestr.s also)
/bin/false (okay)
source code  
/bin/ls
source code  
/bin/printenv (does not support printing out individual environmental variables, just bulk listing)
source code   (needs to link writestr.s also)
/bin/sleep
source code  
/sbin/sync
source code
/sbin/syncd
source code
/bin/true (okay)
source code  

How to make for, say, /bin/echo:

  cpp writestr.s writestr-cpp.s
  cpp echo.s echo-cpp.s
  as --32 -o writestr.o writestr-cpp.s
  as --32 -o echo.o echo-cpp.s
  ld -m elf_i386 -e echo -o -static -o echo echo.o writestr.o
Please note:

There is a makefile here using the same general idea, although it actually does things a little differently. The Makefile creates files called FILE.exe as a standalone version of FILE.s, and also creates a wrapper called driver.exe with all of the individual program code linked. driver.exe then expects to be called via a soft-link FILE --> driver.exe, much like sendmail or BusyBox.

If you take the next step of using strip over the resulting binaries, they are quite small (I believe that all of the standalone versions are 1k or less in size; using the driver.exe approach, these all link together in a stripped binary at 2472 bytes currently.)

These programs aren't particularly clean or elegant; echo for instance calls write(2) many times when it would probably be more reasonable to call write(2) once. Block comparisons and moves are done in a less efficient manner than the x86_64 supports.

It's interesting to do an strace of one of these binaries and a standard library; there are no dependencies on other programs at all, and while the programs, as noted previously, are not elegant, they do tend to do whatever they need to and not much else.

For the programs that use writestr.s to call write(2), the code in writestr.s should loop through on any incomplete write(2), but is not vetted yet. (This is also true of dmesg.s.)

A few places to look for Linux GAS 386/x86-64 information

For 386/x86-64 specific information, you can look at this section:

    Red Hat Enterprise Linux 3: Using as, the Gnu Assembler: Chapter 21. 80386 Dependent Features

For an overall view of using GAS:

    Using as

Demonstrating text segments:

data-text-segment-test-1.asm

data-text-segment-test-2.asm

data-text-segment-test-2.exe

data-text-segment-test.asm

data-text-segment-test.asm

demonstrate-segments-1.asm

Makefile-data-text-segment-test

Makefile-demonstrate-segments