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


59

Linuxの1つのフレーバーでコンパイルされた、以下に示すような非常に単純な小さなプログラムの実行可能ファイルは、異なるフレーバーで実行されますか?または、再コンパイルする必要がありますか?

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

int main()
{
  return (99);
}

2
すばらしい回答をありがとうございました!予想以上に多くのことを学びました。できる限り少ないライブラリに依存するように、意図的にコードを人為的に単純にしました。しかし、私は本当にそれを前もって述べるべきでした。プラットフォーム間での私のC ++コーディングのほとんどは、Visual StudioなどのMicrosoftツールを使用して開発し、* nixシステムにコードを移植して再コンパイルすることを伴いました。
JCDeen

4
ここで表明された多くの側面と考慮事項は私を驚かせました!私は、答えとしていくつかを選択できることを正直に願っています。すべてに感謝します!心から。
JCDeen

2
AndroidはLinuxベースのオペレーティングシステムでもあります。ただし、幸いなglibcことに、コンパイルされたコードを実行したり、その逆を実行したりできます。確かに、それは完全に不可能ではありません
アンダーキャット

2
コマンドラインツールの互換性を最大限に高めるには、glibcの代わりにuClibc、musl、またはDietlibcを使用し、32ビットの実行可能ファイルを静的にリンクすることができます(gcc -m32 -static)。これにより、i386またはamd64 Linuxで実行可能ファイルを実行できるようになります。
pts

10
42を返す必要があります!:)
ホムンクルスレティキュリ

回答:


49

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

-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(本質的にRedHat Enterprise Linux 7.4)は同じ「ディストリビューション」ファミリーにとどまっているため、2002年からのランダムな「ゼロからのLinux」インストールから2018年に他のランダムなLinuxディストリビューションに移行するよりも移植性が高い可能性が高い。

amd64用にコンパイルされたものは、Linuxの32ビットのみのリリースでは実行されません(古いハードウェアは新しいハードウェアを認識しません)。これは、ライブラリやシステムコールさえも後方への移植性がないため、古代の古いもので実行することを目的とした現代のシステムでコンパイルされた新しいソフトウェアにも当てはまります。そのため、コンパイルトリックや古いコンパイラーの入手などが必要になる場合があります古いシステムでコンパイルします。(これは古代の古いものの仮想マシンを保持する正当な理由です。)

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

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

これはamd64システムで処理できます(Linuxシステムでは、OpenBSDはamd64と対照的に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-glibc.x86_64                     1:2.12-4.el7.centos

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

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


14
最初の段落について:いいえ、Intelはそれを「Intel 64」と呼びます(最近では、他のいくつかの名前を先に調べた後)。IA-64はItaniumを指し、x86互換ではありません。
ホブス

1
@hobbsありがとう、これらの参照をamd64に置き換えました。物事の命名はインテルのマーケティング部門に任せます。
-thrig

3
静的リンクに言及しないのはなぜですか?
dcorking

2
変更されるのはライブラリABIだけではありません-カーネルのsyscallインターフェースも時間とともに拡張されます。for GNU/Linux 2.6.32の出力にある(または)に注意してくださいfile /usr/bin/ls
チャールズダフィー

1
@Wilbertおそらく、thrigがRed Hat Enterprise Linux とは異なるRed Hat Linuxについて言及していたことを忘れているでしょう。
ボブ

67

要するに、同じ(または互換性のある)アーキテクチャを使用して、あるホストから別のホストにコンパイル済みのバイナリを使用する場合、別のディストリビューションに使用するのはまったく問題ありません。ただし、コードの複雑さが増すと、インストールされていないライブラリにリンクされる可能性が高くなります。別の場所にインストール。または異なるバージョンでインストールされた場合、増加します。(Debianから派生した)Ubuntu Linuxホストでlddコンパイルすると、次の依存関係を報告するコードを例にとりgcc -o exit-test exit-test.cます。

$ 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つは上記で概説しています。


4
確かに、静的なバイナリを忘れていました。一部のベンダーは静的バイナリを採用し、一部のマルウェア作成者も同じアーキテクチャの
Rui F Ribeiro

8
フォークリフト...?
user253751

2
@immibisデータ(実行可能ファイル)をある環境(ディストリビューション)から別の環境(コピー先環境向けに設計されていない)にコピーすることを意味すると思います。
-wjandrea

13
あなたのLinuxの例は、残念ながらかなり人工的なものであり、ディストリビューションではなくアーキテクチャに関するポイントを示しています。Debianで32ビットバイナリをビルドし、64ビットRHELで実行しようとしました。これらは異なるアーキテクチャです...ライブラリ依存関係がほとんどない同じアーキテクチャのバイナリをうまくコピーできます。
スティーブンキット

7
@MSalters不合理だと言っているのではなく、DopeGhotiが作成しようとしている点を考えると悪い例だと言っています(バイナリをあるディストリビューションから別のディストリビューションにコピーすることはできません-これは間違っています)。もちろん、Intel上の64ビットLinuxは、適切なインフラストラクチャで32ビット実行可能ファイルの実行もサポートしています。この場合の有効な例は、IMOがamd64バイナリをビルドして別のamd64ディストリビューションで実行するか、i386バイナリをビルドして別のi386ディストリビューションで実行することです。
スティーブンキット

25

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

何もハードウェアを有する特定のか、あなたの例のように、単純な源であるならば、あなたはかなりの間からは全く何の問題もなく、それを移動させることができる任意のソースコードなどのLinuxまたはアーキテクチャのバージョンを限り、先のサーバはCの開発パッケージがインストールされているとして、必要なライブラリ、およびインストールされている対応する開発ライブラリ。

遠い古いバージョンのLinuxからより高度なコード、または異なるカーネルバージョンのカーネルモジュールのようなより特定のプログラムを移植する場合、非推奨のライブラリ/ API / ABIを考慮してソースコードを調整および変更する必要があります。


19

することで、デフォルト、あなたはほぼ確実に外部ライブラリの問題に実行されます。他の回答のいくつかは、これらの問題についてより詳細に説明しているので、私は彼らの仕事を複製しません。

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


また、qemu異なるアーキテクチャ向けにコンパイルされたプログラムを実行することもできます(高性能ではありませんが、実行できます)
Jasen

1
Linux Standard Baseツールキットのことすら知らなかったので、ありがとう!私はかなり前にC / C ++を使い始めたので、これらの回答の情報の多くは私にとって新しいものです。とても助かります。
JCDeen

1
Wikipediaの記事は、DebianとUbuntuがLSB実装していないと言う(そしてそうするつもりはありません。)
ブラックジャック

2
@BlackJack-ディストリビューション自体は、コアOSの一部としてその100%を実装していませんが、LSB互換ライブラリとツールキットをオプションパッケージとしてインストールできます。私はUbuntu(たとえば)を使用してLSB互換プログラムを構築し、それをSuseとCentosで実行しました。必要なのapt-get installはいくつかのパッケージだけです。
bta

10

多分。

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

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

急速に変化するライブラリの使用を避けた場合、アーキテクチャの変更を避け、ターゲットとする最も古いディストリビューションでビルドすると、多くのディストリビューションで1つのバイナリが動作する可能性が高くなります。


4

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

ウィキホールの始まり:

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

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


「古い」はこの時点では非常に古いですが、今では。
プラグウォッシュ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.