This is an old revision of the document!


[descriere] Laborator 06 - Compilare; Makefile; gcc

Tutorial

== 1. Documentație functii C 1.1. În limbajul C există o funcție, numită qsort} care permite sortarea unui vector. Pentru a putea afla sintaxa (și modul de utilizare) al acestei funcții se pot căuta informații online, se pot consulta cărți care descriu limbajul C sau se poate folosi un utilitar la îndemâna oricărui utilizator Linux - man}:

<screen> man qsort </screen>

Aceste informații sunt obținute în urma instalării pachetului manpages-dev}.

1.2. Accesați pagina de manual a funcțiilor printf} și scanf}:

<screen> man printf man 3 printf man scanf </screen>

1.3. Intrați în directorul 1}. Fișierul amnesia.c} conține apeluri de funcții pentru care parametrii lipsesc. Urmăriți comentariile referitoare la aceste funcții și completați locurile lipsă căutând în paginile de manual ordinea parametrilor funcțiilor.

Pentru verificare, rulați scriptul memento.sh} din directorul curent.

== 2. Compilatorul gcc.

2.1. Intrați în directorul 2}. Fișierul hello.c} este o sursă C completă. Compilați fișierul utilizând gcc}:

<screen> gcc hello.c </screen>

Ce fișiere s-au creat? Aflați tipul acestora utilizând comanda file}.

Rulați fișierele executabile.

2.2. Compilați sursa specificând și numele fișierului executabil:

<screen> gcc hello.c -o stallman </screen>

Rulați executabilul creat.

2.3. Compilați sursa afișând avertismente:

<screen> gcc hello.c -o linus -Wall </screen>

Deoarece un program pentru care sunt afișate avertismente în timpul compilării conține (foarte probabil) o greșeală/un bug, se recomandă folosirea opțiunii wall} pentru fiecare compilare.

* Pentru acasă: aflați ce fac flag-urile -Wextra și -Werror.

Corectați warning-urile apărute. Recompilați sursa.

2.4. Rulați comenzile următoare:

<screen> gcc -o jobs -Wall hello.c gcc -Wall -o turing hello.c gcc -Wall hello.c -o dijkstra </screen>

Utilizând ls_-l} comparați dimensiunile celor 3 executabile create. Verificați că cele 3 executabile sunt identice vizualizând output-ul fiecăruia.

Faptul că două executabile au aceeași dimensiune și același comportament (output) nu înseamnă neapărat că ele sunt identice.

Calculați md5-ul fiecărui executabil și verificați că este identic.

Folosind comanda md5sum} putem obține un rezumat al conținutului unui fișier. Acest rezumat, cunoscut și sub numele de hash, are proprietatea interesantă că cea mai mică modificare posibilă în fișierul inițial va duce la modificarea completă a md5-ului. Din acest motiv, 2 fișiere cu același md5 pot fi considerate aproximativ identice.

În general, ordinea parametrilor nu contează. Anumiți parametri au argumente. Aici avem parametrul o}. Acest argument trebuie urmat întotdeauna de numele executabilului.

== 3. Etapele compilării

3.1. Intrați în directorul 1}. Vizualizați conținutul fișierului answer.c}. Rulați comanda

<screen> gcc -E answer.c -o answer.i </screen>

Cum s-a modificat fișierul sursă în urma etapei de preprocesare? Ce tip are fișierul generat (folosiți file})?

Etapa de preprocesare realizează substituții la nivelul codului pentru a expanda macro-uri, a include fișiere antet, etc.

3.2. Intrați în directorul 2}. Vizualizați conținutul fișierului answer.c}. Rulați comanda

<screen> gcc -S answer.c -o answer.s </screen>

Ce tip are fișierul generat în etapa de compilare?

Etapa de compilare transformă codul C în cod în limbaj de asamblare, dependent de sistemul pentru care se face compilarea.

3.3. Folosind gcc} efectuați etapa de asamblare:

<screen> gcc -c answer.s -o answer.o </screen>

Ce tip are fișierul obținut?

Etapa de asamblare transformă codul în limbaj de asamblare în cod mașină, cod binar înțeles doar de calculator.

Codul rezultat nu este întotdeauna complet, de exemplu, în cazul folosirii unei funcții definite în alt fișier c} adresa acestei funcții nu va fi cunoscută în momentul asamblării fișierului. Ea va fi completată în etapa următoare

3.4. Ultima etapă din procesul de compilare este cea de link-editare. Folosind gcc, invocați această etapă pentru a obține executabilul final:

<screen> gcc answer.o </screen>

Ce fișier a fost creat?

Ce tip are acest fișier?

Rulați executabilul.

Etapa de link-editare presupune completarea codului mașină generat în etapa anterioară prin includerea informațiilor din alte surse compilate, biblioteci, etc…

== 4. Makefile

4.1. Intrați în directorul 1}. Fișierul sandwich.c} conține un program C valid. Rulați

<screen> make sandwich </screen>

și vizualizați fișierele create (utilizând file} pentru a vedea tipul acestora). Ce comandă este folosită pentru a compila sursa?

