iOS底层原理:苹果开源 objc4-818 源码项目的编译和调试

00.png

开篇

作为一名iOS程序员,探索OC底层原理永不止息,同时也是永远的痛,最开始只能靠猜测!后面慢慢找到了苹果官方开源的源码来辅助看一下,但是尽管这样,还是显得不太直观!如果objc源码能够像我们自己创建的项目一样直接编译调试,像我们自己的代码一样能够直接 LLDB 调试,流程跟踪,那简直不要太爽。废话不多说,开炮~ 哦,不是,是开干~!🚀

一、准备工作

苹果官方开源源码地址:
  1. https://opensource.apple.com/releases/
    0.png
  2. https://opensource.apple.com/tarballs/objc4/
    1.png

上面这两种方式,自行下载即可。我这里下载的是目前最新的objc4-818.2版本!

系统版本、开发工具、objc源码
  • macOS Big Sur 11.1
  • Xcode 12.5.1
  • objc4-818.2
依赖文件

dyld-852.2macOS 11.5
Libc-825.40.1Mac OS X 10.8.5
Libc-1439.141.1macOS 11.5
libclosure-79macOS 11.3 ~ macOS 11.5
libplatform-254.80.2macOS 11.2 ~ macOS 11.5
libplatform-220.100.1macOS 10.15.4~macOS 10.15.6
libpthread-454.120.2macOS 11.4~macOS 11.5
xnu-7195.141.2macOS 11.5

2.png

以上是依赖源文件库的版本,可以到官方Open Source上去下载,如图:

3.png

也可以直接点击上面对应的文件名跳转到GitHub上,然后再下载,比如:dyld-852.2,GitHub结果如图:
4.png

注意:建议依赖的文件库最好去官方Open Source上去下载,这样可以帮助你了解官方网站的玩法,并且版本尽量下载最新版,在macOS新版本找不到对应的文件库就从之前的macOS版本去找。

二、objc源码项目编译和遇到问题解决

下载好objc4-818.2的源码后,解压后打开objc.xcodeproj

5.png

然后选中objc这个target进行编译
6.png

注意:以下错误顺序不分先后,我只是按照我这边遇到错误的顺序依次解决,小伙伴们如果遇到问题对应不上的,可以往前或往后翻一翻,对照相应的错误进行解决

报错1:'unable to find sdk 'macosx.internal'

第一次编译会出现两个'unable to find sdk 'macosx.internal'的报错,objc 和 objc-trampolines 这两个target都有这个问题。


7.png

解决方式:

  • 找到BuildSettings,分别设置 objc、objc-trampolines的Base SDK为macOS。
target -> objc -> BuildSettings -> Base SDK -> macOS
target -> objc-trampolines -> BuildSettings -> Base SDK -> macOS
8.png
报错2:'sys/reason.h' file not found

再次运行,出现'sys/reason.h' file not found错误

9.png

这个reason.h文件在哪儿呢?这时候就需要用到我们前面下载的文件依赖库了。reason.h就在下载的文件库 xnu-7195.141.2/bsd/sys目录下。

解决方式:

  1. objc4-818.2工程的根目录下新建一个名为JQDependencies(文件名随便取)的文件夹,然后JQDependencies下创建子文件夹sys
  2. xnu-7195.141.2/bsd/sys目录下的reason.h文件copy到JQDependencies/sys/目录下
  3. 然后设置一下文件的搜索路径 Header Search Paths


    10.png
11.png
12.png
报错3:'mach-o/dyld_priv.h' file not found

再次运行,会出现'mach-o/dyld_priv.h' file not found这个错误

13.png

这个报错和错误2是同样的道理,是缺少了dyld_priv.h这个文件,dyld_priv.h文件在在dyld-852.2/include/mach-o目录下

解决方式:

  1. JQDependencies目录下同样创建一个mach-o文件夹
  2. dyld_priv.hdyld-852.2/include/mach-o目录下copy到JQDependencies/mach-o目录下
14.png
15.png
  1. 拷贝之后还需要在 dyld_priv.h 文件添加以下宏定义
#define DYLD_MACOSX_VERSION_10_11 0x000A0B00
#define DYLD_MACOSX_VERSION_10_12 0x000A0C00
#define DYLD_MACOSX_VERSION_10_13 0x000A0D00
#define DYLD_MACOSX_VERSION_10_14 0x000A0E00
16.png
  1. 然后在项目中点击# include <mach-o/dyld_priv.h>跳转到dyld_priv.h文件中,查看其中是否有 bridgeos(3.0) 的函数参数报错,如果有报错则删除bridgeos(3.0)参数。
