對LinkMapFile的初步認識

我不是掌櫃發表於2018-11-20

什麼是Link Map File

Link Map File中文直譯為連結對映檔案,它是在Xcode生成可執行檔案的同時生成的連結資訊檔案,用於描述可執行檔案的構造部分,包括了程式碼段和資料段的分佈情況。Xcode在生成可執行檔案的時候預設情況下不生成該檔案,需要開發者手動設定Target –> Build Setting –> Write Link Map File為YES:
Link Map Setting
這裡還可以設定Link Map存放的位置,預設的位置為:

$(TARGET_TEMP_DIR)/$(PRODUCT_NAME)-LinkMap-$(CURRENT_VARIANT)-$(CURRENT_ARCH).txt

例如:

/Users/zhanggui/Library/Developer/Xcode/DerivedData/LinkMapTest-ffnpzjkbsmhwvdcxorqbxpyvjtob/Build/Intermediates.noindex/LinkMapTest.build/Debug-iphonesimulator/LinkMapTest.build/LinkMapTest-LinkMap-normal-x86_64.txt

開發者也可以根據自己的需要自行設定該檔案的位置。

Link Map File的組成

開啟Link Map File,裡面包含了以下幾個部分:

1. Path

# Path: /Users/zhanggui/Library/Developer/Xcode/DerivedData/LinkMapTest-ffnpzjkbsmhwvdcxorqbxpyvjtob/Build/Products/Debug-iphonesimulator/LinkMapTest.app/LinkMapTest

Path是生成可執行檔案的路徑。

2. Arch

# Arch: x86_64

Arch指代架構型別。

3. Object files:

# Object files:
[ 0] linker synthesized
[ 1] /Users/zhanggui/Library/Developer/Xcode/DerivedData/LinkMapTest-ffnpzjkbsmhwvdcxorqbxpyvjtob/Build/Intermediates.noindex/LinkMapTest.build/Debug-iphonesimulator/LinkMapTest.build/LinkMapTest.app-Simulated.xcent
[ 2] /Users/zhanggui/Library/Developer/Xcode/DerivedData/LinkMapTest-ffnpzjkbsmhwvdcxorqbxpyvjtob/Build/Intermediates.noindex/LinkMapTest.build/Debug-iphonesimulator/LinkMapTest.build/Objects-normal/x86_64/ViewController.o
[ 3] /Users/zhanggui/Library/Developer/Xcode/DerivedData/LinkMapTest-ffnpzjkbsmhwvdcxorqbxpyvjtob/Build/Intermediates.noindex/LinkMapTest.build/Debug-iphonesimulator/LinkMapTest.build/Objects-normal/x86_64/main.o
[ 4] /Users/zhanggui/Library/Developer/Xcode/DerivedData/LinkMapTest-ffnpzjkbsmhwvdcxorqbxpyvjtob/Build/Intermediates.noindex/LinkMapTest.build/Debug-iphonesimulator/LinkMapTest.build/Objects-normal/x86_64/AppDelegate.o
[ 5] /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk/System/Library/Frameworks//Foundation.framework/Foundation.tbd
[6]/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk/usr/lib/libobjc.tbd
[7]/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator12.1.sdk/System/Library/Frameworks//UIKit.framework/UIKit.tbd

Object Files列舉了可執行檔案裡所有的obj以及tbd。每一行代表對檔案的編號。例如ViewController.o檔案,其編號為2。編號的具體作用稍後介紹。

4. Sections

# Sections:
# Address    Size        Segment    Section
0x100001730    0x00000333    __TEXT    __text
0x100001A64    0x0000002A    __TEXT    __stubs
0x100001A90    0x00000056    __TEXT    __stub_helper
0x100001AE6    0x00000A27    __TEXT    __objc_methname
0x10000250D    0x0000003C    __TEXT    __objc_classname
0x100002549    0x0000086D    __TEXT    __objc_methtype
0x100002DB6    0x0000007A    __TEXT    __cstring
0x100002E30    0x00000182    __TEXT    __entitlements
0x100002FB4    0x00000048    __TEXT    __unwind_info
0x100003000    0x00000010    __DATA    __nl_symbol_ptr
0x100003010    0x00000010    __DATA    __got
0x100003020    0x00000038    __DATA    __la_symbol_ptr
0x100003058    0x00000010    __DATA    __objc_classlist
0x100003068    0x00000010    __DATA    __objc_protolist
0x100003078    0x00000008    __DATA    __objc_imageinfo
0x100003080    0x00000BE8    __DATA    __objc_const
0x100003C68    0x00000010    __DATA    __objc_selrefs
0x100003C78    0x00000008    __DATA    __objc_classrefs
0x100003C80    0x00000008    __DATA    __objc_superrefs
0x100003C88    0x00000008    __DATA    __objc_ivar
0x100003C90    0x000000A0    __DATA    __objc_data
0x100003D30    0x000000C0    __DATA    __data

單從字面含義理解:每個Section包含了Address、Size、Segment以及Section。介紹之前,這裡先簡單介紹一下Mach-O檔案。
上面第一部分的Path是可執行檔案的路徑,使用iTerm進去到該資料夾,然後使用file命令即可檢視該檔案的型別:

file LinkMapTest

輸出結果為:

LinkMapTest: Mach-O 64-bit executable x86_64

可以知道該檔案是一個Mach-O格式的檔案,它是iOS系統應用執行檔案格式。Mach-O檔案中的虛擬地址最終會被對映到實體地址上,這些地址會被分為不同的段型別:_ TEXT 、 _ DATA以及_ _ LINKEDIT等。各個段的含義如下:

  1. TEXT包含了被執行的程式碼。這些程式碼是隻讀、可執行
  2. DATA包含了包含了將會被更改的資料,例如全域性變數、靜態變數等,可讀寫,但是不可執行
  3. LINKEDIT 包含了載入程式的後設資料,比如函式名稱和地址,只讀。

Segment又被劃分成了不同的Section,不同的Section儲存了不同的資訊,例如 objc _ methname 為方法的名稱。
再回顧上面的Sections,Address是起始位置、Size是大小、Segment是段、Section。

5. Symbols

# Address    Size        File  Name
0x100001730    0x0000003C    [  2] -[ViewController viewDidLoad]
0x100001770    0x00000092    [  3] _main
0x100001810    0x00000080    [  4] -[AppDelegate application:didFinishLaunchingWithOptions:]
0x100001890    0x00000040    [  4] -[AppDelegate applicationWillResignActive:]
0x1000018D0    0x00000040    [  4] -[AppDelegate applicationDidEnterBackground:]
0x100001910    0x00000040    [  4] -[AppDelegate applicationWillEnterForeground:]
0x100001950    0x00000040    [  4] -[AppDelegate applicationDidBecomeActive:]
0x100001990    0x00000040    [  4] -[AppDelegate applicationWillTerminate:]
0x1000019D0    0x00000020    [  4] -[AppDelegate window]
0x1000019F0    0x00000040    [  4] -[AppDelegate setWindow:]
0x100001A30    0x00000033    [  4] -[AppDelegate .cxx_destruct]
0x100001A64    0x00000006    [  5] _NSStringFromClass
0x100001A6A    0x00000006    [  7] _UIApplicationMain
0x100001A70    0x00000006    [  6] _objc_autoreleasePoolPop
0x100001A76    0x00000006    [  6] _objc_autoreleasePoolPush
0x100001A7C    0x00000006    [  6] _objc_msgSendSuper2
0x100001A82    0x00000006    [  6] _objc_retainAutoreleasedReturnValue
0x100001A88    0x00000006    [  6] _objc_storeStrong
0x100001A90    0x00000010    [  0] helper helper
0x100001AA0    0x0000000A    [  5] _NSStringFromClass
0x100001AAA    0x0000000A    [  6] _objc_autoreleasePoolPop
0x100001AB4    0x0000000A    [  6] _objc_autoreleasePoolPush
0x100001ABE    0x0000000A    [  6] _objc_msgSendSuper2
0x100001AC8    0x0000000A    [  6] _objc_retainAutoreleasedReturnValue
0x100001AD2    0x0000000A    [  6] _objc_storeStrong
0x100001ADC    0x0000000A    [  7] _UIApplicationMain
0x100001AE6    0x0000000C    [  2] literal string: viewDidLoad
. . .

根據Sections的起始地址,可以將Symbols分為Sections個數的組,例如0x100001730到0x100001A64之間,就是 test程式碼區。
Symbols包含的資訊有:

  1. Address:起始地址
  2. Size:所佔記憶體大小,這裡使用16進製表示。
  3. File:該Name所在的檔案編號,也就是Object files部分的中括號的數字,例如-[ViewController viewDidLoad]對應的檔案編號為2,根據Object files部分可以看到所屬的檔案為:ViewController.o。這樣可以計算某個o檔案所佔記憶體的大小。只需要把Symbols中編號為o編號Symbols累加統計即可。
  4. Name就是該Sybmols的名稱。

6. Dead Stripped Symbols

# Dead Stripped Symbols:
#            Size        File  Name
<<dead>>     0x00000018    [  2] CIE
<<dead>>     0x00000018    [  3] CIE
<<dead>>     0x00000006    [  4] literal string: class
<<dead>>     0x00000008    [  4] literal string: v16@0:8
<<dead>>     0x00000018    [  4] CIE
. . .

上面便是對Link map file做了簡單的介紹。

itools

花了兩天的時間,根據對Link Map File的學習,使用Ruby寫了一個指令碼檔案,可以方便地統計出指定Link Map File中的元件或者tbd佔用記憶體大小,類似:

AppDelegate.o          8.50KB
ViewController.o          735B
LinkMapDemo.app-Simulated.xcent          386B
main.o          192B
 linker synthesized          128B
libobjc.tbd          120B
Foundation.tbd          24B
UIKit.tbd          24B
總大小為(僅供參考):10.07KB

想了解更多可以訪問https://github.com/ScottZg/itools

總結

  1. 蘋果開發還是有很多細節的東西需要去學習去了解。
  2. 學習一門指令碼語言,也會給平時的開發帶來很大的方便。

參考

  1. Mach-O可執行檔案
  2. iOS調優|深入理解Link Map File
  3. iOS APP可執行檔案的組成


相關文章