X86 asembler
Jan 29, 2016
X86 asembler
80386 procesor
• 32-bitna CISC arhitrktura.
• Pretežno dvoadresna mašina.
• Veličina memorijskog adresnog prostora: 4GB.
• Adresibilna jedinica: bajt.
• Veličina podataka u instrukcijama:– bajt,– reč (word)– dva uzastopna bajta,– dvostruka reč (double word) – četiri uzastopna bajta.
Redosled bajtova podatka u memoriji
• Little-endian – viši bajt na višoj adresi.• Riječi:
– (a): 34 12
– (a+1): 56 34
– (a+2): 78 56
• Duple riječi:– (a) : 78 56 34 12
Zdravo svete! (Linux, int) 3
• .intel_syntax noprefix.arch i386
.data 1
poruka:.ascii “Zdravo svete!\n“kraj_poruke:
.equ duzina_poruke, kraj_poruke – poruka
.text 1
• .globl _start_start:
mov ebx, 1# mov ecx, offset porukalea ecx, porukamov edx, duzina_porukemov eax, 4int 0x80 2
mov ebx,0mov eax,1int 0x80 2
Prevođenje, povezivanje i pokretanje
• Asembliranje:– as zdravo_svete.s –o zdravo_svete.o– korišćena opcija: -o <output_file_name>
• Linkovanje (povezivanje):– ld zdravo_svete.o –o zdravo_svete– korišćena opcija: -o <output_file_name>
• Pokretanje– ./zdravo_svete
Prevođenje i povezivanje pomoću gcc
• Asembliranje:gcc -c -o zdravo_svete.o zdravo_svete.s– korišćene opcije:
• -o <output_file_name>• -c Prevedođenje i asembliranje, bez linkovanja
• Linkovanje (povezivanje):gcc -o zdravo_svete -nostartfiles zdravo_svete.o– korišćene opcije:
-o <output_file_name>-nostartfiles Poveži bez standardnih startup fajlova.
• Ili, i prevođenje i povezivanje jednim pozivom gcc:gcc -o zdravo_svete -nostartfiles zdravo_svete.s
• Druge opcije:-nodefoultlibs Povezivanje bez standardnih biblioteka.-nostdlib Kao –nostartfiles –nodefaultlibs-v Ispisuje komande koje pokreće.-### Kao prethodno, ali bez izvršavanja komandi.
Asemblerske naredbe (Intel sintaksa)
• [labela:] mnemonik [operandi] [#komentar]
• Labela predstavlja adresu na kojoj se nalazi naredba.
• mnemonik je simbolički zapisana komanda.
• Mogu biti do dva operanda.– Prvi je uvijek odredište, a nekad i izvorište.
– Drugi je izvorište.
Transfer podataka
• mov dst, src # dst = src
• lea dst, src # dst = offset(src)1
• lds dst, src # ds:dst = src• les dst, src # es:dst = src
• xchg op1, op2 # mijenja vrijednosti# u operandima op1 i op2
Programski dostupni registri(flat mode, aplikativni režim)
• Opštenamjenski (32-bitni):– eax, ebx, ecx, edx, esi i edi.
• Registri za manipulaciju podacima na steku:– esp i ebp.
• Segmentni registri (16-bitni): – cs, ss, ds, es, fs i gs.– U flat modu, svi ukazuju na početak memorije.
• Registri dostupni samo korišćenjem posebnih instrukcija– programski brojač eip,– statusna riječ procesora eflags.
Preslikavanje logičke u linearnu (flat) adresu
• Logička adresa:(Selektor) + ofset
• Selektor (16b) = segmentni registar• Ofset: adresa iz instrukcije(32bita)
• Selektor pokazuje na deskriptor u okviru deskriptorske tabele (deo operativnog sistema)
• Svi moderni OS podešavaju sadržaj deskriptora svih segmenata tako da pokazuju na logičku adresu 0, tako da je programeru dostupan ceo prostor od 4GB koristeći samo ofset i ne vodeći računa o segmentima tzv. Linearni adr. prostor
• Po potrebi se može uključiti i straničenje, kada se logička adresa preslikava u fizičku adresu.
Opštenamjenski registri
• Nižih 16 bita registara eax, ebx, ecx i edx se može koristiti i kao:– 16-bitni registar: ax, bx, cx i dx;– dva 8-bitna registra:
• Npr. ax ->– ah – viši bajt,– al – niži bajt.
• Imaju i posebne namene:– eax – akumulator,– ebx – bazni registar za adresiranje,– ecx – brojački registar,– edx – pomoćni registar za podatke u nekim instrukcijama,– esi i edi – indeksiranje pri adresiranju.
Registri za rad sa stekom
• esp – pokazivač na vrh steka– pokazuje na zauzetu lokaciju na vrhu steka,
– umanjuje se pre smeštanja podatka,
– uvećava se posle skidanja podatka.
• ebp – bazni registar za pristup argumentima potprograma i lokalnim promenljivim
• Radi sa:• Rečima (16 bita – esp se menja za 2)• Duplim rečima (32 bita – esp se menja za 4)
Rad sa stekom
• push src ; stavlja na stek src
• pop dst ; sa steka upisuje u dst
• pushfd ; čuva eflags na steku
• popfd ; restaurira eflags sa steka (; u neprivilegovanom režimu ; bitovi posle OF neće se promeniti)
• pushf/popf ; koristi samo donjih 16 bita eflags
• pushad/popad ; čuvanje svih registara na steku:; eax, ecx, edx, ebx, esp, ebp,; esi, edi.
Statusna riječ procesora
• CF – prenos.• PF – parnost.• AF – pomoćni prenos za BCD aritmetiku.• ZF – rezultat nula.• SF – negativan rezultat.• TF – prekid posle svake instrukcije.• IF – maskiranje svih maskirajućih prekida.• DF – smjer za operacije nad stringovima.• OF – prekoračenje.• IOPL – I/O priviledge level• NT – nested task flag• RF – resume flag• VM – virtual 8086 mode
VM RF NT IOPL OF DF IF TF SF ZF AF PF CF
Načini adresiranja 1/2
• Neposredno: – mov eax, 10– add ebx, 20h
• Registarsko direktno (svi registri): – mov eax, 2– mov edi, ebx– mov [ebp+6], ecx
• Memorijsko direktno: – mov eax, suma– mov niz+6, edx ...
Načini adresiranja 2/2
• Registarsko indirektno (svi opšte namene):– mov [ebx], eax
• Registarsko indirektno sa pomerajem: – mov [eax+88h], 2– mov niz[edx], 2
• Bazno indeksno (adresa se sastoji od dva registra): – mov [ebx][edx], eax
• Bazno indeksno sa pomjerajem (kao prethodno plus pomjeraj):– mov eax, niz[ebx][edi]
Skaliranje indeksa
• Opšti oblik:– <konstanta>[<bazna_adresa>+<faktor>*<indeks>]
– <faktor> može biti:• 1,• 2,• 4.
• mov eax, suma[ebx+4*esi]• mov vrsta[edx+2*eax], bx
Napomene
• Samo jedan operand u memoriji– postoji nekoliko izuzetaka.
• Podrazumjevana upotreba segmentnih registara:– cs – kod,– ds – podaci, osim ako se u adresnom izrazu koristi ebp– ss – ako se u adresnom izrazu koristi ebp
• Zamjena podrazumjevanog segmentnog registra:– mov ax,ds:[ebp+4]
• Flat režim = samo jedan segment veličine 4GB.
Sistemski pozivi (Linux)
• Tri načina:– direktno,
• kroz prekid 0x80,• kod novijih procesora, instrukcijama SYSENTER i SYSEXIT,
– indirektno, preko funkcija omotača iz standardne biblioteke.
• int 0x80– eax = 1: exit(int)
• ebx: povratna vrijednost programa.– eax = 3: read(int, char*, int)
• ebx: ručka fajla (0 za standardni ulaz),• ecx: adresa bafera,• edx: veličina bafera u B.
– eax = 4: write(int, char*, int)• analogno prethodnom (1 je ručka za standardni izlaz).
Zdravo svete! (Linux, int) 3
• .intel_syntax noprefix.arch i386
.data 1
poruka:.ascii “Zdravo svete!\n“kraj_poruke:
.equ duzina_poruke, kraj_poruke – poruka
.text 1
• .globl _start_start:
mov ebx, 1# mov ecx, offset porukalea ecx, porukamov edx, duzina_porukemov eax, 4int 0x80 2
mov ebx,0mov eax,1int 0x80 2
Zdravo svete! (Linux, libc) 1
• .intel_syntax noprefix.arch i386.dataporuka:.asciz "Zdravo svete!\n”kraj_poruke: .equ duzina_poruke, kraj_poruke – poruka.text.extern write.extern exit
• .globl _start
_start: push duzina_poruke push offset poruka push 1 call write add esp, 12#write(1,&poruka,# duzina_poruke);
push 0 call exit # exit(0);.end
Prevođenje, povezivanje i pokretanje
• Asembliranje:– as -o p1.o p1.s
• Povezivanje:– ld -o p1 -dynamic-linker /lib/ld-linux.so.2 p1.o -l c
– l c: • uključuje biblioteku libc.a1,• važno: navesti biblioteku posle objektnih fajlova koji je koriste2.
– dynamic-linker /lib/ld-linux.so.2• uključuje biblioteku za dinamičko povezivanje.
• Pokretanje– ./zdravo_svete
Potprogrami
• Počinje labelom i opciono:– u redu ispred labele: .type <labela>, @Function
– za potrebe umetanja informacija za debug:• na početku .func <naziv> [, <labela>]1
• na kraju .endfunc
• Završava se instrukcijom za povratak iz potprograma:– ret [exp] # u flat modelu, bliski povratak (samo offset)
– exp: broj koji po povratku treba dodati na esp
• Poziv potprograma:– call dst
– near poziv -> na steku se čuva EIP (u flat režimu se koristi ovaj pristup)
– far poziv -> na steku se čuvaju CS i EIP
– skače na dst
Proslijeđivanje parametara u registrima i globalnim promjenljivim
• Prije poziva se upisuje u registar ili globalno dostupnu memorijsku lokaciju.
• IZBEGAVATI, osim ako je primarna brzina.
• U jednom trenutku smije postojati najviše jedan poziv takve funkcije -> 1
– Da li je dozvoljena rekurzija?– Pozivanje u prekidnim rutinama?– Pozivanje u konkurentnim nitima?
Proslijeđivanje parametara preko steka
• Parametri se prije poziva ostavljaju na steku.
• U funkciji im se pristupa pomoću registra EBP.
• Zato svaka funkcija:
– počinje sa:push ebpmov ebp, esp ili enter 0, 0 1
– završava se sa:mov esp, ebp ili leave 2
pop ebp
• Ovako je obezbjeđeno da svaki poziv funkcije ima svoj zapis na steku (prikaz na sledećem slajdu).
Zapis poziva funkcije
Param[n]
Loc[m]
...
Loc[1]
EBPstaro
retEIP
Param[1]
...
ESP
EBP
+4
+8
-4*m
+4+4*n
-4
Bliski poziv
enter 4*m,0
call naziv_funkcija
Pred poziv funkcije:push param[n]…push param[1]
Zapis tekućeg poziva neke
funkcije
Instrukcija enter
• enter op1, op2:– op1:
• broj bajtova koje je potrebno rezervisati za lokalne prom.• 16-bitna neposredna konstanta.
– op2:• dubina ugniježđenosti funkcije (0-31),• govori koliko pokazivača na prethodne okvire treba iskopirati u
tekući.
– algoritam: 1
• push ebp• mov ebp, esp• Na stek doda pokazivače na okvirove prethodnih nivoa
(stare vrednososti epb registra)• sub esp, op1
Konvencije pozivanja potprograma
• Konvencija pozivanja potprograma definiše:– kako se poravnava stek,– kako se prosleđuju parametri,– ko radi oslobađanje prostora sa steka,– kako se vraća vrednost,– koji registri mogu da se koriste u funkciji bez da se čuvaju.
• Konvencije koje se koriste u gcc-u:– cdecl (podrazumjevana)– stdcall– fastcall
cdecl konvencija pozivanja
• Pred poziv funkcije, stek mora biti poravnat na granicu deljivu sa 16.1
• Argumenti se smeštaju na stek, s desna na levo.
• Prostor na steku oslobađa pozivaoc:– izuzetak je skriveni pokazivač, kojeg oslobađa pozvana funkcija.
• Vrednost se najčešće vraća u registru:– 1 bajt -> AL– 2 bajta -> AX– 4 bajta -> EAX (naredni primjer)– 8 bajtova -> EDX:EAX– struktura ili klasa -> preko skrivenog pokazivača 2
• Funkcija ne mora da čuva registre: eax, ecx i edx
Razlike stdcall i fastcall u odnosu na stdcall
• stdcall– pozvani potprogram skida sve argumente sa steka
• fastcall– prva dva argumenta, ako su celobrojnog tipa, prosleđuju
se: • prvi u ecx• drugi u edx• ostali, kao i oni koji nisu celobrojnog tipa1 prosleđuju se
preko steka
– pozvani potprogram skida sve argumente sa steka• u slučaju da je promenljiv broj argumenata, na stek se
stavljaju svi.
Napisati potprogram za sabiranje dva broja koristeći stdcall i cdecl konvencije 1
• Potprogram:stdcallsaberi: enter 0, 0 mov eax, [ebp+8] add eax, [ebp]+12 leave ret 8
cdeclsaberi: enter 0, 0 mov eax, [ebp+8] add eax, [ebp]+12 leave ret
• Primjer poziva:
push broj1push broj2call saberimov rezultat, eax
push broj1push broj2call saberimov rezultat, eaxadd esp, 8
Napisati potprogram za sabiranje dva broja
push 25
push 33
call saberi
retAdr:
mov rezultat, eax
saberi:
enter 0, 0
mov eax, [ebp+8]
add eax, [ebp]+12
leave
ret 8
EBP_old
retAdr
33
25
?EAX
ESP
EBP
33
25
58
Vraćanje vrijednosti preko steka
typedef struct {int a, b;} struc;
struc f(int x, int y){struc a;a.a = x;a.b = y;return a; 1
}
void main(){struc r = f(0x1122, 0x3344);
}
<f>:push ebpmov ebp, espsub esp, 0x10 # za lokalne promjenljive
mov eax, 0xc[ebp] # eax = xmov [ebp-0x8], eax # a.a = eax
mov eax, [ebp+0x10] # eax + ymov [ebp-0x4], eax # b.b = eax
mov ecx, [ebp+0x8] # ecx = &ret_bafer
mov eax, [ebp-0x8] # eax = a.amov edx, [ebp-0x4] # edx = a.b
mov [ecx], eax # ret_bafer.a = eaxmov [ecx+0x4], edx # ret_bafer.b = edxmov eax, [ebp+0x8] # eax = &ret_bafer
leave ret 0x4 # “oslobađa” skriveni parametar
Sabiranje i oduzimanje
• add dst, src # dst=dst+src• adc dst, src # dst=dst+src+cf
• sub dst, src # dst=dst-src• sbb dst, src # dst=dst-src-cf
• neg dst # dst=-dst
• inc dst # dst=dst+1• dec dst # dst=dst-1
• cmp src1, src2 # setuje flegove na osnovu # src1-src2
Množenje i dijeljenje
• mul src # neoznaceno množenje• imul src # označeno množenje
– src (8-bitni) množi sa al i rezultat ostavlja u ax– src (16-bitni) množi sa ax i rezultat ostavlja u dx:ax– src (32-bitni) množi sa eax i rezultat ostavlja u edx:eax
• div src # neoznačeno dijeljenje• idiv src # označeno dijeljenje
– Dijeli ax sa src (8-bitni) i rezultat ostavlja u al, a ostatak u ah. – Dijeli dx:ax sa src (16-bitni) i rezultat ostavlja u ax, a ostatak u dx– Dijeli edx:eax sa src (32-bitni) i rezultat ostavlja u eax, a ostatak
u edx
• Za množenje postoje i druge forme, sa dva i tri operanda
Proširivanje podataka znakom
• cbw # proširuje ah sa znakom iz al
• cwde # proširuje eax sa znakom iz ax
• cwd # proširuje dx sa znakom iz ax
• cdq # proširuje edx sa znakom iz eax
Logičke operacije
• not dst # dst = ~dst
• and dst, src # dst = dst & src
• or dst, src # dst = dst | src
• xor dst, src # dst = dst ^ src
• test op1, op2 # setuje flegove na osnovu # op1 & op2
Pomjeranje i rotiranje
• shl dst, cnt # pomjeranje logički lijevo• sal dst, cnt # pomjeranje aritmetički lijevo• shr dst, cnt # pomjeranje logički desno• sar dst, cnt # pomjeranje aritmetički desno• ror dst, cnt # rotiranje desno• rol dst, cnt # rotiranje lijevo• rcr dst, cnt # rotiranje kroz cf desno• rcl dst, cnt # rotiranje kroz cf lijevo
• cnt može biti:– 1,– neposredna vrijednost ili– registar cl.
Primjer
• stanje prije: ax=0xf00f, CF=0, cl=2
• shl ax, cl # 0xC03C, CF=1 (bit koji je ispao)• sal ax, cl # 0xC03C, CF=1 (bit koji je ispao)
• shr ax, cl # 0x3C03, CF=1 (bit koji je ispao)• sal ax, cl # 0xFC03, CF=1 (bit koji je ispao)
• ror ax, cl # 0xFC03, CF=1 (poslednji• rol ax, cl # 0xC03F, CF=1 rotirani bit)
• rcr ax, cl # 0xBC03, CF=1 (poslednj• rcl ax, cl # 0xC03D, CF=1 izbačeni bit)
Uslovni skokovi 1/2
• Relativni skok.• Pomjeraj se posmatra kao označeni cijeli broj veličine:
– 8 bita (short jump), ili– 16-bita (near jump), ili– 32-bita (near jump).
• Test pojedinačnih flegova:– jz (je), (Jump if Zero; Jump if Equal)– jnz (jne), (Jump if Not Zero; Jump if Not Equal– js, (Jump if Sign set)– jns,– jp (jpe), (Jump if Parity flag set; Jump if Parity
even)– jnp (jpo), (Jumo if No Parity flag set; Jump if Parity odd)
Uslovni skokovi 2/2
• Poređenje neoznačenih brojeva:– jb (jnae, jc) <(Jump Below; Jump Not Above or Equal)– jnb (jae, jnc) >=– jbe (jna) <=– jnbe (ja) >
• Poređenje označenih brojeva:– jl (jnge) <(Jump Lower; Jump Not Greater or Equal)– jnl (jge) >=– jle (jng) <=– jnle (jg) >
if-then-else
• Viši programski jezici:– if (ecx<=0) {
blok1 } else { blok2}
– Blok1 i blok2 su nizovi instrukcija
• Asembler:– cmp ecx,0
jbe blok1
jmp blok2blok1: …
jmp daljeblok2: …dalje: …
Izračunavanje uslova.
Ako je tačan, skače na blok1.U suprotnom na
blok2.
Lokalne labele
• Smanjuju mogućnost slučajnog ponavljanja iste labele.
• Moguće koristiti 10 različitih:– <cifra>:
• Sa svakog mjesta je moguće referisati prvu labelu:– unazad:
• <cifra>b• odnosi se na prvo pojavljivanje labele “<cifra>:”
prije tekuće instrukcije
– unaprijed• <cifra>f• odnosi se na prvo pojavljivanje labele “<cifra>:”
posle tekuće instrukcije
Primjer upotrebe lokalnih labela
• #Primjer 1:
cmp ecx,0je 1fjmp 2f
1: …jmp 1f
2: …1: …
• #Primjer 2:
1: add eax, ebxsub ebx, 1test ebx, 1je 2fdec ebx
2: jnz 1b
Podrška za petlje 1/2
• Neke moguće implementacije:– Pomoću instrukcija uslovnog skoka
– Pomogu namjenskih instrukcija za formiranje petlji
– Pomoću potprograma i manipulacije povratnom adresom (strogo izbjegavati).
• Instrukcije:– loop lab
– loopz (loope) lab
– loopnz (loopne) lab
Podrška za petlje 2/2
• Algoritam rada:– na početku instrukcije se prvo umanji ecx: ecx=ecx-1– potom se provere uslovi izlaska iz petlje:
• ecx==0, za loop• ecx==0 ili zf==0, za loope• ecx==0 ili zf==1, za loopne
• Lab je labela početak petlje ili instrukcije skoka na početak petlje.
• Lab mora biti u opsegu -128..+127B od adrese sledeće instrukcije.
• jcxz lab ;skače na lab ako je ecx=0
Primjer, suma prvih N brojeva
mov ecx, n # inicijalizacija # brojača
jcxz 2f # ako je ecx = 0, preskače se
# petlja
mov eax, 0 # početna vrijednost # sume
1: add eax, ecx # računanje sume
loop 1b # skok na početak # petlje ako je# ecx>0
2: ... # prva sledeća instrukcija
Bezuslovni skok
• jmp lab # skace na lab
• Za lab se mogu koristiti modovi adresiranja kao i za podatke
• Može se koristiti i indirektno memorijsko adresiranje.
• Primeri:– jmp eax # skače se na adresu zapisanu u eax– jmp [eax] # skače se na adresu zapisanu na adresi iz eax– jmp lab # skače se na labelu lab– jmp [lab] # sa adrese lab se čita adresa skoka– jmp DWORD PTR lab # isto kao prethodno
Minimum dva broja 1
• Napisati program koji sa standardnog ulaza učitava dva broja i na standardni izlaz ispisuje manji od dva unijeta broja.
• Traženje minimuma izdvojiti u funkciju u zasebnom fajlu (koristiti stdcall konvenciju pozivanja).
• Zadatak uraditi:– tako da se oba fajla napišu na asembleru,
– tako da se potprogram napiše na C-u, a program na asembleru,
– tako da se potprogram napiše na asembleru, a da se program napiše na C-u.
Uvoženje i izvoženje simbola
• Izvoženje:– Dvije sintakse:
• .globl <naziv_simbola>• .global <naziv_simbola>
– Ako je simbol definisan, ostaće vidljiv i za fazu povezivanja.
– Ako simbol nije definisan, smatra se da se uvozi spolja.
• Uvoženje:– .extern <naziv_simbola>
p4b.s (potprogram)
.data
.globl izlazni_formatizlazni_format:
.asciz "min(%i, %i) = %i\n"
.text
.globl minmin: 1
enter 0, 0mov ebx, [ebp+8]cmp ebx, [ebp]+12 #isto što i [ebp+12]jle 1fmov eax, [ebp]+12jmp 2f
1: mov eax, ebx2: leave
ret 8 1 .end
p4a.s (program)
.dataulazni_format:
.asciz "%i%i"
.extern izlazni_format
.text.extern printf.extern scanf.extern exit.globl _start
_start:call mainpush 0call exit.extern min
main:enter 8, 0and esp, 0xfffffff0
lea eax, [ebp-4]push eaxsub eax, 4push eaxpush offset ulazni_formatcall scanfadd esp, 12
push [ebp-8]push [ebp-4]call min
push eaxpush [ebp-4]push [ebp-8]push offset izlazni_formatcall printfadd esp, 16leaveret
.end
Prevođenje, povezivanje i pokretanje
• Prevođenje asemblerskog koda:– as -o p4a.o p4a.s– as -o p4b.o p4b.s
• Povezivanje:– ld -o p4 -dynamic-linker /lib/ld-linux.so.2 -l c p4a.o p4b.o
• Pokretanje:– ./p4
P5b.c (potprogram)
char izlazni_format[] = "min(%i, %i) = %i\n";
int __attribute__((stdcall)) min(int a, int b) {return a < b ? a : b;
}
p5a.s (program)
• Isti kao p4a.s
Prevođenje, povezivanje i pokretanje
• Prevođenje:– as -o p5a.o p5a.s
• Prevođenje C koda:– gcc -c -o p5b.o p5b.c– c: prevesti samo do objektnog programa, bez povezivanja
• Povezivanje:– ld -o p5 -dynamic-linker /lib/ld-linux.so.2 -l c p5a.o p5b.o
• Pokretanje– ./p5
• Disasembliranje objektnog fajla koji sadrži funkciju min– objdump –d1 –Mintel2 p5b.o
Disasembliran prevod C funkcije
00000000 <min>: 1
0: 55 push ebp
1: 89 e5 mov ebp, esp
3: 8b 45 08 mov eax, [ebp+0x8]
6: 39 45 0c cmp [ebp+0xc], eax
9: 0f 4e 45 0c cmovle eax, [ebp+0xc]
d: 5d pop ebp
e: c2 08 00 ret 0x8
p6b.s (potprogram)
• Isti kao p4b.s
p6a.c (program)
#include<stdio.h>
extern char izlazni_format;
int __attribute__((stdcall)) min(int x, int y);
int main(){int x1, x2, z;scanf("%i%i", &x1, &x2);z = min(x1,x2);printf(&izlazni_format, x1, x2, z);
return 0;}
Prevođenje, povezivanje i pokretanje
• Prevođenje– gcc -c -o p6a.o p6a.c
• -c: želi se samo prevesti zadati fajl, bez pozivanja.– as -o p6b.o p6b.s
• Povezivanje:– ld -o p6 -dynamic-linker /lib/ld-linux.so.2 -L /usr/lib/gcc/i686-
linux-gnu/4.6/ /usr/lib/i386-linux-gnu/crt1.o /usr/lib/i386-linux-gnu/crti.o /usr/lib/gcc/i686-linux-gnu/4.6/crtbegin.o p6a.o p6b.o -lgcc --as-needed -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no-as-needed /usr/lib/gcc/i686-linux-gnu/4.6/crtend.o /usr/lib/i386-linux-gnu/crtn.o
• Prevođenje i povezivanje u jednoj komandi:– gcc –o p6 p6a.c p6b.s– za prikaz pojedinačnih komandi dodati opciju –v
• Pokretanje– ./p6
Definisanje podataka i poravnavanje
• . balign bajt_multipl, [bajt_za_dopunjavanje], [max]– bajt_multipl1
– bajt_za_dopunu2
– max3
• Rezervisanje prostora u memoriji:– Bajt: .byte init [,init...] – Riječ (2B): .word init [,init...]
.short init [,init...]– Dupla riječ (4B) : .long init [,init...]
.int init [,init...]– Lista stringova: .ascii lista_stringova_odvojenih_zapetama– Lista stringova sa \0:4 .asciz lista_stringova_odvojenih_zapetama
• Ponavljanje sadržaja: – .rept broj_ponavljanja
sadržaj za ponavljanje.endr
Primjer
• a: .byte 2 – jedan bajt kojem se pristupa sa a
• b: .word 3, 8, 6– 3 riječi, sa b se pristupa prvoj, a sa b+2 i b+4 drugim dvijema.
• c: .asciz "Tekst"– niz od 6 bajtova, zadnji je null.
• d: .long c– jedna dupla riječ sa ofsetom od c
• F: .rep 4.byte 0,1.endr
– 8 bajtova inicijalizovanih sa 0,1,0,1,0,1,0,1
Direktive
• .equ noviSimbol, izraz– svako pojavljivanje noviSimbol zamjenjuje vrijednošću izraza.
• .include file– uključuje sadržaj fajla file na poziciju ove direktive.
• OFFSET expr– vraća ofset izraza.
• BYTE PTR expr– pristup bajtu.
• WORD PTR expr– pristup riječi.
• DWORD PTR expr– pristup dvostrukoj riječi.
Sabiranje niza brojeva
• Napisati program koji sa standardnog ulaza učitava i sabira niz brojeva. Program prvo učita broj elemenata niza, a potom i elemente niza. Broj elemenata nije unapred poznat, pa se prostor alocira na steku. Smatrati da će stek biti dovoljno veliki za smeštanje niza, kao i da se zbir računa unutar potprograma.
Sabiranje niza brojeva
.dataporuka: .asciz "Unesite broj elemenata\ niza, pa zatim elemente niza.\n"ulazni_format: .asciz "%i"izlazni_format: .asciz "Suma elemenata unijetog\ niza je = %i\n"
.text
.extern scanf
.extern printf
.extern exit
.globl _start_start:
call mainpush 0call exit # exit(0);
# prvi argument: broje elemenata# naredni argumenti: elementi nizasaberi:
enter 0, 0xor eax, eax # sum = 0mov ecx, [ebp+8] # i = N
jcxz 2f # if (i==0) ret 0;
# adresa prvog elementa: ebp+12# i=N .. 1# adrese elemenata niza:# ebp+12 + (i-1)*4
# sum+=niz[i-1]1: add eax, [ebp+4*ecx]+8
loop 1b
2: leaveret
Sabiranje niza brojeva
main: enter 4, 0
push offset poruka call printf add esp, 4
lea eax, [ebp-4] # eax = &[ebp-4]
push eax # 2. arg za scanf
push offset ulazni_format # 1. arg
call scanf add esp, 8
mov ecx, [ebp-4] jcxz 2f # if (N==0) goto
exit mov ebx, ecx shl ebx, 2 sub esp, ebx mov ebx, esp
1: push ecx
push ebx push offset ulazni_format call scanf # scanf(“%i”, adr_elementa) add esp, 8
pop ecx
add ebx, 4 # adr_elementa++
loop 1b
push DWORD PTR [ebp-4]# push N call saberi # eax = sum add esp, 4
push eax push offset izlazni_format call printf # printf(“…”, sum) add esp, 8
2: leave ret.end
Rekurzivne funkcije
• Napisati rekurzivnu funkciju koja sabira brojeve u opsegu 0..N.
• Koristiti stdcall konvenciju pozivanja.
Primjer – rekurzija, sabiranje N..0
suma: push ebp mov ebp, espmov eax, [ebp+8] # čitanje parametra sa stekacmp eax, 0 # da li se doslo do 0?je dalje # ako jeste, skoci na kraj i
# vrati 0dec eax # ako nije, pozovi suma(i-1)push eaxcall sumaadd eax, [ebp+8] # na tekucu sumu (0..i-1) dodaj i
dalje: pop ebpret 4 # po povratku oslobodi prostor rezervisan
# za parametar na steku
Promjer poziva sa parametrom 3:
3 ;vrijednost programa proslijeđena pred poziv retEIP ;adresa van funkcijeEBPold12 retEIP ;adresa add EAX, [EBP+8]EBPold21retEIP ;adresa add EAX, [EBP+8]EBPold30retEIP ;adresa add EAX, [EBP+8]EBPold3
EBP pokazuje ovdjeEBP+8 pokazuje na poziciju koja sadrži
vrijednost 3Pošto je sa steka očitano 3, što je različito od 0, na stek
se stavlja 3-1, što je parametar za naredni poziv
EBP pokazuje ovdjeEBP+8 pokazuje na poziciju koja sadrži
vrijednost 2
Pošto je sa steka očitano 2, što je različito od 0, na stek
se stavlja 2-1, što je parametar za naredni poziv
EBP pokazuje ovdjeEBP+8 pokazuje na poziciju koja sadrži
vrijednost 1
Pošto je sa steka očitano 1, što je različito od 0, na stek
se stavlja 1-1, što je parametar za naredni poziv
EBP pokazuje ovdjeEBP+8 pokazuje na poziciju
koja sadrži vrijednost 0.Pošto je 0, skače se na kraj.
Primjetiti da je u ovom trenutku EAX=0 što je
početna suma
Sada se kreće sa izlascima iz ranije učinjenih poziva ove
funkcije. Svaki put se po povratku na tekuću sumu doda vrijednost
parametra iz tekućeg poziva. Tekuća suma se uvijek zapiše u
EAX i u njemu se i ostavlja.
EBP pokazuje ovdje. Sa [EBP+8] se čita 1 i
dodaje na tekuću sumu:EAX = EAX+1 (0+1)
EBP pokazuje ovdje. Sa [EBP+8] se čita 2 i
dodaje na tekuću sumu:EAX = EAX+2 (1+2)
EBP pokazuje ovdje. Sa [EBP+8] se čita 3 i
dodaje na tekuću sumu:EAX = EAX+3 (3+3).
Ovo je i povratna vrijednost funkcije.
Uslovno prevođenje
• Početak bloka sa uslovnim prevođenjem:– .if apsolutni_izraz
• da li je vrijednost apsolutnog izraza različita od nule?
– .ifdef name:• da li je simbol name definisan?
– .ifndef
• Opciono može da se pojavi i else grana:– .else– .elseif
• Na kraju mora da se pojavi:– .endif
Makroi 1/2
• Početak makroa:– .macro name [arg[=def_val] {[ |,] [arg[=def_val]} – name – naziv makroa– arg – parametri– def_val – podrazumijevana vrijednost parametra
• Kraj makroa:– .endm
• Izlazak iz makroa sa sredine:– .exitm
• Upotreba parametara:– \naziv_parametra
Primjer upotrebe makroa
• Definicija:
.macro INVOKE, func, p1, p2, p3push \p3push offset \p2push \p1call \funcadd esp, 12
.endm
• Poziv:
INVOKE write,1,poruka,duzina_poruke
Primjer upotrebe makroa
• Definicija:
.macro sum from=0, to=5
.long \from
.if \to-\fromsum "(\from+1)",\to.endif.endm
• Poziv:sum 0,5
• Rezultat1:.long 0.long 1.long 2.long 3.long 4.long 5
Makroi 2/2
• .irp param, <arg,[,arg...]># ovaj segment koda se ponavlja onoliko puta koliko ima# argumenata i to tako da se svaki put umjesto svakog
pojavljivanja# param zamjeni jedna vrijednost arg.
.endm
• .irpc param, <string># isto kao prethodno, osim čto su argumenti slova# navedenog stringa
.endm
• \ - operator prepoznavanja parametra u tekstu
Primjeri upotrebe makroa
• Ulaz:.irp param,0,4,8move d\param,[BX+\param].endr
• Rezultat:move d0, [BX+0]move d4, [BX+4]move d8, [BX+8]
• Ulaz:.irpc param,048 move d\param,[BX+\param] .endr
• Rezultat:move d0, [BX+0]move d4, [BX+4]move d8, [BX+8]
Razlike između Intel i AT&T sintakse
Intel AT&T
Redosled operanada dst, src src, dst 1
Indirektno adresiranje [registar] (registar)
Prefix za registre nema %
Prefix za konstante nema $
Prefix za apsolutne adrese jump i call
nema *
Veličina operanada u memoriji
na osnovu direktiva uz operand
na osnovu sufiksa mnemonika 2
Daleke adrese section:offset $section, $offset
Primjer Intel i AT&T sitaksi
push ebp
mov ebp, esp
mov eax, [ebp+0x8]
cmp [ebp+0xc], eax
cmovle eax, [ebp+0xc]
pop ebp
ret 0x8
push %ebp
mov %esp, %ebp
mov 0x8(%ebp), %eax
cmp %eax, 0xc(%ebp)
cmovle 0xc(%ebp), %eax
pop %ebp
ret $0x8
Primjeri rada sa WIN32 API
Zdravo svete (Windows)
.intel_syntax noprefix
.arch i386
.extern _MessageBoxA@16: PROC
.extern _ExitProcess@4: PROC
.global _start
.equ MB_OK, 0
.datanl:.ascii "Zdravo svete\0“#.asciz "Zdravo svete"
.text _start: push MB_OK push offset nl push offset nl push 0 call _MessageBoxA@16 push 0 call [email protected]
Prevođenje
• Asembliranje:as zdravo_svete.s -o zdravo_svete.obj– korišćena opcija: -o <output_file_name>
• Linkovanje (povezivanje):ld -o zdravo_svete.exe zdravo_svete.obj -l user32 -l kernel32
– korišćene opcije:-o <output_file_name>
-l <library_name>
Primjer – čitanje jednog znaka i vraćanje u registru
.intel_syntax noprefix
.datastdin .long 0.text.extern _readconsolea@20
.type readc, @Functionreadc:
enter 8, 0push ebxlea eax, [ebp-4] # adresa bafera za podatke
lea ebx, [ebp-8] # bafer za broj procitanih # znakova
push 0push ebxpush 1 # broj znakova za citanjepush eax # adresa bafera za podatkepush stdin # stdin handle (naredni slajd)call _readconsolea@20 # stdcall
mov eax, [ebp - 4]and eax, 0ffh
pop ebxleaveret
stdin i stdout ručke
• Da bi se mogao vršiti ispis na konzolu, na početku programa (prije prvog ispisa) je neophodno dohvatiti stdout handle:
.equ STD_OUTPUT_HANDLE, -11push STD_OUTPUT_HANDLEcall GetStdHandlemov stdout, eax
• Da bi se moglo vršiti čitanje standardnog ulaza pripadajuće konzole, na početku programa (prije prvog čitanja) je neophodno dohvatiti stdin handle:
.equ STD_INPUT_HANDLE, -10push STD_INPUT_HANDLEcall GetStdHandlemov stdin, eax
Inline asembler
• Na mesto naredbe se ubacuje :_ _asm_ _(ʺ
# niz redova sa asemblerskim naredbama završenih sa ;
ʺ);
– ili
_ _asm_ _({"instrukcija \n"} : output : input : modify);• output: "=tmp"(naziv promenljive)• input: “tmp"(naziv ulazne promjenljive)• modify: "registar" – spisak registara na čije vrijednosti
kompajler ne treba da računa nakon izvršavanja bloka.• tmp – privremena lokacija (objašnjena na sledećem
slajdu).
Inline asembler – polje tmp
• tmp – oznaka privremene lokacije (registra ili memorije) – ova lokacija će biti korišćena u asembleru umesto
promenljive.
– u asembleru se referiše sa %<redni_broj>
– input -> prije instrukcija iz asm bloka, u tmp će se učitati vrednost promenljive navedene u zagradama.
– output -> nakon instrukcija iz asm bloka, vrednost tmp će se upisati u promenljivu navedenu u zagradama.
– za tmp je moguće koristiti:• registre (eax, ebx, ecx, edx, esi, edi, r-bilo koji registar)1
• memoriju (memory)
Inline asembler - primer
int t;
int main(){int x1, x2, z;
__asm__(// ".intel_syntax noprefix\n“1
"mov ebx, %2\n"\ "add ebx, %3\n" "mov %0, ebx\n"\ "mov %1, ebx\n"
// ".att_syntax\n“1
: "=c"(x1), "=m"(z)\ : "a"(x2), "m"(t)\
: "ebx"\ );
return 0;}
Izlaz koji se dobije kada se c kod prevede na asm:
mov eax, DWORD PTR [ebp-16]2
mov ebx, eax3
add ebx, DWORD PTR t4
mov ecx5, ebxmov DWORD PTR [ebp-20]6, ebx
mov esi, ecx7
mov DWORD PTR [ebp-12], esi
Microsoft asembler
Informativno
Definisanje potprograma (Microsoft asembler)
• name PROC [FAR|NEAR] – start potprograma. – Može biti:
• bliski (pri pozivu se koristi samo EIP) ili• daleki (koriste se i CS i EIP).
– Podrazumjevana vrijednost zavisi od modela (flat – near)– Moguće definisati i konvenciju pozivanja, kao i parametre
• <naziv>PROC[<lang>][<ostali_atributi>] {,<param>:<tip>}<lang> - neka od konvencija pozivanja :
– U slučaju definisanja konvencije pozivanja, umeću se i odgovarajuće instrukcije za navedenu konvenciju pozivanja:npr. za C, na početku: PUSH EBP MOV EBP, ESPi pred svaki ret: POP EBP
• name ENDP – kraj potprograma name. – Ne proizvodi nijednu instrukciju, pa je potrebno ispred staviti ret ili neku drugu
instrukciju za kontrolu toka, kako bi se osigurali da nikada neće doći do neregularne situacije.
Uvoženje i izvoženje simbola
• public name[,name...] – izvoz iz fajla
• extern name:type [,name:type...] – uvoz– Neki asembleri dozvoljavaju izostavljanje tipa– U MASM, tip je obavezan
• Novi pristup je direktiva externdef koja mijenja obje prethodne direktive:– Ukoliko je simbol definisan u fajlu u kojem se nalazi
direktiva, tada je to simbol koji se izvozi– Ukoliko simbol naveden u direktivi nije definisan, tada je to
simbol koji se uvozi– Sintaksa kao kod EXTERN direktive
Definisanje prototipa potprograma
• Direktiva PROTO• Prednosti:
– definiše prototip potprograma => poziv sa INVOKE
– dodjeljuje nazive parametrima (umjesto pomjeraja)– ova direktiva automatski generiše i odgovarajući EXTERNDEF
• Sintaksa:• <naziv>PROTO[<lang>][<ostali_atributi>] {,<param>:<tip>}
<lang> - neka od konvencija pozivanja.• Neophodno je na isti način definisati i sam potprogram (PROTO
zamjeniti sa PROC).• Moguće definisati i lokalne promjenljive:LOCAL <lvar>:<tip> {, <lvar>:<tip>} => pogodno za pristupanje lokalnim promjenljivim bez računanja
pomjeraja.
INVOKE
• Potprogrami definisani u skladu sa prethodnim slajdom, pomoću INVOKE mogu biti pozvani slično pozivima u višim programskim jezicima.
• Primjer:f1 PROC C par1:DWORD, par2:DWORD
mov EAX, par1add EAX, par2
f1 ENDP
f1 PROTO C param1:DWORD, param2:DWORD
INVOKE f1, EAX, EBX ;ekvivalentno sa:;push EBX;push EAX;call f1;add ESP, 8
Definisanje segmenata
• Pojednostavljene direktive (MASM):• klasa segmenta. Standardno:
.stack – stek (ne mora da se navodi)
.code – programski kod
.data – inicijalizovani podaci
.data? – neinicijalizovani podaci• Na početku programa navesti:
– .386P ; za koji procesor su naredbe– .model flat ; za linearne adrese– iza flat je moguće navesti standardnu konvenciju
pozivanja potprograma
Start programa
• end [label] – kraj programa
• ako postoji label, to je oznaka početka programa
• Pri linkovanju više fajlova, samo jedan smije imati ulaznu tačku (početak)
Definisanje podataka i poravnavanje
• even – poravnava sledeću promjenljivu na parnu adresu. Dobra praksa zbog bržeg pristupa memoriji.
• Rezervisanje prostora u memoriji:– Bajt:
[name] DB init [,init...] – Riječ (2B):
[name] DW init [,init...]– Dupla riječ (4B) :
[name] DD init [,init...]– Dvostruka dupla riječ (8B)
[name] DQ init [,init...]• Za svaku početnu vrijednost se rezerviše jedna lokacija.• count DUP (init [,init...])
– ponavlja ono što je u () onoliko puta koliko je vrijednost count.• ? – neinicijalizovana lokacija
Primjer
• a DB 2 – ;jedan bajt kojem se pristupa sa a
• b DW 3 DUP(?)– ;3 neinicijalizovane riječi, sa b se pristupa prvoj, a sa
b+2 i b+4 naredne dvije• c DB "Tekst$"
– ;niz od 6 bajtova• d DD c
– ;jedna dupla riječ sa segmentom i ofsetom od c• f DB 4 DUP(0,1)
– ;8 bajtova inicijalizovanih sa 0,1,0,1,0,1,0,1
Direktive
• name EQU expr – svako pojavljivanje name zamjenjuje sa expr.
• include file – uključuje sadržaj fajla file na poziciju ove direktive.
• OFFSET expr – vraća ofset izraza.
• BYTE PTR expr – pristup bajtu.
• WORD PTR expr – pristup riječi.
• LENGTH var – broj elemenata u nizovnim prom.
Bezimene labele (Microsoft asembler)
• Kao labela se može koristiti @@:
• Na labelu se referiše sa:@B - ako se želi prva bezimena labela unazad
@F - ako se želi prva bezimena labela unaprijed
Uslovno prevođenje
• IFDEF name – uslovno preveđenje.• ELSE• ENDIF
Makroi 1/2
• Početak makroa:name MACRO arg [,arg...]– name – naziv makroa– Arg - parametri
• ENDM – kraj makroa.• LOCAL name [,name...] – lokalni simboli za makro.
– Neophodno za labele u makrou, kako bi se jedan makro mogao ekspandovati više puta u okviru jednog koda bez problema dupliranih labela.
Makroi 2/2
• IRP param, <arg,[,arg...]>– ;ovaj segment koda se ponavlja onoliko puta koliko ima– ;argumenata i to tako da se svaki put umjesto svakog
pojavljivanja– ;param zamjeni jedna vrijednost arg.
• ENDM • IRPC param, <string>
– ;isto kao prethodno, osim čto su argumenti slova navedenog stringa
• ENDM• & - operator prepoznavanja parametra u tekstu