4.2. Intrați în directorul 2}. Fișierul makefile} va fi folosit pentru a compila sursa sandwich.c}. Rulați comenzile:

<screen> make ./sandwich </screen>

Vizualizați fișierul makefile}. Fișierul conține o singură linie sandwich}. Aceasta îi indică utilitarului make} să execute target-ul sandwich}. Deoarece nu există altceva, se va încerca folosirea unei reguli implicite.

Care este regula implicită folosită? (afișată în terminal). Diferă fată de cea anterioară?

4.3. Intrați în directorul 3}. Fisierul makefile} a fost completat astfel încât să se precizeze explicit ce sursă se compilează, pentru a putea diferenția între cele 2 surse prezente în director, sandwich.c} și reply.c}. Rulați comenzile următoare și urmăriți ce se execută:

<screen> make ls rm sandwich make sandwich ls make reply ls ./sandwich ./reply </screen>

Un target poate avea o listă de dependențe. Acestea se scriu pe aceeași linie, după }.

Observați că dacă nu precizăm un target se va executa primul prezentat în Makefile.

4.4 Modificați reply.c} adăugând porțiunea comentată. Rulați comenzile

<screen> make reply make reply </screen>

Deoarece între cele 2 comenzi nu am modificat sursa, nu se va executa nimic pentru a doua comandă.

4.5. Intrați în directorul 5}. Fișierul makefile} a fost completat astfel încât să se poată construi ambele executabile cu aceeași comandă și pentru a putea șterge toate fisierele create pe baza surselor. Rulați comenzile

<screen> ls make ls make clean ls </screen>

Pentru targetul clean} s-au introdus și instrucțiunile necesare producerii acestuia din dependențe. Acestea se pun indentate cu un tab pe liniile imediat următoare definirii targetului.

4.6. Intrați în directorul 6}. Fișierul makefile} este identic cu cel din exercițiul anterior (verificați cu diff}). Rulați comenzile

<screen> touch clean ls make ls make clean ls </screen>

Verificați că ați rulat touch_clean} !!

De ce nu se revine la situația initială a fișierelor din director?

Redenumiți fișierul makefile.good} în makefile} și rulați din nou comenzile de mai sus. Singura diferență este linia all_clean}.

Se recomandă includerea în phony} a tuturor target-urilor ce nu reprezintă fișiere pentru a preveni situația în care un fișier cu același nume există în directorul curent și targetul nu se va executa.

4.7. Intrați în directorul 7}. Rulați comenzile următoare și observați ce se execută:

<screen> make story rm story make story CFLAGS=-Wall rm story make story CC=gcc rm story make story CC=gcc CFLAGS=”-Wall -Wextra -O3” </screen>

Ați folosit variabile makefile} pentru a modifica regula implicită.

Un mic grafic cu etapele compilării și legătura dintre acestea și variabilele makefile este următorul

== 5. Apeluri de sistem

Intrați în directorul 5}. Compilați fișierul traceable.c} utilizând gcc}, salvând executabilul ca traceme}. Rulați comenzile:

<screen> ./traceme strace ./traceme </screen>

Utilitarul strace} permite evidențierea fiecărui apel de sistem din execuția programului. În cazul nostru, printf} se reduce la un apel write}.

Putem evidenția doar anumite apeluri de sistem folosind argumentul e_nume_apel}. Rulați comanda:

<screen> strace -e write ./traceme </screen>

== 6. Biblioteci. nm. ldd. Legare dinamică/statică

6.1. Intrați în directorul 6}. Compilați sursa math.c} utilizând gcc}:

<screen> gcc math.c </screen>

Se observă că avem o eroare undefined_reference_to_sinh}. Compilați programul până la modulul obiect:

<screen> gcc -c math.c -o math.o </screen>

Utilitarul nm} listează simbolurile dintr-un fișier obiect sau dintr-un executabil. Rulați comanda:

<screen> nm math.o </screen>

Liniile ce conțin 'U' listează simbolurile folosite dar nedefinite în math.o}. Liniile cu 'T' listează simbolurile definite (translatate).

Pentru a putea obține executabilul va trebui să definim și simbolul sinh}. Funcția este definită în biblioteca matematică libm.so}. Pentru a da biblioteca ca argument lui gcc} pentru etapa de link-editare va trebui să folosim argumentul l} astfel: * din numele bibliotecii se elimină extensia și prefixul lib}. * ce rămâne se concatenează la l}

Rulați:

<screen> gcc -lm math.c </screen>

Compilarea reușește. Pentru a vedea bibliotecile folosite de executabil vom folosi un alt utilitar - ldd}:

<screen> ldd a.out </screen>

6.2. Folosiți următoarea comandă pentru a compila sursa static:

<screen> gcc math.c -static -o staticmath -lm </screen>

Comparați dimensiunile fișierelor a.out} și staticmath}.

Rulați

<screen> ldd staticmath </screen>

systems/uso/laboratoare/laborator-06.1348415816.txt.gz · Last modified: 2012/09/23 18:56 by ioan.eftimie
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0