文章目录
- 背景
- 如何编译并调试 objc 运行库
- 1、编译 objc 运行库
- 2、理解 dyld 的动态链接过程
- otool
- 3、复制并调整默认的 objc 路径
- install_name_tool
- mac 特殊问题
- 总结
背景
本文写作背景是有位网友求助“为什么自己编译的 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
工具进行验证。