¿Se ejecutará un ejecutable de Linux compilado en un "sabor" de Linux en uno diferente?

59

¿El ejecutable de un programa pequeño y extremadamente simple, como el que se muestra a continuación, que se compila en una versión de Linux, se ejecutará en una versión diferente? ¿O necesitaría ser recompilado?

¿Importa la arquitectura de la máquina en un caso como este?

int main()
{
  return (99);
}
JCDeen
fuente
2
¡Gracias a todos por sus excelentes respuestas! Aprendí mucho más de lo que esperaba. Hice el código artificialmente simple a propósito para que dependiera de la menor cantidad de bibliotecas posible; pero realmente debería haberlo dicho por adelantado. La mayor parte de mi codificación C ++ en plataformas implicaba desarrollar con una herramienta de Microsoft como Visual Studio y luego transferir el código a un sistema * nix y recompilarlo.
JCDeen
44
¡Las muchas facetas y consideraciones expresadas aquí me han sorprendido! Sinceramente, deseo poder elegir varios como LA respuesta. Gracias de nuevo a todos! Sinceramente.
JCDeen
2
Android también es un sistema operativo basado en Linux. Sin embargo, la mejor de las suertes es ejecutar cualquier código compilado glibcallí, o viceversa. Por supuesto, no es del todo imposible .
undercat
2
Para obtener la máxima compatibilidad de una herramienta de línea de comandos, puede usar uClibc, musl o dietlibc en lugar de glibc, y vincular estáticamente su ejecutable de 32 bits ( gcc -m32 -static). Al hacerlo, cualquier Linux i386 o amd64 podrá ejecutar el ejecutable.
pts
10
¡Deberías regresar 42 ! :)
Homunculus Reticulli

Respuestas:

49

Depende. Algo compilado para IA-32 (Intel de 32 bits) puede ejecutarse en amd64 ya que Linux en Intel conserva la compatibilidad con las aplicaciones de 32 bits (con el software adecuado instalado). Aquí está codecompilado en el sistema RedHat 7.3 de 32 bits (circa 2002, gcc versión 2.96) y luego el binario copiado y ejecutado en un sistema Centos 7.4 de 64 bits (circa 2017):

-bash-4.2$ file code
code: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.2.5, not stripped
-bash-4.2$ ./code
-bash: ./code: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
-bash-4.2$ sudo yum -y install glibc.i686
...
-bash-4.2$ ./code ; echo $?
99

Ancient RedHat 7.3 a Centos 7.4 (esencialmente RedHat Enterprise Linux 7.4) se mantiene en la misma familia de "distribución", por lo que probablemente tendrá una mejor portabilidad que pasar de una instalación aleatoria "Linux desde cero" de 2002 a otra distribución aleatoria de Linux en 2018 .

Algo compilado para amd64 no se ejecutaría en versiones de Linux de solo 32 bits (el hardware antiguo no conoce el hardware nuevo). Esto también es cierto para el nuevo software compilado en sistemas modernos destinados a ejecutarse en cosas antiguas y antiguas, ya que las bibliotecas e incluso las llamadas al sistema pueden no ser portátiles hacia atrás, por lo que pueden requerir trucos de compilación u obtener un compilador antiguo, etc. compilando en el viejo sistema. (Esta es una buena razón para mantener las máquinas virtuales de cosas antiguas antiguas).

La arquitectura sí importa; amd64 (o IA-32) es muy diferente de ARM o MIPS, por lo que no se esperaría que el binario de uno de ellos se ejecute en otro. En el nivel de ensamblaje, la mainsección de su código en IA-32 se compila a través gcc -S code.cde

main:
    pushl %ebp
    movl %esp,%ebp
    movl $99,%eax
    popl %ebp
    ret

que un sistema amd64 puede manejar (en un sistema Linux, OpenBSD, por el contrario, en amd64 no admite binarios de 32 bits; la compatibilidad con versiones anteriores con arcos antiguos les da a los atacantes margen de maniobra, por ejemplo, CVE-2014-8866 y amigos). Mientras tanto, en un sistema MIPS big-endian en su main lugar se compila para:

main:
        .frame  $fp,8,$31
        .mask   0x40000000,-4
        .fmask  0x00000000,0
        .set    noreorder
        .set    nomacro
        addiu   $sp,$sp,-8
        sw      $fp,4($sp)
        move    $fp,$sp
        li      $2,99
        move    $sp,$fp
        lw      $fp,4($sp)
        addiu   $sp,$sp,8
        j       $31
        nop

con el que un procesador Intel no tendrá idea de qué hacer, y tampoco para el ensamblaje Intel en MIPS.

Posiblemente podría usar QEMU o algún otro emulador para ejecutar código extranjero (quizás muy, muy lentamente).

¡Sin embargo! Su código es muy simple, por lo que tendrá menos problemas de portabilidad que cualquier otra cosa; los programas suelen utilizar bibliotecas que han cambiado con el tiempo (glibc, openssl, ...); para aquellos que también pueden necesitar instalar versiones anteriores de varias bibliotecas (RedHat, por ejemplo, generalmente coloca "compat" en algún lugar del nombre del paquete para tales)

