This is an old revision of the document!


[importat] Laborator 06 - Compilare; Makefile; gcc

Tutorial

1. Compilatorul gcc.

Descărcați arhiva TODO. Dezarhivați folosind tar:

tar xf lab06.tar.gz

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

gcc hello.c

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

ls -l
file a.out

Rulați fișierele executabile.

./a.out

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

gcc hello.c -o stallman

Rulați executabilul creat.

Compilați sursa afișând avertismente:

gcc hello.c -o linus -Wall

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.

Rulați comenzile următoare:

gcc -o jobs -Wall hello.c
gcc -Wall -o turing hello.c
gcc -Wall hello.c -o dijkstra

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 identice.

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

2. Etapele compilării

Intrați în directorul tutorial/3/1. Vizualizați conținutul fișierului answer.c. Rulați comanda

gcc -E answer.c -o answer.i

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.

Intrați în directorul tutorial/3/2. Vizualizați conținutul fișierului answer.c. Rulați comanda

gcc -S answer.c -o answer.s

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.

Folosind gcc efectuați etapa de asamblare:

gcc -c answer.s -o answer.o

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

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

gcc answer.o

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

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

make sandwich

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

Atunci când un fișier Makefile nu există și nu este specificat, make va folosi comenzi implicite pentru build, în funcție de extensia fișierului primit ca parametru. În acest caz, comanda implicită a fost cc sandwich.c -o sandwich.

Intrați în directorul tutorial/4/2. Fișierul Makefile va fi folosit pentru a compila sursa sandwich.c. Rulați comenzile:

make
./sandwich

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ă?

Intrați în directorul tutorial/4/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ă:

make
ls
rm sandwich
make sandwich
ls
make reply
ls
./sandwich
./reply

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.

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

make reply
make reply

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

Intrați în directorul tutorial/4/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

ls
make
ls
make clean
ls

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.

Intrați în directorul tutorial/4/6. Fișierul Makefile este identic cu cel din exercițiul anterior (verificați cu diff). Rulați comenzile

touch clean
ls
make
ls
make clean
ls

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 .PHONY: 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.

Intrați în directorul tutorial/4/7. Rulați comenzile următoare și observați ce se execută:

make story
rm story
make story CFLAGS=-Wall
rm story
make story CC=gcc
rm story
make story CC=gcc CFLAGS="-Wall -Wextra -O3"

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 tutorial/5. Compilați fișierul traceable.c utilizând gcc, salvând executabilul ca traceme. Rulați comenzile:

./traceme
strace ./traceme

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:

strace -e write ./traceme

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

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

gcc math.c

Se observă că avem o eroare undefined reference to `sinh'. Compilați programul până la modulul obiect:

gcc -c math.c -o math.o

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

nm math.o

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:

gcc -lm math.c

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

ldd a.out

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

gcc math.c -static -o staticmath -lm

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

Executabilul a.out este mai mic decât staticmath, deoarece nu conține codul obiect pentru biblioteca math.

Pentru a observa simbolurile definite în executabilul legat static, rulați:

ldd staticmath

Exerciții

Următoarele exerciții vor simula rezolvarea unei teme de casă

1. Organizare proiect. Fișiere multiple

Creați fișierele README, tema.c, util.c, util.h. Fișierul tema.c va include headerul util.h și va defini funcția main.

Adăugați un fișier Makefile pentru compilarea sursei.

Modificați regula de compilare pentru a compila ambele fișiere .c.

2. Make pentru automatizări

Adăugați o regulă clean pentru ștergerea fișierelor generate.

Pentru trimiterea temei spre corectare, fișierele vor fi arhivate. Adăugați o regulă pack în acest sens. Fișierul arhivă se va numi: tema.tar.gz.

Corectarea făcându-se automat (teoretic), adăugați o regulă send care copiază arhiva pe serverul corector.local, folosind SCP și utilizatorul student cu parola student. Regula va crea arhiva, dacă aceasta nu există.

Revizuiți copierea fișierelor prin SSH, din laboratorul anterior.

3. Variabile make

Modificați fișierul Makefile astfel încât denumirea arhivei să conțină numele vostru și numele materiei, uso.

Definiți o variabilă care să conțină numele fișierelor ce vor intra în arhivă. Modificați regula pack astfel încât să nu regenereze arhiva, decât atunci când unul dintre aceste fișiere s-a modificat.

systems/uso/laboratoare/laborator-06.1348420509.txt.gz · Last modified: 2012/09/23 20:15 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