Programujeme v jazyce Assembler v Linuxu: Závěr aneb testujeme

29. 7. 2004
Doba čtení: 4 minuty

Sdílet

Tento díl uzavírá seriál o programování v jazyce Assembler pod operačním systémem Linux na procesorech x86. Dnes si povíme něco o tom, jak najít chyby v našich aplikacích, jak se alokuje paměť a jak propojit naše programy s jazykem C. To je v dnešní době asi zřejmě dost používaná věc, protože psaní moderních programů v Assembleru už není tak efektivní a hodně lidi využívá tento jazyk pouze pro psaní vlastních funkcí pro použití ve "vyšších jazycích" - stejně jako já ;).

Už si vzpomínám – alokace paměti

Ve chvíli, kdy budeme potřebovat v našich aplikacích alokovat pamět, máme v Assembleru možnost. V Linuxu narozdíl od DOSu nežádáme o blok paměti, nýbrž prosíme kernel o prodloužení sekce .bss, což je prostor pro neinicializované proměnné. Údržba volné a přidělené paměti sekce .bss je na nás. Nepoužijeme-li knihovnu jazyka C, musíme si napsat vlastní manažer paměti. To je však samo o sobě skoro na nový seriál, proto si budeme muset vystačit s použitím heap.asm z Asmutils.

Syscall, které prodlužuje nebo zkracuje délku sekce .bss, se jmenuje brk. Jeho hodnota je 45.

Jediný parametr této funkce obsahuje adresu nového konce .bss. Funkce vrací pro kontrolu poslední platnou adresu datové sekce. Potřebujeme-li zjistit poslední platnou adresu, zavoláme volání brk s nulovým ukazatelem. Funkce pak vrátí poslední adresu. Vypadá to asi takto:

sys_brk 0
add eax, pocet_nutnych_byte
sys_brk eax

(K dosavadnímu konci datové sekce .bss přičteme počet byte, které chceme přidělit, a zavoláme znovu brk.)

Muška jenom zlatá – odlaďujeme

Standardním debuggerem pro Linux je gdb, který je příliš nepraktický a složitý pro debugging assemblerovských aplikací. Proto zde uvedu debugger vytvořený přímo pro debugging aplikací v Assembleru, je jím ALD (Assembly language debugger).

Program je na počátku vývoje, proto podporuje pouze procesory x86 kompatibilní a formát ELF. I přesto je však plnohodnotný a dostačující. Autorem projektu je Patrick Alken.

Debugger spustíme příkazem ald

murphy@murphy# ald
Assembly Language Debugger 0.1.6
Copyright (C) 2000-2002 Patrick Alken

ald>

Naši aplikaci nahrajeme příkazem load:

ald>load nas_programek
echo : ELF Intel 80686 (32 bit), LSB, Executable, Version 1 (current)
Loading debugging symbols ... (no symbols found)
ald> 

Nejdůležitejším příkazem je příkaz s (step), který provede jednu instrukci programu a vrátí řízení debuggeru:

(výpis je ilustrativní)

ald> s
eax = 0x00000000 ebx = 0x00000000 ecx = 0x00000000 edx = 0x000000000
esp = 0xBFFFF8CC ebp = 0x00000000 esi = 0x00000000 edi = 0x00000000
ds = 0x0000002B es = 0x0000002B fs = 0x00000000 gs = 0x00000000
ss = 0x0000002B cs = 0x00000013 eip = 0x08448025 eflags = 0x00000345

Flags : PF ZF TF IF

06448082 5A     push ebx 

Toto nám ukazuje, že další instrukce, která se bude provádět, bude push ebx, ležící na adrese 0×06448082, registr příznaků je nastaven na ZF, zopakování stejného příkazu provedeme stiskem klávesy Enter.

Takto můžeme projet celý program, obsah připravené paměti si prohlédneme příkazem e a zadaním registru. Např.: e ebx

Příkazem c provedeme zbytek programu, kdykoli lze program přerušit přes Ctrl+c. Příkazem help získáme popis všech možných příkazů.

ALD podporuje nastavení breakpointů. Jakmile program narazí na breakpoint, přeruší se a řízení se vrátí zpět ALD. Tato možnost nám dává šanci prohlédnout konkrétní místo v aplikaci (předpokládané místo chyby nebo při běhu bez debuggingu se divně chovající), aniž bychom museli krokovat celý program.

Pokud chceme používat názvy symbolů při debuggingu (možné ve verzi ALD vyšší než 0.1.3), musíme přidat symbolickou informaci do objektového souboru parametrem -g, při spuštění nasm: nasm -g, nebo přidat v souboru MCONFIG řádek DEBUG=y, pokud používáme Asmutils).

Connection … – propojení s jazykem C

V poslední kapitole seriálu si řekneme krátce něco o propojení s jazykem C. Nutno uvést, že funkce jazyka C jsou pro programování v Assembleru dostupné se jménem na svém začátku doplněným o podtržítko, takže getche = _getche, avšak my používáme formát ELF, který toto nevyžaduje, v tom případě podtržítko není na škodu, ale také není nutné. Já ho v ukázce uvádět nebudu.

Ve vyšších jazycích se proměnné předávají buď hodnotou, nebo odkazem, ukládají se na zásobník. Je to stejné jako v Assembleru funkce CALL a RET, uloženy jsou jako přes instrukci PUSH. Avšak tento přístup je velmi nevýhodný, protože přístup k proměnným přes tyto instrukce je velmi nepohodlny a nepříliš šťastný. Proto se do dalšího registru poznamenává adresa offset na zásobníku těsně po vstupu do volaného podprogramu, a tak adresujeme všechny parametry i lokální proměnné relativně k této adrese. Používá se registr (E)BP. Po vstupu do podprogramu je původní hodnota (E)BP uložena na zásobník a registr (E)BP je naplněn hodnotou ukazatele na vrchol zásobníku (E)SP.

Zásobníkovému bloku, které se skládá s parametrů, návratové adresy, lokálních proměnných a původní hodnoty (E)BP, se říká stackframe.

Vytvoříme si jednoduchý program v jazyce C, jenž bude volat funkci print_us, kterou implementujeme v Assembleru, a bude vypisovat součet globální proměnné s jejím jediným parametrem typu int.

#include <stdio.h>

const int plus = 10 ;

void print_us(int);

int main(void){

print_us(15);

}

V Assembleru nejdříve zpřístupníme globální proměnné plus a printf.

extern pus
extern printf

%include "misc/c32.mac"

Tímto si zpřístupníme makra proc, arg a endproc, které nám ulehčí konstrukci podprogramu:

proc print_us
%$co arg

Makrem proc označíme začátek funkce print_us. Makro bude provádět sekvenci push ebp, mov ebp, esp. Další makro arg nadefinuje jediný parametr.

mov eax, [ebp + %$co]
add eax, [plus]

push eax
push str1
call printf

endproc

Tento program sečte předanou hodnotu s nadefinovanou konstantou, výsledek by tedy měl být 25 ;).

bitcoin_skoleni

Přeložíme

nasm -f elf print_us.asm

gcc -o print_us print_us.c print_us.o

Závěr

Dnes jsme si ukázali poslední věci, které potřebujeme pro začátek psaní programů v Assembleru pro Linux. Doufám, že se vám tento seriál líbil a přinesl vám alespoň něco málo nového nebo alespoň hezké čtení.