小研究:Apple 默默改了 arm64e 的 MachO binary header

前幾天遇到使用者跟我反映說更新了 tweak 之後造成 Bad CPU Type 的錯誤,想半天找不到頭緒,最後是看到 @ichitaso 告訴我的解法,才驚覺 Apple 在 Xcode 12、macOS 11.0 以及 iOS 14 對 arm64e 的 binary 做了一些改變。因此筆記一下做個紀錄,不過由於我不是全職的 iOS 開發者,可能大家早就知道這件事了,單純是我 lag 而已 XD。

由於 Xcode 在編譯的時候會自動幫我們設定 archs,一直以來開發者不太需要擔心這個設定。但身為越獄 tweak 開發者的我,不是使用 Xcode 開發,而是直接用 makefile 去搭配 clang 來編譯,而在 iPhone XS Max 之後,Apple 推出了使用 arm64e 指令集的 CPU (A12 以上),因此越獄 tweak 開發者也得編譯 arm64e 的 tweak,才有辦法在這些裝置正常運作。

聽起來沒啥毛病,但是前幾天我陸續遇到有使用者跟我反應 tweak 在更新之後安裝不成功的問題,我用 iPhone X 親測好幾天都沒發現異狀,但持續有使用者不斷反應,最後我才發現問題出在 arm64e 的 binary 上面。

既然使用者反應的問題是 Bad CPU Type,所以我去看了一下 MachO header 關於 CPU 類型的原始碼,Apple 自己有開源,在這裡

可以看到原始碼如下:

根據原始碼可以看出,arm64e 的 CPU subtype 應該是 2。

然而,若對使用 Xcode 12 編譯出來的檔案,使用 MachO View 來看的話,會發現 CPU SubType 是一個奇怪的 0x80000002 的值,跟上面寫的不一樣(也跟以往用 Xcode 11 編譯出來的不一樣)。

根據剛剛的原始碼繼續找下去,會發現:

看起來是這個 0x80000000 是標示為 64bit binary 的一個標記。以往在 x86_64 的 binary 中也有出現,但後續被拿掉了。但 macOS 可以辨認 0x80000003 跟 0x00000003 兩種不同的 CPU Type(皆為 x86_64,只是一個多了 64bit lib flag),不知道為何 Apple 沒有對 arm64e 做一樣的相容處理。

從上面的結果,搭配我自己的實驗可以歸納出:在 Xcode 12 之後,編譯出來的 arm64e binary MachO header 結構有所改變,至少 CPU SubType 變了,其他部分我沒深入研究尚不清楚。而這項變動會造成 iOS 14 以下的 arm64e 裝置無法執行 Xcode 12 以上編譯出來的 arm64e binary。相同地,iOS 14 以上的 arm64e 裝置也無法執行 Xcode 11.7 以下編譯出來的 arm64e binary。

而這項變動一來可能是因為 arm64e 的 ABI 還是實驗性的,二來可能是搭配 Apple Silicon 而做對應的修改,詳細可以看一下這篇。此外,根據我嘗試,用 MachO View 同樣可以看到在 Big Sur 內建軟體中,CPU SubType 一樣是 0x80000002。

至於一般 App 開發者完全不用擔心,雖然 Apple 在兩年前就跟開發者說可以在 Xcode 10.1 準備有 PAC 保護的 binary,但是後續從來沒有開放包含 arm64e 的 binary 上傳到 App Store 或者 TestFlight。甚至在使用 Xcode 產生 IPA 檔案的時候,產出結果並不會包含 arm64e 的 binary 在內。即使 DTK 跟 M1 都是 arm64e 的 CPU,但 Xcode 目前編譯出來給 arm Mac 使用的 binary 架構也一樣是 arm64 而已。

所以最後,頭痛的還是越獄開發者,如前面所述,iOS 14 不支援 Xcode 11.7 以下編譯的 arm64e binary,反之亦同。由於現在並未有 iOS 14 arm64e 的公開越獄,所以尚無有任何影響,但未來若越獄成功,開發者們就得想辦法弄出跨版本支援的 binary 了。

 

發佈留言