XcodeでiPhoneアプリを開発しています。 SwiftとC言語を連携させる際に諸事情で提供していただいた静的ライブラリ(.aファイル)にオブジェクトファイル(.oファイル)を追加する必要がありました。 その際にUniversal Binary(ユニバーサルバイナリ)について知る機会があったのと、静的ライブラリへ追加するまとまった情報がなかったので共有したいと思います。
Universal Binaryとは
Universal BinaryはApple特有のバイナリファイルで異なるアーキテクチャ(CPU)に最適化されたバイナリファイルを複数保持できます。 最適化されたバイナリファイルは対象となるアーキテクチャでしか動かないのですが、Apple製のパソコンはモデルによってアーキテクチャが変わるので単一の最適化されたバイナリファイルだとMacによって動かない現象が起こります。 そのため互換性のない複数のアーキテクチャでもソフトウェアを動作させられるようにUniversal Binaryが使われます。 また32ビットと64ビットの過渡期において32ビットと64ビットのバイナリを混在させるためにも利用されています。 あと同様の意味でfat binary(ファットバイナリ)と呼ばれたりします。
例えばUniversal.aがUniversal Binaryの形式かどうかは以下のコマンドで分かります。
lipo -info Universal.a
Universal Binaryだと結果は以下のように「x86_64」と「arm64」と出力されます。
Architectures in the fat file: Universal.a are: x86_64 arm64
中身はこのような感じになります。
Universal.a
x86_64
Object1.o
Object2.o
Object3.o
arm64
Object1.o
Object2.o
Object3.o
x86_64が少し前のモデルでIntel Macのアーキテクチャです。 arm64はAppleシリコンのアーキテクチャです。最近のCPUです。 Universal Binaryファイル(Universal.a)の中に各アーキテクチャが格納されていて、その中にオブジェクトファイルが並んでいます。 オブジェクトファイルの構成は同じです。
このUniversal Binaryファイルにオブジェクトファイルを以下のように追加したいのです。
Universal.a
x86_64
Object1.o
Object2.o
Object3.o
Object4.o ← new
arm64
Object1.o
Object2.o
Object3.o
Object4.o ← new
静的ライブラリをビルドする際も意識しないとつまずくポイントのようです。 今回は提供してもらった静的ライブラリに手を加えたかったので以下のように追加しました。
Universal Binaryへの追加手順
オブジェクトファイルの作成(コンパイル)
以下のコマンドで作成できます。
clang -O3 Object4.c -o Object4.o -c -arch x86_64 -mios-version-min=13
各オプションの意味は以下のようになっています。
| オプション名 | 意味 |
|---|---|
| -O3 | 最適化レベルの指定です。レベル1~3まであって最高レベルを指定しました。 |
| -o | 出力ファイルの指定です。-o でfileが作成されます。 |
| -c | プリプロセス、コンパイル、およびアセンブルのステップのみを実行します。 |
| -arch | どのアーキテクチャで作成するかを指定します。今回はx86_64とarm64が必要だったのでそれぞれ作成します。 |
| -mios-version-min | iOSの開発バージョンを指定します。 |
オプション指定の中で一番のつまずきポイントは「-mios-version-min」でした。 アーキテクチャ以外にプラットフォームも合わせる必要があって、指定せずコンパイルするとプラットフォームがmacOSとしてコンパイルされます。 動かしたいOSがiPhoneのiOSなので何かしらの方法でiOSにしないと以下のように、「iOSでビルドしているけどオブジェクトファイルがmacOSでビルドされている」みたいなエラーが出ます。 プラットフォームを指定するオプションが見つからなかったので「-mios-version-min」を指定したところiOSとしてビルドされました。
プラットフォーム違いによるエラー
ld: in <ファイルパス>/Universal.a(Object4.o), building for iOS, but linking in object file built for macOS, file '<ファイルパス>/Universal.a' for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
アーキテクチャを指定したアーカイブファイルの取り出し
lipo -thin x86_64 Universal.a -output Universal_x64.a
取り出したアーカイブファイルにオブジェクトファイルを追加する
ar r Universal_x64.a Object4.o
「ar」は単一のアーキテクチャのみに対応したアーカイブコマンドでオブジェクトファイルをアーカイブファイルに追加する際に使用します。 Universal Binaryの操作は「lipo」で行います。
※上記手順でUniversal_arm.aも作成する
オブジェクトファイルを追加したアーカイブファイルでユニバーサルバイナリ形式のアーカイブファイルを作成する
lipo -create Universal_arm.a Universal_x64.a -output Universal.a
作成されるファイルは以下のようになります。
Universal.a
x86_64
Object1.o
Object2.o
Object3.o
Object4.o
arm64
Object1.o
Object2.o
Object3.o
Object4.o
以上の手順をcommandファイルにして実行すると快適でした。
今回の記事は以上となります。 かなりマイナーな内容ですが似たような対応をされる際に参考にしていただけると幸いです。

