Documente Academic
Documente Profesional
Documente Cultură
n limbaj de asamblare
Iulian Bdescu
PREFA
Limbajul de asamblare reprezint o metod de codificare simbolic, relativ
uor de citit i de interpretat de ctre om, a instruciunilor n format binar executate
de procesor. Spre deosebire de alte limbaje de programare, limbajul de asamblare
nu reprezint un limbaj de sine stttor, ci, mai degrab, o categorie de limbaje.
Fiecare familie de procesoare are propriul limbaj n asamblare. Din aceast relaie
de cauzalitate rezult c programatorul, pe lng noiunile specifice limbajului,
trebuie s dein i cunotine minime legate de structura intern a procesorului.
Devine evident c programarea n limbaj de asamblare este mult mai dificil dect
programarea ntr-un limbaj de nivel nalt. De fapt, limbajul de asamblare trebuie
utilizat numai atunci cnd este absolut necesar.
Deoarece asamblarea este o etap intermediar a procesului de compilare,
instrumentele folosite la translatarea codului scris n limbaj de nivel nalt n limbaj
main sunt capabile s gestioneze i programe hibride, n care codul limbajului de
nivel nalt conine secvene scrise n limbaj de asamblare. Altfel spus, compilatorul
accept introducerea de linii scrise n limbaj de asamblare direct n textul surs al
limbajului de nivel nalt, iar editorul de legturi poate combina module obiect
generate din surse scrise n limbaj de nivel nalt cu module obiect obinute din
surse scrise n limbaj de asamblare. Aceste construcii hibride apar din cteva
considerente, dintre care amintim:
optimizri atunci cnd sunt necesare secvene critice sub aspectul
timpului de execuie i consumului de memorie;
acces la instruciuni specifice procesorului;
n prezent, chiar i n aceste situaii, eficiena compilatoarelor a crescut
pn la nivelul n care acestea concureaz cu orice programator n asamblare, cu
excepia unuia foarte bun, iar evoluiile tehnologice fac ca avantajul codului main
optimizat prin instruciuni n asamblare s fie minim. Pe de alt parte, odat cu
rspndirea unor platforme relativ srace n resurse de procesare i memorie, cum
ar fi telefoanele mobile inteligente sau microcontrolere, se preconizeaz o cretere
a cererii de specialiti capabili s foloseasc ct mai eficient resursele existente.
Totodat, sunt i situaii n care programarea n limbaj de asamblare nu poate fi
evitat. De exemplu, anumite componente ale sistemului de operare au restricii
stricte n ce privete performana i consumul de resurse. Acestea nu se pot realiza
dect prin utilizarea ct mai eficient a instruciunilor i caracteristicilor
procesorului. n general, programatorii de sistem au nevoie de cunotine avansate
de programare n limbaj de asamblare.
n cazul programatorilor de aplicaii, principalele motive pentru care se
recomand experiena programrii n limbaj de asamblare const n familiarizarea
cu modul de organizare a programelor n memorie, cu principiile de funcionare a
diverselor componente hardware sau cu rolul sistemului de operare. Toate ajut
programatorul de aplicaii s scrie programe mai eficiente. De asemenea, depanarea
unui program de nivel nalt poate depi nivelul textului surs, ajungndu-se la
Mediul de dezvoltare
n procesul de dezvoltare a programelor, majoritatea programatorilor
folosesc un mediu integrat de dezvoltare (IDE - Integrated Development
Environment). Acesta pune la dispoziie toate uneltele necesare generrii
executabilului din codul surs, dar, totodat, ascunde detaliile acestui proces. n
aceast carte folosim componentele (editor de text, compilator, asamblor, editor de
legturi, depanator) individual, astfel nct rolul fiecruia s poat fi observat
direct. Sistemul de operare este Ubuntu, orice variant ntre 10.04 i 13.04.
Organizarea capitolelor
Primul capitol prezint sistemele de numeraie i modul de reprezentare a
caracterelor alfanumerice n sistemele de calcul. Sunt oferite numai informaii strict
necesare. Scopul este s ne nsuim rapid un set de cunotine minim pe baza cruia
s putem asimila, prin exemple practice, conceptele ulterioare.
Al doilea capitol prezint arhitectura sistemelor de calcul, cu cele dou
faete ale sale, hardware i software. Subiectul este abordat din perspectiva
programatorului. Ne concentrm asupra structurii interne a procesoarelor Intel de
32 de bii.
Al treilea capitol prezint limbajul de asamblare n contextul interaciunii
utilizator - calculator i al poziei sale n ierarhia limbajelor de programare.
Capitolul poate fi privit ca o ncercare de definire a limbajului de asamblare prin
gen i diferen specific, unde genul este reprezentat de clasa limbajelor de
programare, iar diferena specific de proprietile caracteristice. Din acest capitol
se desprinde faptul c limbajul de asamblare este o etap parcurs de compilatoare
n procesul de translatare a codului surs n cod main. Aadar, fie c dorim s-l
folosim sau nu, el este oricum utilizat de compilatoare n procesul de generare a
codului obiect.
Al patrulea capitol prezint procesul de dezvoltare a programelor n limbaj
de asamblare i uneltele utilizate n etapele acestuia. Sunt prezentate numai
informaii eseniale legate de limbajul de asamblare. Toate capitolele urmtoare vor
face referire la acestea.
Al cincilea capitol este dedicat conceptului central al programrii n orice
limbaj, cu att mai mult al programrii n limbaj de asamblare: organizarea i
adresarea memoriei principale.
Al aselea capitol prezint formatul instruciunilor main. Pe lng aceste
informaii, rolul acestui capitol este s familiarizeze cititorul cu modul de
prezentare a informaiilor n documentaia oficial Intel1.
Al aptelea i al optulea capitol acoper toate instruciunile dedicate
1
Cuprins
1.! REPREZENTAREA INFORMAIEI N SISTEMELE DE CALCUL14!
1.1.! Sisteme de numeraie ......................................................................14!
1.1.1.! Sistemul zecimal......................................................................15!
1.1.2.! Sistemul binar ..........................................................................15!
1.1.3.! Sistemul hexazecimal ..............................................................16!
1.2.! Operaii de conversie ......................................................................16!
1.2.1.! Conversia numerelor din zecimal n binar ..............................16!
1.2.2.! Conversia numerelor din zecimal n hexazecimal ...................17!
1.2.3.! Conversia numerelor din hexazecimal n binar .......................17!
1.2.4.! Conversia numerelor din binar n hexazecimal .......................18!
1.2.5.! Conversia numerelor reale.......................................................19!
1.3.! Reprezentarea caracterelor alfanumerice .......................................20!
1.4.! Exerciii ..........................................................................................22!
2.! ARHITECTURA CALCULATOARELOR..........................................24!
2.1.! Structura unui sistem de calcul .......................................................24!
2.1.1.! Arhitectura von Neumann .......................................................24!
2.1.2.! Modelul bazat pe magistral....................................................26!
2.1.1.! Magistrala de sistem ................................................................27!
2.1.2.! Unitatea de intrare/ieire .........................................................28!
2.1.3.! Memoria ..................................................................................29!
2.2.! Arhitectura IA-32 ...........................................................................30!
2.2.1.! Arhitectur i microarhitectur ................................................31!
2.2.2.! Structura de principiu a procesorului ......................................33!
2.2.3.! Funcionarea procesorului .......................................................37!
2.2.4.! Registrele procesorului ............................................................38!
2.2.5.! ntreruperile .............................................................................42!
2.3.! Exerciii ..........................................................................................43!
3.! LIMBAJUL DE ASAMBLARE ...........................................................44!
3.1.! Tipuri de limbaje de programare ....................................................46!
3.1.1.! Limbajul main ......................................................................46!
3.1.2.! Limbajul de asamblare ............................................................48!
3.1.3.! Limbaje de nivel nalt ..............................................................48!
3.2.! Procesul de compilare.....................................................................51!
3.2.1.! Preprocesarea...........................................................................53!
3.2.2.! Compilarea ..............................................................................53!
1. REPREZENTAREA INFORMAIEI N
SISTEMELE DE CALCUL
105
117
109
110
112
97
108
32
117
122
32
105
99
117
9
97
32
46
21329
17013
28016
28257
27765
8314
8291
26997
24864
24878
1819615331
1769300270
1629504117
135
67
33
16
8
4
2
1
:
:
:
:
:
:
:
:
2
2
2
2
2
2
2
2
=
=
=
=
=
=
=
=
67
33
16
8
4
2
1
0
rest
rest
rest
rest
rest
rest
rest
rest
1
1
1
0
0
0
0
1
=>
=>
=>
=>
=>
=>
=>
=>
d! =
d! =
d! =
d! =
d! =
d! =
d! =
d! =
1
1
1
0
0
0
0
1
Zecimal
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Binar
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
1010
1011
1100
1101
1110
1111
Hexazecimal
0
1
2
3
4
5
6
7
8
9
A
B
C
D
E
F
la stnga astfel:
10 0100 0101 1010.
Completm la stnga cu doi de zero,
0010 0100 0101 1010,
i identificm pentru fiecare nibble echivalentul n hexazecimal. Rezultatul este
245A!" .
Aplicm aceast regul pe irul de bii dat la nceputul capitolului. mprit
n octei, obinem:
53
42
69
75
6d
6e
70
61
6c
20
75
7a
20
69
63
75
61
61
20
2e
6d70
6c75
2063
6120
4275
6e61
207a
6975
mprit n dublu cuvinte (unirea dou cte dou a cuvintelor de mai sus):
53696d70
6c752063
61204275
6e61207a
6975612e
perioad, sau se depete capacitatea de reprezentare (se obin cifre suficiente dei
algoritmul nu s-a finalizat). Cifrele care depesc partea fracionar la fiecare
nmulire formeaz numrul n baza ctre care se face conversia.
Exemplul care urmeaz convertete numrul 0.57 n binar i hexazecimal.
Cum partea ntreag este deja 0, convertim numai partea fracionar.
0,57
0,14
0,28
0,56
0,12
0,24
0,48
0,96
0,92
0,84
0,68
0,36
*
*
*
*
*
*
*
*
*
*
*
*
2
2
2
2
2
2
2
2
2
2
2
2
=
=
=
=
=
=
=
=
=
=
=
=
1.14
0.28
0.56
1.12
0.24
0.48
0.96
1.92
1.84
1.68
1.36
0.72
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
=>
d! = 1
d! = 0
d! = 0
d! = 1
d! = 0
d! = 0
d! = 0
d! = 1
d! = 1
d! = 1
d!" = 1
d!! = 0
Aadar,
0,57!" = 0, d! d! d! d! d! d! d! d! d! d! d!" d!! = 0,100100011110! = 0,91E!"
1.4. Exerciii
1.1. Convertii n hexazecimal urmtoarele iruri de bii:
a) 0100 0001 1110 1100
b) 1011 1111 0011 1001
c) AAAA
d) FFFF
e) 5591
f) 66DD
A1
12
FF
80
e)
f)
g)
h)
0C
10
64
4E
i) A000
j) FFFF
k) ABCD
l) 1234
32.45
147.83
3.0125
255.255
e)
f)
g)
h)
15.32
7.8
63.25
18.5
d) 1094
e) 1452
f) 9966
1.9. Efectuai operaiile date mai jos folosind coduri ASCII hexazecimale.
Exprimai rezultatul tot ca reprezentare ASCII.
a)
b)
c)
d)
F + 20!"
F - 20!"
o - 20!"
m - 20!" 4D
e) q Q
f) g F
g) h a + 30!"
h) 4 + 6
2. ARHITECTURA CALCULATOARELOR
Unitatea de
intrare
Unitatea
Aritmetic i
Logic
Unitatea de
ieire
Unitatea de
control
Figura 2.1 Arhitectura von Newmann. Sgeile ngroate reprezint ci de date. Sgeile
subiri reprezint trasee de control.
Magistrala de
sistem
CPU (ALU,
Registre i
Control)
Memorie
Intrare i
Ieire (I/O)
Magistrala de date
Magistrala de adrese
Magistrala de control
Componentele fizice ale unui sistem (structuri mecanice, cabluri, cutii, circuite, etc.).
Random se refer la faptul c timpul de acces la orice unitate de memorie este constant
i independent de locaia fizic a acesteia sau de secvenele accesurilor anterioare.
dect dup finalizarea acestuia. Acest conflict a dat natere la o alt arhitectur
bazat pe tehnica programelor stocate, arhitectura Harvard. n arhitectura Harvard,
programul i datele sunt stocate n memorii diferite, fiecare conectat la procesor
prin propria magistral. Acest lucru permite procesorului s acceseze simultan att
instruciunile ct i datele programului.
n calculatoarele moderne, magistrala care conecteaz procesorul de
modulele externe de memorie nu poate ine pasul cu viteza de execuie a
procesorului. ncetinirea vitezei de transfer pe magistral poart numele de
trangulare von Neumann (von Neumann bottleneck).
Interaciunea procesorului cu dispozitivele de intrare/ieire se face prin
acelai mecanism ntlnit la interaciunea procesorului cu memoria. Dac
procesorul trebuie s citeasc date de la un dispozitiv de intrare, plaseaz adresa
acestuia pe magistrala de adrese i un semnal de citire pe magistrala de control.
Dispozitivul rspunde prin ncrcarea datelor pe magistrala de date.
Cnd procesorul trebuie s trimit date la un dispozitiv de ieire, plaseaz
datele pe magistrala de date, specific adresa dispozitivului pe magistrala de adrese
i activeaz semnalului de scriere pe magistrala de control. Deoarece viteza de
rspuns a diferitelor dispozitive I/O variaz drastic fa de viteza procesorului sau
memoriei, programatorul trebuie s utilizeze tehnici speciale de programare.
O alt caracteristic de baz a magistralelor este dimensiunea acestora,
adic numrul liniilor de conectare; avem magistrale de 8 bii (8 linii de conectare),
16 bii (16 linii de conectare), etc.. Dimensiunea fiecrei magistrale este
determinat de tipul de procesor (de 8, 16, 32, 64 de bii) i determin la rndul su
numrul de locaii de memorie ce pot fi adresate (capacitatea memoriei) i structura
porturilor din dispozitivele de intrare/ieire. De exemplu, o magistral de 32 de bii
poate adresa o memorie RAM de 2!" = 2!" 2! = 4!GB.
2.1.3. Memoria
Memoria sistemului, numit i memorie principal, este folosit la stocarea
informaiilor (instruciuni i date) n format binar i reprezint sursa sau destinaia
tuturor informaiilor. Toate informaiile iniiale i rezultatele prelucrrilor sunt
ncrcate, generate sau stocate temporar n memorie.
Memoria este organizat ca o colecie de locaii de memorare, numerotate
consecutiv, ncepnd cu zero. Unitatea de baz a memoriei este octetul. Ne putem
imagina memoria ca ir liniar de octei suprapui, asemenea unui dulap cu sertare
numerotate, fiecare sertar avnd capacitatea de 8 bii (Figura 2.3). Numrul
fiecrui sertar este ceea ce numim n general adres fizic, iar sertarul, n
programare, se numete locaie de memorie. Adresele permit identificarea fiecrui
octet din memorie. Mulimea total a adreselor fizice constituie spaiul adreselor
fizice, iar numrul de bii dintr-o locaie de memorie reprezint dimensiunea
locaiei sau formatul memoriei. n cazul nostru, considerm dimensiunea locaiei
ca fiind de 8 bii.
Figura 2.3 prezint o memorie adresat printr-o magistral de 20 de bii.
Dimensiunea magistralei, adic numrul liniilor de conectare, determin numrul
de locaii de memorie ce pot fi adresate. n acest caz, adresa fizic a primului octet,
de jos n sus, este reprezentat prin 20 de bii de 0.
FFFFFH
.
.
.
00003H
00002H
1 0 0 0 1 0 0 1 00001H
00000H
0
7
Binar
1
10
100
1000
10000
100000
1000000
10000000
100000000
1000000000
10000000000
100000000000
1000000000000
10000000000000
100000000000000
1000000000000000
10000000000000000
100000000000000000
1000000000000000000
10000000000000000000
100000000000000000000
Zecimal
1
2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
Adresele pornesc de la zero i se succed, ntr-un mod liniar, fr goluri sau ntreruperi,
pn la limita superioar impus de numrul total de bii dintr-o adres logic.
Indicatorul de instruciune
Memorie L1 Cache
Registrul de instruciune
Unitatea de control
Registre
Unitatea
Aritmetic i
Logic
Interfaa cu
magistralele
Registrul indicatorilor de
stare
numrului de adrese, cele dou spaii, al adreselor fizice i logice, pot fi egale sau
inegale. Ca urmare, procesorul trebuie s dispun de un mecanism de conversie a
adreselor (un mecanism de translatare a adreselor logice n adrese fizice). La
procesoarele Intel x86 mecanismul de translatare este inclus pe acelai substrat de
siliciu cu microprocesorul.
Datorit mecanismului de translatare a adreselor, modul de organizare al
memoriei poate fi definit prin software, de ctre sistemul de operare. Dac
procesorul permite adresarea liniar a ntregului spaiu de adrese fizice (adresele
fizice ncep de la 0 i avanseaz liniar), atunci i sistemul de operare poate organiza
memoria ca structur liniar (adresele logice ncep de la 0 i avanseaz liniar). Pe
de alt parte, atunci cnd procesorul poate adresa liniar numai anumite segmente de
memorie, segmente cu lungimi mai mici dect capacitatea total a memoriei,
vorbim de organizare segmentat. n acest caz, spaiul de adrese logice este
mprit n mai multe spaii cu adresare liniar, fiecare de lungime diferit, i o
adres logic este calculat ca sum ntre adresa de nceput a unui segment (adresa
de nceput a unui bloc de memorie) i un deplasament n cadrul acestuia.
Memorii intermediare
Cnd procesorul are nevoie de informaii aflate n memoria principal
trimite ctre unitatea de management a memoriei o cerere de citire memorie.
Unitatea de management a memoriei trimite cererea respectiv la memorie i,
atunci cnd informaia se afl pe magistrala de date, anun procesorul. Lungimea
ntregului ciclu procesor, controler de memorie, memorie, (napoi la) procesor -,
variaz n funcie de viteza memoriei i a magistralei de date. Aadar, o memorie
cu timp de acces mai mic contribuie semnificativ la performana sistemului.
Performana memoriei este cuantificat prin intermediul a doi parametrii:
timpul de acces - perioada de timp necesar memoriei s extrag datele din locaia
adresat, i timpul unui ciclu la memorie - timpul minim ntre dou operaii
succesive de acces la memorie.
Totui, viteza de lucru a memoriei i eforturile de cretere a acesteia reprezint
numai o faet a problemei. Timpul necesar datelor i intruciunilor s parcurg
traseul dintre memorie i procesor este mult mai mare dect timpul consumat de
procesor pentru prelucrarea acestora (acest fenomen se numete trangulare von
Neumann). De aceea, ntre procesor i memoria principal a fost introdus o
memorie intermediar, numit cache. Memoria cache este o memorie foarte rapid,
de mici dimensiuni (tipic, mai puin de 1MB), plasat foarte aproape de procesor.
Este proiectat s pstreze datele i instruciunile solicitate frecvent de procesor.
Pentru c preluarea datelor din cache are loc numai ntr-o fraciune din timpul
necesar accesrii memoriei principale, prezena memoriei cache salveaz foarte
mult timp. Principiul pe care se bazeaz mecanismul cache este regula 80/20, care
spune c, din toate programele, informaiile i datele din calculator, aproximativ
20% sunt utilizate 80% din timp. n consecin, este foarte probabil ca datele i
n lipsa unei surse de energie, informaiile sunt pierdute. Memoria RAM este tot o
memorie volatil.
Decodific
Execut
Extrage
Decodific
Execut
timp
index.
Registre de date
32 de bii
EAX
EBX
ECX
EDX
31
16
15
8 7
AH
BH
CH
DH
0
AL
BL
CL
DL
16 bii
AX
BX
CX
DX
16
15
ESI
EDI
0
SI
DI
Source Index
Destination Index
16
15
0
SP
BP
Stack Pointer
Base Pointer
Stiva este o zon din memoria principal organizat dup principiul LIFO
(Last In First Out), folosit ca mijloc de depozitare a datelor temporare. Stivele
sunt strict necesare n lucrul cu subprograme (proceduri i funcii), cnd registrele
interne trebuie eliberate n vederea execuiei unei funcii care va suprascrie
registrele cu propriile sale date. Eliberarea registrelor se face prin salvarea lor n
stiv, ntr-o anumit ordine, i refacerea lor din stiv la revenirea n programul
apelant.
Registre de control
31
EIP
16
15
0
IP
Instruction Pointer
31
16 15
Indicatori de stare
CF = Carry Flag
PF = Parity Flag
AF = Auxiliary Carry Flag
ZF = Zero Flag
SF = Sign Flag
OF = Overflow Flag
Indicatori de control
DF = Direction Flag
Indicatori de sistem
TF = Trap Flag
IF = Interrupt Flag
IOPL = I/O Privilege Level
NT = Nested Task
RF = Resume Flag
VM = Virtual 8086 Mode
AC = Alignment Check
VIF = Virtual Interrupt Flag
VIP = Virtual Interrrupt Pending
ID = ID flag
2.2.5. ntreruperile
Dispozitivele de intrare/ieire pun la dispoziia sistemelor de calcul
mijloacele prin care acestea pot interaciona cu exteriorul. Un dispozitiv poate fi un
dispozitiv de intrare (de ex., tastatura, mouse-ul), un dispozitv de ieire (de ex.,
imprimanta, monitorul), sau un dispozitiv de intrare i ieire (de ex., discul).
Calculatoarele folosesc dispozitive de intrare/ieire, numite i periferice, din dou
motive principale: s comunice cu exteriorul i s stocheze date. Dispozitive ca
imprimanta, tastatura, modemurile, plcile de reea sunt folosite n comunicaia cu
exteriorul, iar discul la stocarea informaiei. Dei scopurile sunt diferite, sistemul
comunic cu aceste dispozitive prin intermediul aceleiai magistrale de sistem.
Dispozitivele de intrare/ieire nu sunt conectate direct la magistrala de sistem;
comunicaia dintre sistem i periferic este gestionat de un controler de
intrare/ieire.
Procesoarele acceseaz registrele interne ale controlerului de intrare/ieire
prin porturile de intrare/ieire. Un port I/O reprezint adresa unui registru din
2.3. Exerciii
2.1. Care este diferena ntre microarhitectura i arhitectura procesorului?
2.2. Descriei ciclul de execuie.
2.3. Ce rol are memoria principal n structura de ansamblu a unui calculator?
2.4. Ce nseamn arhitectur von Neumann? Care este problema principal a
acesteia?
2.5. Care processor este ultimul reprezentant al arhitecturii IA-32?
2.6. Dac un procesor poate folosi o magistral de adrese de 64 de linii, care este
mrimea spaiului de memorie adresabil? Exprimai adresa ultimei locaii de
memorie n hexazecimal.
2.7. Care sunt registrele de uz general la procesoarele Intel de 32 de bii?
3. LIMBAJUL DE ASAMBLARE
Aplicaie
Crete nivelul
de
abstractizare
Nivel 4
Indepentent de
sistem
Nivel 3
Limbaj de asamblare
Dependent de
sistem
Nivel 2
Limbaj main
Nivel 1
Apeluri de sistem
Nivel 0
Hardware
10110000
10001001
11000010
10001001
11010001
10001001
11001000
7
C2
89
D1
89
C8
7
i octei de date. n plus, programul ar fi rulat numai de tipul de procesor cruia i-a
fost dedicat programul respectiv.
Limbajele de nivel nalt, de ex. C, au fost concepute pentru a nu ine cont
de caracteristicile tehnice specifice unui procesor. Instruciunile limbajului de nivel
nalt pot fi convertite n cod obiect pentru fiecare familie de procesoare n parte.
Totui, codul scris n limbaj de nivel nalt trebuie tradus printr-un mecanism sau
altul n formatul limbajului main. Din acest punct de vedere, programele scrise n
limbaj de nivel nalt pot fi clasificate n trei mari categorii:
compilate
interpretate
hibride
Programe compilate
Majoritatea aplicaiilor sunt create n limbaje compilate. Programatorul
scrie programul folosind sintaxa specific unui limbaj de nivel nalt. Fiierul care
conine programul n format text se numete fiier surs sau cod surs. Acest text
este convertit n cod main specific unui tip de procesor. De obicei, ceea ce se
numete comun compilare este un proces n doi pai:
conversia codului surs n cod obiect. Programul care realizeaz acest pas
se numete compilator.
editarea legturilor ntre diferite module obiect n vederea obinerii
executabilului. Programul care efectueaz acest pas se numete editor de
legturi, sau linker.
Compilator
Fiier surs
Fiier obiect
Editor de
legturi
Alt$fiier$obiect
Fiier$executabil
Biblioteci$de$fiiere$obiect
int main()
{
int i=1;
exit (0);
}
este compilat n urmtoarele instruciuni main:
55
89
83
C7
83
6A
E8
E5
EC
45
EC
00
D1
08
FC 01 00 00 00
0C
FE FF FF
Acest pas produce un fiier intermediar, numit fiier obiect. Fiierul obiect
conine cod obiect ntr-un anumit format neles de sistemul de operare, dei nc
nu poate fi rulat de acesta. Codul obiect conine numai datele i instruciunile
main ale funciilor definite n program. Programul poate avea ns nevoie de
componente aflate n alte fiiere obiect (de ex., funcia exit). Pentru adugarea
acestor componente este necesar nc un pas. Dup transformarea codului surs n
cod obiect, un editor de legturi leag fiierul obiect al programului de alte
fiiere obiect necesare acestuia i creaz fiierul executabil. Rezultatul editorului de
legturi este un executabil ce poate fi rulat numai de tipul de sistem de operare pe
care a fost compilat programul. Din nefericire, fiecare sistem de operare folosete
un format de fiier executabil (sau obiect) diferit. O aplicaie compilat pe un
sistem de operare tip Windows nu va rula pe Linux, sau viceversa.
Fiierele obiect care conin funcii foarte uzuale pot fi combinate ntr-un
singur fiier, numit bibliotec. Bibliotecile pot fi legate de aplicaii n timpul
compilrii (biblioteci statice) sau n timpul rulrii aplicaiei (biblioteci partajate).
Bibliotecile statice sunt acele biblioteci ale cror module obiect (componente) sunt
incluse n fiierul executabil n momentul editrii de legturi. n cazul bibliotecilor
partajate, modulele obiect sunt adresate n momentul lansrii n execuie sau n
momentul rulrii.
Programe interpretate
Spre deosebire de programul compilat, care ruleaz prin fore proprii,
programul interpretat este citit i rulat de un program separat, numit interpretor.
De-a lungul prelucrrii aplicaiei, interpretorul citete i decodific (interpreteaz)
fiecare instruciune n parte. Conversia programului n instruciuni main specifice
c n 1973 a fost rescris ntr-un limbaj de nivel nalt, i anume, C. GCC (GNU C
Compiler), prima versiune nonproprietar de compilator C, a fost scris de Richard
Stallman n 1989 pentru proiectul GNU. Numele su, GNU, care provine de la
GNU's Not Unix, proclam independena de restriciile impuse de drepturile de
copiere. Compilatorul GNU C este foarte folosit, nu numai deoarece este gratuit,
dar i pentru c a impus un standard n ceea ce privete utilitatea.
GCC translateaz un program scris n C n cod main. Realizeaz n
acelai timp cei doi pai ai procesului de compilare, adic compilarea propriu zis
i editarea de legturi. Rezultatul este un executabil ce poate fi stocat n memoria
calculatorului i rulat de procesor. Compilatorul GNU C lucreaz n etape, aa cum
se poate observa din Figura 3.6.
Cod surs
Unitate de
translatare
Cod n
asamblare
Cod obiect
Fiier
executabil
prog.c
prog.i
prog.s
prog.o
a.out
Preprocesare
Compilare
Asamblare
Editarea de legturi
gcc -E
gcc -S
gcc -c
gcc
return 0;
}
3.2.1. Preprocesarea
Etapa de preprocesare presupune nlocuirea/expandarea directivelor de
preprocesare din fiierul surs.
Preprocesorul utilizat de GCC n aceast etap este un program de sine
stttor numit CPP. Preprocesorul CPP analizeaz macrourile (cuvintele din surs
care ncep cu caracterul #) i le expandeaz. n cazul nostru, directiva #include
copiaz coninutul fiierului stdio.h n fiierul surs prog.c. Astfel, rezultatul
preprocesrii este tot un program surs, dar care nu mai include directivele
preprocesor, ci rezultatul acestora.
gcc -E prog.c -o prog.i
sau
3.2.2. Compilarea
Compilarea este etapa n care din fiierul preprocesat se obine un fiier n
limbaj de asamblare.
Rezultatul compilrii este un fiier lizibil, cu sufixul .s, coninnd
programul scris n limbaj de asamblare. Compilatorul propriu-zis folosit de GCC
este CC. CC este un program complex, deoarece nu exist o coresponden unu la
unu ntre instruciunile C i instruciunile n asamblare. n plus, poate optimiza
codul n funcie de cerine precum genereaz varianta mai rapid sau genereaz
varianta mai compact, rezultnd secvene diferite de instruciuni n asamblare.
Totui, niciun compilator nu este infailibil, optimizri complexe nu pot fi realizate
dect de un programator n asamblare.
gcc O0 -S prog.c -o prog.s m32
sau
cc -S prog.c -o prog.s
3.2.3. Asamblarea
Etapa de asamblare translateaz codul scris n limbaj de asamblare n cod
binar.
Asamblorul utilizat de GCC se numete AS. AS este un asamblor care
cunoate sintaxa AT&T. Rezultatul asamblrii este un fiier obiect. Fiierul obiect
reprezint un amestec de cod main i alte informaii necesare n faza de editare
de legturi; conine o list de simboluri (de ex., nume de variabile declarate, dar
nedefinite), locurile n care acestea apar n program, informaii de relocare
(specific adresele de memorie la care vor fi plasate datele i instruciunile
programului n etapa de editare de legturi). Nu n ultimul rnd, fiierele obiect pot
conine informaie de depanare, inclus n corpul lor cu opiunea -g. Aceast
opiune este necesar dac se intenioneaz depanarea programului.
push ebp
mov ebp,esp
sub esp,0x10
mov DWORD PTR [ebp-0x4],0x28
mov DWORD PTR [ebp-0x8],0x32
mov eax,DWORD PTR [ebp-0x4]
mov DWORD PTR [ebp-0xc],eax
mov eax,DWORD PTR [ebp-0x8]
mov DWORD PTR [ebp-0x4],eax
mov eax,DWORD PTR [ebp-0xc]
mov DWORD PTR [ebp-0x8],eax
mov eax,0x0
leave
obiect
sunt:
3.4. Exerciii
3.1. Ce relaie exist ntre limbajul de asamblare i limbajul main?
3.2. De ce este considerat limbajul de asamblare limbaj de nivel sczut iar C limbaj
de nivel nalt?
3.3. De ce este important portabilitatea? Dac doreti ca un program s fie
portabil, scrii programul n C sau n limbaj de asamblare?
3.4. Care este diferena ntre compilator i asamblor?
3.5. Ce rol are editorul de legturi?
3.7. Ce nseamn dezasamblare?
3.8. Ce conine un fiier obiect?
3.9. Care sunt avantajele programelor interpretate?
4. DEZVOLTAREA PROGRAMELOR N
LIMBAJ DE ASAMBLARE
speciale), primul caracter fiind liter sau caracter special. Fiecare etichet are
asociat o valoare - adresa relativ a liniei respective n cadrul segmentului.
Cmpul operaie reprezint mnemonica instruciunii.
Cmpul operanzi definete operandul sau operanzii asociai
instruciunii, conform sintaxei cerute de codul de operaie. Pot fi constante,
simboluri sau expresii de simboluri.
Cmpul comentariu reprezint un text oarecare precedat de caracterul
punct i virgul.
Cmpurile unei declaraii trebuie separate printr-un spaiu sau un caracter
TAB. Numrul acestor caractere despritoare rmne la discreia programatorului.
Dei asamblorul ignor tot ce depete un singur caracter TAB, utilizarea mai
multor caractere de acest fel permite evidenierea structurii programului i d
claritate textului.
...
section .text
global _start
_start:
nop
...
;instruciunile programului
;instruciunea No Operation
section .bss
date neiniializate
section .text
cod
foarte importante. Acestea sunt zonele de stiv i heap. Pentru a nelege mai bine
structura procesului trebuie s vorbim pe scurt de modul n care sistemul de
operare Linux organizeaz memoria principal n modul protejat de adresare.
0FFFFFFFFH
KERNEL SPACE
3 GB
0BFFFFFFFH
stiv
USER SPACE
Bloc superior
segment .bss
Bloc inferior
segment .data
segment .text
08048000H
00000000H
memorie nu sunt cunoscute dect la momentul rulrii (runtime). Zona heap este
partajat de toate bibliotecile i modulele ncrcate dinamic de proces. ncepe la
sfritul segmentului de date neiniializate i crete ctre adrese mai mari.
Aadar, n Linux, codul i datele programului ncep de jos, aproape de
08048000H, iar stiva ncepe de sus, aproape de 0BFFFFFFFH.
Unitate
Byte (octet)
word (cuvnt)
double word
quad word
ten bytes
Indicator (x)
B
W
D
Q
T
Dimensiune (octei)
1
2
4
8
10
Define
Define
Define
Define
Define
valoare iniial.
Byte
Word
Doubleword
Quadword
Ten Bytes
;aloc 1 octet
;aloc 2 octei
;aloc 4 octei
;aloc 8 octei
;aloc 10 octei
vector
db
55h,56h,57h
db
'hello',12,10,'$'
este abrevierea de la
cars
db
db
db
db
db
db
db
db
'h'
'e'
'l'
'l'
'o'
12
10
'$'
dw
10*25
este echivalent cu
_init
dw
250
(02H), iar cel mai puin semnificativ (LSB Least Significant Byte) este cel din
dreapta (A9H). Dac am inversa ordinea lor am obine un numr zecimal diferit
(A902H = 43266). De aceea, trebuie s fim ateni cum scriem valorile zecimale
reprezentate n hexazecimal i, mai ales, s tim cum judec sistemul de calcul
numerele hexazecimale. Din acest ultim punct de vedere, sistemul de calcul are
dou posibiliti:
octetul mai puin semnificativ este introdus la adres de memorie mai
mic, iar octetul mai semnificativ la adres de memorie mai mare;
octetul mai puin semnificativ este introdus la adres de memorie mai
mare, iar octetul mai semnificativ la adres de memorie mai mic.
Posibilitile se exclud reciproc. Despre procesorul care stocheaz octetul
cel mai puin semnificativ la adres mai mic i octetul cel mai semnificativ la
adres mai mare spunem c respect convenia little-endian. Procesorul care
stocheaz octetul cel mai semnificativ la adres mai mic, lucreaz conform
conveniei big-endian. Am spus procesorul, nu sistemul de operare.
I always regret that I didn't fix up some idiosyncrasies of the 8080 when I had a
chance. For example, the 8080 stores the low-order byte of a 16-bit value before
the high-order byte. The reason for that goes back to the 8008, which did it that
way to mimic the behavior of a bit-serial processor designed by Datapoint (a bitserial processor needs to see the least significant bits first so that it can correctly
handle carries when doing additions). Now there was no reason for me to continue
this idiocy, except for some obsessive desire to maintain strict 8080 compatibility.
But if I had made the break with the past and stored the bytes more logically,
nobody would have objected. And today we wouldn't be dealing with issues
involving big-endian and little-endian - the concepts just wouldn't exist.
Stephen Morse
Sistemul de operare Linux utilizeaz ambele tipuri de ordine, n funcie de
arhitectura hardware pe care este instalat. ntreaga arhitectur x86, de la 8086 pn
la Haswell, este little-endian. Alte arhitecturi hardware, ca ARM sau POWER, sunt
big-endian. Unele arhitecturile hardware, MIPS i Intel Itanium, sunt bi-endian, n
sensul c pot fi configurate s interpreteze valorile numerice ori ntr-un fel, ori n
cellalt. Dar asta nu e tot. Limbile europene evalueaz numerele de la dreapta la
stnga, dar irurile de caractere sunt evaluate invers, de la stnga la dreapta. Din
aceast perspectiv, dac un ir arbitrar de digii hexazecimali, ABCD, este
considerat ir de cifre hexazecimale (numr), de exemplu 0ABCDH, atunci MSB
este ABH i LSB este CDH. n memorie, va aprea n ordinea CD AB. Dac un ir
arbitrar de digii hexazecimali, ABCD, este considerat ir de caractere (cuvnt,
text), de exemplu 'ABCD', atunci MSB este caracterul D, iar LSB caracterul A. n
caracterul $
10
12
'o'
'l'
'l'
'e'
'h'
'a'
Etichet
zece
opt
_int
chars
character
_short
sir
cars
char
vector
var
0
Adres
0x80490bb
0x80490ba
0x80490b9
0x80490b8
0x80490b7
0x80490b6
0x80490b5
0x80490b4
0x80490b3
0x80490b2
0x80490b1
0x80490b0
0x80490af
0x80490ae
0x80490ad
0x80490ac
0x80490ab
0x80490aa
0x80490a9
0x80490a8
0x80490a7
0x80490a6
0x80490a5
0x80490a4
0x80490a3
0x80490a2
0x80490a1
0x80490a0
0x804909f
0x804909e
0x804909d
0x804909c
0x804909b
0x804909a
0x8049099
0x8049098
0x8049097
0x8049096
0x8049095
0x8049094
0x8049093
0x8049092
0x8049091
0x8049090
Nume
var
vector
char
cars
sir
_short
character
chars
_int
opt
zece
Deplasament
0
1
4
5
13
18
20
22
26
30
38
equ
equ
equ
equ
equ
db
equ
'Hello world'
$-mesaj
dd
equ
equ
'Hello world'
$-mesaj2
($-mesaj2)/4
n cazul EQU, simbolurile crora le-au fost alocate o valoare nu pot lua alte
j+1
j+10
[EBX+2]
(REServe a Byte)
(REServe a Word)
(REServe a Doubleword)
(REServe a Quadword)
(REServe Ten Bytes)
;rezerv un octet
;rezerv un cuvnt
;rezerv un dublu cuvnt
;rezerv un cuvnt cvintuplu
;rezerv 10 octei
resb 64
resw 1
;rezerv 64 de octei
;rezerv un cuvnt (2 octei)
vector:
resq 10
terminalului. n zona bufferului, liniile libere sunt indicate prin caracterul ~ (tilda).
n acest moment putem introduce text.
Salvarea fiierului nseamn o comand, aadar, comutm n mod comand
apsnd tasta ESC. Putem iei din editor cu urmtoarele comenzi:
ZZ salveaz bufferul pe disc i iei
:x - salveaz bufferul pe disc i iei (la fel ca ZZ)
:wq salveaz bufferul pe disc i iei (la fel ca ZZ)
:q iei din editor (funcioneaz numai dac modificrile sunt salvate)
:q! - iei din editor fr s salvezi bufferul pe disc
Cu urmtoarele comenzi putem scrie bufferul pe disc.
:w
- salveaz bufferul n fiierul curent (fisier.txt)
:w output.txt - salveaz bufferul n fiierul output.txt; nu rescrie fiierul
n caz c acesta exist
:w! output.txt - salveaz bufferul n fiierul output.txt; rescrie fiierul
dac exist.
Prima comand salveaz modificrile n fiierul dat ca argument. A doua i a treia
comand ne permite s salvm bufferul ntr-un fiier nou. Pentru a afla informaii
suplimentare despre comanda :w, putem folosi :help w. Implicit, ecranul va fi
mprit n jumtate, cu partea superioar afind informaii de ajutor. nchiderea
noii ferestre de face cu :q.
Navigarea prin text se face de regul cu tastele sgei. Dar pentru c este
incomod s mutm mna frecvent n colul din dreapta jos al tastaturii se pot folosi
tastele h, l, k, j (un caracter la stnga, dreapta, sus, jos). n plus, Space
deplaseaz cursorul cu un caracter la dreapta i Enter poziioneaz cursorul la
nceputul liniei urmtoare. Alte comenzi care pot deplasa cursorul sunt:
G
gg
^
$
w
b
fx
Fx
%
Modificarea unui cuvnt sau a unei pri de cuvnt (de la cursor pn la sfritul
cuvntului) se face prin poziionarea cursorului n locul de nceput i tastarea
comenzii cw (change word). Editorul trece automat n mod editare. Comanda cw
este o comand compus din alte dou comenzi; n acest caz, din alturarea
comenzilor c (change) i w (word). Alte exemple:
c$
c^
4cw
5dd
3x
3X
2G
schimbi toate apariiile lui text n aceste linii, adaugi opiunea g la sfrit.
Am acoperit numai comenzile de baz. Vim permite cteva comenzi foarte
sofisticate.13
Un lucru foarte important, toate uneltele folosite de noi n procesul de
asamblare (editorul de text, asamblorul) recunosc fiierele text ca fiiere scrise n
limbaj de asamblare dup extensia .asm. Cu alte cuvinte, fiierele surs scrise n
limbaj de asamblare trebuie ntotdeauna salvate pe disc cu extensia .asm (de ex.,
program.asm).
n continuare, scriei programul care urmeaz ntr-un fiier intitulat
prog.asm.
section .data
a
db
0fh
b
db
89
c
dw
045E3h
d
dw
65535
e
dd
001234567h
f
dd
1047000
g
db
0ffh
section .text
global _start
_start:
nop
;ncarc imediatul 8H n registrul de 8 bii AL
mov al, 8h
;ncarc imediatul 1239054 n registrul de 32 de bii EAX
mov eax, 1239054
;copiaz valoarea 89 (aflat n locaia de memorie cu adresa b) n registrul de 8 bii
BL
mov bl,[b]
;copiaz valoarea 45E3H (aflat n locaia de memorie cu adresa c) n registrul de
16 bii CX
mov cx,[c]
;copiaz valoarea 01234567H n registrul de 32 de bii EDX
mov edx,[e]
;ncarc registrul de 32 de bii EAX cu adresa etichetat a
mov eax,a
;ncarc registrul de 32 de bii EBX cu adresa etichetat b
mov ebx,b
13
ftp://ftp.vim.org/pub/vim/doc/book/vimbook-OPL.pdf
4.5.2. Asamblarea
Dup editarea i salvarea programului putem verifica prezena fiierului
surs cu ajutorul comenzii ls. n urma acestei comenzi ar trebui s vedei fiierul
prog.asm. Dac nu, verificai s nu fi omis ceva. Presupunnd c fiierul surs este
prezent, trebuie s l transformm n fiier obiect cu ajutorul unui asamblor. n
paragrafele urmtoarea prezentm dou din cele mai cunoscute asambloare.
NASM (Netwide Assembler) este un asamblor pentru arhitecturi Intel x86,
de la 16 pn la 64 de bii, care suport toate extensiile acestora pn n prezent.
Ruleaz pe o mare varietate de sisteme de operare (Linux, BSD, Windows).
Folosete o sintax similar cu cea Intel, dar mai puin complex, i vine cu un
pachet serios de macroinstruciuni.
YASM este o rescriere complet a asamblorului NASM, sub o licen
diferit14. Suport mai multe sintaxe de asamblare (de ex. NASM, GAS, TASM,
etc.) i la fel de multe formate de fiiere obiect. n general, YASM poate fi folosit
alternativ cu NASM.
Avantaje:
a fost primul care a implementat suport pentru arhitecturi x86_64. Acesta a
fost i marele su avantaj pn la NASM 0.99.00.
14
fiind un proiect mai dinamic, YASM rspunde mai rapid cererilor venite de
la utilizatori (de informaii sau noi opiuni).
poate asambla fiiere scrise att n sintaxa Intel ct i AT&T (gas).
implementeaz o interfa ce poate fi folosit de ctre compilatoare.
Dezavantaje:
NASM a fost utilizat i depanat intensiv. YASM este un proiect activ, e
posibil s nu fie att de bine testat ca NASM.
Datorit resurselor superioare implicate de-a lungul timpului n dezvoltarea
NASM, documentaia este mai complet.
identificate dup drepturile de execuie pe care le dein sau dup culoare verde din
listingul ls.
Atenie! Dac editorul de legturi refuz s proceseze comanda anterioar
i afieaz mesajul ld: i386 architecture of input file `prog.o' is incompatible with
i386:x86-64 output, nseamn c sistemul de operare este de tip x86-64 i editorul
de legturi nu poate lega un fiier obiect generat n format ELF de biblioteci de 64
de bii. O soluie este s asamblai iari programul folosind formatul ELF64. Dar,
pentru c subiectul acestei cri este studiul limbajului de programare pe arhitecturi
IA-32, este mai indicat s generai un executabil compatibil cu astfel de arhitecturi
folosind n procesul editrii de legturi parametrul -m, astfel:
ld -o prog prog.o -m elf_i386
Stop.
Integrarea cu Vim
Comanda :make ruleaz programul Make n directorul curent. Implicit,
Vim recunoate mesajele de eroare i le listeaz, facilitnd deplasarea prin surse.
Deplasarea ntre mesajele de eroare se face utiliznd comenzile :cnext i
:cprev. Aceste comenzi funcioneaz i dac eroarea nu se afl n fiierul curent
(deschide automat fiierele care conin eroarea).
Comanda :copen mparte ecranul pe orizontal i deschide o fereastr
nou, dedicat afirii erorilor. Poziionarea cursorului pe o anumit eroare, urmat
de un Enter sare n programul surs la linia respectiv. Navigarea ntre ferestre se
face cu comenzile de deplasare cunoscute (h,j,k,l) precedate de Ctrl-w.
nchiderea ferestrelor se face normal,:q. Fereastra cu erori poate fi creat la
momentul execuiei Make dac se folosete comanda compus :make | copen.
Dac se prefer mprirea ecranului pe vertical se folosete :make | vert
copen.
Aa cum am spus mai devreme, dac fiierul executabil este mai nou dect
toate fiierele de care depinde, Make va refuza s l regenereze. Cu toate acestea,
exist situaii cnd acest lucru este dezirabil. n special cnd modificai fiierul
makefile i dorii s l testai. Linux pune la dispoziie o comand, numit touch,
care are un singur rol: actualizeaz informaiile de timp ale fiierelor. Dac
executai comanda touch asupra fiierului surs sau obiect, de exemplu touch
prog.asm, fiierul va deveni mai nou dect fiierul executabil i Make va
repeta procesul de generare. Editorul Vim permite rularea comenzilor din linia sa
de comand. De exemplu, comanda anterioar poate fi executat din interiorul
editorului tastnd :!touch prog.asm.
Greeli de programare
Dac n urma procesului de asamblare se obine fiierul executabil,
nseamn c din punct de vedere al sintaxei programul este corect. Aceasta nu
nseamn ns c programul va face ceea ce inteniona programatorul s fac. Se
spune c programul care nu lucreaz conform planului conine una sau mai multe
greeli de programare (bug). O greeal de programare este acel ceva din program
care nu lucreaz aa cum a gndit programatorul s lucreze.
O eroare denot prezena n fiierul surs a ceva inacceptabil pentru
asamblor sau editor de legturi. O eroare previne finalizarea cu succes a procesului
de generare a fiierului executabil. n contrast, o greeal de programare este o
problem descoperit n timpul execuiei unui program. Greelile de programare,
sau de algoritm, nu sunt detectate de asamblor sau de editorul de legturi. Pot fi
beningne, de exemplu cuvinte scrise greit ntr-un mesaj destinat afirii pe ecran,
sau pot cauza ntreruperea prematur a programului. n unele cazuri, greelile de
programare pot ntrerupe execuia programului fr mesaje de avertizare. n
situaiile n care operaiile programului afecteaz sistemul de operare (ncearc s
acceseze zone de memorie protejate, etc.), acesta va ntrerupe programul i va afia
o eroare:
Segmentation Fault
Acest tip de eroare se numete eroare la rulare (runtime error).
De cele mai multe ori ns, programul nu va deranja sistemul de operare,
va rula complet, dar rezultatul nu va fi cel ateptat.
67
0fh
89
045E3h
65535
001234567h
1047000
0ffh
n cazul n care argumentul specific o linie, sunt afiate zece linii n jurul acelei
linii.
(gdb) info source
Current source file is prog.asm
Located in /home/stefan/prog.asm
Contains 29 lines.
Source language is unknown.
Compiled with stabs debugging format.
Does not include preprocessor macro info.
(gdb) list 19
14
mov al, 8h
15
mov bl,[r]
16
mov [r],al
17
mov eax, 1239054
18
mov bl,[b]
19
mov cx,[c]
20
mov edx,[e]
21
mov eax,a
22
mov ebx,b
23
mov [a],ah
mpreun cu argumentul (liniu) afieaz zece linii dinaintea poziiei la care s-a
ajuns prin listarea anterioar.
(gdb) list 4
5
6
7
8
9
10
11
12
_start:
13
b
db
c
dw
d
dw
e
dd
f
dd
g
db
section .text
global _start
89
045E3h
65535
001234567h
1047000
0ffh
nop
db
db
db
67
0fh
89
dd
dd
db
001234567h
1047000
0ffh
10
11
12
13
...
27
28
29
section .text
global _start
_start:
nop
mov eax,1
mov ebx,0
int 80h
Address, iar adresa din fiierul surs, afiat ca o combinaie de nume de fiier i
numr de linie, este dat de ultima coaloan.
Comanda tbreak (temporary break) seteaz un punct de ntrerupere
temporar. Un punct de ntrerupere temporar ntrerupe execuia programului o
singur dat, apoi este ters.
(gdb) tbreak 13
Temporary breakpoint 4 at 0x8048080: file prog.asm, line 13.
(gdb) info b
Num Type
Disp Enb Address What
1
breakpoint keep y 0x08048083 in _start at prog.asm:15
2
breakpoint keep y 0x08048081 in _start at prog.asm:14
4
breakpoint del y 0x08048080 in _start at prog.asm:13
Comanda disable dezactiveaz punctul de ntrerupere. Primete ca
argument numrul punctului de ntrerupere. Fr argumente dezactiveaz toate
punctele de ntrerupere. Activarea unui punct de ntrerupere se face cu enable.
(gdb) disable 2
(gdb) info b
Num Type
Disp Enb Address What
1
breakpoint keep y 0x08048083 in _start at prog.asm:15
2
breakpoint keep n 0x08048081 in _start at prog.asm:14
4
breakpoint del y 0x08048080 in _start at prog.asm:13
Ignorarea punctului de ntrerupere pentru un anumit numr de ori se
realizeaz cu comanda ignore. Comanda ignore primete dou argumente:
numrul punctului de ntrerupere ce trebuie ignorat i numrul de ori pentru care
acesta este srit.
tergerea unui punct de ntrerupere se face cu comanda delete.
Argumentul specific numrul punctului de ntrerupere ters. Dac nu sunt
specificate argumente, terge toate punctele de ntrerupere.
(gdb) delete
Delete all breakpoints? (y or n) y
(gdb) info b
No breakpoints or watchpoints.
Lansarea n execuie a programului se face prin comanda run. Programul
se oprete la punctul de ntrerupere. De acolo putem continua execuia pas cu pas
prin comanda next (abreviat n) sau step (abreviat s). Dei n cazul
programului actual next i step se comport la fel, ele sunt comenzi diferite.
Pentru o linie de cod care apeleaz o funcie, next va sri peste funcie, la
urmtoarea linie de cod, n timp ce step va intra n funcie.
Execuia programului depanat poate fi oprit oricnd cu comanda kill.
Starea programului poate fi consultat cu info program.
(gdb) b *_start+1
Breakpoint 1 at 0x8048081: file prog.asm, line 14.
(gdb) r
Starting program: /home/stefan/prog
Breakpoint 1, _start () at prog.asm:14
warning: Source file is more recent than executable.
14
mov al, 8h
(gdb) n
15
mov bl,[r]
(gdb) s
16
mov [r],al
(gdb) info program
Using the running image of child process 4717.
Program stopped at 0x8048089.
It stopped after being stepped.
Continuare execuiei se face cu ajutorul comenzii continue. Ieirea din
GDB se face prin comanda quit.
Coninutul registrelor este afiat cu info registers, info
registers $registru, info all-registers. Versiunile recente primesc ca
parametru i numele simplu al registrului, fr simbolul $. O abreviere util este de
genul i r ax. Tot de la versiunile mai noi se poate afia coninutul registrelor
mai mici dect cuvntul standard al arhitecturii (32 de bii). Eu am observat aceast
posibilitate de la versiunea 7.2.
(gdb) info registers
eax
0x8 8
ecx
0x0 0
edx
0x0 0
ebx
0x4367
esp
0xffffd400 0xffffd400
ebp
0x0 0x0
esi
0x0 0
edi
0x0 0
eip
0x8048089 0x8048089 <_start+9>
eflags
0x202
[ IF ]
cs
0x23 35
ss
0x2b 43
ds
0x2b 43
es
0x2b 43
fs
0x0 0
gs
0x0 0
(gdb) info registers $eax
eax
0x8 8
(gdb) info registers $eax $ebx $ecx
eax
0x8 8
ebx
0x4367
ecx
0x0 0
(gdb) i r al
Comanda care afieaz valorile locaiilor de memorie este x (examine). Este o
comand complex, care poate primi mai muli parametrii opionali. Apare n
urmtoarea form:
x /nyz &etichet
unde:
Formatul afirii
x
hexazecimal
d
zecimal (decimal)
u
zecimal fr semn (unsigned decimal)
o
octal
t
binar (two)
afieaz adresa att n zecimal ct i ca deplasament fa de cel
a
mai apropiat simbol precedent.
c
afieaz sub form de caractere (character)
s
afieaz ca ir terminat n caracterul 0
t
afieaz ca numr n virgul mobil
i
afieaz ca instruciune n cod main
Valoarea implicit este x. Valoarea implicit se schimb de fiecare dat
cnd este folosit comanda x.
Mrimea unitii
b
octei (byte)
h
2 octei sau jumti de cuvnt (half-word)
w
4 octei sau cuvnt de 32 de bii (word)
g
8 octei sau cuvnt gigant (giant word)
Valoarea implicit este w. Valoarea implicit se modific automat la
fiecare unitate care este specificat cu comanda x.
67
0x43
01000011
67 'C'
67
15
89
-29
0x67
0x45
0x23
0x01
Comanda care poate afia att valoarea unui registru ct i a unei locaii de
memorie este print. Comanda print poate folosi parametrii opionali de
formatare a afirii.
Registrele primite ca argument trebuie precedate de caracterul $ (dollar).
(gdb) print /x $eax
$4 = 0x12e80e
(gdb) print /d $ebx
$5 = 89
(gdb) print /t $ecx
$6 = 100010111100011
n cazul locaiilor de memorie, o etichet simpl dat ca argument duce la
afiarea valorii, o etichet precedat de caracterul & (ampersand) arat adresa
locaiei de memorie respective.
5. MEMORIA
times 100 db
Dar TIMES este mai versatil dect att. Argumentul lui TIMES (n
exemplul precedent argumentul a fost 100) nu este o constant numeric, ci o
expresie numeric. n consecin, se pot construi propoziii mult mai complexe.
buffer:
times
db
'Rezervat:'
19-$+buffer
db '-'
ls -l testMarime
-rwxr-xr-x 1 ubuntu users 1075 2011-02-19 22:05 testMarime
Aadar, am adugat un buffer de 8192 de octei, dar mrimea
programului a crescut cu numai 83 de octei. S testm mrimea programului la
care au fost adugai 8192 de octei iniializai cu 0.
;
;testMarime.asm
mov eax,1
mov ebx,0
este util, de cele mai multe ori dorim s transferm valoarea, nu adresa. Pentru a
obine valoarea, numele etichetei trebuie introdus ntre paranteze ptrate.
mov eax,[d]
;introduce n registrul EAX valoarea adresat de d
mov ecx,[ebx] ;introduce n registrul ECX valoarea adresat de
registrul EBX
Al doilea lucru care trebuie luat n considerare la transferul de date ntre
memorie i registre este dimensiunea datelor transferate. Instruciunea de mai sus
extrage de la o adres de memorie un numr de bii de date. Numrul de bii de
date transferat de la adresa d n registrul EAX nu este specificat direct n corpul
instruciunii. Coninutul locaiei de memorie adresate de eticheta d poate avea
dimensiunea de un octet, un cuvnt, un dublu cuvnt, un quad, etc.. Programatorul
a presupus c valoarea adresat prin eticheta d este reprezentat pe 4 octei i a
folosit registrul EAX. Dac programatorul dorea s extrag din memorie 8 bii de
date folosea ca destinaie un registru de 8 bii, precum AL. Dimensiunea registrului
specific numrul de bii extrai din memorie, sau, altfel spus, dimensiunea datelor
este dedus pe baza registrului destinaie.
;
;MemReg.asm
;
section .data
b
db
55h
w
dw
0ABCDh
d
dd
12345678h
section .text
global _start
_start:
nop
;ncarc n eax adresa variabilei b
mov eax, b
;ncarc n ebx adresa variabilei w
mov ebx, w
;ncarc n ecx adresa variabilei d
mov ecx, d
;ncarc n SI valoarea 0CD55H (16 bii de date ncepnd cu adresa b)
mov si, [b]
;ncarc n DI valoarea 0ABCDH
mov di, [w]
;ncarc n EDX valoarea 12345678H
mov edx, [d]
Valoare
0
60
0
50
0
40
0
30
0
20
0
10
7
Adres
0x804909e
0x804909c
0x804909a
0x8049098
0x8049096
valori
0
EAX
EBX
ECX
EDX
ESI
EDI
EBP
1
2
4
8
fr deplasament
deplasament pe 8 bii
deplasament pe 32 de bii
Schem
[baz]
[deplasament]
[baz + deplasament]
[baz + index]
[index scal]
[index scal + deplasament]
[baz + index scal]
[baz + index scal +
deplasament]
Exemplu
[edx]
[etichet]
sau [0x8049094]
[ebx + 0FH]
[eax + ebx]
[ecx 2]
[eax 4 + 32]
Descriere
Numai baz
Deplasament, adres
simbolic sau explicit
Baz plus constant
Baz plus index
Index nmulit cu scal
Index nmulit cu scal plus
deplasament
[ebp + edi 2]
Baz plus index nmulit cu
scal
[esi + ebp 4 + 1] Baz plus index mnulit cu
scal, plus deplasament
Adresarea indirect
Primul caz presupune c adresa efectiv este reprezentat de valoarea unui
registru de uz general aflat ntre paranteze ptrate. Am vzut deja c, pe lng date,
registrele pot conine adrese de memorie. Registrul care conine o adres de
Octei adresai
1
2
4
8
10
este un termen general pentru buffer sau tablou: o secven de elemente de date
aflate n memorie, toate de acelai tip i mrime.
Dar dac trebuie s parcurgem un vector ale crui elemente nu sunt
reprezentate pe un singur octet, ci pe cuvinte sau dublu cuvinte? Aici intr n scen
conceptul de scal. Urmtorul listing arat modul n care depanatorul
interpreteaz adresarea indexat n contextul formulei:
[baz + index scal + deplasament]
(gdb) disassemble _start
Dump of assembler code for function _start:
0x08048080 <+0>:
nop
=> 0x08048081 <+1>:
mov ebp,0x80490a4
0x08048086 <+6>:
mov ecx,0x4
0x0804808b <+11>:
mov ax,WORD PTR [ebp+ecx*1+0x0]
0x08048090 <+16>:
mov WORD PTR [ebp+ecx*1+0x0],0x46
0x08048097 <+23>:
mov eax,0x1
0x0804809c <+28>:
mov ebx,0x0
0x080480a1 <+33>:
int 0x80
End of assembler dump.
Registrul index este nmulit implicit cu un factor de scal. Pentru c nu
am specificat n instruciune multiplicatorul dorit, asamblorul a presupus c
factorul de scal este egal cu 1. Aadar, adresa efectiv este suma dintre baz i
produsul registrului index cu factorul de scal. n mod obinuit, factorul de scal
este egal cu mrimea elementelor individuale din vector. Dac vectorul const din
cuvinte de 2 octei, scala ar trebui s fie 2. Dac vectorul conine cuvinte de 4
octei, scala ar trebui s fie 4. Pentru elementele de tip quad, scala este 8.
S presupunem c avem un ir de 100 de elemente, fiecare de 4 octei, i c
dorim s extragem elementele 73, 84 i 98. Ct adunm la baz? n primul rnd,
deoarece elementele sunt cuvinte de 32 de bii, nseamn c factorul de scal este
4. Nu mai trebuie dect s specificm indexul elementelor dorite cu ajutorul
unui registru de uz general (altul dect cel folosit pentru baz). Amintii-v c
indexul ncepe de la 0. Aadar, primul element din vector are index 0, al doilea
element index 1, .a.m.d.. Programul exemplific i alt form de adresare
indexat, [index scal + deplasament].
;
;scala.asm
;
section .data
x
equ 1
y
equ 2
z
equ 3
buff:
times 80 dd x
times 10 dd y
times 10 dd z
section .text
global _start
_start:
nop
pot fi ntrebuinate numai cu date aliniate. Aadar, unele instruciuni lucreaz mai
bine cu date aliniate, altele chiar necesit aliniere. Unele sisteme de operare
necesit structuri de date aliniate.
Pentru procesoarele care folosesc memoria cache efectele nealinierii sunt
ntructva mai reduse. n general, accesul la datele nealinitate aflate ntr-o linie
cache nu necesit cicluri adiionale. Totui, accesul la datele nealiniate ntre liniile
de cache includ penalizare de vitez. n plus, accesarea memoriei n ordine
secvenial ajut la creterea probabilitii de cache hit, deoarece mai multe
blocuri de memorie vor fi citite n acelai timp n cache.
S judecm urmtorul caz: presupunem c lucrm cu date de 32 de bii
aliniate (aflate la adrese perfect divizibile cu patru). Procesorul va extrage datele n
grupuri de patru octei (deoarece sunt definite ca DD), de la adrese divizibile cu
patru (tot deoarece sunt definite ca DD).
Datele noastre sunt 1111 2222 3333 4444, aranjate n memorie astfel:
Tabelul 5.2 Date de 32 de bii aliniate
11
33
Valoare
11
22
33
44
22
44
Adres
..00
..00
11
33
Valoare
22
22
44
44
33
11
Adres
..00
..00
4
;aliniaz datele la frontiere de 4 octei
16
;aliniaz datele la frontiere de 16 octei
16,nop
;linie echivalent celei precedente
8, db 0 ;vezi OBS. II de mai jos
4, resb 1 ;aliniaz la frontiere de 4 octei n segmentul BSS
4
;linie echivalent celei precedente
Obs. I. Pentru ambele directive, argumentul trebuie s fie putere a lui doi (2, 4, 8,
16, 32).
Obs. II. Iniial, octeii liberi sunt completai cu valoarea 0x90 (codul main al
instruciunii NOP). Prin opiunea db 0, octeii liberi se completeaz cu 0.
Datele programului alignData.asm sunt nealiniate (directiva align 4
este comentat). Dup sir, urmeaz imediat d1 i d2:
;
;alignData.asm
;
section .data
sir
db
10,20,30,40,50,60
;align 4
d1
dd
0ffffffffh
d2
dd
0aaaaaaaah
section .text
global _start
_start:
nop
mov eax,1
mov ebx,0
int 080h
Dac urmrim ce se ntmpl n segmentul de date, observm:
Valoare
0xaa
0xaa
0xaa
0xaa
0xff
0xff
0xff
0xff
0x3c
0x32
0x28
0x1e
0x14
0x0a
7
Adres
0x804909d
0x804909c
0x804909b
0x804909a
0x8049099
0x8049098
0x8049097
0x8049096
0x8049095
0x8049094
0x8049093
0x8049092
0x8049091
0x8049090
Ultimul digit
...1110
...1100
...1011
...1010
...1001
...1000
...0111
...0110
...0101
...0100
...0011
...0010
...0001
...0000
Dac aliniem datele prin directiva align 4 (urmeaz date de tip DD),
obinem:
Valoare
Adres
Ultimul digit
0xaa
0x804909d
...1110
0xaa
0x804909c
...1100
0xff
0x804909b
...1011
0xff
0x804909a
...1010
0xff
0x8049099
...1001
0xff
0x8049098
...1000
0x90
0x8049097
...0111
0x90
0x8049096
...0110
0x3c
0x8049095
...0101
0x32
0x8049094
...0100
0x28
0x8049093
...0011
0x1e
0x8049092
...0010
0x14
0x8049091
...0001
0x0a
7
0x8049090
0
...0000
6. ARHITECTURA SETULUI DE
INSTRUCIUNI
mov r, m
mov m, i
mov r, m
mov m, r
mov
mov
mov
mov
Primul octet
10111DDD
10110DDD
89
88
rv,iv
rb,ib
rmv,rv
rmb,rb
Al doilea octet
nc 4
11SSSDDD
11SSSDDD
OPCODE
cod operaie
d w mod
1 0
76
Imediat
reg
r/m
543
210
nc 4 octei
Primul octet conine codul operaiei (pe 6 bii), adic tipul prelucrrilor ce
vor fi efectuate la execuia instruciuni (operaie aritmetic, logic, etc.). n cazul
nostru este vorba de transfer de date. Al doilea octet conine informaii cu privire la
registrele care conin operanzii, adresarea memoriei, etc..
Codul main pentru MOV r,r este format din doi octei - primul este
reprezentat n hexazecimal, al doilea n binar. Octetul binar ncorporeaz adresele
registrelor. SSS reprezint codul de trei bii pentru registrul surs. DDD
reprezint codul de trei bii pentru registrul destinaie. Dimensiunea
registrelor depinde de bitul 0 al primului octet. Acesta este notat cu w (word) i
codific lungimea operanzilor:
w=0, operand de tip octet;
w=1, operand de tip cuvnt; la 386, aceast valoare nseamn operand de
dimensiune complet (16/32 de bii n funcie de modul de lucru).
n aceste condiii, adresele registrelor sunt prezentate n Tabelul 6.2.
Registru
Adres
000
001
010
011
100
101
110
111
Cuvnt
w=1
EAX
ECX
EDX
EBX
ESP
EBP
ESI
EDI
Octet
w=0
AL
CL
DL
BL
AH
CH
DH
BH
B801000000
B89B000000
B800010000
B8FEFF0000
BB00000000
B10F
89C8
89D3
88CD
mov
mov
mov
mov
mov
mov
mov
mov
mov
eax,1
eax,155
eax,256
eax,65534
ebx,0
cl,15
eax,ecx
ebx,edx
ch,cl
Primul octet al instruciunii MOV EAX,1, B8, este codificat folosind codul
10111DDD, conform formatului MOV rv,iv. Biii DDD sunt completai cu
adresa registrului EAX, rezultnd codul binar 1011 1000 (B8H). Valoarea
imediatului, ntregul 1, este reprezentat imediat dup acest OPCODE, pe urmtorii
4 octei. Observai cum valoarea imediatului de 4 octei, 00000001H, este stocat
REG
4
R/M
1
Biii 7 i 6 sunt biii Mod. Cnd biii Mod sunt 11, cmpul R/M specific
un registru. n celelalte cazuri, biii R/M codific adrese de memorie.
Biii 5, 4, 3, formeaz cmpul REG, sau registru, i de cele mai multe ori
este desemnat prin notaia /r. Indic registrul care conine unul din
operanzi sau, mpreun cu cei 6 bii din primul octet, specific alte coduri
operaionale (Tabelul 6.4). Acest lucru nseamn c REG este determinat
de primul octet (OPCODE) al instruciunii.
Biii 2,1,0, formeaz cmpul R/M, sau registru/memorie. Dac Mod = 11,
atunci R/M indic registrul care conine al doilea operand. n toate celelalte
cazuri, R/M indic registrul implicat n aflarea locaiei de memorie ce
conine al doilea operand.
Tabelul 6.4 Instruciuni specificate de REG
/r
/0 000 /1 001 /2 010 /3 011
ADD
OR
ADC
SBB
Immed
ROL
ROR
RCL
RCR
Shift
NOT
NEG
Unary TEST i
INC
DEC
IncDec
INC
DEC CALL m CALL
Indir
FAR m
/4 100
AND
SHL
MUL
/5 101
SUB
SHR
IMUL
/6 110
XOR
SAR
DIV
JMP
JMP
FAR
PUSH
/7 111
CMP
IDIV
Unul din cazurile n care cei 6 bii de cod operaional prezeni n primul
octet sunt insuficieni pentru a defini complet operaia, i este necesar studierea
cmpului REG din octetul ModR/M, este cel al instruciunii AND ECX,64.
nregistrarea din Tabelul 6.3 pentru 83 este
Immed rmv,ib
Deoarece rndul intitulat Immed din Tabelul 6.4 are un AND sub /r = /4,
nseamn c instruciunea
AND rmv,ib
poate fi codificat folosind 83 ca OPCODE i 100 pentru biii REG din octetul
ModR/M. Din moment ce primul operand este registrul ECX, biii Mod trebuie s
fie 11 i R/M s conin 001 (adresa registrului ECX). Aadar, octetul ModR/M
este format din biii 11 100 001, sau E1H. ib nseamn imediat de tip octet valoarea sa se adaug la restul codului instruciune. n final, pentru AND ECX,64
se obine 83 E1 40.
octet - n acest caz, octetul care urmeaz octetului 0F. Majoritatea instruciunilor
din acest tabel au aprut pentru prima dat la procesorul 80386. Informaiile
furnizate de acest octet pot fi incluse n urmtoarele clase:
Specific complet o instruciune. De exemplu, 0F CA este codul pentru
BSWAP EDX.
Determin tipul operaiei, dar operanzii sunt specificai de octetul
ModR/M. De exemplu, instruciunea XADD (0F C1).
Determin o categorie de operaii, iar octetul ModR/M specific o anumit
operaie. De exemplu, dac primii doi octei ai instruciunii sunt 0F 01,
operaia este determinat de biii REG ai octetului ModR/M. Din Tabelul
6.6 reiese c, atunci cnd biii REG din ModR/M sunt 011, instruciunea
este LIDT.
Reprezint instruciuni MMX.
Tabelul 6.6 Instruciunile OF specificate de biii REG
/r
/0 000
SLDT
LocalT
rm2
SGDT
GlobalT
m6
/1 001
STR
rm2
SIDT
m6
/2 010
LLDT
rm2
LGDT
m6
/3 011
LTR
rm2
LIDT
m6
Bits
89D8
6689D8
B808000000
66B80800
mov
mov
mov
mov
eax,ebx
ax,bx
eax,8H
AX,8H
unde,
Mod
Reg/Opcode
R/M
Deplasament
Deplasament de
1,2 sau 4
octei
(opional)
Scal
Index
Imediat
Date imediate
de 1,2 sau 4
octei
(opional)
Baz
Pentru orice instruciune se poate folosi un prefix din fiecare grup, n orice
ordine.
Prefixul LOCK determin activarea semnalului de magistral omonim pe
durata execuiei acelei instruciuni. Are ca efect interdicia de cedare a
magistralelor unui alt dispozitiv. ntr-un sistem multiprocesor, acest semnal poate fi
utilizat de operaii atomice18 pentru a obine acces exclusiv la memoria partajat.
Prefixele de repetare provoac repetarea unei instruciuni pentru fiecare
element al unui ir. Acestea pot fi utilizate numai cu instruciuni pe iruri: MOVS,
CMPS, SCAS, LODS, STOS, INS i OUTS (vor fi studiate ntr-un capitol viitor).
Prefixele de segment foreaz unitatea de management a memorie s
foloseasc registrul de segment specificat n loc de cel implicit.
Prefixele de indiciu ramificare permit unui program s indice procesorului
cea mai probabil cale urmat de o instruciune de salt. Aceste prefixe pot fi
utilizate numai cu instruciuni de salt condiionat. Au fost introduse n procesoarele
Pentium 4 i Intel Xeon ca parte din extensiile SSE2.
Prefixul de dimensiune operand modific dimensiunea implicit a datelor
(de la 32 la 16 bii, sau invers).
Prefixul de dimensiune adres modific dimensiunea implicit a adreselor
(de la adrese de 32 de bii comut la adrese de 16 bii, sau invers).
R/M trebuie s se afle codul registrului destinaie, 000 (EAX). Valoarea coninut
n ModR/M se obine grupnd biii Mod REG R/M mpreun. Rezult 11 010 000
n binar, adic D0H. Instruciunea complet este codificat sub forma 89D0.
Tabelul 6.7 Formele de adresare pe 32 de bii cu ajutorul octetului ModR/M
Tabelul 6.7 face parte din manualul oficial pus la dispoziie de Intel pentru
dezvoltatorii de programe19 i prezint toate formele de adresare pe 32 de bii care
folosesc octetul ModR/M. Numerele hexazecimale reprezint valorile octetului
19
ModR/M n aceste cazuri. Prima coloan arat modul de calcul al adresei efective,
a doua i a treia, setarea biilor Mod, respectiv R/M. Coloanele care urmeaz
prezint valoarea octetului ModR/M pe ansamblu, dar i adresa registrului care se
gsete n cmpul REG. Privii rndul corespunztor coloanei Mod = 11 (ultimul
rnd) i cutai codul D0. Prima coloan denot mecanismul de adresare la
registre, coloana R/M arat c n cmpul R/M se afl adresa registrului EAX, iar
coloana pe care se afl D0 arat c n cmpul REG se afl registrul EDX.
Dimensiunea registrelor a fost descifrat anterior cu ajutorul bitului w din octetul
codului de operaie. Considerm urmtorul exemplu
mov ah,ch
Din Tabelul 6.3 codul operaiei pentru o instruciune de genul MOV rmb,rb este
88. Valoarea 0 a bitului w din OPCODE (1000 1000) indic operaie la nivel de
octet. Valoarea 0 a bitului d (10001000) specific faptul c sursa (registrul CH) se
afl n REG i destinaia (registrul AH) n R/M. n concluzie, octetul ModR/M este
format din irul de bii 11 101 100, EC. Codul complet al instruciunii este 88EC.
n cazul
mov edx,ecx
codul instruciunii este 89CA. Dac n acest cod schimbm biii Mod de la 11 la
00, valoarea octetului ModR/M devine 0A (00 001 010). Instruciunea devine:
mov [edx],ecx
n acest caz, biii Mod indic un operand aflat n memorie. Codurile 01 i 10 sunt
utilizate la codificarea deplasamentelor imediate, precum MOV EDI,[EAX+5],
unde deplasamentul este de tip ib sau iv. Aadar, codul pentru MOV
EDI,[EAX+5] este 8B78 05. Codul pentru MOV EDI,[EAX+12345678H]
este 8BB8 78563412. Totui, dac aceast schem de codificare se folosea
uniform nu ar fi fost disponibil destul spaiu pentru formatele complexe ale
modurilor de adresare. Din acest motiv, codul pentru registrul ESP a fost
ndeprtat, poziia acestuia folosindu-se ca intrare ntr-un nou spaiu de codificare
ce folosete octetul SIB. n urmtoarele rnduri descriem pe larg posibilitile
introduse de octetul ModR/M.
Mod = 00 nseamn mod de adresare indirect (prin registre), direct prin
deplasament (R/M = 101) sau SIB fr deplasament (R/M = 100).
Din Tabelul 6.7 reiese c Mod = 00 nseamn adresare bazat. Cmpul R/M
specific un mod de adresare indirect sau bazat/indexat, mai puin pentru R/M =
101, care denot adresare direct prin deplasament, i R/M = 100, care indic ctre
octetul SIB. Slotul ocupat de modul de adresare direct prin deplasament aparinea
adresrii indirecte prin registrul EBP. Intel a decis c n locul acesteia,
programatorii pot utiliza adresarea indirect [EBP+ib], cu ib = 0 (dei
instruciunea este puin mai lung). Aadar, Mod = 00 poate fi folosit pentru
urmtoarele cazuri:
[reg]
[deplasament]
[deplasament + constant]
Mod = 01 nseamn c octetul (octeii) modului de adresare este urmat de
un deplasament (imediat) de 8 bii. Atenie, nu confundai deplasamentul cu
dimensiunea datelor. Dimensiunea datelor este specificat n octetul OPCODE prin
bitul w. Un deplasament de 8 bii nu nseamn date de 8 bii, ci o ajustare n
segment curpins n gama -128..+127. Cmpul deplasament din formatul general al
instruciunilor IA-32 va fi format dintr-un singur octet poziionat imediat dup
octeii OPCODE (rapiditate).
Mod = 10 nseamn c octetul (octeii) modului de adresare este urmat de
un deplasament de 32 de bii.
Pentru fiecare mod de adresare cu octet SIB, cmpul Mod din octetul
ModR/M specific dimensiunea deplasamentului (constant de deplasare). Poate fi
zero, unu sau patru octei.
Tabelul 6.10 Adresarea cu SIB
Mod
00
01
10
R/M
100
100
100
Mod de adresare
SIB
SIB + deplasament
SIB + deplasament
[section .data]
valori: dd 10,20,30,40,50,60
[section .text]
[global _start]
_start:
nop
mov eax,valori
mov ebx,[valori]
mov ecx,[eax]
mov edx,[eax+4]
mov eax,3
16 0000001C
17 00000022
18 00000027
19 0000002A
20 0000002B
21 0000002D
22 0000002F
23 00000032
24 00000037
25
26 0000003B
27 00000040
28 00000045
8BB0[00000000]
BB[00000000]
8B0C83
41
6641
0409
83C009
05FF000000
6605FF00
mov esi,[valori+eax*1]
mov ebx,valori
mov ecx,[ebx+eax*4]
inc ecx
inc cx
add al,9
add eax,9
add eax,255
add ax,255
B801000000
BB00000000
CD80
mov eax,1
mov ebx,0
int 080
Listing 2.
objdump -d -M intel prog
prog:
nop
mov eax,0x80490c8
mov ebx,DWORD PTR ds:0x80490c8
mov ecx,DWORD PTR [eax]
mov edx,DWORD PTR [eax+0x4]
mov eax,0x3
mov esi,DWORD PTR [eax+0x80490c8]
mov ebx,0x80490c8
mov ecx,DWORD PTR [ebx+eax*4]
inc
ecx
inc
cx
add al,0x9
add eax,0x9
add eax,0xff
add ax,0xff
mov eax,0x1
mov ebx,0x0
int 0x80
Segment implicit
CS
DS
Deplasament
EIP
Adresa efectiv
DS
ES
SS
SS
ESI
EDI
ESP
Adresa efectiv
magnitudine
Cel mai mic numr este 1 1111111, adica 127, cel mai mare numr este 0 111
1111, adica +127. De unde rezult c domeniul de reprezentare al numerelor cu
semn pe un octet este -127 ..+127.
Se procedeaz similar pentru numerele reprezentate pe doi octei, pe patru
octei, etc.. Totui, la o privire mai atent, descoperim lucruri nu tocmai plcute.
De exemplu, valoarea zero are dou reprezentri distincte: 10000000 (-0) i
00000000 (+0). Lucru care complic unele operaii matematice. Mai mult,
operaiile aritmetice care folosesc reprezentarea cu bit de semn i magnitudine sunt
complexe. Daca adunm +1 cu 1, rezultatul este -2; rezultat fals (principiul de
adunare al numerelor binare este similar cu cel al numerelor zecimale: se adun
cifr cu cifr i se ine cont de transport):
0000 0001 +
1000 0001
------------1000 0010 = -2
Enumerm dezavantajele reprezentrii n magnitudine i bit de semn:
dou modaliti diferite de a reprezenta valoarea zero;
operaii aritmetice complexe;
numerele cu semn necesit instruciuni aritmetice diferite fa de cele ale
numerelor fr semn.
numrului 127. Cum 127 n binar este 0111 1111, complementul lui fa de unu, i
implicit numrul -127, va fi 1000 0000. Numrul -1 este complementul fa de unu
al irului binar 0000 0001, adic 1111 1110.
Dar i de aceast dat avem dou reprezentri diferite pentru valoarea 0:
0000 0000 (+0) i 1111 1111 (-0), cu efecte nedorite n efectuarea unor operaii
matematice. n plus, aritmetica n complement fa de unu este la fel de complicat.
2
1
0
0000 0010
0000 0001
0000 0000
1111 1111 +
0000 0001
-----------0000 0000
Bii de reprezentare
8
16
32
Plaj de valori
-128 ..+127
-32 768 ..+32 767
-2 147 483 648 ..+ 2 147 483 647
-127
129
00000000
AH
10000001
AL
0
Dei depanatorul, la judecarea valorii din EAX, nu arat biii de 0 din faa
registrului AL, acetia sunt luai n considerare. Dac judecm din aceast
perspectiv valoarea zecimal existent n EAX, obinem chiar 129 (MSB = 0).
movzx destinaie,surs
unde sursa poate fi registru sau locaie de memorie de 8 sau 16 bii, iar
destinaia registru de 16 sau 32 de bii. Activai instruciunea MOVZX din
program i studiai efectul acesteia.
Extinderea ntregilor cu semn
;
;cuSemn.asm
;
section .data
val
db
-127
section .text
global _start
_start:
nop
mov al,[val]
movsx eax,al
mov eax,1
mov ebx,0
int 080h
n programul de mai sus, rezultatul este cel ateptat numai dup folosirea
instruciunii
movsx destinaie,surs
La fel ca n cazul instruciunii MOVZX, sursa poate fi registru sau locaie
de memorie de 8 sau 16 bii, iar destinaia registru de 16 sau 32 de bii.
Extinderea ntregilor cu semn este diferit de extinderea ntregilor fr semn. n
cazul numerelor negative, MOVSX completeaz biii de semn cu 1, nu cu 0. Biii de
zero ar schimba valoarea numerelor negative. De exemplu, octetul -1 (11111111)
ncrcat ntr-o locaie de tip cuvnt unde octetul superior este completat cu bii de
0, d 0000000011111111, care n notaia cu semn este +127, nu -1. Pentru a se
pstra valoarea unui ntreg cu semn, bii introdui trebuie s fie de acelai tip cu
bitul de semn. n acest caz, se obine valoarea 11111111111111. n notaia cu
semn, un ir de bii de 1 reprezent -1.
n cazul unui numr pozitiv, extinderea se face cu bii de 0. Modificai
valoarea lui val n 127.
0000 1111 +
1111 0001
0000 0000
1111 1111 +
0000 0001
0000 0000
1000 1000 +
1000 0011
0000 1011
adunare cu transport(CF=1)
Domeniu de reprezentare
0 .. 255
0 .. 65 535
0 .. 4 294 967 295
Instruciunile aritmetice pot opera cu date de 8, 16 sau 32 de bii. Dac sunt adunai
operanzi mai mari de 32 de bii, se nsumeaz pe rnd dou numere de 32 de bii.
Urmtorul exemplu ilustreaz cum putem aduna pe arhitecturi de 32 de bii dou
numere ntregi fr semn de 64 de bii (folosim reprezentarea hexazecimal):
1 transport din prima adunare
2610 15E8 1357 9AE7 +
59AC B341 FE70 5324
7FBC C92A 11C7 EE0B
Efectum dou operaii de adunare. nti adunm primii 32 de bii mai puin
semnificativi ai operanzilor. Se obine jumtatea inferioar a rezultatului. Totodat,
aceast operaie de adunare poate produce transport, lucru care seteaz indicatorul
de transport. A doua operaie nsumeaz urmtorii 32 de bii ai operanzilor
mpreun cu indicatorul de transport generat de prima adunare. Acest operaie
produce jumtatea superioar a rezultatului de 64 de bii.
n mod similar, adunarea a dou numere de 128 de bii implic un proces n
patru etape, n fiecare se adun cuvinte de 32 de bii.
Domeniu de reprezentare
-128 ..+127
-32 768 ..+32 767
-2 147 483 648 ..+2 147 483 647
14 ctre 15, sau 30 ctre 31, n funcie de numrul de bii ai reprezentrii. Altfel
spus, OF semnalizeaz valorile interzise ale rezultatului n cazul operaiilor n
complement fa de doi. De exemplu, adunm +127 cu +127.
0111 1111 +
0111 1111
1111 1110
Numrul +127 este reprezentat pe un octet cu semn ca 0111 1111. MSB (Most
Significant Bit) este 0. Cnd adunm obinem rezultatul 1111 1110, rezultat eronat
n logica aritmeticii cu semn. MSB este 1 i nu 0, aadar 1111 1110 va fi interpretat
ca numr negativ, adic -2. n astfel de situaii, indicatorul de depire se
poziioneaz n 1.
transport
1000 +
1000
0000
1
0010
0101
1100
mprumut
1011 1100
1111
Instruciune
CMOVA/CMOVNBE
CMOVAE/CMOVNB
CMOVNC
CMOVB/CMOVNAE
Descriere
above/not below or equal
above or equal/not below
not carry
below/not above or equal
Condiie
(CF sau ZF) = 0
CF = 0
CF = 0
CF = 1
CMOVC
CMOVBE/CMOVNA
CMOVE/CMOVZ
CMOVNE/CMOVNZ
CMOVP/CMOVPE
CMOVNP/CMOVPO
carry
below or equal/not above
equal/zero
not equal/not zero
parity/parity even
not parity/parity odd
CF = 1
(CF sau ZF) = 1
ZF = 1
ZF = 0
PF = 1
PF = 0
Instruciune
CMOVGE/CMOVNL
CMOVL/CMOVNGE
CMOVLE/CMOVNG
CMOVO
CMOVNO
CMOVS
CMOVNS
Descriere
greater or equal/not less
less/not greater or equal
less or equal/not greater
overflow
not overflow
sign (negative)
not sign
Condiie
(SF xor OF) = 0
(SF xor OF) = 1
((SF xor OF) or ZF) = 1
OF = 1
OF = 0
SF = 1
SF = 0
mov ebx,0
int 080h
add ebx,10
mov eax,1
mov ebx,0
int 080h
section .data
val
db
132
Indiferent de semnul ntregilor, instruciunea ADD execut adunarea corect,
ceea ce nseamn c poate fi folosit att pentru ntregi cu semn ct i pentru
ntregi fr semn.
Dac rulm programul de mai jos:
;
;adunare2.asm
;
section .text
global _start
_start:
nop
xor eax,eax
xor ebx,ebx
mov al,254
mov bl,1
add bl,al
mov eax,1
mov ebx,0
int 080h
n registrul BL vom avea rezultatul 255. ncrcm n BL valoarea 2 i
reasamblm. Cnd rulm pas cu pas suntem foarte ateni la registrul indicatorilor
de stare. naintea instruciunii de adunare ADD BL,AL, singurul indicator setat
este IF. n urma adunrii, rezultatul din BL este 0, iar registrul indicatorilor de stare
are setat CF. Indicatorul de transport semnalizeaz c a fost depit domeniul de
reprezentare al numerelor fr semn pe un octet. Dup cum tim, valoarea maxim
a unui numr fr semn pe un octet este 255. Rezultatul adunrii 254 + 2 este 256.
Indicatorul de transport a semnalat depirea capacitii registrului care trebuia s
conin rezultatul, adic apariia unui transport spre rangul superior.
Cnd lucrm cu ntregi fr semn, CF semnalizeaz faptul c rezultatul
adunrii a depit limita domeniului de reprezentare. Dac nu suntem siguri c
EBX
d18d50e1
EDX
e09c1528
Aadar, vom aduna registrele EBX i EDX, apoi registrele EAX i ECX,
rezultatul final regsindu-se n EAX:EBX. ncepem prin adunarea registrelor EBX,
EDX folosind ADD. Deoarece este posibil ca suma celor dou valori s depeasc
domeniul de reprezentare adic s fie setat CF, urmtoarele dou registre: EAX i
ECX, vor fi adunate cu ADC.
;
;add64bit.asm
;
section .data
alfa dq 0401d0219d18d50e1H
beta dq 04016edece09c1528H
section .bss
rez
resd 2
section .text
global _start
_start:
nop
mov ebx,[alfa]
mov eax,[alfa+4]
mov edx,[beta]
mov ecx,[beta+4]
add ebx,edx
adc eax,ecx
mov dword [rez],ebx
mov dword [rez+4],eax
mov eax,1
mov ebx,0
int 080h
Se ruleaz urmrindu-se evoluia CF dup prima adunare.
Adunarea unor valori de dimensiune diferit
Cnd adunm dou valori de dimensiuni diferite trebuie s fim ateni la
conversia acestora.
;
;adunare3.asm
;
section .data
b
db
100
w
dw
300
d
dd
65800
section .text
global _start
_start:
nop
mov al,[b]
mov bx,[w]
movsx ax,al
add bx,ax
mov ax,[w]
mov ecx,[d]
movsx eax,ax
add ecx,eax
mov eax,1
mov ebx,0
int 080h
Programul folosete instruciunea MOVSX. Alte instruciuni de extindere
sunt:
;
section .data
b1
db
100
b2
db
-100
w
dw
300
d1
dd
65800
d2
dd
-354059
section .text
global _start
_start:
nop
mov al,[b2]
cbw
mov al,[b1]
cbw
mov bx,[w]
add ax,bx
mov ax,[w]
cwde
mov ecx,[d1]
add eax,ecx
mov eax,[d1]
cdq
mov eax,[d2]
cdq
mov eax,1
mov ebx,0
int 080h
sub ebx,10
mov eax,1
mov ebx,0
int 080h
section .data
val
db
90
Instruciunea SUB execut corect scderea indiferent de semnul ntregilor,
ceea ce nseamn c poate fi folosit att pentru ntregi cu semn ct i pentru
ntregi fr semn.
Dac rulm programul urmtor
;
;scadere2.asm
;
section .text
global _start
_start:
nop
xor
xor
mov
mov
sub
eax,eax
ebx,ebx
al,4
bl,2
bl,al
mov eax,1
mov ebx,0
int 080h
n registrul BL vom avea rezultatul -2. Rulm pas cu pas i suntem foarte
ateni la registrul indicatorilor de stare. naintea instruciunii de scdere SUB
BL,AL, singurul indicator setat este IF. Dup scdere, rezultatul din BL devine 0
iar registrul indicatorilor de stare are setat CF. Indicatorul de transport
semnalizeaz c a fost depit domeniul de reprezentare al numerelor fr semn pe
un octet. Dup cum tim, valoarea minim a unui numr fr semn pe un octet este
0. Rezultatul scderii 2 4 este -2. Indicatorul de transport a semnalat depirea
limitei inferioare a domeniului de reprezentare a numerelor fr semn.
Cnd scdem dou numere fr semn, CF semnalizeaz trecerea
rezultatului sub valoarea zero. n cazul numerelor considerate cu semn, acest lucru
se ntmpl foarte des i CF este lipsit de importan. n schimb, trebuie s inem
cont de indicatorul OF.
S judecm urmtorul exemplu:
;
;scadere3.asm
;
section .text
global _start
_start:
nop
mov eax,7
mov ebx,3
sub ebx,eax
jc sfarsit
mov eax,1
int 080h
sfarsit:
mov eax,1
mov ebx,0
int 080h
Programul scade dou numere fr semn: 3 i 7 (ambele pozitive).
Deoarece rezultatul scderii este un numr negativ (3 7 = - 4), instruciunea de
salt va fi executat. La sfritul programului vom avea n EBX valoarea 0 (n cazul
unui rezultat pozitiv, saltul nu s-ar fi executat).
Cnd scdem dou numere fr semn, 3 i 7, un rezultatul mai mic de zero
este considerat invalid (pentru c limita minim de reprezentare a numerelor fr
semn este 0). Totui, n ciuda faptului c valorile se presupuneau a fi fr semn i
rezultatul invalid, procesorul trece rezultatul -4 n EBX. De ce? Deoarece
procesorul nu tie ce fel de numere gndim noi, cu sau fr semn, el ia n calcul
ambele variante i seteaz indicatorii de stare CF i OF n concordan. Programul
trebuie s determine dac valoarea este n afara domeniului de reprezentare al
ntregilor cu/fr semn. n cazul scderii unor ntregi fr semn, CF indic faptul c
aceasta s-a soldat cu rezultat negativ. n ce privete ntregii cu semn, din moment
ce rezultatul poate fi negativ i CF nu este semnificativ, trebuie s ne bazm pe OF,
care semnalizeaz depirea domeniului de reprezentare a numerelor cu semn.
n concluzie, procesorul nu tie dac numerele sunt cu semn sau fr
semn. El seteaz CF sau OF lund n calcul ambele posibiliti.
CF setat dac a fost depit domeniul de reprezentare al
numerelor fr semn.
o [0 ..255] pentru 8 bii
o [0 ..65535] pentru 16 bii
o [0 ..4294967295] pentru 32 de bii
n cazul unui octet putem reprezenta astfel:
CF = 1
CF = 1
fr semn
0
255
cu semn
- 128
OF = 1
+127
OF este setat atunci cnd adunm sau scdem doi ntregi de acelai semn i
se obine un rezultat de semn diferit.
Scderea numerelor mai mari de 32 de bii
Pentru a scdea dou numere de 64 de bii, mprim numerele n dou
pri de 32 de bii fiecare i efectum operaia de scdere pe rnd. La a doua
operaie se scade i valoarea lui CF. Instruciunea necesar se numete SBB
(Subtract with Borrow).
;
;sub64bit.asm
;
section .data
alfa dq 0401d0219d18d50e1h
beta dq 04016edece09c1528h
section .bss
rez
resd 2
section .text
global _start
_start:
nop
mov ebx,[alfa]
mov eax,[alfa+4]
mov edx,[beta]
mov ecx,[beta+4]
sub ebx,edx
sbb eax,ecx
mov dword [rez],ebx
mov dword [rez+4],eax
mov eax,1
mov ebx,0
int 080h
O instruciune nrudit cu SUB este instruciunea NEG. Instruciunea NEG
calculeaz complementul fa de doi al unei valori. Acelai rezultat se poate obine
prin scderea valorii respective din zero, cu instruciunea SUB. Instruciunea NEG
se execut ns mai rapid.
Surs
8 bii
16 bii
32 bii
Operand implicit
AL
AX
EAX
Rezultat
AX
DX:AX
EDX:EAX
Surs
8 bii
16 bii
32 bii
Operand implicit
AL
AX
EAX
Rezultat
AX
DX:AX
EDX:EAX
cu urmtoarele cazuri:
imul
imul
imul
imul
imul
imul
r16,r/m16
r32,r/m32
r16,imm8
r16,imm16
r32,imm8
r32,imm32
r16,r/m16,imm8
r16,r/m16,imm16
r32,r/m32,imm8
r32,r/m32,imm16
div divizor
Tabelul 7.6 Instruciunea de mprire DIV
Demprit
AX
DX:AX
EDX:EAX
Divizor
8 bii
16 bii
32 bii
Ct
AL
AX
EAX
Rest
AH
DX
EDX
Dac unul din operanzi se afl n memorie, procesorul activeaz n mod automat
semnalul LOCK (n cazul unei structuri multiprocesor, acest lucru asigur accesul
exclusiv la memorie pentru un singur procesor). Dei util n unele situaii mai
speciale, acest proces este mare consumator de timp i penalizeaz performana
programelor.
Instruciunea XCHG este foarte util n operaiile de sortare. De asemenea,
este la fel de util n interschimbarea octeilor unui cuvnt, procedur care
corespunde conversiei ntre formatele little-endian i big-endian. De exemplu,
xchg al,ah
convertete valoarea din registrul AX dintr-o form n cealalt. Gndii-v cum
putei converti dintr-un format n altul valoarea unui registru de 32 de bii. Nu este
o problem lipsit de semnificaie, n unele situaii ordinea octeilor are mare
importan. De exemplu, transferul datelor n reea se face conform conveniei bigendian. S facem un experiment. Am vzut n capitolele precedente c asamblorul
permite forme interesante de adresare imediat. De exemplu, urmtoarea
instruciune este perfect legal i nseamn introducerea unui ir de caractere n
registrul EAX:
mov eax,'ABCD'
Introducei instruciunea ntr-un program i afiai coninutul registrului
EAX imediat dup execuia acesteia:
(gdb) i r eax
eax
0x44434241
1145258561
31
15
'D' (44H)
'C' (43H)
'B' (42H)
'A' (41H)
n registrul EAX sunt caracterele ASCII: 'A' (41H), 'B' (42H), 'C' (43H) i 'D'
(44H). Deoarece caracterele ASCII sunt reprezentate pe 8 bii, cele patru elemente
ale irului ncap perfect n cei 32 de bii ai registrului. Chiar dac la prima vedere
pare c sunt introduse n ordine invers, acest lucru nu este adevrat. Ne aducem
aminte c arhitectura IA-32 lucreaz conform conveniei little-endian, care
stocheaz octetul mai puin semnificativ la adresa mai mic. Acest criteriu se aplic
i la registre. n registrul EAX, AL se afl pe poziia celui mai puin semnificativ
octet, urmat de AH i de ceilali doi octei. ntr-un ir de caractere, caracterul mai
puin semnificativ este cel din extrema stng. Aadar, 'A' este introdus n registrul
AL, 'B' n registrul AH, .a.m.d.. Rezultatul este cel menionat. Dac avei nc
dubii, introducei valoarea 'ABCD' n memorie i comparai ordinea octeilor.
Presupunem c trebuie s transmitem coninutul registrului EAX prin reea
i trebuie s-l convertim n format big-endian.
31
15
'A' (41H)
'B' (42H)
'C' (43H)
'D' (44H)
1094861636
Instruciune
XCHG
BSWAP
XADD
CMPXCHG
CMPCHG8B
Descriere
Comut valori ntre dou registre sau ntre un registru i o locaie
de memorie.
Convertete octeii unui registru de 32 de bii din ordine littleendian n big-endian, sau invers.
Comut dou valori i stocheaz suma n operandul destinaie.
Compar acumulatorul cu o valoare extern i schimb operanzi
n funcie de rezultatul comparaiei.
Compar dou valori de 64 de bii fiecare i le schimb n funcie
de rezultatul comparaiei.
Instruciunea XADD comut valorile ntre dou registre sau ntre un registru
i o locaie de memorie, apoi adun valorile i stocheaz rezultatul n destinaie.
xadd destinaie,surs
unde sursa trebuie s fie obligatoriu un registru de 8, 16 sau 32 de bii, iar
destinaia registru sau locaie de memorie de dimensiunea corespunztoare.
Instruciunea XADD este disponibil ncepnd cu procesoarele 80486.
Instruciunea CMPXCHG compar operandul destinaie cu o valoare din AL,
AX sau EAX.
cmpxchg destinaie,surs
Dac valorile sunt egale, valoarea operandului surs se ncarc n destinaie. Dac
valorile sunt diferite, operandul destinaie este ncrcat n EAX, AX sau Al.
Operandul destinaie poate fi registru sau locaie de memorie de 8, 16 sau 32 de
bii. Operandul surs trebuie s fie un registru de dimensiune corespunztoare.
Instruciunea CMPXCHG este disponibil ncepnd cu procesoarele 80486.
;
;cmpxchg.asm
;
section .data
val dd
5
section .text
global _start
_start:
nop
mov eax,5
mov ebx,3
cmpxchg [val],ebx
mov eax,1
mov ebx,0
int 80h
Instruciunea CMPXCHG8B are acelai efect cu instruciunea CMPXCHG,
numai c lucreaz cu valori de 8 octei (de aici i 8B de la sfrit). Aceast
instruciune a aprut odat cu procesorul Pentium. Formatul instruciunii primete
un singur operand:
cmpxchg8b destinaie
Operandul destinaie adreseaz o locaie de memorie de 8 octei. Acetia sunt
comparai cu valoarea coninut n perechea de registre EDX:EAX (EDX registrul
superior i EAX registrul inferior). Dac valorile sunt egale, valoarea de 64 de bii
aflat n perechea de registre ECX:EBX este compiat n locaia de memorie
adresat de destinaie. Dac nu sunt egale, valoarea din locaia de memorie este
ncrcat n perechea de registre EDX:EAX.
;
;cmpxchg8b.asm
;
section .data
val dq
1122334455667788h
section .text
global _start
_start:
nop
mov edx,11223344h
mov eax,55667788h
mov ecx,ffffffffh
mov ebx,aaaaaaaah
cmpxchg8b [val]
mov eax,1
mov ebx,0
int 80h
AND
0
1
0
0
0
1
0
1
OR
0
1
0
0
1
1
1
1
XOR
0
1
0
0
1
1
1
0
NOT
0
1
1
0
Exemple:
0000 1111 AND
1001 0110
0000 0110
1100 1001 OR
0100 1010
1100 1011
xor eax,eax
xor al,al
;eax = 0
;al = 0
00001111
; al = 0000 0011
00001111
; al = 1110 1111
n urma operaiei SAU logic, biii din registrul AL aflai pe poziia celor cu
valoarea 0 din masc vor rmne neschimbai, cei aflai pe poziie 1 din masc vor
fi setai.
Pe lng instruciunile echivalente funciilor logice, exist i instruciunea
TEST. TEST realizeaz operaia logic I bit cu bit ntre doi operanzi, dar nu
modific nici sursa nici destinaia. TEST nu genereaz rezultat, dar modific
indicatorii de stare. Este echivalentul logic al instruciunii CMP.
Instruciunea TEST ne permite s testm un singur bit dintr-un operand sau
s poziionm indicatorii de stare fr a modifica operandul. De exemplu, n cazul
instruciunii
test al,00000100b
indicatorul de zero se poziioneaz n 1 dac bitul din registru aflat pe poziia
echivalent a celui marcat cu 1 n masc este 0.
n urma instruciunii
test ax,ax
indicatorii de stare sunt poziionai ca i cnd a fost efectuat o instruciune AND,
dar coninutul registrului AX nu se modific.
CF
0
CF
0
;
section .text
global _start
_start:
nop
mov al,-64
shl al,1
movsx ebx,al
shl al,1
movsx ecx,al
mov al,-64
sal al,1
movsx ebx,al
sal al,1
movsx ecx,al
mov eax,1
mov ebx,0
int 080h
Instruciunea SAR (Shift Arithmetic Right) este diferit de instruciunea
SHR, deoarece completarea biilor vacani cu zero ar duce la suprascrierea celui
mai semnificativ bit (bitul de semn). Astfel, instruciunea SAR introduce bitul cel
mai puin semnificativ n CF i deplaseaz cu o poziie spre dreapta ceilali bii, dar
conserv bitul cel mai semnificativ.
CF
S 1 0 0 0 0 0 1
Astfel, dac deplasm un octet spre dreapta cu instruciunea SAR, numai
cei 7 bii mai puin semnificativi sunt deplasai. Cel mai semnificativ bit se
pstreaz.
;
;deplAritm2.asm
;
section .text
global _start
_start:
nop
mov al,-4
sar al,1
movsx ebx,al
mov al,-4
shr al,1
movsx ecx,al
mov eax,1
mov ebx,0
int 080h
Instruciuni de deplasare dubl
Setul de instruciuni cuprinde i dou instruciuni pentru deplasri de 32 i
64 de bii. Aceste dou instruciuni primesc ca operanzi de intrare cuvinte sau
dublu cuvinte i genereaz un rezultat de aceeai lungime (cuvnt sau dublu
cuvnt). Instruciunile de deplasare dubl au trei operanzi:
shld destinaie,surs,contor
shrd destinaie,surs,contor
unde destinaia i sursa pot fi cuvinte sau dublu cuvinte. Operandul
destinaie poate fi registru sau locaie de memorie, iar operandul surs obligatoriu
registru. Contorul numrului de bii deplasai poate fi specificat direct, ca valoarea
imediat, sau de registrul CL.
O diferen semnificativ fa de instruciunile de deplasare este c biii
introdui n destinaie prin deplasament provin de la operandul surs.
shld
shrd
15/31
CF
destinaie(reg/mem)
15/31
surs(reg)
15/31
15/31
surs(reg)
destinaie(reg/mem)
CF
Instruciunile din acest grup modific numai indicatorul de transport. Ceilali cinci
unde operandul este un cuvnt sau dublu cuvnt aflat n memorie sau registru.
Destinaia va reine indexul primului bit de 1 ntlnit n operand n timpul scanrii
directe sau inverse. Destinaia trebuie s fie obligatoriu un registru de 16 sau 32 de
bii. Dac toi biii sunt zero, indicatorul de zero se poziioneaz n 1; n caz
contrar, ZF = 0 i registrul destinaie memoreaz poziia. Instruciunile de scanare
pe bit afecteaz numai indicatorul de zero. Ceilali cinci indicatori de stare rmn
nemodificai.
Condiie
ZF = 1
ZF = 0
SF < > OF, valori cu semn
SF < > OF sau ZF = 1, valori cu semn
SF = OF, valori cu semn
SF = OF i ZF = 0, valori cu semn
CF =1, valori fr semn
SETBE/SETNA
SETNB/SETAE/SETNC
SETNBE/SETA
SETO/SETNO
SETP/SETPE
SETNP/SETPO
SETS/SETNS
CF = 1 sau ZF = 1, fr semn
CF = 0, valori fr semn
CF = 0 i ZF = 0, fr semn
OF = 1 / respectiv OF = 0
PF = 1, paritate par
PF = 0, paritate impar
SF = 1 / respectiv SF = 0
adres
mov eax,1
mov ebx,0
int 080h
nop
mov ebx,DWORD PTR ds:0x80490b0
mov edi,0x1
mov eax,DWORD [edi*4+0x80490b0]
cmova ebx,eax
jmp 80480a3 <sfarsit>
inc edi
mov ecx,DWORD [edi*4+0x80490b0]
cmova ebx,ecx
080480a3 <sfarsit>:
80480a3:
b8 01 00 00 00
80480a8:
bb 00 00 00 00
80480ad:
cd 80
mov eax,0x1
mov ebx,0x0
int 0x80
adres
unde <condiie> rezult din starea indicatorilor de stare CF, OF, PF, SF, ZF,
iar adresa este o etichet n cadrul codului.
Tabelul 7.8 Instruciuni de salt condiionat
Instruciune
Descriere
JA
Jump if Above
JAE
Jump if Above or Equal
JB
Jump if Below
JBE
Jump if Below or Equal
JC
Jump if Carry
Condiie
CF=0 and ZF=0
CF=0
CF=1
CF=1 or ZF=1
CF=1
JCXZ
JE
JG
JGE
JL
JLE
JMP
JNA
JNAE
JNB
JNBE
JNC
JNE
JNG
JNGE
JNL
JNLE
JNO
JNP
JNS
JNZ
JO
JP
JPE
JPO
JS
JZ
Jump if CX Zero
Jump if Equal
Jump if Greater (signed)
Jump if Greater or Equal (signed)
Jump if Less (signed)
Jump if Less or Equal (signed)
Unconditional Jump
Jump if Not Above
Jump if Not Above or Equal
Jump if Not Below
Jump if Not Below or Equal
Jump if Not Carry
Jump if Not Equal
Jump if Not Greater (signed)
Jump if Not Greater or Equal (signed)
Jump if Not Less (signed)
Jump if Not Less or Equal (signed)
Jump if Not Overflow (signed)
Jump if No Parity
Jump if Not Signed (signed)
Jump if Not Zero
Jump if Overflow (signed)
Jump if Parity
Jump if Parity Even
Jump if Parity Odd
Jump if Signed (signed)
Jump if Zero
CX=0
ZF=1
ZF=0 and SF=OF
SF=OF
SF != OF
ZF=1 or SF != OF
unconditional
CF=1 or ZF=1
CF=1
CF=0
CF=0 and ZF=0
CF=0
ZF=0
ZF=1 or SF != OF
SF != OF
SF=OF
ZF=0 and SF=OF
OF=0
PF=0
SF=0
ZF=0
OF=1
PF=1
PF=1
PF=0
SF=1
ZF=1
mov ebx,0
int 080h
Atunci cnd valoarea din EAX devine 2, comparaia va seta indicatorul de
stare ZF i instruciunea de salt condiionat nu se va efectua. Astfel, ceea ce este
cuprins ntre eticheta bucla i instruciunea JNZ bucla este o secven de
program care se repet de un anumit numr de ori. Putem presupune c scopul este
s incrementm registrul EDX cu 3.
Dei putem folosi acest cod, Intel a pus la dispoziie un set de instruciuni
dedicate realizrii de bucle repetitive.
Tabelul 7.9 Instruciuni iterative
Instruciune
LOOP
LOOPE/LOOPZ
LOOPNE/LOOPNZ
Descriere
Repet bucla pn cnd registrul ECX devine 0
Repet bucla pn cnd registrul ECX devine 0 sau ZF = 0
Repet bucla pn cnd registrul ECX devine 0 sau ZF = 1
MOVS<x>
unde x reprezint dimensiunea locaiei de memorie ce va fi tranferat la
execuia instruciunii: B, W sau D. Astfel, instruciunea mbrac trei forme:
Destinaie
DS:ESI
7
;sir.asm
;
section .data
sir1 db
10,20,30,40,50,60,70,80
section .bss
sir2 resb 8
section .text
global _start
_start:
nop
mov esi, sir1
;sau lea esi,[sir1]
mov edi, sir2
;sau lea edi,[sir2]
movsb
movsb
movsb
movsb
movsb
movsb
movsb
movsb
mov eax,1
mov ebx,0
int 080h
Instruciunea LEA (Load Effective Address) calculeaz adresa efectiv al celui de
al doilea operand i o stocheaz n registrul specificat ca prim operand. n cazul de
mai sus, rezultatul este identic cu cel al instruciunii MOV ESI,sir1 (n acest caz
sir1 nu este inclus n paranteze ptrate deoarece ne intereseaz tocmai adresa).
Spre deosebire de instruciunea MOV, LEA necesit paranteze ptrate pentru cel de
al doilea operand al su:
lea edi,[sir1]
eax,[ebx+ecx*4+100]
Observaii:
Pentru a copia un ir de 8 octei, avem nevoie de 8 instruciuni MOVSB.
MOVSB nu face dect s copieze un singur octet de la adresa dat de
ESI la adresa dat de EDI i apoi s incrementeze automat ESI i EDI.
Putem micora numrul de linii ale programului nlocuind cele 8
instruciuni MOVSB cu 4 instruciuni MOVSW sau cu 2 instruciuni
MOVSD.
De asemenea, putem copia irul ncepnd cu primul ctre ultimul element
al irului (de la adres mai mic ctre adres mai mare) sau ncepnd de la ultimul
ctre primul element (de la adres mai mare ctre adres mai mic). Direcia de
execuie a instruciunii MOVS poate fi setat prin intermediul indicatorul de direcie
(DF - Direction Flag) din registrul EFLAGS.
dac DF = 0 (iniial), instruciunile MOVS incrementeaz ESI i EDI cu
1, 2 respectiv 4.
daca DF = 1, instruciunile MOVS decrementeaz ESI i EDI cu 1, 2,
respectiv 4.
Poziionarea indicatorului de direcie se face cu ajutorul instruciunilor
CLD (CLear Direction) i STD (SeT Direction). Cnd copiem irul de la sfrit la
nceput trebuie s fim ateni ca n ESI i EDI s avem adresele de sfrit. n acest
caz, exemplul trebuie modificat astfel:
lea
lea
std
esi, [sir1+7]
edi, [sir2+7]
Instruciune
REPE
REPNE
REPZ
REPNZ
Descriere
Repeat while equal
Repeat while not equal
Repeat while zero
Repeat while not zero
Fiecare din aceste forme are dou variante, n funcie de numrul de bii ai
procesorului.
Pentru procesoarele de 16 bii, LODSB copiaz un singur octet de la locaia
de memorie adresat de registrele DS:SI n registrul AL (modul real de adresare a
memoriei) i automat, n funcie de starea indicatorului DF, incrementeaz sau
decrementeaz SI.
Pentru procesoarele de 32 de bii, LODSB copiaz un singur octet de la
locaia de memorie adresat de registrele DS:ESI n registrul AL (modul protejat de
adresare a memoriei) i automat, n funcie de starea indicatorului DF,
incrementeaz sau decrementeaz ESI.
Instruciunile LODSW i LODSD au aceeai sintax, numai c vor copia
cte 2, respectiv 4 octei, n registrul AX, respectiv EAX, i vor incrementa sau
decrementa corespunztor ESI.
IA-32
AL
7
0
DS:ESI
7
ES:EDI
IA-32
AL
7
0
IA-32
Fiecare din aceste trei forme are dou variante, n funcie de numrul de
bii ai procesorului, similar instruciunilor MOVS i LODS.
Instruciunile STOSW i STOSD au aceeai sintax, numai c vor copia n
memorie cte 2, respectiv 4 octei, din registrul AX, respectiv EAX, i vor
incrementa sau decrementa EDI.
Urmtorul program exemplific comportamentul instruciunilor LODSD i
STOSD.
;
;addElem.asm
;
section .data
vector1 dd 10,20,30,40,50,60,70,80,90
section .bss
vector2 resd 9
section .text
global _start
_start:
nop
lea esi,[vector1]
lea edi,[vector2]
mov ecx,9
L1:
lodsd
add eax,5
stosd
dec ecx
jnz L1
mov eax,1
mov ebx,0
int 80h
Destinaie
DS:ESI
7
;cmpStr.asm
;
section .data
sir1 db "Sir de caractere"
sir2 db "Sir decaractere"
section .text
global _start
_start:
nop
mov eax,1
lea esi,[sir1]
lea edi,[sir2]
mov ecx,16
cld
repe cmpsb
je egale
mov ebx,ecx
int 80h
egale:
mov ebx,0
int 80h
Nu este necesar s rulai programul prin intermediul depanatorului.
Valoarea din registrul EBX poate fi obinut din linie de comand cu:
./cmpStr
echo $?
7.10.1.
eax
eax
eax
ax
eax
mov eax,1
mov ebx,0
int 080h
Asamblm i rulm programul pas cu pas. Adresa de nceput a stivei se afl
n registrul ESP. La momentul acestei rulri adresa coninut de ESP este
0xbfc7b4f0. n urma introducerii registrului ECX n stiv, adresa devine
0xbfc7b4ec,
00
03
ba
c4
7
00
03
ba
c4
01
5e
00
00
00
64
7
ESP = 0xbfc7b4ea
ESP = 0xbfc7b4e6
0
7.10.2.
Instruciuni
Descriere
PUSHA/POPA Introduce/extrage din stiv toi regitrii de uz general de 16 bii
PUSHAD/POPAD Introduce/extrage din stiv toi regitrii de uz general de 32 de
bii
PUSHF/POPF Introduce/extrage din stiv primii 16 bii mai puin
semnificativi din reg. EFLAGS
PUSHFD/POPFD Introduce/extrage din stiv registrul EFLAGS
Registrele salvate n stiv de ctre instruciunea PUSHAD sunt: EAX, ECX,
EDX, EBX, ESP, EBP, ESI, EDI. Primul registru care poate fi extras este EDI.
Instruciunea POPAD extrage din stiv n ordinea invers a salvrii lor cu PUSHAD.
;
;stiva2.asm
;
section .bss
val resd 1
section .text
global _start
_start:
nop
mov eax,0ffffaaaah
mov ebx,012345678h
mov ecx,055556666h
mov edx,011112222h
mov ebp,0bbbbcccch
mov esi,0ddddeeeeh
mov edi,088889999h
pusha
pushf
popf
popa
pushad
pop dword [val]
pushfd
popad
popfd
mov eax,1
mov ebx,0
int 080h
7.10.3.
Instruciunile PUSH i POP nu sunt singurele care pot introduce sau extrage
date din stiv. Putem introduce sau extrage date din stiv prin adresare bazat,
folosind ca adres de baz adresa memorat n registrul ESP. De cele mai multe ori
ns, n locul utilizrii directe a lui ESP, se copiaz adresa din ESP n EBP.
Instruciunile care acceseaz parametrii stocai n stiv folosesc ca adres de baz
valoarea lui EBP. Dar nu intrm acum n detalii, acesta este subiectul unui capitol
viitor.
Stiva este utilizat n trei scopuri principale:
spaiu de stocare a datelor temporare;
transferul controlului n program;
transmiterea parametrilor n timpul unui apel de procedur.
n paragrafele urmtoare dezbatem primul caz, cel al salvrii temporare a datelor,
celelalte dou sunt studiate pe larg n capitolul intitulat Funcii.
De exemplu, s presupunem c dorim s interschimbm coninutul a dou
locaii de memorie de 32 de bii. Nu putem interschimba coninutul acestora direct,
deoarece instruciunea XCHG nu poate primi ca operanzi dou locaii de memorie.
ns putem folosi un registru intermediar. Secvena de instruciuni
mov eax,[val1]
xchg [val2],eax
mov [val1],eax
ndeplinete sarcina propus, dar are nevoie de un registru suplimentar. Din cauza
numrului limitat de registre de uz general, acesta nu este tocmai uor de gsit. De
obicei conine date care trebuie salvate nainte de secven i refcute dup, ca n
urmtorul exemplu:
;
;swap.asm
;
section .data
val1 dd
125
val2 dd
225
section .text
global _start
_start:
nop
mov eax,0ffffffffh
push eax
mov eax,[val1]
xchg eax,[val2]
mov [val1],eax
pop eax
mov eax,1
mov ebx,0
int 080h
Codul acceseaz memoria de cinci ori. V amintii c instruciunea XCHG,
n cazul n care acceseaz memoria, este mare consumatoare de timp? Din
considerente de performan, probabil ar fi fost mai indicat s folosim patru
instruciuni MOV, astfel:
mov
mov
mov
mov
eax,[val1]
ebx,[val2]
[val1],ebx
[val2],eax
n acest caz, programul complet acceseaz memoria de opt ori (4 instruciuni MOV
plus 4 instruciuni necesare salvrii i restaurrii registrelor intermediare). Dar stiva
este o structur de tip LIFO, secvena de instruciuni POP realizeaz procesul
invers al secvenei de instruciuni PUSH. O modalitate elegant de interschimbare a
unor valori este s folosim numai stiva.
;
;swapWithStack.asm
;
section .data
var1 dd
125
var2 dd
225
section .text
global _start
_start:
nop
mov eax,0ffffffffh
push [var1]
push [var2]
pop [var1]
pop [var2]
mov eax,1
mov ebx,0
int 080h
De aceast dat nu este necesar salvarea coninutului vreunui registru (nu mai
folosim registre intermediare). Observai i faptul c instruciunile PUSH i POP
permit transferul datelor de la memorie la memorie.
Stiva este folosit frecvent atunci cnd trebuie eliberat un set de registre
utilizat de secvena de cod care urmeaz.
7.11. Exerciii
2
2
2
2
2
2
2
=
=
=
=
=
=
=
1
1
1
0
1
1.7
1.4
0.8
1.6
1.2
0.4
0.8
Nu ajungem niciodat la zero. Acest numr are o reprezentare finit n zecimal, dar
infinit n binar. Echivalentul lui 1/6 n zecimal (0.1666666...). Calculatorul va
putea numai s-l aproximeze. O caracteristic cheie a operaiilor cu numere ntregi
este c rezultatul este ntotdeauna precis. De exemplu, dac adunm doi ntregi,
obinem ntodeauna rezultatul exact. n contrast, operaiile cu numere reale sunt
ntotdeauna predispuse la aproximri.
Numerele reale reprezentate n notaie tiinific au trei componente
N = (1)! M2!
unde,
E, exponent
8 bii
Mantis
scdem 16. Reinei c exponenii care au toi biii de zero sau unu sunt rezervai
pentru numere speciale (precum zero sau infinit).
Revenind la reprezentarea numrului 32, valoarea iniial a exponentului, 6, se
adun cu valoarea predefinit, 16, i rezult 22:
0 1 0 1 1 0 1 0 0 0 0 0 0 0
Dar mai ntmpinm o problem deloc de neglijat. Numerele pot avea mai
multe reprezentri. Altfel spus, sistemul nu garanteaz unicitatea reprezentrii.
Toate formele urmtoare reprezint numrul 32 (0.12! , 0.012! , 0.0012! ,
etc.).
0 1 0 1 1 0 1 0 0 0 0 0 0 0
0 1 0 1 1 1 0 1 0 0 0 0 0 0
0 1 1 0 0 0 0 0 1 0 0 0 0 0
0 1 1 0 0 1 0 0 0 1 0 0 0 0
0 1 0 0 1 1 1 0 1 0 1 0 0 0
0 1 0 1 0 1 0 1 1 1 1 0 0 0
Condiie
E = 255 i M 0
E= 255 i M = 0
E = 0 i M 0
E= 0 i M = 0
Valoare N
NaN
(1)!
(1)! 2!!"# (0. M)
(1)! 0
Reprezentri
Not a Number
Numr denormalizat
0
Zero
Zero nu poate fi reprezentat direct. Deoarece avem bit de semn, pentru 0 exist
reprezentare pozitiv i negativ.
Infinit
Avem reprezentare separat pentru plus i minus infinit. Un numr diferit de zero
mprit la zero d infinit. De exemplu, 1.0/0.0 produce infinit.
NaN
De obicei apare n cazurile unor operaii definite greit. Exemplul standard este
mprirea 0.0/0.0, care nu are o valoare definit. Ele sunt identificate printr-un
exponent care are toi biii de 1 i o mantis diferit de cea pentru configuraia
valorilor de tip infinit. Exist dou tipuri de astfel de configuraii, denumite NaN
de semnalizare (SNaN Signaling NaN) i NaN tcut (QNaN Quiet NaN).
SNaN are mantisa de forma 1.0xx ... x, unde x poate avea orice
valoare (dar nu pot fi toi zero, deoarece reprezint valoarea infinit). Cnd
ntlnete o astfel de configuraie FPU genereaz o excepie ce semnalizeaz
efectuarea unei operaii invalide. Programatorul o poate folosi pentru a indica unele
condiii de eroare, cum ar fi o variabil n virgul mobil neiniializat.
Cealalt configuraie, QNaN, are mantisa de forma 1.1xxx...x. Primul
1 este cel implicit. O astfel de configuraie apare cnd rezultatul unei operaii
aritmetice este nedefinit matematic.
Orice instruciune care are un operand de unul din cele dou tipuri de NaN
va genera un rezultat de tip NaN.
Numere denormalizate
Fa de numerele normalizate, acestea sunt numere cu mai puini bii de precizie i
valori mai mici. S presupunem c permitem tuturor biilor dintr-un numr
0000 0000
care este mapat la numrul 0. (1!" )2!!"# , unde 1!" nseamn un ir de 23 de bii
de 1. Din moment ce punctul binar este urmat de 23 de bii, acest numr are
precizie de 23 de bii.
Cel mai mic numr denormalizat este format din irul de bii:
0
0000 0000
care se traduce prin 0.0!! 12!!"# , sau 1.02!!"# . Acest numr are un singur bit
de precizie. Cei 22 de zero nu afecteaz precizia numrului. Dac nu credei,
gndii-v c numrul zecimal 256 are trei digii de precizie, iar 00256 are tot trei
digii de precizie. Cifrele de zero nu afecteaz numrul digiilor de precizie. n mod
similar, dac avem 0.000256, cifrele de zero ne ajut s plasm 256 corect, dar nu
sunt digii care trebuie plasai n mantis. 0.02560 are patru digii de precizie
deoarece cifra 0 din dreapta se adaug preciziei. Aadar, n urma punctului binar
avem 22 de bii de zero urmai de un 1, i cei 22 de zero nu au legtur cu numrul
de bii ai mantisei.
Prin denormalizare am obinut 1.02!!"# ca fiind cel mai mic numr, spre
deosebire de 1.02!!"# , cea mai mic valoare pe care am fi obinut-o dac
numrul ar fi fost normalizat. S-au sacrificat totui 22 de bii de precizie.
Un ir de bii 0! n cmpul exponent este mapat la un exponent -126.
Totui, pentru numerele n virgul mobil reprezentate cu standardul IEEE-754,
valoarea predefinit este -127. De ce este -126 n loc de -127?
Ca s rspundem la aceast ntrebare trebuie s ne uitm la cel mai mic
numr pozitiv normalizat,
0000 0001
adic 1.02!!"# .
S studiem cele dou posibiliti pe care le avem pentru a reprezenta cel
mai mare numr pozitiv denormalizat.
0. (1!" )2!!"# (bias 127)
0. (1!" )2!!"# (bias 126 - acesta este folosit de IEEE 754 n simpl
precizie)
Amndou sunt mai mici dect valoarea minim a numerelor normalizate,
1.02!!"# . Acesta este un lucru bun, deoarece dorim s evitm suprapunerea
numerelor normalizate i denormalizate. De asemenea, observai c numrul cu
exponent -126 este mai mare dect numrul cu exponent -127. Aadar, prin
alegerea lui -126, diferena dintre cel mai mic numr normalizat i cel mai mare
numr denormalizat este mai mic. Aceasta este i raiunea pentru care standardul
n simpl precizie IEEE-754 folosete ca cel mai mic numr denormalizat
0. (1!" )2!!"# .
Reprezentarea n dubl precizie
Numerele n dubl precizie folosesc un cuvnt cu semn format dintr-un
exponent de 11 bii i o mantis de 52 de bii, pentru un total de 64 de bii.
Valoarea predefinit este 1023 (excess-1023).
Valorile pe care le poate lua acest format sunt prezentate n Tabelul 8.2.
Tabelul 8.2 Valorile reprezentrii n dubl precizie
Condiie
E = 2047 i M 0
E= 2047 i M = 0
0 < E < 2047
E = 0 i M 0
E= 0 i M = 0
Valoare N
NaN
(1)!
(1)! 2!!!"#$ (1. M)
(1)! 2!!"## (0. M)
(1)! 0
Reprezentri
Not a Number
Numr normalizat
Numr denormalizat
0
precizie, dar, aa cum vom vedea n seciunile urmtoare, toate registrele interne
ale unitii de calcul n virgul mobil sunt de 80 de bii.
Formatul numerelor n virgul mobil cu precizie extins folosete 64 de
bii pentru mantis i 15 bii pentru exponent. Valoarea predefinit este 16.383,
genernd exponeni ntre -16382 i +16383, pentru un domeniu de valori cuprins
ntre 3.3710!!"#$ i 1.1810!"#$ .
64 63
0
1
2
3
4
5
6
7
15
0
Control
15
0
Stare
Exponent
Mantis
Tag
un registru specific, ci la registrul aflat n acel moment n vrful stivei (TOS Top
Of Stack). Registrul urmtor este numit ST1 .a.m.d., pn la ultimul registru,
ST7. Vrful curent al stivei este indicat de un cmp de trei bii aflat n registrul de
stare, TOP (stack TOP). Operaiile de ncrcare decrementeaz TOP i introduc
valoarea n noul vrf al stivei, operaiile de stocare extrag valoarea din vrful
curent al stivei i incrementeaz TOP.
Fiecare registru de date reprezint valoarea n precizie extins. Aceste
registre rein de obicei rezultate intermediare i utilizarea formatului extins
mbuntete acurateea rezultatului final.
Starea i coninutul fiecrui registru de date este indicat printr-o etichet
de doi bii. Deoarece sunt 8 registre de date, avem nevoie de 16 bii. Aceti 16 bii
sunt stocai n registrul de etichete (tag register).
0
C
3
TOP
C
2
C
1
C
0
E
S
S
F
P
E
U
E
O
E
Z
E
D
E
I
E
Indicator FPU
C0
C2
C3
Indicator CPU
CF
PF
ZF
cauza operaiei invalide. Dac acest bit este 1, operaia invalid a fost rezultatul
unei depiri a stivei de registre; n caz contrar, o instruciune aritmetic a ntlnit
un operand invalid. Atenie, FPU seteaz bitul SF la detecia unei condiii de
depire, dar nu-l terge cnd detecteaz o condiie de operand invalid. Ca rezultat,
dac nu a fost ters explicit de la ultima condiie de depire a stivei de registre,
starea indicatorului SF poate fi 1 i n cazul unei excepii aritmetice. n acest caz,
natura erorii este indicat de codul de condiie C1: depire superioar (C1 = 1) sau
depire inferioar (C1 = 0).
mprire cu zero dempritul este un numr finit nenul i mpritorul
este zero; sau, mai general spus, cnd rezultatul unei operaii cu numere finite este
infinit. Rezultatul este reprezentat corect, ca infinit cu semn. FPU seteaz bitul ZE
(Zero divide Exception).
Depire superioar rezultatul unei operaii este prea mare pentru a
putea fi reprezentat (depete marginea superioar a domeniului de reprezentare).
Rezultatul este reprezentat ca infinit. FPU seteaz bitul OE (Overflow Exception).
Depire inferioar rezultatul unei operaii este prea mic pentru a putea
fi reprezentat (depete marginea inferioar a domeniului de reprezentare).
Rezultatul este reprezentat ca zero. FPU seteaz bitul UE (Underflow Exception).
Operand denormalizat unul din operanzi nu este normalizat sau rezultatul
nu se poate reprezenta normalizat (de exemplu, rezultatul este att de mic nct este
imposibil normalizarea lui). FPU seteaz bitul DE (Denormal Exception).
Rezultat inexact rezultatul operaiei este inexact din cauza unei rotunjiri.
Aceast excepie de precizie apare cnd FPU nu poate reprezenta exact rezultatul
unei instruciuni n virgul mobil. Cazuri de genul mpririi 1/3, ce furnizeaz un
rezultat care se repet la infinit (0.33..3), sau cnd un numr real este convertit la
un format cu o precizie mai mic i se pierd bii prin aceast conversie. FPU
seteaz bitul PE (Precision Exception). De obicei, aceast excepie este mascat (n
registrul de control) deoarece rotunjirea sau trunchierea rezultatului este
satisfctoare.
Biii de excepie se pstreaz pn cnd programatorul ncarc registrul de
stare cu o valoare nou. Acest lucru permite programatorului s scrie o secven de
instruciuni i s plaseze un singur test de detecie al erorilor la sfritul acesteia, n
loc s scrie un test dup fiecare instruciune.
Registrul de control
Registrul de control permite programatorului s controleze cteva opiuni
de prelucrare.
15
RC
PC
P U O Z D I
M M M M M M
Precizia de calcul
PC (Precision Control) specific formatul utilizat pentru reprezentarea rezultatului
operaiilor de +, -, , / i extragerea rdcinii ptrate:
00 simpl precizie;
01 neutilizat;
10 dubl precizie;
11 precizie extins, utilizat implicit.
Registrul de etichete
Acest registru conine 8 cmpuri de cte doi bii, fiecare cmp
corespunznd unui registru de date. T0 este cmpul pentru registrul 0, nu pentru
ST0, care, la un moment dat, poate fi oricare dintre cele 8 registre. Fiecare cmp
ofer informaii despre coninutul registrelor respective:
00 registrul conine un numr corect n virgul mobil;
01 registrul conine valoarea 0.0;
10 registrul conine valoarea infinit, un numr denormalizat sau
incorect;
11 registrul este gol (neutilizat).
Registrul de etichete nu poate fi utilizat direct de ctre programator, dar poate fi
folosit depanatoare de exemplu, ca s examineze i s interpreteze adecvat
coninutul registrelor de date.
fld surs
Aceast instruciune ncarc (depune) operandul surs n registrul din vrful
stivei. Acest lucru are loc prin decrementarea indicatorului stivei i copierea
coninutului sursei n noul vrf al stivei. Operandul surs poate fi un registru al
stivei sau orice tip de numr real din memorie. nainte de ncrcarea n ST0,
operanzii din memorie n simpl sau dubl precizie sunt convertii automat la
formatul temporar de 80 de bii. Instruciunea FLD ST0 va copia valoarea din
vrful curent al stivei n noul vrf al stivei.
Unele instruciuni de acest tip nu primesc niciun operand. Ele introduc n
stiv anumite constante uzuale.
Instruciune
FLDZ
FLD1
FLDPI
FLDL2T
FLDL2E
FLDLG2
FLDLN2
Descriere
Introduce valoarea +0.0 n ST0
Introduce valoarea +1.0 n ST0
Introduce valoarea n ST0
Introduce valoarea !"#210 n ST0
Introduce valoarea !"#2! n ST0
Introduce valoarea !"#102 n ST0
Introduce valoarea ln 2 n ST0
fd8 dq
fd9 dq
fd10 dq
fe1 dt
fe2 dt
section .bss
m32 resd
m64 resq
section .text
global _start
_start:
nop
finit
fld dword
fld dword
fld qword
fld qword
fld qword
fld qword
fld tword
fld tword
fst dword
fst qword
12.34
3.14
3.141592653589793238462
3.141592653589793238462 ;pi
12.34
1
1
[fs3]
[fs2]
[fd1]
[fd2]
[fd3]
[fd5]
[fe1]
[fe2]
[m32]
[m64]
mov eax,1
mov ebx,0
int 80h
YASM accept constante n virgul mobil numai ca argumente pentru directivele
DD, DQ i DT. Ele sunt exprimate n forma tradiional: digii, urmai de punct, ali
digii opionali, apoi un E opional urmat de exponent. Punctul este obligatoriu,
astfel nct asamblorul s poat diferenia ntre DD 1, care declar un ntreg, de DD
1.0 care declar un numr n virgul mobil. n YASM, aproape orice instruciune
care adreseaz operanzi din memorie trebuie s indice mrimea corespunztoare cu
unul din prefixele DWORD, QWORD sau TWORD.
n GDB, valorile zecimale pot fi afiate cu opiunea f a comenzii x:
(gdb) x /1wf &fs1
0x80490cc <fs1>:
0
(gdb) x /1wf &fs2
0x80490d0 <fs2>:
1
(raw 0x4002c570a40000000000)
;fpuconsts.asm
;
section .text
global _start
_start:
nop
fld1
fldz
fldpi
fldl2t
fldl2e
fldlg2
fldln2
mov eax,1
mov ebx,0
int 80h
(gdb) info all
st0
0.6931471805599453094286904741849753
st1
0.30102999566398119522564642835948945
st2
1.4426950408889634073876517827983434
st3
3.3219280948873623478083405569094566
st4
3.1415926535897932385128089594061862
st5
0
(raw 0x00000000000000000000)
st6
1
(raw 0x3fff8000000000000000)
st7
0
(raw 0x00000000000000000000)
Valorile sunt plasate n ordinea invers introducerii lor.
Instruciune
FADD
FSUB
FSUBR
Descriere
Adunare n virgul mobil
Scdere n virgul mobil
Scdere invers n virgul mobil
FMUL
FDIV
FDIVR
5.5
2.5
0
0
0
0
0
15
(raw 0x4002f000000000000000)
(raw 0x4001b000000000000000)
(raw 0x4000a000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
7
(raw 0x4001e000000000000000)
5.5
2.5
23
(raw 0x4003b800000000000000)
(raw 0x4001b000000000000000)
(raw 0x4000a000000000000000)
st3
(raw 0x00000000000000000000)
5.5
0
0
0
0
7
(raw 0x4001b000000000000000)
25.5
(raw 0x4003cc00000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x00000000000000000000)
(raw 0x4001e000000000000000)
23
(raw 0x4003b800000000000000)
Dac ne-am fi ateptat s rezulte o valoare de 5.5 ne-am nelat. Registrul ST2 nu
avea valoarea 0, ci era neutilizat (eticheta acestuia din registrul de etichete este 11).
Operaia de adunare ntre o valoare i un registru neutilizat a ntors o excepie de
tip operaie invalid; de aici rezult valoarea NaN n registrul ST0.
Urmtoarele dou instruciuni, folosind NaN ca operand, au ca rezultat aceeai
valoare.
Este foarte important s inem evidena strii registrelor de date. Fiecare
variant a instruciunilor aritmetice specific registrul, rolul su n operaie i dac
stiva de registre gliseaz sau nu. Nu trebuie s facem presupuneri, de cele mai
multe ori sunt greite.
Scderea
Instruciunea FSUB are un format similar cu instruciunea de adunare.
Instruciunea de scdere
fsub surs
execut operaia
ST0 = ST0 surs
mprirea
Instruciunea de mprire are versiuni similare instruciunii de scdere.
Cteva din acestea sunt:
FDIV surs mparte coninutul registrului ST0 la surs i
pstreaz rezultatul n ST0 (ST0 = ST0/surs). Operandul surs
poate fi o valoare n simpl sau dubl precizie aflat n memorie.
FDIVR surs mparte coninutul sursei la registrul ST0 i
pstreaz rezultatul n ST0 (ST0 = surs/ST0).
FDIV ST(i),ST0 mparte ST(i) la ST0 i stocheaz rezultatul n
ST(i).
FDIV ST0,ST(i) mparte ST0 la ST(i) i stocheaz rezultatul n
ST0.
FDIVP mparte ST1 la ST0, reine rezultatul n ST1 i gliseaz ST0.
FDIVRP mparte ST0 la ST1, reine rezultatul n ST1 i gliseaz
ST0.
FDIVP ST(i),ST0 mparte ST(i) la ST0, stocheaz rezultatul n
ST(i), i gliseaz ST0.
FDIVRP ST(i),ST0 mparte ST0 din ST(i), stocheaz rezultatul
n ST(i) i gliseaz ST0.
FIDIV ntreg mparte ST0 la un ntreg de 16 sau 32 de bii aflat
n memorie.
Instruciune Descriere
FSIN
Calculeaz sinus din valoarea lui ST0
FCOS
Calculeaz cosinus din valoarea lui ST0
FSINCOS
FABS
FCHS
FSCALE
FSQRT
FRNDINT
F2XM1
FPATAN
FPTAN
FYL2X
FYL2XP1
Instruciune
FCOM surs
FCOM
FCOM ST(i)
FCOMP
FCOMP ST(i)
FCOMP surs
FCOMPP
FTST
FICOM ntreg
Descriere
Compar ST0 cu o valoare de 32 sau 64 de bii din memorie
Compar registrul ST0 cu registrul ST1
Compar registrul ST0 cu alt registrul de date
Compar ST0 cu ST1 i gliseaz stiva de registre
Compar ST0 cu alt registru de date i gliseaz stiva de registre
Compar ST0 cu o valoare din memorie i gliseaz stiva
Compar ST0 cu ST1 i gliseaz stiva de dou ori
Compar registrul ST0 cu valoarea 0.0
Compar ST0 un ntreg de 16 sau 32 de bii
Rezultatul comparaiei seteaz biii codurilor de condiii C0, C2 i C3, din registrul
de stare.
Condiie
ST0 > surs
ST0 < surs
ST0 = surs
C3
0
0
1
C2
0
0
0
C0
0
1
0
C3
0
0
0
0
1
1
1
C2
0
0
1
1
0
0
1
C0
0
1
0
1
0
1
1
Tipul neacceptat este un format care nu face parte din standardul IEEE 754.
Celelalte tipuri au fost deja ntalnite pe parcurs.
Pentru a determina starea biilor de condiie (rezultatul comparaiei) trebuie
s copiem valoarea registrului de stare n registrul AX sau la o locaie de memorie
cu instruciunea FSTSW, i apoi s o ncrcm n registrul EFLAGS cu
instruciunea SAHF.
;
;fcom.asm
;
section .data
fs1 dd
1.923
fs2 dd
4.5532
section .text
global _start
_start:
nop
fld dword [fs1]
fcom dword [fs2]
fstsw AX
sahf
ja greater
jb lessthan
mov eax,1
mov ebx,0
int 80h
greater:
mov eax,1
mov ebx,2
int 80h
lessthan:
mov eax,1
mov ebx,1
int 80h
Instruciunea SAHF mut biii 0, 2, 4, 6 i 7 din AH pe poziia biilor CF, PF, AF,
ZF i SF din registrul EFLAGS. Acest mapare a biilor din registrul de stare FPU
la respectivii bii EFLAGS este una intenionat. Aadar, instruciunea FSTSW
urmat de SAHF are ca rezultat:
mutarea bitului C0 pe poziia CF
mutarea bitului C2 pe poziia PF
mutarea bitului C3 pe poziia ZF
n acest moment putem determina rezultatul comparaiei prin aceleai folosite
pentru numerele ntregi, JA, JB i JZ.
Programul fcom.asm produce rezultate diferite n funcie de valorile setate
n memorie. Codul rezultat poate fi observat cu ajutorul comenzii echo:
./fcom
echo $?
1
Rezultatul 1 indic faptul c prima valoare fs1 este mai mic dect valoarea fs2.
Putei modifica valorile din program astfel nct s v asigurai c acesta
funcioneaz corect.
ncepnd cu procesoarele Pentium P6, Intel a pus la dispoziie un nou
mecanism de comparare: familia de instruciuni FCOMI. Instruciunea FCOMI i
variantele sale realizeaz comparaii n virgul mobil i indic rezultatul acestora
direct prin biii CF, PF i ZF din registrul EFLAGS.
Instruciune
FCOMI
FCOMIP
FUCOMI
FUCOMIP
Descriere
Compar registrul ST0 cu registrul ST(i)
Compar ST0 cu registrul ST(i) i gliseaz stiva de registre
Verific corectitudinea valorilor nainte de comparare
Verific corectitudinea valorilor nainte de comparare i gliseaz
stiva
Aa cum reiese din Tabelul 8.7, o limitare a instruciunilor FCOMI este c pot
compara numai valori din registrele de date FPU, nu i un registru de date cu o
valoare din memorie. ns ultimele dou instruciuni ofer un serviciu indisponibil
instruciunilor din familia FCOM. FUCOMI i FUCOMIP verific faptul c
valorile ce vor fi comparate se regsesc ntr-un format valid (folosesc registrul de
etichetare). Dac este prezent o valoare nenormalizat se ntoarce o excepie.
Rezultatul instruciunilor FCOMI asupra biilor din registrul EFLAGS au
urmtoarele semnificaii:
Condiie
ST0 > ST1
ST0 < ST1
ST0 = ST1
;
;fcomi.asm
;
section .data
fs1 dd
1.4444
fs2 dd
4.5532
section .text
global _start
_start:
nop
fld dword [fs2]
fld dword [fs1]
fcomi st0,st1
ja greater
jb lessthan
mov eax,1
mov ebx,0
int 80h
greater:
ZF
0
0
1
PF
0
0
0
CF
0
1
0
mov eax,1
mov ebx,2
int 80h
lessthan:
mov eax,1
mov ebx,1
int 80h
Deoarece instruciunea FCOMI compar numai valori din registre FPU, valorile
din memorie sunt ncrcate n ordine invers, astfel nct, la momentul comparrii,
valoarea fs1 s se afle n registrul ST0.
Instruciune
FCMOVB
FCMOVE
FCMOVBE
FCMOVU
FCMOVNB
FCMOVNE
FCMOVNBE
FCMOVNU
Descriere
Mut dac ST0 este mai mic dect ST(i) (CF=1)
Mut dac ST0 este egal cu ST(i) (ZF=1)
Mut dac ST0 este mai mic sau egal cu ST(i) (CF=1 sau ZF=1)
Mut dac ST0 este neordonat (PF=1)
Mut dac ST0 nu este mai mic dect ST(i) (CF=0)
Mut dac ST0 nu este egal cu ST(i) (ZF=0)
Mut dac ST0 nu este mai mic sau egal cu ST(i) (CF=0 sau ZF=0)
Mut dac ST0 nu este neordonat (PF=0)
Instruciuni de control
Aceste instruciuni se folosesc pentru activiti de iniializare, gestionare a
excepiilor i comutare de proces. Ele permit salvarea strii curente a unitii de
calcul n virgul mobil (contextul FPU) i revenirea la aceasta dup ncheierea
altui proces.
Una din aceste instuciuni este FSTENV, utilizat pentru stocarea ntr-o
0x0b 0xff
0xff
0x0f 0xff
0xff
0x8a
0x80
0x04
0x00
0x00
0x05
0x01
0xc4
0x90
0x00
0x00
0xff
0xff
Dup salvarea strii curente, unitatea FPU este iniializat i se introduc din nou
cteva valori de date. Privii valorile nregistrate de registrele FPU nainte i dup
execuia instruciunii FLDENV. Observai c n urma execuiei FLDENV valorile
registrelor de date ST0...ST7 nu au fost restaurate, dar registrele de control, stare i
etichete indic valorile dinainte de instruciunea FSTENV.
Instruciunea FSTENV stocheaz contextul FPU, dar nu i valorile
registrelor de date. Salvarea context FPU plus date se face cu instruciunea FSAVE.
Instruciunea FSAVE copiaz ntr-o locaie de memorie de 108 octei toate
registrele interne ale unitii FPU (inclusiv registrele de date), dup care o
reiniializeaz. La restaurarea cu instruciunea FRSTOR, toate registrele sunt
readuse la valoarea dinaintea execuiei FSAVE.
;
;fpusave.asm
;
section .data
fs1 dd
34.78
fs2 dd
78.34
fs3 dd
100.1
fs4 dd
200.1
w
dw
0b7fH
section .bss
buffer
resb 108
section .text
global _start
_start:
nop
finit
fld dword [fs1]
fld dword [fs2]
fldcw [w]
fsave [buffer]
fld dword [fs3]
fld dword [fs4]
frstor [buffer]
mov eax,1
mov ebx,0
int 80h
n urma execuiei FSAVE, zona de memorie nu conine numai valorile din
registrele de control, stare i etichete, ci i pe cele ale registrelor de date. De
asemenea, FSAVE realizeaz automat iniializarea unitii FPU (de aceea acest
program nu conine al doilea FINIT). Dup execuia instruciunii FRSTOR putei
observa c toate registrele sunt readuse la starea dinaintea FSAVE.
Alte instruciuni din aceast categorie sunt prezentate pe scurt n Tabelul
8.9. Multe din instuciunile de control ale unitii FPU au dou forme:
form wait, prefixat numai cu litera F, de exemplu FINIT.
form non-wait, prefixat cu FN, de exemplu FNINIT.
Termenii wait i non-wait se refer la modul n care instruciunile trateaz
excepiile n virgul mobil. Excepiile au fost discutate anterior n seciunea
dedicat registrului de stare. Instruciunile n virgul mobil pot genera ase tipuri
Instruciune
FINIT/FNINIT
FCLEX/FNCLEX
FSAVE/FNSAVE
FRSTOR
FSTENV/FNSTENV
FLDENV
FSTCW/FNSTCW
FSTSW/FNSTSW
FLDCW
FDECSTP
FINCSTP
FFREE ST(i)
FNOP
Descriere
Iniializeaz unitatea FPU
terge toi indicatorii de excepie din cuvntul de stare
Salveaz n memorie contextul complet al unitii FPU
ncarc din memorie contextul complet al unitii FPU
Salveaz n memorie contextul FPU
ncarc contextul FPU din memorie
Salveaz n memorie cuvntul de stare curent
Salveaz cuvntul de stare curent n AX sau ntr-o locaie de
memorie
ncarc cuvntul de stare din memorie
Decrementeaz cmpul TOP din cuvntul de stare. Dac
TOP este 0, prin decrementare devine 7.
Incrementeaz cmpult TOP. Dac TOP este 7, devine 0.
Marcheaz ST(i) ca neutilizat
Echivalentul NOP de la numere ntregi
8.4. Exerciii
8.1. Convertii manual urmtoarele numere n format IEEE 754 de 32 de bii.
a)
b)
c)
d)
1.1
-0.1
2005.0
0.0039
e) -2015.3125
f) 0.33
g) -0.67
h) 3.14
8.2. Convertii manual n numere zecimale urmtoarele valori date n format IEEE
754 simpl precizie.
a) 4000 0000
e) c180 4000
b) bf80 0000
c) 3d80 0000
d) c259 48b4
f) 42f6 e666
g) 3f99 999a
h) 42c8 1000
Rdcinile sunt reale dac ! ! 4!", n caz contrar, rdcinile sunt imaginare.
Rulai programul i observai efectul fiecrei instruciuni. Modificai valorile
coeficienilor i, pentru fiecare caz, la finalul programului, afiai rdcinile cu
ajutorul comenzii x /1gf &r1, respectiv &r2.
section .data
a dq 2.0
b dq -3.0
c dq 1.0
section .bss
r1 resq 1
r2 resq 1
real resb 1
section .text
global _start
_start:
nop
finit
fld qword [a]
fadd ST0
fld qword [a]
fld qword [c]
fmulp ST1
fadd ST0
fadd ST0
fchs
9. FUNCII
call <nume_funcie>
exit
<nume_funcie>:
ret
Figura 9.1 Apelarea funciilor
eax,1
ebx,0
080h
[temp],ebx
ebx,eax
eax,[temp]
;complementul fa de unu
;complementul fa de doi
int 080h
Instruciunea CALL, la apelare, salveaz n stiv coninutul registrului EIP
i l iniializeaz cu adresa de nceput a funciei. Numele acesteia este nlocuit de
asamblor cu deplasamentul calculat ntre locaia lui CALL i locaia funciei.
Aadar, n programul anterior, numele functie este un deplasament. Acest
deplasament, dup salvarea coninutului registrului EIP n stiv, este adunat la
valoarea EIP. Se ajunge astfel la nceputul funciei. Evident, valoarea
deplasamentului poate fi negativ sau pozitiv, n funcie de poziia funciei n
program. Instruciunea RET restabilete n EIP adresa salvat de instruciunea
CALL.
mov ebx,eax
mov eax,[temp]
;revenire din funcie n programul principal
ret
;nceputul programului principal
global _start
_start:
nop
mov eax,[d1]
mov ebx,[d2]
;intrare n funcie
call _functie
;revenire din funcie
mov [d1],eax
mov [d2],ebx
mov eax,1
mov ebx,0
int 080h
Se observ cum funcia primete parametri de intrare de la programul
principal prin registrele EAX i EBX. Rezultatul este returnat programului prin
aceleai registre.
Aceast metod este rapid, toate argumentele se gsesc n registre. ns,
dac folosim registre i n corpul funciei, nu exist nicio garanie c valoarea
acestora va fi identic cu cea de la intrarea n funcie. Altfel spus, e posibil ca
valorile registrelor de la ieirea din funcie s difere de cele de pn la intrarea n
funcie. Din acest motiv este indicat s urmrim registrele folosite de funcie pentru
procesul ei intern. Att registrele ct i locaiile de memorie folosite n interiorul
funciei pot avea alt valoare la rentoarcerea n program. Dac o funcie modific
registrele folosite de programul principal este crucial s salvm coninutul lor
nainte de apelul funciei. Salvarea se poate face cu instruciunile PUSH, PUSHA.
Restaurarea cu POP, POPA. De asemenea, trebuie s fim ateni care sunt registrele
folosite de funcie pentru returnarea rezultatului. Dac acestea sunt suprascrise,
funcia a fost efectuat inutil.
Din moment ce procesorul nu dispune de foarte multe registre de uz
general, un dezavantaj implicit al metodei este numrul mic de argumente care
poate fi furnizat funciei.
Dezavantajele modalitii de transmitere a parametrilor prin intermediul
registrelor:
neconvenabil - dac se transmite un numr mare de parametrii,
push 5
call functie
mov [rez1],eax
push 10
call functie
mov [rez2],eax
mov eax,1
mov ebx,0
int 080h
functie:
pop eax
neg eax
ret
0xbfc65ccc: 5
(gdb) n
20
pop eax
(gdb) print /x $esp
$3 = 0xbfc65cc8
(gdb) x /xw 0xbfc65cc8
0xbfc65cc8: 0x0804808b
(gdb) x /2xw 0xbfc65cc8
0xbfc65cc8: 0x0804808b
0x00000005
(gdb) n
21
neg eax
(gdb) print /x $esp
$4 = 0xbfc65ccc
(gdb) print /x $eax
$5 = 0x804808b
(gdb) n
23
ret
(gdb) print /x $eax
$6 = 0xf7fb7f75
(gdb) n
0x00000005 in ?? ()
Stiv
Paramentrul funciei
ESP
Adresa de revenire
31
Paramentru 3 funcie
Parametru 2 funcie
Paramentru 1 funcie
Adresa de revenire
31
ESP
0
Paramentru 3 funcie
ESP + 12
Parametru 2 funcie
ESP + 8
Paramentru 1 funcie
ESP + 4
Adresa de revenire
ESP
31
functie:
push ebx
mov eax,[esp+4]
neg eax
pop ebx
ret
Prin introducerea lui EBX n stiv, ESP + 4 indic adresa de revenire. Iari
extragem adresa de revenire i i calculm complementul fa de doi. De aceast
dat ns, din moment ce nu am extras-o cu POP, ci numai am adresat-o indirect,
nu pierdem valoarea adresei de revenire n program. ns rezultatul funciei este
eronat - am calculat complementul fa de doi al adresei de revenire, nu al
parametrului de intrare.
Rezolvare I:
Prima instruciune a funciei trebuie s salveze vrful iniial al stivei. Se copiaz
astfel valoarea registrului ESP n registrul EBP. Acest mecanism garanteaz c
exist ntotdeauna un registru care indic corect vrful iniial al stivei (existent la
momentul apelrii funciei). Datele introduse de funcie nu afecteaz EBP, doar
ESP.
functie:
mov ebp,esp
push ebx
mov eax,[ebp+4]
neg eax
pop ebx
mov esp,ebp
ret
Problem II:
Registrul EBP poate fi folosit de programul principal. Salvarea adresei de nceput a
stivei n EBP i suprascrie acestuia valoarea anterioar i poate deregla execuia
corect a programului apelant.
Rezolvare II:
nainte de copierea valorii din ESP n EBP, salvm registrul EBP n stiv. De
acum, codul de nceput i sfrit al funciei seamn cu un prolog, respectiv un
epilog.
functie:
push ebp
mov ebp,esp
prolog
<corpul funciei>
mov esp,ebp
pop ebp
epilog
ret
Stiv
EBP + 16
Paramentru 3 funcie
Parametru 2 funcie
EBP + 12
Paramentru 1 funcie
EBP + 8
Adresa de revenire
EBP + 4
ESP
Vechiul EBP
31
EBP
0
prolog
push ebx
mov eax,[ebp+4]
mov [ebp-4],2
;variabil local 1
mov [ebp-8],6
;variabil local 2
mov [ebp-12],7 ;variabil local 3
neg eax
add eax,[ebp-4]
pop ebx
mov esp,ebp
pop ebp
epilog
ret
Stiv
Paramentru 3 funcie
EBP + 16
Parametru 2 funcie
EBP + 12
Paramentru 1 funcie
EBP + 8
Adresa de revenire
EBP + 4
Vechiul EBP
ESP
EBP
Variabil local 1
EBP - 4
Variabil local 2
EBP - 8
Variabil local 3
31
EBP - 12
0
Problem III:
Aa cum reiese din ultima figur, ESP nc indic vechea adres a EBP. Dac
funcia plaseaz n stiv variabile locale (ca n exemplul anterior Variabila local
1,2,3), dar n corpul ei mai conine i instruciuni PUSH, acestea, folosind ESP,
suprascriu valorile variabilelor locale. Pierdem variabilele funciei.
functie:
push ebp
mov ebp,esp
push ebx
mov eax,[ebp+4]
mov [ebp-4],2
mov [ebp-8],6
mov [ebp-12],7
push 20
push 60
push 70
neg eax
add eax,[ebp-4]
pop ebx
mov esp,ebp
pop ebp
;variabil local 1
;variabil local 2
;variabil local 3
Suprascriu variabilele locale
ret
Rezolvare III:
La nceputul funciei, imediat dup secvena de intrare n funcie (dup prolog),
rezervm n stiv un anumit spaiu pentru variabilele locale, scznd din ESP
valoarea dorit. Astfel, dac vor fi introduse n stiv date suplimentare, acestea vor
fi plasate sub variabilele locale. n aceste condiii, registrul ESP poate fi folosit n
mod obinuit, cu instruciunea PUSH, fr s afecteze variabilele funciei.
La sfritul funciei, registrul ESP este rescris cu valoarea sa de nceput
(n epilog) i variabilele funciei vor fi pierdute din stiv de aici i denumirea de
variabile locale.
Funcia programului nostru devine:
functie:
push ebp
prolog
mov ebp,esp
sub esp,12 ;rezerv 12 octei pentru variabilele locale
push ebx
mov eax,[ebp+4]
mov [ebp-4],2
;variabila local 1
mov [ebp-8],6
;variabila local 2
mov [ebp-12],7 ;variabila local 3
push 20
push 60
variabilele locale nu sunt suprascrise
push 70
neg eax
add eax,[ebp-4]
pop ebx
mov esp,ebp
pop ebp
ret
epilog
Corpul funciei
mov esp,ebp
pop ebp
ret
Aspectul final al stivei este artat n Figura 9.2. Informaia stocat n stiv
parametrii de intrare, adresa de revenire n programul principal, vechea adres
EBP i variabilele locale formeaz ceea ce poart numele de cadru de stiv (stack
Paramentru 3 funcie
EBP + 16
Parametru 2 funcie
EBP + 12
Paramentru 1 funcie
EBP + 8
Adresa de revenire
EBP + 4
Vechiul EBP
-12
ESP
EBP
Variabil local 1
EBP - 4
Variabil local 2
EBP - 8
Variabil local 3
EBP - 12
31
0
Figura 9.2 Aspectul stivei
Parametru 3 funcie
Paramentru 2 funcie
Parametru 1 funcie
31
ESP
0
POP extrage din stiv, elementul extras cu POP nu mai rmne n stiv.
Adresarea bazat l copiaz, elementul rmne n stiv.
Totui, dac programul principal folosete stiva i n alte scopuri este de
preferat ca acesta s regseasc stiva exact n stadiul n care se afla nainte de
apelul funciei (eliberat de parametrii de intrare ai unei funcii care s-a sfrit).
Procesul de eliberare a stivei de parametrii nedorii poate fi realizat de
ctre:
funcia apelat;
programul apelant (programul principal).
Dac funciile primesc un numr fix de parametri, se prefer prima metod.
n acest caz, codul de ndeprtare a parametrilor este scris o singur dat, n corpul
funciei, chiar dac funcia este apelat de mai multe ori. Avei n vedere c nu
putei folosi o metod de genul
functie:
<corpul_funciei>
leave
add esp,12
ret
deoarece la execuia lui RET, ESP trebuie s indice ctre adresa de revenire n
programul principal. Soluia este reprezentat de operandul opional care poate fi
specificat imediat dup RET.
ret operand
care rezult n urmtoarea secven de instruciuni:
EIP SS:ESP
ESP ESP + 4 + operand
Operandul trebuie s fie un imediat de 16 bii. Din moment ce scopul acestei valori
opionale este s descarce parametrii introdui n stiv, operandul are ntotdeauna o
valoare pozitiv.
Totui, dac o funcie primete un numr variabil de parametri, trebuie s
folosim a doua metod. Aceasta este i metoda utilizat de compilatoarele C. Din
programul principal, parametrii de intrare pot fi extrai cu instruciunea POP, dar
cel mai indicat este s reiniializm registrul ESP cu adresa anterioar apelului
funciei. Pentru aceasta se adun la ESP mrimea parametrilor de intrare introdui
n stiv. De exemplu, dac introducem n stiv, ca parametri de intrare pentru o
funcie, trei ntregi a cte 4 octei fiecare, pentru ndeprtarea acestora din stiv, la
sfritul textului funciei din programul principal, imediat dup instruciunea de
apel, trebuie s adunm valoarea din ESP cu 12.
Tabelul 9.1 Exemple de eliberare a stivei din interiorul programului principal
push eax
push ebx
push ecx
call functie
add esp,12
push eax
push ebx
call functie
add esp,8
push eax
call functie
add esp,4
(gdb) i r ebp
ebp
0xffffd7c0
(gdb) i r esp
esp
0xffffd7c0
0xffffd7c0
0xffffd7c0
0x08048092
0x0000000a
0x00000096
Am afiat patru cuvinte de 32 de bii ncepnd de la adresa EBP. Prima valoare din
stnga, 0x00000000, reprezint adresa anterioar a registrului EBP, a doua,
0x08048092, reprezint adresa de revenire n programul apelant (adresa care va fi
ncrcat n registrul EIP de instruciunea RET), a treia i a patra valoare sunt
datele de intrare n funcie - 10, respectiv 150, n hexazecimal.
Urmtoarele dou instruciuni efectueaz nmulirea. Prima copiaz n
registrul EAX un parametru din stiv prin adresare bazat. Rezultatul rmne n
combinaia de registre EDX:EAX. Deoarece valoarea este mic, registrul EDX
rmne 0.
(gdb) si
0x080480a5
20
(gdb) i r eax
eax
0xa
10
(gdb) si
0x080480a8
21
(gdb) i r eax
eax
0x5dc 1500
(gdb) i r edx
edx
0x0
0
mov eax,[ebp+8]
(gdb) i r esp
esp
0xffffd7d0
0xffffd7d0
0x08048090
0x080490b4
0x080490b0
variabile locale). Acest fapt aduce la rndul su i alt beneficiu: nu este imperios
necesar ca funcia s se afle n acelai fiier cu programul principal. n continuare
artm cum putem crea fiiere separate pentru funcii, cum trebuie s le asamblm
i cum trebuie s le legm de fiierul programului principal.
Structura funciei scrise ntr-un fiier separat este similar structurii unui
program obinuit, numai c, n loc de directiva _start, trebuie s declarm ca
etichet global numele funciei.
section .text
global _functie
_functie:
De asemenea, n fiierul programului principal trebuie s folosim directiva
extern <nume_funcie>.
Din perspectiva procesului de asamblare, fiecare fiier .asm separat este
considerat un modul, indiferent dac conine o etichet _start (caz n care este
considerat program principal) sau dac reprezint o simpl funcie. Fiecare modul
conine cod i, posibil, cteva definiii de date. Directiva extern indic
asamblorului c funcia respectiv se afl ntr-un alt modul. Directiva global
atenioneaz asamblorul c funcia respectiv poate fi adresat din exteriorul
modului. Convenia extern global se aplic i declaraiilor de date. Putem
declara orice etichet de date ca global i ea poate fi utilizat de orice modul n
care numele etichetei respective apare declarat ca extern. Mai mult, fiierele de
funcii pot partaja ntre ele date i funcii, n orice combinaie, att timp ct toate
declaraiile globale i externe sunt realizate corect. Un modul care conine funcii
sau date declarate global se spune c export acele elemente. Similar, un modul
care utilizeaz funcii sau date externe se spune c import acele elemente.
Modulele asamblate separat au structur similar cu programele normale, cu o
excepie important: modulele externe nu conin un program principal, deci nu au
adres de nceput. Acest lucru nseamn c nu exist o etichet _start care s
indice editorului de legturi punctul de la care trebuie s nceap execuia
programului. Modulele de funcii nu sunt destinate s ruleze singure, astfel nct
prezenta etichetei _start este inutil. Editorul de legturi va genera o eroare ori
de cte ori ntlnete mai mult de o etichet _start n modulele pe care le
proceseaz la un moment dat. Dac toate declaraiile sunt corecte, modulele pot
comunica unul cu altul prin intermediul apelurilor de funcie, i orice funcie poate
adresa orice definiie de date din oricare din fiierele legate de editorul de
legturi.
Scriem codul funciei care calculeaz complementul fa de doi ntr-un
fiier separat de cel al programului principal. Vor rezulta dou fiiere:
functie.asm, pentru corpul funciei, i main.asm, pentru programul principal.
;
;functie.asm
;
section .bss
mem resd 1
section .text
global _functie
_functie:
push ebp
mov ebp,esp
sub esp,8
mov eax,[ebp+8]
neg eax
leave
ret
;
;main.asm
;
section .bss
rez1 resd 1
rez2 resd 1
section .text
extern _functie
global _start
_start:
nop
push 5
call _functie
add esp,4
mov [rez1],eax
push 10
call _functie
add esp,4
mov [rez2],eax
mov eax,1
mov ebx,0
int 080h
Nu exist o limit cu privire la numrul de directive extern prezent ntrun modul. Un modul (program sau funcie) poate primi oricte directive extern,
corespunztor numrului de funcii externe apelate. Pentru un aspect compact,
funciile pot fi declarate i pe o singur linie, separate prin virgul.
extern f1, f2, f3
Totui, declaraiile care trec de limita liniei nu sunt recunoscute.
Directivele globale trebuie declarate naintea definirii lor n codul surs. n
practic, acest lucru nseamn c directivele global apar n codul surs la
nceputul seciunii TEXT, naintea oricrei funcii. Similar, toate elementele de date
globale sunt declarate ca atare n segmentul DATA naintea definirii lor. Elementele
care nu sunt declarate global sunt private, n sensul c nu pot fi accesate dect
din interiorul modulului care le conine.
Asamblm cu urmtoarele comenzi:
main.asm
functie.asm
functie.o melf_i386
section .text
global _functie
_functie:
push ebp
mov ebp,esp
sub esp,8
mov eax,[ebp+8]
mov [mem],eax
mov eax,0ffffffffh
mov ebx,0aaaaaaaah
mov ecx,022222222h
push eax
push ebx
mov [ebp-4],ecx
mov dword [ebp-4],033333333h
neg dword [mem]
mov eax,[mem]
pop eax
leave
ret
Rulai programul pas cu pas i observai cum se modific coninutul stivei.
Poate ai observat c n aceste ultime funcii nu am folosit instruciunea
ENTER, ci am preferat secvena clasic de intrare n funcie. Majoritatea
compilatoarelor au aceeai preferin. Acest lucru se explic prin faptul c
instruciunea ENTER are probleme de performan. Procesoarele moderne
decodeaz ENTER n 10 pn la 20 de microoperaii, n timp ce secvena de trei
instruciuni este decodat n 4 pn la 6, n funcie de arhitectur. Diferena de
vitez este destul de mare. n plus, secvena de trei instruciuni poate fi optimizat
de asamblor.
9.9. Exerciii
9.1. Scriei urmtoarea funcie C:
/* f.c */
int f(void) {
return 0;
}
Afiai echivalentul ei n asamblare cu comanda:
gcc -S f.c -o - -m32 -masm=intel
-O0
int h(void) {
return 56;
}
#include <stdio.h>
void swap( int *num1, int *num2 ) ;
int a = 256;
int b = 128;
int a = 256;
int b = 128;
void main( )
{
swap( a, b ) ;
void main( )
{
swap( &a, &b ) ;
temp = num2 ;
num2 = num1 ;
num1 = temp ;
}
temp = *num2 ;
*num2 = *num1 ;
*num1 = temp ;
}
-O0 -o f
este periculos. Persoane ru intenionate le-ar putea modifica sau folosi n scopuri
improprii. n al doilea rnd, aceste secvene se modific odat cu mbuntirea i
evoluia sistemului de operare. Evoluia presupune adugarea, modificarea sau
tergerea unor instruciuni main din motive de securitate sau de optimizare.
Din aceste motive, apelul funciilor de sistem se face prin ceea ce n limbaj
de specialitate se numete poart de apel (call gate) i anume, o poart de acces
aflat ntre spaiul utilizator, acolo unde ruleaz programele obinuite, i spaiul
kernel, acolo unde i desfoar activitatea componenta sistemului de operare care
gestioneaz resursele hardware ale sistemului. Poarta de apel este implementat
printr-o ntrerupere software.
n sistemele x86, chiar la nceputul memoriei RAM, la adresa 0, se afl un
tabel special format din 256 de intrri. Fiecare intrare reprezint o adres de
memorie de 4 octei. Astfel, primii 1024 de octei de memorie sunt rezervai acestui
tabel. Fiecare intrare (adres) din tabel se numete vector de ntrerupere. Din acest
motiv, tabelul este denumit tabelul vectorilor de ntrerupere. n funcie de poziia
sa n tabel, fiecare vector are un numr de identificare cuprins ntre 0 i 255. La
iniializarea calculatorului, BIOS-ul i sistemul de operare20 ncarc locaiile din
tabel cu adresele rutinelor de ntrerupere specifice lor. O actualizare a sistemului de
operare poate modifica att rutinele ct i adresele prezente n tabelul vectorilor de
ntrerupere, n schimb numrul (indexul) ntreruperii care conine respectiva adres
rmne nemodificat. Cu alte cuvinte, rolul locaiilor din tabelul vectorilor de
ntrerupere este standard, secvena de ntrerupere i adresa ei n memorie se poate
modifica (de ex., adugarea unor noi instruciuni la o rutin de ntrerupere crete
dimensiunea acesteia i trebuie ncrcat la alt adres de memorie), dar
ntotdeauna va fi apelat de la vectorul cu acelai numr. Din aceast cauz, nc de
la prima apariie a sistemului Linux i pn n prezent, numrul de ntrerupere 80H
a indicat ntotdeauna ctre un dispecer de apel (servicii), un fel de manager al
tuturor funciilor de sistem puse la dispoziie de kernelul Linux. Adresa
dispecerului difer de la distribuie la distribuie sau chiar de la o versiune de Linux
la alta, dar indiferent de acest lucru, programele l pot apela prin intermediul
locaiei 80H din tabelul vectorilor de ntrerupere. Din motive de securitate, tabelul
vectorilor de ntrerupere aparine sistemului de operare i programatorul nu are
acces direct la adresele sale. Totui, programatorul dispune de o instruciune ce
poate interoga respectivul tabel - instruciunea INT (INTerrupt). Singura diferen
real ntre ntreruperile hardware i ntreruperile software const n evenimentul
care declaneaz saltul procesorului la tabela vectorilor de ntrerupere. n cazul
unei ntreruperi hardware, evenimentul declanator este un semnal electric aplicat
pe un pin al procesorului. Dup ce recunoate semnalul electric, procesorul
introduce n stiv adresa de revenire n program i execut rutina de tratare a
respectivei ntreruperi. Pentru ntreruperile software, evenimentul declanator este
20
intruciunea INT. Cnd este executat instruciunea INT 80H, procesorul extrage
adresa gsit la locaia 80H a tabelei vectorilor de ntrerupere i execut secvena
de instruciuni indicat de acea adres. n acest mod se efectueaz i se controleaz
tranziia din spaiul utilizator n spaiul kernel. Procesul este ilustrat n Figura 10.1.
Prima dat, instruciunea
INT 80H introduce n stiv
adresa urmtoarei instruciuni
Stiva
Adresa de revenire
Codul programului
INT 80h
(Urmtoarea instruciune)
SPAIUL UTILIZATOR
SPAIUL KERNEL
Linux
Dispecer de apel
Tabela vectorilor de
ntrerupere
Vector 80h
anterior n stiv pentru a reveni n program. Acest proces seamn cu cel prin care
funciile studiate n capitolul precedent folosesc perechea de instruciuni CALL i
RET. CALL introducea n stiv adresa urmtoarei instruciuni i srea la funcie,
instruciunea RET de la sfritul funciei extrgea adresa din stiv i permitea
continuarea execuiei programului de la prima instruciune sub CALL. Este timpul
s nelegem rolul ultimelor trei instruciuni din toate programele studiate n
aceast carte pn n prezent.
;
;ntrerupe execuia programului i red controlul sistemului de operare
;
mov eax,1 ;specific funcia de sistem Exit
mov ebx,0 ;cod de ntoarcere zero (SUCCESS)
int 80h
;efectueaz apel de sistem n vederea ntreruperii
execuiei
Programatorul, pentru a ntrerupe execuia programului, trebuie s plaseze
identificatorul de apel sys_exit, 1, n registrul EAX i un cod de ntoarcere n EBX,
apoi specific instruciunea INT 80H. Codul de ntoarcere este o valoare numeric
aflat la discreia sa. Tehnic, nu sunt restricii (trebuie numai s ncap ntr-un
registru de 32 de bii) dar, prin convenie, o valoare de ntoarcere 0 nseamn c
operaia s-a finalizat cu succes (procesul a fost ntrerupt normal). O valoare diferit
de 0 indic o eroare (ntmpinat de kernel n procesul de ntrerupere): fiierul nu
poate fi gsit, discul este la capacitate maxim, etc..
Fiecare program trebuie ntrerupt n acest mod. Chiar dac un program nu
implementeaz funcia sys_exit, pn la urm va fi ntrerupt, dar sistemul de
operare va afia o eroare tip Segmentation fault i nu putem tii cum a fost
finalizat procesul de ntrerupere.
cum am menionat, ordinea n care valorile de intrare sunt plasate n registre este
foarte important. Dispecerul de apel ateapt datele de intrare n ordinea
urmtoare:
EBX, primul parametru;
ECX, al doilea parametru;
EDX, al treilea parametru;
ESI, al patrulea parametru;
EDI, al cincilea parametru.
Funciile de sistem care necesit mai mult de ase parametrii de intrare
folosesc o metod diferit de preluare a acestora: dispecerul adreseaz i citete
parametrii prin intermediul registrului EBX. n acest caz, registrul EBX conine
adresa unei locaii de memorie de la care parametrii sunt stocai n ordine
secvenial.
O alt problem const n stabilirea corect a numrului de parametrii i a
registrului corespunztor unui anumit parametru. Semnificaia parametrilor, la fel
ca i numrul acestora, difer de la funcie la funcie. Asemenea oricrei comenzi
Linux, odat ce ai aflat numele funciei de sistem poi gsi definiia acesteia n
paginile manual. Seciunea 2 a paginilor de manual conine definiiile tuturor
funciilor de sistem disponibile. Accesul la definiia unei funcii de sistem se face
cu ajutorul comenzii man urmat de numrul 2:
man 2 exit
Numrul 2 specific seciunea paginilor de manual. Deoarece unele funcii de
sistem au acelai nume cu unele comenzi listate n seciunea 1, implicit pentru
paginile de manual, nu uitai s includei 2 explicit. Altfel, n loc s obinei
definiia funciei de sistem, vor fi afiate opiuni de comand. Pagina de manual
conine patru pri:
Nume: arat numele funciei de sistem;
Rezumat: arat modul n care putem utiliza funcia de sistem
respectiv;
Descriere: o scurt descriere a funciei de sistem;
Valoarea returnat: valoarea returnat la sfritul funciei de sistem.
Rezumatul este scris pentru programatori de C, dar pot beneficia de aceste
informaii i cei care programeaz n limbaj de asamblare. Rezumatul funciei de
sistem EXIT arat c aceasta primete un singur parametru de intrare (valoarea
ntre paranteze) i nu returneaz nicio valoare.
Funcia de sistem WRITE este utilizat la scrierea datelor ntr-un descriptor
de fiier. Dac afim pagina sa de manual, rezumatul se prezint astfel:
SYNOPSIS
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
Parametrii sunt menionai de la stnga la dreapta, aa cum apar n rezumat.
Primul parametru (fd) este un ntreg care reprezint descriptorul de fiier pentru
dispozitivul de ieire, un identificator de fiier. Al doilea parametru (buf) este
adresa (pointer) de la care ncepe irul ce trebuie scris la dispozitiv. Al treilea
parametru (count) reprezint dimensiunea n octei a irului care trebuie scris.
Utiliznd aceast convenie, valorile de intrare sunt atribuite urmtoarelor registre:
EBX, descriptorul de fiier;
ECX, adresa de memorie a irului (pointer-ul);
EDX, numrul de octei care trebuie scris din memorie n fiier.
Un exemplu de utilizare a acestei funcii de sistem este dat n programul
hello.asm.
;
;afieaz la monitor mesajul Hello, World!
;
section .data
mesaj db Hello, World, 0xa
;0xA = ASCII newline
lungime
equ $ - mesaj
section .text
global _start
_start:
nop
;pentru a putea seta punctul de ntrerupere n depanator
mov eax,4 ;EAX = identificatorul funciei de sistem write
mov ebx,1 ;EBX = descriptorul de fiier 1 (STDOUT)
mov ecx,mesaj ;ECX = adresa de nceput a irului care va fi trimis la
STDOUT
mov edx,lungime ;EDX = numrul de octei trimii la STDOUT
int 80h
;specific kernelului s execute funcia indicat n EAX
;WRITE returneaz n EAX numrul de caractere scris la monitor
mov eax,1 ;EAX = identificatorul funciei de sistem EXIT
mov ebx,0 ;EBX = codul de ntoarcere din SO n caz de succes
int 80h
;specific kernelului s execute funcia indicat n EAX
Probabil v este cunoscut cam tot ce se ntmpl n programul hello.asm.
Identificatorul de apel pentru funcia de sistem WRITE (4) este plasat n registrul
Fiier
Standard Input
Standard Output
Standard Error
Identificator C
STDIN
STDOUT
STDERR
Descriptor de fiier
0
1
2
Hardware
Tastatur
Monitor
Monitor
n acelai mod. Totui, n sistemele Unix putem separa ieirile standard ale
programului de mesajele de eroare (sau alte mesaje specifice modului n care se
comport programul) printr-un mecanism numit redirecionare I/O.
Programul hello.asm folosete descriptorul de fiier STDOUT ca s afieze
un text pe ecranul terminalului. Urmtoarea valoare de intrare specific adresa de
la care ncepe irul care trebuie afiat. Observai c specificarea locaiei de
memorie se face prin adresare direct. Aceasta presupune ca n registrul ECX s se
afle adresa explicit a locaiei de memorie de la care ncepe textul mesajului.
Ultimul parametru de intrare specific lungimea irului care trebuie afiat. n loc s
specifice n clar numrul de octei ai mesajului, programul determin lungimea
acestuia printr-un mic artificiu prezentat deja n capitolul dedicat structurilor de
date. Aa cum reiese din pagina de manual a funciei de sistem WRITE, valoarea
returnat reprezint numrul de octei scrii sau o valoare negativ (-1) - dac
apelul s-a ncheiat cu o eroare. Valoarea returnat este plasat n registrul EAX.
Programatorul trebuie s verifice acest valoare, n special de posibilitatea apariiei
vreunei erori. ntotdeauna trebuie s inem cont de tipul de date al valorii returnate.
Unele funcii de sistem folosesc tipuri de date exotice. Acesta este i cazul nostru:
funcia WRITE returneaz o valoare de tip ssize_t. Tipul de date ssize_t nu
este unul din cele ntlnite n limbajul de asamblare. De fapt, nu ine de limbajul de
programare, ci este un sinonim pentru o valoare de tip ntreg folosit de sistemul de
operare Linux. Reprezint numrul de caractere scris la un descriptor de fiier sau
valoarea -1 dac apare vreo eroare.
Urmtorul program demonstreaz modul n care putem gestiona valorile
returnate de o funcie de sistem.
;
;CallReturn.asm
;
section .bss
pid resb 4
uid resb 4
gid resb 4
section .text
global _start
_start:
nop
mov eax,20
int 80h
mov [pid],eax
mov eax,24
int 80h
mov [uid],eax
mov eax,47
int 80h
mov [gid],eax
end:
mov eax,1
mov ebx,0
int 80h
Programul CallReturn.asm folosete trei apeluri de sistem separate:
Indicatorul de apel Funcia de sistem
20
getpid
24
getuid
47
getgid
Descriere
Specific identificatorul programului
care ruleaz
Specific identificatorul persoanei care
ruleaz programul
Specific identificatorul de grup al
persoanei care ruleaz programul
id
uid=1000(ubuntu) gid=1000(ubuntu) groups=1000(ubuntu)
Urmtorul program permite introducerea datelor de la tastatur prin funcia
de sistem READ.
;
;readInput.asm
;
section .data
lung_nume dd
0
;;bufferul este zona de memorie n care vom stoca numele introdus, 40 de
caractere ASCII 0x2d
buffer
db
"--------------------------------------"
msg_cerere db
Introducei numele: ,0xa
msg_cerere_lung equ $ - msg_cerere
msg_salut db
"Salut, "
msg_salut_lung equ $ - msg_salut_lung
;;dac dorii s folosii pentru buffer memorie neiniializat, comentai linia
buffer din .data i activai urmtoarele dou linii.
;section .bss
;
buffer resb
40
section .text
global _start
_start:
nop
mov
mov
mov
mov
int
eax,4
ebx,1
ecx,msg_cerere
edx,msg_cerere_lung
80h
mov eax,3
mov ebx,0
mov ecx,buffer
mov edx,40
int 80h
;read returneaz n EAX numrul de caractere introduse de la tastatur
mov [lungime_nume],eax
mov
mov
mov
mov
int
eax,4
ebx,1
ecx,msg_salut
edx,msg_salut_lung
80h
mov
mov
mov
mov
int
eax,4
ebx,1
ecx,buffer
edx,[lungime_nume]
80h
mov eax,1
mov ebx,0
int 80h
Funcia de sistem READ este identificat prin cifra 3. Are nevoie ca
descriptorul de fiier s fie specificat n registrul EBX, iar adresa i dimensiunea
zonei de memorie la care vor fi stocate datele trebuie introdus n registrul ECX,
respectiv n EDX. Va returna numrul de caractere citit din fiier sau, n cazul n
care operaiunea nu s-a putut desfura (de ex., nu exist fiierul), un cod de eroare.
Codurile de eroare sunt ntodeauna reprezentate prin numere negative (-1).
Observai c funcia de sistem pentru scriere necesit aceeai parametrii ca funcia
de sistem pentru citire, cu excepia faptului c datele care vor fi scrise trebuie s se
afle deja n zona de memorie tampon (buffer).
10.2.1.
Buffer-ul este creat prin rezervarea static sau dinamic a unui spaiu de
memorie. Rezervarea dinamic nu face obiectul acestei cri. n program am folosit
dou modaliti statice de rezervare a spaiului. O prim modalitate a fost
declararea acestuia n zona datelor iniializate (prin directive standard DB, DW,
DD, DQ, DT). Totui, pe lng faptul c este destul de incomod s iniializezi cu o
valoare numrul respectiv de octei, o astfel de declaraie crete numrul de octei
ai executabilului. n exemplul anterior, mrimea este infim, dar presupunnd c
avem nevoie de un buffer de 1025 de octei, ambele probleme devin semnificative.
Soluia a fost deja dezvluit: rezervarea unui spaiu temporar de stocare n zona
datelor neiniializate. Soluia este foarte util deoarece nu trebuie s iniializm
zona respectiv cu o valoare i nici nu cretem suplimentar mrimea executabilului.
Directiva
buffer
resb
40
10.2.2.
Macroinstruciuni
;
%macro Write 2
mov eax,4
mov ebx,1
mov ecx, %1
mov edx, %2
int 80h
%endmacro
%macro Read 2
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,%2
int 80h
%endmacro
%macro Exit 0
mov eax,1
mov ebx,0
int 80h
%endmacro
section .data
cerere_nume
db
"Introduceti numele: "
cerere_size
equ $-cerere_nume
salut db
"Salut, "
salut_size equ $-salut
buffer
db
"01234567890123456789"
lungime
dd
0
nr_caractere
db
20
section .text
global _start
_start:
nop
Write cerere_nume,cerere_size
Read buffer,[nr_caractere]
mov [lungime],eax
Write salut,salut_size
Write buffer,[lungime]
Exit
Macroinstruciunile din programul salutMacro.asm sunt simple i uor de
interpretat. Niciuna nu conine intruciuni de salt. Dar codul din macroinstruciune,
la fel ca orice funcie, poate utiliza salturi condiionate sau necondiionate. Aici
mov ebx,0
int 80h
%endmacro
section .bss
BUFLEN
equ 1024
buffer
resb BUFLEN
section .text
global _start
_start:
nop
;citim un buffer de la stdin
citeste:
Read buffer,BUFLEN
mov esi,eax
cmp eax,0
je sfarsit
Majuscule buffer,eax
Write buffer,esi
jmp citeste
sfarsit:
;ntrerupem execuia programului
Exit
Eticheta din interiorul unei macroinstruciuni precedat de dou simboluri procent
%% devine local. Dac eticheta marcheaz o anumit poziie din macroinstruciune
trebuie urmat de dou puncte. Dac este folosit ca operand pentru instruciuni de
apel sau de salt (precum JA, JB i JNZ n programul anterior), nu trebuie urmat
de dou puncte. Este foarte important s nelegem c, dac etichetele Next i LC
nu erau marcate ca locale prin adugarea prefixului %%, n cadrul programului ar fi
existat mai multe instane de asemenea etichete (presupunnd c
macroinstruciunea ar fi fost invocat de mai multe ori) i asamblorul ar fi generat
o nou eroare de etichet duplicat ncepnd cu a doua apariie.
Modul n care asamblorul trateaz macroinstruciunile i etichetele locale
acestora se poate observa din fiierul majuscule.lst.
1
%line 31+1 majuscule.asm
2
3
[section .bss]
4
BUFLEN equ 1024
5 00000000 <gap>
buffer resb BUFLEN
6
[section .text]
7
8
9 00000000 90
10
11
12 00000001 B803000000
13
14 00000006 BB00000000
15 0000000B B9[00000000]
16 00000010 BA00040000
17 00000015 CD80
18
19 00000017 89C6
20 00000019 83F800
21 0000001C 7430
22
23 0000001E BA[00000000]
24
25 00000023 89C1
26 00000025 807C0AFF61
27 0000002A 720A
28 0000002C 807C0AFF7A
29 00000031 7703
30 00000033 806C0AFF20
31 00000038 49
32 00000039 75E8
33
34
35 0000003B B804000000
36
37 00000040 BB01000000
38 00000045 B9[00000000]
39 0000004A 89F2
40 0000004C CD80
41
42 0000004E EBAF
43
44 00000050 B801000000
45
46 00000055 BB00000000
47 0000005A CD80
[global _start]
_start:
nop
citeste:
mov eax,3
%line 41+0 majuscule.asm
mov ebx,0
mov ecx,buffer
mov edx,BUFLEN
int 80
%line 42+1 majuscule.asm
mov esi,eax
cmp eax,0
je sfarsit
mov edx,buffer
%line 46+0 majuscule.asm
mov ecx,eax
..@4.LC: cmp byte [edx+ecx-1],'a'
jb ..@4.Next
cmp byte [edx+ecx-1],'z'
ja ..@4.Next
sub byte [edx+ecx-1],20
..@4.Next: dec ecx
jnz ..@4.LC
%line 47+1 majuscule.asm
mov eax,4
%line 48+0 majuscule.asm
mov ebx,1
mov ecx, buffer
mov edx, esi
int 80
%line 49+1 majuscule.asm
jmp citeste
sfarsit:
mov eax,1
%line 51+0 majuscule.asm
mov ebx,0
int 80
10.2.3.
Funcii de lucru cu transferuri de
intrare/ieire
Macroinstruciunile i funciile pot fi combinate. n acest moment deinem
destule cunotine pentru a putea scrie cteva funcii care mijlocesc interaciunea
noastr cu sistemul: citirea unui ir de caractere de la tastur, afiarea unui ir de
caractere la monitor, etc..
Scriem urmtoarele linii ntr-un fiier numit io.inc i salvm.
extern f_ReadStr f_WriteStr
extern f_nwln
%macro
ReadStr
push
push
mov
mov
call
pop
pop
%endmacro
1-2 101
ESI
EDI
EDI,%1
ESI,%2
f_ReadStr
EDI
ESI
%macro
WriteStr 1
push
ECX
mov
ECX,%1
call
f_WriteStr
pop
ECX
%endmacro
%macro
nwln
call
%endmacro
0
f_nwln
%macro WriteChar
mov eax,4
mov ebx,1
mov ecx,%1
mov edx,1
int 80h
%endmacro
%macro
ReadChar
mov eax,3
mov ebx,0
mov ecx,%1
mov edx,1
int 80h
%endmacro
%macro Exit
mov eax,1
mov ebx,0
int 80h
%endmacro
mov ebx,edi
mov esi,temp_str ;adresa de nceput pt. buffer-ul intern funciei
.bucla:
mov al,byte [esi]
;ncarc n AL primul caracter din irul
introdus
cmp al,0xa
;verifica dac utilizatorul a tastat ENTER
je .sfarsit
;ENTER nseamn sfritul irului
mov byte [ebx],al
;copiaz caracterul n DESTINATIE
inc ebx
;urmtorul octet din DESTINATIE
inc esi
;urmatorul caracter din buffer-ul intern
loop .bucla
;repet pn cnd irul s-a sfrit
.sfarsit:
mov byte [ebx],0x0
;adaug caracterul NULL
popf
popad
ret
Funcia f_ReadStr citete de la tastatur un ir de caracterele pe care l ncheie
cu un caracter NULL. Funcia f_WriteStr efectueaz operaia invers: scrie la
monitor un ir de caractere terminat n NULL. Primete un singur parametru:
adresa de la care ncepe irul de afiat. Declararea funciei f_WriteStr se face
tot n fiierul io.inc.
;
;funcia f_WriteStr
;
section .text
global f_WriteStr
f_WriteStr:
pushad
pushf
mov ebx,1
;STDOUT
mov edx,1
;count = 1 caracter
.repeta:
mov eax,4
;funcia de sistem WRITE
;;n ECX este adresa sirului surs. Parametrul %1 din macro.
cmp byte [ecx],0x0
;verific dac este caracterul NULL
je .sfarsit
;dac este NULL, irul s-a sfrit
int 80h
;afieaz caracterul
inc ecx
jmp .repeta
.sfarsit:
popf
popad
;urmtorul caracter
;se iese din bucl numai cnd ECX = 0
ret
Obsevai c etichetele din cadrul funciilor ncep cu punct. Acest punct marcheaz
eticheta ca fiind local (similar celor dou simboluri procent din
macroinstruciuni). n programe, eticheta local permite utilizarea aceluiai cuvnt
n zone de cod diferite. Etichetele cu punct sunt locale fa de prima etichet
global (etichetele care nu ncep cu punct) care le precede. Din acest motiv, o
etichet local este vizibil numai dup eticheta global creia i aparine. n
consecin, eticheta local nu poate fi adresat de instruciuni aflate naintea
etichetei globale corespunztoare (i care, repetm, este prima etichet global
aflat naintea etichetei locale). n cazul nostru, deoarece le folosim n corpul
funciilor, pare c etichetele locale nu sunt precedate de nicio etichet global. Dar,
ntr-un program, numele funciilor nu reprezint altceva dect etichete globale.
Etichetele locale din cadrul funciilor sunt cel puin locale funciilor n care sunt
definite. Firete, putem folosi etichete globale i n corpul funciilor, lucru care
limiteaz i mai mult vizibilitatea etichetei locale.
Funcia f_nwln trimite la monitor caracterul \n.
;
;funcia f_nwln
;
section .data
new_line
db
0xa
section .text
global f_nwln
f_nwln:
pushad
mov eax,4
;sys_write
mov ebx,1
mov ecx,new_line
mov edx,1
int 80h
popad
ret
Nume
Operand Descriere
Afieaz un ir terminat n NULL adresat de
WriteStr surs
m
eticheta surs.
Citete Nr. de caractere de la tastatur i le
ReadStr dest[,Nr.]
m
stocheaz n dest ca ir terminat n NULL.
nwln
Afieaz caracterul de linie nou.
and al,0x0f
xlatb
mov [caracter],al
WriteChar caracter
nwln
Exit
Instruciunea XLATB nlocuiete caracterul din registrul AL cu un octet din tabela
hexTable. Adresa de nceput a tabelei se ncarc, n prealabil, n EBX.
Coninutul registrului AL este tratat ca index n aceast tabel de conversie
(translaie). Valoarea octetului corespunztor indexului nlocuiete valoarea index
din AL. Instruciunea se utilizeaz ndeosebi pentru conversii de cod.
Observai c programul ncepe cu directiva %include io.inc. Aceasta
foreaz asamblorul s includ n codul surs coninutul fiierului io.inc din
directorul curent. Efectul se poate observa n fiierul .lst. Aducei-v aminte c
YASM difereniaz caracterele mici de cele mari.
Pentru a genera executabilul trebuie s asamblm pe rnd funciile i
programul principal, apoi s editm legturile:
yasm -f elf -g stabs f_ReadStr.asm
yasm -f elf -g stabs f_WriteStr.asm
yasm -f elf -g stabs f_nwln
yasm -f elf -g stabs hex.asm
ld -o hex hex.o f_WriteStr.o f_nwln.o f_ReadStr.o
melf_i386
Programul nu funcioneaz corect. Dac depanai programul cu GDB devine
evident c problema este legat de rescrierea registrului AH n corpul celei de a
doua macroinstruciuni WriteChar. Pierdem caracterul introdus de la tastatur.
Macroinstruciunile sunt simple secvene de cod introduse n program de fiecare
dat cnd scriem numele acestora. Cu ct programele sunt mai complexe i
folosesc mai multe registre, cu att crete posibilitatea s greim.
n cazul acestui program putem salva coninutul registrelor nainte de
intrarea n macroinstruciune cu PUSHAD. Avem i alte posibiliti, putem salva
numai coninutul registrului EDX sau putem include instruciunile de salvare i
recuperare a registrelor chiar n corpul macroinstruciunii. Dar, o soluie mai
elegant este s folosim funcii. Modificm nregistrrile corespunztoare
macroinstruciunilor ReadChar i WriteChar din io.inc astfel:
%macro
WriteChar
push EAX
mov AL,byte [%1]
call f_WriteChar
pop EAX
%endmacro
%macro ReadChar 1
push EAX
call f_ReadChar
mov byte [%1],AL
pop EAX
%endmacro
Crem dou fiiere f_ReadChar.asm i f_WriteChar.asm n care
introducem instruciunile funciilor de citire, respectiv afiare, caracter.
section .bss
temp_char resb 256
section .text
global f_ReadChar
f_ReadChar:
pushf
mov eax,3
mov ebx,0
mov ecx,temp_char
mov edx,1
int 80h
mov al,byte [temp_char]
popf
ret
section .bss
temp_char resb 256
section .text
global f_WriteChar
f_WriteChar:
pushad
pushf
mov byte
mov
mov
mov
mov
int
[temp_char],al
eax,4
ebx,1
ecx,temp_char
edx,1
80h
popf
popad
ret
Nume
WriteChar surs
ReadChar dest
Operand
Descriere
Afieaz un caracter din surs.
m
Citete un caracter n dest.
m
10.2.4.
Operaii cu fiiere
Programul urmtor ilustreaz citirea unui fiier, stocarea coninutului ntrun buffer i afiarea acestuia pe ecran. Observai faptul c pentru a stoca datele
fiierului avem nevoie de un buffer. De asemenea, la citirea unui fiier trebuie
specificat cantitatea de informaie pe care dorim s o citim, iar aceast cantitate nu
poate depi mrimea buffer-ului. Numrul de octei citit din fiier (returnat de
funcia de sistem) este salvat ntr-o variabil (nrCitit). Fiierul este creat nainte
de rularea programului cu comanda:
echo Acesta este un fisier text > fisier.txt
;
;citireFisier.asm
;
section .data
numeFisier db fisier.txt,0
descriptor dd
0
nrCitit
dd
0
msgErr
db Nu am putut deschide fiierul,0xa
msgErr_lung
equ $ - msgErr
section .bss
MAXBUF
equ 100000
buffer
resb MAXBUF
section .text
global _start
_start:
nop
mov eax,5 ;identificatorul open
mov ebx,numeFisier
mov ecx,0 ;O_RDONLY
mov edx,0644q
int 80h
;open returneaz -1 n eax n caz de probleme
test eax,eax
jns readFile
;dac sunt probleme, TEST EAX,EAX seteaz SF, afim mesaj de eroare i
exit
mov
mov
mov
mov
int
mov
mov
int
eax,4
ebx,1
ecx,msgErr
edx,msgErr_lung
80h
eax,1
ebx,0
80h
readFile:
;salvm descriptorul de fiier
mov [descriptor],eax
;citim fiierul
mov eax,3
mov ebx,[descriptor]
mov ecx,buffer
mov edx,MAXBUF
int 80h
;salveaz numrul de octei citii
mov [nrCitit],eax
;afim buffer-ul
mov eax,4
mov ebx,1
mov ecx,buffer
mov edx,[nrCitit]
int 80h
;nchidem fiierul
mov eax,6
mov ebx,descriptor
int 80h
;ncheiem execuia programului
mov eax,1
mov ebx,0
int 80h
Tabelul 10.2 Moduri de operare asupra fiierelor de ctre funcia de sistem OPEN
Indicator
Nr. n octal
O_RDONLY
00000
O_WRONLY
00001
O_RDWR
00002
Descriere
Deschide fiierul numai n modul citire.
Deschide fiierul numai n modul scriere.
Deschide fiierul att pentru citire ct i pentru
O_CREAT
O_TRUNC
O_APPEND
00100
01000
02000
scriere.
Dac nc nu exist, creaz fiierul.
Dac fiierul exist, terge coninutul acestuia.
Adaug coninut. Scrie la sfritul fiierului, nu la
nceput.
;
;creareFisier.asm
;
section .data
msgErr
msgErr_lung
equ $ - msgErr
numeFisier db
date.txt
descriptor dd
0
buffer
db
What do you think about that?,0xa
db
I think you're alive...,0xa
buffer_lung
equ $ - buffer
section .text
global _start
_start:
nop
;creaz fiierul
mov eax,8 ;identificatorul funciei de sistem create
mov ebx,numeFisier
mov ecx,0644q
;permisiunile fiierului creat
int 80h
test eax,eax
jns createFile
;afieaz mesaj de eroare n caz c nu a putut fi creat
mov eax,4
mov ebx,1
mov ecx,msgErr
mov edx,msgErr_lung
int 80h
mov eax,1
mov ebx,0
int 80h
;n caz de succes salveaz descriptorul ntors de create n registrul EAX
createFile:
mov [descriptor],eax
;scrie n fiier
mov eax,4
mov ebx,[descriptor]
mov ecx,buffer
mov edx,buffer_lung
int 80h
;nchide fiierul
mov eax,6
mov ebx,[descriptor]
int 80h
;ieire din program
mov eax,1
mov ebx,0
int 80h
10.2.5.
4 GB
0FFFFFFFFH
KERNEL SPACE
Memoria virtual a
kernelului
(cod, date, heap, stiv)
3 GB
0BFFFFFFFH
Stiv program
Bloc superior
ESP
Biblioteci partajate
040000000H
USER SPACE
Heap
section .bss
Bloc inferior
section .data
ncrcate din
fiierul executabil
section .text
08048000H
00000000H
lucru. Numrul acestora difer de la sistem la sistem, dar poate fi aproape de 200.
La finalul listei de adrese pentru variabilele de mediu este nc un indicator. Acesta
marcheaz sfritul directorului de stiv.
La primele sisteme Linux adresa de nceput a stivei era ntotdeauna
0BFFFFFFFH. n prezent, din considerente de securitate, sistemele Linux schimb
puin baza stivei odat cu fiecare rulare a programelor. De aceea, adresele de
nceput ale stivei pot diferi de la rulare la rulare, de obicei cu cteva milioane de
octei. n plus, ntre sfritul listei de adrese i nceputul irurilor de elemente se
afl o zon variabil de memorie neutilizat.
Structura stivei poate fi observat cu ajutorul depanatorului. Rulm
programul prezentat n seciunea anterioar: gdb program. Am vzut c
programul nu este lansat imediat n execuie, ci este inut n memorie pn cnd
0x00000000
Calea absolut a executabilului
Variabilele mediu propriu-zise
(fiecare reprezentat ca ir de caractere terminat cu
NULL)
Argumentele din linia de comand propriu-zise
(fiecare reprezentat ca ir de caractere terminat cu
NULL)
Numele programului
Variabile de sistem i spaiu liber
0x00000000
Adresa variabilei de mediu 3
Adresa variabilei de mediu 2
Adresa variabilei de mediu 1
0x00000000
Adresa argumentului 3
Adresa argumentului 2
Adresa argumentului 1
Adresa numelui de progam
Numr de argumente
0
31
ESP
Argument list to give program being debugged when it is started is "10 20".
n acest caz am introdus dou argumente: numerele 10 i 20. Chiar dac programul
nu folosete argumente din linie de comand, putem simula introducere lor n stiv.
Verificarea argumentelor introduse se face cu show args. O alt posibilitate este
s le specificm imediat dup inia de rulare. Nu uitai ca n prealabil s setai
punctul de oprire.
(gdb) b *_start+1
Breakpoint 1 at 0x8048081: file main.asm, line 9.
(gdb) r 10 20
Starting program: /home/stefan/program 10 20
Breakpoint 1, _start () at main.asm:9
9
push 5
(gdb) p $esp
$1 = (void *) 0xffffd400
Observai c linia Starting program indic prezena argumentelor din linia de
comand. Indicatorul de stiv ESP conine adresa vrfului stivei, 0xffffd400.
Afim primele 20 de cuvinte ncepnd cu aceast adres, n format hexazecimal.
(gdb) x /20xw 0xffffd400
0xffffd400:
0x00000003 0xffffd580
0xffffd410:
0x00000000 0xffffd59b
0xffffd420:
0xffffd5f3
0xffffd603
0xffffd430:
0xffffd670
0xffffd69a
0xffffd440:
0xffffdbb6
0xffffdbdc
0xffffd595
0xffffd5bd
0xffffd60e
0xffffd6ba
0xffffdc0e
0xffffd598
0xffffd5d0
0xffffd65e
0xffffd6c6
0xffffdc1e
Atenie, este important s reinem c toate argumentele din linia de comand sunt
specificate ca iruri, chiar dac arat ca numere.
Argumentele din linia de comand sunt urmate de un indicator NULL care
separ adresele argumentelor de adresele variabilelor de mediu. Listm cteva
variabile de mediu pe baza adreselor lor.
(gdb) x /s
0xffffd59b:
(gdb) x /s
0xffffd5bd:
(gdb) x /s
0xffffd5d0:
(gdb) x /s
0xffffd5f3:
(gdb) x /s
0xffffd603:
(gdb) x /s
0xffffd65e:
0xffffd59b
"ORBIT_SOCKETDIR=/tmp/orbit-stefan"
0xffffd5bd
"SSH_AGENT_PID=1188"
0xffffd5d0
"GIO_LAUNCHED_DESKTOP_FILE_PID=4500"
0xffffd5f3
"SHELL=/bin/bash"
0xffffd603
"TERM=xterm"
0xffffd65e
"WINDOWID=73400324"
La rndul lor, acestea se vor sfri cu un indicator NULL. Numrul lor depinde de
aplicaiile prezente n sistem.
10.3.1.
Formatul BCD
Zecimal
0
1
2
3
4
5
6
7
8
9
Binar
0
1
10
11
100
101
110
111
1000
1001
BCD compactat
0000
0001
0010
0011
0100
0101
0110
0111
1000
1001
BCD necompactat
00000000
00000001
00000010
00000011
00000100
00000101
00000110
00000111
00001000
00001001
10.3.2.
BCD compactat
0001 0101
0001 0000 0000
1001 1001
BCD necompactat
00000001 00000101
00000001 00000000 00000000
00001001 00001001
mov edx,1
int 80h
;; exit
mov eax,1
mov ebx,0
int 80h
La final, din motive pur estetice, funcia de afiare a rezultatului este urmat de
funcia de trecere la linie nou. Executabilul primete argumente din linie de
comand astfel:
./calc 3 + 5
Fiecare argument este desprit prin spaiu. Cnd rulm programul prin intermediul
depanatorului, pentru specificarea argumentelor folosim comanda set args 3
+ 5.
Primele trei instruciuni ale programului ncarc adresele argumentelor n
trei registre de uz general (n vederea adresrii lor prin metoda indirect).
(gdb) p /x $esi
$1 = 0xffffd598
(gdb) x /s $esi
0xffffd598:
"3"
(gdb) x /s $edi
0xffffd59c:
"5"
(gdb) x /s $ecx
0xffffd59a:
"+"
Pe baza semnului se alege modulul aritmetic corespunztor. n consecin,
argumentul de semn este comparat pe rnd cu fiecare semn n parte. Codul
semnului putea fi introdus n instruciune direct ca numr hexazecimal, dar am ales
formatul caracter ASCII, deoarece acesta este subiectul acestei seciuni.
(gdb) p /x $dl
$5 = 0x2b
(gdb) p /c $dl
$6 = 43 '+'
Adunarea ASCII
Problemele ncep imediat dup instruciunea de adunare.
(gdb) n
17
mov al,[esi]
(gdb) n
18
adc al,[edi]
(gdb) n
19
jmp .end
(gdb) p /x $eax
$7 = 0x68
Aa cum reiese din exemplul de mai jos, instruciunea adun corect caracterele
ASCII 3 (33H) i 5 (35H).
33H = 0011 0011 +
35H = 0011 0101
68H = 0110 1000
ns 68H reprezint caracterul ASCII 'h', nu caracterul '8' (38H).
Pe de alt parte, valorile 33H i 35H, privite din perspectiva formatului
BCD compactat, sunt valori valide pentru cifrele 3 i 5. La fel i 68H
reprezentare valid pentru cifra 8. Digitul inferior reprezint valoarea cifrei (3, 5,
respectiv 8), iar digitul superior este ignorat (l putem considera 0, i atunci avem
03 + 05 = 08). De fapt, deoarece cei 4 bii inferiori conin valoarea BCD, toi digiii
ASCII de la '0' la '9' pot fi considerai digii BCD valizi. n consecin, n BCD
compactat, adunarea 33H + 35H = 68H este corect. Rezultatul poate fi afiat cu
uurin dac setm digitul superior la valoarea 3. Totui, acest mecanism nu poate
fi aplicat n cazurile n care digitul rezultat este mai mare ca 9. S considerm
adunarea '7' + '5'.
37H = 0011 0111 +
35H = 0011 0101
6CH = 0110 1100
Chiar dac ignorm digitul superior (6), suma tot trebuia s fie de forma 01 02H.
Iat un prim caz de eroare n BCD. Adunarea a dou cifre valide are ca rezultat o
combinaie invalid (necunoscut). n acest caz, 1100 (C). Principiul coreciei n
BCD susine ca, n astfel de cazuri, digitul cu combinaie inexistent trebuie adunat
cu 0110 (6 n zecimal). 6 este diferena ntre baza hexazecimal i cea zecimal.
0000 1100 +
0000 0110
0001 0010
Aadar, dac am avea posibilitatea s adunm n BCD, adunarea '7' + '5' ar da ntradevr '12', deoarece '7' i '5' pot fi tratate ca valori BCD legale. Ei bine, Intel pune
la dispoziie o astfel de instruciune: instruciunea AAA (ASCII Adjust after
Addition). Instruciunea AAA se folosete dup o adunare efectuat cu instruciunea
ADD sau ADC. Suma rezultat n registrul AL este convertit n reprezentare BCD
n doi pai:
dac digitul inferior din registrul AL este mai mare ca 9, sau dac este
setat indicatorul de condiii AF din registrul EFLAGS, adun 6 la AL
i 1 la AH (n urma operaiei, indicatorii de condiii AF i CF sunt
setai).
n toate cazurile, digitul superior este trecut n zero.
n aceste condiii, modulul de adunare din programul calc.asm poate fi
scris astfel:
;; modulul de adunare
mov al,[esi]
adc al,[edi]
aaa
or ax,3030h
cmp ah,30h
jne L0_1
mov [rez],al
jmp .end
L0_1:
xchg ah,al
mov [rez],ax
jmp .end
Pentru a putea afia rezultatul, instruciunea OR AX,3030H seteaz
pentru fiecare cifr BCD digitul superior la 3. Se obine un ir de dou caractere
ASCII. n cazul adunrii '7' + '5' irul ASCII din AX este 3132H. Compararea lui
AH cu 30H are urmtoarele dou scopuri:
dac rezultatul poate fi reprezentat cu un singur caracter, nu includem
n faa acetuia caracterul 0 (pentru 4 + 4 afim 8, nu 08);
dac rezultatul este reprezentat pe dou caractere, trebuie s inem cont
de ordinea corect a octeilor. Octetul superior (AH) este trecut n
memorie la adres superioar, octetul inferior (AL) este trecut n
memorie la adres inferioar. Primul afiat va fi AL.
Dac dorim s adunm un numr cu mai muli digii, trebuie s folosim o
;terge registrul AH
;AL = 38H
;AL = 38H 33H = 05H
;AX = 0005H
;AL = 35H
;terge registrul AH
;AL = 33H
;AL = 33H 38H = FBH
;AX = FF05H
;AX = FF35H
sub al,[edi]
aas
or al,30h
mov [rez],al
jmp .end
Atenie, deoarece ar fi complicat programul, modulul de scdere nu ia n
considerare rezultatul negativ. Scopul nostru este s nelegem instruciunea AAS.
nmulirea ASCII
Instruciunea de corecie a rezultatului unei operaii de nmulire este AAM
(ASCII Adjust after Multiplication). Foarte important, spre deosebire de adunare i
scdere, nmulirea nu trebuie efectuat cu caractere ASCII, ci cu numere n format
BCD necompactat. Instruciunea AAM mparte valoarea din AL cu 10 i pstreaz
ctul n AH i restul n AL.
mov al,3
mov bl,8
mul bl
aam
or ax,3030H
mov [rez],al
jmp .end
.L2_1:
xchg ah,al
mov [rez],ax
jmp .end
Deoarece pentru interpretorul de comenzi bash semnul stelu este caracter
special, cnd efectuai o nmulire, argumentul de semn din linia de comand
trebuie precedat de caracterul \ (ESCAPE), asfel:
./calc 3 \* 5
mprirea ASCII
Instruciunea AAD (ASCII Adjust before Division) corecteaz valoarea
dempritului din acumulator naintea mpririi a dou numere n format BCD
necompactat. Instruciunea AAD nmulete AH cu 10, l adun la AL (aceti doi
pai convertesc numrul din BCD necompactat n binar) i apoi l iniializeaz cu
zero. De exemplu:
mov ax,0205H
mov bl,08H
aad
div bl
sub bl,30h
aad
div bl
or ax,3030h
mov [rez],ax
jmp .end
La fel ca la nmulire, cnd specificm argumentul de semn trebuie s
folosim caracterul ESCAPE.
10.3.3.
10.3.4.
;; afiseaza rezultatul
fara_transport:
WriteStr msg_rez
WriteInt [rez]
nwln
sfarsit:
Exit
Pe lng funciile declarate n io.inc, programul mai folosete dou:
f_ReadInt i f_WriteInt. Prima citete de la tastatur un ir de caractere, l
convertete n numr, i ntoarce rezultatul n registrul EAX. A doua preia un
numr din registrul EAX, l convertete n ir de caractere, i afieaz rezultatul la
monitor. Pentru citirea de la tastatur i afiarea la monitor sunt folosite procedurile
cunoscute f_ReadStr i f_WriteStr. Aadar, n paragrafele urmtoare ne
vom concentra atenia asupra conversiei caracter - valoare binar. Dar, mai nti s
introducem n io.inc macroinstruciunile care le corespund:
extern f_ReadInt
extern f_WriteInt
%macro
WriteInt 1
push
EAX
mov
EAX,%1
call
f_WriteInt
pop
EAX
%endmacro
%macro ReadInt 1
%ifnidni %1,EAX
push
EAX
call
f_ReadInt
mov
%1,EAX
pop
EAX
%else
call f_ReadLInt
%endif
%endmacro
%ifidn (if identity) este o directiv a asamblorului YASM care testeaz dac
textul introdus este identic cu cel declarat. Construcia %ifidn text1,text2
va determina asamblarea codului ce o urmeaz numai i numai dac text1 i
text2, dup expandarea liniei respective, sunt buci identice de text. Spaiile
albe nu sunt luate n considerare. %ifidni (if identity insensitive) este similar
lui %ifdni numai c nu este sensibil la litere mari sau mici. %ifnidn (if not
identity) este forma negativ a lui %ifidn. Evident, exist i varianta %ifnidni.
Toate fac parte din familia directivelor de asamblare condiionat (directive folosite
de preprocesorul YASM). Directivele de asamblare condiionat permit asamblarea
unor seciuni numai n cazurile n care sunt ndeplinite anumite condiii. Sintaxa
general este:
%if <condiie1>
;intruciuni asamblate numai n cazul ndeplinirii <condiie1>
%elif <condiie2>
;instruciuni asamblate numai n cazul nendeplinirii <condiie1> i
ndeplinirii <condiie2>
%else
;instruciuni asamblate dac nu este ndeplinit nicio condiie anterioar
%endif
Aa se explic i apariia %else i %endif din macroinstruciunea noastr.
Similar, sunt posibile i expresiile %elifidn, %elifnidn, %elifidni i
%elifnidni. Preprocesorul YASM implementeaz o gam larg de directive
foarte utile.21
10.3.5.
http://www.nasm.us/doc/nasmdoc4.html
.sfarsit_conversie:
Acesta este algoritmul de conversie propriu-zis. Observai c verificm
validitatea caracterelor din buffer. Un lucru la care nu ne-am gndit nainte. Dar, la
o privire mai atent, secvena are totui o problem. De fapt, mai multe.
Utilizatorul poate introduce numere negative. n definitiv, putem aduna 30 cu -20.
n acest caz, irul de caractere '20' va fi precedat de caracterul '-'. Sau, dintr-un
exces de precizie, utilizatorul poate specifica explicit semnul numerelor pozitive
(de ex, +30) sau poate introduce spaii albe naintea caracterelor numerice. Aceste
posibiliti trebuie adresate. n plus, v aducei aminte c numrul are maxim 12
caractere. Un numr cu mai mult de 12 caractere nu poate fi reprezentat, aadar
este invalid. Secvena urmtoare de instruciuni trebuie s apar n cod imediat
dup introducerea caracterelor n buffer.
.sarim_peste_spatii:
inc esi
cmp byte [esi],0x20
je .sarim_peste_spatii
urmtorul caracter
;urmtorul caracter
;Spaiu?
;dac da, ignor-l i treci la
push edx
push esi
push edi
pushf
.citeste_caracter:
mov esi,0x10
mov edi,buffer
call f_ReadStr
;;deoarece am introdus dou spaii i '-''4''7''5', bufferul conine 7 caractere, ultimul
este caracterul NULL, introdus de funcia de citire.
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
mov esi,buffer
;ncarc adresa bufferului n ESI
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ESI
dec esi
.sarim_peste_spatii:
inc esi
cmp byte [esi],0x20
je .sarim_peste_spatii
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;
ESI
mov edi,esi
;salvam adresa de nceput n EDI
.sir_fara_spatii:
mov ecx,0xc
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 12 ESI = EDI
mov al,byte [esi]
cmp al,'+'
je .caracter_valid
cmp al,'-'
je .caracter_valid
.testeaza_caracter:
cmp al,0x30
jb .caracter_invalid
cmp al,0x39
ja .caracter_invalid
.caracter_valid:
inc esi
dec ecx
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 11 EDI ESI
jcxz .depasire_nr_caractere
mov al,byte [esi]
;ncarc n AL urmtorul caracter
jmp .testeaza_caracter
;;napoi la eticheta .testeaz_caracter. Din aceast bucl se iese cnd
numrul de caractere depete 12 sau cnd se ajunge la caracterul NULL. Aadar,
porile de ieire din bucl sunt reprezentate de etichetele .depasire_nr_
caractere sau .caracter_invalid.
.depasire_nr_caractere:
push ecx
mov ecx,mesaj_depasire
call f_WriteStr
pop ecx
jmp .citeste_caracter
.niciun_caracter_valid:
push ecx
mov ecx,mesaj_lipsa_numar
f_WriteStr
pop ecx
jmp .citeste_caracter
;sri la nceput
;;la primul caracter invalid se ajunge aici. Primul caracter invalid poate fi caracterul
NULL, aa cum ne ateptm, sau poate fi orice alt caracter (de ex., utilizatorul
introduce 687a). De aceea, prima instruciune din bucl are grij s introduc
caracterul NULL pe poziia primului caracter invalid.
;;n cazul nostru, se iese din bucl cnd ajungem la caracterul NULL.
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 8 EDI
ESI
.caracter_invalid:
mov byte [esi],0x0
;introduce caracterul NULL
cmp ecx,0xc
;dac ECX = 12, nseamn c este
vorba de primul caracter. Dac primul caracter este invalid, afim mesaj.
je .niciun_caracter_valid
mov esi,edi
;dac ECX != 12, avem un numr valid. n
acest caz, reintroducem adresa primului caracter n ESI i pregtim registrele
pentru conversia ASCII - binar.
xor eax,eax
;EAX = 0
xor ecx,ecx
;ECX = 0
mov ebx,0xa
;EBX conine multiplicatorul
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;ECX = 0 ESI = EDI
mov cl,byte [esi]
;ncarc n CL primul caracter
cmp cl,'-'
;semnul - ?
je .sari_semn
;da, sri peste caracterul de semn
cmp cl,'+'
;semnul + ?
jne .converteste_caracter ;nu este caracter de semn; sri la
bucla de conversie
.sari_semn:
inc esi
;sri peste semn
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;CL = 0x2d EDI ESI
;;conversia ASCII - binar
.converteste_caracter:
mov cl,byte [esi]
cmp cl,0x0
je .sfarsit_conversie
sub cl,0x30
mul ebx
jb .depasire_nr_caractere
add eax,ecx
jb .depasire_nr_caractere
cmp eax,0x80000000
ja .depasire_nr_caractere
inc esi
;urmtorul caracter
jmp .converteste_caracter
;;n acest moment numrul este n registrul EAX
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;CL = NULL EDI
ESI
.sfarsit_conversie:
mov esi,edi
;ncarc n ESI adresa de nceput a irului
mov cl,byte [esi]
;ncarc n CL primul caracter
;; 0x20 0x20 0x2d 0x34 0x37 0x35 NULL
;;
;;
;;CL = 0x2d ESI = EDI
;;verificm dac EDX este zero. Dac este diferit de zero nseamn c numrul a
depit domeniul de reprezentare.
cmp edx,0x0
jne .depasire_nr_caractere ;
cmp eax,0x80000000
jb .numar_OK
;nr. < 2.147.483.648, valid.
cmp cl,0x2d
;negativ?
jne .depasire_nr_caractere ;Pozitiv. Numrul pozitiv nu
trebuie s fie mai mare de 2.147.483.647. Cum aici s-a ajuns deoarece nr. >
2.147.483.648, nseamn c a fost depit plaja de valori.
.numar_OK:
;numr < 2.147.483.648.
cmp cl,0x2d
;negativ?
jne .sfarsit
;pozitiv => utilizatorul a introdus un numr
pozitiv, fr semn explicit.
neg eax
;negativ => calculm complementul fa de doi.
.sfarsit:
popf
pop edi
pop esi
pop edx
pop ecx
pop ebx
ret
10.3.6.
475 / 10
47 / 10
4 / 10
Ct
47
4
0
Rest
5 + 30H
7 + 30H
4 + 30H
Valoare ASCII
35H
37H
34H
;;
ESI
cmp eax,0x0
;verificm semnul
jge .numar_pozitiv
;pozitiv
mov byte [esi],0x2d
;n caz c numrul este negativ rescrie
primul octet al bufferului cu semnul '-'. In acest mod, la sfarsit stim daca numarul
este pozitiv sau negativ.
neg eax
;calculeaza complementul fata de 2
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ _ _
;;
;;
;;
ESI, EAX = 0xEB (235)
.numar_pozitiv:
;numar pozitiv
mov ebx,0xa
;divizorul
add esi,0xb
;srim la adresa ultimului caracter (ESI +
11)
mov byte [esi],0x0
;introducem un caracter NULL
dec esi
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ _ NULL
;;
;;
;;EAX = 0xEB (235)
ESI
mov ecx,0xa
;contorizm
numrul
de
caractere
procesate
.converteste_numar:
mov edx,0x0
;EDX = 0
div ebx
;mprim EDX:EAX la EBX
add dl,0x30
;restul din DL se transform n caracter
mov byte [esi],dl
;se introduce pe prima pozitie din dreapta a
numarului
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ 0x35 NULL
;;
;;
;;EAX = 23
ESI
dec esi
;se decremeanteaza ESI
dec ecx
;ECX este contorul, numrul nu trebuie s
depeasc 11 cifre
cmp eax,0x0
;se compar EAX cu zero
;;Buffer: 0x2d _ _ _ _ _ _ _ _ _ _ 0x35 NULL
;;
;;
10.4.1.
Dac definete vreun simbol necesar, modulul obiect respectiv este copiat n
executabil. De aceea, bibliotecile trebuie specificate la sfritul liniei de comand22.
10.4.2.
mov ebx,[ebp-4]
mov esp,ebp
pop ebp
ret
Primele dou linii ale funciei reprezint prologul C standard, destinat
crerii cadrului de stiv, iar ultimele trei linii formeaz epilogul C standard. A treia
i a patra linie de la sfrit salveaz i restaureaz registrul EBX, deoarece
bibliotecile partajate scrise n cod independent de poziie folosesc acest registru
pentru memorarea adresei GOT.
Partea important este format din instruciunea CALL i urmtoarele dou
linii. Combinaia CALL i POP obine adresa etichetei .get_GOT, fr a fi necesar
s cunotem dinainte adresa la care va fi ncrcat programul (instruciunea CALL
este codificat relativ la poziia curent). Instruciunea POP ncarc n registrul
EBX adresa etichetei .get_GOT (poziia curent). Instruciunea ADD utilizeaz
unul din tipurile speciale de relocare, GOTPC. Referirea la simbolul care marcheaz
nceputul GOT prin wrt ..gotpc se finalizeaz cu obinerea distanei ntre
cmpul operand al instruciunii ADD i adresa GOT. De aceea este necesar s
adaugm la rezultat adresa de nceput a seciunii curente, reprezentat prin dou
simboluri $$, i s scdem distana ntre adresa seciunii curente i adresa
.get_GOT, aflat deja n registrul EBX. La sfritul instruciunii ADD, EBX
conine adresa de nceput a GOT.
Modificm codul funciei f_nwln.asm astfel nct s devin independent
de poziie.
;
;funcia f_nwln , PIC
;
extern _GLOBAL_OFFSET_TABLE_
section .data
new_line: db
0xa
section .text
global f_nwln:function
f_nwln:
push ebp
mov ebp,esp
push ebx
call .get_GOT
.get_GOT:
pop ebx
Aceasta ncarc adresa new_line ntr-o intrare din GOT. Editorul static,
atunci cnd creaz biblioteca partajat, colecteaz mpreun toate relocrile de tip
..got i construiete GOT astfel nct s se asigure c acesta prezint toate
intrrile necesare.
Elementele de date de tip common sunt adresate n acelai mod. Directiva
common se folosete la declararea elementelor de date comune. Un element de
date comun este asemntor unuia global, numai c este declarat n seciunea
datelor neiniializate.
common
data 4
element_de_date_global
;GREIT
va furniza adresa PLT pentru funcia respectiv, acolo unde programul apelant
crede c se afl aceasta.
Apelul funciilor din exteriorul bibliotecilor
Apelarea funciilor din afara bibliotecii partajate trebuie s se realizeze prin
intermediul unui PLT. Fa de adresa la care este ncrcat biblioteca n memorie
PLT este plasat la un deplasament cunoscut, astfel nct codul bibliotecii s poat
efectua apeluri PLT ntr-un mod independent de poziie. PLT conine instruciuni
de salt ctre deplasamentele coninute de GOT, astfel nct apelurile de funcii
ctre alte biblioteci partajate sau rutine din programul principal pot fi transmise
transparent ctre destinaiile lor reale.
Pentru a apela o rutin extern trebuie s folosit un alt tip special de
relocare PIC, wrt ..plt. Aceasta este mai simpl dect variantele care lucreaz
cu GOT: pur i simplu se nlocuiete CALL f_nwln cu versiunea relativ la PLT,
CALL f_nwln wrt ..plt.
10.4.3.
00000175 t .n_so
00001178 a _DYNAMIC
000011f0 a _GLOBAL_OFFSET_TABLE_
000011fd A __bss_start
000011fd A _edata
00001200 A _end
000011fc d new_line
00000140 T f_nwln
0000014e t f_nwln.get_GOT
Dezasamblorul objdump ne permite s studiem modul n care operatorul wrt a
acionat asupra instruciunilor:
objdump -D -M intel libioapi.so
00000130 <f_nwln>:
130: 55
131: 89 e5
133: 53
134: e8 00 00 00 00
push
mov
push
call
ebp
ebp,esp
ebx
139 <f_nwln.get_GOT>
00000139 <f_nwln.get_GOT>:
139: 5b
pop ebx
13a: 81 c3 7f 10 00 00
add
ebx,0x107f
140: 8d b3 0c 00 00 00
lea
esi,[ebx+0xc]
146: b8 04 00 00 00
mov eax,0x4
14b: bb 01 00 00 00
mov ebx,0x1
150: 89 f1
mov ecx,esi
152: ba 01 00 00 00
mov edx,0x1
157: cd 80
int 0x80
159: 8b 5d fc
mov ebx,DWORD PTR [ebp-0x4]
15c: 89 ec
mov esp,ebp
15e: 5d
pop ebp
15f: c3
ret
000011b8 <.got.plt>:
11b8:
60
11b9:
11 00
...
Disassembly of section .data:
pusha
adc DWORD PTR [eax],eax
000011c4 <new_line>:
11c4:0a
.byte 0xa
n urma instruciunii LEA ESI,[EBX + 0xc], n registrul ESI se va
afla adresa new_line, 000011c4. Dac scdem din aceasta 0xc rezult adresa
000011b8, adic adresa de nceput a tabelei GOT.
10.4.4.
Aplicaie
Aplicaie
Spaiul utilizator
Aplicaie
Biblioteca C
Drivere de dispozitiv
Hardware
Cod de conversie
%d
%u
%x
%c
%s
%%
Descriere
Argumentul este convertit n zecimal
Argumentul este convertit n zecimal fr semn
Argumentul este convertit n hexazecimal
Argumentul este considerat ca fiind un singur caracter
Argumentul este un ir de caractere
Afieaz simbolul procent
db
ntotdeauna %d + %d = %d,0
10.5.1.
-o args -lc
20
30
Amintii-v c primul parametru este considerat numele programului, aadar,
primul argument din linia de comand este al doilea parametru. O greeal ntlnit
frecvent const n compararea cu zero a numrului de argumente din linia de
comand. Din moment ce numele programului se va regsi ntotdeauna pe linia de
comand, numrul de parametri nu va fi niciodat zero.
Urmtorul program afieaz variabilele de mediu.
;
;env.asm
;
section .data
Args db "%s",10,0
section .text
extern printf
extern exit
global _start
_start:
nop
mov ebp,esp
add ebp,12
bucla:
cmp dword [ebp],0
je sfarsit
push dword [ebp]
push Args
call printf
add esp,12
add ebp,4
loop bucla
sfarsit:
push 0
call exit
./env
USERNAME=stefan
DEFAULTS_PATH=/usr/share/gconf/gnome.default.path
GIO_LAUNCHED_DESKTOP_FILE=/usr/share/applications/terminator.desktop
XDG_CONFIG_DIRS=/etc/xdg/xdg-gnome:/etc/xdg
PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
DESKTOP_SESSION=gnome
PWD=/home/stefan
GDM_KEYBOARD_LAYOUT=us
LANG=en_US.UTF-8
GDM_LANG=en_US
MANDATORY_PATH=/usr/share/gconf/gnome.mandatory.path
UBUNTU_MENUPROXY=libappmenu.so
COMPIZ_CONFIG_PROFILE=ubuntu
GDMSESSION=gnome
SHLVL=1
HOME=/home/stefan
LANGUAGE=en_US:en
Cnd n linia de comand nu sunt specificai parametrii, seciunea variabilelor de
mediu ncepe la un deplasament de 12 octei fa de adresa stocat n registrul ESP.
Sfritul seciunii este marcat cu un caracter NULL. Compararea valorii din stiv
cu zero verific faptul c am ajuns la sfritul seciunii. Dac adresa nu este zero,
se afieaz irul indicat de aceasta. Variabilele de mediu prezente n sistem depind
de aplicaiile active i de setrile locale. Putem verifica acest lucru prin crearea
unei noi variabile.
export TEST=/home/test
./env | grep TEST
TEST=/home/test
Acest pas creaz fiierul proc.o. A doua etap compileaz modulul C i editeaz
legturile ntre cele dou module obiect:
gcc -g stabs -o program main.c proc.o m32
Editorul de legturi este invocat automat de ctre GCC.
Registru
Descriere
Destinat s pstreze valoarea returnat de funcie; poate fi modificat
EAX
pn la revenirea din funcie.
EBX
Destinat s indice tabelul global de deplasamente; trebuie pstrat
ECX
Disponibil pentru uzul intern al funciei
EDX
Disponibil pentru uzul intern al funciei
EBP
Indic adresa de baz a stivei; trebuie pstrat
ESP
Indic noua locaie a stivei n cadrul funciei; trebuie pstrat
EDI
Utilizat ca registru local de ctre programul C; trebuie pstrat
ESI
Utilizat ca registru local de ctre programul C; trebuie pstrat
Destinat s pstreze valoarea n virgul mobil returnat de funcie;
ST(0)
poate fi modificat pn la revenirea din funcie
ST(1) - ST(7) Disponibile pentru uzul intern al funciei
Regulile care trebuie respectate de orice funcie pentru a putea fi apelat dintr-un
program C formeaz aa numitele convenii de apel C. Toate funciile
implementate n bibliotecile C respect aceste reguli. Aceleai convenii trebuie
respectate cu strictee i de funciile scrise n asamblare. Pe scurt, acestea sunt:
Funcia trebuie s pstreze valorile iniiale ale registrelor EBX,
ESP, ESI, EDI. Funcia poate folosi aceste registre n corpul ei dar,
11.1.1.
section .text
global proc
proc:
push ebp
;seteaz cadrul de stiv
mov ebp,esp
sub esp,12
push ebx
;funcia trebuie s pstreze EBP, EBX, ESI, & EDI
push esi
push edi
< codul funciei >
pop
pop
pop
mov
pop
ret
edi
esi
ebx
esp,ebp
ebp
adresate din interiorul funciei, relativ la registrul EBP. De exemplu, dac prima
variabil local ocup 4 octei, adresa locaiei va fi [EBP-4]. A doua variabil
local poate fi adresat cu [EBP-8], iar a treia cu [EBP-12].
Totui, funciile n asamblare pot declara propriile segmente .data i
.bss. n acest caz, la momentul compilrii, aceste zone de memorie vor fi
combinate cu zonele .data i .bss ale programului C.
11.1.2.
Cadrul de stiv
Stiva
EBP+16:
EBP+12:
EBP+8:
EBP+4:
EBP+0:
EBP apelant
EBP-4:
EBX apelant
EBP-8:
ESI apelant
EBP-12:
EDI apelant
EBP-16:
(locaie temporar)
EBP-20:
(locaie temporar)
EBP-24:
(locaie temporar)
EBP-28:
(locaie temporar)
EBP-32:
(locaie temporar)
EBP-36:
(locaie temporar)
ESP
Memorie neutilizat
ESP se deplaseaz
n sus i jos
Cadrul de stiv este creat prin salvarea n stiv a registrului EBP (registrul
EBP al funciei apelante) i copierea indicatorului de stiv al funciei apelante n
registrul EBP. Odat ce EBP este ancorat la acel capt al cadrului de stiv,
indicatorul de stiv, ESP, se poate deplasa liber, n funcie de necesitile funciei.
Primele dou instruciuni din prolog:
push ebp
mov ebp,esp
n urma acestor dou instruciuni, registrul EBP este considerat ancora noului cadru
de stiv (sau indicator de cadru). Toate elementele aflate deasupra lui n stiv
(deasupra cadrului de stiv al funciei curente) nu pot fi adresate dect prin
intermediul acestuia. Acolo sunt i parametrii de intrare ai funciei, dac aceasta
necesit aa ceva. Un alt motiv pentru care EBP nu trebuie modificat este c
valoarea indicatorului de stiv al apelantului, ESP, se afl n EBP. Revenirea din
funcie cu un ESP modificat nseamn funcionarea defectuoas a programului
apelant.
nainte de finalul funciei, cadrul de stiv trebuie eliminat. Acesta este rolul
ultimelor dou instruciuni din epilog:
mov esp,ebp
pop ebp
n acest moment, cadrul de stiv nu mai exist i putem executa n
siguran instruciunea RET. Instruciunea RET red controlul programului C.
11.1.3.
Compilarea modulelor
Compilatorul GCC poate obine fiiere obiect din funcii scrise n limbaj
de asamblare i le poate aduga la corpul programului C. Fiierul obiect al funciei
n asamblare este generat cu ajutorul asamblorului YASM.
Urmtoarea funcie n asamblare afieaz un mesaj prin intermediul
funciei standard printf.
;
;proc.asm
;
section .data
Msg db "Mesaj din interiorul functiei in asamblare.",10,0
section .text
global proc
extern printf
proc:
push ebp
mov ebp,esp
sub esp,12
push ebx
push esi
push edi
push Msg
call printf
add esp,4
pop
pop
pop
mov
edi
esi
ebx
esp,ebp
pop ebp
ret
Funcia proc.asm folosete secvenele prolog i epilog specifice conveniei
de apel C. Observai cum numele su este declarat global i faptul c textul
mesajului este declarat n seciunea datelor iniializate. Pentru afiarea acestuia se
folosete funcia standard printf, declarat ca extern. Asamblm funcia n
asamblare:
yasm -f elf -g stabs proc.asm melf_i386
Odat creat fiierul obiect, acesta poate fi specificat n linia de comand a
compilatorului, alturi de fiierul C surs. Fiierul programului principal este numit
main.c.
/* programul principal apeleaz funcia n asamblare */
#include <stdio.h>
int main()
{
extern void proc();
proc();
printf("Mesaj din interiorul programului principal.\n");
return 0;
}
Programul principal apeleaz funcia n asamblare pe baza numelui. Parantezele
rotunde indic faptul c numele respectiv reprezint o funcie. Foarte important,
funcia trebuie declarat ca funcie extern. n acest caz, funcia nu necesit
parametri de intrare i nici nu returneaz vreo valoare. Urmtoarele comenzi creaz
executabilul i l ruleaz.
gcc -g stabs -o program main.c proc.o m32
./program
Mesaj din interiorul functiei in asamblare.
Mesaj din interiorul programului principal.
Codul compilat poate fi dezasamblat cu programul objdump:
objdump -d program -M intel -j .text
Vei observa c sunt prezente mai multe seciuni. Cele care ne intereseaz sunt
main i proc. Seciunea main conine codul n asamblare generat de compilator
pentru programul C.
080483f4 <main>:
80483f4:
55
80483f5:
89 e5
80483f7:
83 e4 f0
80483fa:
83 ec 10
80483fd:
e8 1e 00 00 00
8048402:
c7 04 24 00 85 04 08
8048409:
e8 16 ff ff ff
804840e:
b8 00 00 00 00
8048413:
c9
8048414:
c3
push ebp
mov ebp,esp
and esp,0xfffffff0
sub esp,0x10
call 8048420 <proc>
mov DWORD PTR [esp],0x8048500
call 8048324 <puts@plt>
mov eax,0x0
leave
ret
push
mov
sub
push
push
push
push
call
add
pop
pop
pop
mov
pop
ret
ebp
ebp,esp
esp,0xc
ebx
esi
edi
0x804a018
8048314 <printf@plt>
esp,0x4
edi
esi
ebx
esp,ebp
ebp
Sintaxa AT&T
"prog.c"
main, @function
%ebp
%esp, %ebp
$16, %esp
push
mov
sub
ebp
ebp, esp
esp, 16
11.2.2.
Formatul de baz
instruciunile pe linii separate. n acest caz, fiecare instruciune trebuie nchis ntre
ghilimele.
asm ( movl $1, %eax\n\t
movl $0, %ebx\n\t
int $0x80);
Acest format este mai uor de citit i neles. Seciunea asm poate fi inclus
oriunde n codul surs. Urmtorul program demonstreaz cum arat o seciune asm
ntr-un program.
/* global.c AT&T Exemplu care utilizeaz variabile C */
#include <stdio.h>
int a = 50;
int b = 60;
int rez;
int main() {
asm ( pusha\n\t
movl a, %eax\n\t
movl b, %ebx\n\t
imull %ebx, %eax\n\t
movl %eax, rez\n\t
popa);
printf(the answer is %d\n, rez);
return 0;
}
Variabilele a, b i rez sunt definite n programul C ca variabile globale i sunt
folosite n seciunea asm. Observai c, n interiorul seciunii asm, valorile sunt
utilizate ca locaii de memorie i nu ca elemente de date imediate. Variabilele pot fi
de asemenea folosite n alt parte a programului.
Variabilele de date trebuie declarate ca globale. n interiorul seciunii asm nu
putem folosi variabile locale.
Codul n asamblare generat de compilator arat astfel:
gcc -S global.c -o - -m32
.file "global.c"
.globl a
.data
.align 4
.type a, @object
.size a, 4
a:
.long 50
.globl b
.align 4
.type b, @object
.size b, 4
b:
.long 60
.comm rez,4,4
.section
.rodata
.LC0:
.string
"the answer is %d\n"
.text
.globl main
.type main, @function
main:
pushl %ebp
movl %esp, %ebp
andl $-16, %esp
subl $16, %esp
#APP
# 8 "global.c" 1
pusha
movl a, %eax
movl b, %ebx
imull %ebx, %eax
movl %eax, rez
popa
# 0 "" 2
#NO_APP
movl rez, %edx
movl $.LC0, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $0, %eax
leave
ret
.size main, .-main
.ident"GCC: (Ubuntu/Linaro 4.5.2-8ubuntu4) 4.5.2"
.section
.note.GNU-stack,"",@progbits
Observai c a i b au fost definite n segmentul datelor iniializate i au primit
valoare, iar rez a fost definit n segmentul datelor neiniializate. Sintaxa AT&T
definete dou directive prin care putem declara date neiniializate, .comm este
una dintre ele. Nu intrm n detalii.
Codul generat folosete prologul i epilogul C standard, ultimul
implementat prin instruciunea leave. n cadrul codului generat se afl o regiune
cuprins ntre simbolurile #APP i #NO_APP. Aceasta este seciunea care conine
codul specificat cu asm. Observai efectul caracterelor \n i \t asupra amplasrii
codului.
Din exemplu reiese nc o caracteristic important: secvena util de
instruciuni n asamblare trebuie precedat de instruciunea PUSHA i ncheiat cu
instruciunea POPA. Este important de reinut c nainte de execuia codului n
asamblare trebuie s salvai valorile iniiale ale registrelor, iar la final trebuie s
restaurai coninutul acestora deoarece este posibil ca GCC s foloseasc acele
registre n alt parte a codului surs (dac sunt modificate n interiorul seciunii
asm, pot aprea efecte nedorite).
Sintaxa Intel
Pn acum codul inserat n program a respectat sintaxa AT&T. A fost mai
uor pentru noi s folosim sintaxa AT&T pe o platform Unix, cu un compilator ce
recunoate nativ sintaxa AT&T. S vedem cum se modific programul anterior
dac specificm seciunea asm n sintaxa Intel.
/* global.c Intel - Exemplu care utilizeaz variabile C */
#include <stdio.h>
int a = 50;
int b = 60;
int rez;
int main()
{
asm (".intel_syntax noprefix\n\t"
"pusha\n\t"
"mov eax,a\n\t"
"mov ebx,b\n\t"
"imul eax,ebx\n\t"
"mov rez,eax\n\t"
"popa\n\t"
".att_syntax prefix");
printf("the answer is %d\n", rez);
return 0;
}
Observai c la nceputul secvenei n asamblare am inserat ".intel_syntax
noprefix\n\t". Aceasta indic asamblorului GAS c urmtoarea secven de
instruciuni n asamblare folosete sintaxa Intel. noprefix nseamn c registrele
nu au nevoie de simbolul % ca prefix. Ultima linie, ".att_syntax prefix",
indic asamblorului GAS c instruciunile urmtoare vor fi asamblate n sintaxa
implicit, AT&T. n acest caz, compilarea se face n mod obinuit:
gcc -g stabs -o global global.c m32
Dac lipsea ultima linie din seciunea asm, ".att_syntax noprefix",
puteam obine executabilul fornd compilatorul GCC s tranduc tot codul n
sintaxa Intel:
gcc -g stabs -o global global.c -masm=intel m32
Modificatorul volatile
Cnd introducem instruciuni n asamblare n programe C trebuie s ne
gndim ce efect ar putea avea procesul de compilare asupra acestora. n etapele de
transformare a codului surs n cod de asamblare, compilatorul poate ncerca s
optimizeze codul scris n asamblare n vederea creterii performanei. De obicei,
acest lucru are loc prin eliminarea funciilor neutilizate, partajarea registrelor ntre
variabile care nu sunt folosite n acelai timp sau rearanjarea codului pentru o
parcurgere mai uoar.
ns este posibil ca optimizarea s produc efecte nedorite, n special
asupra funciilor din seciunea asm. Modificatorul volatile, plasat imediat
dup cuvntul cheie asm, indic compilatorului faptul c nu este dorit optimizarea
acelei seciuni de cod. Formatul este urmtorul:
asm volatile (cod n asamblare);
Utilizarea __asm__
n unele cazuri, cuvntul asm, utilizat la identificarea unei seciuni de cod
n asamblare, trebuie modificat. Specificaiile ANSI C utilizeaz cuvntul asm n
alte scopuri, lucru care mpiedic utilizarea sa ca identificator de seciune n
asamblare. Dac scriei cod utiliznd conveniile ANSI C, trebuie s utilizai
11.2.3.
Formatul extins
Formatul de baz are cteva limitri. n primul rnd toate valorile de intrare
i de ieire trebuie s utilizeze variabile globale definite n program. n plus, trebuie
s fim extrem de ateni s nu modificm valorile vreunui registru. Formatul extins
pune la dispoziie cteva opiuni adiionale ce ne permit s controlm exact modul
n care este interpretat codul n asamblare. Formatul versiunii extinse a seciunii
asm arat astfel:
asm (cod n asamblare : locaii de ieire : operanzi
de intrare : lista de modificri);
Acest format are patru seciuni, fiecare separat prin dou puncte:
cod n asamblare - propoziiile n asamblare propriu zise;
locaii de ieire - registre i/sau locaii de memorie ce vor conine valorile
de ieire la finalul codului n asamblare;
operanzi de intrare - registre i/sau locaii de memorie ce conin valorile de
intrare necesare codului din seciunea scris n asamblare;
lista de modificri - registre ce vor fi modificate de ctre codul n
asamblare.
Prezena tuturor seciunilor nu este obligatorie. Dac codul n asamblare nu
genereaz valori de ieire, seciunea locaiilor de ieire poate lipsi. n schimb,
separarea seciunilor prin dou puncte se pstreaz. Doar dac seciunea codului n
asamblare nu modific niciun registru ultimele dou puncte pot fi omise.
n formatul asm de baz valorile de intrare i ieire erau definite ca
variabile globale n programul C i ncorporate n seciunea n asamblare pe baza
numelui acestora. n formatul extins putem atribui valori de intrare i ieire att
registrelor ct i locaiilor de memorie. Formatul listei de valori de intrare i ieire
este:
caracter_de_control (variabil)
unde variabila este o variabil C declarat n program. n formatul asm extins pot
fi folosite att variabile globale ct i locale. Caracterul de control (constraint)
specific unde trebuie plasat variabila (pentru valori de intrare) sau unde trebuie
depozitat (pentru valori de ieire). Acesta definete locaia final a unei variabile:
registru sau locaie de memorie.
Tabelul 11.2 Caractere de control
Caracter de control
a
b
c
d
S
D
r
q
A
f
m
V
i
n
g
Descriere
Folosete registrele %eax, %ax sau %al
Folosete registrele %ebx, %bx sau %bl
Folosete registrele %ecx, %cx sau %cl
Folosete registrele %edx, %dx sau %dl
Folosete registrele %esi sau %si
Folosete registrele %edi sau %di
Folosete orice registru de uz general
Folosete registrul %eax, %ebx, %ecx sau %edx
Combin registrele %eax i %edx pentru valori de 64 de bii
Folosete un registrul pentru variabile n virgul mobil
Folosete locaia de memorie a variabilei
Folosete numai o locaie de memorie direct
Folosete un ntreg imediat
Folosete un ntreg imediat de valoare cunoscut
Folosete orice registru sau locaie de memorie disponibil
Urmtorul exemplu
asm volatile (".intel_syntax noprefix\n\t"
"cld\n\t"
"rep\n\t"
"stosl\n\t"
".att_syntax prefix\n\t"
: /* nicio locaie de ieire */
: "c" (contor), "a" (valoare), "D" (dest)
: "%ecx", "%edi"
);
ncarc valoarea aflat n registrul EAX n locaia de memorie indicat de
adresa dest, de un numr de ori contor. Linia operanzilor de intrare
: "c" (contor), "a" (valoare), "D" (dest)
ncarc contor n ECX, valoare n EAX i dest n EDI. Aceste informaii
pot ajuta compilatorul s optimizeze codul. De exemplu, n timpul operaiunilor de
alocare a registrelor compilatorul ar putea aranja ca valoarea s se afle deja n
registrul EAX sau, dac codul n asamblare s-ar afla ntr-o bucl, ar putea pstra
coninutul EAX de-a lungul execuiei acesteia.
Observai c nu am specificat nimic n seciunea locaiilor de ieire. Nu
este necesar ca valorile de ieire s fie ntotdeauna precizate. Valorile de intrare ale
unor instruciuni indic i valorile de ieire. n cazul nostru, valoarea de ieire este
deja definit ca fiind una din valorile de intrare (adresa indicat de registrul EDI),
aadar aceasta nu mai trebuie specificat n cmpul locaiilor de ieire. Dar,
deoarece nu am definit explicit nicio valoare de ieire, este important s folosim
cuvntul cheie volatile. n caz contrar, din moment ce seciunea asm nu
produce niciun rezultat, compilatorul poate considera toat seciunea inutil i
renun la ea (nu o introduce n fiierul executabil).
Lista de modificri
: "%ecx", "%edi"
indic compilatorului faptul c registrele EAX i EDI au fost rescrise
(valorile acestora la finalul secvenei n asamblare difer de cele avute la intrarea n
seciune). n lista de modificri registrele sunt scrise cu prefixul %. Dac scriem
ntr-o locaie de memorie trebuie s includem n list cuvntul memory. Dac
sunt modificai indicatorii de condiii din registrul EFLAGS este indicat s
introducem n lista de modificri operatorul cc (conditional codes), care anun
Registrul de ieire este modificat cu semnul egal; indicm faptul c asupra lui pot fi
efectuate numai operaii de scriere. Compilatorul ncarc valorile variabilelor
val1 i val2 n registrele EDX i ECX. Val1 i val2 pot fi variabile globale
sau locale. n ultimul caz, ele se gsesc n zona de stiv a programului. Rezultatul
generat n registrul EAX este apoi transferat n variabila rez. Observai c
registrele au ca prefix dou semne %% n loc de unul singur. Acest lucru este
necesar deoarece, n secvena n asamblare, fiecare operand este adresat de
compilator pe baza unui numr (placeholder) precedat de semnul %. Compilatorul
atribuie fiecrei valori de intrare sau ieire prezente n cmpurile formatului extins
un numr, pe baza poziiei sale n list, ncepnd cu zero. De exemplu:
asm (cod n asamblare
: =r (rez)
: r (val1), r (val2)
);
va produce urmtoarele coduri:
%0, pentru registrul care conine variabila rez.
%1, pentru registrul care conine variabila val1.
%2, pentru registrul care conine variabila val2.
Metoda permite utilizarea n secvena de asamblare att a registrelor ct i a
locaiilor de memorie. Numrul total de operanzi este limitat la zece sau la numrul
maxim de operanzi pe care l poate lua o instruciune (care este mai mare). Codul
n asamblare arat astfel:
imull %1, %2
movl %2, %0
Deoarece GCC identific operanzii pe baza %0, %1, .a.m.d, %edx ar fi interpretat
ca fiind parametrul %e, care nu exist i, n consecin, ar fi ignorat. Apoi ar
ncerca s gseasc simbolul dx, simbol invalid, deoarece nu are prefixul %, dar
care oricum nu era ce se intenionase.
Aadar, pentru o secven n asamblare, de exemplu una care nmulete o
valoare cu 5, putem declara registrele explicit
asm ("leal (%%ebx,%%ebx,4), %%ebx"
: "=b" (x)
: "b" (x) );
caz n care trebuie s avem grij s folosim dou simboluri procent, %%, sau putem
permite compilatorului s aleag registrele. Cu excepia cazului n care avem
nevoie explicit de un anumit registru, cel mai bine este s permitem compilatorului
s aleag. Numrul registrelor este limitat i se poate ntampla ca GCC s nu poat
folosi registrele specificate fr s ascund valorile anterioare.
asm ("leal (%1,%1,4), %0"
: "=r" (x)
: "r" (x) );
Mai mult, dac dorim ca variabila s foloseasc acelai registru, att pentru intrare
ct i pentru ieire, putem specifica registrul atribuit pe baza codului %0.
asm ("leal (%0,%0,4), %0"
: "=r" (x)
: "0" (x) );
Dac se lucreaz cu un numr mare de valori de intrare i ieire, metoda numeric
poate deveni problematic. De aceea, compilatorul GNU pune la dispoziie o
variant alternativ, i anume, declararea unor denumiri. Numele este declarat n
seciunea care definete valorile de intrare sau ieire i respect urmtorul format:
%[nume] caracter_de_control (variabil)
Bibliografie
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
Internals.com, http://www.internals.com/articles/protmode/introd
uction.htm
Randal Bryant, David OHallaron, Computer Systems A
Programmers Perspective, 2nd Edition, Pearson Education, Inc,
2011, ISBN: 0-13-610804-0.
Drago Acostchioaie, Programare C i C++ pentru Linux,
Editura Polirom, 2002, ISBN: 973-681-112-3.
blog.interlinked.org, Vim Introduction and Tutorial, Tutorial,
http://blog.interlinked.org/tutorials/vim_tutorial.html
Free Software Foundation, GCC 4.6.3 Manual, 2010, http://gcc.
gnu.org/onlinedocs/gcc-4.6.3/gcc/
NASM Development Team, NASM The Netwide Assembler,
ver. 2.10.01, 2012, http://www.nasm.us/xdoc/2.10.01/nasmdoc.pdf
Peter Johnson, Yasm User Manual, 2012, http://www.tortall.net
/projects/yasm/manual/manual.pdf
Tool Interface Standards, Executable and Linkable Format
(ELF), Portable Formats Specification, ver. 1.1, http://www.sky
free.org/linux/references/ELF_Format.pdf
Phillip, Using Assembly Language in Linux, Articol, 2001,
http://asm.sourceforge.net//articles/linasm.html
Bharata Rao, Inline assembly for x86 in Linux, Articol, 2001,
http://www.ibm.com/developerworks/linux/library/l-ia/index.html
Brennan Underwood, Brennans Guide to Inline Assembly, ver.
1.1.2.2, Articol, http://www.delorie.com/djgpp/doc/brennan/bren
nan_att_inline_djgpp.html
Ram Narayan, Linux assemblers: A comparison of GAS and
NASM, Articol, 2007, http://www.ibm.com/developerworks/linux
/library/l-gas-nasm/index.html
iecc.com, Dynamic Linking and Loading, rev. 2.3, Articol, 1999,
http://www.iecc.com/linker/linker10.html
Ashish Bansal, Shared objects for the object disoriented!,
Articol, 2001, http://www.ibm.com/developerworks/library/lshobj/
Baris Simsek, Libraries, EnderUNIX Software Development
Team,
2004,
http://www.enderunix.org/simsek/articles/
libraries.pdf