Chastity’s Source for ELF 32-bit executable creation

I wrote an example program using a custom made ELF-32 header using data declaration statements. I wrote many comments which reference the official specification. This was an exercise both in programming and also Technical Reading and Writing. I had to read enough of the specification PDF file to understand what I was doing. I then tried to write descriptive comments that at the very least I would understand when I need to remind myself how this format is created.

;Chastity's Source for ELF 32-bit executable creation
;
;All data as defined in this file is based off of the specification of the ELF file format.
;I first looked at the type of file created by FASM's "format ELF executable" directive.
;It is great that FASM can create an executable file automatically. (Thanks Tomasz Grysztar, you are a true warrior!)
;However, I wanted to understand the format for theoretical use in other assemblers like NASM.

;The Github repository with the spec I used is here.
;<https://github.com/xinuos/gabi>
;And this is the wikipedia article which linked me to the specification document
;<https://en.wikipedia.org/wiki/Executable_and_Linkable_Format>

;This file contains a raw binary ELF32 header created using db,dw,dd commands.
;After that, it proceeds to assemble a real "Hello World!" program

;Header for 32 bit ELF executable (with comments based on specification)

db 0x7F,"ELF" ;ELFMAGIC: 4 bytes that identify this as an ELF file. The magic numbers you could say.
db 1          ;EI_CLASS: 1=32-bit 2=64-bit
db 1          ;EI_DATA: The endianness of the data. 1=ELFDATA2LSB 2=ELFDATA2MSB For Intel x86 this is always 1 as far as I know.
db 1          ;EI_VERSION: 1=EV_CURRENT (ELF identity version 1) (which is current at time of specification Version 4.2 I was using)
db 9 dup 0    ;padding zeros to bring us to address 0x10
dw 2          ;e_type: 2=ET_EXEC (executable instead of object file)
dw 3          ;e_machine : 3=EM_386 (Intel 80386)
dd 1          ;e_version: 1=EV_CURRENT (ELF object file version.)

p_vaddr=0x8048000
e_entry=0x8048054 ;we will be reusing this constant later 

dd e_entry    ;e_entry: the virtual address at which the program starts
dd 0x34       ;e_phoff: where in the file the program header offset is
db 8 dup 0    ;e_shoff and e_flags are unused in this example,therefore all zeros
dw 0x34       ;e_ehsize: size of the ELF header
dw 0x20       ;e_phentsize: size of program header which happens after ELF header
dw 1          ;e_phnum: How many program headers. Only 1 in this case
dw 0x28       ;e_shentsize: Size of a section header
dw 0          ;e_shnum number of section headers
dw 0          ;e_shstrndx: section header string index (not used here)

;That is the end of the 0x34 byte (52 bytes decimal) ELF header. Sadly, this is not the end and a program header is also required (what drunk person made this format?)

dd 1          ;p_type: 1=PT_LOAD
dd 0          ;p_offset: Base address from file (zero)
dd p_vaddr    ;p_vaddr: Virtual address in memory where the file will be.
dd p_vaddr    ;p_paddr: Physical address. Same as previous

image_size=0x1000 ;Chosen size for file and memory size. At minimum this must be as big as the actual binary file (code after header included)
                  ;By choosing a default size of 0x1000, I am assuming all assembly programs I write will be less than 4 kilobytes

dd image_size  ;p_filesz: Size of file image of the segment. Must be equal to the file size or greater
dd image_size  ;p_memsz: Size of memory image of the segment, which may be equal to or greater than file image.

dd 7           ;p_flags: permission flags: 7=4(Read)+2(Write)+1(Execute)
dd 0           ;p_align; Alignment (none)

;important FASM directives
use32          ;tell assembler that 32 bit code is being used
org e_entry    ;origin of new code begins at the entry point

;now, the actual hello world program
mov eax,4      ;invoke SYS_WRITE (kernel opcode 4 on 32 bit systems)
mov ebx,1      ;write to the STDOUT file
mov ecx,msg    ;pointer/address of string to write
mov edx,13     ;number of bytes to write
int 80h

mov eax,1 ;function SYS_EXIT (kernel opcode 1 on 32 bit systems)
mov ebx,0 ;return 0 status on exit - 'No Errors'
int 80h   ;call Linux kernel with interrupt

msg db 'Hello World!',0Ah

;This is the makefile I use when assembling and running this program

;main-fasm:
;	fasm ELF-32-hello.asm
;	chmod +x ELF-32-hello.bin
;	./ELF-32-hello.bin

I made a repository for examples like this. Others may want to understand the header used on Linux systems.

https://github.com/chastitywhiterose/ELF

Comments

Leave a comment