Hlavní navigace

Programujeme v jazyce Assembler v Linuxu: Trochu praxe

8. 7. 2004
Doba čtení: 5 minut

Sdílet

V dnešním, v pořadí druhém dílu seriálu o Assembleru a jeho praxi v Linuxu si povíme něco o chybových kódech, jak se o nich dozvědět více, napíšeme si program, který vše demonstruje, a zmíníme se o ASMUTILS.

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ů.

Tabulka č. 578
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):

Tabulka č. 579
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:

Tabulka č. 580
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í:

Tabulka č. 581
%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ě.