gcc utilise les termes «architecture» pour désigner le «jeu d'instructions» d'un processeur spécifique, et «cible» couvre la combinaison du processeur et de l'architecture, ainsi que d'autres variables telles que ABI, libc, endian-ness et plus (incluant éventuellement le "métal nu"). Un compilateur typique a un ensemble limité de combinaisons cibles (probablement un ABI, une famille de CPU, mais peut-être à la fois 32 et 64 bits). Un compilateur croisé signifie généralement soit un compilateur avec une cible autre que le système sur lequel il s'exécute, soit un compilateur avec plusieurs cibles ou ABI (voir aussi ceci ).
Les binaires sont-ils portables sur différentes architectures CPU?
En général, non. Un binaire en termes conventionnels est un code objet natif pour un CPU ou une famille de CPU particulier. Mais, il existe plusieurs cas où ils peuvent être modérément à hautement portables:
- une architecture est un surensemble d'une autre (généralement les binaires x86 ciblent i386 ou i686 plutôt que le dernier et le meilleur x86, par exemple
-march=core2
)
- une architecture fournit une émulation ou une traduction native d'une autre (vous avez peut-être entendu parler de Crusoe ), ou fournit des co-processeurs compatibles (par exemple PS2 )
- le système d'exploitation et le support d'exécution prennent en charge plusieurs architectures (par exemple, la possibilité d'exécuter des binaires x86 32 bits sur x86_64), ou de rendre la machine virtuelle / JIT transparente (Android utilisant Dalvik ou ART )
- il existe un support pour les binaires "fat" qui contiennent essentiellement du code en double pour chaque architecture prise en charge
Si vous parvenez d'une manière ou d'une autre à résoudre ce problème, l' autre problème binaire portable des innombrables versions de bibliothèque (glibc que je vous regarde) se présentera alors. (La plupart des systèmes embarqués vous évitent au moins ce problème particulier.)
Si vous ne l'avez pas déjà fait, c'est le bon moment pour courir gcc -dumpspecs
et gcc --target-help
voir ce que vous affrontez.
Les gros fichiers binaires ont divers inconvénients , mais ont encore des utilisations potentielles ( EFI ).
Il y a cependant deux autres considérations qui manquent dans les autres réponses: ELF et l'interpréteur ELF, et le support du noyau Linux pour les formats binaires arbitraires . Je n'entrerai pas dans les détails sur les binaires ou le bytecode pour les processeurs non réels ici, bien qu'il soit possible de les traiter comme "natifs" et d'exécuter des binaires de bytecode Java ou Python compilés , ces binaires sont indépendants de l'architecture matérielle (mais dépendent à la place sur la version de VM appropriée, qui exécute finalement un binaire natif).
Tout système Linux contemporain utilisera des binaires ELF (détails techniques dans ce PDF ), dans le cas des binaires ELF dynamiques, le noyau est chargé de charger l'image en mémoire mais c'est le travail de `` l'interpréteur '' défini dans l'ELF en-têtes pour faire le gros du travail. Normalement, cela implique de s'assurer que toutes les bibliothèques dynamiques dépendantes sont disponibles (à l'aide de la section `` Dynamique '' qui répertorie les bibliothèques et certaines autres structures qui répertorient les symboles requis) - mais il s'agit presque d' une couche d'indirection générale.
$ file /bin/ls
/bin/ls: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses \
shared libs), stripped
$ readelf -p .interp /bin/ls
String dump of section '.interp':
[ 0] /lib/ld-linux.so.2
( /lib/ld-linux.so.2
est également un binaire ELF, il n'a pas d'interprète et est un code binaire natif.)
Le problème avec ELF est que l'en-tête dans le binaire ( readelf -h /bin/ls
) le marque pour une architecture spécifique, classe (32 ou 64 bits), endian-ness et ABI (les gros binaires "universels" d'Apple utilisent un autre format binaire Mach-O au lieu de cela, qui résout ce problème, il est originaire de NextSTEP). Cela signifie qu'un exécutable ELF doit correspondre au système sur lequel il doit être exécuté. Une trappe d'échappement est l'interpréteur, il peut s'agir de n'importe quel exécutable (y compris celui qui extrait ou mappe des sous-sections spécifiques à l'architecture du binaire d'origine et les appelle), mais vous êtes toujours limité par le ou les types d'ELF que votre système autorisera à exécuter . (FreeBSD a une manière intéressante de gérer les fichiers Linux ELF , il brandelf
modifie le champ ELF ABI.)
Il existe (en utilisant binfmt_misc
) la prise en charge de Mach-O sur Linux , il y a un exemple qui vous montre comment créer et exécuter un gros binaire (32 et 64 bits). Les fourchettes de ressources / ADS , comme cela a été fait à l'origine sur le Mac, pourraient être une solution de contournement, mais aucun système de fichiers Linux natif ne prend en charge cela.
Plus ou moins la même chose s'applique aux modules du noyau, les .ko
fichiers sont également ELF (bien qu'ils n'aient pas d'interpréteur). Dans ce cas, il y a une couche supplémentaire qui utilise la version du noyau ( uname -r
) dans le chemin de recherche, quelque chose qui pourrait théoriquement être fait à la place dans ELF avec le versioning, mais à une certaine complexité et peu de gain, je suppose.
Comme indiqué ailleurs, Linux ne prend pas en charge nativement les binaires fat, mais il existe un projet fat-binary actif: FatELF . Il existe depuis des années , il n'a jamais été intégré au noyau standard en partie à cause de problèmes de brevets (maintenant expirés). À l'heure actuelle, il nécessite à la fois la prise en charge du noyau et de la chaîne d'outils. Il n'utilise pas l' binfmt_misc
approche, cela contourne les problèmes d'en-tête ELF et permet également d'utiliser des modules de noyau lourds.
- Si j'ai une application compilée pour s'exécuter sur une «cible x86, linux OS version xyz», puis-je simplement exécuter le même binaire compilé sur un autre système «ARM target, linux OS version xyz»?
Pas avec ELF, ça ne vous laissera pas faire ça.
- Si ce n'est pas vrai, le seul moyen est d'obtenir le code source de l'application pour reconstruire / recompiler en utilisant la chaîne d'outils appropriée 'par exemple, arm-linux-gnueabi'?
La réponse simple est oui. (Les réponses compliquées incluent l'émulation, les représentations intermédiaires, les traducteurs et JIT; à l'exception du cas de "rétrogradation" d'un binaire i686 pour utiliser uniquement les opcodes i386, ils ne sont probablement pas intéressants ici, et les corrections ABI sont potentiellement aussi difficiles que la traduction de code natif. )
- De même, si j'ai un module de noyau chargeable (pilote de périphérique) qui fonctionne sur une «cible x86, linux OS version xyz», puis-je simplement charger / utiliser le même .ko compilé sur un autre système «cible ARM, linux OS version xyz» ?
Non, ELF ne vous laissera pas faire ça.
- Si ce n'est pas vrai, le seul moyen est d'obtenir le code source du pilote pour reconstruire / recompiler en utilisant la chaîne d'outils appropriée 'par exemple, arm-linux-gnueabi'?
La réponse simple est oui. Je crois que FatELF vous permet de créer un .ko
multi-architecture, mais à un moment donné, une version binaire pour chaque architecture prise en charge doit être créée. Les choses qui nécessitent des modules du noyau sont souvent fournies avec la source et sont construites selon les besoins, par exemple VirtualBox le fait.
C'est déjà une longue réponse décousue, il n'y a qu'un détour de plus. Le noyau a déjà une machine virtuelle intégrée, bien que dédiée: la machine virtuelle BPF qui est utilisée pour faire correspondre les paquets. Le filtre lisible par l'homme "hôte foo et non port 22") est compilé en un bytecode et le filtre de paquets du noyau l' exécute . Le nouvel eBPF n'est pas seulement pour les paquets, en théorie que le code VM est portable sur n'importe quel linux contemporain, et llvm le supporte mais pour des raisons de sécurité, il ne sera probablement pas adapté à autre chose que des règles administratives.
Maintenant, en fonction de votre générosité avec la définition d'un exécutable binaire, vous pouvez (ab) utiliser binfmt_misc
pour implémenter un support binaire gras avec un script shell et des fichiers ZIP comme format de conteneur:
#!/bin/bash
name=$1
prog=${1/*\//} # basename
prog=${prog/.woz/} # remove extension
root=/mnt/tmpfs
root=$(TMPDIR= mktemp -d -p ${root} woz.XXXXXX)
shift # drop argv[0], keep other args
arch=$(uname -m) # i686
uname_s=$(uname -s) # Linux
glibc=$(getconf GNU_LIBC_VERSION) # glibc 2.17
glibc=${glibc// /-} # s/ /-/g
# test that "foo.woz" can unzip, and test "foo" is executable
unzip -tqq "$1" && {
unzip -q -o -j -d ${root} "$1" "${arch}/${uname_s}/${glibc}/*"
test -x ${root}/$prog && (
export LD_LIBRARY_PATH="${root}:${LD_LIBRARY_PATH}"
#readlink -f "${root}/${prog}" # for the curious
exec -a "${name}" "${root}/${prog}" "$@"
)
rc=$?
#rm -rf -- "${root}/${prog}" # for the brave
exit $rc
}
Appelez cela "wozbin", et configurez-le avec quelque chose comme:
mount binfmt_misc -t binfmt_misc /proc/sys/fs/binfmt_misc
printf ":%s:%s:%s:%s:%s:%s:%s" \
"woz" "E" "" "woz" "" "/path/to/wozbin" "" > /proc/sys/fs/binfmt_misc/register
Cela enregistre les .woz
fichiers avec le noyau, le wozbin
script est appelé à la place avec son premier argument défini sur le chemin d'un .woz
fichier appelé .
Pour obtenir un .woz
fichier portable (gras) , créez simplement un test.woz
fichier ZIP avec une hiérarchie de répertoires afin:
i686/
\- Linux/
\- glibc-2.12/
armv6l/
\- Linux/
\- glibc-2.17/
Dans chaque répertoire arch / OS / libc (un choix arbitraire), placez le test
binaire spécifique à l'architecture et les composants tels que les .so
fichiers. Lorsque vous l'appelez, le sous-répertoire requis est extrait dans un système de fichiers en mémoire tmpfs ( /mnt/tmpfs
ici) et appelé.