fbpx Compilar con GCC en Linux: gcc -o, Flags y Múltiples Archivos | Certificação Linux

Cómo Compilar Programas en Linux con GCC: C, C++ y Makefile

📅 07/03/2019 ⏱ 12 min ✍️ Uira Ribeiro
Linuxcomandoscómo-hacer
Cómo Compilar Programas en Linux con GCC: C, C++ y Makefile
Exam Pass App — mais de 7.000 questões LPI, offline, em 5 idiomas, disponível no Android e na App Store
Disponible en
Google Play
Disponible en
App Store

Un compilador es un programa especial que procesa instrucciones escritas en un lenguaje de programación particular y las transforma en lenguaje o código de máquina.

Generalmente, un programador escribe instrucciones en un lenguaje de alto nivel como Pascal o C, usando un editor de textos. El archivo que se crea contiene lo que llamamos fuente del programa.

El programador luego ejecuta el compilador del lenguaje apropiado, especificando el nombre del archivo que contiene las instrucciones fuente.

Cuando se ejecuta, el compilador primero analiza todas las instrucciones del lenguaje sintácticamente una tras otra y luego, en uno o más estadios o "pasos" sucesivos, construye el código de salida.

Tradicionalmente, la salida de la compilación se llama código objeto o a veces módulo objeto. El código objeto es el código de máquina que el procesador puede ejecutar.

Tradicionalmente en algunos sistemas y lenguajes, un paso adicional es necesario después de la compilación.

Este paso está destinado a resolver la ubicación relativa de instrucciones y datos cuando más de un módulo objeto se ejecuta al mismo tiempo y tienen referencias cruzadas de uno a otro. Este proceso se conoce como vinculación.

El compilador más utilizado en Linux es la Colección de Compiladores GNU - GCC. Compila códigos C ANSI, así como C++, Java y Fortran. GCC soporta varios niveles de verificación de errores en los códigos fuente, produce información de depuración y puede además optimizar el archivo objeto producido.

La compilación implica hasta cuatro estadios: preprocesamiento, compilación propiamente dicha, ensamblaje y vinculación, siempre en este orden.

GCC es capaz de preprocesar y compilar varios archivos en uno o más archivos en ensamblador.

El archivo de ensamblador genera uno o más archivos llamados objeto y estos se vinculan a las bibliotecas (linking) para convertirse en un archivo ejecutable.

PREPROCESAMIENTO

El preprocesamiento es responsable de expandir las macros e incluir los archivos de encabezado en el archivo fuente. El resultado es un archivo que contiene el código fuente expandido.

COMPILACIÓN

El siguiente estadio llamado "compilación propiamente dicha" es responsable de traducir el código fuente preprocesado en lenguaje ensamblador (lenguaje de máquina) para un procesador específico.

ENSAMBLADOR

El estadio siguiente se llama ensamblador. En esta etapa GCC convierte el código de máquina de un procesador específico en un archivo objeto.

Si en este código existen llamadas externas de funciones, gcc deja sus direcciones indefinidas para ser rellenadas posteriormente por el estadio de vinculación.

ENLAZADOR

El último estadio llamado vinculación, o linker, es responsable de vincular los archivos objeto para crear un archivo ejecutable.

Lo hace rellenando las direcciones de las funciones indefinidas en los archivos objeto con las direcciones de las bibliotecas externas del sistema operativo.

Esto es necesario porque los archivos ejecutables necesitan muchas funciones externas del sistema y de bibliotecas de C para ejecutarse.

Las bibliotecas pueden vincularse al ejecutable rellenando las direcciones de las bibliotecas en las llamadas externas o de forma estática cuando las funciones de las bibliotecas se copian al ejecutable.

En el primer caso, el programa utilizará bibliotecas de forma compartida y dependerá de ellas para funcionar.

Este esquema ahorra recursos, porque una biblioteca utilizada por muchos programas necesita cargarse solo una vez en la memoria. El tamaño del ejecutable será pequeño.

Sin embargo, si la biblioteca instalada es de una versión diferente a la que el ejecutable necesita, el programa no se ejecutará hasta que se instale la versión apropiada de la biblioteca.

En el segundo caso, el programa es independiente, ya que las funciones que necesita están en su código.

Este esquema permite que cuando haya un cambio de versión de biblioteca el programa no sea afectado. La desventaja será el tamaño del ejecutable y necesidades de más recursos.

GCC - Colección de Compiladores GNU

GNU Compiler Collection - colección de compiladores GNU - son compiladores completos del lenguaje C ANSI con soporte para K&R C, C++, Objective C, Java y Fortran. GCC ofrece también diferentes niveles de verificación de errores del código fuente e información para depuración y optimizaciones del programa objeto.

