web-dev-qa-db-ja.com

Linuxの「フレーバー」でコンパイルされたLinux実行可能ファイルは、別のLinuxで実行されますか?

Linuxの1つのフレーバーでコンパイルされた、次に示すような非常にシンプルなプログラムの実行可能ファイルは、別のフレーバーで実行されますか?それとも再コンパイルする必要がありますか?

このような場合、マシンアーキテクチャは重要ですか?

int main()
{
  return (99);
}
60
JCDeen

場合によります。 IA-32(インテル32ビット)用にコンパイルされたものは、AMD64で実行できます。これは、Intel上のLinuxが(適切なソフトウェアがインストールされた)32ビットアプリケーションとの下位互換性を保持しているためです。 RedHat 7.3 32ビットシステム(2002年頃、gccバージョン2.96)でコンパイルしたcodeと、バイナリがCentos 7.4 64ビットシステムにコピーされて実行されます(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

古代RedHat 7.3からCentos 7.4(基本的にはRedHat Enterprise Linux 7.4)は同じ「ディストリビューション」ファミリーに留まっているため、2002年からいくつかのランダムな「ゼロからのLinux」インストールから2018年に他のランダムなLinuxディストリビューションに移行するよりも移植性が高い。

AMD64用にコンパイルされたものは、Linuxの32ビットのみのリリースでは動作しません(古いハードウェアは新しいハードウェアを認識しません)。ライブラリや システムコールでも は下位互換性がない可能性があるため、コンパイルのトリックや古いコンパイラの入手が必要になる可能性があるため、これは現代のシステムでコンパイルされた新しいソフトウェアにも当てはまります。など、またはおそらく代わりに古いシステムでコンパイルします。 (これは、古代の古いものの仮想マシンを維持するための正当な理由です。)

アーキテクチャは重要です。 AMD64(またはIA-32)はARMまたはMIPSとは大きく異なります。そのため、これらの1つからのバイナリが別のバイナリで実行されることは想定されていません。アセンブリレベルでは、main IA-32上のコードのセクションは、gcc -S code.cを介してコンパイルされ、

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

aMD64システムが処理できるもの(Linuxシステムでは、AMD64とは対照的にOpenBSDは32ビットバイナリをサポートしていません;古いアーキテクチャとの下位互換性 CVE-2014-8866 や友人など、攻撃者に余裕を与えます。一方、 ビッグエンディアンMIPSシステムmainでは、代わりに次のようにコンパイルされます。

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

intelプロセッサは何をどうすればいいのか分からず、MIPS上のIntelアセンブリについても同様です。

QEMUまたは他のエミュレーターを使用して外部コードを実行する可能性があります(おそらく非常に遅くなります)。

しかしながら!あなたのコードは非常に単純なコードなので、何よりも移植性の問題が少なくなります。プログラムは通常、時間とともに変化するライブラリ(glibc、opensslなど)を使用します。それらのために、さまざまなライブラリの古いバージョンをインストールする必要があるかもしれません(たとえば、RedHatは通常、そのようなパッケージ名のどこかに「compat」を置きます)

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

あるいは、glibcを使用する古い方法のABI変更(アプリケーションバイナリインターフェース)、または最近のC++ 11またはその他のC++リリースによる変更について心配する必要があります。古いバイナリがこれを実行したかどうかは、古いLinuxディストリビューションがほとんどすべてを動的にコンパイルしているか(RedHat:はい)かどうかに依存しますが、静的(ディスク上のバイナリサイズを大幅に増やす)をコンパイルしてライブラリの問題を回避することもできます。一方、patchelfのようなものは、動的(ELF、ただしおそらくa.out形式ではない)バイナリを再調整して他のライブラリを使用できます。

しかしながら!プログラムを実行できることと、実際にそれを使用して何か有用なことを行うことです。古い32ビットIntelバイナリは、恐ろしい、バックポートされていないセキュリティ問題があるOpenSSLのバージョンに依存している場合、またはプログラムが最新のWebサーバーとまったく交渉できない場合があります(最新のものとして)サーバーが古いプログラムの古いプロトコルと暗号を拒否する)、またはSSHプロトコルバージョン1がサポートされなくなった、または...

50
thrig

要するに、同じ(または互換性のある)architectureを使用して、あるホストから別のホストにコンパイル済みバイナリを取得している場合、別のホストに完全に問題なく実行できますdistribution 。ただし、コードの複雑さが増すにつれて、インストールされていないライブラリにリンクされる可能性があります。別の場所にインストールされています。または別のバージョンでインストールすると、増加します。 (Debian派生の)Ubuntu Linuxホスト上でgcc -o exit-test exit-test.cを使用してコンパイルすると、lddが次の依存関係を報告するコードを例にとります。

$ 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)

