主頁 > 移動端開發 > 從APP的啟動說起

從APP的啟動說起

2020-09-22 19:15:44 移動端開發

iOS里面APP的啟動,程序有些復雜,今天我們來抽絲剝繭,一步步探討一下APP的啟動會經歷哪些程序,

首先,用戶點擊iPhone里面的某個APP的icon,Kernel內核會開始初始化空間并創建行程, 在呼叫exec_active_image后,開始加載Mach-O檔案,

這里我們簡要說一下Mach-O檔案,

Mach-O

Mach-O是iPhone下的可執行檔案格式,我們的APP對應的ipa檔案,解壓縮以后就會看到這個Mach-O檔案,我們可以用MachOView這個軟體來查看一下,如圖:

(注:這里使用的是x86架構下的mach-o檔案,也就是模擬器生成的,如果是arm架構的話會有一些區別,不過區別不大,整體結構差不多)

 我們拿其中幾個比較重要的來講解一下,

Mach64 Header:描述了Mach-O的CPU架構、檔案型別以及加載命令等資訊,

Load Commands:一系列的加載的命令集合,在Mach-O檔案加載的時候用于給kernel和dyld呼叫,如圖:

LC_SEGMENT_64(__PAGEZERO):映射虛擬記憶體的第一頁地址和大小,一般是4G(0x1000000)大小,

LC_SEGMENT_64(__TEXT):代碼段的Header,里面記錄了__TEXT的各種型別的偏移地址,如圖:

表明了__stubs的偏移地址以及一些相關的頭資訊,其他的Header也類似,

LC_SEGMENT_64(__DATA):資料段,里面記錄的資訊也是偏移地址和一些相關頭資訊,

LC_SEGMENT_64(__LINKEDIT):記錄的是動態鏈接相關的偏移地址和頭資訊(主要是dyld),動態鏈接十分重要,我們在后面會說到,

LC_DYLD_INFO_ONLY:記錄了動態鏈接的rebase,binding,lazy binding等的頭資訊和偏移地址,

LC_SYMTAB:符號表的資訊,記錄符號表的位置,偏移量,資料個數等,通常跟Symbol Table還有String Table一起來查找符號地址,如下圖:

在__Text代碼段找到代碼-[XFCorrelationNewsJSExport onl oad]的符號地址:0x1000014E0,通過LC_SYMTAB中的Symbol Table Offset找到地址 0x0012C218,然后根據此地址找到Symbols -[XFCorrelationNewsJSExport onl oad] 的偏移地址 0x00006D70 與 String Table的起始地址相加后計算出符號地址為:0x0017DB7C,然后就可以找到我們符號對應的字串,如果要收集crash,也就可以拿到符號地址對應的符號的名字了,

LC_LOAD_DYLINKER:該Mach-O使用的聯結器資訊,記錄了具體使用哪個聯結器接管內核后續的加載作業,以及聯結器的位置資訊,

LC_LOAD_DYLIB:依賴庫資訊,dyld會通過這個段去加載動態庫,列出了所有依賴的動態庫,

Mach-O檔案就暫時介紹到這里,后續提到動態聯結器(dyld),動態庫(dylib),動態庫的延遲系結問題時,還會繼續介紹Mach-O相關的Section,

這里分享一點關于Mach-O的小感悟,一開始我在看Mach-O檔案的各個section和segment的時候,覺得這么多的section,這么多的segment,我怎么可能搞清楚每一個都是干什么的,就算搞清楚了,時間長了也會忘記,后來我仔細想了一下,覺得Mach-O只是一種作業系統認識的可執行檔案格式,所以他的各個section或者segment都是為了在不同的時候和不同的階段提供不同的資訊給作業系統使用的,所以,我個人認為,只需要了解他的大致結構(MachHeader)和比較核心的幾個點(Load Commands,動態庫和動態鏈接相關)就可以了,

在加載了Mach-O后,會開始載入動態聯結器,

我們來簡要說一下動態聯結器,

動態聯結器

在介紹動態聯結器之前,我們有必要先介紹一下什么是鏈接,什么是動態鏈接,

