TP 4 : profilage et debuggage

Sources \r: \r

\r

appel.c bizarre.c bug.c cmp-poids.c hello.c poids.c swap.c warning.c \r

\r\r

\r

\r

1er hors-oeuvre

\r

objectif\r: apprendre à ne pas négliger les warnings ! \r

\r

Le\rprogramme warning.c est simple\r: lecture de deux entiers calcul du produit et affichage.... \r \rCompiler. \r\r \r%% gcc\rwarning.c -o warning.exe \r \rLancer une exécution pour calculer le produit de 2 par 3.\rCommentaire ? \r \rCompiler avec l'option -Wall \r \r%% gcc -Wall\rwarning.c -o warning.exe \r \rVoilà, il ne faut négliger aucun avertissement du\rcompilateur. Expliquer. \r\r \r

\r

2nd hors-oeuvre \r ^^^^^^^^^^^^^^^^

\robjectif : \rillustration du buffer overflow. \r

Jeter un coup\rd'oeil au programme bizarre.c.\rLe main est composé de trois appels successifs : bonjour(), foo(), et aurevoir().\rL'exécution devrait produire : \r\r ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

\r

%% Bonjour !!\r%% ...\r%% Aurevoir !?\r \rCompiler le programme bizarre.c \r \r%% gcc -Wall\rbizarre.c -o bizarre.exe \r \r\rLancer une execution... \rCommenter sans expliquer ! \r

\r

3*e hors-oeuvre*

\robjectif : \rillustration du lien entre le système et le processus. \r \rJeter un coup d'oeil au\rprogramme hello.c. \rCompiler hello.c \r\r

% gcc -Wall hello.c\r-o hello.exe \r \rLancer une execution \r \r% \r./hello.exe

\r

Commenter\rsans expliquer ! \r

\r

Exercice 1

\robjectif : detecter\run debordement de tableau \r\rgdb \r: file, quit, backtrace, list, print \r

Compiler\rbug.c \r \r%% gcc -Wall\rbug.c -o bug.exe \r \rLancer une execution : \r \r%% ./bug.exe 10 \r \rLe programme plante : mais ou ca ? \r\r \rLancer gdb \r \r%% gdb \r \rCharger le fichier a debugger. \r \rgdb > file\rbug.exe \r \rLancer la même exécution. \r\r \rgdb> run 100 \r \rDans quelle procédure se trouve l'erreur ? \r \rAfficher la liste des appels. \r \rgdb >\rbacktrace \r\r \rAfficher le contexte \r \rgdb > list \r \rPas grand chose !!! Pour obtenir plus d'information, \ril faut compiler avec l'option de debuggage.. \r \rQuitter gdb \r\r \rgdb > quit \r \rCompiler avec l'option -g \r \rLancer gdb sur bug.exe \r \r% gdb bug.exe \r\r \rLancer la même exécution. \r \rgdb> run 100 \r \rL'erreur est toujours là ! \rOn apprend qu'elle concerne une instruction de la ligne 6 du programme, cette ligne se\rtrouve\rdans la procédure place. \r\r \rAfficher le contexte. \r \rgdb > list \r \rAfficher les valeurs des variables \r \rgdb > print i \rgdb > print j \r\r \rConclusion. \r \r

\r

Exercice 2 \r ^^^^^^^^^^

\robjectif : detecter un débordement de pile \rgcc : option -D (macrodéfinition) \r\rgdb : backtrace \r

Compiler\rappel.c \r \r% gcc appel.c\r-o appel.exe \r \rLancer l'exécution \r \r\r% ./appel.exe\r5 23 \r \rCommentaire ? \rLancer la même execution sous gdb \r \r% gdb appel.exe \r \rgdb > run 5\r23 \r\r \rVous apprenez le lieu de l'erreur. \rComment en est-on arrivé\rlà ? consulter la liste des appels... \r \rgdb >\rbacktrace

\r\r

Commentaire\r? \r

\r

Compiler\ren passant la macrodéfinition max=10000 \r

\r

% gcc\r-Dmax=10000 appel.c -o appel.exe \r

\r

Lancer\rla même execution sous gdb \r\r \r% gdb appel.exe \r \rgdb > run 5\r23 \r \rConsulter la pile des appels. \r \rgdb >\rbacktrace \r\r \rCommenter la différence du nombre d'appels empilés. \r

\r

Exercice 3 \r ^^^^^^^^^^

\robjectif : tracer une\rexécution pour vérification. \roption gcc : -g ( debuggage ) \r\rcommandes gdb : run, break, info, next,\rstep, display \r

\rCompiler le programme poids.c\rl'option de debuggage \r \r%% gcc\r-g poids.c -o poids.exe \r \rLancer \r\r \r%% gdb\rpoids.exe \r \rvous êtes sous le debuggeur. \r \r[ a ] Langer le programme avec l'argument de ligne de commande : 7 \r \rgdb> run 7 \r \rLe programme poids.exe est ok.\rCommenter. \r\r \r[ b ] Faire un deuxième essai.

\r

gdb> run 31 \r \r[ c ] \r \rPlacer un point d'arrêt à l'entrée de la fonction main. \r\r \rgdb>\rbreak main \r \rLancer une exécution. \r \rgdb> run 7 \r \r\rLe programme s'arrête sur quelle ligne ? \r \rPasser à l'instruction suivante. \r \rgdb> next \r \rItérer plusieurs fois jusquau bout...

\r\r

