一、开发环境

mac OS 10.15.7
Xcode Version 12.4 (12D4e)

源码下载地址


二、需要下载的库 点击进入查找下载


三、配置下载库文件


1.在objc4-818.2项目的根目录创建一个Common文件夹

2.Common文件夹里的结构如下:

3.Common文件内容相对下载的库文件

  1. _simple.h = libplatform-254.60.1/private/_simple.h

  2. Block_private.h = libclosure-78/Block_private.h

  3. CrashReporterClient.h = Libc-825.24/include/CrashReporterClient.h

  4. kern/restartable.h = xnu-7195.60.75/osfmk/kern/restartable.h

  5. mach-o/dyld_priv.h = dyld-832.7.1/include/mach-o/dyld_priv.h

  6. objc-shared-cache.h = dyld-832.7.1/include/objc-shared-cache.h

  7. os/base_private = xnu-7195.60.75/libkern/os/base_private.h

  8. os/lock_private = libplatform-254.60.1/private/os/lock_private.h

  9. os/tsd = xnu-7195.60.75/libsyscall/os/tsd.h

  10. pthread/spinlock_private.h = libpthread-454.60.1/private/pthread/spinlock_private.h

  11. pthread/tsd_private = libpthread-454.60.1/private/pthread/tsd_private.h

  12. sys/reason.h = xnu-7195.60.75/bsd/sys/reason.h

  13. System/machine/cpu_capabilities.h = xnu-7195.60.75/osfmk/machine/cpu_capabilities.h

  14. System/pthread_machdep.h = Libc-583/pthreads/pthread_machdep.h


四、编译前配置

  1. 将baseSDK 选择为 macOS

  2. 将 objc 下的脚本内 macosx.internal 改成 macosx

  3. 在Header Search Paths 添加 Common 的文件路径 (这里只添加Debug就可以了)


五、开始编译,解决报错

  1. 我们会遇到 bridgeos(3.0)和 bridgeos(4.0) 找不到的问题。
    解决:因为Bridge OS是Apple独立的T2安全芯片使用的嵌入式操作系统, 而在这里我们用不到这个系统所以删掉,bridgeos(3.0)和,bridgeos(4.0)即可。

  2. CrashReporterClient.h报错。
    解决:在Build Settings -> Preprocessor Macros 中加入:LIBC_NO_LIBCRASHREPORTERCLIENT

  3. 因为找不到头文件/宏/方法的地方,需要注释的代码如下:
    (1) objc-runtime.mm

    #include <os/feature_private.h>
    
    if (!os_feature_enabled_simple(objc4, preoptimizedCaches, true)) {
        DisablePreoptCaches = true;
    }
    
    if (!dyld_program_sdk_at_least(dyld_fall_2020_os_versions))
    DisableAutoreleaseCoalescingLRU = true;
    
    

    (2)objc-runtime-new.mm

        if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_11)) {
            DisableNonpointerIsa = true;
            if (PrintRawIsa) {
                _objc_inform("RAW ISA: disabling non-pointer isa because "
                             "the app is too old.");
            }
        }
    
        dyld_program_sdk_at_least(dyld_fall_2018_os_versions)
    
        STATIC_ASSERT((~ISA_MASK & MACH_VM_MAX_ADDRESS) == 0  ||
              ISA_MASK + sizeof(void*) == MACH_VM_MAX_ADDRESS);
    

    (3)objc-oc.mm

            if (!dyld_program_sdk_at_least(dyld_platform_version_macOS_10_13)) {
            DisableInitializeForkSafety = true;
            if (PrintInitializing) {
                _objc_inform("INITIALIZE: disabling +initialize fork "
                             "safety enforcement because the app is "
                             "too old.)");
            }
        }
    

    (4)objc-class.mm

    #include <os/linker_set.h>
    
    LINKER_SET_FOREACH(_dupi, const objc_duplicate_class **, "__objc_dupclass") {
    const objc_duplicate_class *dupi = *_dupi;
    if (strcmp(dupi->name, name) == 0) {
            return;
        }
    }
    
    

    (5)objc-cache.mm

    #include <Cambria/Traps.h>
    #include <Cambria/Cambria.h>
    
    

    (6)NSObject.mm

    #include <os/feature_private.h>
    #include <os/reason_private.h>
    #include <os/variant_private.h>
    
    if (DebugPoolAllocation || sdkIsAtLeast(10_12, 10_0, 10_0, 3_0, 2_0)) {
            // OBJC_DEBUG_POOL_ALLOCATION or new SDK. Bad pop is fatal.
            _objc_fatal
            ("Invalid or prematurely-freed autorelease pool %p.", token);
    }
    

    (7)pthread_machdep.h

    inline static void *
    _pthread_getspecific_direct(unsigned long slot)
    {
            void *ret;
    #if defined(__i386__) || defined(__x86_64__)
    #if defined(__OPTIMIZE__)
            asm volatile("mov %%gs:%P1, %0" : "=r" (ret) : "i" (slot * sizeof(void *) + _PTHREAD_TSD_OFFSET));
    #else
            asm("mov %%gs:%P2(,%1,%P3), %0" : "=r" (ret) : "r" (slot), "i" (_PTHREAD_TSD_OFFSET), "i" (sizeof (void *)));
    #endif
    #elif defined(__ppc__)
            void **__pthread_tsd;
            asm volatile("mfspr %0, 259" : "=r" (__pthread_tsd));
            ret = __pthread_tsd[slot + (_PTHREAD_TSD_OFFSET / sizeof(void *))];
    #elif defined(__ppc64__)
            register void **__pthread_tsd asm ("r13");
            ret = __pthread_tsd[slot + (_PTHREAD_TSD_OFFSET / sizeof(void *))];
    #elif defined(__arm__) && defined(_ARM_ARCH_6)
        void **__pthread_tsd;
        __asm__ ("mrc p15, 0, %0, c13, c0, 3" : "=r"(__pthread_tsd));
        ret = __pthread_tsd[slot + (_PTHREAD_TSD_OFFSET / sizeof(void *))];
    #elif defined(__arm__) && !defined(_ARM_ARCH_6)
            register void **__pthread_tsd asm ("r9");
            ret = __pthread_tsd[slot + (_PTHREAD_TSD_OFFSET / sizeof(void *))];
    #else
    #error no pthread_getspecific_direct implementation for this arch
    #endif
            return ret;
    }
    
    

六、libobjc.order 路径问题 (这个文件其实就是苹果用来对底层源码进行的二进制重排)

这里也可以直接把根目录的libobjc.order 拖进来


七、Other Linker Flags 中删除 -lCrashReporterClient 和 -loah


以上全部操作完毕,command+B 编译成功!


开始调试

新建一个 Target:MyTest
绑定二进制依赖关系


最后可能遇到的问题


上图所示,我加一个MyTextObjc的类,发现MyTextObjc内断点可以走到,但是main.m断点不走。
此时查看 Build Settings -> Enable Hardended Runtime 也设置的为NO。
最后发现必须在Complie Sources 中设置main.m为第一个加载的文件。就解决了,具体原因,我们在以后的编译原理中继续探索。