compat-glibc.x86_64                     1:2.12-4.el7.centos

o posiblemente preocuparse por los cambios de ABI (Application Binary Interface) para cosas viejas que usan glibc, o cambios más recientes debido a C ++ 11 u otras versiones de C ++. También se podría compilar estática (aumentando enormemente el tamaño binario en el disco) para tratar de evitar problemas de biblioteca, aunque si algún binario antiguo hizo esto depende de si la distribución Linux anterior estaba compilando casi todo lo dinámico (RedHat: sí) o no. Por otro lado, cosas como patchelfpueden reiniciar binarios dinámicos (ELF, pero probablemente no a.outformatear) para usar otras bibliotecas.

¡Sin embargo! Poder ejecutar un programa es una cosa, y hacer algo útil con él es otra. Los viejos binarios de Intel de 32 bits pueden tener problemas de seguridad si dependen de una versión de OpenSSL que tiene algún problema de seguridad horrible y no respaldado, o el programa puede no ser capaz de negociar con servidores web modernos (como los modernos los servidores rechazan los protocolos y cifrados antiguos del programa antiguo), o el protocolo SSH versión 1 ya no es compatible, o ...

thrig
fuente
14
re primer párrafo: no, Intel lo llama "Intel 64" (en estos días, después de pasar por otros nombres anteriormente). IA-64 se refiere a Itanium, no a nada compatible con x86.
hobbs
1
@hobbs gracias, he reemplazado esas referencias con amd64; Dejaré el nombre de las cosas al departamento de marketing de Intel.
thrig
3
¿Por qué no mencionar enlaces estáticos?
dcorking
2
No solo cambian las ABI de la biblioteca, sino que la interfaz syscall del kernel también se extiende con el tiempo. Tenga en cuenta el for GNU/Linux 2.6.32(o tal) en la salida de file /usr/bin/ls.
Charles Duffy
1
@Wilbert Probablemente te estés perdiendo de que el thrig se refería al Red Hat Linux que es distinto de Red Hat Enterprise Linux.
Bob
67

En resumen: si está llevando un binario compilado de un host a otro utilizando la misma arquitectura (o una arquitectura compatible) , puede estar perfectamente bien llevarlo a otra distribución . Sin embargo, a medida que aumenta la complejidad del código, la probabilidad de estar vinculado a una biblioteca que no está instalada; instalado en otra ubicación; o instalado en una versión diferente, aumenta. Tomando por ejemplo su código, para el cual lddinforma las siguientes dependencias cuando se compila gcc -o exit-test exit-test.cen un host Ubuntu Linux (derivado de Debian):

$ ldd exit-test
    linux-gate.so.1 =>  (0xb7748000)
    libc.so.6 => /lib/i386-linux-gnu/libc.so.6 (0xb757b000)
    /lib/ld-linux.so.2 (0x8005a000)

Obviamente este binario no se ejecutará si lo pateo a, por ejemplo, una Mac ( ./exit-test: cannot execute binary file: Exec format error). Intentemos moverlo a una caja RHEL:

$ ./exit-test
-bash: ./exit-test: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

Oh querido. ¿Por qué podría ser esto?

$ ls /lib/ld-l* # reference the `ldd` output above
ls: cannot access /lib/ld-l*: No such file or directory

Incluso para este caso de uso, el montacargas falló debido a la falta de bibliotecas compartidas.

Sin embargo, si lo compilo gcc -static exit-test-static exit-test.c, portarlo al sistema sin las bibliotecas funciona bien. A expensas, por supuesto, del espacio en disco:

$ ls -l ./exit-test{,-static}
-rwxr-xr-x  1 username  groupname    7312 Jan 29 14:18 ./exit-test
-rwxr-xr-x  1 username  groupname  728228 Jan 29 14:27 ./exit-test-static

Otra solución viable sería instalar las bibliotecas necesarias en el nuevo host.

Al igual que con muchas cosas en el universo U&L, este es un gato con muchas máscaras, dos de las cuales se describen anteriormente.

DopeGhoti
fuente
44
De hecho, me olvidé de los binarios estáticos. Algunos proveedores adoptan archivos binarios estáticos, y algunos autores de malware también para maximizar la compatibilidad binaria entre las versiones de
Rui F Ribeiro
8
Carretilla elevadora ...?
user253751
2
@immibis Creo que significa copiar datos (el ejecutable) de un entorno (distribución) a otro, donde los datos no están diseñados para el entorno de destino.
wjandrea
13
Desafortunadamente, su ejemplo de Linux es bastante artificial e ilustra su punto de vista sobre arquitecturas en lugar de distribuciones: construyó un binario de 32 bits en Debian e intentó ejecutarlo en RHEL de 64 bits; esas son arquitecturas diferentes ... Los binarios de la misma arquitectura con tan pocas dependencias de la biblioteca se pueden copiar perfectamente.
Stephen Kitt
77
@MSalters No estoy diciendo que no sea razonable, estoy diciendo que es un mal ejemplo dado el punto que DopeGhoti está tratando de hacer (no puedes copiar binarios de una distribución a otra, lo cual está mal). Por supuesto, Linux de 64 bits en Intel también admite la ejecución de ejecutables de 32 bits, con la infraestructura adecuada. Un ejemplo válido en este caso IMO sería construir un amd64binario y ejecutarlo en otra amd64distribución, o construir un i386binario y ejecutarlo en otra i386distribución.
Stephen Kitt
25