gdb> next \r \r[ d ] \r \rRefaire [ c ] en remplacant next par step. \r\r \rQuelle est la différence entre next et step ? \r \r[ e ] \r \rPlacer un point d'arrêt à l'entrée de la fonction poids. \r\r \rgdb> break\rpoids \r \rConsulter l'information sur les points d'arrêts. \r \rgdb> info\rbreak \r \rLancer \r\r \rgdb> run 7 \r \rContinuer \r \rgdb> next \r \rContinuer avec next jusqua l'arret du programme. \rComparer avec [b] \r\r \r[ f ] \r \rSuspendre le point d'arrêt sur main. \r \rgdb>disable\r1 \r \rPréparer le tracage des valeurs de z et z-1 en décimale \r\r \rgdb> display\rz \rgdb> display\rz-1

\r

et\ren binaire \r

\r

gdb> display /t z\rgdb> display\r/t z-1 \r\r \rExécuter avec le parametre 249 \r \rgdb> run 249 \r \rContinuer pas à pas jusquà l'arrêt du\rprogramme. \r\r

\r

Exercice 3 \r ^^^^^^^^^^

\robjectif : profilage d'un\rexécutable. \rshell : time \rgcc \r: option -O (optimisation) \r\r-pg \r(profilage) \rgprof : option -b \r

\r[ a ] Compiler le programme cmp-poids.c. \r \r% gcc -Wall\r-pg cmp-poids.c -o cmp-poids.exe \r \rMesurer le temps de d'exécution cmp-poids.exe\r20 \r\r \r% time\r./cmp-poids.exe 20 \r \rFaire d'autres mesures avec l'argument instancié à 21,\r22, 23... \rComment évolue le temps d'execution ? \r \r[ b ] Refaire comme dans [ a ] mais avec l'option d'optimisation -O2 \r \r% gcc -Wall\r-pg cmp-poids.c -o cmp-poids.exe -O2 \r\r \r[ c ] Compiler le programme cmp-poids.c\ravec l'option de profilage. \r \r% gcc -Wall\r-pg cmp-poids.c -o cmp-poids.exe \r \rLancer l'execution : \r

\r

% cmp-poids.exe 24. \r\r \rVotre répértoire contient des nouveaux fichiers. Lesquels\r? \r \r% ls -alt \r

\r

Analyser\rles performances des fonctions poids1\ret poids2 au moyen de\rla commande gprof sans option\r\r \r \r% gprof cmp-poids.exe \r \rLa même chose via\run pipe more \r \r% gprof\rcmp-poids.exe | more \r \rLa même chose en bref \r\r \r% gprof\rcmp-poids.exe -b \r\r

\r

Exercice 4 \r ^^^^^^^^^^

\r

\robjectif : \rpassage par valeur, par adresse. \rgdb : watch, disassemble \r\r

Compiler\rla source swap.c \r \r% gcc\r-Wall -g swap.c -o swap.exe \r \rLancer le debuggeur sur swap.exe \r \r% gdb swap.exe \r \r[ a ] Placer un point d'arret sur la procedure swap_val. \r\r \rgdb> b\rswap_val \r \rLancer l'exécution \r \rgdb> run \r \r \rPréparer la trace des variables x, y du main : \r\r \rgdb>\rdisplay main::x\rgdb>\rdisplay main::y\r \rainsi que celles des variables locales x, y \r\r \rgdb>\rdisplay x\rgdb>\rdisplay y \r \rRépéter : \r\r \rgdb> next \r \rCommenter. \r \r[ b ] Effacer les traces \r \rgdb>\rundisplay \r \r\rRelancer le programme. \r \rgdb> run \r \rLister le code machine de la procédure main \r \rgdb>\rdisassemble main \r\r \rRepérer les appels de swap_adr\ret swap_val. \rFaire la calcul des adresses effective empilées avant\rl'instruction : call \r0x8048328 <swap_adr> \r \rgdb> print\r$ebp + 0xfffffffc \r\r \rAfficher les adresses de x et y. \r \rgdb> print\r&x \rgdb> print\r&y \r \rConclusion. \r\r

\r

Exercice 5 \r ^^^^^^^^^^

\r

\robjectif :\rrésoudre le puzzle du\rprogramme hello.exe ! \rgcc : ligne de commande. \r\r

\r\rCompiler le programme hello.c. \rLancer une execution. \rExpliquer.

\r

Indication\r: \rPlacer un point d'arrêt sur le main. \rLancer une exécution. Afficher les valeurs de argv[0], argv[1]\retc... \r

\r\r

Exercice 6 \r ^^^^^^^^^^

\robjectif \r: résoudre le puzzle\rdu programme bizarre.exe ! \rassembleur : instruction CALL \r\r

Compiler\rle programme. \r\rLancer une exécution. \rExpliquer. \r

\r

Indication\r: \rSous gdb, desassembler les\rsous-programmes main et foo du programme bizarre.c. \rRepérer les adresses des appels des procédures bonjour(), foo() et aurevoir(). \r\r \rUne instruction CALL ptr\rs'écrit sur 5 octets. Placée à l'adresse\r<adr> elle consiste à empiler l'adresse de retour\r<adr+5> avant de \rfaire un saut à\r\rl'adresse ptr. Vérifier ce fait en placant un point\rd'arrêt sur foo() afin de consulter le contenu de la pile, par\rexemple: \r

\r

display /x *( ( (\rint* ) adr) + 2) \r \rA l'entrée d'une procedure\rle registre de base de pile est sauvegarde par empilage ( 4 octet ). \rL'adresse de retour empilee par\rCALL est donc décalée de 8 octets par rapport à\rcelle de\rla première variable locale. \r\r

\r

Ecrire un programme analogue en\rdéclarant une variable char\radr[10] à la place de char * adr. \rVous changerez l'adresse de retour par une instruction adr[ X ] += 5 où X est à déterminer.\r\r \r* \r*