Notas técnicas sobre las herramientas

Esta sección intenta explicar algunos de los razonamientos y detalles técnicos que hay detrás del sistema de construcción. No es esencial que entiendas todo esto inmediatamente. La mayor parte tendrá sentido cuando hayas hecho una construcción real. Eres libre de volver aquí en cualquier momento.

El principal objetivo del Capítulo 5 es proporcionar un entorno temporal sano al que podamos entrar con chroot y a partir del cual podamos generar una construcción limpia y libre de problemas del sistema LFS en el Capítulo 6. Por el camino intentaremos independizarnos todo lo posible del sistema anfitrión, y para eso construimos unas herramientas principales autocontenidas y autohospedadas. Debería tenerse en cuenta que el proceso de construcción ha sido diseñado de forma que se minimice el riesgo para los nuevos lectores y, al mismo tiempo, facilitar el máximo valor educacional. En otras palabras, se pueden usar técnicas mas avanzadas para construir el sistema.

Importante: Antes de continuar, deberías informarte del nombre de tu plataforma de trabajo, conocido con frecuencia como target triplet (triplete del objetivo). Para muchos el "target triplet" será, por ejemplo: i686-pc-linux-gnu. Una forma simple de determinar tu "target triplet" es ejecutar el guión config.guess que se incluye con las fuentes de muchos paquetes. Desempaqueta las fuentes de Binutils, ejecuta el guión: ./config.guess y anota el resultado.

Igualmente necesitarás saber el nombre del enlazador dinámico de tu plataforma, también conocido como cargador dinámico, que no debe confundirse con el enlazador estándar ld que es parte de Binutils. El enlazador dinámico lo suministra Glibc y su trabajo es encontrar y cargar las librerías compartidas necesarias para un programa, preparar el programa y ejecutarlo. Para la mayoría el nombre del enlazador dinámico será ld-linux.so.2. En plataformas menos conocidas puede ser ld.so.1 y en las nuevas plataformas de 64 bits puede que incluso sea algo totalmente diferente. Debes poder determinar el nombre del enlazador dinámico de tu plataforma mirando en el directorio /lib de tu sistema anfitrión. Un modo seguro es inspeccionar un binario cualquiera de tu sistema anfitrión ejecutando: 'readelf -l <nombre del binario> | grep interpreter' y anotar la salida. La referencia autorizada que cubre todas las plataformas está en el fichero shlib-versions en la raíz del árbol de las fuentes de Glibc.

Algunas claves técnicas sobre cómo funciona el método de construcción del Capítulo 5:

Se instala primero Binutils debido a que tanto GCC como Glibc realizan varias pruebas de capacidades sobre el ensamblador y el enlazador en sus respectivas fases ./configure para determinar qué características deben activarse o desactivarse. Esto es mas importante de lo que uno podría pensar. Un GCC o Glibc incorrectamente configurado puede provocar unas herramientas sutilmente rotas cuyo impacto podría no notarse hasta casi finalizada la construcción de una distribución completa. Por suerte, un fallo en el banco de pruebas normalmente nos avisará antes de perder demasiado tiempo.

Binutils instala su ensamblador y su enlazador en dos ubicaciones, /tools/bin y /tools/$TARGET_TRIPLET/bin. En realidad, las herramientas de una ubicación son enlaces duros a la otra. Un aspecto importante del enlazador es su orden de búsqueda de librerías. Puede obtenerse información detallada de ld pasándole la opción --verbose. Por ejemplo: 'ld --verbose | grep SEARCH' mostrará las rutas de búsqueda actuales y su orden. Puedes ver qué ficheros son realmente enlazados por ld compilando un programa simulado y pasándole la opción --verbose. Por ejemplo: 'gcc dummy.c -Wl,--verbose 2>&1 | grep succeeded' te mostrará todos los ficheros abiertos con éxito durante el enlazado.

El siguiente paquete instalado es GCC y durante su fase ./configure verás, por ejemplo:

checking what assembler to use... /tools/i686-pc-linux-gnu/bin/as
checking what linker to use... /tools/i686-pc-linux-gnu/bin/ld

comprobando qué ensamblador usar... /tools/i686-pc-linux-gnu/bin/as
comprobando qué enlazador usar... /tools/i686-pc-linux-gnu/bin/ld

Esto es importante por la razón mencionada antes. También demuestra que el guión configure de GCC no explora los directorios del $PATH para encontrar las herramientas a usar. Sin embargo, durante la operación real del propio gcc, no se utilizan necesariamente las mismas rutas de búsqueda. Puedes saber cual es el enlazador estándar que utilizará gcc ejecutando: 'gcc -print-prog-name=ld'. Puedes obtener información detallada a partir de gcc pasándole la opción -v mientras compilas un programa simulado. Por ejemplo: 'gcc -v dummy.c' te mostrará los detalles sobre las fases de preprocesamiento, compilación y ensamblado, incluidas las rutas de búsqueda de gcc y su orden.

