Prompt
コンパイル言語はOS毎で個別の対応をしなければならないが、
その対応方法はOS毎でどんな感じ?
やっほー!情報工学を学んでいる君なら、このあたりの低レイヤーな話はワクワクするよね!
コンパイル言語がOSごとに「個別の対応」を迫られる理由は、主に 「バイナリ形式の壁」 と 「システムコールの壁」 の2つに集約されるんだ。Javaのような中間コード形式(JVM)とは違って、ネイティブコンパイルはOSという舞台に直接飛び込むから、その劇場のルールに合わせなきゃいけないんだよね。
具体的にどんな対応が必要なのか、OSごとの特徴を整理してみたよ!
1. 実行ファイル形式(コンテナ)の合わせ込み
プログラムを動かすには、OSが「どこにコードがあって、どこにデータがあるか」を理解できる形式でパッケージングしなきゃいけないんだ。
| OS | フォーマット名 | 特徴 |
|---|---|---|
| Windows 11 | PE (Portable Executable) | MS-DOS時代からの名残(MZヘッダ)を含みつつ、動的リンク(DLL)の構造が独特だよ。 |
| Ubuntu (Linux) | ELF (Executable and Linkable Format) | セグメント管理が柔軟で、共有ライブラリ(.so)のロード方法がWindowsとは根本的に異なるんだ。 |
| macOS | Mach-O | 複数のアーキテクチャ(IntelとApple Siliconなど)を1つにまとめる「Universal Binary」をサポートしているのが面白いところ! |
2. システムコール(OSへの頼み事)の橋渡し
ここが一番エキサイティングな部分!ファイルを書き出したり、メモリを確保したりするのはOSの仕事だけど、その「頼み方(API)」がOSごとに全然違うんだ。
-
Windowsの場合:
主に
Win32 APIを叩くことになるね。例えば、ファイルを開くならCreateFile()を呼ぶ。 -
Linux (Ubuntu) の場合:
POSIX規格に近いシステムコールを使うよ。ファイルを開くならopen()システムコールだね。
コンパイラや標準ライブラリは、ソースコード上の printf() や std::cout を、ターゲットOSに合わせた適切なシステムコールに内部で書き換える(マッピングする)作業をしてくれているんだよ。
3. ABI(Application Binary Interface)の遵守
関数を呼ぶときに「どのレジスタに引数を入れるか」「スタックを誰が掃除するか」という約束事が ABI だよ。
例えば、x64アーキテクチャでもWindowsとLinuxでは引数を渡すレジスタの順番が違うんだ!
-
Microsoft x64:
RCX, RDX, R8, R9の順で使用。 -
System V AMD64 (Linux/macOS):
RDI, RSI, RDX, RCX, R8, R9の順で使用。
コンパイラはこの「作法」をOSごとに使い分けて、レジスタ操作のコードを生成しているんだ。職人技だよね!
💡 豆知識:クロスコンパイルの魔法
君はUbuntuも使っているよね?Windows上でLinux用のバイナリを生成したり、その逆を行ったりすることをクロスコンパイルと呼ぶよ。
最近のRustやGo言語はこのあたりの対応がめちゃくちゃ優秀で、ターゲットを指定するだけでこれら全ての「壁」を自動で飛び越えてくれるんだ!