iOS學習之深入理解程序編譯過程

iOS學習之深入理解程序編譯過程

https://juejin.im/post/5a352bb0f265da433562d5e3

常用的clang命令

clang -rewrite-objc main.m?將obj文件重寫為 c, c++文件

clang -Xclang -ast-dump -fsyntax-only main.m?生成文件生成樹

clang -Xclang -dump-tokens main.m?這里會把代碼切成一個個 Token,比如大小括號,等于號還有字符串等

根據(jù)一個簡單的例子來觀察是如何進行編譯的

#import?

#define?DEFINEEight?8

int?main(){

@autoreleasepool?{

int?eight?=?DEFINEEight;

int?six?=?6;

NSString*?site?=?[[NSString?alloc]?initWithUTF8String:"starming"];

int?rank?=?eight?+?six;

NSLog(@"%@?rank?%d",?site,?rank);

}

return?0;

}

編譯流程

在命令行編譯

xcrun -sdk iphoneos clang -arch armv7 -F Foundation -fobjc-arc -c main.m -o main.oxcrun -sdk iphoneos clang main.o -arch armv7 -fobjc-arc -framework Foundation -o main# 這樣還沒法看清clang的全部過程,可以通過-E查看clang在預處理處理這步做了什么。clang -E main.m# 執(zhí)行完后可以看到文件#1"/System/Library/Frameworks/Foundation.framework/Headers/FoundationLegacySwiftCompatibility.h"1 3#185"/System/Library/Frameworks/Foundation.framework/Headers/Foundation.h"2 3#2"main.m"2int main(){? ? @autoreleasepool {? ? ? ? int eight = 8;? ? ? ? int six = 6;? ? ? ? NSString* site = [[NSString alloc] initWithUTF8String:"starming"];? ? ? ? int rank = eight + six;? ? ? ? NSLog(@"%@ rank %d", site, rank);? ? }? ? return 0;}#這個過程的處理包括宏的替換,頭文件的導入,以及類似#if的處理。預處理完成后就會進行詞法分析,這里會把代碼切成一個個 Token,比如大小括號,等于號還有字符串等。clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m# 然后是語法分析,驗證語法是否正確,然后將所有節(jié)點組成抽象語法樹 AST 。clang -fmodules -fsyntax-only -Xclang -ast-dump main.m# 完成這些步驟后就可以開始IR中間代碼的生成了,CodeGen 會負責將語法樹自頂向下遍歷逐步翻譯成 LLVM IR,IR 是編譯過程的前端的輸出后端的輸入。clang -S -fobjc-arc -emit-llvm main.m -o main.ll# 這里 LLVM 會去做些優(yōu)化工作,在 Xcode 的編譯設置里也可以設置優(yōu)化級別-01,-03,-0s,還可以寫些自己的 Pass。# Pass 是 LLVM 優(yōu)化工作的一個節(jié)點,一個節(jié)點做些事,一起加起來就構成了 LLVM 完整的優(yōu)化和轉化。# 如果開啟了 bitcode 蘋果會做進一步的優(yōu)化,有新的后端架構還是可以用這份優(yōu)化過的 bitcode 去生成。clang -emit-llvm -c main.m -o main.bc# 生成匯編clang -S -fobjc-arc main.m -o main.s# 生成目標文件clang -fmodules -c main.m -o main.o# 生成可執(zhí)行文件,這樣就能夠執(zhí)行看到輸出結果clang main.o -o main# 執(zhí)行./main# 輸出starming rank 14

下面是完整步驟

編譯信息寫入輔助文件,創(chuàng)建文件架構 .app 文件

處理文件打包信息

執(zhí)行 CocoaPod 編譯前腳本,checkPods Manifest.lock

編譯.m文件,使用 CompileC 和 clang 命令

鏈接需要的 Framework

編譯 xib

拷貝 xib ,資源文件

編譯 ImageAssets

處理 info.plist

執(zhí)行 CocoaPod 腳本

拷貝標準庫

創(chuàng)建 .app 文件和簽名

