自定义section注册Objective-C类对象

5,030 阅读2分钟

背景

如果想在 自定义section 存储具体服务类,实现服务的控制反转,在常见的做法是定义一个字符串指针,指向目标类的类名,用运行时的方法获得目标类。下面列出一个简单的例子

// 定义
char *className = "MyClass" __attribute((used, section("__DATA,__mysection")));

// 读取
const struct mach_header_64 *mhp = (const struct mach_header_64 *)_dyld_get_image_header(0);  // 注:主macho不一定是0
const char *className = (const char *)getsectiondata(mhp, "__DATA", "__mysection", NULL);
Class classObjc = NSClassFromString([NSString stringWithFormat:@"%s", className]);

本文将介绍另外一种写法,直接在 自定义section 存储Objective-C的类对象,就像 __objc_classlistsection 一样,大概效果如下。

// 定义
Class classObjc = [MyClass class] __attribute((used, section("__DATA,__mysection")));

// 读取
const struct mach_header_64 *mhp = (const struct mach_header_64 *)_dyld_get_image_header(0); // 注:主macho不一定是0 
Class classObjc = (Class)getsectiondata(mhp, "__DATA", "__mysection", NULL);

完整代码地址

推理步骤

  1. 定义一个空类文件 objc.m
@interface MyClass : NSObject
@end

@implementation MyClass
@end
  1. 用命令重写成C++文件 objc.cpp
clang -framework Foundation -rewrite-objc -fobjc-arc objc.m -o objc.cpp
  1. 从C++文件 objc.cpp 提取代码
  • 类结构体定义(struct _class_t)
// 类结构体
struct _class_t {
    struct _class_t *isa;
    struct _class_t *superclass;
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;
};

// 以下是其它相关的结构体
struct _prop_t {
    const char *name;
    const char *attributes;
};
struct _protocol_t;
struct _objc_method {
    struct objc_selector * _cmd;
    const char *method_type;
    void  *_imp;
};
struct _protocol_t {
    void * isa;  // NULL
    const char *protocol_name;
    const struct _protocol_list_t * protocol_list; // super protocols
    const struct method_list_t *instance_methods;
    const struct method_list_t *class_methods;
    const struct method_list_t *optionalInstanceMethods;
    const struct method_list_t *optionalClassMethods;
    const struct _prop_list_t * properties;
    const unsigned int size;  // sizeof(struct _protocol_t)
    const unsigned int flags;  // = 0
    const char ** extendedMethodTypes;
};
struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;
    const char *type;
    unsigned int alignment;
    unsigned int  size;
};
struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    unsigned int reserved;
    const unsigned char *ivarLayout;
    const char *name;
    const struct _method_list_t *baseMethods;
    const struct _objc_protocol_list *baseProtocols;
    const struct _ivar_list_t *ivars;
    const unsigned char *weakIvarLayout;
    const struct _prop_list_t *properties;
};
  • MyClass 的类数据
// MyClass的类数据
extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_MyClass __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_MyClass,
    0, // &OBJC_CLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_CLASS_RO_$_MyClass,
};

// 以下是其它相关数据
extern "C" __declspec(dllimport) struct objc_cache _objc_empty_cache;
static struct _class_ro_t _OBJC_METACLASS_RO_$_MyClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    1, sizeof(struct _class_t), sizeof(struct _class_t), 
    (unsigned int)00"MyClass",
    00000, 
};
static struct _class_ro_t _OBJC_CLASS_RO_$_MyClass __attribute__ ((used, section ("__DATA,__objc_const"))) = {
    0, sizeof(struct MyClass_IMPL), sizeof(struct MyClass_IMPL), 
    (unsigned int)00"MyClass",
    00000, 
};
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;
extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_MyClass __attribute__ ((used, section ("__DATA,__objc_data"))) = {
    0, // &OBJC_METACLASS_$_NSObject,
    0, // &OBJC_METACLASS_$_NSObject,
    0, // (void *)&_objc_empty_cache,
    0, // unused, was (void *)&_objc_empty_vtable,
    &_OBJC_METACLASS_RO_$_MyClass,
};
extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;

从C++文件 objc.cpp 提取的代码,大致可以获取以下信息

信息例子
类的符号格式OBJC_CLASS_$_ + 类名
指向类对象地址写法&OBJC_CLASS_$_NSObject

结论

自定义section 存储Objective-C的类对象,代码如下

// 结构体声明
struct _prop_t {
    const char *name;
    const char *attributes;
};
struct _protocol_t;
struct _objc_method {
    struct objc_selector * _cmd;
    const char *method_type;
    void  *_imp;
};
struct _protocol_t {
    void * isa;  // NULL
    const char *protocol_name;
    const struct _protocol_list_t * protocol_list; // super protocols
    const struct method_list_t *instance_methods;
    const struct method_list_t *class_methods;
    const struct method_list_t *optionalInstanceMethods;
    const struct method_list_t *optionalClassMethods;
    const struct _prop_list_t * properties;
    const unsigned int size;  // sizeof(struct _protocol_t)
    const unsigned int flags;  // = 0
    const char ** extendedMethodTypes;
};
struct _ivar_t {
    unsigned long int *offset;  // pointer to ivar offset location
    const char *name;
    const char *type;
    unsigned int alignment;
    unsigned int  size;
};
struct _class_ro_t {
    unsigned int flags;
    unsigned int instanceStart;
    unsigned int instanceSize;
    unsigned int reserved;
    const unsigned char *ivarLayout;
    const char *name;
    const struct _method_list_t *baseMethods;
    const struct _objc_protocol_list *baseProtocols;
    const struct _ivar_list_t *ivars;
    const unsigned char *weakIvarLayout;
    const struct _prop_list_t *properties;
};
struct _class_t {
    struct _class_t *isa;
    struct _class_t *superclass;
    void *cache;
    void *vtable;
    struct _class_ro_t *ro;
};

// 定义
__attribute__((dllimport)) struct _class_t OBJC_CLASS_$_MyClass;
struct _class_t *classObjc = &OBJC_CLASS_$__MyClass __attribute((used, section("__DATA,__mysection")));

// 读取
const struct mach_header_64 *mhp = (const struct mach_header_64 *)_dyld_get_image_header(0); // 注:主macho不一定是0 
Class classObjc = (Class)getsectiondata(mhp, "__DATA", "__mysection", NULL);

More

  • 这种写法,可以利用编译检查拼写的正确性,并减少读取步骤。