Un exécutable Linux compilé sur un «type» de Linux peut-il fonctionner sur un autre?

59

L'exécutable d'un petit programme extrêmement simple, tel que celui présenté ci-dessous, compilé sur un type de Linux s'exécutera-t-il sur un autre type? Ou aurait-il besoin d'être recompilé?

L'architecture de la machine est-elle importante dans un cas comme celui-ci?

int main()
{
  return (99);
}
JCDeen
la source
2
Merci à tous pour des réponses remarquables! J'ai appris beaucoup plus que prévu. J'ai volontairement artificiellement simplifié le code afin qu'il repose sur le moins de bibliothèques possible; mais j'aurais vraiment dû le dire à l'avance. La plupart de mes codages C ++ sur diverses plates-formes impliquaient de développer avec un outil Microsoft tel que Visual Studio, puis de transférer le code sur un système * nix et de le recompiler.
JCDeen
4
Les nombreuses facettes et considérations exprimées ici m'ont émerveillé! J'espère sincèrement que je pourrais en choisir plusieurs comme LA réponse. Merci encore à tous! Cordialement.
JCDeen
2
Android est également un système d'exploitation basé sur Linux. Bonne chance, cependant, en utilisant n'importe quel code compilé glibclà-bas, ou vice versa. Certes, ce n'est pas tout à fait impossible .
undercat
2
Pour une compatibilité maximale d'un outil de ligne de commande, vous pouvez utiliser uClibc, musl ou dietlibc au lieu de glibc, et lier statiquement votre exécutable 32 bits ( gcc -m32 -static). Ce faisant, tout i386 ou amd64 Linux pourra exécuter le fichier exécutable.
Pts le
10
Vous devriez retourner 42 ! :)
Homunculus Reticulli le

Réponses:

49

Ça dépend. Quelque chose compilé pour IA-32 (Intel 32 bits) peut fonctionner sur amd64 car Linux sur Intel conserve la compatibilité descendante avec les applications 32 bits (avec un logiciel approprié installé). Voici votre codecompilation sur le système 32 bits RedHat 7.3 (circa 2002, version 2.96 gcc), puis le fichier binaire copié sur et exécuté sur un système Centos 7.4 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 à Centos 7.4 (essentiellement RedHat Enterprise Linux 7.4) reste dans la même famille de "distribution"; sa portabilité sera donc probablement meilleure que celle de passer d'une installation "Linux from scratch" aléatoire de 2002 à une autre distribution Linux aléatoire en 2018 .

