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 16:03] catalin.vasile3004 [Încărcarea datelor în registre] |
bune-practici [2015/11/30 16:48] catalin.vasile3004 [Segmentation Fault debugging: GDB quicky] |
||
---|---|---|---|
Line 154: | Line 154: | ||
==== Segmentation Fault debugging: GDB quicky ==== | ==== 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: | **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ă. | + | - Î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 se înmagazinează mai multe instrucţiuni de la adresa de la care se aduce cod/instrucţiuni, 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. | - Î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. | + | - 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 poate afla şi din userspace ce instrucţiune a cauzat Segmentation Fault. |
Exemplu de cod cu probleme: | Exemplu de cod cu probleme: | ||
<file asm segfault.asm> | <file asm segfault.asm> | ||
Line 192: | Line 192: | ||
Segmentation fault | Segmentation fault | ||
</code> | </code> | ||
+ | Cum rulăm gdb: | ||
+ | <code> | ||
+ | gdb nume_binar | ||
+ | </code> | ||
+ | Exemplu: | ||
+ | <code bash> | ||
+ | catalin.vasile3004@fep ~ $ gdb ./segfault | ||
+ | GNU gdb (GDB) Red Hat Enterprise Linux (7.2-60.el6) | ||
+ | Copyright (C) 2010 Free Software Foundation, Inc. | ||
+ | License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html> | ||
+ | This is free software: you are free to change and redistribute it. | ||
+ | There is NO WARRANTY, to the extent permitted by law. Type "show copying" | ||
+ | and "show warranty" for details. | ||
+ | This GDB was configured as "x86_64-redhat-linux-gnu". | ||
+ | For bug reporting instructions, please see: | ||
+ | <http://www.gnu.org/software/gdb/bugs/>... | ||
+ | Reading symbols from /export/home/acs/stud/c/catalin.vasile3004/load...(no debugging symbols found)...done. | ||
+ | (gdb) | ||
+ | </code> | ||
+ | În acest moment s-a deschis consola debugger-ului, **dar programul NU rulează**. | ||
+ | Pentru a rula programul: | ||
+ | <code> | ||
+ | set disassembly-flavor intel | ||
+ | run param1 param2 param3 < fisier.in > fisier.out | ||
+ | </code> | ||
+ | Cu **run**-ul dat ca exemplu, e ca şi cum am fi rulat programul în felul următor: | ||
+ | <code bash> | ||
+ | ./segfault param1 param2 param3 < fisier.in > fisier.out | ||
+ | </code> | ||
+ | ''set disassembly-flavor intel'' vă ajută pentru a afişa eventualele printări de cod de asamblare într-o sintaxă cunoscută. Limbajul de asamblare reprezintă un set de alias-uri pentru instrucţiunile din binarul unui program. Aceste alias-uri nu au o formă standardizată motiv pentru care acestea diferă de la un asamblor la altul. By default, tool-urile din Linux folosesc sintaxa [[https://en.wikibooks.org/wiki/X86_Assembly/GAS_Syntax|AT&T]]. 99% din tool-urile din Linux (gdb NU se află printre ele) pot primii argumentul ''-M intel'' pentru a afişa sau a trata codul de asamblare ca şi cum ar fi în sintaxa recomandată de Intel (care se regăseşte şi la NASM). Programe care pot primi acest flag sunt: gcc (gas), objdump, etc. | ||
+ | **Revenind la gdb**, în momentul în care rulăm o să ne dea următoarea eroare: | ||
+ | <code bash> | ||
+ | # ... | ||
+ | number: 0 | ||
+ | number: 0 | ||
+ | number: 0 | ||
+ | number: 0 | ||
+ | |||
+ | Program received signal SIGSEGV, Segmentation fault. | ||
+ | 0x08048423 in keep_printing () | ||
+ | </code> | ||
+ | Pentru a vedea ce instrucţiunea a provocat segfault, putem da următoarea comandă: | ||
+ | <code> | ||
+ | (gdb) display/10i $pc | ||
+ | 1: x/10i $pc | ||
+ | => 0x8048423 <keep_printing+1>: push DWORD PTR [ecx*4+0x804a02c] | ||
+ | 0x804842a <keep_printing+8>: push 0x804a020 | ||
+ | 0x804842f <keep_printing+13>: call 0x80482f0 <printf@plt> | ||
+ | 0x8048434 <keep_printing+18>: add esp,0x8 | ||
+ | 0x8048437 <keep_printing+21>: pop ecx | ||
+ | 0x8048438 <keep_printing+22>: inc ecx | ||
+ | 0x8048439 <keep_printing+23>: cmp ecx,DWORD PTR ds:0x804a040 | ||
+ | 0x804843f <keep_printing+29>: jl 0x8048422 <keep_printing> | ||
+ | 0x8048441 <keep_printing+31>: ret | ||
+ | 0x8048442 <keep_printing+32>: xchg ax,ax | ||
+ | </code> | ||
+ | * **$pc** este o variabilă **gdb**, şi vine de la **P**rogram **C**ounter (este pointer-ul la instrucţiunea curentă). | ||
+ | * **display** face dump de la un pointer dat ca argument, în cazul nostru **$pc** | ||
+ | * **i**-ul îi spune lui **display** să interpreteze datele de acolo ca şi cum ar fi instrucţiuni | ||
+ | * **10** îi sune lui **display** câţi operanzi/instrucţiuni de tipul **i** (instrucţiune) să afişeze\\ \\ | ||
+ | Prin ''<keep_printing+some_number>'', **gdb** incearcă să ne arate cam pe unde ar fi această instrucţiune. În cazul nostru instrucţiunea este aproape de label-ul **keep_printing**.\\ | ||
+ | Pentru a vedea ce valoare a avut un registru la momentul în care s-a declanşat **segfault**-ul, puteţi da: | ||
+ | <code> | ||
+ | (gdb) print $nume_registru | ||
+ | </code> | ||
+ | În cazul nostru s-ar putea să ne intereseze ce valoare are **ecx**. Pentru a afla acest lucru: | ||
+ | <code> | ||
+ | (gdb) print $ecx | ||
+ | </code> | ||
+ | |||
===== Categorie 3 ===== | ===== Categorie 3 ===== | ||
* TODO | * TODO | ||
* TODO | * TODO |