Obsah
1. Assemblery a vyšší programovací jazyky
6. Uroboros – překlad FASMu sebou samým
7. „Hello, world!“ naprogramovaný v různých assemblerech pro různé architektury
8. Volání funkcí kernelu na architektuře IA-32 a x86–64
9. Realizace programu „Hello, world“ v 32bitovém Linuxu
10. Varianty pro GNU Assembler se syntaxemi AT&T a Intel
12. Realizace programu „Hello, world“ v Linuxu na platformě x86–64
13. Varianty pro GNU Assembler a Tiny CC Assembler
15. Realizace programu „Hello, world“ pro 16bitový DOS
16. Binární formát vs. formát COM
19. Repositář s demonstračními příklady
1. Assemblery a vyšší programovací jazyky
Naprostá většina programovacích jazyků, které se používají v současnosti, patří mezi takzvané vysokoúrovňové jazyky, což mj. znamená, že zdrojové kódy vyvíjených aplikací se v nich zapisují s využitím takových jazykových konstrukcí, které mikroprocesor nedokáže vykonat přímo, protože jsou příliš abstraktní. Aby bylo možné programy vytvořené například v programovacím jazyku Python či v klasickém céčku skutečně spustit, je nutné je nějakým způsobem transformovat do (jednodušší) podoby, kterou již mikroprocesor dokáže provést. Výsledná (transformovaná) podoba programu se nazývá strojový kód (machine code) a pro zmíněnou transformaci se používají překladače (což je případ programovacích jazyků C, C++, D, Go, Rustu a celé řady dalších). Alternativní cestu představují interpretry, v nichž program realizovaný ve strojovém kódu vlastně simuluje nějaký abstraktnější procesor, kterým se zdrojové kódy interpretují (což je případ programovacích jazyků typu Python, Perl, Ruby, BASH, ale například i minulý týden zmíněného BASICu atd.).
V některých specifických situacích, zejména při programování mikrořadičů, však může být vhodnější vytvářet programy či jejich části v nízkoúrovňovém jazyku, který se přibližuje strojovému kódu. Vzhledem k tomu, že přímý zápis strojového kódu je pracný, náchylný k chybám (výpočty relativních adres a vůbec práce se složitějšími datovými strukturami) a prakticky dlouhodobě neudržovatelný, používá se již sedmdesát let poněkud odlišný přístup – použití takzvaného assembleru. Assembler neboli též jazyk symbolických adres (JSA), popř. alternativně jazyk symbolických instrukcí (JSI) je nízkoúrovňovým programovacím jazykem, který na hierarchii jazyků sice stojí poměrně vysoko nad strojovým kódem, ovšem na straně druhé je umístěn hluboko pod vyššími kompilovanými programovacími jazyky typu C či interpretovanými jazyky typu Python.
2. Úloha assemblerů
Typickou vlastností většiny existujících assemblerů je jejich vazba na určitý typ mikroprocesoru, popř. řadu mikroprocesorů (architekturu). Týká se to především sady dostupných instrukcí. Programy se ve většině typech assemblerů zapisují formou symbolických jmen instrukcí, přičemž každá instrukce je představována svou mnemotechnickou zkratkou a případnými operandy (konstantami, adresami, nepřímými adresami, jmény pracovních registrů procesoru atd.). Příkladem může být instrukce LD pro načtení hodnoty do registru, přičemž LD vzniklo ze slova load. A ještě lépe je to vidět na instrukcích ADD či SUB vzniklých ze slov addition a subtraction.
Tvorba programů či jejich částí v assembleru již není v současnosti tak rozšířená, jako v letech, kdy bylo nutné využít všechny možnosti dostupného hardware a kdy překladače vysokoúrovňových programovacích jazyků nedokázaly provádět pokročilé optimalizace. I přesto se však kupodivu assembler stále v některých statistikách drží na předních příčkách programovacích jazyků; viz například měsíčně vydávanou statistiku dostupnou na https://www.tiobe.com/tiobe-index/. S assemblerem se tak dnes můžeme většinou setkat v oblasti „malých“ osmibitových mikrořadičů, například z řady PIC, u zastaralé, ale stále používané platformy 8051, ale například i u mikrořadičů Z8. Důvodem může být snaha o využití omezených prostředků (výpočetní výkon, malé kapacity RAM a EPROM/Flash atd.) nebo nutnost provádění nízkoúrovňových operací – přístup k řídicím registrům, bit banging atd.
Ovšem v dnešním článku nás bude zajímat především Linux na dnes maintreamové platformě x86–64, popř. na 32bitové platformě x86.
3. Assemblery v Linuxu
V tomto odstavci budeme pod termínem „assembler“ chápat programový nástroj, jenž je určený pro transformaci zdrojového kódu naprogramovaného v jazyku symbolických adres do objektového kódu (ten je nutné ještě slinkovat) nebo přímo do strojového kódu (což právě provádí dnes popisovaný FASM). Pro operační systém Linux vzniklo hned několik takových nástrojů, přičemž některé nástroje jsou komerční a jiné patří mezi open source. Z nekomerčních nástrojů se jedná o známý GNU Assembler (zkráceně GAS), dále pak o nástroj nazvaný Netwide assembler (NASM), nástroj Yasm Modular Assembler či až překvapivě výkonný vasm. NASM a Yasm jsou pro první krůčky v assembleru velmi dobře použitelné, neboť mají dobře zpracovaný mechanismus reakce na chyby, dají se v nich psát čitelné programy atd. Zajímavý je taktéž projekt TinyCC Assembler, který je součástí Tiny C Compileru a s nímž jsme se již na stránkách Roota setkali.
Zásadní problém při snaze o použití Netwide assembleru či Yasmu však nastává v případě, kdy je nutné vyvíjet aplikace určené pro jinou architekturu, než je i386 či x86_64, a to z toho důvodu, že tyto dva nástroje nedokážou pracovat s odlišnou instrukční sadou. Naproti tomu GNU Assembler tímto problémem ani zdaleka netrpí, takže se v následujícím textu s GNU Assemblerem ještě setkáme (a budeme ho porovnávat s FASMem).
GNU Assembler (zkráceně jen gas) je součástí skupiny nástrojů nazvaných GNU Binutils. Jedná se o nástroje určené pro vytváření a správu binárních souborů obsahujících takzvaný „objektový kód“, dále nástrojů určených pro práci s nativními knihovnami i pro takzvané profilování (profilováním se testuje, ve které části programu se stráví nejvíce času). Mezi nástroje spadající do GNU Binutils patří vedle GNU Assembleru i linker ld (ten dnes použijeme, i když FASM ho nevyžaduje), profiler gprof, správce archivů strojových funkcí ar, nástroj pro odstranění symbolů z objektových a spustitelných souborů strip a několik pomocných utilit typu nm, objdump (i ten dnes použijeme), size a v neposlední řadě také strings.
GNU Assembler je možné použít buď pro překlad uživatelem vytvořených zdrojových kódů nebo pro zpracování kódů vygenerovaných překladači vyšších programovacích jazyků (GCC atd.). Zajímavé je, že všechny moderní verze GNU Assembleru podporují jak původní AT&T syntaxi, tak i (podle mnoha programátorů, včetně autora dnešního článku, čitelnější) syntaxi používanou společností Intel.
4. FASM – Flat Assembler
Kromě výše uvedených assemblerů pro Linux, z nichž nejznámější je pravděpodobně právě GNU Assembler následovaný Netwide Assemblerem, však existuje ještě jeden zajímavý a v některých ohledech i specifický projekt. Jedná se o FASM neboli flat assembler (nikoli fast assembler, i když i toto jméno by bylo příhodné). FASM má poměrně velké množství zajímavých vlastností. Jedná se o assembler určený pro platformy x86, IA-32 i x86–64. Pod těmito zkratkami se skrývá původní šestnáctibitová instrukční sada procesorů řady 8086, dále 32bitové rozšíření této instrukční sady (někdy označované i386, i686 atd. podle konkrétní varianty, dostupných pracovních registrů atd.) a konečně 64bitové rozšíření této instrukční sady.
Dále FASM podporuje generování výstupu v různých formátech, včetně „plochého“ binárního formátu (pouze kódy instrukcí a případná statická data), souborů COM z DOSu (dtto to samé, ovšem s posunutou základní adresou), formátů ELF, COFF, PE, MZ atd. K této důležité problematice se ještě vrátíme v dalším textu.
Zajímavé je, že způsob překladu či formát výstupního souboru, který assembler generuje, se nezadává přepínači zadávanými na příkazové řádce ani s využitím konfiguračních souborů. Tyto informace jsou totiž přímo součástí zdrojových kódů a navíc je zaručeno, že FASM pro stejné vstupní zdrojové kódy vždy vygeneruje bitově naprosto totožný výstup (což je poněkud paradoxně problematika, ke které se v IT zase vracíme). Samozřejmě ovšem můžeme tento koncept „ohnout“, například tak, že vstupní zdrojový soubor bude obsahovat informace o výstupním formátu, ale bude direktivou include při překladu vkládat například obecné knihovní subrutiny atd.
A navíc je samotný FASM naprogramován přímo ve FASM, tedy sám v sobě (viz jeho zdrojové kódy), což znamená, že dnes již (bez bootstrapingu, viz též Můžeme věřit překladačům? Projekty řešící schéma „důvěřivé důvěry“) může přeložit sám sebe. Díky podpoře různých platforem (16bitová, 32bitová, 64bitová) i různých výstupních formátů lze FASM přeložit (cross překladem) na různé platformy (DOS, Windows, Linux, MenuetOS…) a přitom budou výsledné spustitelné binární soubory s assemblerem naprosto totožné se soubory získanými instalací FASM například z repositářů nějaké distribuce (to si ostatně taktéž ověříme).
5. Instalace Flat Assembleru
V případě, že je FASM součástí vaší linuxové distribuce, je jeho instalace většinou naprosto triviální. Příkladem může být instalace na Linux Mintu:
$ sudo apt-get install fasm
Průběh instalace:
Reading package lists... Done Building dependency tree Reading state information... Done The following additional packages will be installed: libc6-i386 The following NEW packages will be installed: fasm libc6-i386 0 upgraded, 2 newly installed, 0 to remove and 5 not upgraded. Need to get 2 885 kB of archives. After this operation, 15,1 MB of additional disk space will be used. ## poznámka: nejvíce místa z oněch 15,1 MB zabere libc6 a nikoli samotný FASM Do you want to continue? [Y/n] y Get:1 http://archive.ubuntu.com/ubuntu focal-updates/main amd64 libc6-i386 amd64 2.31-0ubuntu9.15 [2 726 kB] Get:2 http://archive.ubuntu.com/ubuntu focal/universe amd64 fasm amd64 1.73.22-1 [159 kB] Fetched 2 885 kB in 4s (745 kB/s) Selecting previously unselected package libc6-i386. (Reading database ... 296267 files and directories currently installed.) Preparing to unpack .../libc6-i386_2.31-0ubuntu9.15_amd64.deb ... Unpacking libc6-i386 (2.31-0ubuntu9.15) ... Selecting previously unselected package fasm. Preparing to unpack .../fasm_1.73.22-1_amd64.deb ... Unpacking fasm (1.73.22-1) ... Setting up libc6-i386 (2.31-0ubuntu9.15) ... Setting up fasm (1.73.22-1) ... Processing triggers for man-db (2.9.1-1) ... Processing triggers for libc-bin (2.31-0ubuntu9.15) ...
Pokud ovšem FASM pro vaši distribuci Linuxu nenaleznete, popř. pokud potřebujete například jeho variantu pro DOS apod., je nutné postupovat poněkud specifickým způsobem, a to z toho důvodu, že FASM je naprogramován přímo ve FASM a tedy není zde možné využít klasický bootstraping). Bude tedy zapotřebí si ze stránky https://flatassembler.net/download.php stáhnout již přeložený FASM a ten následně použít.
6. Uroboros – překlad FASMu sebou samým
To, že současný FASM je možné přeložit opět pouze FASMem ovšem poměrně zásadním způsobem narušuje schéma „důvěřivé důvěry“ o kterém jsme se zmínili v předchozím textu. Nicméně si alespoň můžeme otestovat, zda takto získaný spustitelný FASM přeloží sám sebe korektně – jestli tedy budou výsledné binární spustitelné soubory totožné se soubory získanými ze stránky Flat Assembleru (což ovšem nezaručuje neexistenci zadních vrátek).
V adresáři se staženým Flat Assemblerem se totiž nachází i jeho zdrojové kódy. Pokusíme se tedy přeložit FASM jak pro platformu IA-32 (32bitová varianta původní šestnáctibitové sady x86), tak i pro platformu x86–64 (64bitová varianta – resp. ne varianta, ale zcela nová instrukční sada).
Překlad 32bitové varianty FASMu:
$ cd ~/fasm/sources/linux $ ../../fasm fasm.asm flat assembler version 1.73.32 (16384 kilobytes memory, x64) 5 passes, 1.0 seconds, 107115 bytes.
Překlad 64bitové varianty FASMu:
$ cd x64/ $ ../../../fasm fasm.asm flat assembler version 1.73.32 (16384 kilobytes memory, x64) 5 passes, 119689 bytes.
Spustitelné binární soubory, které jsou výsledkem překladu, porovnáme se soubory, které jsme získali rozbalením FASMu. Porovnáme tedy jak 32bitovou variantu FASMu, tak i variantu 64bitovou, a to například nástrojem diff nebo cmp:
$ cd ~/fasm/sources/linux $ cmp -b fasm source/linux/fasm $ cmp -b fasm.x64 source/linux/x64/fasm
V tomto případě by neměly příkazy cmp vypsat žádné informace o rozdílných bajtech – získali jsme tedy bitově přesné obrazy spustitelných souborů.
7. „Hello, world!“ naprogramovaný v různých assemblerech pro různé architektury
Pokusme se nyní v různých assemblerech zapsat program, který po svém spuštění na terminál vypíše zprávu „Hello, world!“ a následně se ukončí. Přitom si budeme chtít tuto operaci vyzkoušet pro různé architektury (pro jednoduchost zůstaneme u platforem x86, IA-32 a x86–64) a pro dva různé operační systémy. Budeme tedy potřebovat realizovat dvě operace:
- Výpis zprávy na terminál (tedy na STDOUT)
- Ukončení procesu
Vzhledem k tomu, že i ta nejjednodušší aplikace naprogramovaná v assembleru musí nějakým způsobem ukončit svou činnost, je nutné buď zavolat vhodnou knihovní funkci (z libc), popř. použít takzvaný „syscall“. V kontextu operačního systému Linux se pod tímto termínem skrývá volání nějaké funkce umístěné přímo v jádru operačního systému. A jak uvidíme dále, je prakticky totožná operace dostupná i ve starobylém šestnáctibitovém operačním systému DOS.
V praxi to funguje jak v DOSu, tak i v Linuxu následovně: podle požadavků konkrétní funkce se naplní pracovní registry, popř. datové struktury uložené v operační paměti, následně se číslo služby uloží do předem známého pracovního registru a zavolá se nějaká specifická instrukce, která přímo či nepřímo zavolá jádro (nepřímé volání spočívá v tom, že se vyvolá SW přerušení).
Systém | Architektura | Číslo služby v | Instrukce pro syscall | Návratová hodnota v |
---|---|---|---|---|
Linux | ARM 32 s EABI | r7 | SWI 0h | r0 |
Linux | ARM 64 (AArch-64) | x8 | SVC #0 | x0 |
DOS | x86 | ah | INT 21h | ax |
Linux | IA-32 | eax | INT 80h | eax |
Linux | x86_64 | rax | SYSCALL | rax |
Linux | Motorola 68k | d0 | TRAP #0 | d0 |
Programy, které si ukážeme a odladíme dnes, budou používat následující dvě systémová volání (syscally), které se v Linuxu jmenují:
Syscall | Význam |
---|---|
sys_exit | ukončení procesu s předáním návratového kódu |
sys_write | zápis přes deskriptor souboru (například do standardního výstupu) |
V šestnáctibitovém DOSu se oproti tomu jedná o služby"
Syscall | Význam |
---|---|
4 Terminate process with return code | ukončení procesu s předáním návratového kódu |
9 Print string | zápis přes deskriptor souboru (například do standardního výstupu) |
8. Volání funkcí kernelu na architektuře IA-32 a x86–64
Prvním programem, s nímž se v dnešním článku seznámíme, je program typu „Hello world!“. Tento program je možné v assembleru mikroprocesorů řady IA-32 a x86–64 realizovat poměrně snadno, a to z toho důvodu, že samotné jádro operačního systému obsahuje systémové volání (syscall) určené pro zápis sekvence bajtů do souboru specifikovaného svým deskriptorem, což je konkrétně volání sys_write, o kterém jsme se taktéž zmínili. My sice prozatím neumíme v assembleru pracovat se soubory, to však v tomto případě vůbec nevadí, protože pro každý nový proces jsou automaticky vytvořeny tři deskriptory: standardní vstup, standardní výstup a chybový výstup. A právě standardní výstup použijeme pro výpis řetězce „Hello world!“.
Na 32bitovém systému vypadá příslušný syscall takto:
Registr | Význam | Obsah |
---|---|---|
eax | číslo syscallu | sys_write=4 |
ebx | číslo deskriptoru | stdout=1 |
ecx | adresa řetězce/bajtů | nastaví se do .text, .data atd. segmentu |
edx | počet bajtů pro zápis | strlen(„Hello world!\n“), lze vypočítat při assemblingu |
Instrukce pro vyvolání syscallu je int 80h (SW přerušení).
Na 64bitovém systému vypadá příslušný syscall takto:
Registr | Význam | Obsah |
---|---|---|
rax | číslo syscallu | sys_write=1 |
rdi | číslo deskriptoru | stdout=1 |
rsi | adresa řetězce/bajtů | nastaví se do .text, .data atd. segmentu |
rdx | počet bajtů pro zápis | strlen(„Hello world!\n“), lze vypočítat při assemblingu |
Instrukce pro vyvolání syscallu je přímo syscall.
A druhým voláním bude ukončení procesu. Zde je nutné na 64bitovém systému naplnit registr rax číslem syscallu a registr rdi návratovým celočíselným kódem:
Registr | Význam | Obsah |
---|---|---|
rax | číslo syscallu | sys_exit=60 |
rdi | návratový kód | 0 |
32bitový Linux:
Registr | Význam | Obsah |
---|---|---|
eax | číslo syscallu | sys_exit=1 |
ebx | návratový kód | 0 |
9. Realizace programu „Hello, world“ v 32bitovém Linuxu
Podívejme se nyní na to, jak bude vypadat zápis programu typu „Hello world!“ napsaný v různých assemblerech ve variantě pro mikroprocesory s architekturou IA-32. Celý program volá pouze dvě služby jádra zmíněné a popsané výše: sys_write a sys_exit. U sys_write se nastaví registry způsobem popsaným ve výše uvedené tabulce (tedy číslo syscallu, adresu řetězce, délku řetězce a číslo STDOUT). Z pohledu programátora je „nejsložitější“ naplnění registru ecx, protože ten musí obsahovat adresu řetězce (resp. bloku bajtů). V syntaxi Intelu to vypadá následovně:
mov ecx, offset hello_lbl # adresa retezce, ktery se ma vytisknout
nebo při použití syntaxe AT&T:
mov $hello_lbl,%ecx # adresa retezce, ktery se ma vytisknout
Přičemž message je jméno návěští (label) neboli pojmenovaná adresa. Samotný řetězec může ležet buď v sekci .data nebo i .text (což je kryptické označení pro segment se strojovými instrukcemi). Povšimněte si, že řetězec není ukončen znakem s ASCII kódem 0. To není nutné, protože systémová služba přesně zná délku řetězce (bloku bajtů), na rozdíl od céčkovských funkcí, které naopak hledají ukončující nulu.
10. Varianty pro GNU Assembler se syntaxemi AT&T a Intel
Nejprve si ukažme realizaci v GNU Assembleru (standard), a to při použití výchozí syntaxe dle AT&T:
# asmsyntax=as # Jednoducha aplikace typu "Hello world!" naprogramovana # v assembleru GNU as - je pouzita "AT&T" syntaxe # # Autor: Pavel Tisnovsky # Linux kernel system call table sys_write = 4 sys_exit = 1 #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Hello World!\n" # string, ktery JE ukoncen nulou #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov $sys_write, %eax # cislo syscallu pro funkci "write" mov $1,%ebx # standardni vystup mov $hello_lbl,%ecx # adresa retezce, ktery se ma vytisknout mov $13,%edx # pocet znaku, ktere se maji vytisknout int $0x80 # volani Linuxoveho kernelu movl $sys_exit,%eax # cislo sycallu pro funkci "exit" movl $0,%ebx # exit code = 0 int $0x80 # volani Linuxoveho kernelu
Syntaxe používaná společností Intel je (podle názoru autora článku) ovšem mnohem čitelnější:
# asmsyntax=as # Jednoducha aplikace typu "Hello world!" naprogramovana # v assembleru GNU as - pouzita je "Intel" syntaxe. # # Autor: Pavel Tisnovsky .intel_syntax noprefix # Linux kernel system call table sys_write = 4 sys_exit = 1 #----------------------------------------------------------------------------- .section .data hello_lbl: .string "Hello World!\n" # string, ktery JE ukoncen nulou #----------------------------------------------------------------------------- .section .bss #----------------------------------------------------------------------------- .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov eax, sys_write # cislo syscallu pro funkci "write" mov ebx, 1 # standardni vystup mov ecx, offset hello_lbl # adresa retezce, ktery se ma vytisknout mov edx, 13 # pocet znaku, ktere se maji vytisknout int 0x80 # volani Linuxoveho kernelu mov eax, sys_exit # cislo sycallu pro funkci "exit" mov ebx, 0 # exit code = 0 int 0x80 # volani Linuxoveho kernelu
$ as hello_world.s -o hello_world.o $ ld -s hello_world.o
11. Varianty pro assemblery NASM a FASM
V případě Netwide assembleru (NASM) se program typu „Hello world“ zapisuje sice stejnými instrukcemi (což je pochopitelné), ale některými maličkostmi je zápis odlišuje od GNU Assembleru. NASM používá syntaxi podle Intelu, ale například u klíčových slov section (názvy sekcí a vlastně i segmentů) se nemusí používat tečka na začátku. Zajímavější je ovšem skutečnost, že adresu zprávy není nutné zjišťovat (v čase překladu) pomocí offset, protože si NASM dokáže odvodit, kdy pracujeme s adresou. Poněkud nešikovné je použití equ pro definice pojmenovaných konstant:
; asmsyntax=nasm ; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru NASM. ; ; Autor: Pavel Tisnovsky ; Linux kernel system call table sys_exit equ 1 sys_write equ 4 ;----------------------------------------------------------------------------- section .data hello: db 'Hello world!',10 ; $ obsahuje aktualni adresu v dobe prekladu helloLength: equ $-hello ;----------------------------------------------------------------------------- section .bss ;----------------------------------------------------------------------------- section .text global _start ; tento symbol ma byt dostupny i linkeru _start: mov eax,sys_write ; cislo syscallu pro funkci "write" mov ebx,1 ; standardni vystup mov ecx,hello ; adresa retezce, ktery se ma vytisknout mov edx,helloLength ; helloLength je konstanta, nikoli adresa! int 80h ; volani Linuxoveho kernelu mov eax,sys_exit ; cislo sycallu pro funkci "exit" mov ebx,0 ; exit code = 0 int 80h ; volani Linuxoveho kernelu
Konečně se dostáváme k flat assembleru. V něm je zápis programu možná nejčitelnější; viz například způsob zjištění adresy zprávy umístěním do hranatých závorek. Pojmenované konstanty se vytváří čitelně s využitím operátoru =. A navíc si povšimněte odlišného způsobu definice segmentů (klíčové slovo segment) a uvedení formátu výsledného binárního souboru na úvodním řádku pomocí klíčového slova format (výsledkem je tedy spustitelný soubor ve formátu ELF):
; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru FASM pro Linux na x86. ; ; Autor: Pavel Tisnovsky format ELF executable 3 sys_exit = 1 sys_write = 4 stdout = 1 segment readable executable entry main main: lea ecx, [msg] ; adresa retezce, ktery se ma tisknout mov edx, length ; delka retezce vcetne konce radku mov ebx, stdout ; handle specialniho souboru stdout mov eax, sys_write ; cislo syscallu pro funkci sys_write int 80h ; volani linuxoveho kernelu mov eax, sys_exit ; cislo syscallu pro funkci sys_exit xor ebx, ebx ; navratovy kod int 80h ; volani linuxoveho kernelu segment readable writable msg db 'Hello world!', 10 length = $ - msg
FASM linker nepotřebuje, takže je překlad do nativního spustitelného kódu triviální:
$ fasm hello_world_fasm.asm
12. Realizace programu „Hello, world“ v Linuxu na platformě x86–64
Pojďme nyní přejít z 32bitové platformy IA-32 na 64bitovou platformu x86–64. Změní se především jména a šířky pracovních registrů a z nějakého důvodu i čísla služeb operačního systému a registry použité pro předávání parametrů. Navíc se jádro systému volá instrukcí syscall, což je vyhrazená instrukce právě pro tyto účely. Ostatní části programu se však nezmění.
13. Varianty pro GNU Assembler a Tiny CC Assembler
První 64bitová varianta programu typu „Hello world“ je psána v GNU Assembleru v původní AT&T syntaxi:
# asmsyntax=as # Sablona pro zdrojovy kod Linuxoveho programu naprogramovaneho # v GNU assembleru (GAS). # # Autor: Pavel Tisnovsky # Linux kernel system call table sys_write = 1 sys_exit = 60 .section .text .global _start # tento symbol ma byt dostupny i linkeru _start: mov $sys_write, %rax # cislo sycallu pro funkci "sys_write" na architekture x86-64 mov $1, %rdi # handle, 1 = STDOUT mov $message, %rsi # adresa zpravy mov $length, %rdx # delka zpravy syscall # zavolat funkci Linuxoveho kernelu mov $sys_exit, %rax # cislo sycallu pro funkci "sys_exit" na architekture x86-64 mov $0, %rdi # exit code = 0 syscall # zavolat funkci Linuxoveho kernelu message: .ascii "Hello, world!\n" # zprava, ktera se ma vypsat length = . - message # vypocet delky zpravy primo v prubehu prekladu
Druhá varianta je pro změnu naprogramována v assembleru, který je součástí Tiny C Compileru (TinyCC). I zde se používá syntaxe AT&T, takže zápis se do značné míry podobá předchozímu programu:
# asmsyntax=as # Sablona pro zdrojovy kod Linuxoveho programu naprogramovaneho # v assembleru tcc. # # Autor: Pavel Tisnovsky # Linux kernel system call table sys_write = 1 sys_exit = 60 .section .text .global main # tento symbol ma byt dostupny i linkeru main: mov $sys_write, %rax # cislo sycallu pro funkci "sys_write" na architekture x86-64 mov $1, %rdi # handle, 1 = STDOUT mov $message, %rsi # adresa zpravy mov $length, %rdx # delka zpravy syscall # zavolat funkci Linuxoveho kernelu mov $sys_exit, %rax # cislo sycallu pro funkci "sys_exit" na architekture x86-64 mov $0, %rdi # exit code = 0 syscall # zavolat funkci Linuxoveho kernelu message: .ascii "Hello, world!\n" # zprava, ktera se ma vypsat length = . - message # vypocet delky zpravy primo v prubehu prekladu
14. Varianta pro FASM
První varianta aplikace typu Hello world naprogramovaná ve FASMu pro architekturu x86–64 může vypadat následovně. Povšimněte si rozdílu na řádku s definicí format – je zde nutné použít specifikaci formátu ELF64 a nikoli jen ELF:
; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru FASM pro Linux na x86-64. ; ; Autor: Pavel Tisnovsky format ELF64 executable 3 sys_exit = 60 sys_write = 1 stdout = 1 segment readable executable entry main main: lea rsi, [msg] ; adresa retezce, ktery se ma tisknout mov rdx, 13 ; delka retezce vcetne konce radku mov rdi, stdout ; handle specialniho souboru stdout mov rax, sys_write ; cislo syscallu pro funkci sys_write syscall ; volani linuxoveho kernelu mov rax, sys_exit ; cislo syscallu pro funkci sys_exit xor rdi, rdi ; navratovy kod syscall ; volani linuxoveho kernelu segment readable writable msg db 'Hello world!', 10
A samozřejmě opět můžeme využít triku ve výpočtu délky zprávy odečtením aktuální adresy (počítané při překladu) s adresou návěští zprávy. Ve FASMu se přitom aktuální adresa zapisuje znakem $ a nikoli tečkou:
; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru FASM pro Linux na x86-64. ; ; Autor: Pavel Tisnovsky format ELF64 executable 3 sys_exit = 60 sys_write = 1 stdout = 1 segment readable executable entry main main: lea rsi, [msg] ; adresa retezce, ktery se ma tisknout mov rdx, length ; delka retezce vcetne konce radku mov rdi, stdout ; handle specialniho souboru stdout mov rax, sys_write ; cislo syscallu pro funkci sys_write syscall ; volani linuxoveho kernelu mov rax, sys_exit ; cislo syscallu pro funkci sys_exit xor rdi, rdi ; navratovy kod syscall ; volani linuxoveho kernelu segment readable writable msg db 'Hello world!', 10 length = $ - msg
15. Realizace programu „Hello, world“ pro 16bitový DOS
Vzhledem k tomu, že FASM podporuje mj. i formáty MS DOSu a rozeznává šestnáctibitové instrukce, můžeme se pokusit program typu „Hello world“ upravit pro staré dobré (?) mikroprocesory Intel 8088 a Intel 8086. Pro tisk zprávy použijeme funkci DOSu číslo 9, které se v šestnáctibitovém registru DX předá adresa zprávy. Délku není zapotřebí uvádět, protože zpráva musí být ukončena znakem „$“ (nikoli nulou!), což ovšem není pouze specifikum DOSu (ostatně tato služba byla „převzata“ ze CP/M):
mov ah, 9 ; kod funkce Print String mov dx, message ; adresa zpravy ukoncene dolarem int 21h ; volani funkce DOSu
Ukončení programu nemusí být realizováno funkcí DOSu, ale pouze instrukcí retn (near return) s předáním návratového kódu v AX (tím ušetříme několik taktů a několik bajtů):
mov ah, 9 ; kod funkce Print String mov dx, message ; adresa zpravy ukoncene dolarem int 21h ; volani funkce DOSu xor ax, ax ; navratovy kod retn ; navrat do DOSu
Samotná zpráva je uložena přímo v kódovém segmentu (což je ale ve výsledku jedno, protože v DOSu se neřeší ochrana paměti):
; zprava v kodovem segmentu message db 'Hello world!$'
16. Binární formát vs. formát COM
Úryvky kódu z předchozí kapitoly se pokusíme spojit do uceleného zdrojového kódu:
; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru FASM pro DOS, vysledkem je "flat" soubor. ; ; ; pouziti DOS funkce Print String ; https://www.stanislavs.org/helppc/int_21-9.html format binary as "COM" mov ah, 9 ; kod funkce Print String mov dx, message ; adresa zpravy ukoncene dolarem int 21h ; volani funkce DOSu xor ax, ax ; navratovy kod retn ; navrat do DOSu ; zprava v kodovem segmentu message db 'Hello world!$'
Výše uvedený program však nebude funkční. Je tomu tak z toho důvodu, že se u formátu COM předpokládá, že program začíná na adrese 100h (tedy kódový segment CS+100h) a nikoli na začátku kódového segmentu. To znamená, že adresa zprávy nebude při překladu vypočtena korektně.
Korektní program potřebuje nejprve změnit počitadlo adres příkazem org (origin). Upravená a již funkční varianta programu tedy bude vypadat následovně:
; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru FASM pro DOS, vysledkem je soubor .COM. ; ; ; pouziti DOS funkce Print String ; https://www.stanislavs.org/helppc/int_21-9.html format binary as "COM" ; nutne pro COM - pocatecni adresa kodu org 0x100 mov ah, 9 ; kod funkce Print String mov dx, message ; adresa zpravy ukoncene dolarem int 21h ; volani funkce DOSu xor ax, ax ; navratovy kod retn ; navrat do DOSu ; zprava v kodovem segmentu message db 'Hello world!$'
Zajímavé bude zjistit, jak se vlastně liší binární soubory vzniklé překladem obou zde uvedených zdrojových kódů. Porovnání binárního obsahu po převodu do hexa vyjádření naznačí, že se liší jediný bajt:

