GCC で小さな「Hello World」プログラムをコンパイルし、Linux で動作しました。次に、プログラムに.exe拡張子を追加して、Windowsで動作するかどうかを確認しました。しかし、うまくいきませんでした。プログラムが Windows で動作しない理由を教えてください。基本的に、バイナリとアセンブリの命令は、Windows と Linux でコンパイルされたプログラムと同じです (OS 固有のライブラリが使用されていない場合)。OSはプログラムをRAMにロードするだけで済み、プロセッサがそれを実行します。では、なぜうまくいかないのでしょうか。
4 に答える
あなたの質問を小さなサブセットに分解します
GCC で小さな「Hello World」プログラムをコンパイルし、Linux で動作しました。
システムにインストールされている通常の GCC と binutils は、実行されるのと同じターゲットとホストに対してコンパイルするように構成されているため、これは予想されることです。簡単に言えば、Linux カーネルと GNU C 標準ライブラリ (または互換性のある libc) ランタイムに適した実行可能ファイルを生成します。
また、プログラム自体が実行されるオペレーティング システムや CPU アーキテクチャとは異なる種類のオペレーティング システムや CPU アーキテクチャで実行するプログラムを構築できるコンパイラもあります。これらはいわゆるクロスコンパイラであり、たとえば、デスクトップ コンピューターでスマートフォン アプリをビルドするために使用しています。Windows Android NDK を使用してネイティブの Android 実行可能ファイルを作成すると、実際には Linux カーネルと別の CPU (おそらく ARM または MIPS) 用にクロスコンパイルされます。
次に、プログラムに.exe拡張子を追加して、Windowsで動作するかどうかを確認しました。
ここで、あなたは 1 つの誤解に陥りました。そもそもなぜそうなったのか、私には理解できません。ファイル名の接尾辞は、それができることで何かをしなければならなかった. Windows はこれを使用してレジストリを検索し、それをどう処理するかを説明しますが、それだけです。Windows では、「実行可能」として認識させたい任意のサフィックスを登録できます。Linux (Unixoid である) は、より単純なことを行いますが、IMHO はより強力です。各ファイルには、実行できる場合はフラグのセットがあり、実行できる場合は実際に誰が実行できるかを示します。
しかし、実行可能なプログラムとしてシステムにとって実際に有用であるためには、別の何かが必要であり、ファイル名に関係なく、(特定の OS の) すべての実行可能ファイルが共有されます: 特定の内部形式。Windows はいわゆるPE
フォーマットを使用します (PE はポータブル実行可能ファイルの略で、これをサポートする唯一の OS が Windows であるため、少しばかげています)。Linux と *BSD は ELF (Executable and Linkage Format) を使用し、MacOS X はMach Binary Formatと呼ばれるものを使用し、Intel CPU への移行はMach Universal Binary形式に拡張されています。技術的には、Mach カーネル (MacOS X の基盤) もELF形式を理解できますが、それは MacOS X では有効になっていないことがわかります。
両方のファイル形式は基本的に同じことを行いますが (ファイルをメモリにロードする方法、ファイルが依存するライブラリ、ロードする必要がある場所、それらをロードする場所、実際にプログラムを開始するポイントを記述します)、それらの構造は大きく異なります。 . Windows は ELF を読み込んで実行する方法を知りませんし、Linux は PE を読み込んで実行する方法を知りません。
また、どちらか一方が他方をサポートしていたとしても、システムには、プログラムがロードして実行することを期待するライブラリがまだありません。少なくとも、OS から起動パラメータを取得したり、OS 固有のメモリ管理機能へのインターフェイスを提供したりするなどの「平凡な」ことを担当する (オペレーティング システム) 依存のランタイム環境が必要です。 Linux と Windows では異なります。
OS固有のものの上に構築され、他のOSをプログラムにエミュレートし、外部実行可能ファイルをロード、リンク、および起動する方法も知っている互換性ラッパーを提供することは完全に可能であることに注意してください。Windows プログラムを Linux 上にロードして実行するためのラッパーは WINE と呼ばれ、Linux 実行可能ファイルを Windows 上で実行する LINE と呼ばれる逆のものも存在します。
しかし、うまくいきませんでした。プログラムが Windows で動作しない理由を教えてください。
Windows は Linux とは異なります。まったく異なるシステム レベル API を持ち、異なるバイナリ形式を使用し、stdin/stdout (C 標準ライブラリで定義されている) などの重要なものでさえ、まったく異なる方法で実装されています。一体、彼らは異なる呼び出し 規約を使用しています
Linux は、標準の C 呼び出し規則 (stdcall) を使用します。Windows はほとんどの場合、pascal と stdcall 規則のハイブリッドのような cdecl 規則を使用します。
基本的に、バイナリとアセンブリの命令は、Windows と Linux でコンパイルされたプログラムと同じです (OS 固有のライブラリが使用されていない場合)。
いいえ、そうではありません。呼び出し規約の違いだけでも、異なるアセンブリが生成されます。
OSはプログラムをRAMにロードするだけで済み、プロセッサがそれを実行します。
それほど単純ではありません。また、何か役に立つことを行うには、プログラムがオペレーティング システムに「接続」されている必要があります。コンソールに表示される文字列を見たいですか? 次に、それを行ういくつかのオペレーティング システム機能に接続する必要があります。
Linux では、コンソールは既にそこにあり、現在のまたはに内部的に関連付けられている特別なファイル、、、が/dev/console
あり/dev/stdin
ます。プロセス内では、これらは常にファイル記述子 0、1、および 2 にマップされます。/dev/stdout
/dev/stderr
pts
tty
Windows では、プログラムは独自のコンソール ウィンドウを開く必要がある場合と開かない場合があります (CLI から開始された場合はそれを使用します)。したがって、コールする必要がAllocConsole
あり、フォールバックとして失敗した場合はAttachConsole(parent_PID)
; 次に、GetStdHandle
stdin、stdout、stderr を取得するために使用できます。これが取得するHANDLE
s は任意であり、C stdio (printf、fwrite など) および C++ iostream の内部実装に接続する必要があります。
また、CPU はバイナリの一部を "魔法のように" 実行しません。オペレーティング システムは、新しいプロセスを作成する必要があります。そのためには、特定の重要なコードが存在するバイナリの場所 (エントリ ポイント) を認識している必要があります。これらのエントリ ポイントは、(とりわけ) バイナリ ファイル形式の特別な場所に記述されます。また、既に説明したように、Windows は Linux とは大きく異なる実行可能バイナリ フォーマット (PE) しか認識しません。バイナリをロードしてリンクしたら、実際に開始する必要があります。上記で概説したように、バイナリを開始する手順も Linux と Windows で異なります。
後半の編集ですが、次のように記述する必要があります。
*また、プログラムの開始時に、実行可能ファイルのバイナリが完全に RAM に読み込まれるわけではないことを理解することも非常に重要です。何が起こるかというと、いわゆる「仮想メモリ マッピング」が作成されます。これは、仮想アドレス空間がオペレーティング システムによって作成され、他のいくつかのデータ構造と共に、これが一般にプロセスと呼ばれるものであることを意味します。仮想アドレス空間は、いわゆる「ページ」のセットによって支えられています。これは、実際のメモリまたはストレージ デバイスにマップされるアドレス空間のチャンクです。実際、通常、RAM は常にディスク操作のキャッシュとして使用されます。プログラム バイナリがマップされると、仮想メモリ ページは常にディスクストレージを指し、RAMはそのためのキャッシュとしてのみ機能します。動的に割り当てられたメモリは、ストレージ デバイスのスワップ領域をキャッシュしているディスク キャッシュから取得されます。スワップ デバイスがない場合は、汎用ディスク キャッシュから取得されます。*
ご覧のとおり、これらは実行するタスクの完全に異なるリストです。それに、呼び出し規約とバイナリ形式の違いを追加します。
では、なぜうまくいかないのでしょうか。
実行可能ファイルには、不活性なアセンブリ命令だけではありません。プログラムは、意味のあるものを生成するためにオペレーティング システムと対話する必要があります。そして、Windows と Linux は非常に異なります。
最新の OS はそれほど簡単ではありません。Linux 実行可能ファイルを取得し、EXE 拡張子を追加して Windows で実行すると、動作しません。最新の OS には、コードとデータ以外の実行可能ファイルに多くの追加情報があります。Linux はELF 実行形式を使用し、Windows はEXE 形式を使用します。この形式には、再配置情報、メモリ サイズ、パーティション、およびこの実行形式を使用する OS のみに関連する情報が含まれます。したがって、ファイルの拡張子を変更するだけでは機能しません。
また、OS のシステムコールとその呼び方も異なります。Linux はシステム コールにソフトウェア割り込み (INT 0x80 IIRC) を使用しますが、Windowsは別のシステムを使用します。これは、システム コールでさえ同じ方法で実行されず、C コンパイラがそのターゲットの正しいlibcに対してリンクする必要があるため、ターゲットごとに再コンパイルする必要があることを意味します。
Linux と Windows の実行可能ファイルの主な違いはフォーマットです。Linux はELF フォーマットを使用し、Windows はPEフォーマットを使用します。
各プラットフォームでプログラムをコンパイルする必要があります。MinGWは、Windows にとって興味深いものになる可能性があります。
コードが同じであるというあなたの仮定は正確には正しくありません。数学計算が同じように機能するのは事実ですが、異なるオペレーティング システムがプログラムに同じインターフェイスを提供するという点で間違っています。見る: