gccは「アーキテクチャ」という用語を使用して特定のCPUの「命令セット」を意味し、「ターゲット」はCPUとアーキテクチャの組み合わせ、およびABI、libc、エンディアンなどのその他の変数を対象としています。 (おそらく「ベアメタル」を含む)。典型的なコンパイラーには、ターゲットの組み合わせの限られたセットがあります(おそらく1つのABI、1つのCPUファミリー、しかしおそらく32ビットと64ビットの両方)。通常、クロスコンパイラは、実行するシステム以外のターゲットを持つコンパイラ、または複数のターゲットまたはABIを持つコンパイラのいずれかを意味します(これも参照)。
バイナリは異なるCPUアーキテクチャ間で移植可能ですか?
一般的に、いいえ。従来の用語のバイナリは、特定のCPUまたはCPUファミリのネイティブオブジェクトコードです。ただし、中程度から非常に移植性の高い場合がいくつかあります。
- あるアーキテクチャは別のアーキテクチャのスーパーセットです(一般的に、x86バイナリは、最新かつ最高のx86ではなく、i386またはi686をターゲットにしています
-march=core2
)。
- あるアーキテクチャは、別のアーキテクチャのネイティブエミュレーションまたは変換(Crusoeを聞いたことがあるかもしれません)、または互換性のあるコプロセッサ(PS2など)を提供します。
- OSとランタイムがマルチアーキテクチャをサポートする(たとえば、x86_64で32ビットx86バイナリを実行する機能)、またはVM / JITをシームレスにする(DalvikまたはARTを使用するAndroid )
- サポートされている各アーキテクチャの重複コードを本質的に含む「脂肪」バイナリのサポートがあります
どうにかこの問題を解決できた場合、無数のライブラリバージョンの他の移植可能なバイナリの問題(私が見ているglibc)が現れます。(ほとんどの組み込みシステムは、少なくともその特定の問題からあなたを救います。)
まだ実行していない場合は、今すぐ実行してgcc -dumpspecs
、gcc --target-help
何に直面しているかを確認するのに最適なタイミングです。
ファットバイナリにはさまざまな欠点がありますが、それでも潜在的な用途(EFI)があります。
ただし、他の回答には2つの考慮事項がありません。ELFとELFインタープリター、および任意のバイナリ形式の Linuxカーネルサポートです。ここでは、非リアルプロセッサのバイナリまたはバイトコードについて詳しく説明しませんが、これらを「ネイティブ」として扱い、Javaまたはコンパイル済みPythonバイトコードバイナリを実行することは可能ですが、そのようなバイナリはハードウェアアーキテクチャに依存しません最終的にネイティブバイナリを実行する、関連するVMバージョンで)。
現代のLinuxシステムはELFバイナリ(このPDFの技術的詳細)を使用します。動的ELFバイナリの場合、カーネルはイメージをメモリにロードする役割を担いますが、それはELFで設定された「インタープリター」の仕事ですヘビーリフティングを行うヘッダー。通常、これには、すべての依存動的ライブラリが利用可能であることを確認することが含まれます(ライブラリおよび必要なシンボルをリストする他の構造をリストする「動的」セクションの助けを借りて)—これはほぼ汎用の間接レイヤーです。
$ 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
もELFバイナリであり、インタープリターを持たず、ネイティブバイナリコードです。)
ELFの問題は、バイナリ(readelf -h /bin/ls
)のヘッダーが特定のアーキテクチャ、クラス(32ビットまたは64ビット)、エンディアンネス、およびABI(Appleの「ユニバーサル」ファットバイナリが代替バイナリ形式Mach-Oを使用することをマークすることです。この問題を解決する代わりに、これはNextSTEPで発生しました)。つまり、ELF実行可能ファイルは、実行対象のシステムと一致する必要があります。エスケープハッチの1つはインタープリターです。これは任意の実行可能ファイル(元のバイナリのアーキテクチャ固有のサブセクションを抽出またはマップし、それらを呼び出すものを含む)になりますが、システムで実行できるELFのタイプに制約されます。 。(FreeBSDはの興味深い方法があるのLinux ELFファイルを処理し、そのbrandelf
ELF ABIのフィールドを変更します。)
LinuxでMach-Oを(を使用してbinfmt_misc
)サポートします。そこには、ファット(32ビットおよび64ビット)バイナリを作成して実行する方法を示す例があります。もともとMacで行われていたリソースフォーク/ ADSは回避策になる可能性がありますが、ネイティブLinuxファイルシステムはこれをサポートしていません。
カーネルモジュールに.ko
もほぼ同じことが当てはまりますが、ファイルもELFです(インタープリターセットはありません)。この場合uname -r
、検索パスでカーネルバージョン()を使用する追加のレイヤーがあります。これは、バージョニングを使用してELFで理論的に実行できるものですが、多少の複雑さと少しの向上が疑われます。
他の場所で述べたように、Linuxはファットバイナリをネイティブにサポートしていませんが、FatELFというアクティブなファットバイナリプロジェクトがあります。それは何年も前からありましたが、(現在期限切れの)特許の懸念のために、標準カーネルに統合されることはありませんでした。現時点では、カーネルとツールチェーンの両方のサポートが必要です。binfmt_misc
アプローチを使用しません。これにより、ELFヘッダーの問題が回避され、ファットカーネルモジュールも使用できるようになります。
- 「x86ターゲット、Linux OSバージョンxyz」で実行するようにコンパイルされたアプリケーションがある場合、別のシステム「ARMターゲット、Linux OSバージョンxyz」で同じコンパイル済みバイナリを実行できますか?
ELFではなく、これを行うことはできません。
- 上記が当てはまらない場合、唯一の方法は、関連するツールチェーン「たとえば、arm-linux-gnueabi」を使用してアプリケーションのソースコードを再構築/再コンパイルすることです?
簡単な答えはイエスです。(複雑な回答には、エミュレーション、中間表現、翻訳者、JITが含まれます.i686バイナリを「ダウングレード」してi386オペコードのみを使用する場合を除き、ここではおそらく興味深いものではなく、ABIフィックスアップはネイティブコードの翻訳と同じくらい難しいでしょう。 )
- 同様に、「x86ターゲット、Linux OSバージョンxyz」で動作するロード可能なカーネルモジュール(デバイスドライバー)がある場合、別のシステム「ARMターゲット、Linux OSバージョンxyz」で同じコンパイル済み.koをロード/使用できますか?
いいえ、ELFではこれを実行できません。
- 上記が当てはまらない場合、唯一の方法は、関連するツールチェーン「たとえば、arm-linux-gnueabi」を使用してドライバのソースコードを再構築/再コンパイルすることです?
簡単な答えはイエスです。FatELFを使用する.ko
とマルチアーキテクチャのビルドが可能になると思いますが、ある時点で、サポートされているすべてのアーキテクチャのバイナリバージョンを作成する必要があります。多くの場合、カーネルモジュールを必要とするものはソースに付属しており、必要に応じてビルドされます。たとえば、VirtualBoxはこれを行います。
これはすでに長いとりとめのない答えです。もう1つだけ迂回路があります。カーネルには専用の仮想マシンがすでに組み込まれています。パケットを照合するために使用されるBPF VMです。人間が判読できる「ホストfooでポート22ではない」フィルター)はバイトコードにコンパイルされ、カーネルパケットフィルターがそれを実行します。新しいeBPFはパケット用だけではありません。理論的には、VMコードは現代のLinux間で移植可能であり、llvmはそれをサポートしますが、セキュリティ上の理由から、おそらく管理ルール以外には適していません。
バイナリ実行可能ファイルの定義にどれだけ寛大であるかに応じbinfmt_misc
て、シェルスクリプトとコンテナ形式としてのZIPファイルを使用して、ファットバイナリサポートを(ab)use することができます。
#!/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
}
これを「wozbin」と呼び、次のように設定します。
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
これ.woz
により、ファイルがカーネルに登録されwozbin
、最初の引数が呼び出された.woz
ファイルのパスに設定されて、代わりにスクリプトが呼び出されます。
ポータブル(脂肪).woz
ファイルを取得するにtest.woz
は、ディレクトリ階層を持つZIPファイルを作成するだけです。
i686/
\- Linux/
\- glibc-2.12/
armv6l/
\- Linux/
\- glibc-2.17/
各arch / OS / libcディレクトリ(任意の選択)内に、アーキテクチャ固有のtest
バイナリと.so
ファイルなどのコンポーネントを配置します。起動すると、必要なサブディレクトリがtmpfsインメモリファイルシステム(/mnt/tmpfs
ここ)に抽出され、起動されます。