常用的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 <Foundation/Foundation.h> #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.o xcrun -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" 2 int 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 # 然后是語法分析,驗證語法是否正確,然后將所有節(jié)點組成抽象語法樹 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é)點,一個節(jié)點做些事,一起加起來就構(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)
在Xcode中查看編譯過程.png
-
使用編譯 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è)置 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 編譯的架構(gòu),比如arm7 -f 以-f開頭的。 -W 以-W開頭的,可以通過這些定制編譯警告 -D 以-D開頭的,指的是預(yù)編譯宏,通過這些宏可以實現(xiàn)條件編譯 -iPhoneSimulator11.1.sdk 編譯采用的iOS SDK版本 -I 把編譯信息寫入指定的輔助文件 -F 需要的Framework -c 標(biāo)識符指明需要運行預(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 ... 各個項目中的.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 的依賴項目,在 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 的過程中各個階段的選項的設(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 指向了可用的配置項,包含 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的輸出是一個純文本格式的文件,里面包含重要的編譯信息及報錯信息,這也是Apple用來分析你的應(yīng)用的主要方式,通過這種方式可以發(fā)現(xiàn)應(yīng)用中是否使用了私有庫等不符合Apple提交應(yīng)用規(guī)范的內(nèi)容,但對于我們開發(fā)人員,LinkMap卻是一個用于分析源碼及查看Crash的有效途徑
-
為什么要使用 LinkMap
當(dāng)一個中大型iOS項目在不斷迭代更新的過程中,代碼量日漸壯大,需要重構(gòu)和review的代碼也越來越多,可一旦代碼達(dá)到一定程度后變得不是那么可控,為了使得項目還可以持續(xù)可集成穩(wěn)健的開發(fā)下去,縮小iOS安裝包大小是必須要做的事情,通常會從壓縮圖片和音頻文件開始,使用開發(fā)工具查找冗余不用的資源文件,這一階段之后只能通過對代碼的重構(gòu)來達(dá)到可執(zhí)行文件整體瘦身的效果。當(dāng)從事參與的一個項目在不斷迭代過程中,App的安裝包在不斷變大,通過自己的shell腳本分析,多達(dá)幾十萬行,這時候非常有瘦身的必要,這其中包括了.h.m.mm.cpp.rss格式文件。觀察項目中引入的Pods文件及相關(guān)第三方庫,多達(dá)上百個庫,這時候這樣一個中大型App就涉及到應(yī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中的位置和大?。?Symbols)
- App的編譯路徑(#Path)
-
LinkMap服務(wù)的開啟方式及文件目錄
- 在 Build Settings 里設(shè)置 Write Link Map File 為 Yes 后每次編譯都會在指定目錄生成這樣一個文件。
Xcode->Project->Build Settings-> Search map -> 設(shè)置 Write Link Map Files 選項為YES(這里需要注意的是不是設(shè)置Pods.xcodeproj的LinkMap而是xxx-xxxxx.xcodeproj,其他項目也要去設(shè)置主工程的對應(yī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 - 例如:我的一個項目 LitteleTest:
/Users/lanya/Library/Developer/Xcode/DerivedData/littleTest-fcueraakmygtmodagachcreqjbjw/Build/Intermediates.noindex/littleTest.build/Debug/littleTest.build/littleTest-LinkMap-normal-x86_64.txt
- 在 Build Settings 里設(shè)置 Write Link Map File 為 Yes 后每次編譯都會在指定目錄生成這樣一個文件。
-
現(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: # Address Size Segment Section 0x100000B10 0x000002D9 __TEXT __text 0x100000DEA 0x00000054 __TEXT __stubs 0x100000E40 0x0000009C __TEXT __stub_helper 0x100000EDC 0x0000006E __TEXT __objc_methname 0x100000F4A 0x0000003B __TEXT __cstring 0x100000F85 0x00000007 __TEXT __objc_classname 0x100000F8C 0x0000001D __TEXT __objc_methtype 0x100000FAC 0x00000048 __TEXT __unwind_info 0x100001000 0x00000010 __DATA __nl_symbol_ptr 0x100001010 0x00000070 __DATA __la_symbol_ptr 0x100001080 0x00000060 __DATA __cfstring 0x1000010E0 0x00000008 __DATA __objc_classlist 0x1000010E8 0x00000008 __DATA __objc_imageinfo 0x1000010F0 0x00000170 __DATA __objc_const 0x100001260 0x00000020 __DATA __objc_selrefs 0x100001280 0x00000008 __DATA __objc_classrefs 0x100001288 0x00000008 __DATA __objc_superrefs 0x100001290 0x00000010 __DATA __objc_ivar 0x1000012A0 0x00000050 __DATA __objc_data -
App中具體目標(biāo)文件在對應(yīng)的section中的位置和大?。?Symbols):Symbols 是對 Sections 進(jìn)行了再劃分。這里會描述所有的 methods,ivar 和字符串,及它們對應(yīng)的地址,大小,文件編號信息。
# Symbols: # Address Size File Name 0x100000B10 0x00000106 [ 1] _main 0x100000C20 0x000000A0 [ 2] -[Test init] 0x100000CC0 0x00000060 [ 2] -[Test setObject:] 0x100000D20 0x00000040 [ 2] -[Test obj] 0x100000D60 0x00000040 [ 2] -[Test setObj:] 0x100000DA0 0x00000049 [ 2] -[Test .cxx_destruct] 0x100000DEA 0x00000006 [ 3] _NSHomeDirectory 0x100000DF0 0x00000006 [ 3] _NSLog 0x100000DF6 0x00000006 [ 4] _objc_autoreleasePoolPop 0x100000DFC 0x00000006 [ 4] _objc_autoreleasePoolPush 0x100000E02 0x00000006 [ 4] _objc_autoreleaseReturnValue 0x100000E08 0x00000006 [ 4] _objc_destroyWeak 0x100000E0E 0x00000006 [ 4] _objc_loadWeakRetained 0x100000E14 0x00000006 [ 4] _objc_msgSend 0x100000E1A 0x00000006 [ 4] _objc_msgSendSuper2 0x100000E20 0x00000006 [ 4] _objc_release 0x100000E26 0x00000006 [ 4] _objc_retain 0x100000E2C 0x00000006 [ 4] _objc_retainAutoreleasedReturnValue 0x100000E32 0x00000006 [ 4] _objc_storeStrong 0x100000E38 0x00000006 [ 4] _objc_storeWeak 0x100000E40 0x00000010 [ 0] helper helper 0x100000E50 0x0000000A [ 3] _NSHomeDirectory 0x100000E5A 0x0000000A [ 3] _NSLog 0x100000E64 0x0000000A [ 4] _objc_autoreleasePoolPop 0x100000E6E 0x0000000A [ 4] _objc_autoreleasePoolPush 0x100000E78 0x0000000A [ 4] _objc_autoreleaseReturnValue 0x100000E82 0x0000000A [ 4] _objc_destroyWeak 0x100000E8C 0x0000000A [ 4] _objc_loadWeakRetained 0x100000E96 0x0000000A [ 4] _objc_msgSend 0x100000EA0 0x0000000A [ 4] _objc_msgSendSuper2 0x100000EAA 0x0000000A [ 4] _objc_release 0x100000EB4 0x0000000A [ 4] _objc_retain 0x100000EBE 0x0000000A [ 4] _objc_retainAutoreleasedReturnValue 0x100000EC8 0x0000000A [ 4] _objc_storeStrong 0x100000ED2 0x0000000A [ 4] _objc_storeWeak 0x100000EDC 0x00000006 [ 1] literal string: alloc 0x100000EE2 0x00000014 [ 1] literal string: initWithUTF8String: 0x100000EF6 0x00000020 [ 1] literal string: stringByAppendingPathComponent: 0x100000F16 0x00000005 [ 2] literal string: init 0x100000F1B 0x0000000B [ 2] literal string: setObject: 0x100000F26 0x0000000E [ 2] literal string: .cxx_destruct 0x100000F34 0x00000004 [ 2] literal string: obj 0x100000F38 0x00000008 [ 2] literal string: setObj: 0x100000F40 0x00000005 [ 2] literal string: obj_ 0x100000F45 0x00000005 [ 2] literal string: _obj 0x100000F4A 0x00000009 [ 1] literal string: starming 0x100000F53 0x0000000B [ 1] literal string: %@ rank %d 0x100000F5E 0x00000013 [ 1] literal string: Documents/neuer.db 0x100000F71 0x00000003 [ 1] literal string: %@ 0x100000F74 0x00000004 [ 2] literal string: obj 0x100000F78 0x0000000D [ 2] literal string: T@,W,N,V_obj 0x100000F85 0x00000005 [ 2] literal string: Test 0x100000F8A 0x00000002 [ 2] literal string: 0x100000F8C 0x00000008 [ 2] literal string: @16@0:8 0x100000F94 0x0000000B [ 2] literal string: v24@0:8@16 0x100000F9F 0x00000008 [ 2] literal string: v16@0:8 0x100000FA7 0x00000002 [ 2] literal string: @ 0x100000FAC 0x00000048 [ 0] compact unwind info 0x100001000 0x00000008 [ 0] non-lazy-pointer-to-local: dyld_stub_binder 0x100001008 0x00000008 [ 0] non-lazy-pointer 0x100001010 0x00000008 [ 3] _NSHomeDirectory 0x100001018 0x00000008 [ 3] _NSLog 0x100001020 0x00000008 [ 4] _objc_autoreleasePoolPop 0x100001028 0x00000008 [ 4] _objc_autoreleasePoolPush 0x100001030 0x00000008 [ 4] _objc_autoreleaseReturnValue 0x100001038 0x00000008 [ 4] _objc_destroyWeak 0x100001040 0x00000008 [ 4] _objc_loadWeakRetained 0x100001048 0x00000008 [ 4] _objc_msgSend 0x100001050 0x00000008 [ 4] _objc_msgSendSuper2 0x100001058 0x00000008 [ 4] _objc_release 0x100001060 0x00000008 [ 4] _objc_retain 0x100001068 0x00000008 [ 4] _objc_retainAutoreleasedReturnValue 0x100001070 0x00000008 [ 4] _objc_storeStrong 0x100001078 0x00000008 [ 4] _objc_storeWeak 0x100001080 0x00000020 [ 1] CFString 0x1000010A0 0x00000020 [ 1] CFString 0x1000010C0 0x00000020 [ 1] CFString 0x1000010E0 0x00000008 [ 2] anon 0x1000010E8 0x00000008 [ 0] objc image info 0x1000010F0 0x00000048 [ 2] l_OBJC_METACLASS_RO_$_Test 0x100001138 0x00000080 [ 2] l_OBJC_$_INSTANCE_METHODS_Test 0x1000011B8 0x00000048 [ 2] l_OBJC_$_INSTANCE_VARIABLES_Test 0x100001200 0x00000018 [ 2] l_OBJC_$_PROP_LIST_Test 0x100001218 0x00000048 [ 2] l_OBJC_CLASS_RO_$_Test 0x100001260 0x00000008 [ 1] pointer-to-literal-cstring 0x100001268 0x00000008 [ 1] pointer-to-literal-cstring 0x100001270 0x00000008 [ 1] pointer-to-literal-cstring 0x100001278 0x00000008 [ 2] pointer-to-literal-cstring 0x100001280 0x00000008 [ 1] objc-class-ref 0x100001288 0x00000008 [ 2] anon 0x100001290 0x00000008 [ 2] _OBJC_IVAR_$_Test.obj_ 0x100001298 0x00000008 [ 2] _OBJC_IVAR_$_Test._obj 0x1000012A0 0x00000028 [ 2] _OBJC_CLASS_$_Test 0x1000012C8 0x00000028 [ 2] _OBJC_METACLASS_$_Test
-
-
-
dSYM
- 定義:在每次編譯后都會生成一個 dSYM 文件,程序在執(zhí)行中通過地址來調(diào)用方法函數(shù),而 dSYM 文件里存儲了函數(shù)地址映射,這樣調(diào)用棧里的地址可以通過 dSYM 這個映射表能夠獲得具體函數(shù)的位置。一般都會用來處理 crash 時獲取到的調(diào)用棧 .crash 文件將其符號化。
- 作用: 當(dāng)release的版本 crash的時候,會有一個日志文件,包含出錯的內(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)制文件,因為其中包含了需要支持不同CPU架構(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)容是不可以修改的,因為在 .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)體
struct mach_header { uint32_t magic; cpu_type_t cputype; cpu_subtype_t cpusubtype; uint32_t filetype; uint32_t ncmds; uint32_t sizeofcmds; uint32_t flags; }; struct segment_command { uint32_t cmd; uint32_t cmdsize; char segname[16]; uint32_t vmaddr; uint32_t vmsize; uint32_t fileoff; uint32_t filesize; vm_prot_t maxprot; vm_prot_t initprot; uint32_t nsects; uint32_t flags; };- 根據(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í)行文件后就是在啟動時進(jìn)行動態(tài)鏈接了,進(jìn)行符號和地址的綁定。首先會加載所依賴的 dylibs,修正地址偏移,因為 iOS 會用 ASLR 來做地址偏移避免攻擊,確定 Non-Lazy Pointer 地址進(jìn)行符號地址綁定,加載所有類,最后執(zhí)行 load 方法和 clang attribute 的 constructor 修飾函數(shù)。
- 參考文章: 深入剖析iOS編譯