在 Xcode 中查看 clang 編譯 .m 文件的過程

在 Xcode 編譯過后,可以通過 Show the report navigator 里對應 target 的 build 中查看每個 .m 文件的 clang 編譯信息??梢灾苯釉?help 中搜索 “ Show the report navigator ” 就會出現(xiàn)

使用編譯 Masonry 框架的 MASCompositeConstraint.m 為例, 首先對任務進行描述

CompileC /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Objects-normal/x86_64/MASCompositeConstraint.o

Masonry/Masonry/MASCompositeConstraint.m normal x86_64 objective-c com.apple.compilers.llvm.clang.1_0.compiler

更新工作路徑,同時設置 PATH

cd /Users/lanya/Desktop/Neuer_iOS/Pods

? ? export LANG=en_US.US-ASCII

? ? export PATH="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/usr/bin:/Applications/Xcode.app/Contents/Developer/usr/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"

接下來是實際的編譯命令

先介紹一下 clang 的命令參數(shù),再看??的編譯命令會更容易理解

clang 命令參數(shù)

-x 編譯語言比如objective-c

-arch 編譯的架構,比如arm7

-f 以-f開頭的。

-W 以-W開頭的,可以通過這些定制編譯警告

-D 以-D開頭的,指的是預編譯宏,通過這些宏可以實現(xiàn)條件編譯

-iPhoneSimulator11.1.sdk 編譯采用的iOS SDK版本

-I 把編譯信息寫入指定的輔助文件

-F 需要的Framework

-c 標識符指明需要運行預處理器,語法分析,類型檢查,LLVM生成優(yōu)化以及匯編代碼生成.o文件

-o 編譯結果

具體的編譯過程

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang -x objective-c -arch x86_64 -fmessage-length=0 -fdiagnostics-show-note-include-stack -fmacro-backtrace-limit=0 -std=gnu11 -fobjc-arc -fmodules -gmodules -fmodules-cache-path=/Users/lanya/Library/Developer/Xcode/DerivedData/ModuleCache -fmodules-prune-interval=86400 -fmodules-prune-after=345600 -fbuild-session-file=/Users/lanya/Library/Developer/Xcode/DerivedData/ModuleCache/Session.modulevalidation -fmodules-validate-once-per-build-session -Wnon-modular-include-in-framework-module -Werror=non-modular-include-in-framework-module -fmodule-name=Masonry -fapplication-extension -Wno-trigraphs -fpascal-strings -O0 -fno-common -Wno-missing-field-initializers -Wno-missing-prototypes -Werror=return-type -Wdocumentation -Wunreachable-code -Wno-implicit-atomic-properties -Werror=deprecated-objc-isa-usage -Werror=objc-root-class -Wno-arc-repeated-use-of-weak -Wduplicate-method-match -Wno-missing-braces -Wparentheses -Wswitch -Wunused-function -Wno-unused-label -Wno-unused-parameter -Wunused-variable -Wunused-value -Wempty-body -Wuninitialized -Wconditional-uninitialized -Wno-unknown-pragmas -Wno-shadow -Wno-four-char-constants -Wno-conversion -Wconstant-conversion -Wint-conversion -Wbool-conversion -Wenum-conversion -Wno-float-conversion -Wnon-literal-null-conversion -Wobjc-literal-conversion -Wshorten-64-to-32 -Wpointer-sign -Wno-newline-eof -Wno-selector -Wno-strict-selector-match -Wundeclared-selector -Wno-deprecated-implementations -DPOD_CONFIGURATION_DEBUG=1 -DDEBUG=1 -DCOCOAPODS=1 -DOBJC_OLD_DISPATCH_PROTOTYPES=0 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator11.2.sdk -fasm-blocks -fstrict-aliasing -Wprotocol -Wdeprecated-declarations -mios-simulator-version-min=8.0 -g -Wno-sign-conversion -Winfinite-recursion -Wcomma -Wblock-capture-autoreleasing -Wstrict-prototypes -Wunguarded-availability -fobjc-abi-version=2 -fobjc-legacy-dispatch -index-store-path /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Index/DataStore -iquote /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Masonry-generated-files.hmap -I/Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Masonry-own-target-headers.hmap -I/Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Masonry-all-non-framework-target-headers.hmap -ivfsoverlay /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/all-product-headers.yaml -iquote /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Masonry-project-headers.hmap -I/Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Products/Debug-iphonesimulator/Masonry/include -I/Users/lanya/Desktop/Neuer_iOS/Pods/Headers/Private -I/Users/lanya/Desktop/Neuer_iOS/Pods/Headers/Public -I/Users/lanya/Desktop/Neuer_iOS/Pods/Headers/Public/PgyUpdate -I/Users/lanya/Desktop/Neuer_iOS/Pods/Headers/Public/Pgyer -I/Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/DerivedSources/x86_64 -I/Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/DerivedSources -F/Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Products/Debug-iphonesimulator/Masonry -include /Users/lanya/Desktop/Neuer_iOS/Pods/Target\ Support\ Files/Masonry/Masonry-prefix.pch -MMD -MT dependencies -MF /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Objects-normal/x86_64/MASCompositeConstraint.d --serialize-diagnostics /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Objects-normal/x86_64/MASCompositeConstraint.dia -c /Users/lanya/Desktop/Neuer_iOS/Pods/Masonry/Masonry/MASCompositeConstraint.m -o /Users/lanya/Library/Developer/Xcode/DerivedData/NEUer-bjvoyplxzoxgkpgkiodfvurkgzwn/Build/Intermediates.noindex/Pods.build/Debug-iphonesimulator/Masonry.build/Objects-normal/x86_64/MASCompositeConstraint.o

