Executing a flat binary file under Linux

The Linux kernel can load several different binary formats - ELF is just the most common, though the a.out format is also pretty well known.

The supported binary formats are controlled by which binfmt modules are loaded or compiled in to the kernel (they're under the Filesystem section of the kernel config). There's a binfmt_flat for uClinux BFLT flat format binaries which are pretty minimal - they can even be zlib compressed which will let you make your binary even smaller, so this could be a good choice.

It doesn't look like nasm natively supports this format, but it's pretty easy to add the necessary header manually as Jim Lewis describes for ELF. There's a description of the format here.


Is there some reason you don't want to use "-f elf" instead of "-f bin"?

I think Linux won't run a binary that's not in ELF format. I can't find a tool that converts flat binaries to ELF, but you can cheat by putting the ELF information in foo.asm, using the technique described here :

We can look at the ELF specification, and /usr/include/linux/elf.h, and executables created by the standard tools, to figure out what our empty ELF executable should look like. But, if you're the impatient type, you can just use the one I've supplied here:

 BITS 32

               org     0x08048000

 ehdr:                                                 ; Elf32_Ehdr
               db      0x7F, "ELF", 1, 1, 1, 0         ;   e_ident
       times 8 db      0
               dw      2                               ;   e_type
               dw      3                               ;   e_machine
               dd      1                               ;   e_version
               dd      _start                          ;   e_entry
               dd      phdr - $$                       ;   e_phoff
               dd      0                               ;   e_shoff
               dd      0                               ;   e_flags
               dw      ehdrsize                        ;   e_ehsize
               dw      phdrsize                        ;   e_phentsize
               dw      1                               ;   e_phnum
               dw      0                               ;   e_shentsize
               dw      0                               ;   e_shnum
               dw      0                               ;   e_shstrndx

 ehdrsize      equ     $ - ehdr

 phdr:                                                 ; Elf32_Phdr
               dd      1                               ;   p_type
               dd      0                               ;   p_offset
               dd      $$                              ;   p_vaddr
               dd      $$                              ;   p_paddr
               dd      filesize                        ;   p_filesz
               dd      filesize                        ;   p_memsz
               dd      5                               ;   p_flags
               dd      0x1000                          ;   p_align

 phdrsize      equ     $ - phdr

 _start:

 ; your program here

  filesize      equ     $ - $$

This image contains an ELF header, identifying the file as an Intel 386 executable, with no section header table and a program header table containing one entry. Said entry instructs the program loader to load the entire file into memory (it's normal behavior for a program to include its ELF header and program header table in its memory image) starting at memory address 0x08048000 (which is the default address for executables to load), and to begin executing the code at _start, which appears immediately after the program header table. No .data segment, no .bss segment, no commentary — nothing but the bare necessities.

So, let's add in our little program:

 ; tiny.asm
               org     0x08048000

 ;
 ; (as above)
 ;

_start: mov bl, 42 xor eax, eax inc eax int 0x80 filesize equ $ - $$

and try it out:

 $ nasm -f bin -o a.out tiny.asm
 $ chmod +x a.out
 $ ./a.out ; echo $?
 42