17.png

18.png
报错4:'os/lock_private.h' file not found

再次编译运行,会出现'os/lock_private.h' file not found错误


19.png

同理,我们在依赖库中找到文件位置,创建对应文件路径并将目标文件copy到对应的路径下即可。lock_private.h文件在libplatform-254.80.2/private/os目录下

解决方式:

  1. JQDependencies目录下同样创建一个os文件夹
  2. lock_private.hlibplatform-254.80.2/private/os目录下copy到JQDependencies/os目录下
    此处就不贴图了,按照报错2的方式去对应的路径下找文件即可。
报错5:'os/base_private.h' file not found

再次编译运行,会出现'os/base_private.h' file not found错误

同理,我们在依赖库中找到文件位置,创建对应文件路径并将目标文件copy到对应的路径下即可。base_private.h文件在libplatform-254.80.2/private/os目录下

解决方式:

  1. base_private.hlibplatform-254.80.2/private/os目录下copy到JQDependencies/os目录下
报错6:'pthread/tsd_private.h' file not found

再次编译运行,会出现 'pthread/tsd_private.h' file not found错误

同理,我们在依赖库中找到文件位置,创建对应文件路径并将目标文件copy到对应的路径下即可。tsd_private.h文件在libpthread-libpthread-454.120.2/private/pthread*目录下

解决方式:

  1. JQDependencies目录下同样创建一个pthread文件夹
  2. tsd_private.hlibpthread-libpthread-454.120.2/private/pthread目录下copy到JQDependencies/pthread目录下
报错7:'pthread/spinlock_private.h' file not found

同理,我们在依赖库中找到文件位置,创建对应文件路径并将目标文件copy到对应的路径下即可。spinlock_private.h文件也在libpthread-libpthread-454.120.2/private/pthread*目录下

解决方式:

  1. spinlock_private.hlibpthread-libpthread-454.120.2/private/pthread目录下copy到JQDependencies/pthread目录下
报错8:'System/machine/cpu_capabilities.h' file not found

同样是找到文件位置,cpu_capabilities.h文件在xnu-7195.141.2/osfmk/machine目录下

解决方式:

  1. JQDependencies目录下同样创建一个System/machine文件夹。
  2. cpu_capabilities.hxnu-7195.141.2/osfmk/machine目录下copy到JQDependencies/System/machine/目录下

这里要注意的是:上面的文件丢失都是创建的单层文件夹,这里是System和machine两层文件夹,主要是看报错提示的路径,比如:'System/machine/cpu_capabilities.h'需要的System/machine/两层

报错9:'os/tsd.h' file not found

同样是找到文件位置,tsd.h文件在xnu-7195.141.2/libsyscall/os目录下

解决方式:

  1. tsd.hxnu-7195.141.2/libsyscall/os目录下copy到JQDependencies/os/目录下
报错10:'System/pthread_machdep.h' file not found

同样是找到文件位置,pthread_machdep.h文件在Libc-825.40.1/pthreads目录下。
注意:经过一番探索,我发现pthread_machdep.h只在Libc-825.40.1及以下版本才有

20.png

解决方式:

  1. pthread_machdep.hLibc-825.40.1/pthreads目录下copy到JQDependencies/System/目录下
报错11:'CrashReporterClient.h' file not found

同样是找到文件位置,CrashReporterClient.h文件在Libc-825.40.1/include目录下。

解决方式:

  1. CrashReporterClient.hLibc-825.40.1/include目录下copy到JQDependencies/目录下
    21.png

导入CrashReporterClient.h文件后,如果还报错,用以下两种方式解决:

  • 第一种:需要在 Build Settings -> Preprocessor Macros 中加入:LIBC_NO_LIBCRASHREPORTERCLIENT


    22.png
  • 第二种:在CrashReporterClient.h中添加如下宏定义

#define LIBC_NO_LIBCRASHREPORTERCLIENT
23.png
报错12:Typedef redefinition with different types ('int' vs 'volatile OSSpinLock' (aka 'volatile int'))

pthread_lock_t重复定义了

解决方式:

  • 注释掉 在*pthread_machdep.h文件中的定义
    24.png
报错13:

Static declaration of '_pthread_has_direct_tsd' follows non-static declaration
和 Static declaration of '_pthread_getspecific_direct' follows non-static declaration

解决方式:

  • 把这些声明在*pthread_machdep.h文件中注释掉就行
    25.png
