Kde je chyba? – chybové kódy
Návratová hodnota systémového volání se předává prostřednictvím, námi již zmiňovaného, registru EAX. Ta nám ukazuje stav, jak naše systémové volání skončilo. Pokud se jedná o chybu, toto číslo je vždy záporné, je-li kladné, bylo vše provedeno s úspěchem. Avšak hodnota tohoto čísla (je myšleno záporného) nám říká blíže co se vlastně stalo. Assembleru stačí většinou rozlišit zda vše proběhlo v pořádku (+), nebo došlo k chybě (-). Ale to nám přeci nestačí, pokud k chybě dojde (jako že v 99% případů programu při jeho tvorbě dojde a já se s tím jedním procentem ještě neměl tu čest setkat :)), potřebujeme získat přesnější informace o tom, co se vlastně stalo a jaká situace tuto chybu vyvolala. Ke každému volání jsou spousty možností chybových kódů. Nelze uvést jejich seznam, protože pro všechna systémová volání je jich spousta, ale není těžké si v manuálových stránkách jednotlivého volání potřebný kód vyhledat.
Kolik to dá – hodnoty systémových volání
Uvedeme si hodnoty systémových volání exit, fork, read, write, open a close. Pro podrobný přehled informací o těchto voláních vás odkazuji na manuálové stránky jednotlivých volání, protože jejich rozsah je obrovský a zabral by stovky takovýchto článků.
Volání | hodnota volání |
exit | 1 |
fork | 2 |
read | 3 |
write | 4 |
open | 5 |
close | 6 |
Praktický příklad použití, ukončíme svou aplikaci za použití systémového volání exit (1):
mov eax, 1 | ;přiřadíme hodnotu volání exit |
mov ebx, 0 | ;parametr je nula |
int 0×80 | ;zavoláme si jádro, tím se náš program ukončí |
Hurá do pokusů – program „Hello, ROOT.cz“
Máme zde k dispozici datové toky STDIN, STDOUT a STDERR, první pro standardní vstup, druhý pro standardní výstup a třetí pro standardní chybový výstup, který je používán k odlaďování aplikace. Program, který budeme psát, bude zapisovat na STDOUT a pak se ukončí. Pro ukončení programu použijeme výše zmíněnou ukázku. Jak zapsat do STDOUT výstupu, si hned ukážeme.
Použijeme systémové volání (funkci) write, která vyžaduje tři parametry: handle – identifikační číslo, pointer na data k uložení (v našem případě však dojde k vytištění, ne fyzickému uložení) a počet bajtů k zápisu. Výstupem této funkce je počet skutečně zapsaných (předaných) bajtů nebo chybová hodnota. Opět vždy záporná. Zdrojový kód přeložíme překladačem NASM do objektového souboru (s koncovkou .o), poté ho buildneme (sestavíme) pomocí programu ld, tím z našeho kódu uděláme spustitelný soubor.
Hovořili jsme o formátu spustitelného souboru ELF. Aby náš překladač NASM věděl, že má vytvořit tento formát, musíme mu to sdělit pomocí argumentu:
nasm -f elf
Program ld potřebuje adresu paměti první instrukce programu, aby ji mohl vykonat po zavedení programu do paměti, proto si ji označíme návěstím, které bude sloužit jen pro tento účel, já obvykle používám globální symbol
_beginning
Náš program bude rozložen do programových sekcí, zde je jeho kód:
SECTION .text | ;začátek sekce test |
global _beginning | ;definice globálního symbolu |
_beginning: | ;už to vše začíná |
mov eax, 4 | ;číslo systémového voání /write/ |
mov ebx, 1 | ;STDOUT se značí |
mov ecx, pozdrav | ;doplnění adresy řetězce |
mov edx, 014 | ;počet znaků našeho pozdravu |
int 0×80 | ;kernel může přijít ;) |
mov eax, 1 | ;zde se nám prográmek ukončí |
mov ebx, 0 | |
int 0×80 | |
SECTION .data | ;sekce s uloženými daty |
pozdrav db „Hello, ROOT.cz“, 0×a | ;řetězec + konec řádku (LF) |
len equ $ – pozdrav | ;přiřazení délky řetězce symbolu |
… teď už jen překlad:
nasm -f elf test.asm
a sestavení a nalinkování:
ld -s -o test test.o
a program nás hezky pozdraví :), jestli někoho napadá efektivnější způsob, jak tuto aplikaci napsat, nechť ho přidá do diskuse.
ASMUTILS se nám představují
S pomocí této sbírky programů pro Assembler se dá výrazně ulehčit práce na programování v tomto jazyce (mimochodem zkuste hádat, v jakém jazyce jsou tyto programy napsány :). Komunikují tedy přímo s jádrem, nevyžadují žádné další knihovny.
Jsou vhodné pro rescue disky. Jsou zcela přenositelné na systémy FreeBSD, OpenBSD, NetBSD, Unixware, Solaris, AtheOS a BeOS.
Nabízejí nám pro každý operační systém sadu symbolických konstant a systémových maker, které zajišťují přenositelnost našeho programu na jiné operační systémy. To by v jiném případě vyžadovalo nutnost dosti radikálních změn v kódu celého programu a předělání celých sekcí používajících systémová volání. To proto, že v různých OS se hodnoty syscall liší. Samozřejmě nám to i velmi zpřehlední náš kód.
Přepišme si tedy náš program jako malou ukázku nabízených možností:
%include „system.inc“ | |
CODESEG | ;začátek sekce programu |
START: | ;první instrukce |
sys_write STDOUT, pozdrav, len | ;makro pro zápis do registrů |
sys_exit 0 | ;konec programu exit(0); |
DATASEG | |
pozdrav db „Hello, ROOT.cz“, 0×a | |
len equ $-pozdrav | |
END | ;konec sekce |
Efektivnost je daleko vyšší, o přehlednosti ani nemluvě. Až programy ASMUTILS doplní tato makra, dostaneme téměř stejný kód jako předtím, jen ho celý neděláme ručně. Pokud tento kód budeme chtít přeložit na jiném OS, změníme jen platformu v souboru MCONFIG, makra vše udělají za nás.
Jak fungují makra ASMUTILS
Systémová volání mají své parametry. Ty makru předáme jako jeho součást, budou doplněny do potřebného registru. V každém systému je tento registr jiný, proto makro hodnoty přiřadí do registrů právě pro systém, pro který chceme. Stejným způsobem budou doplněny ostatní instrukce specifické pro daný systém (volání jádra atd.).
Všechna makra systémových volání jsou uvozena prefixem sys_. Za podtržítko doplníme název systémového volání (sys_exit, sys_write, sys_fork atd.). V Linuxu se parametry systémovách volání předávají v řadě za sebou ve stejných registrech. Toto není závislé na systémové službě. Návratové hodnoty jsou vždy v registru EAX.
Parametrem makra může být libovolný přípustný operand. Návratový kód si uložíme do proměnné return
sys_exit [return]
Ttoto je ekvivalentní k:
mov eax, 1 mov ebx, [return] int 0x80
Klíčové slovo EMPTY proto funguje, víme-li, že správnou hodnotu již registr obsahuje.
Toto jsou možnosti použití ASMUTILS. Další vlastnosti a usnadnění si můžete přečíst k dokumentaci k tomuto balíku programů.
Závěrem
Dnes jsme se dozvěděli, co jsou to ASMUTILS, udělali si malý příklad na systémová volání a vysvětlili si jejich použití a funkci. Tím jsme si připravili půdu pro další díl seriálu, ve kterém se podíváme blíže na I/O (vstupně výstupní) funkce. Budeme se učit pracovat se soubory.
Tak zase příště.