Proceso de compilaci贸n de la GNU
Al compilar un programa en c, normalmente correr铆amos este comando:
gcc hello.c -o h1.xtn
Sin embargo en realidad es un proceso que consta de 4 pasos:
- Preprocesado
- Compilaci贸n
- Ensamblado
- Enlazado
Pongamos como ejemplo un programa muy sencillo en C y lo guardamos en hello.c
.
#include <stdio.h>
#define NUM 0
int main(void) {
printf("hello world\n");
return NUM;
}
C es uno de los lenguajes de alto nivel m谩s antiguos y por convenci贸n se ponen nombran con la extensi贸n
.c
, sin embargo, no es necesario nombrarlo as铆.
Preprocesado
cpp hello.c > hello.i
El preprocesador busca comandos que inicien con #
, estas se conocen como directivas del preprocesador, luego los ejecuta realizando las siguientes acciones:
- Quitar los comentarios
-
Expandir las macros
En este caso,
return NUM;
pasar铆a a serreturn 0;
-
Expandir los archivos de cabecera (incluidos)
#include <stdio.h>
El preprocesador busca en el sistema los archivos de cabecera.
stdio.h
por lo general est谩n en/usr/include/
para C y expandir谩 el archivo en ese lugar.Esto porque es un archivo est谩ndar, pero en realidad el preprocesador comenzar铆a buscando en el directorio actual, luego en los directorios especificados como configuraci贸n manual y finalmente en los directorios est谩ndar.
Para este ejemplo se incluy贸
stdio.h
, porque es donde se encuentraprintf
. De hecho inspeccionandohello.i
ostdio.h
, se encuentra la declaraci贸n:extern int printf (const char *__restrict __format, ...);
Sin embargo, como podemos darnos cuenta por
extern
, es una funci贸n que est谩 definida en un archivo externo. Es por eso que los archivos de cabecera NO son librer铆as, las librer铆as son aquellas donde se implementan dichas funciones.Ahora bien, si insertamos ese prototipo directamente en
hello.c
de la siguiente forma://#include <stdio.h> extern int printf (const char *__restrict __format, ...); #define NUM 0 int main(void) { printf("hello world\n"); return NUM; }
Al realizar el preprocesado, el archivo queda mucho m谩s corto. Esto tiene la ventaja de que hace el programa m谩s portable, es decir menos pesado. Pero por otro lado el programa principal se hace menos legible a medida que se van agregando muchas de estas declaraciones y por lo tanto se hace m谩s dif铆cil de mantener.
Compilaci贸n
gcc -Wall -S hello.i -o hello.s
La opci贸n
-Wall
S贸lo est谩 para imprimir todos los Warnings, pero no es necesario
En esta etapa se toma la salida del preprocesador y se genera lenguaje ensamblador, que es la versi贸n a煤n legible para el humano del lenguaje m谩quina.
Podr铆a decirse que es la etapa principal de el compilador, traduce el c贸digo c a lenguaje ensamblador, pero tambi茅n busca la optimization y tambi茅n hacer m谩s seguro el c贸digo.
Ensamblado
as hello.s -o hello.o
En esta etapa se traduce del lenguaje ensamblador a lenguaje m谩quina, tambi茅n se conoce como c贸digo objeto.
Este archivo es completamente ilegible pero se puede obtener informaci贸n de 茅l por medio del comando objdump
, por ejemplo, objdump -h
nos da informaci贸n de las cabeceras del archivo y objdump -d
desensambla el archivo, que sin embargo es igual que el hello.s
.
Con el comando nm
se listan los s铆mbolos del archivo objeto. Para el caso de este ejemplo se obtiene la siguiente salida:
U _GLOBAL_OFFSET_TABLE_
0000000000000000 T main
U puts
Las U
(undefined) significan que est谩n definidas en otro lugar, mientras que la T
indica que main
est谩 definido dentro de la secci贸n de c贸digo del archivo.
Enlazado (linking)
gcc hello.o -o h1.xtn
Esta etapa junta todos los c贸digos objeto necesarios y los junta en uno s贸lo. As铆 los que se marcan con una U
en la tabla de s铆mbolos, va a enlazar el c贸digo o copiarlo directamente dependiendo si se hace un enlazado din谩mico o est谩tico.