diff --git a/articles/tiny-elf/page.mmd b/articles/tiny-elf/page.mmd new file mode 100644 index 0000000..1e07737 --- /dev/null +++ b/articles/tiny-elf/page.mmd @@ -0,0 +1,127 @@ +Title: Slim Summer Elf +Brief: Making of minimal x86 (Linux) ELF executable. +Date: 1684666702 +Tags: Programming, Linux, C +CSS: /style.css + +Code below was composed for [4mb-jam](https://itch.io/jam/4mb-jam-2023) which I didn't finish. + +It was tested on `Zorin OS 16.2 x86_64`, but should be compatible with any x86/i386 +operating system using ELF executable format (besides the system call part) + +What's going on: +* Amalgamation of shell script and C source, it's possible because of them both having +`#if` style directives and similar comment syntax. Also note the usage of shebang starting with `/`. +* ELF header is created via `GNU Assembler`. +* Custom link script allows for drop of unnecessary data produced by `GNU Linker` by default. There's *a lot* of it it turns out. +* One can pass a `DEBUG` argument when compiling so that debug symbols would be generated. + +### Source ### + +```c +//bin/sh +#if 0 +set -e +cat << ELF_HEADER_SOURCE > ./elf-header.as +elfoff = 0x08048000 +vaddr = 0x08048000 +.text +ehdrstart: + .byte 0x7F # e_ident + .ascii "ELF" + .skip 3, 1 + .skip 9, 0 + .word 2 # e_type + .word 3 # e_machine + .long 1 # e_version + .long entry # e_entry + .long phdrstart - elfoff # e_phoff + .long 0 # e_shoff + .long 0 # e_flags + .word ehdrsize # e_ehsize + .word 32 # e_phentsize + .word 1 # e_phnum + .skip 6, 0 +ehdrsize = . - ehdrstart + +phdrstart: +#PT_LOAD + .long 1 # p_type + .long 0 # p_offset + .long vaddr # p_vaddr + .long 0 # p_paddr + .long filesz # p_filesz + .long memsz # p_memsz + .long 7 # p_flags + .long 0x0000 # p_align +ELF_HEADER_SOURCE + +cat << LINKER_SCRIPT_SOURCE > ./ld.scr +SECTIONS { + . = 0x08048000; + filestart = .; + + .elf : { ./elf-header.o (.text) } + .text ALIGN(0x1) : SUBALIGN(0x1) { *(.text*) *(.rodata*) } + .data ALIGN(0x1) : SUBALIGN(0x1) { *(.data*) } + + filesz = . - filestart; + .bss : { *(.bss*) } + memsz = . - filestart; + + /DISCARD/ : { + *(.note.*) + *(.gnu*) + *(.gcc*) + *(.comment) + *(.eh_frame*) + } +} +OUTPUT_FORMAT(binary) +LINKER_SCRIPT_SOURCE + +CFLAGS="-x c -std=gnu99 $0 -m32 -nostdlib -fno-pie -DELF -Wall -Wextra -Wpedantic -Werror" +LDFLAGS="-m elf_i386" + +if [ ! -z "$1" ] && [ $1 = DEBUG ]; +then + CFLAGS="$CFLAGS -O0 -g3 -ggdb -DDEBUG" + LDFLAGS="$LDFLAGS -eentry" +else + CFLAGS="$CFLAGS -Os" + LDFLAGS="$LDFLAGS -T ld.scr -s" +fi + +as --32 -o elf-header.o elf-header.as +cc -c -o ./elf.o $CFLAGS +ld -o elf ./elf-header.o ./elf.o $LDFLAGS +./elf +exit; +#endif + +#ifdef ELF + +/* https://github.com/Jorengarenar/CMObALL/blob/master/cmoball.h */ + +/* https://man7.org/linux/man-pages/man2/exit.2.html */ +#define SYS_EXIT(p_return_code) \ + { \ + asm volatile("int $0x80" : : "a"(1), "b"(p_return_code)); \ + __builtin_unreachable(); \ + } + +/* https://man7.org/linux/man-pages/man2/write.2.html */ +#define SYS_WRITE(p_fd, p_msg, p_msg_len) \ + asm volatile("int $0x80" \ + : "=a"(sys_result) \ + : "a"(4), "b"(p_fd), "c"(p_msg), "d"(p_msg_len)) + +__attribute((naked)) void entry(void) { + static int sys_result; + SYS_WRITE(1, "hello world!\n", 13); + SYS_EXIT(0); +} + +#endif /* #ifdef ELF */ + +```