解決Xcode11無法運行iOS9模擬器問題

注: 本文已過時踩窖。macosx > 10.14.99 則此方法不行诺擅。

馬上2020年了最域,所以你可能基本不需要用到iOS9模擬器了谴分,本文純灌水,不感興趣的同學可以不看镀脂。

問題描述

當Xcode11工程中引入了iOS11才有的CoreML等庫(雖然庫是weak依賴的)牺蹄,運行iOS9模擬器,啟動時會崩潰薄翅,提示如下:

dyld: Library not loaded: /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate
  Referenced from: /System/Library/Frameworks/CoreML.framework/CoreML
  Reason: no suitable image found.  Did find:
    /System/Library/Frameworks/Accelerate.framework/Versions/A/Accelerate: mach-o, but not built for iOS simulator

看提示沙兰,加載CoreML.framework時,它加載了MacOSX系統(tǒng)路徑下的CoreML.framework翘魄,導致后續(xù)加載它依賴的庫時鼎天,發(fā)生錯誤告警。然而暑竟,iOS10的模擬器斋射,同樣沒有CoreML庫,為什么不會有這問題但荤?

貌似Mac OSX系統(tǒng)低于10.14.1 加載相同的iOS9模擬器鏡像也不會有問題罗岖。
另外,附錄A提供了手動安裝低版本Xcode模擬器的方法供參考腹躁。

分析

首先桑包,我們知道應用啟動時,針對模擬器纺非,/usr/lib/dyld會加載模擬器鏡像下的dyld_sim哑了,然后轉交給dyld_sim來加載模擬器版本的庫赘方。

(lldb) image list
[  0] 97B86B7D-AF80-3222-B291-A0973B774C3B 0x000000010c0ec000 /Users/vincent/Library/Developer/CoreSimulator/Devices/F56758C0-3F87-4ED6-A373-CA542AD17C13/data/Containers/Bundle/Application/06ED7A2E-E9AA-4974-BC21-DF22D204180E/MyiOS9.app/MyiOS9
[  1] CE635DB2-D47E-3C05-A0A3-6BD982E7E750 0x000000010e1b0000 /usr/lib/dyld
[  2] 49268249-F1CD-35FC-BFFD-B4B8F3751B0D 0x000000010c0fe000 /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 9.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim

因為dyld是開源的,可以拿到源碼來更好地分析弱左。先通過dyld_sim中的LC_SOURCE_VERSION的load command來查看對應dyld源碼版本:

?  ~ otool -l /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS\ 9.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim | grep -A 3 "LC_SOURCE_VERSION"
      cmd LC_SOURCE_VERSION
  cmdsize 16
  version 390.7

這里查看到對應源碼版本390.7蒜焊,同樣的,拿到iOS 10.1的dyld_sim對應的源碼版本421.0

dyld下載源碼科贬,可以下載到421.0的源碼泳梆,但并沒有390.7的源碼,可以下個比較接近的360.22用來分析榜掌。

從錯誤開始优妙,查找mach-o, but not built for iOS simulator錯誤提示。發(fā)現(xiàn)它是由isSimulatorBinary 函數判定的憎账,通過該函數判定庫文件是Mac OSX還是模擬器版本的庫套硼,如果不是模擬器版,就會拋上述異常出去胞皱。

下面來分析360.22的源碼和421.0的源碼在判斷模擬器時的差別:

WeChatWorkScreenshot_f6bde04d-a64f-4ea8-99a8-89d8a6c4bda5.png

可以看到邪意,兩個版本,判斷庫文件是否為模擬器反砌,有兩個主要的區(qū)別:

  • 低版本只讀取了一頁(4096雾鬼,4k大小)的macho頭部,而高版本按mh->sizeofcmds來確定cmdsReadEnd邊界(該版本最大可以取32K)宴树。
  • 超過邊界cmdsReadEnd策菜,低版本返回true,而高版本返回false酒贬。

看下目前問題的表現(xiàn)又憨,能否由這兩段差異的代碼來解釋。目前的問題表現(xiàn)如下:

  1. iOS9 dyld_sim锭吨,加載系統(tǒng)的/System/Library/Frameworks/CoreML.frameworks時蠢莺,沒有報異常,把它當模擬器版本加載了零如。

  2. iOS9 dyld_sim躏将,加載CoreML依賴的/System/Library/Frameworks/Accelerate.framework時,卻提示它不是模擬器版埠况,并崩潰耸携。

  3. iOS10 dyld_sim,不會加載CoreML辕翰,因為正確識別它為模擬器版本了夺衍。

  4. 老的MacOS(更低版本的CoreML.framework),iOS9 dyld_sim識別CoreML正常喜命,模擬器可以正常運行沟沙。

用otool查看CoreML.frameworkAccelerate.framework的sizeofcmds大小河劝,如下:

? Frameworks otool -l CoreML.framework/CoreML| head -4
CoreML.framework/CoreML:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x00           6    23       4080 0x02918085
?  Frameworks otool -l Accelerate.framework/Accelerate| head -4
Accelerate.framework/Accelerate:
Mach header
      magic cputype cpusubtype  caps    filetype ncmds sizeofcmds      flags
 0xfeedfacf 16777223          3  0x00           6    17        968 0x02000085

CoreML加上header的20字節(jié),4080+20 > 4096矛紫,而Accelerate很小赎瞎,沒有超過4096。

這里還要補充一個背景颊咬,新版本的CoreML.framework等系統(tǒng)庫务甥,除了load commands超過4K,還廢棄掉了LC_VERSION_MIN_MACOSX等load command喳篇,改用LC_BUILD_VERSION來描述系統(tǒng)版本敞临。

Frameworks otool -l CoreML.framework/CoreML| grep -A 4 LC_BUILD_VERSION
       cmd LC_BUILD_VERSION
   cmdsize 32
  platform 1
       sdk 10.14
     minos 10.14

所以,來總結下iOS9模擬器啟動崩潰問題的原因麸澜。新MacosX系統(tǒng)升級后(好像是10.14.1)挺尿,/System/Library/Frameworks/CoreML.frameworks等庫廢棄了LC_VERSION_MIN_MACOSX等load command,導致之前的dyld_sim炊邦,無法判斷動態(tài)庫是否模擬器版本编矾。當無法判斷時,iOS10以下的dyld_sim對load command超過4K的庫馁害,認為是模擬器版本進行了加載窄俏,導致了后面的崩潰問題。iOS10以上的dyld_sim沒法區(qū)別時蜗细,默認認為不是模擬器版本裆操,所以加載CoreML.frameworks時拋異常怒详,但這個庫是weak依賴的炉媒,所以表現(xiàn)正常。

看起來理論可以解釋昆烁,但iOS9的模擬器沒有源碼吊骤,它的邏輯和390.7源碼的一致嗎?我們通過HopperDisassembler簡單分析下這個isSimulatorBinary函數静尼。

WeChatWorkScreenshot_25d0f1a2-c240-4328-aa14-0675f32ecac9.png

可以看到白粉,除了多判斷了0x2f<=rsi<0x31(這里hopper錯翻譯成rsi<0x31)其他邏輯和360.22基本一致。

這里也可以通過調試鼠渺,來確認邏輯鸭巴。附錄B給出具體的調試方法,供參考拦盹。

解決

說了這么多鹃祖,你可能要問了,一開始為啥模擬器要去加載Mac OSX里的動態(tài)庫呢普舆?原因是恬口,dyld_sim默認支持加載操作系統(tǒng)里任意路徑的動態(tài)庫校读,不過它會先加模擬器鏡像路徑前綴,沒找到才會嘗試原始路徑:

source3.png

所以看起來祖能,像/System/Library/Frameworks/CoreML.frameworks這個庫歉秫,只要在模擬器鏡像路徑(DYLD_ROOT_PATH)下沒找到,它就會找到Mac OSX下面去养铸。

這里DYLD_ROOT_PATH=/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS\ 9.3.simruntime/Contents/Resources/RuntimeRoot

道理都懂雁芙,但怎么修復呢?我們無法修改dyld_sim钞螟、/System/Library/Frameworks/CoreML.frameworks却特,因為這些都是蘋果的庫,做了codesign筛圆,改完無法加載裂明。通過lldb動態(tài)修改也不靠譜,因為無法確認debugger接入時機太援,可能attach上時闽晦,CoreML都加載進去了。

好在這里有個trick提岔,可以成功騙過了dyld_sim仙蛉。只要拷貝一個模擬器鏡像目錄下其他正常的庫,如CoreFoundation.frameworks碱蒙,并把它改名為CoreML.frameworks(記得也要改名里面的macho文件)荠瘪,那么它加載 DYLD_ROOT_PATH/System/Library/Frameworks/CoreML.frameworks時就可以正常加載而不會報錯(實際加載的是CoreFoundation),就不會找到Mac OSX系統(tǒng)里去導致后面的問題赛惩。完美~~~

同樣發(fā)現(xiàn)哀墓, Vision.framework和Intents.framework也有相同的問題∨缂妫可以同樣操作一把修復相關問題篮绰。

附錄A. Xcode安裝低版本模擬器