Obrázek 1: Rozdíl mezi „flat souborem“ a spustitelnou aplikací ve formátu COM.
Užitečnější bude se podívat na zpětný překlad neboli disassembling:
0100: B4 09 MOV AH, 009h 0102: BA 0A 00 MOV DX, 0000Ah 0105: CD 21 INT 021h 0107: 31 0108: C0 0109: C3 RET # zde začíná text zprávy, nejedná se tedy o instrukce 010A: 48 DEC AX 010B: 65 010C: 6C 010D: 6C 010E: 6F 010F: 20 0110: 77 6F JA 0181h 0112: 72 6C JB 0180h 0114: 64 0115: 21 0116: 24
Soubor COM se od „flat souboru“ odlišuje pouze v adrese zprávy – viz zvýrazněná instrukce s adresou:
0100: B4 09 MOV AH, 009h 0102: BA 0A 01 MOV DX, 0010Ah 0105: CD 21 INT 021h 0107: 31 0108: C0 0109: C3 RET # zde začíná text zprávy, nejedná se tedy o instrukce 010A: 48 DEC AX 010B: 65 010C: 6C 010D: 6C 010E: 6F 010F: 20 0110: 77 6F JA 0181h 0112: 72 6C JB 0180h 0114: 64 0115: 21 0116: 24