?

編譯完第三方庫后會進行構建我們程序的 target

Create product structure? ? ? ? ? ? ? ? ? ? ? ? ?

Process product packaging

Run custom shell script 'Check Pods Manifest.lock'

Compile ... 各個項目中的.m文件

Link /Users/... 路徑

Copy ... 靜態(tài)文件

Compile asset catalogs

Compile Storyboard file ...

Process info.plist

Link Storyboards

Run custom shell script 'Embed Pods Frameworks'

Run custom shell script 'Copy Pods Resources'

...

Touch NEUer.app

Sign NEUer.app

Target 在 Build 過程的控制

在 Xcode 的 Project editor 中的 Build Setting,Build Phases 和 Build Rules 能夠控制編譯的過程。

Build Phases

構建可執(zhí)行文件的規(guī)則。指定 target 的依賴項目,在 target build 之前需要先 build 的依賴。在 Compile Source 中指定所有必須編譯的文件,這些文件會根據(jù) Build Setting 和 Build Rules 里的設置來處理。

在 Link Binary With Libraries 里會列出所有的靜態(tài)庫和動態(tài)庫,它們會和編譯生成的目標文件進行鏈接。

build phase 還會把靜態(tài)資源拷貝到 bundle 里。

可以通過在 build phases 里添加自定義腳本來做些事情,比如像 CocoaPods 所做的那樣。

Bulid Rules

指定不同文件類型如何編譯。每條 build rule 指定了該類型如何處理以及輸出在哪??梢栽黾右粭l新規(guī)則對特定文件類型添加處理方法。

Bulid Settings

在 build 的過程中各個階段的選項的設置。

pbxproj 工程文件

* build 過程控制的這些設置都會被保存在工程文件 .pbxproj 里。在這個文件中可以找 rootObject 的 ID 值

* 然后根據(jù)這個 ID 找到 main 工程的定義。

? ```objective-c

? /* Begin PBXProject section */

? ? ? ? ? 2EC5E1AA1E7814B200BAB0EF /* Project object */ = {

? isa = PBXProject;

? ......

? /* End PBXProject section */

? ```

* 在 targets 里會指向各個 taget 的定義