鏈接

鏈接就是通過聯結器將執行檔案中參考的其他符號(變數和方法)做地址重定位的程序,鏈接分為:靜態鏈接和動態鏈接,

靜態鏈接

現在假設檔案A,里面有方法 a(),方法a()里面參考了檔案B里面的方法b(),那么在編譯器編譯的時候,會將方法a里面呼叫的方法b的地址以0x0,0x2等這些來暫時代替,然后輸出可執行檔案C,等到呼叫靜態聯結器的時候,由靜態聯結器來將真實的方法b的地址(這里的真實地址其實是指的虛擬地址)修改到C對應的位置上,

這里有個問題就是靜態聯結器如何知道哪些符號的地址需要重定位呢?

因為在編譯A的時候,會生成一個重定位表,里面記錄了哪些符號需要被重定位,

動態鏈接

動態鏈接區別于靜態鏈接在于鏈接的時機不同,靜態鏈接是編譯的時候做鏈接,而動態鏈接是在APP啟動時做鏈接,而且對于動態庫而言,里面的方法并不會做鏈接操作,只有當第一次運行到這個方法時,才會去做鏈接操作,從而得到真正的地址,這也叫:延遲系結,

動態鏈接主要是針對動態庫(dylib,或者也可以叫共享庫)的鏈接操作,在系統的/usr/lib目錄下,存放了大量供系統與應用程式呼叫的動態庫檔案,動態庫不能直接運行,而是需要通過系統的動態聯結器(dyld)進行加載到記憶體后執行,當dyld加載完動態庫以后,不同的APP可以使用同樣的動態庫(跨行程共享代碼和部分資料),但是需要注意的是,對于各行程共享的部分,只包括代碼和不需要修改的資料部分,對于會變動的資料部分,是會被分離出來,每個行程一個副本,

這里有一個問題,就是如何才能在各個行程間共享可以共享的動態庫的代碼和無需修改的資料呢?

因為各行程呼叫動態庫的地址都是各個行程的虛擬地址,彼此獨立,所以你沒辦法修正動態庫的代碼的地址來適應所有行程呼叫,于是有人想到了用絕對地址,雖然可以滿足這一要求的,但是會帶來新的問題,即:

- 程式每引入一個共享庫或者共享庫更新后占用空間更大,就需要預留更大的虛擬空間(但是事實上并不是每個函式都會被呼叫到),可執行檔案或許就要重新編譯,
- 共享物件更新時,內部的符號地址可能變化,可執行檔案又得重新編譯,

所以用到了地址無關代碼 (PIC, Position-independent Code) 技術:

無論目標模塊(包括共享目標模塊)被加載到記憶體中的什么位置,資料段總是緊跟著地址段的,因此,代碼段中的任意指令與資料段中的任意變數之間的距離在運行時都是一個常量,而與代碼和資料加載的絕對記憶體位置無關,

例子:

 1 //動態庫代碼 Person.h
 2 extern const NSString * _Nonnull str;
 3 
 4 extern int add(int a, int b);
 5 
 6 NS_ASSUME_NONNULL_BEGIN
 7 
 8 @interface Person : NSObject
 9 
10 - (void)printStr:(NSString *)str;
11 
12 @end
13 
14 //動態庫代碼 Person.m
15 const NSString * _Nonnull str  = @"abc";
16 
17 int add(int a, int b) {
18     return a + b;
19 }
20 
21 @implementation Person
22 
23 - (void)printStr:(NSString *)str {
24     
25     NSLog(@"sss:%@", str);
26 }
27 
28 @end
29 
30 //另一個專案引入動態庫后呼叫的代碼
31 - (void)viewDidLoad {
32     [super viewDidLoad];
33     // Do any additional setup after loading the view.
34     Person *person = [[Person alloc] init];
35     [person printStr:@"ttt"];
36     
37     NSLog(@"%@", str);
38     
39     NSLog(@"%d", add(3, 5));
40 }

動態鏈接對于資料參考和方法參考,處理的方式有些區別,