Obrázek 3: Turbo Debugger dokáže načíst aplikaci přeloženou do formátu COM (s korektní adresou zprávy).

Obrázek 4: Ovšem „flat soubor“ do Turbo Debuggeru načíst nelze.
17. Formát EXE
Spustitelné soubory ve formátu COM vlastně obsahují pouze sekvenci instrukcí a případná statická data. Nenalezneme zde žádné hlavičky, podporu pro relokace, podporu pro rozdělení kódu a dat do většího množství segmentů atd. Navíc je celková délka COM souboru omezena na 64kB – 256 (což je ona hodnota 100h). Pro rozsáhlejší aplikace vznikl již pro 16bitový DOS formát nazvaný „MZ“ (pojmenováno po jednom z autorů DOSu). Spustitelné soubory v tomto formátu mají koncovku .exe. A i takové soubory dokáže Flat Assembler vytvořit. Postačuje změnit řádek s deklarací format a na začátku si „pohrát“ se segmentovými registry (to můžeme – opět zde není žádná ochrana paměti):
; Jednoducha aplikace typu "Hello world!" naprogramovana ; v assembleru FASM pro DOS, vysledkem je soubor .EXE. ; ; ; pouziti DOS funkci Print String a Terminate Process ; https://www.stanislavs.org/helppc/int_21-9.html ; https://www.stanislavs.org/helppc/int_21-4c.html format MZ push cs pop ds ; DS = CS mov ah, 9 ; kod funkce Print String mov dx, message ; adresa zpravy ukoncene dolarem int 21h ; volani funkce DOSu mov ax,4C00h ; kod funkce Terminate Process With Return Code int 21h ; volani funkce DOSu ; zprava v kodovem segmentu message db 'Hello world!$'
Soubor ve formátu MZ je delší, než jednoduchý COM:
-rw-r--r--. 1 ptisnovs ptisnovs 23 May 10 15:23 hello.com -rw-r--r--. 1 ptisnovs ptisnovs 59 May 10 15:21 hello.exe
A i interně se odlišují, protože COM nemá hlavičku, kdežto MZ (.exe) ano:
$ od -t x1 hello.com 0000000 b4 09 ba 0a 01 cd 21 31 c0 c3 48 65 6c 6c 6f 20 0000020 77 6f 72 6c 64 21 24 0000027 $ od -t x1 hello.exe 0000000 4d 5a 3b 00 01 00 00 00 02 00 00 01 ff ff 02 00 0000020 00 10 00 00 00 00 00 00 1c 00 00 00 00 00 00 00 0000040 0e 1f b4 09 ba 0e 00 cd 21 b8 00 4c cd 21 48 65 0000060 6c 6c 6f 20 77 6f 72 6c 64 21 24 0000073
18. Makra
Nástroje typu „assembler“ je možné podle principu jejich práce rozdělit do několika kategorií. Do první kategorie spadají assemblery interaktivní, které uživateli nabízejí poměrně komfortní vývojové prostředí, v němž je v případě potřeby možné zapisovat jednotlivé instrukce, spouštět programy, krokovat je, vypisovat obsahy pracovních registrů mikroprocesoru, prohlížet si obsah operační paměti, zásobníku atd. Velkou výhodou byla nezávislost těchto assemblerů na rychlém externím paměťovém médiu, proto jsme se s nimi mohli setkat například na osmibitových domácích mikropočítačích či dnes na různých zařízeních typu IoT (i když zde úlohu pouhého interaktivního assembleru mnohdy přebírá interaktivní debugger). Druhý typ assemblerů je široce používán dodnes – jedná se vlastně o běžné překladače, kterým se na vstupu předloží zdrojový kód a po překladu se výsledný nativní kód taktéž uloží na paměťové médium (odkud ho lze přímo spustit, což se dělo například v operačním systému DOS, popř. ho ještě před spuštěním slinkovat, což je případ Linuxu a dalších moderních operačních systémů).
Assemblery spadající do druhé kategorie jsou mnohdy vybaveny více či méně sofistikovaným systémem maker; odtud ostatně pochází i jejich často používané označení macroassembler. Makra, která se většinou aplikují na zdrojový kód v první fázi překladu, je možné použít pro různé činnosti, ať již se jedná o zjednodušení zápisu kódu či o jeho zkrácení a zpřehlednění. Existují například sady poměrně složitých maker, která do assembleru přidávají některé konstrukce známé z vyšších programovacích jazyků – rozvětvení, programové smyčky, deklaraci objektů atd. Flat assembler, podobně jako prakticky všechny další moderní assemblery, práci s makry podporují, i když se způsob zápisu maker i jejich základní vlastnosti od sebe odlišují.
Makra v assembleru, tedy i v námi používaném Flat assembleru, provádí textové substituce, což mj. znamená, že expanze maker je vykonána v první fázi překladu. Ve Flat assembleru deklarace makra začíná direktivou macro a končí znakem }. Za direktivou macro musí následovat jméno makra a popř. i jeho parametry. Na dalších řádcích je pak vlastní text makra. Použití makra je ještě jednodušší než jeho deklarace – kdekoli se prostě uvede jméno makra s případnými parametry. Jakmile Flat assembler zjistí, že se ve zdrojovém kódu nachází jméno makra, provede jeho expanzi, takže se vlastně případné instrukce, ze kterých se text makra skládá, přímo vloží do kódu na místo volání makra.
Podívejme se nyní na velmi jednoduchý demonstrační příklad, kterým je makro bez parametrů. Toto makro jsme nazvali exit a v jeho těle se zavolá syscall (funkce jádra) sloužící k ukončení procesu:
; Deklarace makra pro ukonceni aplikace macro exit { mov eax, sys_exit ; cislo sycallu pro funkci "exit" mov ebx, 0 ; exit code = 0 int 0x80 ; volani Linuxoveho kernelu }
Povšimněte si, že v těle makra se může nacházet libovolný text, včetně komentářů, symbolů deklarovaných mimo makro (sys_exit) atd. Volání makra nazvaného exit vypadá takto:
segment readable executable entry main main: ... ... ... exit ; ukonceni aplikace
Makra mohou v případě potřeby (a ta je poměrně častá) akceptovat i parametry, ovšem práce s nimi může být zpočátku poněkud neobvyklá. Jména parametrů se zadávají již v hlavičce makra přímo za jeho názvem, tedy následujícím způsobem:
macro jméno_makra parametr1, parametr2, { ... ... ... }
Podívejme se na praktický příklad – konkrétně na makro určené pro výpis zprávy na standardní výstup. Tomuto makru se předává adresa řetězce a jeho délka. Uvnitř těla makra se před parametry v GNU assembleru zapisuje zpětné lomítko, ve FASMu nikoli:
; Deklarace makra pro vytisteni zpravy na standardni vystup macro writeMessage message,messageLength { mov ecx, message ; adresa retezce, ktery se ma vytisknout mov edx, messageLength ; pocet znaku, ktere se maji vytisknout call write_message }
Příklad volání tohoto makra:
writeMessage message1,message1len writeMessage message2,message2len writeMessage message3,message3len
Následuje kód programu, který po svém překladu a spuštění vypíše na standardní výstup trojici zpráv:
; Ukazka pouziti maker ve FASMu ; ; Autor: Pavel Tisnovsky format ELF executable 3 ; Linux kernel system call table sys_exit = 1 sys_write = 4 ; Dalsi konstanty pouzite v programu - standardni streamy std_input = 0 std_output = 1 ; Deklarace makra pro ukonceni aplikace macro exit { mov eax, sys_exit ; cislo sycallu pro funkci "exit" mov ebx, 0 ; exit code = 0 int 0x80 ; volani Linuxoveho kernelu } ; Deklarace makra pro vytisteni zpravy na standardni vystup macro writeMessage message,messageLength { mov ecx, message ; adresa retezce, ktery se ma vytisknout mov edx, messageLength ; pocet znaku, ktere se maji vytisknout call write_message } segment readable executable entry main main: writeMessage message1,message1len writeMessage message2,message2len writeMessage message3,message3len exit ; ukonceni aplikace ; Podprogram pro vytisteni zpravy na standardni vystup ; Ocekava se, ze v ecx bude adresa zpravy a v edx jeji delka write_message: mov eax, sys_write ; cislo syscallu pro funkci "write" mov ebx, std_output ; standardni vystup int 0x80 ret segment readable writable message1 db 'Hello world', 10 message1len = $ - message1 ; delka prvni zpravy message2 db 'Vitejte na root.cz', 10 message2len = $ - message2 ; delka druhe zpravy message3 db 'Assembler je fajn', 10 message3len = $ - message3 ; delka druhe zpravy
19. Repositář s demonstračními příklady
Všechny výše popsané demonstrační příklady byly uloženy do repositáře dostupného na adrese https://github.com/tisnik/presentations/. V tabulce umístěné pod tímto odstavcem jsou uvedeny odkazy na tyto příklady:
20. Odkazy na Internetu
- flat assembler: Assembly language resources
https://flatassembler.net/ - FASM na Wikipedii
https://en.wikipedia.org/wiki/FASM - Fresh IDE FASM inside
https://fresh.flatassembler.net/ - MS-DOS Version 4.0 Programmer's Reference
https://www.pcjs.org/documents/books/mspl13/msdos/dosref40/ - INT 21 – DOS Function Dispatcher (DOS)
https://www.stanislavs.org/helppc/int21.html - DOS API (Wikipedia)
https://en.wikipedia.org/wiki/DOS_API - Mikrořadiče eZ8 aneb potomek legendárního Z80
https://www.root.cz/clanky/mikroradice-ez8-aneb-potomek-legendarniho-z80/ - Osmibitové mikrořadiče PIC (2)
https://www.root.cz/clanky/osmibitove-mikroradice-pic-2/ - Mikrořadiče a jejich aplikace v jednoduchých mikropočítačích (2)
https://www.root.cz/clanky/mikroradice-a-jejich-aplikace-v-jednoduchych-mikropocitacich-2/ - Původní domovská stránka Tiny C Compileru
https://bellard.org/tcc/ - Tiny C Compiler na Wikipedii
https://en.wikipedia.org/wiki/Tiny_C_Compiler - TinyCC Assembler
https://bellard.org/tcc/tcc-doc.html#asm - Repositář Tiny C Compileru
https://repo.or.cz/w/tinycc.git - Linux System Call Table for x86 64
https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/ - A Practical Guide to GCC Inline Assembly
https://blog.alex.balgavy.eu/a-practical-guide-to-gcc-inline-assembly/ - Bit banging
https://en.wikipedia.org/wiki/Bit_banging - Warnings Are Your Friend – A Code Quality Primer
https://hackaday.com/2018/11/06/warnings-are-your-friend-a-code-quality-primer/ - Defending Against Compiler-Based Backdoors
https://blog.regehr.org/archives/1241 - Reflections on Trusting Trust
https://www.win.tue.nl/~aeb/linux/hh/thompson/trust.html - Coding Machines (povídka)
https://www.teamten.com/lawrence/writings/coding-machines/ - Stage0
https://bootstrapping.miraheze.org/wiki/Stage0 - Projekt stage0 na GitHubu
https://github.com/oriansj/stage0 - Bootstraping wiki
https://bootstrapping.miraheze.org/wiki/Main_Page - Bootstrapped 6502 Assembler
https://github.com/robinluckey/bootstrap-6502 - IBM Basic assembly language and successors (Wikipedia)
https://en.wikipedia.org/wiki/IBM_Basic_assembly_language_and_successors - X86 Assembly/Bootloaders
https://en.wikibooks.org/wiki/X86_Assembly/Bootloaders - run6502, lib6502 — 6502 microprocessor emulator
http://piumarta.com/software/lib6502/ - Simple Computer Simulator Instruction-Set
http://www.science.smith.edu/dftwiki/index.php/Simple_Computer_Simulator_Instruction-Set - Bootstrapping#Computing (Wikipedia)
https://en.wikipedia.org/wiki/Bootstrapping#Computing - Bootstrapping (compilers)
https://en.wikipedia.org/wiki/Bootstrapping_%28compilers%29 - Bootstrapable Builds
http://bootstrappable.org/ - What is a coder's worst nightmare?
https://www.quora.com/What-is-a-coders-worst-nightmare/answer/Mick-Stute - Linux Assembly
http://asm.sourceforge.net/ - Tombstone diagram (Wikipedia)
https://en.wikipedia.org/wiki/Tombstone_diagram - History of compiler construction (Wikipedia)
https://en.wikipedia.org/wiki/History_of_compiler_construction - Self-hosting (Wikipedia)
https://en.wikipedia.org/wiki/Self-hosting - GNU Mes: Maxwell Equations of Software
https://gitlab.com/janneke/mes - Tiny C Compiler
https://bellard.org/tcc/ - Welcome to C–
https://www.cs.tufts.edu/~nr/c--/index.html - c4 – C in four functions
https://github.com/rswier/c4 - Tiny BASIC (Wikipedia)
https://en.wikipedia.org/wiki/Tiny_BASIC - ARM GCC Inline Assembler Cookbook
http://www.ethernut.de/en/documents/arm-inline-asm.html - Extended Asm – Assembler Instructions with C Expression Operands
https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html - ARM inline asm secrets
http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/ - How to Use Inline Assembly Language in C Code
https://gcc.gnu.org/onlinedocs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C - GCC-Inline-Assembly-HOWTO
http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html - A Brief Tutorial on GCC inline asm (x86 biased)
http://www.osdever.net/tutorials/view/a-brief-tutorial-on-gcc-inline-asm - GCC Inline ASM
http://locklessinc.com/articles/gcc_asm/ - System cally pro AArch64 na Linuxu
https://github.com/torvalds/linux/blob/master/include/uapi/asm-generic/unistd.h - C Functions Without Arguments
https://eklitzke.org/c-functions-without-arguments - GNU Assembler Examples
http://cs.lmu.edu/~ray/notes/gasexamples/ - X86 Assembly/Arithmetic
https://en.wikibooks.org/wiki/X86_Assembly/Arithmetic - Art of Assembly – Arithmetic Instructions
http://oopweb.com/Assembly/Documents/ArtOfAssembly/Volume/Chapter6/CH06–2.html - The GNU Assembler Tutorial
http://tigcc.ticalc.org/doc/gnuasm.html - The GNU Assembler – macros
http://tigcc.ticalc.org/doc/gnuasm.html#SEC109 - Generating Mixed Source and Assembly List using GCC
http://www.systutorials.com/240/generate-a-mixed-source-and-assembly-listing-using-gcc/ - Calling subroutines
http://infocenter.arm.com/help/index.jsp?topic=/com.arm.doc.kui0100a/armasm_cihcfigg.htm - ARM Assembly Language Programming
http://peter-cockerell.net/aalp/html/frames.html - ASM Flags
http://www.cavestory.org/guides/csasm/guide/asm_flags.html - Status Register
https://en.wikipedia.org/wiki/Status_register - Linux assemblers: A comparison of GAS and NASM
http://www.ibm.com/developerworks/library/l-gas-nasm/index.html - Programovani v assembleru na OS Linux
http://www.cs.vsb.cz/grygarek/asm/asmlinux.html - Is it worthwhile to learn x86 assembly language today?
https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1 - Why Learn Assembly Language?
http://www.codeproject.com/Articles/89460/Why-Learn-Assembly-Language - Is Assembly still relevant?
http://programmers.stackexchange.com/questions/95836/is-assembly-still-relevant - Why Learning Assembly Language Is Still a Good Idea
http://www.onlamp.com/pub/a/onlamp/2004/05/06/writegreatcode.html - Assembly language today
http://beust.com/weblog/2004/06/23/assembly-language-today/ - Assembler: Význam assembleru dnes
http://www.builder.cz/rubriky/assembler/vyznam-assembleru-dnes-155960cz - Assembler pod Linuxem
http://phoenix.inf.upol.cz/linux/prog/asm.html - AT&T Syntax versus Intel Syntax
https://www.sourceware.org/binutils/docs-2.12/as.info/i386-Syntax.html - Linux Assembly website
http://asm.sourceforge.net/ - Using Assembly Language in Linux
http://asm.sourceforge.net/articles/linasm.html - vasm
http://sun.hasenbraten.de/vasm/ - vasm – dokumentace
http://sun.hasenbraten.de/vasm/release/vasm.html - The Yasm Modular Assembler Project
http://yasm.tortall.net/ - 680×0:AsmOne
http://www.amigacoding.com/index.php/680×0:AsmOne - ASM-One Macro Assembler
http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler - ASM-One pages
http://www.theflamearrows.info/documents/asmone.html - Základní informace o ASM-One
http://www.theflamearrows.info/documents/asminfo.html - Linux Syscall Reference
http://syscalls.kernelgrok.com/ - Programming from the Ground Up Book – Summary
http://savannah.nongnu.org/projects/pgubook/