报错14:/JQDependencies/os/lock_private.h:288:37: Expected ','
26.png

解决方式:

  • 把这个函数中的bridgeos(4.0)参数删除即可
报错15:Use of undeclared identifier 'dyld_fall_2020_os_versions'

** 解决方式:**

  • 注释掉就行


    27.png

    28.png
报错16:'objc-shared-cache.h' file not found

同样是找到文件位置,objc-shared-cache.h文件在dyld-852.2/include目录下。

解决方式:

  1. objc-shared-cache.hdyld-852.2/include目录下copy到JQDependencies/目录下
报错17:'objc-bp-assist.h' file not found

解决方式:

  • 注释掉objc-bp-assist.h这个文件的引用
报错18:Use of undeclared identifier 'dyld_platform_version_macOS_10_13'

解决方式:

  • 注释掉该段代码


    29.png

    30.png
报错19:Use of undeclared identifier 'dyld_platform_version_macOS_10_11'

解决方式:

  • 注释掉该段代码


    31.png
32.png
报错20:Use of undeclared identifier 'dyld_fall_2018_os_versions'

解决方式:

  • 注释掉该段代码


    33.png

    34.png
报错21:'_simple.h' file not found

同样是找到文件位置,_simple.h'文件在libplatform-libplatform-220.100.1/private目录下。

解决方式:

  1. _simple.hlibplatform-libplatform-220.100.1/private目录下copy到JQDependencies/目录下
报错22:'os/linker_set.h' file not found

同样是找到文件位置,linker_set.h'文件在xnu-7195.141.2/bsd/sys目录下。

解决方式:

  1. linker_set.hxnu-7195.141.2/bsd/sys目录下copy到JQDependencies/os目录下
报错23:'Cambria/Traps.h' file not found

解决方式:

  • 文件开源网站找不到 ,选择注释头文件引用


    35.png
报错24:'Block_private.h' file not found

同样是文件缺失,Block_private.h这个文件在
libclosure-79/目录下

解决方式:

  1. Block_private.hlibclosure-79/目录下copy到JQDependencies/目录下
报错25:'kern/restartable.h' file not found

同样是文件缺失,restartable.h这个文件在
xnu-7195.141.2/osfmk/kern/目录下

解决方式:

  1. JQDependencies/目录下创建一个kern文件夹
  2. restartable.hxnu-7195.141.2/osfmk/kern/目录下copy到JQDependencies/kern/目录下
报错26:**Use of undeclared identifier 'oah_is_current_process_translated' 和

Use of undeclared identifier 'objc_thread_get_rip'**


36.png

解决方式:

  • objc_cache.mm文件中注释掉1121-1128行,保留1127行,如图:
    37.png
报错27:'os/feature_private.h' file not found

解决方式:

  • 这个引用头文件直接注释掉


    38.png
报错28:'os/reason_private.h' file not found

同样是文件缺失,reason_private.h这个文件在
xnu-7195.141.2/libkern/os/目录下

解决方式:

  1. reason_private.hxnu-7195.141.2/libkern/os/目录下copy到JQDependencies/os/目录下
报错29:'os/variant_private.h' file not found

同样是文件缺失,variant_private.h这个文件在
Libc-1439.141.1/os/目录下

解决方式:

  1. variant_private.hLibc-1439.141.1/os/目录下copy到JQDependencies/os/目录下
报错30:Use of undeclared identifier 'dyld_platform_version_bridgeOS_2_0'
39.png

解决方式:

  • 注释掉这5行代码


    40.png
报错31:/os/variant_private.h:243:75: Expected ','
41.png

解决方式:

  • 删除bridgeos这个参数
报错32:** '_static_assert' declared as an array with a negative size**
42.png

解决方式:

  • 注释掉这两行代码


    43.png
报错33:Can't open order file: /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/AppleInternal/OrderFiles/libobjc.order
44.png

解决方式:

  • target -> objc -> Build Settings,在工程的Order File中添加搜索路径$(SRCROOT)/libobjc.order
45.png
报错34:Library not found for -lCrashReporterClient

没有找到CrashReporterClient这个Library库,


46.png

解决方式:

  • target -> objc -> Build Settings,在工程的Other Link Flags中删除-lCrashReporterClient
    47.png
报错35:Library not found for -loah

和报错34类似,没有找到 oah 这个Library库

解决方式:

  • target -> objc -> Build Settings,在工程的Other Link Flags中删除-loah
    48.png
报错36:xcodebuild:1:1: SDK "macosx.internal" cannot be located.

