This shows you the differences between two versions of the page.
Both sides previous revision Previous revision Next revision | Previous revision Next revision Both sides next revision | ||
bune-practici [2015/11/30 15:11] catalin.vasile3004 [Erori des întâlnite] |
bune-practici [2015/11/30 16:03] catalin.vasile3004 [Încărcarea datelor în registre] |
||
---|---|---|---|
Line 16: | Line 16: | ||
===== Erori des întâlnite ===== | ===== Erori des întâlnite ===== | ||
==== Confuzii la accesarea datelor în memorie (operatorul de dereferenţiere) ==== | ==== Confuzii la accesarea datelor în memorie (operatorul de dereferenţiere) ==== | ||
+ | Pentru cei care sunt la început de drum la a învăţa assembly, este o confunzie foarte mare cum se foloseşte operatorul de dereferenţiere din asamblare: ''[ ]''\\ | ||
+ | Care este diferenţa între ''op reg, var'' şi ''op reg, [var]''?\\ | ||
+ | În 99.999999999999% din cazuri, operaţia fără paranteze pătrate înseamnă să foloseşti adresa acelei variabile pe post de operand. Exemple: | ||
+ | <code asm> | ||
+ | section .data | ||
+ | var: DD 34 | ||
+ | section .text | ||
+ | mov eax, var ; put var's >>address<< into the eax register | ||
+ | add eax, var ; add to eax, the >>address<< of var | ||
+ | </code> | ||
+ | Acest cod este echivalent cu următorul cod din **C**: | ||
+ | <code c> | ||
+ | int var = 34; | ||
+ | eax = &var; /* mov eax, var */ | ||
+ | eax = eax + &var; /* add eax, var */ | ||
+ | </code> | ||
+ | În cazul în care foloseşti paranteze pătrate: | ||
+ | <code asm> | ||
+ | section .data | ||
+ | var: DD 34 | ||
+ | section .text | ||
+ | mov eax, [var] ; put var's >>value<< into eax | ||
+ | add eax, [var] ; add to eax, the >>value<< of var | ||
+ | </code> | ||
+ | Acest lucru ar fi echivalent în **C** cu: | ||
+ | <code c> | ||
+ | int var = 34; | ||
+ | eax = var; /* mov eax, [var] */ | ||
+ | eax = eax + var; /* add eax, [var] */ | ||
+ | </code> | ||
+ | Printre singurele instrucţiuni care fac abatare de la aceste reguli, este **lea** (load effective address). | ||
+ | <code asm> | ||
+ | section .data | ||
+ | var: DD 34 | ||
+ | section .text | ||
+ | lea eax, [var] ; put var's >>address<< into the eax register | ||
+ | </code> | ||
+ | În rest, toate celelalte instrucţiuni aderă la regulile enunţate mai sus. Dacă or mai exista şi alte instrucţiuni care se comportă ca **lea**, cel mai probabil nu vor fi tratate în aceste laboratoare. | ||
==== Încărcarea datelor în registre ==== | ==== Încărcarea datelor în registre ==== | ||
Adesea apar erori chiar la încărcarea datelor în registre. | Adesea apar erori chiar la încărcarea datelor în registre. | ||
Line 114: | Line 151: | ||
</code> | </code> | ||
Poate că nu ai un cod care compilează, dar măcar nu ai un cod care compilează şi ruleaza greşit. | Poate că nu ai un cod care compilează, dar măcar nu ai un cod care compilează şi ruleaza greşit. | ||
+ | |||
+ | ==== Segmentation Fault debugging: GDB quicky ==== | ||
+ | **gdb** este un debugger în linie de comandă. Unul din lucrurile la care ne poate ajuta acesta este să găsim punctele în care ne dă **Segmentation Fault** un program. Mulţi abordează această problemă prin imbricarea de **printf**-uri în puncte intermediare în program. Acest lucru nu prea ajută. Uitaţi cam cum este prelucrat un program de un procesor: | ||
+ | - Într-o singură etapă se aduc mai multe instrucţiuni din memorie. Accesul la memorie este scump, şi dacă la fiecare instrucţiune de 5-6 bytes ne-am duce în memorie, nu am avea o performanţă foarte bună. Din acest motiv s-a inventat un modul în procesor, numit prefetching, în care un procesor înmagazinează mai multe instrucţiuni de la adresa de la care aduce cod, pentru ca execuţia să fie mai fluidă. | ||
+ | - În momentul în care procesorul îşi dă seama că una din instrucţiuni accesează o zonă nevalidă din memorie, trimite un semnal către sistemul de operare. Şi sistemul de operare este tot o bucată de cod care se execută pe procesor. Până când acest semnal trezeşte codul din sistemul de operare, e foarte posibil ca programul să mai fi executat o căruţă de instrucţiuni, din acest motiv, o înşiruire de printf-uri s-ar putea executa şi după instrucţiunea care a produs Segmentation Fault-ul. | ||
+ | - Sistemul de operare se trezeşte şi închide forţat programul care a cauzat probleme. Printre datele primite de la semnal se regăseşte şi adresa instrucţiunii care a cauzat Segmentation Fault. Cu un debugger, se pot afla şi din userspace ce instrucţiune a cauzat Segmentation Fault. | ||
+ | Exemplu de cod cu probleme: | ||
+ | <file asm segfault.asm> | ||
+ | extern printf | ||
+ | |||
+ | section .data | ||
+ | str: DB `number: %d\n` | ||
+ | nr: DD 1, 2, 3, 4, 5 | ||
+ | len: DD 4000 | ||
+ | |||
+ | section .text | ||
+ | |||
+ | global main | ||
+ | |||
+ | main: | ||
+ | xor ecx, ecx | ||
+ | keep_printing: | ||
+ | push ecx ; save ecx, because it will be destroyed by printf call | ||
+ | push dword [nr + 4*ecx] | ||
+ | push str | ||
+ | call printf | ||
+ | add esp, 8 | ||
+ | pop ecx ; restore ecx | ||
+ | inc ecx | ||
+ | cmp ecx, [len] | ||
+ | jl keep_printing | ||
+ | ret | ||
+ | </file> | ||
+ | Programul parcurge un vector şi afişează valorile sale. Deşi programul are doar 5 elemente, **len**-ul este setat greşit la 4000 de elemente. Dacă compilăm şi rulam programul acesta ne va da un **segfault**: | ||
+ | <code bash> | ||
+ | # ... | ||
+ | number: 0 | ||
+ | number: 0 | ||
+ | number: 0 | ||
+ | Segmentation fault | ||
+ | </code> | ||
===== Categorie 3 ===== | ===== Categorie 3 ===== | ||
* TODO | * TODO | ||
* TODO | * TODO |