También soporta la plataforma moderna de procesadores Intel IA-64. La versión 7.0 incluye nuevas APIs y bibliotecas C++. El manual también fue sustancialmente reescrito y mejorado.

Preprocesamiento

El preprocesamiento es responsable de expandir las macros e incluir los archivos de encabezado en el archivo fuente. El resultado es un archivo que contiene el código fuente expandido.

El preprocesamiento del simple ejemplo siguiente generará un archivo en C con más de 800 líneas de código expandido.

Vea cómo queda este simple programa estilo Hola Mundo que imprime Certificación Linux en la pantalla:

#include <stdio.h>

int main()

{

        printf("Certificación Linux!\n");

};

La opción "-E" de GCC le dice al compilador que haga solo el preprocesamiento del código fuente.

# gcc –E teste.c –o teste.i

Vea que el archivo preprocesado teste.i generó 841 líneas de código preprocesado:

# cat teste.i | wc

    841    2074   16879

Compilación en GCC

El siguiente estadio llamado "compilación propiamente dicha" es responsable de traducir el código fuente preprocesado en lenguaje ensamblador (lenguaje de máquina) para un procesador específico.

Para que vea el resultado del estadio de compilación, la opción "-S" (mayúscula) de GCC genera el código de máquina para un procesador específico.

El código siguiente es el ejemplo anterior ya preprocesado que fue compilado en ensamblador para el procesador Intel Xeon:

# gcc –S teste.i  

# cat teste.s

.file "teste.c"

.section .rodata

.LC0:

.string "Certifica\303\247\303\243o Linux!"

.text

.globl main

.type main, @function

main:

.LFB0:

.cfi_startproc

pushq %rbp

.cfi_def_cfa_offset 16

.cfi_offset 6, -16

movq %rsp, %rbp

.cfi_def_cfa_register 6

movl $.LC0, %edi

call puts

popq %rbp

.cfi_def_cfa 7, 8

ret

.cfi_endproc

.LFE0:

.size main, .-main

.ident "GCC: (GNU) 4.8.5 20150623 (Red Hat 4.8.5-28)"

.section .note.GNU-stack,"",@progbits

Ensamblador del Código en gcc

El estadio siguiente se llama ensamblador. En esta etapa GCC convierte el código de máquina de un procesador específico en un archivo objeto. Si en este código existen llamadas externas de funciones, gcc deja sus direcciones indefinidas para ser rellenadas posteriormente por el estadio de vinculación.

El siguiente comando hará la compilación del código ensamblador del ejemplo anterior al código de máquina:

# as teste.s -o teste.o

El comando as es un compilador ensamblador disponible en el paquete GNU GCC.

El resultado será un archivo "teste.o" que contiene instrucciones de máquina del ejemplo con la referencia indefinida para la función externa printf.

La línea "call printf" del código ensamblador informa que esta función está definida en una biblioteca y deberá ser llamada durante el procesamiento.

Enlazador del código con las bibliotecas

El último estadio llamado vinculación, o linker,  es responsable de vincular los archivos objeto para crear un archivo ejecutable.

Lo hace rellenando las direcciones de las funciones indefinidas en los archivos objeto con las direcciones de las bibliotecas externas del sistema operativo.

Esto es necesario porque los archivos ejecutables necesitan muchas funciones externas del sistema y de bibliotecas de C para ejecutarse.

Las bibliotecas pueden vincularse al ejecutable rellenando las direcciones de las bibliotecas en las llamadas externas o de forma estática cuando las funciones de las bibliotecas se copian al ejecutable.

En el primer caso, el programa utilizará bibliotecas de forma compartida y dependerá de ellas para funcionar.

Este esquema ahorra recursos, porque una biblioteca utilizada por muchos programas necesita cargarse solo una vez en la memoria. El tamaño del ejecutable será pequeño.

En el segundo caso, el programa es independiente, ya que las funciones que necesita están en su código.

Este esquema permite que cuando haya un cambio de versión de biblioteca el programa no sea afectado. La desventaja será el tamaño del ejecutable y necesidades de más recursos.

Internamente el estadio de vinculación es muy complejo, pero GCC lo hace de forma transparente a través del comando:

# gcc teste.o –o teste

El resultado será un ejecutable llamado teste.

# ./teste

Certificación Linux!

Este programa fue vinculado a las bibliotecas de forma compartida. El comando ldd informa cuáles son las bibliotecas vinculadas al ejecutable:

# ldd teste

linux-vdso.so.1 =>  (0x00007ffd69cff000)

libc.so.6 => /lib64/libc.so.6 (0x00007f8ee7991000)

/lib64/ld-linux-x86-64.so.2 (0x00007f8ee7d5e000)

Las bibliotecas libc.so.6 y ld-linux.so.2 son referenciadas dinámicamente por el programa teste.