A continuación se instala Glibc. Las consideraciones más importantes para la construcción de Glibc son el compilador, las herramientas de binarios y las cabeceras del núcleo. Normalmente el compilador no es problema, pues Glibc siempre utilizará el gcc que se encuentre en un directorio del $PATH. Las herramientas de binarios y las cabeceras del núcleo pueden ser algo mas problemáticas. Así que no nos arriesgaremos y haremos uso de las opciones disponibles de configure para forzar las opciones correctas. Después de ejecutar ./configure puedes revisar el contenido del fichero config.make en el directorio glibc-build para ver todos los detalles importantes. Encontrarás algunas cosas interesantes como el uso de CC="gcc -B/tools/bin/" para controlar qué herramientas de binarios son usadas, y también el uso de las opciones -nostdinc y -isystem para controlar la ruta de búsqueda de cabeceras del compilador. Estos detalles ayudan a resaltar un aspecto importante del paquete Glibc: es muy autosuficiente en cuanto a su maquinaria de construcción y generalmente no se apoya en las opciones por defecto de las herramientas.

Después de la instalación de Glibc, haremos algunos ajustes para asegurar que la búsqueda y el enlazado tengan lugar solamente dentro de nuestro directorio /tools. Instalaremos un ld ajustado, que tiene limitada su ruta de búsqueda interna a /tools/lib. Entonces retocaremos los ficheros specs de gcc para que apunten a nuestro nuevo enlazador dinámico en /tools/lib. Este último paso es vital para el proceso completo. Como se mencionó antes, dentro de cada ejecutable compartido ELF se fija la ruta a un enlazador dinámico. Puedes verificar esto mediante: 'readelf -l <nombre del binario> | grep interpreter'. Retocando los ficheros specs de gcc estaremos seguros de que todo binario compilado desde aquí hasta el final del Capítulo 5 usará nuestro nuevo enlazador dinámico en /tools/lib.

La necesidad de utilizar el nuevo enlazador dinámico es también la razón por la que aplicamos el parche Specs en el segundo paso de GCC. De no hacer esto los propios programas de GCC incluirían dentro suyo el nombre del enlazador dinámico del directorio /lib del sistema anfitrión, lo que arruinaría nuestro objetivo de librarnos del anfitrión.

Durante el segundo paso de Binutils podremos usar la opción --with-lib-path de configure para controlar la ruta de búsqueda de librerías de ld. A partir de este punto el corazón de las herramientas está autocontenido y autohospedado. El resto de los paquetes del Capítulo 5 se construirán todos contra la nueva Glibc en /tools como debe ser.

Tras entrar en el entorno chroot en el Capítulo 6, el primer gran paquete a instalar es Glibc, debido a su naturaleza autosuficiente. Una vez que esta Glibc se instale dentro de /usr, haremos un rápido cambio en las opciones por defecto de las herramientas y procederemos a la construcción real del sistema LFS en el Capítulo 6.

Notas sobre el enlazado estático

Muchos programas han de realizar, dentro de sus tareas específicas, muchas operaciones comunes y, en ocasiones, triviales. Esto incluye reservar memoria, explorar directorios, leer y escribir ficheros, manejar cadenas, emparejar patrones, cálculo y muchas otras tareas. En vez de obligar a cada programa a reinventar la rueda, el sistema GNU facilita todas estas funciones básicas dentro de librerías listas para usar. La principal librería en cualquier sistema Linux es Glibc.

Hay dos formas de enlazar las funciones de una librería en un programa que las utilice: estática o dinámicamente. Cuando un programa se enlaza estáticamente el código de las funciones usadas se incluye dentro del ejecutable, resultando un programa algo abultado. Cuando un programa se enlaza dinámicamente lo que se incluye es una referencia al enlazador dinámico, el nombre de la librería y el nombre de la función, resultando un ejecutable mucho más pequeño. (Un tercer método es utilizar la interfaz de programación del enlazador dinámico. Mira la página de manual de dlopen para obtener más información.)

En Linux se usa por defecto enlazado dinámico y tiene tres ventajas fundamentales sobre el enlazado estático. Primero, sólo necesitas en tu disco duro una copia del código de la librería ejecutable, en vez de tener muchas copias del mismo código dentro de un montón de programas, ahorrando espacio en disco. La segunda es que cuando varios programas usan la misma función de una librería al mismo tiempo sólo hace falta cargar una copia del código de la función, ahorrando espacio en memoria. Por último, cuando se corrige un error o se mejora una función de una librería sólo necesitas recompilar esta librería, en vez de tener que recompilar todos los programas que hacen uso de esta función mejorada.

Si el enlace dinámico tiene varias ventajas, ¿por qué enlazamos estáticamente los dos primeros paquetes en este capítulo? La razón es triple: histórica, educacional y técnica. Histórica porque las anteriores versiones de LFS enlazaban estáticamente cada paquete en este capítulo. Educacional porque conocer la diferencia es útil. Técnica porque ganamos un elemento de independencia sobre el anfitrión al hacerlo. Por ejemplo, estos programas pueden usarse independientemente del sistema anfitrión. Sin embargo, hay que mencionar que se puede conseguir una construcción correcta del sistema LFS al completo cuando los dos primeros paquetes se construyen dinámicamente.