常用的clang命令
-
clang -rewrite-objc main.m
將obj檔案重寫為 c, c++檔案 -
clang -Xclang -ast-dump -fsyntax-only main.m
生成檔案生成樹 -
clang -Xclang -dump-tokens main.m
這裡會把程式碼切成一個個 Token,比如大小括號,等於號還有字串等 -
根據一個簡單的例子來觀察是如何進行編譯的
#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在預處理處理這步做了什麼。 clang -E main.m # 執行完後可以看到檔案 # 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; } # 這個過程的處理包括巨集的替換,標頭檔案的匯入,以及類似#if的處理。預處理完成後就會進行詞法分析,這裡會把程式碼切成一個個 Token,比如大小括號,等於號還有字串等。 clang -fmodules -fsyntax-only -Xclang -dump-tokens main.m # 然後是語法分析,驗證語法是否正確,然後將所有節點組成抽象語法樹 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 會去做些優化工作,在 Xcode 的編譯設定裡也可以設定優化級別-01,-03,-0s,還可以寫些自己的 Pass。 # Pass 是 LLVM 優化工作的一個節點,一個節點做些事,一起加起來就構成了 LLVM 完整的優化和轉化。 # 如果開啟了 bitcode 蘋果會做進一步的優化,有新的後端架構還是可以用這份優化過的 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 # 生成可執行檔案,這樣就能夠執行看到輸出結果 clang main.o -o main # 執行 ./main # 輸出 starming rank 14 複製程式碼
-
下面是完整步驟
- 編譯資訊寫入輔助檔案,建立檔案架構 .app 檔案
- 處理檔案打包資訊
- 執行 CocoaPod 編譯前指令碼,checkPods Manifest.lock
- 編譯.m檔案,使用 CompileC 和 clang 命令
- 連結需要的 Framework
- 編譯 xib
- 拷貝 xib ,資原始檔
- 編譯 ImageAssets
- 處理 info.plist
- 執行 CocoaPod 指令碼
- 拷貝標準庫
- 建立 .app 檔案和簽名
-
在 Xcode 中檢視 clang 編譯 .m 檔案的過程
- 在 Xcode 編譯過後,可以通過 Show the report navigator 裡對應 target 的 build 中檢視每個 .m 檔案的 clang 編譯資訊。可以直接在 help 中搜尋 “ Show the report navigator ” 就會出現
-
使用編譯 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 的命令引數,再看?的編譯命令會更容易理解
clang 命令引數 -x 編譯語言比如objective-c -arch 編譯的架構,比如arm7 -f 以-f開頭的。 -W 以-W開頭的,可以通過這些定製編譯警告 -D 以-D開頭的,指的是預編譯巨集,通過這些巨集可以實現條件編譯 -iPhoneSimulator11.1.sdk 編譯採用的iOS SDK版本 -I 把編譯資訊寫入指定的輔助檔案 -F 需要的Framework -c 識別符號指明需要執行前處理器,語法分析,型別檢查,LLVM生成優化以及彙編程式碼生成.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 ... 靜態檔案 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
- 構建可執行檔案的規則。指定 target 的依賴專案,在 target build 之前需要先 build 的依賴。在 Compile Source 中指定所有必須編譯的檔案,這些檔案會根據 Build Setting 和 Build Rules 裡的設定來處理。
- 在 Link Binary With Libraries 裡會列出所有的靜態庫和動態庫,它們會和編譯生成的目標檔案進行連結。
- build phase 還會把靜態資源拷貝到 bundle 裡。
- 可以通過在 build phases 裡新增自定義指令碼來做些事情,比如像 CocoaPods 所做的那樣。
-
Bulid Rules
- 指定不同檔案型別如何編譯。每條 build rule 指定了該型別如何處理以及輸出在哪。可以增加一條新規則對特定檔案型別新增處理方法。
-
Bulid Settings
- 在 build 的過程中各個階段的選項的設定。
-
pbxproj 工程檔案
* build 過程控制的這些設定都會被儲存在工程檔案 .pbxproj 裡。在這個檔案中可以找 rootObject 的 ID 值
* 然後根據這個 ID 找到 main 工程的定義。
```objective-c
/* Begin PBXProject section */
2EC5E1AA1E7814B200BAB0EF /* Project object */ = {
isa = PBXProject;
......
/* End PBXProject section */
```
* 在 targets 裡會指向各個 taget 的定義
```objective-c
targets = (
2EC5E1B11E7814B200BAB0EF /* EWork */,
);
// 根據 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開發領域,LinkMap的輸出是一個純文字格式的檔案,裡面包含重要的編譯資訊及報錯資訊,這也是Apple用來分析你的應用的主要方式,通過這種方式可以發現應用中是否使用了私有庫等不符合Apple提交應用規範的內容,但對於我們開發人員,LinkMap卻是一個用於分析原始碼及檢視Crash的有效途徑
-
為什麼要使用 LinkMap
-
當一箇中大型iOS專案在不斷迭代更新的過程中,程式碼量日漸壯大,需要重構和review的程式碼也越來越多,可一旦程式碼達到一定程度後變得不是那麼可控,為了使得專案還可以持續可整合穩健的開發下去,縮小iOS安裝包大小是必須要做的事情,通常會從壓縮圖片和音訊檔案開始,使用開發工具查詢冗餘不用的資原始檔,這一階段之後只能通過對程式碼的重構來達到可執行檔案整體瘦身的效果。當從事參與的一個專案在不斷迭代過程中,App的安裝包在不斷變大,通過自己的shell指令碼分析,多達幾十萬行,這時候非常有瘦身的必要,這其中包括了.h.m.mm.cpp.rss格式檔案。觀察專案中引入的Pods檔案及相關第三方庫,多達上百個庫,這時候這樣一箇中大型App就涉及到應用瘦身的問題,如何才能有效解決程式碼不可控的問題,如何能提高專案中底層基礎架構的穩定性及健壯性,相信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)
- App的編譯路徑(#Path)
-
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
- 在 Build Settings 裡設定 Write Link Map File 為 Yes 後每次編譯都會在指定目錄生成這樣一個檔案。
-
現在來說一說 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 在可執行檔案中的位置和大小。每個 Section 的 Segment 的型別分為 __TEXT 程式碼段和 __DATA 資料段兩種。
# 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中具體目標檔案在對應的section中的位置和大小(#Symbols):Symbols 是對 Sections 進行了再劃分。這裡會描述所有的 methods,ivar 和字串,及它們對應的地址,大小,檔案編號資訊。
# 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 檔案,程式在執行中通過地址來呼叫方法函式,而 dSYM 檔案裡儲存了函式地址對映,這樣呼叫棧裡的地址可以通過 dSYM 這個對映表能夠獲得具體函式的位置。一般都會用來處理 crash 時獲取到的呼叫棧 .crash 檔案將其符號化。
- 作用: 當release的版本 crash的時候,會有一個日誌檔案,包含出錯的記憶體地址, 使用symbolicatecrash工具能夠把日誌和dSYM檔案轉換成可以閱讀的log資訊,也就是將記憶體地址,轉換成程式裡的函式或變數和所屬於的 檔名.(如何設定 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檔案格式的縮寫,是一種可執行檔案、目的碼、共享程式庫、動態載入程式碼和核心DUMP。是a.out格式的一種替代。Mach-O 提供更多的可擴充套件性和更快的符號表資訊存取。Mach-O應用在基於Mach核心的系統上,目前NeXTSTEP、Darwin、Mac OS X(iPhone)都是使用這種可執行檔案格式。
-
記錄編譯後的可執行檔案,物件程式碼,共享庫,動態載入程式碼和記憶體轉儲的檔案格式。不同於 xml 這樣的檔案,它只是二進位制位元組流,裡面有不同的包含元資訊的資料塊,比如位元組順序,cpu 型別,塊大小等。檔案內容是不可以修改的,因為在 .app 目錄中有個 _CodeSignature 的目錄,裡面包含了程式程式碼的簽名,這個簽名的作用就是保證簽名後 .app 裡的檔案,包括資原始檔,Mach-O 檔案都不能夠更改。
-
Mach-O 的內容:
- Mach-O Header:包含位元組順序,magic,cpu 型別,載入指令的數量等
- Load Commands:包含很多內容的表,包括區域的位置,符號表,動態符號表等。每個載入指令包含一個元資訊,比如指令型別,名稱,在二進位制中的位置等。
- 原始段資料(Raw segment data):可以擁有多個段(segment),每個段可以擁有零個或多個區域(section)。每一個段(segment)都擁有一段虛擬地址對映到程式的地址空間。
-
先看看描述這個檔案的結構體
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; }; 複製程式碼
- 根據這個結構體,需要先取出 magic,然後根據偏移量取出其它的資訊。遍歷 ncmds 能夠獲得所有的 segment。cputype 包含了 CPU_TYPE_I386,CPU_TYPE_X86_64,CPU_TYPE_ARM,CPU_TYPE_ARM64 等多種 CPU 的型別。
-
Mach-O 檔案參考文章
-
dyld動態連結
- 生成可執行檔案後就是在啟動時進行動態連結了,進行符號和地址的繫結。首先會載入所依賴的 dylibs,修正地址偏移,因為 iOS 會用 ASLR 來做地址偏移避免攻擊,確定 Non-Lazy Pointer 地址進行符號地址繫結,載入所有類,最後執行 load 方法和 clang attribute 的 constructor 修飾函式。
-
參考文章: 深入剖析iOS編譯