Napiši najmanjši možen program, ki izpiše vsaj 1 zaslon pesmice "Živel je mož, imel je psa, lepo ga je učil. Nekoč mu ukrade kos mesa, zato ga je ubil. Na grob napiše mu:"Živel je mož, imel je psa .... pod OS Linux ali BSD.
Če napišemo preprost program v C-ju, s print funkcijo je program, vsaj na mojem sistemu velik 8520 bajtov. Če se pri prevajanju znebimo executabla smo program oklestili za dobri 2000 bajtov oz. na natan;no 6248 bajtov. Ker je to skoraj največ kar lahko dosežemo v C-ju se presilimo na asembler.
Prvi poskus v asemblerju nam vrne kaj klavrne rezultate, saj je program velik 6376 bajtov. Vendar če program napišemo malo drugače, tako da ne uporabljamo knjižnic, ampak samo sistemske klice takoj pridemo na 800 bajtov, kar je pa že, kar precejšen napredek. Vendar ali gre še bolje?
ELF (Executable and Linking Format) definira binarni format izvršilne datoteke datotek v Linuxu. Ko pokličeš izvršilno datoteko, mora OS vedeti, kako program pravilno naložiti v spomin, znati mora povezati dinamične knjižnice in vedeti, kje aj začne izvajati program. Vsaka ELF datoteka se začne s strukturo, ki ji rečemo ELF header. ELF header je dolg 52 bajtov in vsebuje informacije, ki opisujejo datoteko. Torej tu se skriva odgovor na naše vprašanje ali gre še bolje. Če ELF header prilagodimo svojim potrebam in odstranimo, kar je za nas nepomembno in nepotrebno lahko naredimo program še manjši.
0x7F ELF je "magic number", na podlagi katere lahko OS identificira da gre za ELF datoteko. 2 - 64 bitov, 1 - little endian, 1 - original ELF, 0 - ni važno na kateri platformi poganjamo.
ehdr: db 0x7F, "ELF", 2, 1, 1, 0 ; e_ident |
e_type 2 nam predstavlja d agre za executable datoteko.
e_machine 0x3e nam pove da poganjamo na x86_64 platformi
e_version 1 nam pove, da gre za original elf
times 8 db 0 dw 2 ; e_type dw 0x3e ; e_machine dd 1 ; e_version |
Sledijo podatki o začetku programa njegovi velikosti in zastavicah:
e_entry _start - naslov zacetka programa
e_phoff in e_shoff predstavljata začetek programskega in section headerja
e_phentsize - velikost program headerja
dd _start ; e_entry dd phdr - $$ ; e_phoff dd 0 ; e_shoff dd 0 ; e_flags dw ehdrsize ; e_phentsize dw phdrsize ; e_phentsize ehdrsize equ $ - ehdr |
V program headerju so definrani segmenti, ki jih program uporablja v run-timu in nekatere druge informacije.
p_type - pove nam kakšne vrste segmentov opisuje proram header oz. kako moramo te podatke interpretirati.
p_offset - offset od začetka datoteke v katerem se nahaja prvi segment.
p_vaddr - viratualni naslov prvega bajta segmenta.
p_paddr - fizični naslov segmenta.
p_filesz - velikost datoteke v bajtih.
p_memsz - velikost v spominu v bajtih.
p_flags - zastavice. 5 = PT_SHLIB - rezervirana zastavica, brez definirane semantike.
p_align - kako je program poravnan v spominu.
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 |
Na naslov z labelo text si shranimo besedilo pesmi, ki jo moramo izpisovati, v length pa si shranimo dolzino pesmi.
text db "Zivel je moz, imel je psa lepo ga je redil.",10,"Nekoc mu ukrade kos mesa, zato ga je ubil." ,10,"Na vrt ga pokopal je, na grob mu napisal je:", 10 length equ $-text |
V edx nalozimo dolžino programa, nato si v ecx shranimo pointer na besedilo, ki ga želimo izpisati. V ebx si shranimo konstanto s katero povemo kam želimo besedilo izpisovati, v našem primeru je to 1, ki nam pove da želimo izpisovati na zaslon. V eax naložimo write system komand in nato kličemo prekinite 0x80, ki pokliče kernel.
_start: loop: mov edx, length ; arg3, length of string to print mov ecx, text ; arg2, pointer to string mov ebx,1 ; arg1, where to write, screen mov eax,4 ; write sysout command to int 80 hex int 0x80 ; interrupt 80 hex, call kernel jmp loop filesize equ $ - $$ |
-f bin - zastavica ki pove nasm, kaksnega tipa je izhod, v nasem primeru binarni.
-o - zastavica, ki ti omogoci, da das izhodni datoteki svoje ime.
nasm -f bin pesmica.asm -o pesmica |
Z chmod +x vsem uporabnikom dodaš še pravice za poganjanje tega programa
in ga z ukazom ./pesmica peženeš.
Če je vse v redu bi moral program nenehno izpisovati pesmico Živel je mož...
chmod +x pesmica ./pesmica |
S tem, da sem naredil svoj ELF header in sem ohranil samo tisto minimalno, kar program potrebuje za delovanje sem uspel iz začetnih 8520 bajtov prišel na 232 bajtov, kar pa niti ni slabo :)
Velikost programa lahko tudi sami preverite z ukazom:
wc -c pesmica |
http://www.muppetlabs.com/~breadbox/software/tiny/teensy.html
http://refspecs.linuxbase.org/elf/elf.pdf
http://www.muppetlabs.com/~breadbox/software/ELF.txt
https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
http://www.sco.com/developers/gabi/2003-12-17/ch4.eheader.html
http://www.sco.com/developers/gabi/latest/ch5.pheader.html