Flat assembler: vývojářský nástroj ze staré školy

14. 5. 2024
Doba čtení: 35 minut

Sdílet

Autor: Root.cz s využitím DALL-E
Seznámíme se s assemblerem nazvaným FASM, což je zkratka sousloví flat assembler. Ten se v několika ohledech odlišuje od GNU Assembleru i například od NASMu. Mezi zajímavé vlastnosti patří podpora pro cross překlad.

Obsah

1. Assemblery a vyšší programovací jazyky

2. Úloha assemblerů

3. Assemblery v Linuxu

4. FASM – Flat Assembler

5. Instalace Flat Assembleru

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

11. Varianty pro NASM a FASM

12. Realizace programu „Hello, world“ v Linuxu na platformě x86–64

13. Varianty pro GNU Assembler a Tiny CC Assembler

14. Varianta pro FASM

15. Realizace programu „Hello, world“ pro 16bitový DOS

16. Binární formát vs. formát COM

17. Formát EXE

18. Makra

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

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.

Poznámka: v dalším textu budu používat přímo označení assembler a nikoli sáhodlouhé názvy jazyk symbolických adres nebo jazyk symbolických instrukcí.

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) ...
Poznámka: povšimněte si, že se nainstalovala 32bitová knihovna libc6-i386 na 64bitovém systému, ovšem nemusíte se bát – Flat Assembler bude schopen plnohodnotně pracovat i s 64bitovou instrukční sadou x86–64.

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/dow­nload.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.
Poznámka: mimochodem si povšimněte rychlosti překladu – pokud se žádný čas nevypíše, je čas mnohem kratší než jedna sekunda.

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:

  1. Výpis zprávy na terminál (tedy na STDOUT)
  2. 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
Poznámka: povšimněte si podobnosti mezi Linuxem na 32bitové platformě IA-32 a šestnáctibitovým DOSem.

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
Poznámka: čísla syscallů jsou na platformě x86–64 odlišná od syscallů na platformě x86! Pro x86–64 je naleznete na stránce https://blog.rchapman.org/pos­ts/Linux_System_Call_Table_for_x86_64/.

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
Poznámka: GNU Assembler pouze provádí překlad do objektového kódu, takže je výsledek nutné slinkovat:
$ 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
Poznámka: první segment s kódem není zapisovatelný, druhý segment s daty zapisovatelný je. Tudíž by bylo možné zprávu modifikovat bez pádu procesu.

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
Poznámka: k této problematice se ještě vrátíme, a to nikoli pouze v kontextu formátu MZ, ale i formátu ELF(64).

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
Poznámka: ve skutečnosti se nejedná o volání makra ve smyslu volání subrutiny (podprogramu), ale skutečně pouze o textovou expanzi prováděnou v době překladu programu ze zdrojového kódu do kódu objektového.

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:

zabbix_tip

        writeMessage message1,message1len
        writeMessage message2,message2len
        writeMessage message3,message3len
Poznámka: opět je pravděpodobně přesnější namísto volání použít slovo expanze.

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/pre­sentations/. V tabulce umístěné pod tímto odstavcem jsou uvedeny odkazy na tyto příklady:

# Příklad Popis příkladu Cesta
1 hello_bin.asm varianta pro x86 a systém DOS, výsledkem je binární soubor https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_bin.asm
2 hello_com.asm varianta pro x86 a systém DOS, výsledkem je soubor typu COM https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_com.asm
3 hello_exe.asm varianta pro x86 a systém DOS, výsledkem je soubor typu EXE https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_exe.asm
       
4 hello_i386.asm varianta pro IA-32 a systém Linux https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_i386.asm
5 hello_nasm.asm varianta pro IA-32 a systém Linux, naprogramováno v NASMu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_nasm.asm
6 hello_ia32_gas_intel.s varianta pro IA-32 a systém Linux, naprogramováno v GASu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_ia32_gas_in­tel.s
7 hello_ia32_gas_att.s varianta pro IA-32 a systém Linux, naprogramováno v GASu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_ia32_gas_at­t.s
       
8 hello_x86_64_gas.s varianta pro x86–64, naprogramováno v GASu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_x86_64_gas­.s
9 hello_x86_64_tcc.s varianta pro x86–64, naprogramováno v Tiny C Assembleru https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_x86_64_tcc­.s
10 hello_x86_64_1.asm varianta pro x86–64, naprogramováno ve FASMu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_x86_64_1.asm
11 hello_x86_64_2.asm varianta pro x86–64, naprogramováno ve FASMu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/hello_x86_64_2.asm
       
12 macros.asm makra ve FASMu https://github.com/tisnik/ocaml-examples/tree/master/assem­bler/fasm/macros.asm