? ```objective-c

? targets = (

? 2EC5E1B11E7814B200BAB0EF /* EWork */,

? );

? // 根據(jù) 2EC5E1B11E7814B200BAB0EF 可以找到具體各個的定義

? /**

? 這個里面又有更多的 ID 可以得到更多的定義,其中 buildConfigurationList 指向了可用的配置項,包含 Debug 和 Release??梢钥吹竭€有 buildPhases,buildRules 和 dependencies 都能夠通過這里索引找到更詳細的定義。

? */

? /* Begin PBXNativeTarget section */

? 2EC5E1B11E7814B200BAB0EF /* EWork */ = {

? isa = PBXNativeTarget;

? buildConfigurationList = 2EC5E1CC1E7814B200BAB0EF /* Build configuration list for PBXNativeTarget "EWork" */;

? buildPhases = (

? 73F5AAE2AEC5EE766978C0E2 /* [CP] Check Pods Manifest.lock */,

? 2EC5E1AE1E7814B200BAB0EF /* Sources */,

? 2EC5E1AF1E7814B200BAB0EF /* Frameworks */,

? 2EC5E1B01E7814B200BAB0EF /* Resources */,

? B42D03564A9A71BAD7183E61 /* [CP] Embed Pods Frameworks */,

? 4672989246AFA7B2776DFA56 /* [CP] Copy Pods Resources */,

? );

? buildRules = (

? );

? dependencies = (

? );

? name = EWork;

? productName = EWork;

? productReference = 2EC5E1B21E7814B200BAB0EF /* EWork.app */;

? productType = "com.apple.product-type.application";

? };

? /* End PBXNativeTarget section */

? ? // 比如 XCConfigurationList

? ? /* Begin XCConfigurationList section */

? ? 2EC5E1AD1E7814B200BAB0EF /* Build configuration list for PBXProject "EWork" */ = {

? ? isa = XCConfigurationList;

? ? buildConfigurations = (

? ? 2EC5E1CA1E7814B200BAB0EF /* Debug */,

? ? 2EC5E1CB1E7814B200BAB0EF /* Release */,

? ? );

? ? defaultConfigurationIsVisible = 0;

? ? defaultConfigurationName = Release;

? ? };

? ? 2EC5E1CC1E7814B200BAB0EF /* Build configuration list for PBXNativeTarget "EWork" */ = {

? ? isa = XCConfigurationList;

? ? buildConfigurations = (

? ? 2EC5E1CD1E7814B200BAB0EF /* Debug */,

? ? 2EC5E1CE1E7814B200BAB0EF /* Release */,

? ? );

? ? defaultConfigurationIsVisible = 0;

? ? defaultConfigurationName = Release;

? ? };

? ? /* End XCConfigurationList section */

? ```

編譯后生成的二進制內容 Link Map File

應用沙盒路徑的獲取

LinkMapFile

首先來說一說什么是 LinkMap

在iOS開發(fā)領域,LinkMap的輸出是一個純文本格式的文件,里面包含重要的編譯信息及報錯信息,這也是Apple用來分析你的應用的主要方式,通過這種方式可以發(fā)現(xiàn)應用中是否使用了私有庫等不符合Apple提交應用規(guī)范的內容,但對于我們開發(fā)人員,LinkMap卻是一個用于分析源碼及查看Crash的有效途徑

為什么要使用 LinkMap

當一個中大型iOS項目在不斷迭代更新的過程中,代碼量日漸壯大,需要重構和review的代碼也越來越多,可一旦代碼達到一定程度后變得不是那么可控,為了使得項目還可以持續(xù)可集成穩(wěn)健的開發(fā)下去,縮小iOS安裝包大小是必須要做的事情,通常會從壓縮圖片和音頻文件開始,使用開發(fā)工具查找冗余不用的資源文件,這一階段之后只能通過對代碼的重構來達到可執(zhí)行文件整體瘦身的效果。當從事參與的一個項目在不斷迭代過程中,App的安裝包在不斷變大,通過自己的shell腳本分析,多達幾十萬行,這時候非常有瘦身的必要,這其中包括了.h.m.mm.cpp.rss格式文件。觀察項目中引入的Pods文件及相關第三方庫,多達上百個庫,這時候這樣一個中大型App就涉及到應用瘦身的問題,如何才能有效解決代碼不可控的問題,如何能提高項目中底層基礎架構的穩(wěn)定性及健壯性,相信LinkMap能給予我們一些答案。