Quelque chose compilé pour amd64 ne fonctionnerait pas avec les versions 32 bits de Linux uniquement (l'ancien matériel ne connaît pas le nouveau matériel). Ceci est également vrai pour les nouveaux logiciels compilés sur des systèmes modernes destinés à fonctionner sur des choses anciennes et anciennes, car les bibliothèques et même les appels système peuvent ne pas être portables en arrière, et peuvent donc nécessiter des astuces de compilation, ou l'obtention d'un ancien compilateur, etc. compiler sur l'ancien système. (C’est une bonne raison de conserver les machines virtuelles d’anciennes choses anciennes.)

L'architecture compte; amd64 (ou IA-32) est très différent d'ARM ou de MIPS, de sorte que le binaire de l'un de ceux-ci ne devrait pas s'exécuter sur un autre. Au niveau de l' Assemblée de la mainsection de votre code sur IA-32 par compiles gcc -S code.cà

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

qu’un système amd64 peut traiter (sur un système Linux - OpenBSD, en revanche, sur amd64 ne prend pas en charge les fichiers binaires 32 bits; la compatibilité avec les anciens systèmes d’archives donne une certaine marge de manoeuvre aux attaquants, par exemple CVE-2014-8866 et ses amis). En attendant, un système MIPS big-endian main compile à la place:

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

qui un processeur Intel n'aura aucune idée de quoi faire, et de même pour l'assemblage Intel sur MIPS.

Vous pouvez éventuellement utiliser QEMU ou un autre émulateur pour exécuter du code étranger (peut-être très, très lentement).

Pourtant! Votre code est très simple et aura donc moins de problèmes de portabilité qu'autre chose; les programmes utilisent généralement des bibliothèques qui ont changé au fil du temps (glibc, openssl, ...); pour ceux-là, il se peut que vous deviez également installer des versions plus anciennes de diverses bibliothèques (par exemple, RedHat place typiquement "compat" quelque part dans le nom du paquet)

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

ou éventuellement vous soucier des modifications ABI (Application Binary Interface) pour les anciennes choses qui utilisent glibc, ou plus récemment des modifications dues à C ++ 11 ou à d'autres versions C ++. On pourrait aussi compiler statique (en augmentant considérablement la taille du binaire sur le disque) pour éviter les problèmes de bibliothèque, bien que le comportement d’un ancien fichier binaire dépende du fait que l’ancienne distribution Linux compilait presque tout ce qui était dynamique (RedHat: oui) ou non. D’autre part, des éléments tels que les patchelfbinaires dynamiques de régénération (ELF, mais probablement pas de a.outformat) peuvent utiliser d’autres bibliothèques.

Pourtant! Pouvoir exécuter un programme est une chose et faire quelque chose d’utile avec elle en est une autre. Les anciens fichiers binaires Intel 32 bits peuvent poser des problèmes de sécurité s’ils dépendent d’une version d’OpenSSL comportant un problème de sécurité horrible et non rétroporté. Sinon, le programme risque de ne pas pouvoir négocier du tout avec les serveurs Web modernes (comme le les serveurs rejettent les anciens protocoles et les chiffrements de l'ancien programme), ou la version 1 du protocole SSH n'est plus prise en charge, ou ...

thrig
la source
14
Premier paragraphe: non, Intel l’appelle "Intel 64" (ces jours-ci, après avoir passé sous d’autres noms plus tôt). IA-64 fait référence à Itanium, pas à quoi que ce soit compatible x86.
Hobbs
1
@ Hobbs merci, j'ai remplacé ces références par amd64; Je laisserai la dénomination au département marketing d’Intel.
Thrig
3
pourquoi ne pas mentionner les liens statiques?
départ du
2
Ce ne sont pas seulement les ABI de bibliothèque qui changent, l'interface syscall du noyau est également étendue dans le temps. Notez le for GNU/Linux 2.6.32(ou tel) dans la sortie de file /usr/bin/ls.
Charles Duffy
1
@ Wilbert Il vous manque probablement cette explication qui faisait référence à Red Hat Linux , distinct de Red Hat Enterprise Linux.
Bob
67

En bref: Si vous prenez un fichier binaire compilé d'un hôte à un autre en utilisant la même architecture (ou une architecture compatible) , vous pourrez peut-être parfaitement le faire passer à une autre distribution . Cependant, au fur et à mesure que la complexité du code augmente, la probabilité d'être lié à une bibliothèque non installée; installé dans un autre endroit; ou installé à une version différente, augmente. Prenons par exemple votre code, pour lequel les lddrapports suivants dépendent des dépendances lors de la compilation avec gcc -o exit-test exit-test.cun hôte Ubuntu Linux (dérivé 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)

De toute évidence, cette binaire ne fonctionnera pas si je la remplace par un Mac ( ./exit-test: cannot execute binary file: Exec format error). Essayons de le déplacer dans une boîte RHEL:

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

Oh cher. Pourquoi cela pourrait-il être?

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

Même pour ce cas d'utilisation, le chariot élévateur a échoué en raison de l'absence de bibliothèques partagées.

Cependant, si je le compile avec gcc -static exit-test-static exit-test.c, le porter sur le système sans les bibliothèques fonctionne très bien. Bien sûr, au détriment de l'espace disque:

$ 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

Une autre solution viable consisterait à installer les bibliothèques requises sur le nouvel hôte.

Comme beaucoup de choses dans l'univers U & L, il s'agit d'un chat avec plusieurs peaux, dont deux sont décrites ci-dessus.

DopeGhoti
la source
4
En effet, j'ai oublié les binaires statiques. Certains fournisseurs adoptent des fichiers binaires statiques et certains auteurs de programmes malveillants afin d'optimiser la compatibilité binaire entre les versions Linux de la même architecture
Rui F Ribeiro
8
Chariot élévateur ...?
user253751
2
@immibis Je pense que cela signifie que vous copiez des données (l'exécutable) d'un environnement (distribution) vers un autre, où les données ne sont pas conçues pour l'environnement de destination.
Wjandrea
13
Malheureusement, votre exemple Linux est assez artificiel et illustre votre point de vue sur les architectures plutôt que sur les distributions: vous avez construit un binaire 32 bits sur Debian et avez essayé de l'exécuter sur RHEL 64 bits; ce sont des architectures différentes ... Les binaires de même architecture avec si peu de dépendances de bibliothèque peuvent être copiés sans problème.
Stephen Kitt
7
@MSalters Je ne dis pas que c'est déraisonnable, je dis que c'est un mauvais exemple compte tenu de l'argument que DopeGhoti essaie de faire (vous ne pouvez pas copier des fichiers binaires d'une distribution à une autre - ce qui est faux). Bien sûr, Linux 64 bits sur Intel prend également en charge l’exécution d’exécutables 32 bits, avec l’infrastructure appropriée. Un exemple valable dans ce cas serait de créer un amd64fichier binaire et de l'exécuter sur une autre amd64distribution, ou de créer un i386fichier binaire et de l'exécuter sur une autre i386distribution.
Stephen Kitt
25

Ajout aux excellentes réponses @thrig et @DopeGhoti: Les systèmes d’exploitation Unix ou similaires, y compris Linux, étaient traditionnellement toujours conçus et alignés davantage pour la portabilité du code source que pour les fichiers binaires.

Si vous n’avez rien de matériel spécifique ou une source simple, comme dans votre exemple, vous pouvez le déplacer sans problème entre pratiquement toutes les versions de Linux ou d’architecture en tant que code source, à condition que les packages de développement C soient installés sur les serveurs de destination , les bibliothèques nécessaires et les bibliothèques de développement correspondantes installées.

En ce qui concerne le portage de code plus avancé d'anciennes versions de Linux ou de programmes plus spécifiques, tels que des modules de noyau pour différentes versions du noyau, vous devrez peut-être adapter et modifier le code source afin de prendre en compte des bibliothèques / API / ABI obsolètes.

Rui F Ribeiro
la source
19

Par défaut , vous rencontrerez presque certainement des problèmes avec les bibliothèques externes. Certaines des réponses apportent plus de détails sur ces problèmes, je ne vais donc pas reproduire leur travail.

Cependant, vous pouvez compiler de nombreux programmes, même non triviaux, pour pouvoir être portables entre des systèmes Linux. La clé est la boîte à outils appelée Linux Standard Base . Le LSB est conçu pour créer uniquement ce type d'applications portables. Compilez une application pour LSB v5.0 et elle fonctionnera sur tout autre environnement Linux (de même architecture) implémentant LSB v5.0. Quelques distributions Linux sont compatibles LSB, et d'autres incluent les boîtes à outils / bibliothèques LSB en tant que paquet installable. Si vous construisez votre application à l'aide des outils LSB (comme l' lsbccencapsuleur pour gcc) et établissez un lien avec la version des bibliothèques LSB, vous créerez une application portable.

bta
la source
et avec qemuvous, vous pouvez même exécuter des programmes compilés pour différentes architectures (pas très performantes, mais vous pouvez les exécuter)
Jasen
1
Je n'étais même pas au courant de la boîte à outils Linux Standard Base, alors merci! J'ai commencé à travailler avec C / C ++ il y a très longtemps, une grande partie de l'information contenue dans ces réponses est nouvelle pour moi. Et très utile.
JCDeen
1
L'article de Wikipedia dit que Debian et Ubuntu n'implémentent pas LSB (et n'ont aucune intention de le faire.)
BlackJack
2
@ BlackJack: la distribution elle-même n'implémente pas 100% de celle-ci dans le système d'exploitation principal, mais vous pouvez installer des bibliothèques et des kits d'outils compatibles LSB en tant que packages facultatifs. J'ai utilisé Ubuntu (par exemple) pour créer des programmes compatibles LSB qui s'exécutaient ensuite sur Suse et Centos. Vous n'avez besoin apt-get installque de quelques paquets.
BTA
10

Peut être.

Les choses qui ont tendance à le casser incluent.

  1. Différentes architectures. De toute évidence, des architectures totalement différentes ne fonctionneront pas (à moins que vous n'ayez quelque chose comme le mode utilisateur qemu avec binfmt_misc mais ce n'est pas une configuration normale). Les fichiers binaires x86 peuvent fonctionner sur amd64, mais uniquement si les bibliothèques 32 bits requises sont disponibles.
  2. Versions de la bibliothèque. Si le survol est faux, il ne trouvera pas la bibliothèque du tout. Si la surversion est identique mais que le binaire est construit sur une version plus récente de la bibliothèque, le chargement peut échouer à cause de nouveaux symboles ou de nouvelles versions de symboles. En particulier, la glibc est un utilisateur intensif de la version de symboles, il est donc très probable que les binaires construits contre une nouvelle glibc échouent avec une ancienne glibc.

Si vous évitez d'utiliser des bibliothèques à changement rapide, évitez les changements d'architecture et utilisez la distribution la plus ancienne que vous souhaitez cibler, vous avez de bonnes chances de faire fonctionner un binaire sur plusieurs distributions.

plugwash
la source
4

En plus de certaines des choses mentionnées précédemment, il y a eu quelques changements dans le format de fichier exécutable. La plupart du temps, Linux utilise ELF, mais les anciennes versions utilisaient a.out ou COFF.

Le début d'un wikihole:

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

Il pourrait y avoir un moyen d’obtenir des versions plus anciennes pour utiliser des formats plus récents, mais personnellement, je ne me suis jamais penché sur la question.

Ben
la source
"plus vieux" est à ce stade très vieux maintenant.
Plugwash