你的Xcode11不一定安裝了iOS9的模擬器。如果沒有安裝過季惯,需要手動安裝(模擬器下載列表可能找不到iOS9了)吠各。從iPhoneSimulatorSDK9_3 下載,并從dmg中提取出pkg安裝包勉抓。

我們知道贾漏,Xcode的模擬器鏡像都是存放在/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS x.x.simruntime下,但從蘋果下載的pkg安裝時默認是安裝到/下藕筋。我們可以安裝后把/Contents下的文件全部拷到上述鏡像目錄下纵散,也可以像如下方法,修改pkg安裝路徑。

# 1. 解壓pkg到temp目錄
pkgutil --expand iPhoneSimulatorSDK9_3.pkg temp

# 2. 修改解包目錄里的PackageInfo文件困食,設置install-location边翁,類似如下:

<pkg-info auth="root" identifier="com.apple.pkg.iPhoneSimulatorSDK9_3" install-location="/Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 9.3.simruntime/" postinstall-action="none" format-version="2" version="9.3.1.1460411551">

# 3. 重新生成pkg包
pkgutil --flatten temp MyiPhoneSimulatorSDK9_3.new.pkg

# 4. 雙擊新的pkg安裝,并看鏡像目錄下是否有iOS 9.3.simruntime的目錄

安裝完后硕盹,重啟Xcode符匾,在模擬器列表中就可以看到9.3版本的模擬器了。

附錄B. 調試dyld_sim

這里介紹如何調試本文描述的問題瘩例。dyld啟動時啊胶,很快就加載完了,如果庫加載有問題垛贤,很快就崩潰了焰坪,都跑不到代碼里(初始化的代碼也沒機會運行)。但通過shell里的lldb用waitfor方式等待調試聘惦,在attach時某饰,它會自動發(fā)送SIGSTOP,此時就可以看到dyld_sim的代碼善绎。

?  ~ lldb -n MyiOS9 -w
(lldb) process attach --name "MyiOS9" --waitfor

上述命令等待MyiOS9的模擬器App黔漂。點擊模擬器中的app,它就可以斷點進去(當然斷的時機不確定)禀酱。

lldb1.png
lldb2.png

同時加載的庫也不多炬守。

(lldb) image list
[  0] 97B86B7D-AF80-3222-B291-A0973B774C3B 0x000000010eb2a000 /Users/vincent/Library/Developer/CoreSimulator/Devices/F56758C0-3F87-4ED6-A373-CA542AD17C13/data/Containers/Bundle/Application/06ED7A2E-E9AA-4974-BC21-DF22D204180E/MyiOS9.app/MyiOS9
[  1] CE635DB2-D47E-3C05-A0A3-6BD982E7E750 0x00000001152d1000 /usr/lib/dyld
[  2] 49268249-F1CD-35FC-BFFD-B4B8F3751B0D 0x000000010eb3c000 /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 9.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim
[  3] 29506256-362E-38EB-9D36-D03C6D57E363 0x000000010eba8000 /Users/vincent/Library/Developer/CoreSimulator/Devices/F56758C0-3F87-4ED6-A373-CA542AD17C13/data/Containers/Bundle/Application/06ED7A2E-E9AA-4974-BC21-DF22D204180E/MyiOS9.app/Frameworks/lolz.dylib

查看isSimulatorBinary位置:

