調試 iOS 的 objc 運行時,你可能還需要掌握這些知識

背景

本文寫作背景是有位網友求助“爲什麼自己編譯的 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 commandLC_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 工具進行驗證。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章