前言
由于Apple官方已经将Objective-C runtime的源码开源,所以在学习Objective-C runtime的过程中可以使用开源的代码一边调试一边学习。但是因为Apple官方没很清晰地罗列依赖以及本地配置方法,故写下本文档作为参考。
Objc4源码Xcode调试方法
首先判断macOS的版本,若是macOS Big Sur (macOS 11)及以下,参考 github.com/hubupc/objc… 进行配置,并在 opensource.apple.com/tarballs/ 中下载所需依赖的库。
若是macOS Monterey (macOS 12),则可以参考接下来的配置方法。如果怕麻烦不想手动配置各种繁琐的操作,可以跳转至末尾【附录】下载已经配置好的工程直接上手调试。
objc4-838.1源码编译
以下配置操作,以Xcode 13.2.1面向Mac平台的编译操作为例。
下载objc4-838.1源码及其依赖
最新的objc4-838.1的源代码没有同步发到Apple官方的开源网站( opensource.apple.com/ )上,故需要到Apple官方的GitHub仓库( github.com/apple-oss-d… )上下载最新的源代码。
主源码库:
- objc4-838.1
依赖库:
-
xnu-8019.41.5
-
dyld-940
-
Libc-1506.40.4
-
Libc-825.40.1
-
libclosure-79
-
libplatform-273.40.1
-
libpthread-485.60.2
配置依赖库目录
-
解压objc4-838.1并作为主工程使用
-
在主工程根目录下,新建名为PrivateHeaders的文件夹
-
打开objc工程
-
首先,点击左侧项目导航窗口中的工程文件objc。然后,在右侧设置窗口中,TARGETS 栏目下选中objc。接着,点击Build Settings,并选择All和Combined,在右上侧搜索栏中输入【search paths】。最后,在Search Paths栏目下,双击打开Header Search Paths,新增【$(SRCROOT)/PrivateHeaders】。
配置依赖
首先先将Scheme选择objc,设备选择My Mac。
然后使用快捷键command + B,即可进行编译。不出意料的话,编译器会报错。
虽然直接进行编译会报错,但是我们接下来的配置操作都基于Error的信息来操作。首先,点击左侧项目导航并排的感叹号图标。然后,点击Buildtime。最后,在下方的Filter栏中点击最右侧的叉号图标,即可筛选出Error信息。
接下来的配置操作,依赖于编译中产生的Error的信息。实际操作过程中,报错顺序可能跟下文有些差异,建议通过全局检索关键字的方式来查找解决Error对应的办法。以下是可能会产生Error信息以及对应的解决办法:
- unable to find sdk ‘macosx.internal’ (target: objc)
点击左侧项目导航窗口中的工程文件objc。然后,在右侧设置窗口中,TARGETS 栏目下选中objc。接着,点击Build Settings,并选择All和Combined,在右上侧搜索栏中输入【Architectures】。最后,在Architectures栏目下,将Base SDK由macosx.internal改为macOS。
- unable to find sdk ‘macosx.internal’ (target: objc-trampolines)
跟(1)的操作类似,只是Step 2中TARGETS选择objc-trampolines
- ‘os/feature_private.h’ file not found (target: objc ; file: objc-runtime.mm)
注释objc-runtime.mm Line 36、Line 444-446。
注释NSObject.mm Line 43。
- ‘sys/reason.h’ file not found (target: objc ; file: objc-os.h)
将xnu-8019.41.5根目录下的bsd/sys/reason.h文件拷贝到主工程依赖库文件夹PrivateHeaders的sys/(如没有对应的文件夹自行新建)。
- ‘mach-o/dyld_priv.h’ file not found (target: objc ; file: objc-os.h)
将dyld-940根目录下的include/mach-o/dyld_priv.h文件拷贝到主工程依赖库文件夹PrivateHeaders的mach-o/(如没有对应的文件夹自行新建)。
- Expected ‘,’ (target: objc ; file: dyld_priv.h)
在dyld_priv.h中检索所有的bridgeos(3.0),并删除。
- ‘os/lock_private.h’ file not found (target: objc ; file: objc-os.h)
将libplatform-273.40.1根目录下的private/os/lock_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘os/base_private.h’ file not found (target: objc ; file: lock_private.h)
将xnu-8019.41.5根目录下的libkern/os/base_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘pthread/tsd_private.h’ file not found (target: objc ; file: lock_private.h)
将libpthread-485.60.2根目录下的private/pthread/tsd_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders的pthread/(如没有对应的文件夹自行新建)。
- ‘System/machine/cpu_capabilities.h’ file not found (target: objc ; file: tsd_private.h)
将xnu-8019.41.5根目录下的osfmk/machine/cpu_capabilities.h文件拷贝到主工程依赖库文件夹PrivateHeaders的System/machine/(如没有对应的文件夹自行新建)。
- Expected ‘,’ (target: objc ; file: lock_private.h)
在lock_private.h中检索所有的bridgeos(4.0),并删除。
- ‘os/tsd.h’ file not found (target: objc ; file: tsd_private.h)
将xnu-8019.41.5根目录下的libsyscall/os/tsd.h文件拷贝到主工程依赖库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘pthread/spinlock_private.h’ file not found (target: objc ; file: tsd_private.h)
将libpthread-485.60.2根目录下的private/pthread/spinlock_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders的pthread/(如没有对应的文件夹自行新建)。
- ‘System/pthread_machdep.h’ file not found (target: objc ; file: objc-os.h)
将Libc-825.40.1根目录下的pthreads/pthread_machdep.h文件拷贝到主工程依赖库文件夹PrivateHeaders的System/(如没有对应的文件夹自行新建)。
- ‘CrashReporterClient.h’ file not found (target: objc ; file: objc-os.h)
将Libc-825.40.1根目录下的include/CrashReporterClient.h文件拷贝到主工程依赖库文件夹PrivateHeaders。
接着,点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Settings,并选择All和Combined,在右上侧搜索栏中输入【Preprocessor Macros】。最后,在Apple Clang – Preprocessing栏目下,双击打开Preprocessor Macros,新增【LIBC_NO_LIBCRASHREPORTERCLIENT】。
- ‘objc-shared-cache.h’ file not found (target: objc ; file: objc-opt.h)
将dyld-940根目录下的include/objc-shared-cache.h文件拷贝到主工程依赖库文件夹PrivateHeaders。
- Typedef redefinition with different types (‘int’ vs ‘volatile OSSpinLock’ (aka ‘volatile int’)) Static & declaration of ‘_pthread_has_direct_tsd’ follows non-static declaration & Static declaration of ‘_pthread_getspecific_direct’ follows non-static declaration & Static declaration of ‘_pthread_setspecific_direct’ follows non-static declaration (target: objc ; file: pthread_machdep.h)
注释pthread_machdep.h Line 214-299。
- Use of undeclared identifier ‘dyld_platform_version_macOS_10_13’ (target: objc ; file: objc-os.mm)
注释objc-os.mm Line 568-575。
- ‘_simple.h’ file not found (target: objc ; file: objc-errors.mm)
将libplatform-273.40.1根目录下的private/_simple.h文件拷贝到主工程依赖库文件夹PrivateHeaders。
- ‘Cambria/Traps.h’ file not found (target: objc ; file: objc-cache.mm)
注释objc-cache.mm Line 87-88。
- ‘os/linker_set.h’ file not found (target: objc ; file: objc-class.mm)
将Libc-1506.40.4根目录下的os/linker_set.h文件拷贝到主工程依赖库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘kern/restartable.h’ file not found (target: objc ; file: objc-cache.mm)
将xnu-8019.41.5根目录下的osfmk/kern/restartable.h文件拷贝到主工程依赖库文件夹PrivateHeaders的kern/(如没有对应的文件夹自行新建)。
- ‘Block_private.h’ file not found (target: objc ; file: objc-block-trampolines.mm)
将libclosure-79根目录下的Block_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders。
- Use of undeclared identifier ‘oah_is_current_process_translated’ (target: objc ; file: objc-cache.mm)
注释objc-cache.mm Line 1123-1130。
- Use of undeclared identifier ‘dyld_fall_2020_os_versions’ (target: objc ; file: objc-runtime.mm)
注释objc-runtime.mm Line 379-380。
- ‘os/reason_private.h’ file not found (target: objc ; file: NSObject.mm)
将xnu-8019.41.5根目录下的libkern/os/reason_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- ‘os/variant_private.h’ file not found (target: objc ; file: NSObject.mm)
将Libc-1506.40.4根目录下的os/variant_private.h文件拷贝到主工程依赖库文件夹PrivateHeaders的os/(如没有对应的文件夹自行新建)。
- Use of undeclared identifier ‘dyld_platform_version_bridgeOS_2_0’ & Use of undeclared identifier ‘dyld_platform_version_iOS_10_0’ & Use of undeclared identifier ‘dyld_platform_version_macOS_10_12’ & Use of undeclared identifier ‘dyld_platform_version_tvOS_10_0’ & Use of undeclared identifier ‘dyld_platform_version_watchOS_3_0’ (target: objc ; file: NSObject.mm)
注释NSObject.mm Line 1147-1151。
- Expected ‘,’ (target: objc ; file: variant_private.h)
在variant_private.h中检索所有的bridgeos和bridgeos(4.0),并删除。
- Mismatch in debug-ness macros (target: objc ; file: objc-runtime.mm)
注释objc-runtime.mm Line 128。
- Use of undeclared identifier ‘dyld_platform_version_macOS_10_11’ (target: objc ; file: objc-runtime-new.mm)
注释objc-runtime-new.mm Line 3528-3534。
- ‘_static_assert’ declared as an array with a negative size (target: objc ; file: objc-runtime-new.mm)
注释objc-runtime-new.mm Line 176-177。
- Use of undeclared identifier ‘dyld_fall_2018_os_versions’ (target: objc ; file: objc-runtime-new.mm)
注释objc-runtime-new.mm Line 8381-8403。
- Library not found for -lCrashReporterClient (target: objc)
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Settings,并选择All和Combined,在右上侧搜索栏中输入【other linker】。最后,在Other Linker Flags栏目下,双击打开Any macOS SDK,删除【-lCrashReporterClient】。
(同样的操作删除Release下Any macOS SDK中的【-lCrashReporterClient】)
- Library not found for -loah (target: objc)
与(34)相同的操作删除【-loah】。
- SDK “macosx.internal” cannot be located (target: objc)
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc。然后,点击Build Phases。最后,展开Run Script (markgc)栏目,将脚本中的【macosx.internal】改为【macosx】。
附上完整脚本内容:
set -x
/usr/bin/xcrun -sdk macosx clang++ -Wall -mmacosx-version-min=10.12 -arch x86_64 -std=c++11 "${SRCROOT}/markgc.cpp" -o "${BUILT_PRODUCTS_DIR}/markgc"
"${BUILT_PRODUCTS_DIR}/markgc" "${BUILT_PRODUCTS_DIR}/libobjc.A.dylib"
源码编译成功
当编译后出现以下图标,代表编译成功:
至此,关于objc4-838.1源码前期配置以及编译的过程就结束了。
源码调试工程
通过上述的操作,我们成功编译了objc4-838.1。但是,直接学习objc4的源码难度还是太高了。此时,我们通过创建调试工程来达到学习源码的目的。
创建源码调试工程
首先我们需要创建调试工程:
-
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目底部点击加号。然后,在弹出的项目创建框中,选中macOS,在搜索栏中输入【Command Line Tool】。接着,选择Command Line Tool,点击右下角的Next。
-
在跳转的项目信息框中,填入Product Name(本文例子填写的是【objc_test】,可以自行命名)。Language选择Objective-C。点击Finish。
-
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击General。接着,在Frameworks and Libraries栏目下点击加号。
-
选择libobjc.A.dylib,点击Add。
-
在Frameworks and Libraries栏目下,选中libobjc.A.dylib,点击Embed,选择Do Not Embed。
-
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Phases。接着,展开Dependencies栏目,点击下方的加号。最后选中objc,点击Add。
-
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Settings,并选择All和Combined,在右上侧搜索栏中输入【Signing】。最后,点击Enable Hardened Runtime,选择No。
-
点击左侧项目导航窗口中的工程文件objc,在右侧设置窗口中,TARGETS 栏目下选中objc_test。然后,点击Build Settings,并选择All和Combined,在右上侧搜索栏中输入【objc_msgSend】。最后,点击Enable Strict Checking of objc_msgSend Calls,选择No。
运行源码调试工程
展开左侧项目导航窗口中的工程文件objc_test,点击main.m。接着,将Scheme选择objc_test,设备选择My Mac。快捷键Command+R即可编译且运行工程。
至此,源码调试工程创建并运行成功。我们可以在objc_test工程中编写代码调用objc runtime相关的的API,并且可以同时在objc工程中添加断点来调试。
错误排查
- objc工程无法触发断点
Scheme选择objc,快捷键command + B确保objc工程编译成功。若仍未能成功,参考【创建源码调试工程】和【运行源码调试工程】的操作,确保工程配置文件的设置符合预期。
附录
macOS Monterey (macOS 12)可以直接在 code.byted.org/caiyixian/o… 下载已经完全配置好的工程。
参考链接
今天的文章Objective-C runtime 源码调试分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/14373.html