(lldb) image lookup -rn "isSimulatorBinary" dyld_sim
1 match found in /Library/Developer/CoreSimulator/Profiles/Runtimes/iOS 9.3.simruntime/Contents/Resources/RuntimeRoot/usr/lib/dyld_sim:
        Address: dyld_sim[0x00007fff5fc05633] (dyld_sim.__TEXT.__text + 17971)
        Summary: dyld_sim`dyld::isSimulatorBinary(unsigned char const*, char const*)

接下來就可以設置斷點了:

br set -a '0x000000010eba8000+17971+0x1000' -c '(int)strstr($rsi, "CoreML") != 0'

這里0x000000010eba8000是dyld_sim的隨機加載地址(通過image list查看),17971是函數相對dyld_sim.__TEXT.__text的偏移剂跟,0x1000是__TEXT.__text段在dyld_sim鏡像里的文件偏移减途。

你也可以調試loadPhase0__cxa_throw曹洽、__cxa_begin_catch等函數鳍置,如果函數在attach上之后運行的話。

注衣洁,如果要調本文說的CoreML的加載情況墓捻,最好給工程加個動態(tài)庫,讓這個動態(tài)庫再去依賴CoreML坊夫,不然lldb attach上時,基本CoreML的依賴解析已經處理完了撤卢。

參考資料

  1. XNU环凿、dyld源碼分析Mach-O和動態(tài)庫的加載過程

  2. dyld:iOS8 and iOS9 simulators in macOS 10.14.1 system crash caused by dyld loading error dynamic library

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市放吩,隨后出現(xiàn)的幾起案子智听,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,214評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件到推,死亡現(xiàn)場離奇詭異考赛,居然都是意外死亡,警方通過查閱死者的電腦和手機莉测,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,307評論 2 382
  • 文/潘曉璐 我一進店門颜骤,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人捣卤,你說我怎么就攤上這事忍抽。” “怎么了董朝?”我有些...
    開封第一講書人閱讀 152,543評論 0 341
  • 文/不壞的土叔 我叫張陵鸠项,是天一觀的道長。 經常有香客問我子姜,道長祟绊,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,221評論 1 279
  • 正文 為了忘掉前任哥捕,我火速辦了婚禮久免,結果婚禮上,老公的妹妹穿的比我還像新娘扭弧。我一直安慰自己阎姥,他們只是感情好,可當我...
    茶點故事閱讀 64,224評論 5 371
  • 文/花漫 我一把揭開白布鸽捻。 她就那樣靜靜地躺著呼巴,像睡著了一般。 火紅的嫁衣襯著肌膚如雪御蒲。 梳的紋絲不亂的頭發(fā)上衣赶,一...
    開封第一講書人閱讀 49,007評論 1 284
  • 那天,我揣著相機與錄音厚满,去河邊找鬼府瞄。 笑死,一個胖子當著我的面吹牛碘箍,可吹牛的內容都是我干的遵馆。 我是一名探鬼主播,決...
    沈念sama閱讀 38,313評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼丰榴,長吁一口氣:“原來是場噩夢啊……” “哼货邓!你這毒婦竟也來了?” 一聲冷哼從身側響起四濒,我...
    開封第一講書人閱讀 36,956評論 0 259
  • 序言:老撾萬榮一對情侶失蹤换况,失蹤者是張志新(化名)和其女友劉穎职辨,沒想到半個月后,有當地人在樹林里發(fā)現(xiàn)了一具尸體戈二,經...
    沈念sama閱讀 43,441評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡舒裤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,925評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了觉吭。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腾供。...
    茶點故事閱讀 38,018評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖亏栈,靈堂內的尸體忽然破棺而出台腥,到底是詐尸還是另有隱情,我是刑警寧澤绒北,帶...
    沈念sama閱讀 33,685評論 4 322
  • 正文 年R本政府宣布黎侈,位于F島的核電站,受9級特大地震影響闷游,放射性物質發(fā)生泄漏峻汉。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,234評論 3 307
  • 文/蒙蒙 一脐往、第九天 我趴在偏房一處隱蔽的房頂上張望休吠。 院中可真熱鬧,春花似錦业簿、人聲如沸瘤礁。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,240評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柜思。三九已至,卻和暖如春巷燥,著一層夾襖步出監(jiān)牢的瞬間赡盘,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,464評論 1 261
  • 我被黑心中介騙來泰國打工缰揪, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留陨享,地道東北人。 一個月前我還...
    沈念sama閱讀 45,467評論 2 352
  • 正文 我出身青樓钝腺,卻偏偏與公主長得像抛姑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子拍屑,可洞房花燭夜當晚...
    茶點故事閱讀 42,762評論 2 345

推薦閱讀更多精彩內容

  • 前言 dyld全稱the dynamic link editor途戒,即動態(tài)鏈接器,其本質是Mach-O文件僵驰,他是專門...
    01_Jack閱讀 3,730評論 2 14
  • 背景 一個項目做的時間長了,啟動流程往往容易雜亂,庫也用的越來越多蒜茴,APP的啟動時間也會慢慢變長星爪。本次將針對iOS...
    醬油瓶2閱讀 3,493評論 0 12
  • 一,市場狀況 德陽旌陽區(qū)粉私,劍南春大本營顽腾,人口750000,人均GDP70000元诺核,是中國重裝設備生產基地抄肖,外來人口...
    殷石閱讀 347評論 0 0
  • 夜黑云掩月, 草青水流肥窖杀。 寒意已漸起漓摩, 并非是宮闕。 深圳大學城圖書館-小記入客。
    神秘嘉賓方閱讀 230評論 0 1
  • 前幾天跟我媽吵了幾句管毙,這個星期都沒咋打電話,今天打電話桌硫,卻一直無法接通夭咬,有點兒心煩意亂。 有時候铆隘,我會想人為什么活...
    靜靜diary閱讀 164評論 0 0