Además de las excelentes respuestas @thrig y @DopeGhoti: los sistemas operativos Unix o similares a Unix, incluido Linux, siempre se diseñaron y alinearon más tradicionalmente para la portabilidad del código fuente que los binarios.

Si no tiene nada específico de hardware o es una fuente simple como en su ejemplo, puede moverlo sin ningún problema entre casi cualquier versión de Linux o arquitectura como código fuente , siempre que los servidores de destino tengan instalados los paquetes de desarrollo C , las bibliotecas necesarias y las bibliotecas de desarrollo correspondientes instaladas.

En la medida en que transfiera código más avanzado de versiones anteriores de Linux distantes en el tiempo, o programas más específicos como módulos de kernel para diferentes versiones de kernel, es posible que deba adaptar y modificar el código fuente para tener en cuenta las bibliotecas / API / ABI en desuso.

Rui F Ribeiro
fuente
19

De forma predeterminada , es casi seguro que tenga problemas con las bibliotecas externas. Algunas de las otras respuestas entran en más detalles sobre esos problemas, por lo que no duplicaré su trabajo.

Usted puede , sin embargo, compila muchos programas - incluso los no triviales - para ser portable entre sistemas Linux. La clave es el kit de herramientas llamado Linux Standard Base . El LSB está diseñado para crear solo este tipo de aplicaciones portátiles. Compile una aplicación para LSB v5.0 y se ejecutará en cualquier otro entorno Linux (de la misma arquitectura) que implemente LSB v5.0. Algunas distribuciones de Linux son compatibles con LSB, y otras incluyen kits de herramientas / bibliotecas LSB como un paquete instalable. Si crea su aplicación utilizando las herramientas LSB (como el lsbcccontenedor para gcc) y se vincula a la versión LSB de las bibliotecas, creará una aplicación portátil.

bta
fuente
e qemuincluso puede ejecutar programas compilados para una arquitectura diferente (no es de alto rendimiento, pero puede ejecutarlos)
Jasen
1
Ni siquiera conocía el kit de herramientas de Linux Standard Base, ¡así que gracias! Empecé a trabajar con C / C ++ hace mucho tiempo, por lo que gran parte de la información en estas respuestas es nueva para mí. Y muy servicial.
JCDeen
1
El artículo de Wikipedia dice que Debian y Ubuntu no implementan LSB (y no tienen intención de hacerlo).
BlackJack
2
@ BlackJack: la distribución en sí misma no implementa el 100% como parte del sistema operativo principal, pero puede instalar bibliotecas y kits de herramientas compatibles con LSB como paquetes opcionales. He usado Ubuntu (por ejemplo) para construir programas compatibles con LSB que luego se ejecutaron en Suse y Centos, solo necesitas apt-get installun par de paquetes.
bta
10

Tal vez.

Las cosas que tienden a romperlo incluyen.

  1. Diferentes arquitecturas. Obviamente, arquitecturas totalmente diferentes no funcionarán (a menos que tenga algo como el modo de usuario qemu con binfmt_misc, pero esa no es una configuración normal). Los binarios x86 pueden funcionar en amd64 pero solo si las bibliotecas de 32 bits requeridas están disponibles.
  2. Versiones de la biblioteca. Si la versión es incorrecta, no encontrará la biblioteca en absoluto. Si la versión es la misma pero el binario está construido contra una versión más nueva de la biblioteca que la que está ejecutando, entonces puede fallar al cargar debido a nuevos símbolos o nuevas versiones de símbolos. En particular, glibc es un gran usuario de versiones de símbolos, por lo que es muy probable que los binarios construidos contra un glibc más nuevo fallen con un glibc más antiguo.

Si evita usar bibliotecas que cambian rápidamente, evite los cambios de arquitectura y construya sobre la distribución más antigua a la que desea apuntar, tiene una buena oportunidad de hacer que un binario funcione en muchas distribuciones.

lavado
fuente
4

Además de algunas de las cosas mencionadas anteriormente, ha habido algunos cambios en el formato de archivo ejecutable. En su mayor parte, Linux usa ELF, pero las versiones anteriores usaban a.out o COFF.

El comienzo de un wikihole:

https://en.wikipedia.org/wiki/Comparison_of_executable_file_formats

Puede haber una forma de obtener versiones anteriores para ejecutar formatos más nuevos, pero personalmente nunca lo he investigado.

Ben
fuente
"más viejo" es en este punto muy viejo ahora sin embargo.
lavado