Transcript V1a Asm x86

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
poruka
lea ecx, poruka
mov edx, duzina_poruke
mov eax, 4
int 0x80 2
mov ebx,0
mov eax,1
int 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
• les dst, src
# ds: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
VM RF
•
•
•
•
•
•
•
•
•
•
•
•
•
NT
IOPL
OF DF IF
TF SF ZF
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
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
poruka
lea ecx, poruka
mov edx, duzina_poruke
mov eax, 4
int 0x80 2
mov ebx,0
mov eax,1
int 0x80 2
Zdravo svete! (Linux, libc) 1
• .intel_syntax noprefix
.arch i386
.data
poruka:
.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 ebp
mov ebp, esp
– završava se sa:
mov esp, ebp
pop ebp
ili
enter 0, 0
ili
leave
1
2
• Ovako je obezbjeđeno da svaki poziv funkcije ima svoj zapis na
steku (prikaz na sledećem slajdu).
Zapis poziva funkcije
Bliski poziv
Zapis tekućeg
poziva neke
funkcije
Loc[m]
-4*m
...
Loc[1]
enter 4*m,0
-4
EBPstaro
retEIP
+4
Param[1]
+8
...
Param[n]
+4+4*n
ESP
EBP
call naziv_funkcija
Pred poziv
funkcije:
push param[n]
…
push param[1]
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:
stdcall
saberi:
enter 0, 0
mov eax, [ebp+8]
add eax, [ebp]+12
leave
ret 8
cdecl
saberi:
enter 0, 0
mov eax, [ebp+8]
add eax, [ebp]+12
leave
ret
• Primjer poziva:
push broj1
push broj2
call saberi
mov rezultat, eax
push broj1
push broj2
call saberi
mov rezultat, eax
add esp, 8
Napisati potprogram za sabiranje dva broja
EAX
58
?
push 25
push 33
call saberi
retAdr:
mov rezultat, eax
EBP_old
saberi:
enter 0, 0
mov eax, [ebp+8]
add eax, [ebp]+12
leave
ret 8
retAdr
33
25
ESP
EBP
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
mov
sub
ebp
ebp, esp
esp, 0x10 # za lokalne promjenljive
mov
mov
eax, 0xc[ebp]
[ebp-0x8], eax
# eax = x
# a.a = eax
mov
mov
eax, [ebp+0x10]
[ebp-0x4], eax
# eax + y
# b.b = eax
mov
ecx, [ebp+0x8]
# ecx = &ret_bafer
mov
mov
eax, [ebp-0x8]
edx, [ebp-0x4]
# eax = a.a
# edx = a.b
mov
mov
mov
[ecx], eax
[ecx+0x4], edx
eax, [ebp+0x8]
# ret_bafer.a = eax
# ret_bafer.b = edx
# eax = &ret_bafer
leave
ret
0x4
# “oslobađa” skriveni parametar
Sabiranje i oduzimanje
• add dst, src
• adc dst, src
# dst=dst+src
# dst=dst+src+cf
• sub dst, src
• sbb dst, src
# dst=dst-src
# dst=dst-src-cf
• neg dst
# dst=-dst
• inc dst
• dec dst
# dst=dst+1
# dst=dst-1
• cmp src1, src2 # setuje flegove na osnovu
# src1-src2
Množenje i dijeljenje
• mul src
• imul src
# neoznaceno množenje
# 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
• idiv src
# neoznačeno dijeljenje
# 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
sal dst, cnt
shr dst, cnt
sar dst, cnt
ror dst, cnt
rol dst, cnt
rcr dst, cnt
rcl dst, cnt
# pomjeranje logički lijevo
# pomjeranje aritmetički lijevo
# pomjeranje logički desno
# pomjeranje aritmetički desno
# rotiranje desno
# rotiranje lijevo
# rotiranje kroz cf desno
# 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
• sal ax, cl
# 0xC03C, CF=1 (bit koji je ispao)
# 0xC03C, CF=1 (bit koji je ispao)
• shr ax, cl
• sal ax, cl
# 0x3C03,
# 0xFC03,
CF=1 (bit koji je ispao)
CF=1 (bit koji je ispao)
• ror ax, cl
• rol ax, cl
# 0xFC03,
# 0xC03F,
CF=1 (poslednji
CF=1
rotirani bit)
• rcr ax, cl
• rcl ax, cl
# 0xBC03, CF=1 (poslednj
# 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),
jnz (jne),
js,
jns,
jp (jpe),
jnp (jpo),
(Jump if Zero; Jump if Equal)
(Jump if Not Zero; Jump if Not Equal
(Jump if Sign set)
(Jump if Parity flag set; Jump if Parity even)
(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)
jnl (jge)
jle (jng)
jnle (jg)
<(Jump Lower; Jump Not Greater or Equal)
>=
<=
>
if-then-else
• Viši programski jezici:
– if (ecx<=0) {
• Asembler:
Ako je tačan,
–
skače na blok1.
U suprotnom na
blok2.
blok1
} else {
blok2
}
– Blok1 i blok2 su nizovi
instrukcija
Izračunavanje uslova.
cmp
jbe
jmp
blok1: …
jmp
blok2: …
dalje: …
ecx,0
blok1
blok2
dalje
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,0
je 1f
jmp 2f
1: …
jmp 1f
2: …
1: …
• #Primjer 2:
1: add eax, ebx
sub ebx, 1
test ebx, 1
je 2f
dec 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
1:
2:
mov
ecx, n
# inicijalizacija
# brojača
jcxz
2f
# ako je ecx = 0, preskače se
# petlja
mov
eax, 0
# početna vrijednost
# sume
add
eax, ecx
# računanje sume
loop 1b
# skok na početak
# petlje ako je
# ecx>0
...
# 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
jmp
jmp
jmp
jmp
eax
[eax]
lab
[lab]
DWORD
# skače se na adresu zapisanu u eax
# skače se na adresu zapisanu na adresi iz eax
# skače se na labelu lab
# sa adrese lab se čita adresa skoka
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_format
izlazni_format:
.asciz "min(%i, %i) = %i\n"
.text
.globl
min: 1
enter
mov ebx,
cmp ebx,
jle 1f
mov eax,
jmp 2f
1: mov eax,
2: leave
ret 8 1
.end
min
0, 0
[ebp+8]
[ebp]+12
[ebp]+12
ebx
#isto što i [ebp+12]
p4a.s (program)
.data
ulazni_format:
.asciz "%i%i"
.extern izlazni_format
.text
.extern printf
.extern scanf
.extern exit
.globl _start
_start:
call main
push 0
call exit
.extern min
main:
enter 8, 0
and esp, 0xfffffff0
lea
push
sub
push
push
call
add
eax, [ebp-4]
eax
eax, 4
eax
offset ulazni_format
scanf
esp, 12
push [ebp-8]
push [ebp-4]
call min
push eax
push [ebp-4]
push [ebp-8]
push offset izlazni_format
call printf
add esp, 16
leave
ret
.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
0: 55
1: 89
3: 8b
6: 39
9: 0f
d: 5d
e: c2
<min>:
1
e5
45 08
45 0c
4e 45 0c
08 00
push
mov
mov
cmp
cmovle
pop
ret
ebp
ebp, esp
eax, [ebp+0x8]
[ebp+0xc], eax
eax, [ebp+0xc]
ebp
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-linuxgnu/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 --asneeded -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:
– Riječ (2B):
– Dupla riječ (4B) :
– Lista stringova:
– Lista stringova sa \0:4
•
Ponavljanje sadržaja:
– .rept broj_ponavljanja
sadržaj za ponavljanje
.endr
.byte init [,init...]
.word init [,init...]
.short init [,init...]
.long init [,init...]
.int init [,init...]
.ascii lista_stringova_odvojenih_zapetama
.asciz lista_stringova_odvojenih_zapetama
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
.data
# prvi argument: broje elemenata
poruka:
# naredni argumenti: elementi niza
.asciz "Unesite broj elemenata\ saberi:
niza, pa zatim elemente niza.\n"
enter
0, 0
ulazni_format:
xor eax, eax
# sum = 0
.asciz "%i"
mov ecx, [ebp+8]
# i = N
izlazni_format:
.asciz "Suma elemenata unijetog\
jcxz 2f
# if (i==0) ret 0;
niza je = %i\n"
# adresa prvog elementa: ebp+12
.text
# i=N .. 1
.extern scanf
# adrese elemenata niza:
.extern printf
# ebp+12 + (i-1)*4
.extern exit
.globl
_start
# sum+=niz[i-1]
_start:
1: add eax, [ebp+4*ecx]+8
call main
loop 1b
push 0
call exit
# exit(0);
2: leave
ret
Sabiranje niza brojeva
1: push ecx
main:
enter 4, 0
push
ebx
push
offset ulazni_format
call scanf # scanf(“%i”, adr_elementa)
add
esp, 8
push
call
add
offset poruka
printf
esp, 4
lea
push
eax, [ebp-4] # eax = &[ebp-4]
eax
# 2. arg za scanf
push
offset ulazni_format
call
add
scanf
esp, 8
mov
jcxz
mov
shl
sub
mov
ecx,
2f
ebx,
ebx,
esp,
ebx,
# 1. arg
[ebp-4]
# if (N==0) goto exit
ecx
2
ebx
esp
pop
ecx
add
ebx, 4
loop
1b
push
call
add
DWORD PTR [ebp-4] # push N
saberi
# eax = sum
esp, 4
push
push
call
add
eax
offset izlazni_format
printf
# printf(“…”, sum)
esp, 8
2: leave
ret
.end
# adr_elementa++
Rekurzivne funkcije
• Napisati rekurzivnu funkciju koja sabira brojeve u
opsegu 0..N.
• Koristiti stdcall konvenciju pozivanja.
Primjer – rekurzija, sabiranje N..0
EBP
pokazuje
ovdje
EBP
pokazuje
ovdje. Sa
suma:
EBP+8
pokazuje
na 3 i
[EBP+8]
se čita
push ebp
sadrži
Promjer poziva Pošto
sa poziciju
parametrom
je sakoja
steka
3:očitano
3,
dodaje
na tekuću
sumu:
mov ebp, esp
vrijednost
30, na
što
je
različito
od
stek
EAX
=
EAX+3
(3+3).
mov eax, [ebp+8] # čitanje parametra
sa steka
EBP
pokazuje
ovdje.
Sa poziv
3
;vrijednost
programa
se
stavlja
proslijeđena
3-1,
što
je
pred
Ovo
je
i
povratna
cmp eax, 0
# da li se doslo do[EBP+8]
0? pokazuje
EBP
ovdje
se
čita
2poziv
i
retEIP
;adresa
parametar
van
funkcije
za
naredni
vrijednost
funkcije.
Pošto
je
sa
steka
očitano
je
dalje
# ako jeste, skocidodaje
naEBP+8
kraj
i
pokazuje
na 2,
na
tekuću
sumu:
EBPold1
što
je
različito
od(1+2)
0, na stek
# vrati
0
poziciju
koja
sadrži
EAX
= EAX+2
2
EBP
pokazuje
ovdje.
EBP
pokazuje
ovdje
se
stavlja
2-1, Sa
što
je
dec eax
# ako nije, pozovi suma(i-1)
vrijednost
2
retEIP ;adresa add
[EBP+8]
EAX,
[EBP+8]
se
čita
1 i očitano
EBP+8
pokazuje
na
parametar
za
naredni
poziv 1,
push eax
Pošto
je sa
steka
EBPold2
dodaje
sumu:
poziciju
koja
sadrži
call suma
štona
jetekuću
različito
od
0, na stek
1
EAX
=
EAX+1
(0+1)
add eax, [ebp+8] # na tekucu sumu (0..i-1)
dodaj
i je
se vrijednost
stavlja
1-1,1 što
EBP
pokazuje
ovdjeiz
retEIP
;adresa
add
EAX,
[EBP+8]
Sada
se krećezasanaredni
izlascima
dalje: pop ebp
parametar
poziv
EBP+8
pokazuje
na
poziciju
EBPold3
ranijeprostor
učinjenih rezervisan
poziva ove
ret 4
# po povratku oslobodi
koja sadrži vrijednost 0.
0
# za parametar
na funkcije.
steku Svaki put se po povratku
Pošto
je 0, doda
skačevrijednost
se na kraj.
retEIP ;adresa add
EAX,
[EBP+8]
na tekuću
sumu
Primjetiti
da je poziva.
u ovom
EBPold3
parametra
iz tekućeg
trenutku
EAX=0
što jeu
Tekuća suma
se uvijek
zapiše
početna
EAX i u njemu
se i suma
ostavlja.
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, p3
push
\p3
push
offset \p2
push
\p1
call
\func
add
esp, 12
.endm
• Poziv:
INVOKE write,1,poruka,duzina_poruke
Primjer upotrebe makroa
• Definicija:
.macro sum from=0, to=5
.long \from
.if \to-\from
sum "(\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,8
move d\param,[BX+\param]
.endr
• Ulaz:
.irpc param,048
move d\param,[BX+\param]
.endr
• Rezultat:
move d0, [BX+0]
move d4, [BX+4]
move d8, [BX+8]
• 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
.data
nl:
.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 _ExitProcess@4
.end
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
.data
stdin .long 0
.text
.extern _readconsolea@20
push 0
push ebx
push 1
# broj znakova za citanje
push eax # adresa bafera za podatke
push stdin # stdin handle (naredni slajd)
call _readconsolea@20 # stdcall
.type readc, @Function
readc:
enter 8, 0
push ebx
lea eax, [ebp-4] # adresa bafera za podatke
lea ebx, [ebp-8] # bafer za broj procitanih
# znakova
mov eax, [ebp - 4]
and eax, 0ffh
pop ebx
leave
ret
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, -11
push STD_OUTPUT_HANDLE
call GetStdHandle
mov 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, -10
push STD_INPUT_HANDLE
call GetStdHandle
mov 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;
Izlaz koji se dobije kada se c kod prevede na asm:
int main(){
int x1, x2, z;
mov
__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;
}
eax, DWORD PTR [ebp-16]2
mov ebx, eax3
add ebx, DWORD PTR t4
mov ecx5, ebx
mov DWORD PTR [ebp-20]6, ebx
mov
mov
esi, ecx7
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, ESP
i 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, par1
add 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