資料參考:

編譯器在代碼段和資料段之間創建了一個GOT(Global Offset Table,全域偏移表),里面存盤的是目標模塊參考的動態庫中的變數,如圖:

 

初始狀態下,這些GOT中的地址都是0x0,到了app啟動的時候,在Binding階段(后面會講到)動態聯結器會將GOT中的資料地址都做一次修正,因為GOT是一個陣列,所以修正的方式比較簡單,即:GOT[n] = 代碼段的地址 + 代碼段與資料段的固定偏移 + GOT資料大小

方法參考(延遲系結):

編譯器在編譯的時候會在__TEXT,__stubs里面將動態庫的add方法生成一個占位,這個占位主要用來指向__DATA,____la_symbol_ptr里面對應的項,如圖:

當運行到上面的代碼第39行,目標函式呼叫動態庫中的add方法,對應匯編如圖:

bl是匯編指令,跳轉到子程式的意思,使用Hopper Disassembler查看一下匯編,如圖:

ldr:將記憶體中的值存入到暫存器x16中,此時0x10000c018正好對應__DATA,____la_symbol_ptr中的項,

br:x16  跳轉到x16指向的地址,如圖:

第一次呼叫add方法的時候,__DATA,____la_symbol_ptr里面尚未記錄add的地址,而是指向__TEXT,__stub_helper里面相關的內容(0x0000001000065E4),如圖:

w16:暫存器x16的低32位

.long 0x0000003f 找尋Dynamic Loader Info 中Lazy Binding Info的偏移3f的符號

上述代碼的意思就是:跳轉到__TEXT,__stub_helper頭部(65CC),然后呼叫 dyld_stub_binder(動態聯結器的入口) 進行符號系結,最后會將 add 的地址放到 __la_symbol_ptr 處,下次再呼叫就可以直接取add的地址呼叫了,

繞了這么大一圈終于完成了方法的系結,簡化一下:

生產stub占位 -> 運行時呼叫 -> 指向la_symbol_ptr -> 如果有地址則回傳地址,如果沒有地址則指向stub_helper -> 呼叫dyld_stub_binder來系結方法地址并修正la_symbol_ptr的地址,

這里會產生一個問題,為什么需要la_symbol_ptr,直接在stub里面修改地址不就完了嗎?

因為stub是代碼段,而代碼段是只讀的,動態庫的指導思想就是共享代碼段,分離出可變資料段,所以需要la_symbol_ptr,

綜上所述,我們可以簡單羅列一下靜態鏈接庫和元件的區別:

1、靜態鏈接庫在編譯后,庫里的方法及變數地址就確定了(虛擬地址),元件則是在運行時才能確定,而動態庫中的方法則需要到呼叫到的時候才能確定,

2、靜態鏈接庫會打包進APP中,而元件則在系統的/usr/lib目錄下,如果是自己制作的動態庫,也會隨著APP一起打包進去,

動態聯結器(dyld)
蘋果作業系統的重要組成部分,負責鏈接和裝載動態庫,當xnu內核(開源的系統底層代碼,下載地址)加載了動態聯結器以后,APP將從內核態過度到用戶態,

dyld本身也是mach-o格式的檔案,但是dyld中不會再參考其他動態庫的東西,所以就不存在動態系結這個程序了,拿MachOView看看如圖:

動態聯結器也是開源的,下載地址,

接下來App的啟動就進入Rebase,Binding階段了,

這幾個階段都是由dyld來控制的,我們來簡單分析一下他的這幾個程序

Rebasing

在過去,會把 dylib 加載到指定地址,所有指標和資料對于代碼來說都是對的,dyld 就無需做任何 fix-up 了,如今用了 ASLR 后會將 dylib 加載到新的隨機地址(actual_address),這個隨機的地址跟代碼和資料指向的舊地址(preferred_address)會有偏差,dyld 需要修正這個偏差(slide),做法就是將 dylib 內部的指標地址都加上這個偏移量,偏移量的計算方法如下:

Slide = actual_address - preferred_address

Binding

