文章目錄
背景
本文寫作背景是有位網友求助“爲什麼自己編譯的 objc 運行時,在 mac 設備上無法進行調試?”
考慮到更多的同學是 iOS 開發工程師,本文將嘗試更加廣度和深度的角度講解 iOS 的相關技巧,相信看完本篇本章後,對 mac 設備的調試也不在話下。
效果圖:
OK,下面開始進入正題。
如何編譯並調試 objc 運行庫
1、編譯 objc 運行庫
首先,擋在開發者面前的第一步是編譯 objc 運行庫。這份工作和普通的 iOS 日常開發遇到的類似(通常需要解決一些頭文件缺失導致的編譯失敗等問題)。考慮到網絡上的文章都會重點講解如何解決頭文件問題。本文不再做詳細解釋。讀者可以根據自己的系統版本選擇合適 objc 版本編譯。
2、理解 dyld 的動態鏈接過程
在 iOS 的世界中,動態庫都是通過 dyld 完成動態鏈接工作的,如下所示,默認情況下,libobjc
是作爲系統庫的一部分被 map 並在程序運行時動態進行 load 操作。
dyld: Using shared cached for /usr/lib/libobjc.A.dylib
dyld: loaded: <20AC082F-2DB7-3974-A2D4-8C5E01787584> /usr/lib/libobjc.A.dylib
從上面的日誌,我們很容易發現,dyld 是通過 shared cache
加載 libobjc
庫,並且 libobjc
的原始路徑是 /usr/lib/libobjc.A.dylib
。
shared cache
是一種提高 APP 的啓動速度的優化手段。通過將所有的系統庫打包爲單一的共享庫文件,可以將系統庫間的依賴關係進行緩存。
如果讀者對 mach-o 文件格式熟悉的話,很容易發現這個路徑是寫在 mach-o 的 load commands
部分。
otool
otool 是一款常見的調試工具,在 Mac 最新的命令行工具中,它實際上是由 llvm-objdump
僞裝後的工具。
通過 otool -l file-path
,可以將合法的 mach-o 文件的 load command
打印到控制檯。
我們使用 otool 工具驗證一下:
otool -l ~/Library/Developer/Xcode/DerivedData/testLock-fqojwzmsigqvkigfdphosmtuudwt/Build/Products/Debug-iphoneos/testLock.app/testLock | grep libobjc -C 3
Load command 15
cmd LC_LOAD_DYLIB
cmdsize 56
name /usr/lib/libobjc.A.dylib (offset 24)
time stamp 2 Thu Jan 1 08:00:02 1970
current version 228.0.0
compatibility version 1.0.0
上面的信息表明,第 15 個 load command
是 LC_LOAD_DYLIB
(load a dynamically linked shared library) ,文件位置是 /usr/lib/libobjc.A.dylib
。
3、複製並調整默認的 objc 路徑
爲了讓 dyld 加載自行編譯的 libobjc
,我們首先需要將 libobjc
複製到安裝包中。
然後,讓 load command
的路徑指向這個二進制文件。
install_name_tool
install_name_tool
同樣是一款常用的調試工具。它可以實現 mach-o文件的動態庫的增刪改操作。
我們使用 install_name_tool
搭配 otool
工具驗證一下。
install_name_tool -change /usr/lib/libobjc.A.dylib @rpath/libobjc.A.dylib ~/Library/Developer/Xcode/DerivedData/testBlock-hcugstfyttogsqgdxjjypicnuusw/Build/Products/Release-iphoneos/testBlock.app/testBlock
很明顯, libobjc
的鏈接位置發生了改變 @rpath/libobjc.A.dylib
。
ps.爲了方便開發,我們可以通過以下方法實現 Xcode 編譯完成後自動替換指向路徑。
install_name_tool -change /usr/lib/libobjc.A.dylib @rpath/libobjc.A.dylib $TARGET_BUILD_DIR/$EXECUTABLE_PATH
mac 特殊問題
iOS 的原理講解完畢了,我們切換一下思路,回到網友遇到的 Mac 無法調試的問題。
默認情況下,iOS 的應用都會通過對二進制簽名保證安全,但是,Mac 卻提供了關閉關操作的開關:Disable Library Validation Entitlement
官方鏈接。
如下圖所示,只有當圖中的 Disable Library Validation
被勾選時,我們纔可以讓自己的 Mac 程序加載任意的二進制文件。否則,就只能加載同一個開發者賬號簽名的文件。
總結
本文講解了調試 libobjc
的原理,並簡單介紹了 install_name_tool
調整庫路徑 和 otool
工具進行驗證。