La opción "-static" del compilador copia las funciones externas de las bibliotecas al ejecutable:

# gcc –static teste.c –o teste1

El comando ldd informará que el ejecutable teste1 no tiene vinculaciones dinámicas:

# ldd teste1

not a dynamic executable

La diferencia en el tamaño de los ejecutables es muy grande. El programa teste que utiliza vinculaciones dinámicas tiene 8.279 bytes.

El teste1 tiene 1.884.604 bytes. Por lo tanto, solo utilice la copia de las bibliotecas al ejecutable si es estrictamente necesario.

Compilando de la manera correcta

Una vez que el software ha sido descargado de Internet y extraído el contenido de su paquete, es hora de compilar el código fuente.

Es muy común que los desarrolladores al crear su software y distribuir su código fuente, incorporen dos archivos especiales que facilitan mucho la compilación de los programas: configure y Makefile.

configure

El "configure" es un script que el desarrollador crea para verificar si la máquina tiene todos los requisitos necesarios para la compilación del software. Esto generalmente implica verificar si existe el compilador necesario y si todas las bibliotecas necesarias están instaladas.

El configure también permite que el usuario habilite o deshabilite alguna función del software en el momento de la compilación.

Puede ver las opciones que el configure permite con la opción "--help":

$ ./configure --help

Una vez que configure se ejecuta, hace toda la verificación de programas, bibliotecas y dependencias para compilar el software.

$ ./configure

Creará un archivo especial llamado Makefile, que contiene las directivas de compilación del software. 

Si hubiera algún problema o falta de alguna dependencia, el configure alertará al usuario, para que esta dependencia sea satisfecha y entonces el configure pueda ser ejecutado nuevamente, hasta que el Makefile sea generado.

~/httpd-2.4.41$ ./configure checking for chosen layout... Apachechecking for working mkdir -p... yesconfigure: Configuring Apache Portable Runtime library...checking for APR... noconfigure: error: APR not found.  Please read the documentation.

Makefile

El Makefile es un archivo con la forma de un script, que contiene los comandos para compilar el software, personalizados para la máquina en cuestión, con las opciones que el configure habilitó o deshabilitó.

Para que el software sea compilado, es necesario la utilidad make para leer el contenido del Makefile y disparar el proceso de compilación del software.

make

La utilidad make es necesaria para compilar múltiples archivos de código fuente de un proyecto. Utiliza un archivo de descripción generalmente nombrado como Makefile. El contenido de este archivo contiene reglas que definen las dependencias entre archivos fuente y los comandos necesarios para la compilación.

A partir de este archivo de descripción crea secuencias de comandos que son interpretadas por el shell. Generalmente el compilador gcc es invocado con diversas opciones que completan las dependencias de otros archivos objetos y bibliotecas.

Incluso los proyectos de software más pequeños contienen varios archivos que tienen una interdependencia y el comando make y el Makefile facilitan mucho el proceso de compilar software.

Para compilar el software, simplemente escriba make en el directorio actual del proyecto de software:

$ make

De esta forma, make leerá el Makefile y hará todo el proceso de compilación del software.

Es posible que durante la compilación ocurran errores, principalmente de falta de bibliotecas o problemas en la versión de las bibliotecas, que no fueron previstos por el desarrollador al crear el configure. 

Una vez compilado el software, se puede usar la directiva "install" de make para instalar el software recién compilado en los directorios apropiados en Linux:

$ make install

Hecho esto, el software será debidamente instalado en el sistema.

Los programas que se construyen de esta forma generalmente fueron empaquetados usando un conjunto de programas referidos como autotools. Esta suite incluye autoconf, automake y muchos otros programas, todos ellos trabajan juntos para hacer que la vida de un mantenedor de software sea significativamente más fácil. El usuario final no ve estas herramientas, pero eliminan la dificultad de configurar un proceso de instalación que se ejecutará de forma consistente en diferentes distribuciones Linux.

Aprenda mucho más sobre Linux en nuestro curso en línea. Puede hacer la matrícula aquí. Si ya tiene una cuenta, o desea crear una, simplemente ingrese o cree su usuario aquí.

¿Le gustó? Comparta

Uira Ribeiro

Prof. Uirá Ribeiro

Chair do Board do Linux Professional Institute

Después de años viendo profesionales perder horas en tareas simples por no conocer las herramientas correctas de Linux, decidí enseñar.
Hoy soy autor de la mayoría de los Materiales de Aprendizaje oficiales de la LPI y mentor de más de 14,000 alumnos certificados.

Conoce mi historia completa →

Eso fue solo una pieza. El curso enseña el camino completo — desde tu primer comando hasta el día del examen.

Certificação Linux — a plataforma líder em certificações Linux no Brasil
WhatsApp