主要是針對那些外部符號做的系結操作,比如我們上面說的GOT中的內容,

剩余啟動事件

App啟動到這里接下來就是進入到Runtime環節,會初始化Runtime環境并初始化,處理category和呼叫+load()方法,

initializers 呼叫所有動態庫的initializer方法,初始化動態庫,

呼叫App的main函式,正式進入App的生命周期,

小結

App的啟動我們來回顧一下,主要分為:加載Mach-O、加載dyld、rebase、binding、加載dylib,Runtime、Initializer、main這幾個程序,我們主要講解了一下Mach-O的檔案結構,動態鏈接的GOT和動態系結程序,還簡單介紹了rebase和binding,

可以看出來,App的啟動程序十分復雜,還有很多細節和知識點需要我們仔細深入研究和學習,

轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/106772.html

標籤:其他

上一篇:Android Weekly Notes Issue #428

下一篇:騰訊開發10年大佬:iOS開發現狀與發展淺析!給你一個職業規劃的方向

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【從零開始擼一個App】Dagger2

    Dagger2是一個IOC框架,一般用于Android平臺,第一次接觸的朋友,一定會被搞得暈頭轉向。它延續了Java平臺Spring框架代碼碎片化,注解滿天飛的傳統。嘗試將各處代碼片段串聯起來,理清思緒,真不是件容易的事。更不用說還有各版本細微的差別。 與Spring不同的是,Spring是通過反射 ......

    uj5u.com 2020-09-10 06:57:59 more
  • Flutter Weekly Issue 66

    新聞 Flutter 季度調研結果分享 教程 Flutter+FaaS一體化任務編排的思考與設計 詳解Dart中如何通過注解生成代碼 GitHub 用對了嗎?Flutter 團隊分享如何管理大型開源專案 插件 flutter-bubble-tab-indicator A Flutter librar ......

    uj5u.com 2020-09-10 06:58:52 more
  • Proguard 常用規則

    介紹 Proguard 入口,如何查看輸出,如何使用 keep 設定入口以及使用實體,如何配置壓縮,混淆,校驗等規則。

    ......

    uj5u.com 2020-09-10 06:59:00 more
  • Android 開發技術周報 Issue#292

    新聞 Android即將獲得類AirDrop功能:可向附近設備快速分享檔案 谷歌為安卓檔案管理應用引入可安全隱藏資料的Safe Folder功能 Android TV新主界面將顯示電影、電視節目和應用推薦內容 泄露的Android檔案暗示了傳說中的谷歌Pixel 5a與折疊屏新機 谷歌發布Andro ......

    uj5u.com 2020-09-10 07:00:37 more
  • AutoFitTextureView Error inflating class

    報錯: Binary XML file line #0: Binary XML file line #0: Error inflating class xxx.AutoFitTextureView 解決: <com.example.testy2.AutoFitTextureView android: ......

    uj5u.com 2020-09-10 07:00:41 more
  • 根據Uri,Cursor沒有獲取到對應的屬性

    Android: 背景:呼叫攝像頭,拍攝視頻,指定保存的地址,但是回傳的Cursor檔案,只有名稱和大小的屬性,沒有其他諸如時長,連ID屬性都沒有 使用 cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DURATIO ......

    uj5u.com 2020-09-10 07:00:44 more
  • Android連載29-持久化技術

    一、持久化技術 我們平時所使用的APP產生的資料,在記憶體中都是瞬時的,會隨著斷電、關機等丟失資料,因此android系統采用了持久化技術,用于存盤這些“瞬時”資料 持久化技術包括:檔案存盤、SharedPreference存盤以及資料庫存盤,還有更復雜的SD卡記憶體儲。 二、檔案存盤 最基本存盤方式, ......

    uj5u.com 2020-09-10 07:00:47 more
  • Android Camera2Video整合到自己專案里

    背景: Android專案里呼叫攝像頭拍攝視頻,原本使用的 MediaStore.ACTION_VIDEO_CAPTURE, 后來因專案需要,改成了camera2 1.Camera2Video 官方demo有點問題,下載后,不能直接整合到專案 問題1.多次拍攝視頻崩潰 問題2.雙擊record按鈕, ......

    uj5u.com 2020-09-10 07:00:50 more
  • Android 開發技術周報 Issue#293

    新聞 谷歌為Android TV開發者提供多種新功能 Android 11將自動填表功能整合到鍵盤輸入建議中 谷歌宣布Android Auto即將支持更多的導航和數字停車應用 谷歌Pixel 5只有XL版本 搭載驍龍765G且將比Pixel 4更便宜 [圖]Wear OS將迎來重磅更新:應用啟動時間 ......

    uj5u.com 2020-09-10 07:01:38 more
  • 海豚星空掃碼投屏 Android 接收端 SDK 集成 六步驟

    掃碼投屏,開放網路,獨占設備,不需要額外下載軟體,微信掃碼,發現設備。支持標準DLNA協議,支持倍速播放。視頻,音頻,圖片投屏。好點意思。還支持自定義基于 DLNA 擴展的操作動作。好像要收費,沒體驗。 這里簡單記錄一下集成程序。 一 跟目錄的build.gradle添加私有mevan倉庫 mave ......

    uj5u.com 2020-09-10 07:01:43 more
