では、Universal Binaryがどういった実装なのかを見てみよう。
まず、Mac OS XのバイナリフォーマットはほかのUNIXで一般的なELFやCOFFではなく、Mach-Oという形式である。Mach-Oの構造を簡単に説明すると図2のようになる。先頭にMachヘッダがあり、続いて可変長のLoad Commandsがあり、最後に実際のコードやデータをつなぎ合わせたデータ領域がある。Machヘッダには、Machヘッダであることを示す32ビットのmagicナンバー*「0xfeedface」と、対応するCPUのアーキテクチャを示す数値が記載されている(図3)。
Load Commandsはダイナミックローダー(dyld)によって実行されるコマンドの集まりで、コードを格納するTEXTセグメントやDATAセグメント、スタックといったメモリ領域を確保、確保したメモリ領域に対して実行ファイルの一部をマップしたり、レジスタの値を設定したり、スレッドを呼び起こしTEXTセグメントにマップされたコードを実行するといった処理が記載されている(図4)。実行ファイルのデータ領域は特に構造を持たないが、このLoad Commandsを読み解いていくことで複数のバイナリデータをつなぎ合わせたものであると分かる。
Universal Binaryでは、このMachヘッダの前にFATヘッダとfat_archという構造体が置かれる(図5)。FATヘッダにはそれを表すmagicナンバー「0xcafebabe」が記載され、続けてその後に幾つfat_arch構造体が続いているかが示されている。fat_arch構造体にはCPUのアーキテクチャを示す数値とそのアーキテクチャに対応するMachヘッダへのオフセットが記録されている(図6、コラム3)。
Darwinカーネルのexecシステムコールは、実行するファイルの先頭が「0xfeedface」だったらそのままdyldに制御を移すが、「0xcafebabe」の場合はこのfat_arch構造体を順番に読み、自身のアーキテクチャに最も適合するものを選択、Machヘッダを読み込みdyldに制御を移す(実行例1、2)。
% od -x /bin/ls | less
0000000 feed face 0000 0012 0000 0000 0000 0002
0000020 0000 000b 0000 0648 0000 0085 0000 0001
0000040 0000 0038 5f5f 5041 4745 5a45 524f 0000
% od -x a.out | less
0000000 cafe babe 0000 0002 0000 0012 0000 0000
0000020 0000 1000 0000 4554 0000 000c 0000 0007
0000040 0000 0003 0000 6000 0000 39b0 0000 000c
0000060 0000 0000 0000 0000 0000 0000 0000 0000
*
0010000 feed face 0000 0012 0000 0000 0000 0002
0010020 0000 000a 0000 0544 0000 0085 0000 0001
0010040 0000 0038 5f5f 5041 4745 5a45 524f 0000
なお、先頭16バイトが「0x2321」(ASCIIで「#!」)だった場合、一般のUNIXと同様に実行ファイルはスクリプトだと見なされる。「#!」に続けて指定されたインタープリタに制御が移り、スクリプトが実行される。
Intel CPUを搭載したMacのカーネルでは、若干処理が異なる。実行ファイルにx86のコードが含まれていない場合、今度はPowerPCのコードを探し、/usr/libexec/oahにあるtransitというバイナリを経由してこれを「実行」する。残念ながらこの部分に関してはまだソースが公開されてないため詳細は不明だが、このtransitというコマンドと、transitからMach-IPCを使って起動されるtransitedというサーバプロセスが、PowerPCコードをIntelのコードに変換するトランスレータ、Rosettaの実体であろう。
プログラム中に表れる、明確な意味や役割を持つ具体的な値。
Copyright © ITmedia, Inc. All Rights Reserved.