20. Odkazy na Internetu

  1. flat assembler: Assembly language resources
    https://flatassembler.net/
  2. FASM na Wikipedii
    https://en.wikipedia.org/wiki/FASM
  3. Fresh IDE FASM inside
    https://fresh.flatassembler.net/
  4. MS-DOS Version 4.0 Programmer's Reference
    https://www.pcjs.org/docu­ments/books/mspl13/msdos/dos­ref40/
  5. INT 21 – DOS Function Dispatcher (DOS)
    https://www.stanislavs.or­g/helppc/int21.html
  6. DOS API (Wikipedia)
    https://en.wikipedia.org/wiki/DOS_API
  7. Mikrořadiče eZ8 aneb potomek legendárního Z80
    https://www.root.cz/clanky/mikroradice-ez8-aneb-potomek-legendarniho-z80/
  8. Osmibitové mikrořadiče PIC (2)
    https://www.root.cz/clanky/osmibitove-mikroradice-pic-2/
  9. 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/
  10. Původní domovská stránka Tiny C Compileru
    https://bellard.org/tcc/
  11. Tiny C Compiler na Wikipedii
    https://en.wikipedia.org/wi­ki/Tiny_C_Compiler
  12. TinyCC Assembler
    https://bellard.org/tcc/tcc-doc.html#asm
  13. Repositář Tiny C Compileru
    https://repo.or.cz/w/tinycc.git
  14. Linux System Call Table for x86 64
    https://blog.rchapman.org/pos­ts/Linux_System_Call_Table_for_x86_64/
  15. A Practical Guide to GCC Inline Assembly
    https://blog.alex.balgavy.eu/a-practical-guide-to-gcc-inline-assembly/
  16. Bit banging
    https://en.wikipedia.org/wi­ki/Bit_banging
  17. Warnings Are Your Friend – A Code Quality Primer
    https://hackaday.com/2018/11/06/war­nings-are-your-friend-a-code-quality-primer/
  18. Defending Against Compiler-Based Backdoors
    https://blog.regehr.org/archives/1241
  19. Reflections on Trusting Trust
    https://www.win.tue.nl/~a­eb/linux/hh/thompson/trus­t.html
  20. Coding Machines (povídka)
    https://www.teamten.com/law­rence/writings/coding-machines/
  21. Stage0
    https://bootstrapping.mira­heze.org/wiki/Stage0
  22. Projekt stage0 na GitHubu
    https://github.com/oriansj/stage0
  23. Bootstraping wiki
    https://bootstrapping.mira­heze.org/wiki/Main_Page
  24. Bootstrapped 6502 Assembler
    https://github.com/robinluc­key/bootstrap-6502
  25. IBM Basic assembly language and successors (Wikipedia)
    https://en.wikipedia.org/wi­ki/IBM_Basic_assembly_lan­guage_and_successors
  26. X86 Assembly/Bootloaders
    https://en.wikibooks.org/wi­ki/X86_Assembly/Bootloaders
  27. run6502, lib6502 — 6502 microprocessor emulator
    http://piumarta.com/software/lib6502/
  28. Simple Computer Simulator Instruction-Set
    http://www.science.smith.e­du/dftwiki/index.php/Simple_Com­puter_Simulator_Instructi­on-Set
  29. Bootstrapping#Computing (Wikipedia)
    https://en.wikipedia.org/wi­ki/Bootstrapping#Computing
  30. Bootstrapping (compilers)
    https://en.wikipedia.org/wi­ki/Bootstrapping_%28compi­lers%29
  31. Bootstrapable Builds
    http://bootstrappable.org/
  32. What is a coder's worst nightmare?
    https://www.quora.com/What-is-a-coders-worst-nightmare/answer/Mick-Stute
  33. Linux Assembly
    http://asm.sourceforge.net/
  34. Tombstone diagram (Wikipedia)
    https://en.wikipedia.org/wi­ki/Tombstone_diagram
  35. History of compiler construction (Wikipedia)
    https://en.wikipedia.org/wi­ki/History_of_compiler_con­struction
  36. Self-hosting (Wikipedia)
    https://en.wikipedia.org/wiki/Self-hosting
  37. GNU Mes: Maxwell Equations of Software
    https://gitlab.com/janneke/mes
  38. Tiny C Compiler
    https://bellard.org/tcc/
  39. Welcome to C–
    https://www.cs.tufts.edu/~nr/c--/index.html
  40. c4 – C in four functions
    https://github.com/rswier/c4
  41. Tiny BASIC (Wikipedia)
    https://en.wikipedia.org/wi­ki/Tiny_BASIC
  42. ARM GCC Inline Assembler Cookbook
    http://www.ethernut.de/en/do­cuments/arm-inline-asm.html
  43. Extended Asm – Assembler Instructions with C Expression Operands
    https://gcc.gnu.org/online­docs/gcc/Extended-Asm.html
  44. ARM inline asm secrets
    http://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/
  45. How to Use Inline Assembly Language in C Code
    https://gcc.gnu.org/online­docs/gcc/Using-Assembly-Language-with-C.html#Using-Assembly-Language-with-C
  46. GCC-Inline-Assembly-HOWTO
    http://ibiblio.org/gferg/ldp/GCC-Inline-Assembly-HOWTO.html
  47. A Brief Tutorial on GCC inline asm (x86 biased)
    http://www.osdever.net/tu­torials/view/a-brief-tutorial-on-gcc-inline-asm
  48. GCC Inline ASM
    http://locklessinc.com/ar­ticles/gcc_asm/
  49. System cally pro AArch64 na Linuxu
    https://github.com/torval­ds/linux/blob/master/inclu­de/uapi/asm-generic/unistd.h
  50. C Functions Without Arguments
    https://eklitzke.org/c-functions-without-arguments
  51. GNU Assembler Examples
    http://cs.lmu.edu/~ray/no­tes/gasexamples/
  52. X86 Assembly/Arithmetic
    https://en.wikibooks.org/wi­ki/X86_Assembly/Arithmetic
  53. Art of Assembly – Arithmetic Instructions
    http://oopweb.com/Assembly/Do­cuments/ArtOfAssembly/Volu­me/Chapter6/CH06–2.html
  54. The GNU Assembler Tutorial
    http://tigcc.ticalc.org/doc/gnu­asm.html
  55. The GNU Assembler – macros
    http://tigcc.ticalc.org/doc/gnu­asm.html#SEC109
  56. Generating Mixed Source and Assembly List using GCC
    http://www.systutorials.com/240/ge­nerate-a-mixed-source-and-assembly-listing-using-gcc/
  57. Calling subroutines
    http://infocenter.arm.com/hel­p/index.jsp?topic=/com.ar­m.doc.kui0100a/armasm_cih­cfigg.htm
  58. ARM Assembly Language Programming
    http://peter-cockerell.net/aalp/html/frames.html
  59. ASM Flags
    http://www.cavestory.org/gu­ides/csasm/guide/asm_flag­s.html
  60. Status Register
    https://en.wikipedia.org/wi­ki/Status_register
  61. Linux assemblers: A comparison of GAS and NASM
    http://www.ibm.com/develo­perworks/library/l-gas-nasm/index.html
  62. Programovani v assembleru na OS Linux
    http://www.cs.vsb.cz/gryga­rek/asm/asmlinux.html
  63. Is it worthwhile to learn x86 assembly language today?
    https://www.quora.com/Is-it-worthwhile-to-learn-x86-assembly-language-today?share=1
  64. Why Learn Assembly Language?
    http://www.codeproject.com/Ar­ticles/89460/Why-Learn-Assembly-Language
  65. Is Assembly still relevant?
    http://programmers.stackex­change.com/questions/95836/is-assembly-still-relevant
  66. Why Learning Assembly Language Is Still a Good Idea
    http://www.onlamp.com/pub/a/on­lamp/2004/05/06/writegreat­code.html
  67. Assembly language today
    http://beust.com/weblog/2004/06/23/as­sembly-language-today/
  68. Assembler: Význam assembleru dnes
    http://www.builder.cz/rubri­ky/assembler/vyznam-assembleru-dnes-155960cz
  69. Assembler pod Linuxem
    http://phoenix.inf.upol.cz/li­nux/prog/asm.html
  70. AT&T Syntax versus Intel Syntax
    https://www.sourceware.or­g/binutils/docs-2.12/as.info/i386-Syntax.html
  71. Linux Assembly website
    http://asm.sourceforge.net/
  72. Using Assembly Language in Linux
    http://asm.sourceforge.net/ar­ticles/linasm.html
  73. vasm
    http://sun.hasenbraten.de/vasm/
  74. vasm – dokumentace
    http://sun.hasenbraten.de/vas­m/release/vasm.html
  75. The Yasm Modular Assembler Project
    http://yasm.tortall.net/
  76. 680×0:AsmOne
    http://www.amigacoding.com/in­dex.php/680×0:AsmOne
  77. ASM-One Macro Assembler
    http://en.wikipedia.org/wiki/ASM-One_Macro_Assembler
  78. ASM-One pages
    http://www.theflamearrows­.info/documents/asmone.html
  79. Základní informace o ASM-One
    http://www.theflamearrows­.info/documents/asminfo.html
  80. Linux Syscall Reference
    http://syscalls.kernelgrok.com/
  81. Programming from the Ground Up Book – Summary
    http://savannah.nongnu.or­g/projects/pgubook/

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.