最新发布
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:40:31 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:40:11 more
  • 歡迎頁輪播影片

    如圖,引導開始,球從上落下,同時淡入文字,然后文字開始輪播,最后一頁時停止,點擊進入首頁。 在來看看效果圖。 重力球先不講,主要歡迎輪播簡單實作 首先新建一個類 TextTranslationXGuideView,用于影片展示 文本是類似的,最后會有個圖片箭頭影片,布局很簡單,就是一個 TextVi ......

    uj5u.com 2023-04-20 08:39:36 more
  • 【FAQ】關于華為推送服務因營銷訊息頻次管控導致服務通訊類訊息

    一. 問題描述 使用華為推送服務下發IM訊息時,下發訊息請求成功且code碼為80000000,但是手機總是收不到訊息; 在華為推送自助分析(Beta)平臺查看發現,訊息發送觸發了頻控。 二. 問題原因及背景 2023年1月05日起,華為推送服務對咨詢營銷類訊息做了單個設備每日推送數量上限管理,具體 ......

    uj5u.com 2023-04-20 08:39:13 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:16:23 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:16:15 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:15:46 more
  • iOS從UI記憶體地址到讀取成員變數(oc/swift)

    開發除錯時,我們發現bug時常首先是從UI顯示發現例外,下一步才會去定位UI相關連的資料的。XCode有給我們提供一系列debug工具,但是很多人可能還沒有形成一套穩定的除錯流程,因此本文嘗試解決這個問題,順便提出一個暴論:UI顯示例外問題只需要兩個步驟就能完成定位作業的80%: 定位例外 UI 組 ......

    uj5u.com 2023-04-19 09:14:53 more
  • FIDE重磅更新!性能飛躍!體驗有禮!

    FIDE 開發者工具重構升級啦!實作500%性能提升,誠邀體驗! 一直以來不少開發者朋友在社區反饋,在使用 FIDE 工具的程序中,時常會遇到諸如加載不及時、代碼預覽/渲染性能不如意的情況,十分影響開發體驗。 作為技術團隊,我們深知一件趁手的開發工具對開發者的重要性,因此,在2023年開年,FinC ......

    uj5u.com 2023-04-19 09:14:08 more
  • 游戲內嵌社區服務開放,助力開發者提升玩家互動與留存

    華為 HMS Core 游戲內嵌社區服務提供快速訪問華為游戲中心論壇能力,支持玩家直接在游戲內瀏覽帖子和交流互動,助力開發者擴展內容生產和觸達的場景。 一、為什么要游戲內嵌社區? 二、游戲內嵌社區的典型使用場景 1、游戲內打開論壇 您可以在游戲內繪制論壇入口,為玩家提供沉浸式發帖、瀏覽、點贊、回帖、 ......

    uj5u.com 2023-04-19 09:08:34 more