V tomto článku sa budeme venovať programovaniu v assemblery pre procesory x86 kompatibilné. Článok bude rozdelený na 2 časti. V prvej bude popísané systémové volanie a v druhom si ukážeme ako prepojovať assembler s vyšším programovacím jazykom, konkrétne jazykom C.
Ako kompilátor som sa rozhodol použit NASM (Netwide ASM) je rozšírený ako medzi linuxovými užívateľmi tak medzi užívateľmi windowsu. Používa intel syntax ktorá mi príde prehľadnejšia ako tá AT&T ktorú používa napríklad GAS, taktiež má bohatú podporu makier ktoré sa veľmi podobaju tým z gcc. A nakoniec ako objektový formát si zvolíme ELF (Executable Linked Format) ktorý je pod týmto systémom najpoužívanejší
Systémové volanie
Ak máme potrebné hodnoty uložené v daných registroch môžeme zavolat prerušenie. Prerušenie na volanie systému ma v linuxe číslo 0×80.
Čísla všetkých systémových volaní môžeme nájst v súbore /usr/include/asm/unistd.h na niektorých systémoch to môže byť aj /usr/include/asm/unistd_32.h.
Ako to teda vlastne vyzerá:
vl4kn0@cybernode /usr/include/asm $ cat unistd_32.h
#ifndef _ASM_X86_UNISTD_32_H
#define _ASM_X86_UNISTD_32_H
/*
* This file contains the system call numbers.
*/
#define __NR_restart_syscall 0
#define __NR_exit 1
#define __NR_fork 2
#define __NR_read 3
#define __NR_write 4
#define __NR_open 5
#define __NR_close 6
...
tých volaní je tam samozrejme viac (okolo 334). Aké číslo budeme potrebovať zistíme ak od mena konštanty odstraníme prefix __NR_ čize napríklad na vypnutie programu zavolám __NR_exit ktoré ako vidíme má číslo jedna.
Aby sme zistili aké preberá argumenty zavoláme na pomoc man
man 2 exit
Dostaneme manuálovú stránku k tomuto volaniu a zameriame sa na konkrétne túto časť:
NAME
_exit, _Exit - terminate the calling process
SYNOPSIS
#include <unistd.h>
void _exit(int status);
Ako vidíme máme tu krátky popis funkcie a definíciu. void _exit(int status); tu vidíme, že nevracia žiadnu hodnotu a preberá ako parameter status ukončenia programu.
Krátky príklad:
section .text
global _start
_start:
mov eax,1 ; ulozime cislo volania (exit)
mov ebx,0 ; ulozime status
int 0x80 ; zavolame prerusenie operacneho systemu
Tento príklad nespraví nič iné ako to, že sa vypne. Pre doplnenie dodám, že global _start sa dava na to aby linker (ld) vedel kde program začína.
Na to aby sme to skompilovali to zapíšeme do súboru napríklad example.asm a skompilujeme príkazom:
nasm -f elf -o example.o example.asm
prepínačom -f povieme nasm, že ide o formát elf a prepínačom -o hovoríme aký názov bude mať výstupný súbor.
vsetko co treba urobit teraz je program nalinkovat to urobíme programom ld:
ld -o example example.o
Tak to je všetko, máme spustiteľný súbor ktorý nerobí vôbec nič.
Teraz ked vieme čo a ako ukážme si pár príkladov. Čo by sme boli za tutoriál keby sme tu nemali “Hello world” príklad. Podme sa naňho pozriet bližšie.
Ako vieme v linuxe rozlisujeme 3 základné súbory: stdin, stdout, stderr. Tieto súbory sú väčšinou nalinkované do práve otvoreného terminálu. Vidieť to môžeme tu:
cybernode ~ # ls -l /dev | grep std
lrwxrwxrwx. 1 root root 15 2009-08-05 13:08 stderr -> /proc/self/fd/2
lrwxrwxrwx. 1 root root 15 2009-08-05 13:08 stdin -> /proc/self/fd/0
lrwxrwxrwx. 1 root root 15 2009-08-05 13:08 stdout -> /proc/self/fd/1|
Ako vidíme každý z týchto súborov je link. Keď sa pozrieme čo sa za tým linkom skrýva:
cybernode ~ # ls -l /proc/self/fd/0
lrwx------. 1 root root 64 2009-08-05 23:39 /proc/self/fd/0 -> /dev/pts/0
tento náš súbor ukazuje na práve otvorený terminál. Ale to bola len omáčka naokolo. Takže keď to zhrnieme. Každý otvorený súbor v linuxe má svoje ID. Toto ID je doležíté na rozoznávanie súborov s ktorýmy pracujeme. Je dôležité si zapamätať, že ID 0,1,2 sú vyhradené pre stdin, stdout, stderr.
Stdin má ID 0 a je to štandardný vstup. Používať ho budeme najmä na vstup z klávesnice. Stdout má ID 1 a budeme ho používať na výstup na obrazovku a stderror má ID 2 a budeme ho používať na vypisovanie chybových správ.
Omáčka je za nami poďme sa pozrieť na náš hello world príklad:
Program bude postupovať takto:
načítame premenné, zavoláme systémové volanie write, zistíme veľkost premennej a vypíšeme na obrazovku. Nakoniec zavoláme systémové volanie exit. Zistíme teda niečo o systémovom volaní exit:
cybernode ~ # cat /usr/include/asm/unistd_32.h | grep write
#define __NR_write 4
Vidíme teda, že write má číslo 4. Dalej zistíme niečo o parametroch:
cybernode ~ # man 2 write
...
NAME
write - write to a file descriptor
SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
...
Parametre teda budú ID súboru kam chceme premennú zapísať, v našom prípade to bude 1(stdout). Druhý parameter bude adresa premennej, a posledný bude veľkosť. Podme sa teda pozrieť na hotový príklad:
section .data
hello db "Hello world",0xa,0x0 ; Premenna s textom hello world zakoncena novym riadkom a nulovym znakom
len equ $ - hello ; dlzka nasho retazca, znak $ znamena aktualnu adresu
section .text
global _start
_start:
mov eax,4 ; cislo systemoveho volania
mov ebx,1 ; prvy parameter: id suboru
mov ecx,hello ; adresa prveho znaku retazca
mov edx,len ; dlzka retazca
int 0x80 ; zavolame prerusenie nech sa o to postara system
mov eax,1 ; zavolame exit
mov ebx,0 ; ktory prebera ako parameter status
int 0x80 ; zavolame system
uložíme do súboru hello.asm a skompilujeme pomocou prikazov:
nasm -f elf -o hello.o hello.asm
zlinkujeme
ld -o hello hello.o
spustíme:
cybernode ~ # ./hello
Hello world
cybernode ~#
Toto bude zatial všetko. V dalšej časti si povieme o linkovaní C a asm. Ak sa vám článok páčil napíšte to do komentárov, ak nie napíšte čo by ste chceli zmeniť.






Si ty ale sikula, len tak dalej. Skoda, ze uz mi to nic nehovori, assembler som neusoval uz nejaky ten piatok.
[...] Citaj dalej. [...]