Este artículo explica cómo correr una aplicación de 32 bit (Intel i386) en un sistema Debian de 64 bit (Intel x86-64, o "amd64" según la nomenclatura que utiliza Debian).

No es mi intención ahondar técnica y teóricamente en conceptos de compilación, enlazado y librerías dinámicas en este artículo. Sin embargo quisiera explicar someramente por qué no es posible ejecutar directamente un binario compilado para i386 en un sistema amd64 o x86-64.

El proceso de compilación de software consiste en traducir un programa, aplicación o librería, desde un lenguaje de programación a código máquina. Es decir a instrucciones que serán ejecutadas directamente por el procesador. Ahora bien, cada procesador (CPU) tiene diferentes características y soporta un conjunto específico de instrucciones. Esto es lo que define a la arquitectura. Algunas de las arquitecturas de CPU más populares actualmente son i386 (Intel 32 bit), x86-64 (Intel/AMD 64 bit), ARM y sus variantes, Power (IBM), MIPS, SPARC (Sun), Itanium (Intel) y otras.

El compilador (software que se encarga de traducir un programa desde un lenguaje de programación a código máquina) traduce el programa a instrucciones en una arquitectura específica. Una vez compilado el programa podrá ser ejecutado sólo en procesadores que soporten dicha arquitectura. Aunque para el caso de las arquitecturas amd64 o x86-64 (AMD e Intel de 64 bit), éstas permiten ejecutar instrucciones pertenecientes al set de instrucciones i386 (Intel de 32 bit) nativamente. Es por ello que, en general, es posible ejecutar un binario de 32 bit en un sistema Debian amd64. En este punto cabe destacar que el sistema operativo (en este ejemplo Debian) es una pieza de software más, y como tal es compilado para una arquitectura especifica. Es por ello que se dispone de diferentes arquitecturas al momento de descargar una imagen.

A fin de ejecutar un binario de 32 bit en un sistema de 64 bit, parece suficiente con que el hardware soporte la arquitectura. Sin embargo esto no es así. Todo programa que se precie de hacer algún trabajo útil requerirá alguna librería compartida. De mínima la libc, librería que provee el funcionamiento básico para todo programa escrito en lenguaje C.

Una librería o biblioteca de código es un módulo que provee un conjunto específico de funciones, procesos y tipos de datos para ser utilizados por cualquier programa que la incluya. Un programa puede hacer uso de librerías enlazadas estáticamente (librerías que pasan a formar parte del programa una vez compilado y enlazado), o librerías enlazadas dinámicamente (librerías que se enlazad al momento de ejecutar el programa). El enlazado dinámico tiene como objeto evitar la duplicación de código. Es decir permitir que una misma librería sea utilizada por muchos programas al mismo tiempo, sea efectivamente compartida por todos. De aquí el nombre shared library. Lógicamente existen librerías que implementan funciones básicas, las cuales son utilizadas por casi todos los programas en ejecución.

Aquí es donde surgen los problemas. Un binario compilado para i386 puede correr en un procesador amd64, pero requiere contar con las librerías compiladas para 32 bit, ya que no es posible enlazar dinámicamente un binario de 32 bit con una librería de 64 bit. Simplemente no son compatibles.

La solución entonces consiste en contar con todas las librerías necesarias, disponibles en sus versiones para 32 y 64 bit (tampoco es posible reemplazar las librerías de 64 bit por las de 32 bit porque romperíamos el resto del sistema). Este concepto se conoce como "multilib", tener cada librería en sus versiones para 32 y 64 bit.

En Debian y derivados, cada compilador para cada lenguaje de programación disponible cuenta con su respectivo paquete con el sufijo "-multilib", los cuales se encargan de soportar este mecanismo:

root@linuxito:/appv2# apt-cache search multilib
g++-4.4-multilib - GNU C++ compiler (multilib files)
gcc-4.4-multilib - GNU C compiler (multilib files)
gfortran-4.4-multilib - GNU Fortran 95 compiler (multilib files)
g++-4.6-multilib - GNU C++ compiler (multilib files)
gcc-4.6-multilib - GNU C compiler (multilib files)
gfortran-4.6-multilib - GNU Fortran compiler (multilib files)
gobjc++-4.6-multilib - GNU Objective-C++ compiler (multilib files)
gobjc-4.6-multilib - GNU Objective-C compiler (multilib files)
g++-4.7-multilib - GNU C++ compiler (multilib files)
gcc-4.7-multilib - GNU C compiler (multilib files)
gccgo-4.7-multilib - GNU Go compiler (multilib files)
gfortran-4.7-multilib - GNU Fortran compiler (multilib files)
gobjc++-4.7-multilib - GNU Objective-C++ compiler (multilib files)
gobjc-4.7-multilib - GNU Objective-C compiler (multilib files)
g++-multilib - GNU C++ compiler (multilib files)
gcc-multilib - GNU C compiler (multilib files)
gccgo-multilib - Go compiler, based on the GCC backend (multilib files)
gfortran-multilib - GNU Fortran 95 compiler (multilib files)
gobjc++-multilib - GNU Objective-C++ compiler (multilib files)
gobjc-multilib - GNU Objective-C compiler (multilib files)

Veamos un ejemplo

El sistema operativo corresponde a la arquitectura x86_64 (Intel 64 bit):

root@linuxito:/appv2# uname -m
x86_64

En dicho sistema he instalado un binario (myapp) de 32 bit:

root@linuxito:/appv2# file myapp
myapp: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.18, not stripped

Según la salida de file, se trata de un binario que utiliza librerías compartidas enlazadas dinámicamente. Veamos con ldd qué librerías enlaza dicho programa:

root@linuxito:/appv2# ldd myapp
        not a dynamic executable

Según ldd no es un ejecutable, a pesar de que file diga lo contrario. Esto se debe a que el binario tiene el formato ELF de 32 bit, pero el sistema es de 64 bit. Para solucionar este inconveniente basta con instalar las librerías compartidas de 32 bit del compilador gcc (GNU C Compiler):

# apt-get install gcc-multilib

Habiendo instalado este paquete, es posible verificar con ldd qué librerías compartidas enlaza dinámicamente y ejecutarlo en el sistema de 64 bit:

root@linuxito:/appv2# ldd myapp
        linux-gate.so.1 =>  (0xf76e2000)
        libc.so.6 => /lib32/libc.so.6 (0xf7570000)
        /lib/ld-linux.so.2 (0xf76e3000)

Finalmente cabe destacar que, si el programa requiere utilizar librerías compartidas no provistas por los paquetes con el sufijo "-multilib", siempre es posible traerlas desde un sistema de 32 bit (respetando las dependencias y utilizando un directorio específico para librerías de 32 bit). A tal fin es necesario entender cómo funcionan las rutas donde se encuentran las librerías y las variables de entorno PATH y LD_LIBRARY_PATH. Temas que abordaré con mayor detenimiento en artículos posteriores.

Para más información sobre el mecanismo multilib y multiarch, revisar con detenimiento la documentación en la Wiki de Debian:

Para entender mejor los mecanismos de compilación, enlazado, carga y formatos de ejecutables, existe un interesante artículo que cubre en detalle todos estos aspectos: The compiler, assembler, linker, loader and process address space tutorial.


Tal vez pueda interesarte


Compartí este artículo