iOS學(xué)習(xí)之深入理解程序編譯過程
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ù)一個簡單的例子來觀察是如何進(jìn)行編譯的
#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在預(yù)處理處理這步做了什么。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;}#這個過程的處理包括宏的替換寝蹈,頭文件的導(dǎo)入李命,以及類似#if的處理。預(yù)處理完成后就會進(jìn)行詞法分析箫老,這里會把代碼切成一個個 Token封字,比如大小括號,等于號還有字符串等槽惫。clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m# 然后是語法分析周叮,驗(yàn)證語法是否正確,然后將所有節(jié)點(diǎn)組成抽象語法樹 AST 界斜。clang -fmodules -fsyntax-only -Xclang -ast-dump main.m# 完成這些步驟后就可以開始IR中間代碼的生成了,CodeGen 會負(fù)責(zé)將語法樹自頂向下遍歷逐步翻譯成 LLVM IR合冀,IR 是編譯過程的前端的輸出后端的輸入各薇。clang -S -fobjc-arc -emit-llvm main.m -o main.ll# 這里 LLVM 會去做些優(yōu)化工作,在 Xcode 的編譯設(shè)置里也可以設(shè)置優(yōu)化級別-01君躺,-03峭判,-0s,還可以寫些自己的 Pass棕叫。# Pass 是 LLVM 優(yōu)化工作的一個節(jié)點(diǎn)林螃,一個節(jié)點(diǎn)做些事,一起加起來就構(gòu)成了 LLVM 完整的優(yōu)化和轉(zhuǎn)化俺泣。# 如果開啟了 bitcode 蘋果會做進(jìn)一步的優(yōu)化疗认,有新的后端架構(gòu)還是可以用這份優(yōu)化過的 bitcode 去生成。clang -emit-llvm -c main.m -o main.bc# 生成匯編clang -S -fobjc-arc main.m -o main.s# 生成目標(biāo)文件clang -fmodules -c main.m -o main.o# 生成可執(zhí)行文件伏钠,這樣就能夠執(zhí)行看到輸出結(jié)果clang main.o -o main# 執(zhí)行./main# 輸出starming rank 14
下面是完整步驟
編譯信息寫入輔助文件横漏,創(chuàng)建文件架構(gòu) .app 文件
處理文件打包信息
執(zhí)行 CocoaPod 編譯前腳本,checkPods Manifest.lock
編譯.m文件熟掂,使用 CompileC 和 clang 命令
鏈接需要的 Framework
編譯 xib
拷貝 xib 缎浇,資源文件
編譯 ImageAssets
處理 info.plist
執(zhí)行 CocoaPod 腳本
拷貝標(biāo)準(zhǔn)庫
創(chuàng)建 .app 文件和簽名
在 Xcode 中查看 clang 編譯 .m 文件的過程
在 Xcode 編譯過后,可以通過 Show the report navigator 里對應(yīng) target 的 build 中查看每個 .m 文件的 clang 編譯信息赴肚∷囟澹可以直接在 help 中搜索 “ Show the report navigator ” 就會出現(xiàn)
使用編譯 Masonry 框架的 MASCompositeConstraint.m 為例沈堡, 首先對任務(wù)進(jìn)行描述
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
更新工作路徑疲牵,同時(shí)設(shè)置 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"
接下來是實(shí)際的編譯命令
先介紹一下 clang 的命令參數(shù),再看??的編譯命令會更容易理解
clang 命令參數(shù)
-x 編譯語言比如objective-c
-arch 編譯的架構(gòu)祠乃,比如arm7
-f 以-f開頭的横朋。
-W 以-W開頭的仑乌,可以通過這些定制編譯警告
-D 以-D開頭的,指的是預(yù)編譯宏,通過這些宏可以實(shí)現(xiàn)條件編譯
-iPhoneSimulator11.1.sdk 編譯采用的iOS SDK版本
-I 把編譯信息寫入指定的輔助文件
-F 需要的Framework
-c 標(biāo)識符指明需要運(yùn)行預(yù)處理器晰甚,語法分析衙传,類型檢查,LLVM生成優(yōu)化以及匯編代碼生成.o文件
-o 編譯結(jié)果
具體的編譯過程
/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
?
編譯完第三方庫后會進(jìn)行構(gòu)建我們程序的 target
Create product structure? ? ? ? ? ? ? ? ? ? ? ? ?
Process product packaging
Run custom shell script 'Check Pods Manifest.lock'
Compile ... 各個項(xiàng)目中的.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
構(gòu)建可執(zhí)行文件的規(guī)則。指定 target 的依賴項(xiàng)目扁远,在 target build 之前需要先 build 的依賴俊鱼。在 Compile Source 中指定所有必須編譯的文件,這些文件會根據(jù) Build Setting 和 Build Rules 里的設(shè)置來處理畅买。
在 Link Binary With Libraries 里會列出所有的靜態(tài)庫和動態(tài)庫并闲,它們會和編譯生成的目標(biāo)文件進(jìn)行鏈接。
build phase 還會把靜態(tài)資源拷貝到 bundle 里谷羞。
可以通過在 build phases 里添加自定義腳本來做些事情帝火,比如像 CocoaPods 所做的那樣。
Bulid Rules
指定不同文件類型如何編譯湃缎。每條 build rule 指定了該類型如何處理以及輸出在哪犀填。可以增加一條新規(guī)則對特定文件類型添加處理方法嗓违。
Bulid Settings
在 build 的過程中各個階段的選項(xiàng)的設(shè)置九巡。
pbxproj 工程文件
* build 過程控制的這些設(shè)置都會被保存在工程文件 .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 指向了可用的配置項(xiàng),包含 Debug 和 Release乏盐〖岩ぃ可以看到還有 buildPhases,buildRules 和 dependencies 都能夠通過這里索引找到更詳細(xì)的定義父能。
? */
? /* 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 */
? ```
編譯后生成的二進(jìn)制內(nèi)容 Link Map File
LinkMapFile
首先來說一說什么是 LinkMap
在iOS開發(fā)領(lǐng)域神凑,LinkMap的輸出是一個純文本格式的文件,里面包含重要的編譯信息及報(bào)錯信息何吝,這也是Apple用來分析你的應(yīng)用的主要方式溉委,通過這種方式可以發(fā)現(xiàn)應(yīng)用中是否使用了私有庫等不符合Apple提交應(yīng)用規(guī)范的內(nèi)容,但對于我們開發(fā)人員爱榕,LinkMap卻是一個用于分析源碼及查看Crash的有效途徑
為什么要使用 LinkMap
當(dāng)一個中大型iOS項(xiàng)目在不斷迭代更新的過程中瓣喊,代碼量日漸壯大,需要重構(gòu)和review的代碼也越來越多黔酥,可一旦代碼達(dá)到一定程度后變得不是那么可控藻三,為了使得項(xiàng)目還可以持續(xù)可集成穩(wěn)健的開發(fā)下去洪橘,縮小iOS安裝包大小是必須要做的事情,通常會從壓縮圖片和音頻文件開始棵帽,使用開發(fā)工具查找冗余不用的資源文件熄求,這一階段之后只能通過對代碼的重構(gòu)來達(dá)到可執(zhí)行文件整體瘦身的效果。當(dāng)從事參與的一個項(xiàng)目在不斷迭代過程中逗概,App的安裝包在不斷變大弟晚,通過自己的shell腳本分析,多達(dá)幾十萬行逾苫,這時(shí)候非常有瘦身的必要卿城,這其中包括了.h.m.mm.cpp.rss格式文件。觀察項(xiàng)目中引入的Pods文件及相關(guān)第三方庫铅搓,多達(dá)上百個庫瑟押,這時(shí)候這樣一個中大型App就涉及到應(yīng)用瘦身的問題,如何才能有效解決代碼不可控的問題狸吞,如何能提高項(xiàng)目中底層基礎(chǔ)架構(gòu)的穩(wěn)定性及健壯性勉耀,相信LinkMap能給予我們一些答案。
LinkMap 的構(gòu)成
App的編譯路徑(#Path)
# Path: /Users/lanya/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Products/Debug/littleTest
App對應(yīng)的架構(gòu)(#Arch)
# Arch: x86_64
App的完整的目標(biāo)文件列表(#Object files)
App的段表(#Section)
App中具體目標(biāo)文件在對應(yīng)的section中的位置和大刑F(#Symbols)
LinkMap服務(wù)的開啟方式及文件目錄
在 Build Settings 里設(shè)置 Write Link Map File 為 Yes 后每次編譯都會在指定目錄生成這樣一個文件。Xcode->Project->Build Settings-> Search map -> 設(shè)置 Write Link Map Files 選項(xiàng)為YES(這里需要注意的是不是設(shè)置Pods.xcodeproj的LinkMap而是xxx-xxxxx.xcodeproj至壤,其他項(xiàng)目也要去設(shè)置主工程的對應(yīng)編譯選項(xiàng)威始,以此類推
文件位于指定的路徑,默認(rèn)是在~/Library/Developer/Xcode/DerivedData/xxx-xxx-fwtuexpkzxsfkjaootcqwizogrhf/Build/Intermediates/xx-xxx.build/Debug-iphonesimulator/xxx-xxx.build/xxx-xxx-LinkMap-normal-x86_64.txt
例如:我的一個項(xiàng)目 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的完整的目標(biāo)文件列表(#Object files): 這個部分的內(nèi)容都是 .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中具體目標(biāo)文件在對應(yīng)的section中的位置和大小(#Symbols):Symbols 是對 Sections 進(jìn)行了再劃分畴栖。這里會描述所有的 methods随静,ivar 和字符串,及它們對應(yīng)的地址吗讶,大小燎猛,文件編號信息。
# 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í)行中通過地址來調(diào)用方法函數(shù)重绷,而 dSYM 文件里存儲了函數(shù)地址映射,這樣調(diào)用棧里的地址可以通過 dSYM 這個映射表能夠獲得具體函數(shù)的位置膜毁。一般都會用來處理 crash 時(shí)獲取到的調(diào)用棧 .crash 文件將其符號化昭卓。
作用: 當(dāng)release的版本 crash的時(shí)候,會有一個日志文件,包含出錯的內(nèi)存地址, 使用symbolicatecrash工具能夠把日志和dSYM文件轉(zhuǎn)換成可以閱讀的log信息,也就是將內(nèi)存地址,轉(zhuǎn)換成程序里的函數(shù)或變量和所屬于的 文件名.(如何設(shè)置 release 版本愤钾? Product -> scheme -> EditScheme)
如何找到:/Users/用戶名/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Products/Release
dSYM崩潰日志的錯誤定位:需要使用 symbolicatecrash 這個 Xcode 自帶的工具進(jìn)行錯誤轉(zhuǎn)換。 找到 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 文件放在同一個目錄下
具體操作請看這篇:總結(jié)的很好
Mach-O 文件
首先來看看胖二進(jìn)制的含義:以上是維基百科的解釋,但是主要來說火焰,胖二進(jìn)制是比普通二進(jìn)制文件的內(nèi)容要多的二進(jìn)制文件劲装,因?yàn)槠渲邪诵枰С植煌珻PU架構(gòu)的iOS設(shè)備的兼容信息。
含義:Mach-O昌简,是Mach object文件格式的縮寫占业,是一種可執(zhí)行文件、目標(biāo)代碼纯赎、共享程序庫谦疾、動態(tài)加載代碼和核心DUMP。是a.out格式的一種替代犬金。Mach-O 提供更多的可擴(kuò)展性和更快的符號表信息存取念恍。Mach-O應(yīng)用在基于Mach核心的系統(tǒng)上,目前NeXTSTEP晚顷、Darwin峰伙、Mac OS X(iPhone)都是使用這種可執(zhí)行文件格式。
記錄編譯后的可執(zhí)行文件该默,對象代碼瞳氓,共享庫,動態(tài)加載代碼和內(nèi)存轉(zhuǎn)儲的文件格式栓袖。不同于 xml 這樣的文件匣摘,它只是二進(jìn)制字節(jié)流,里面有不同的包含元信息的數(shù)據(jù)塊裹刮,比如字節(jié)順序音榜,cpu 類型,塊大小等捧弃。文件內(nèi)容是不可以修改的赠叼,因?yàn)樵?.app 目錄中有個 _CodeSignature 的目錄,里面包含了程序代碼的簽名塔橡,這個簽名的作用就是保證簽名后 .app 里的文件梅割,包括資源文件,Mach-O 文件都不能夠更改葛家。
Mach-O 的內(nèi)容:
Mach-O Header:包含字節(jié)順序户辞,magic,cpu 類型癞谒,加載指令的數(shù)量等
Load Commands:包含很多內(nèi)容的表底燎,包括區(qū)域的位置刃榨,符號表,動態(tài)符號表等双仍。每個加載指令包含一個元信息枢希,比如指令類型,名稱朱沃,在二進(jìn)制中的位置等苞轿。
原始段數(shù)據(jù)(Raw segment data):可以擁有多個段(segment),每個段可以擁有零個或多個區(qū)域(section)逗物。每一個段(segment)都擁有一段虛擬地址映射到進(jìn)程的地址空間搬卒。
先看看描述這個文件的結(jié)構(gòu)體
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ù)這個結(jié)構(gòu)體,需要先取出 magic翎卓,然后根據(jù)偏移量取出其它的信息契邀。遍歷 ncmds 能夠獲得所有的 segment。cputype 包含了 CPU_TYPE_I386失暴,CPU_TYPE_X86_64坯门,CPU_TYPE_ARM,CPU_TYPE_ARM64 等多種 CPU 的類型逗扒。
Mach-O 文件參考文章
dyld動態(tài)鏈接
生成可執(zhí)行文件后就是在啟動時(shí)進(jìn)行動態(tài)鏈接了古戴,進(jìn)行符號和地址的綁定。首先會加載所依賴的 dylibs矩肩,修正地址偏移允瞧,因?yàn)?iOS 會用 ASLR 來做地址偏移避免攻擊,確定 Non-Lazy Pointer 地址進(jìn)行符號地址綁定蛮拔,加載所有類,最后執(zhí)行 load 方法和 clang attribute 的 constructor 修飾函數(shù)痹升。