解决方式:

  • 选择 target -> objc -> Build Phases -> Run Script(markgc)
  • 把脚本文本macosx.internal改成macosx,和刚开始我们选的base sdk中的macosx对齐
49.png

OK!到此处,再次编译,** Build Succeeded**。objc这个源码库终于可以编译成功了!


50.png

三、objc源码调试

1. 新建target:JQObjcBuildDemo
51.png

52.png
2. 给JQObjcBuildDemo添加依赖库
  • 选择 target -> JQObjcBuildDemo -> General -> Frameworks and Libraries ,点击"+"添加 libobjc.A.dylib
    53.png
54.png
  • 然后检查一下,依赖库有没有添加进来。选择 target -> objc -> Build Phases,看一下DependenciesLink Binary With Libraries,如下图一样添加上了,就OK了!(ps: 如果没有添加,这里手动再添加一次。)
    55.png
3. 调试objc源码
  1. Build Settings中将Enable Hardened Runtime设置为NO,否则在objc源码下的断点没法断住,这个是Xcode 11的新特性。
56.png
  1. 新建JQPerson类
  2. main.m中引入并实例化一个JQPerson
  3. 编译运行,如图
57.png
  1. 按住command,点击[JQPerson alloc]alloc,跳转到objc的源码中+ (id)alloc{,在这里打上断点
58.png

59.png
  1. 注意:源码中+ (id)alloc{这个断点要先禁用,等到程序断点断在了JQPerson p = [JQPerson alloc];这一行的时候,再去objc源码中开启+ (id)alloc{*这个方法的断点。否则,项目一运行,会有很多alloc方法断住,一步步执行,你会爆炸的。切记!!!
60.png

61.png

62.png

好了,到此,我们就可以愉快的进行objc的源码调试了,感兴趣的小伙伴赶紧来试试吧!

四、总结

  1. 项目中缺失的文件可能在你下载的依赖库中找不到,这个时候就要考虑一下去依赖库的新版本或者旧版本中找一下。比如我上面遇到的:'System/pthread_machdep.h'文件只有在Libc-825.40.1及以下版本才有。
  2. 缺失文件汇总
# 缺失文件名                               (对应的依赖库路径)          
1. 'sys/reason.h' file not found                         (xnu-7195.141.2/bsd/sys)
2. 'mach-o/dyld_priv.h' file not found                   (dyld-852.2/include/mach-o)
3. 'os/lock_private.h' file not found                    (libplatform-254.80.2/private/os)
4. 'os/base_private.h' file not found                    (libplatform-254.80.2/private/os)
5. 'pthread/tsd_private.h' file not found                (libpthread-libpthread-454.120.2/private/pthread)
6. 'pthread/spinlock_private.h' file not found           (libpthread-libpthread-454.120.2/private/pthread)
7. 'System/machine/cpu_capabilities.h' file not found    (xnu-7195.141.2/osfmk/machine)
8. 'os/tsd.h' file not found                             (xnu-7195.141.2/libsyscall/os)
9. 'System/pthread_machdep.h' file not found             (Libc-825.40.1/pthreads)
10. 'CrashReporterClient.h' file not found               (Libc-825.40.1/include)
11. 'objc-shared-cache.h' file not found                 (dyld-852.2/include)
12. 'objc-bp-assist.h' file not found                    文件的引用注释掉
13. '_simple.h' file not found                           (libplatform-libplatform-220.100.1/private)
14. 'os/linker_set.h' file not found                     (xnu-7195.141.2/bsd/sys)
15. 'Cambria/Traps.h' file not found                     文件在开源网站找不到 ,文件的引用注释掉
16. 'Block_private.h' file not found                    (libclosure-79/)
17. 'kern/restartable.h' file not found                 (xnu-7195.141.2/osfmk/kern/)
18. 'os/feature_private.h' file not found                文件的引用注释掉
19. 'os/reason_private.h' file not found                (xnu-7195.141.2/libkern/os/)
20. 'os/variant_private.h' file not found               (Libc-1439.141.1/os/)
  1. 已经完成 objc4-818 源码项目,大家可以直接去GitHub下载:JQObjc4-818.2BuildDebug

五、参考文章

最新macOS 10.15下objc4-779.1源码编译调试

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,841评论 5 472
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,415评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,904评论 0 333
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,051评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,055评论 5 363
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,255评论 1 278
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,729评论 3 393
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,377评论 0 255
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,517评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,420评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,467评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,144评论 3 317
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,735评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,812评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,029评论 1 256
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,528评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,126评论 2 341