X 86 asembler 80386 procesor 32 bitna CISC
X 86 asembler
80386 procesor • 32 -bitna CISC arhitrktura. • Pretežno dvoadresna mašina. • Veličina memorijskog adresnog prostora: 4 GB. • 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 i 386. data 1 poruka: . ascii “Zdravo svete!n“ kraj_poruke: . equ duzina_poruke, kraj_poruke – poruka. text 1 • . globl _start: mov ebx, 1 # mov ecx, offset poruka lea ecx, poruka mov edx, duzina_poruke mov eax, 4 int 0 x 80 2 mov ebx, 0 mov eax, 1 int 0 x 80 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. 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 op 1, op 2 # mijenja vrijednosti # u operandima op 1 i op 2
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 (16 b) = segmentni registar • Ofset: adresa iz instrukcije(32 bita) • 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 4 GB 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, 20 h • 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+88 h], 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 4 GB.
Sistemski pozivi (Linux) • Tri načina: – direktno, • kroz prekid 0 x 80, • kod novijih procesora, instrukcijama SYSENTER i SYSEXIT, – indirektno, preko funkcija omotača iz standardne biblioteke. • int 0 x 80 – 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 i 386. data 1 poruka: . ascii “Zdravo svete!n“ kraj_poruke: . equ duzina_poruke, kraj_poruke – poruka. text 1 • . globl _start: mov ebx, 1 # mov ecx, offset poruka lea ecx, poruka mov edx, duzina_poruke mov eax, 4 int 0 x 80 2 mov ebx, 0 mov eax, 1 int 0 x 80 2
Zdravo svete! (Linux, libc) 1 • . intel_syntax noprefix • . globl _start. arch i 386. data _start: poruka: push duzina_poruke. asciz "Zdravo svete!n” push offset poruka kraj_poruke: push 1. equ duzina_poruke, call write kraj_poruke – poruka add esp, 12. text #write(1, &poruka, . extern write # duzina_poruke); . extern exit push 0 call exit # exit(0); . end
Prevođenje, povezivanje i pokretanje • Asembliranje: – as -o p 1. s • Povezivanje: – ld -o p 1 -dynamic-linker /lib/ld-linux. so. 2 p 1. o -l c – l c: • uključuje biblioteku libc. a 1, • važno: navesti biblioteku posle objektnih fajlova koji je koriste 2. – 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 Zapis tekućeg poziva neke funkcije Bliski poziv Loc[m] -4*m . . . Loc[1] -4 enter 4*m, 0 EBPstaro ret. EIP +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 op 1, op 2: – op 1: • broj bajtova koje je potrebno rezervisati za lokalne prom. • 16 -bitna neposredna konstanta. – op 2: • 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, op 1
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 tipa 1 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 broj 1 push broj 2 call saberi mov rezultat, eax add esp, 8
Napisati potprogram za sabiranje dva broja EAX 58 ? push 25 push 33 call saberi ret. Adr: mov rezultat, eax EBP_old saberi: enter 0, 0 mov eax, [ebp+8] add eax, [ebp]+12 leave ret 8 ret. Adr 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(0 x 1122, 0 x 3344); } <f>: push mov sub ebp, esp, 0 x 10 # za lokalne promjenljive mov eax, 0 xc[ebp] [ebp-0 x 8], eax # eax = x # a. a = eax mov eax, [ebp+0 x 10] [ebp-0 x 4], eax # eax + y # b. b = eax mov ecx, [ebp+0 x 8] # ecx = &ret_bafer mov eax, [ebp-0 x 8] edx, [ebp-0 x 4] # eax = a. a # edx = a. b mov mov [ecx], eax [ecx+0 x 4], edx eax, [ebp+0 x 8] # ret_bafer. a = eax # ret_bafer. b = edx # eax = &ret_bafer leave ret 0 x 4 # “oslobađa” skriveni parametar
Sabiranje i oduzimanje • add dst, src • adc dst, src # dst=dst+src+cf • sub dst, src • sbb dst, src # dst=dst-src-cf • neg dst # dst=-dst • inc dst • dec dst # dst=dst+1 # dst=dst-1 • cmp src 1, src 2 # setuje flegove na osnovu # src 1 -src 2
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 op 1, op 2 # setuje flegove na osnovu # op 1 & op 2
Pomjeranje i rotiranje • • shl dst, cnt sal dst, cnt shr dst, cnt sar 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 lijevo # rotiranje kroz cf desno # rotiranje kroz cf lijevo • cnt može biti: – 1, – neposredna vrijednost ili – registar cl.
Primjer • stanje prije: ax=0 xf 00 f, CF=0, cl=2 • shl ax, cl • sal ax, cl # 0 x. C 03 C, CF=1 (bit koji je ispao) • shr ax, cl • sal ax, cl # 0 x 3 C 03, # 0 x. FC 03, CF=1 (bit koji je ispao) • ror ax, cl • rol ax, cl # 0 x. FC 03, # 0 x. C 03 F, CF=1 (poslednji CF=1 rotirani bit) • rcr ax, cl • rcl ax, cl # 0 x. BC 03, CF=1 (poslednj # 0 x. C 03 D, 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 blok 1. U suprotnom na blok 2. blok 1 } else { blok 2 } – Blok 1 i blok 2 su nizovi instrukcija Izračunavanje uslova. cmp jbe jmp blok 1: … jmp blok 2: … dalje: … ecx, 0 blok 1 blok 2 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 1 f jmp 2 f 1: … jmp 1 f 2: … 1: … • #Primjer 2: 1: add eax, ebx sub ebx, 1 test ebx, 1 je 2 f dec ebx 2: jnz 1 b
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. . +127 B 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 2 f # ako je ecx = 0, preskače se # petlja mov eax, 0 # početna vrijednost # sume add eax, ecx # računanje sume loop 1 b # 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 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>
p 4 b. s (potprogram). data. globl izlazni_format: . asciz "min(%i, %i) = %in". text. globl min: 1 enter mov ebx, cmp ebx, jle 1 f mov eax, jmp 2 f 1: mov eax, 2: leave ret 8 1. end min 0, 0 [ebp+8] [ebp]+12 ebx #isto što i [ebp+12]
p 4 a. s (program). data ulazni_format: . asciz "%i%i". extern izlazni_format. text. extern printf. extern scanf. extern exit. globl _start: call main push 0 call exit. extern min main: enter 8, 0 and esp, 0 xfffffff 0 lea push sub push call add eax, [ebp-4] 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 p 4 a. s – as -o p 4 b. s • Povezivanje: – ld -o p 4 -dynamic-linker /lib/ld-linux. so. 2 -l c p 4 a. o p 4 b. o • Pokretanje: –. /p 4
P 5 b. c (potprogram) char izlazni_format[] = "min(%i, %i) = %in"; int __attribute__((stdcall)) min(int a, int b) { return a < b ? a : b; }
p 5 a. s (program) • Isti kao p 4 a. s
Prevođenje, povezivanje i pokretanje • Prevođenje: – as -o p 5 a. s • Prevođenje C koda: – gcc -c -o p 5 b. c – c: prevesti samo do objektnog programa, bez povezivanja • Povezivanje: – ld -o p 5 -dynamic-linker /lib/ld-linux. so. 2 -l c p 5 a. o p 5 b. o • Pokretanje –. /p 5 • Disasembliranje objektnog fajla koji sadrži funkciju min – objdump –d 1 –Mintel 2 p 5 b. o
Disasembliran prevod C funkcije 0000 0: 55 1: 89 3: 8 b 6: 39 9: 0 f d: 5 d e: c 2 <min>: 1 e 5 45 08 45 0 c 4 e 45 0 c 08 00 push mov cmp cmovle pop ret ebp, esp eax, [ebp+0 x 8] [ebp+0 xc], eax, [ebp+0 xc] ebp 0 x 8
p 6 b. s (potprogram) • Isti kao p 4 b. s
p 6 a. c (program) #include<stdio. h> extern char izlazni_format; int __attribute__((stdcall)) min(int x, int y); int main(){ int x 1, x 2, z; scanf("%i%i", &x 1, &x 2); z = min(x 1, x 2); printf(&izlazni_format, x 1, x 2, z); return 0; }
Prevođenje, povezivanje i pokretanje • Prevođenje – gcc -c -o p 6 a. c • -c: želi se samo prevesti zadati fajl, bez pozivanja. – as -o p 6 b. s • Povezivanje: – ld -o p 6 -dynamic-linker /lib/ld-linux. so. 2 -L /usr/lib/gcc/i 686 -linuxgnu/4. 6/ /usr/lib/i 386 -linux-gnu/crt 1. o /usr/lib/i 386 -linux-gnu/crti. o /usr/lib/gcc/i 686 -linux-gnu/4. 6/crtbegin. o p 6 a. o p 6 b. o -lgcc --asneeded -lgcc_s --no-as-needed -lc -lgcc --as-needed -lgcc_s --no -as-needed /usr/lib/gcc/i 686 -linux-gnu/4. 6/crtend. o /usr/lib/i 386 linux-gnu/crtn. o • Prevođenje i povezivanje u jednoj komandi: – gcc –o p 6 a. c p 6 b. s – za prikaz pojedinačnih komandi dodati opciju –v • Pokretanje –. /p 6
Definisanje podataka i poravnavanje • . balign bajt_multipl, [bajt_za_dopunjavanje], [max] – bajt_multipl 1 – bajt_za_dopunu 2 – max 3 • Rezervisanje prostora u memoriji: – Bajt: – Riječ (2 B): – Dupla riječ (4 B) : – Lista stringova sa : 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
Direktive • . equ novi. Simbol, izraz – svako pojavljivanje novi. Simbol 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 2 f # if (i==0) ret 0; niza je = %in" # 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 1 b 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, 2 f ebx, esp, ebx, # 1. arg [ebp-4] # if (N==0) goto exit ecx 2 ebx esp pop ecx add ebx, 4 loop 1 b push call add DWORD PTR [ebp-4]# push N saberi # eax = sum esp, 4 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. 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 2 poziv i ret. EIP ; adresa parametar van funkcije za naredni vrijednost funkcije. Pošto je sa steka očitano je dalje # ako jeste, skocidodaje na kraj i sumu: EBP+8 pokazuje na 2, na tekuću EBPold 1 što je različito od(1+2) 0, na stek # vrati 0 poziciju koja sadrži EAX = EAX+2 EBP pokazuje ovdje se stavlja 2 -1, Sa što je dec eax # ako nije, pozovi suma(i-1) vrijednost 2 [EBP+8] se čita 1 i očitano ret. EIP ; adresa add EAX, [EBP+8] EBP+8 pokazuje na parametar za naredni poziv 1, push eax Pošto je sa steka dodaje sumu: EBPold 2 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) se vrijednost stavljadodaj 1 -1, 1 štoi je EBP pokazuje ovdjeiz ret. EIP ; adresa add EAX, Sada se [EBP+8] krećezasanaredni izlascima dalje: pop ebp parametar poziv EBP+8 pokazuje na poziciju EBPold 3 ranijeprostor učinjenih poziva ove ret 4 # po povratku oslobodi rezervisan 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. ret. EIP ; adresa add EAX, [EBP+8] na tekuću sumu Primjetiti da je poziva. u ovom EBPold 3 parametra iz tekućeg trenutku EAX=0 zapiše što jeu Tekuća suma se uvijek 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: –. 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, p 1, p 2, p 3 push p 3 push offset p 2 push p 1 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 • Rezultat 1: . 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 dparam, [BX+param]. endr • Ulaz: . irpc param, 048 move dparam, [BX+param]. endr • Rezultat: move d 0, [BX+0] move d 4, [BX+4] move d 8, [BX+8]
Razlike između Intel i AT&T sintakse Intel AT&T Redosled operanada dst, 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+0 x 8] cmp [ebp+0 xc], eax cmovle eax, [ebp+0 xc] pop ebp ret 0 x 8 push %ebp mov %esp, %ebp mov 0 x 8(%ebp), %eax cmp %eax, 0 xc(%ebp) cmovle 0 xc(%ebp), %eax pop %ebp ret $0 x 8
Primjeri rada sa WIN 32 API
Zdravo svete (Windows). intel_syntax noprefix. arch i 386. extern _Message. Box. A@16: PROC. extern _Exit. Process@4: PROC. global _start. equ MB_OK, 0. data nl: . ascii "Zdravo svete “ #. asciz "Zdravo svete" . text _start: push MB_OK push offset nl push 0 call _Message. Box. A@16 push 0 call _Exit. Process@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 user 32 -l kernel 32 – 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. 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 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 mov eax, [ebp - 4] and eax, 0 ffh 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 Get. Std. Handle 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 Get. Std. Handle 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 x 1, x 2, z; mov __asm__( // ". intel_syntax noprefixn“ 1 "mov ebx, %2n" "add ebx, %3n" "mov %0, ebxn" "mov %1, ebxn" // ". att_syntaxn“ 1 : "=c"(x 1), "=m"(z) : "a"(x 2), "m"(t) : "ebx" ); return 0; } eax, DWORD PTR [ebp-16]2 mov ebx, eax 3 add ebx, DWORD PTR t 4 mov ecx 5, ebx mov DWORD PTR [ebp-20]6, ebx mov esi, ecx 7 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: f 1 PROC C par 1: DWORD, par 2: DWORD mov EAX, par 1 add EAX, par 2 f 1 ENDP f 1 PROTO C param 1: DWORD, param 2: DWORD INVOKE f 1, EAX, EBX ; ekvivalentno sa: ; push EBX ; push EAX ; call f 1 ; 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: –. 386 P ; 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: – Riječ (2 B): – Dupla riječ (4 B) : – Dvostruka dupla riječ (8 B) [name] DB init [, init. . . ] [name] DW init [, init. . . ] [name] DD init [, init. . . ] [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
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
- Slides: 98