LinkMap 的構成

App的編譯路徑(#Path)

# Path: /Users/lanya/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Products/Debug/littleTest

App對應的架構(#Arch)

# Arch: x86_64

App的完整的目標文件列表(#Object files)

App的段表(#Section)

App中具體目標文件在對應的section中的位置和大小(#Symbols)

LinkMap服務的開啟方式及文件目錄

在 Build Settings 里設置 Write Link Map File 為 Yes 后每次編譯都會在指定目錄生成這樣一個文件。Xcode->Project->Build Settings-> Search map -> 設置 Write Link Map Files 選項為YES(這里需要注意的是不是設置Pods.xcodeproj的LinkMap而是xxx-xxxxx.xcodeproj,其他項目也要去設置主工程的對應編譯選項,以此類推

文件位于指定的路徑,默認是在~/Library/Developer/Xcode/DerivedData/xxx-xxx-fwtuexpkzxsfkjaootcqwizogrhf/Build/Intermediates/xx-xxx.build/Debug-iphonesimulator/xxx-xxx.build/xxx-xxx-LinkMap-normal-x86_64.txt

例如:我的一個項目 LitteleTest:/Users/lanya/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Intermediates.noindex/littleTest.build/Debug/littleTest.build/littleTest-LinkMap-normal-x86_64.txt

現(xiàn)在來說一說 LinkMap 各部分的作用

App的完整的目標文件列表(#Object files): 這個部分的內容都是 .m 文件編譯后的 .o 和需要 link 的 .a 文件。前面是文件編號,后面是文件路徑。

# Object files:[? 0] linker synthesized? ? [? 1] /Users/lanya/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Intermediates.noindex/littleTest.build/Debug/littleTest.build/Objects-normal/x86_64/main.o? ? [? 2] /Users/lanya/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Intermediates.noindex/littleTest.build/Debug/littleTest.build/Objects-normal/x86_64/Test.o? ? [? 3] /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/System/Library/Frameworks//Foundation.framework/Foundation.tbd? ? [? 4] /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.13.sdk/usr/lib/libobjc.tbd

App的段表(#Section):這里描述的是每個 Section 在可執(zhí)行文件中的位置和大小。每個 Section 的 Segment 的類型分為 __TEXT 代碼段和 __DATA 數(shù)據(jù)段兩種。

? # Sections:# AddressSize? ? SegmentSection? ? 0x100000B100x000002D9__TEXT__text? ? 0x100000DEA0x00000054__TEXT__stubs? ? 0x100000E400x0000009C__TEXT__stub_helper? ? 0x100000EDC0x0000006E__TEXT__objc_methname? ? 0x100000F4A0x0000003B__TEXT__cstring? ? 0x100000F850x00000007__TEXT__objc_classname? ? 0x100000F8C0x0000001D__TEXT__objc_methtype? ? 0x100000FAC0x00000048__TEXT__unwind_info? ? 0x1000010000x00000010__DATA__nl_symbol_ptr? ? 0x1000010100x00000070__DATA__la_symbol_ptr? ? 0x1000010800x00000060__DATA__cfstring? ? 0x1000010E00x00000008__DATA__objc_classlist? ? 0x1000010E80x00000008__DATA__objc_imageinfo? ? 0x1000010F00x00000170__DATA__objc_const? ? 0x1000012600x00000020__DATA__objc_selrefs? ? 0x1000012800x00000008__DATA__objc_classrefs? ? 0x1000012880x00000008__DATA__objc_superrefs? ? 0x1000012900x00000010__DATA__objc_ivar? ? 0x1000012A00x00000050__DATA__objc_data

App中具體目標文件在對應的section中的位置和大小(#Symbols):Symbols 是對 Sections 進行了再劃分。這里會描述所有的 methods,ivar 和字符串,及它們對應的地址,大小,文件編號信息。

# Symbols:# AddressSize? ? File? Name? ? 0x100000B100x00000106[? 1] _main? ? 0x100000C200x000000A0[? 2] -[Test init]? ? 0x100000CC00x00000060[? 2] -[Test setObject:]? ? 0x100000D200x00000040[? 2] -[Test obj]? ? 0x100000D600x00000040[? 2] -[Test setObj:]? ? 0x100000DA00x00000049[? 2] -[Test .cxx_destruct]? ? 0x100000DEA0x00000006[? 3] _NSHomeDirectory? ? 0x100000DF00x00000006[? 3] _NSLog? ? 0x100000DF60x00000006[? 4] _objc_autoreleasePoolPop? ? 0x100000DFC0x00000006[? 4] _objc_autoreleasePoolPush? ? 0x100000E020x00000006[? 4] _objc_autoreleaseReturnValue? ? 0x100000E080x00000006[? 4] _objc_destroyWeak? ? 0x100000E0E0x00000006[? 4] _objc_loadWeakRetained? ? 0x100000E140x00000006[? 4] _objc_msgSend? ? 0x100000E1A0x00000006[? 4] _objc_msgSendSuper2? ? 0x100000E200x00000006[? 4] _objc_release? ? 0x100000E260x00000006[? 4] _objc_retain? ? 0x100000E2C0x00000006[? 4] _objc_retainAutoreleasedReturnValue? ? 0x100000E320x00000006[? 4] _objc_storeStrong? ? 0x100000E380x00000006[? 4] _objc_storeWeak? ? 0x100000E400x00000010[? 0] helper helper? ? 0x100000E500x0000000A[? 3] _NSHomeDirectory? ? 0x100000E5A0x0000000A[? 3] _NSLog? ? 0x100000E640x0000000A[? 4] _objc_autoreleasePoolPop? ? 0x100000E6E0x0000000A[? 4] _objc_autoreleasePoolPush? ? 0x100000E780x0000000A[? 4] _objc_autoreleaseReturnValue? ? 0x100000E820x0000000A[? 4] _objc_destroyWeak? ? 0x100000E8C0x0000000A[? 4] _objc_loadWeakRetained? ? 0x100000E960x0000000A[? 4] _objc_msgSend? ? 0x100000EA00x0000000A[? 4] _objc_msgSendSuper2? ? 0x100000EAA0x0000000A[? 4] _objc_release? ? 0x100000EB40x0000000A[? 4] _objc_retain? ? 0x100000EBE0x0000000A[? 4] _objc_retainAutoreleasedReturnValue? ? 0x100000EC80x0000000A[? 4] _objc_storeStrong? ? 0x100000ED20x0000000A[? 4] _objc_storeWeak? ? 0x100000EDC0x00000006[? 1] literal string: alloc? ? 0x100000EE20x00000014[? 1] literal string: initWithUTF8String:? ? 0x100000EF60x00000020[? 1] literal string: stringByAppendingPathComponent:? ? 0x100000F160x00000005[? 2] literal string: init? ? 0x100000F1B0x0000000B[? 2] literal string: setObject:? ? 0x100000F260x0000000E[? 2] literal string: .cxx_destruct? ? 0x100000F340x00000004[? 2] literal string: obj? ? 0x100000F380x00000008[? 2] literal string: setObj:? ? 0x100000F400x00000005[? 2] literal string: obj_? ? 0x100000F450x00000005[? 2] literal string: _obj? ? 0x100000F4A0x00000009[? 1] literal string: starming? ? 0x100000F530x0000000B[? 1] literal string: %@ rank %d? ? 0x100000F5E0x00000013[? 1] literal string: Documents/neuer.db? ? 0x100000F710x00000003[? 1] literal string: %@? ? 0x100000F740x00000004[? 2] literal string: obj? ? 0x100000F780x0000000D[? 2] literal string: T@,W,N,V_obj? ? 0x100000F850x00000005[? 2] literal string: Test? ? 0x100000F8A0x00000002[? 2] literal string:? ? 0x100000F8C0x00000008[? 2] literal string: @16@0:8? ? 0x100000F940x0000000B[? 2] literal string: v24@0:8@16? ? 0x100000F9F0x00000008[? 2] literal string: v16@0:8? ? 0x100000FA70x00000002[? 2] literal string: @? ? 0x100000FAC0x00000048[? 0] compact unwind info? ? 0x1000010000x00000008[? 0] non-lazy-pointer-to-local: dyld_stub_binder? ? 0x1000010080x00000008[? 0] non-lazy-pointer? ? 0x1000010100x00000008[? 3] _NSHomeDirectory? ? 0x1000010180x00000008[? 3] _NSLog? ? 0x1000010200x00000008[? 4] _objc_autoreleasePoolPop? ? 0x1000010280x00000008[? 4] _objc_autoreleasePoolPush? ? 0x1000010300x00000008[? 4] _objc_autoreleaseReturnValue? ? 0x1000010380x00000008[? 4] _objc_destroyWeak? ? 0x1000010400x00000008[? 4] _objc_loadWeakRetained? ? 0x1000010480x00000008[? 4] _objc_msgSend? ? 0x1000010500x00000008[? 4] _objc_msgSendSuper2? ? 0x1000010580x00000008[? 4] _objc_release? ? 0x1000010600x00000008[? 4] _objc_retain? ? 0x1000010680x00000008[? 4] _objc_retainAutoreleasedReturnValue? ? 0x1000010700x00000008[? 4] _objc_storeStrong? ? 0x1000010780x00000008[? 4] _objc_storeWeak? ? 0x1000010800x00000020[? 1] CFString? ? 0x1000010A00x00000020[? 1] CFString? ? 0x1000010C00x00000020[? 1] CFString? ? 0x1000010E00x00000008[? 2] anon? ? 0x1000010E80x00000008[? 0] objc image info? ? 0x1000010F00x00000048[? 2] l_OBJC_METACLASS_RO_$_Test? ? 0x1000011380x00000080[? 2] l_OBJC_$_INSTANCE_METHODS_Test? ? 0x1000011B80x00000048[? 2] l_OBJC_$_INSTANCE_VARIABLES_Test? ? 0x1000012000x00000018[? 2] l_OBJC_$_PROP_LIST_Test? ? 0x1000012180x00000048[? 2] l_OBJC_CLASS_RO_$_Test? ? 0x1000012600x00000008[? 1] pointer-to-literal-cstring? ? 0x1000012680x00000008[? 1] pointer-to-literal-cstring? ? 0x1000012700x00000008[? 1] pointer-to-literal-cstring? ? 0x1000012780x00000008[? 2] pointer-to-literal-cstring? ? 0x1000012800x00000008[? 1] objc-class-ref? ? 0x1000012880x00000008[? 2] anon? ? 0x1000012900x00000008[? 2] _OBJC_IVAR_$_Test.obj_? ? 0x1000012980x00000008[? 2] _OBJC_IVAR_$_Test._obj? ? 0x1000012A00x00000028[? 2] _OBJC_CLASS_$_Test? ? 0x1000012C80x00000028[? 2] _OBJC_METACLASS_$_Test

dSYM

定義:在每次編譯后都會生成一個 dSYM 文件,程序在執(zhí)行中通過地址來調用方法函數(shù),而 dSYM 文件里存儲了函數(shù)地址映射,這樣調用棧里的地址可以通過 dSYM 這個映射表能夠獲得具體函數(shù)的位置。一般都會用來處理 crash 時獲取到的調用棧 .crash 文件將其符號化。

作用: 當release的版本 crash的時候,會有一個日志文件,包含出錯的內存地址, 使用symbolicatecrash工具能夠把日志和dSYM文件轉換成可以閱讀的log信息,也就是將內存地址,轉換成程序里的函數(shù)或變量和所屬于的 文件名.(如何設置 release 版本? Product -> scheme -> EditScheme)

如何找到:/Users/用戶名/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Products/Release

dSYM崩潰日志的錯誤定位:需要使用 symbolicatecrash 這個 Xcode 自帶的工具進行錯誤轉換。 找到 symbolicatecrash :?find /Applications/Xcode.app -name symbolicatecrash -type f

找到位置為:?/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/Library/PrivateFrameworks/DVTFoundation.framework/symbolicatecrash

之后將 symbolicatecrash, crash, dSYM 文件放在同一個目錄下

具體操作請看這篇:總結的很好

Mach-O 文件

首先來看看胖二進制的含義:以上是維基百科的解釋,但是主要來說,胖二進制是比普通二進制文件的內容要多的二進制文件,因為其中包含了需要支持不同CPU架構的iOS設備的兼容信息。

含義:Mach-O,是Mach object文件格式的縮寫,是一種可執(zhí)行文件、目標代碼、共享程序庫、動態(tài)加載代碼和核心DUMP。是a.out格式的一種替代。Mach-O 提供更多的可擴展性和更快的符號表信息存取。Mach-O應用在基于Mach核心的系統(tǒng)上,目前NeXTSTEP、Darwin、Mac OS X(iPhone)都是使用這種可執(zhí)行文件格式。

記錄編譯后的可執(zhí)行文件,對象代碼,共享庫,動態(tài)加載代碼和內存轉儲的文件格式。不同于 xml 這樣的文件,它只是二進制字節(jié)流,里面有不同的包含元信息的數(shù)據(jù)塊,比如字節(jié)順序,cpu 類型,塊大小等。文件內容是不可以修改的,因為在 .app 目錄中有個 _CodeSignature 的目錄,里面包含了程序代碼的簽名,這個簽名的作用就是保證簽名后 .app 里的文件,包括資源文件,Mach-O 文件都不能夠更改。

Mach-O 的內容:

Mach-O Header:包含字節(jié)順序,magic,cpu 類型,加載指令的數(shù)量等

Load Commands:包含很多內容的表,包括區(qū)域的位置,符號表,動態(tài)符號表等。每個加載指令包含一個元信息,比如指令類型,名稱,在二進制中的位置等。

原始段數(shù)據(jù)(Raw segment data):可以擁有多個段(segment),每個段可以擁有零個或多個區(qū)域(section)。每一個段(segment)都擁有一段虛擬地址映射到進程的地址空間。

先看看描述這個文件的結構體

structmach_header{uint32_tmagic;cpu_type_tcputype;cpu_subtype_tcpusubtype;uint32_tfiletype;uint32_tncmds;uint32_tsizeofcmds;uint32_tflags;};structsegment_command{uint32_tcmd;uint32_tcmdsize;charsegname[16];uint32_tvmaddr;uint32_tvmsize;uint32_tfileoff;uint32_tfilesize;vm_prot_tmaxprot;vm_prot_tinitprot;uint32_tnsects;uint32_tflags;};

根據(jù)這個結構體,需要先取出 magic,然后根據(jù)偏移量取出其它的信息。遍歷 ncmds 能夠獲得所有的 segment。cputype 包含了 CPU_TYPE_I386,CPU_TYPE_X86_64,CPU_TYPE_ARM,CPU_TYPE_ARM64 等多種 CPU 的類型。

Mach-O 文件參考文章

Mach-O 具體定義

趣談 Mach-O加載過程

dyld動態(tài)鏈接

生成可執(zhí)行文件后就是在啟動時進行動態(tài)鏈接了,進行符號和地址的綁定。首先會加載所依賴的 dylibs,修正地址偏移,因為 iOS 會用 ASLR 來做地址偏移避免攻擊,確定 Non-Lazy Pointer 地址進行符號地址綁定,加載所有類,最后執(zhí)行 load 方法和 clang attribute 的 constructor 修飾函數(shù)。

?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

友情鏈接更多精彩內容