This shows you the differences between two versions of the page.
so:laboratoare:laborator-05 [2019/03/16 22:25] ioana_elena.ciornei |
so:laboratoare:laborator-05 [2022/03/02 09:32] (current) teodor_stefan.dutu [Exercițiul 0 - GSOC] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Laborator 05 - Gestiunea memoriei ====== | ====== Laborator 05 - Gestiunea memoriei ====== | ||
- | ===== Materiale ajutătoare ===== | ||
- | *[[http://elf.cs.pub.ro/so/res/laboratoare/lab05-slides.pdf | lab05-slides.pdf]] | ||
- | *[[http://elf.cs.pub.ro/so/res/laboratoare/lab05-refcard.pdf | lab05-refcard.pdf]] | ||
==== Nice to read ==== | ==== Nice to read ==== | ||
* TLPI - Chapter 7, ''Memory Allocation'' | * TLPI - Chapter 7, ''Memory Allocation'' | ||
+ | |||
+ | ===== Link-uri către secțiuni utile ===== | ||
+ | |||
+ | * [[#Gestiunea memoriei]] | ||
+ | * [[#Spațiul de adresă al unui proces]] | ||
+ | * [[#Alocarea/Dealocarea memoriei]] | ||
+ | * [[#Lucru cu memoria - Probleme]] | ||
+ | * [[#GDB - Detectarea zonei de acces nevalid de tip page fault]] | ||
+ | * [[#mcheck - verificarea consistenței heap-ului]] | ||
+ | * [[#Leak-uri de memorie]] | ||
+ | * [[#Valgrind]] | ||
+ | * [[#mtrace]] | ||
+ | * [[#Dublă dealocare]] | ||
+ | | ||
===== Gestiunea memoriei ===== | ===== Gestiunea memoriei ===== | ||
Line 23: | Line 34: | ||
Este, în consecință, fundamentală cunoașterea contextului în care acționează subsistemul de gestiune a memoriei și înțelegerea interfeței puse la dispoziție programatorului de către sistemul de operare. | Este, în consecință, fundamentală cunoașterea contextului în care acționează subsistemul de gestiune a memoriei și înțelegerea interfeței puse la dispoziție programatorului de către sistemul de operare. | ||
+ | |||
===== Spațiul de adresă al unui proces ===== | ===== Spațiul de adresă al unui proces ===== | ||
Line 317: | Line 329: | ||
DIE (mat == NULL, "HeapAlloc"); | DIE (mat == NULL, "HeapAlloc"); | ||
- | for (i = 0; i < n; i++) { | + | for (i = 0; i < m; i++) { |
mat[i] = HeapAlloc(processHeap, 0, n * sizeof(**mat)); | mat[i] = HeapAlloc(processHeap, 0, n * sizeof(**mat)); | ||
if (mat[i] == NULL) { | if (mat[i] == NULL) { | ||
Line 842: | Line 854: | ||
====== Exerciții ====== | ====== Exerciții ====== | ||
- | ===== Exercițiul 0 - Joc interactiv (2p) ===== | + | <note important> |
+ | În cadrul laboratoarelor vom folosi repository-ul de git al materiei SO - https://github.com/systems-cs-pub-ro/so. Va trebui sa clonați repository-ul pe masinile virtuale folosind comanda: ''git clone https://github.com/systems-cs-pub-ro/so''. Dacă doriți să descărcați repositoryul în altă locație, folosiți comanda ''git clone https://github.com/systems-cs-pub-ro/so ${target}''. | ||
- | * Detalii desfășurare [[http://ocw.cs.pub.ro/courses/so/meta/notare#joc_interactiv|joc]]. | + | Pentru a actualiza repository-ul, folosiți comanda ''git pull origin master'' din interiorul directorului în care se află repository-ul. Recomandarea este să îl actualizați cât mai frecvent, înainte să începeți lucrul, pentru a vă asigura că aveți versiunea cea mai recentă. În cazul în care gitul detectează conflicte la nivelul vreunui fişier, folosiți următoarele comenzi pentru a vă păstra modificările: |
+ | <code> | ||
+ | git stash | ||
+ | git pull origin master | ||
+ | git stash pop | ||
+ | </code> | ||
+ | Pentru mai multe informații despre folosirea utilitarului git, urmați ghidul de la https://gitimmersion.com. | ||
+ | </note> | ||
+ | <note tip>Pentru a vă ajuta la implementarea exercițiilor din laborator, în directorul ''utils'' din arhivă există un fișier ''utils.h'' cu funcții utile. | ||
+ | </note> | ||
+ | ==== Exercițiul 0 - GSOC ==== | ||
- | ===== Linux ===== | + | Google Summer of Code este un program de vară în care studenții |
+ | (indiferent de anul de studiu) sunt implicați în proiecte Open Source | ||
+ | pentru a își dezvolta skill-urile de programare, fiind răsplătiți cu o | ||
+ | bursă a cărei valoare [[https://developers.google.com/open-source/gsoc/help/student-stipends|depinde de țară]] | ||
+ | ([[https://developers.google.com/open-source/gsoc|pagină principală GSOC]]). | ||
- | <note important>În rezolvarea laboratorului folosiți arhiva de sarcini [[http://elf.cs.pub.ro/so/res/laboratoare/lab05-tasks.zip | lab05-tasks.zip]]</note> | + | UPB se află în top ca număr de studenți acceptați; în fiecare an fiind |
+ | undeva la aprox. 30-40 de studenți acceptați. | ||
+ | Vă încurajăm să aplicați! | ||
+ | |||
+ | ===== Linux ===== | ||
- | <note tip>Pentru a vă ajuta la implementarea exercițiilor din laborator, în directorul ''utils'' din arhivă există un fișier ''utils.h'' cu funcții utile. | ||
- | </note> | ||
==== Exercițiul 1 - Zone de stocare a variabilelor ==== | ==== Exercițiul 1 - Zone de stocare a variabilelor ==== | ||
Intrați în directorul ''1-counter'' și implementați funcția ''inc()'' care întoarce de fiecare dată un întreg reprezentând numărul de apeluri până în momentul respectiv al funcției ''inc'' (**nu** aveți voie să folosiți variabile globale). | Intrați în directorul ''1-counter'' și implementați funcția ''inc()'' care întoarce de fiecare dată un întreg reprezentând numărul de apeluri până în momentul respectiv al funcției ''inc'' (**nu** aveți voie să folosiți variabile globale). | ||
- | |||
==== Exercițiul 2 - Spațiul de adresă al unui proces ==== | ==== Exercițiul 2 - Spațiul de adresă al unui proces ==== | ||
Line 892: | Line 920: | ||
==== Exercițiul 4 - Rezolvarea unei probleme de tip Segmentation Fault ==== | ==== Exercițiul 4 - Rezolvarea unei probleme de tip Segmentation Fault ==== | ||
- | Intrați în directorul ''4-gdb'' și inspectați sursa. Programul ar trebui să citescă un mesaj de la ''stdin'' și să-l afișeze. Compilați și rulați sursa. Rulați încă o dată programul din ''gdb'' (revedeți [[so:laboratoare-2013:resurse:gdb#rulare gdb|rularea unui program din gdb]]). | + | Intrați în directorul ''4-gdb'' și inspectați sursa. Programul ar trebui să citescă un mesaj de la ''stdin'', să îi transforme fiecare literă în majusculă și apoi să-l afișeze. Compilați și rulați sursa. |
- | Pentru a identifica exact unde crapă programul folosiți comanda [[http://inside.mines.edu/fs_home/lwiencke/elab/gdb/gdb_42.html | backtrace]]. Pentru detalii despre comenzile din gdb folosiți comanda ''help'':<code bash> | + | Rulați încă o dată programul din ''gdb'' (revedeți [[so:laboratoare-2013:resurse:gdb#rulare gdb|rularea unui program din gdb]] și [[#acces nevalid | detectarea unui acces nevalid de tip page fault]]): |
- | (gdb) help | + | <code bash> |
- | </code> | + | (gdb) run |
+ | Starting program: /home/student/lab05/skel/lin/4-gdb/fault | ||
+ | Give input string:text | ||
- | Schimbați frame-ul curent cu frame-ul funcției ''main'' (revedeți [[#acces nevalid | detectarea unui acces nevalid de tip page fault]]):<code bash> | + | Program received signal SIGSEGV, Segmentation fault. |
- | (gdb) frame main | + | 0x0000555555554809 in upper_string (msg=0x0) at fault.c:27 |
+ | 27 while (msg[i] != '\0') { | ||
+ | (gdb) bt | ||
+ | #0 0x0000555555554809 in upper_string (msg=0x0) at fault.c:27 | ||
+ | #1 0x0000555555554835 in main () at fault.c:40 | ||
+ | (gdb) | ||
</code> | </code> | ||
- | Inspectați valoarea variabilei ''buf'':<code bash> | ||
- | (gdb) print buf | ||
- | </code> | ||
- | Acum dorim să vedem de ce este ''buf = NULL'', urmărind pașii: | + | Acum dorim să vedem de ce este ''msg = NULL'', urmărind pașii: |
* Omorâți actualul proces:<code bash> | * Omorâți actualul proces:<code bash> | ||
(gdb) kill | (gdb) kill | ||
</code> | </code> | ||
- | * Puneți un breakpoint la începutul funcției ''main'':<code bash> | + | * Puneți un breakpoint la începutul funcției ''read_message'':<code bash> |
- | (gdb) break main | + | (gdb) break read_message |
</code> | </code> | ||
- | * Rulați programul și inspectați valoarea lui ''buf'' înainte și după apelul funcției ''malloc'' (folosiți ''next'' pentru a trece la instrucțiunea următoare, fără a urmări apelul de funcție). | + | * Finalizați funcția:<code bash> |
+ | (gdb) finish | ||
+ | Run till exit from #0 read_message () at fault.c:14 | ||
+ | Give input string:text | ||
+ | 0x0000555555554825 in main () at fault.c:38 | ||
+ | 38 message = read_message(); | ||
+ | Value returned is $1 = 0x0 | ||
+ | </code> | ||
+ | |||
+ | * După cum puteți vedea, ''read_message'' returneză ''0x0'' în loc de adresa mesajului citit. | ||
* Explicați sursa erorii, apoi rezolvați-o. | * Explicați sursa erorii, apoi rezolvați-o. | ||
+ | <note tip> | ||
+ | * https://stackoverflow.com/questions/40290049/why-does-gcc-return-0-instead-of-the-address-of-a-stack-allocated-variable | ||
+ | * https://github.com/gcc-mirror/gcc/commit/f22a2cb7500755d69ee5965985d8621c735579c0 | ||
+ | </note> | ||
==== Exercițiul 5 - Lucru cu memoria - Valgrind ==== | ==== Exercițiul 5 - Lucru cu memoria - Valgrind ==== | ||
Line 932: | Line 977: | ||
* în funcția ''take_snapshot'' salvați în structura de date ce reține imaginea stivei câmpurile adresă și valoare. | * în funcția ''take_snapshot'' salvați în structura de date ce reține imaginea stivei câmpurile adresă și valoare. | ||
- | Ce reține structura ''stack_elements''? | + | Ce reține structura ''stack_element''? |
Funcția ''f2'' pune pe stivă un vector de 3 întregi. În ce ordine sunt puse elementele vectorului pe stivă? | Funcția ''f2'' pune pe stivă un vector de 3 întregi. În ce ordine sunt puse elementele vectorului pe stivă? | ||
Line 939: | Line 984: | ||
<note tip> | <note tip> | ||
- | Dezasamblați executabilul. Observați că înainte de call ''f2'' se pune pe stivă ''instruction pointer-ul(eip)'' care este adresa primului byte de după call. La intrarea în funcție controlul s-a transmis de la caller la callee. Acesta din urmă salvează vechiul ''base pointer(ebp)'' iar ebp va conține adresa vârfului stivei. | + | Dezasamblați executabilul. Observați că înainte de call ''f2'' se pune pe stivă ''instruction pointer-ul(eip) / rip (x86-64)'' care este adresa primului byte de după call. La intrarea în funcție controlul s-a transmis de la caller la callee. Acesta din urmă salvează vechiul ''base pointer(ebp) / rbp (x86-64)'' iar ebp va conține adresa vârfului stivei. |
</note> | </note> | ||
- | Folosiți gdb pentru a afla informații despre cum este pus array-ul pe stivă și ce index trebuie suprascris: <code bash> | + | Folosiți gdb pentru a afla informații despre cum este pus array-ul pe stivă: <code bash> |
(gdb) break f2 | (gdb) break f2 | ||
(gdb) run | (gdb) run | ||
Line 948: | Line 993: | ||
</code> | </code> | ||
- | Aveți grijă la proctectori de stivă [[https://mudongliang.github.io/2016/05/24/stack-protector.html|Stack protectors]]. | + | Aveți grijă la protectori de stivă [[https://mudongliang.github.io/2016/05/24/stack-protector.html|Stack protectors]]. |
- | In funcția ''f2'' bufferul ''v'' se află pe stivă sub adresa de return a funcției (IP-ul la care se întoarce programul dupa ce execută ''f2''). Scriind în bufferul ''v'' mai multe elemente decat are acesta alocate pe stivă, vom putea suprascrie adresa de return a lui ''f2'' cu o alta adresă (aici, adresa funcției ''show_message''). Atenție, după adresa de return este salvat pe stivă base pointerul și abia apoi găsim și bufferul ''v''. | + | In funcția ''f2'' bufferul ''v'' se află pe stivă sub adresa de return a funcției (IP-ul la care se întoarce programul dupa ce execută ''f2''). Scriind în bufferul ''v'' mai multe elemente decat are acesta alocate pe stivă, vom putea suprascrie adresa de return a lui ''f2'' cu o altă adresă (aici, adresa funcției ''show_message''). Atenție, după adresa de return este salvat pe stivă base pointerul și abia apoi găsim și bufferul ''v''. |
Folosindu-vă de vectorul ''v'' **fortați execuția** funcției ''show_message'' **fără** a o apela explicit. Astfel, după apelul funcției ''f2'', fluxul programului nu se va mai întoarce în funcția ''f1'', ci va executa ''show_message''. Urmăriți comentariile marcate cu //TODO2// (revedeți partea din laborator referitoare la [[#stiva | stivă ]]) | Folosindu-vă de vectorul ''v'' **fortați execuția** funcției ''show_message'' **fără** a o apela explicit. Astfel, după apelul funcției ''f2'', fluxul programului nu se va mai întoarce în funcția ''f1'', ci va executa ''show_message''. Urmăriți comentariile marcate cu //TODO2// (revedeți partea din laborator referitoare la [[#stiva | stivă ]]) | ||
+ | Folosiți-vă de cum arată stiva pentru a afla indexul în cadrul vectorului ''vv'' la care se află IP-ul. | ||
+ | |||
<note tip> | <note tip> | ||
Line 972: | Line 1019: | ||
- | ==== Exercițiul 9 - Lucrul cu stiva ==== | + | <hidden>==== Exercițiul 9 - Lucrul cu stiva ==== |
Intrați în directorul ''9-bad_stack'' și analizați fișierul ''bad_stack.c''. Compilați și rulați programul. | Intrați în directorul ''9-bad_stack'' și analizați fișierul ''bad_stack.c''. Compilați și rulați programul. | ||
Line 983: | Line 1030: | ||
<note tip>Indicație: Mutați variabila ''lab_so'' din funcția ''my_fun()'' într-o altă zonă de memorie.</note> | <note tip>Indicație: Mutați variabila ''lab_so'' din funcția ''my_fun()'' într-o altă zonă de memorie.</note> | ||
- | + | </hidden> | |
- | ===== Exerciții BONUS (3 SO Karma) ===== | + | ===== Exerciții BONUS ===== |
==== BONUS Windows ==== | ==== BONUS Windows ==== | ||
Line 1014: | Line 1061: | ||
<note tip>Pentru rularea programului de test, nu uitați să exportați ''LD_LIBRARY_PATH'' (revedeți secțiunea de [[so:laboratoare-2013:laborator-01#biblioteci in linux | biblioteci partajate din laboratorul 1]])</note> | <note tip>Pentru rularea programului de test, nu uitați să exportați ''LD_LIBRARY_PATH'' (revedeți secțiunea de [[so:laboratoare-2013:laborator-01#biblioteci in linux | biblioteci partajate din laboratorul 1]])</note> | ||
- | ===== Soluții ===== | ||
- | |||
- | [[http://elf.cs.pub.ro/so/res/laboratoare/lab05-sol.zip | lab05-sol.zip]] | ||
===== Resurse utile ===== | ===== Resurse utile ===== |