言うまでもなく、たとえばMac(./exit-test: cannot execute binary file: Exec format error)に引き継いだ場合、このバイナリは動作しません。 RHELボックスに移動してみましょう。

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

まあ。これはなぜでしょうか?

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

このユースケースでも、共有ライブラリがないため、フォークリフトは失敗しました。

ただし、gcc -static exit-test-static exit-test.cを使用してコンパイルすると、ライブラリなしでシステムに移植しても問題なく機能します。もちろんディスク容量を犠牲にして:

$ 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

別の実行可能なソリューションは、必要なライブラリを新しいホストにインストールすることです。

U&Lユニバースの多くのものと同様に、これは多くのスキンを持つ猫で、そのうち2つは上記で概説されています。

69
DopeGhoti

優れた@thrigおよび@DopeGhotiの回答に加えて:LinuxまたはLinuxを含むUnixまたはUnixのようなOSは、従来より常にバイナリよりソースコードの移植性のために設計および調整されていました。

あなたの例のようにハードウェア固有のものもシンプルなソースもない場合は、ほとんど問題なくany Linuxまたはアーキテクチャのバージョンからソースコードとして(限り宛先サーバーにはC開発パッケージがインストールされているため、必要なライブラリ、および対応する開発ライブラリがインストールされています。

以前のバージョンのLinuxからより高度なコードを移植するか、異なるカーネルバージョンのカーネルモジュールなどのより具体的なプログラムを移植する場合、非推奨のライブラリ/ API/ABIに対応するためにソースコードを適応および変更する必要がある場合があります。

25
Rui F Ribeiro

defaultを指定すると、ほぼ確実に外部ライブラリに関する問題が発生します。他の回答のいくつかはそれらの問題についてより詳細に説明しているので、私は彼らの仕事を複製しません。

あなたはできますがただし、多くのプログラムをコンパイルします-重要なものも含めて-Linuxシステム間で移植できるようにします。キーは Linux Standard Base と呼ばれるツールキットです。 [〜#〜] lsb [〜#〜] は、これらのタイプのポータブルアプリケーションのみを作成するために設計されています。 LSB v5.0のアプリケーションをコンパイルすると、LSB v5.0を実装する(同じアーキテクチャーの)他のLinux環境で実行されます。いくつかのLinuxディストリビューションはLSBに準拠しており、他のディストリビューションにはインストール可能なパッケージとしてLSBツールキット/ライブラリが含まれています。 LSBツール(lsbccgccラッパーなど)を使用してアプリケーションをビルドし、LSBバージョンのライブラリにリンクすると、移植可能なアプリケーションが作成されます。

20
bta

多分。

それを壊す傾向があるものは含まれています。

  1. 異なるアーキテクチャ。明らかに完全に異なるアーキテクチャは機能しません(binfmt_miscを使用したユーザーモードqemuのようなものがない限り、これは通常の構成ではありません)。 x86バイナリはAMD64で動作する可能性がありますが、必要な32ビットライブラリが利用可能な場合のみです。
  2. ライブラリのバージョン。 soversionが間違っている場合、ライブラリはまったく見つかりません。バージョンが同じでも、バイナリが実行されているライブラリよりも新しいバージョンのライブラリに対してビルドされている場合、新しいシンボルまたは新しいバージョンのシンボルが原因でロードに失敗する可能性があります。特に、glibcはシンボルバージョン管理のヘビーユーザーであるため、新しいglibcに対してビルドされたバイナリは、古いglibcで失敗する可能性が非常に高くなります。

急速に変化するライブラリーの使用を避け、アーキテクチャーの変更を避け、ターゲットにしたい最も古いディストリビューションでビルドすると、1つのバイナリーを多くのディストリビューションで機能させることができます。

11
plugwash

前述のいくつかのことに加えて、実行可能ファイルの形式にいくつかの変更が加えられました。 Linuxではほとんどの場合ELFを使用しますが、古いバージョンではa.outまたはCOFFを使用していました。

ウィキホールの始まり:

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

古いバージョンで新しいフォーマットを実行する方法があるかもしれませんが、私は個人的にそれを調べたことがありません。

4
Ben