如何看clang编译后的C语言代码  

这是一篇比较轻松的文章,说一说clang编译后的代码怎么看。clang编译后的是C++的源代码,但其实也仅是用到了struct结构,其本质是C语言源代码,所以标题里就写“C语言代码”了。10多行的代码在编译之后能达到上万行代码,如果是第一次看,还是会有点懵的,所以记录一下要如何找到自己想要的核心代码,以便有小伙伴想要了解这块时,少走一点弯路。

准备OC代码,并编译

OC的代码写的越多,引用的Framework越多,编译后的代码量也会越多,就越影响自己阅读代码。我这里就想看一下编译后的OC类、实例对象、方法等等是怎样的关联,所以只引用了<Foundation/Foundation.h>就够用了。完整的OC代码如下,一会将编译这份代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// main.m

#import <Foundation/Foundation.h>
@interface EOCObject : NSObject
@property (nonatomic, copy) NSString *name;
@end

@implementation EOCObject
- (void)work {
NSLog(@"I'm working!");
}
@end

int main(int argc, char * argv[]) {
@autoreleasepool {
EOCObject *obj = [[EOCObject alloc] init];
[obj work];
return 0; // 为了简便,这里可以直接返回0。
}
}

现在打开终端,执行如下命令:

1
clang -rewrite-objc main.m

警告可以忽略,没有报error就可以,然后在与main.m同一个目录下会生成一个main.cpp文件。

如果引用了其他Framework包,比如<UIKit/UIKit.h>,可以指定使用模拟器中的SDK编译:

1
xcrun -sdk iphonesimulator clang -rewrite-objc -fobjc-arc main.m

分析生成的.cpp文件

现在打开刚才生成的main.cpp文件,从上面往下看。上面定义了一堆基本类型结构,是编译任何一个.m文件都会有的。

首先声明了objc_selector,也就是SEL。还有objc_class,OC中类的struct结构。然后是超类结构和定义Protocol

1
2
3
4
5
6
7
8
9
10
11
12
13
#ifndef __OBJC2__
#define __OBJC2__
#endif
struct objc_selector; struct objc_class;
struct __rw_objc_super {
struct objc_object *object;
struct objc_object *superClass;
__rw_objc_super(struct objc_object *o, struct objc_object *s) : object(o), superClass(s) {}
};
#ifndef _REWRITER_typedef_Protocol
typedef struct objc_object Protocol;
#define _REWRITER_typedef_Protocol
#endif

接着导入需要的基本方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
#define __OBJC_RW_DLLIMPORT extern
__OBJC_RW_DLLIMPORT void objc_msgSend(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper(void);
__OBJC_RW_DLLIMPORT void objc_msgSend_stret(void);
__OBJC_RW_DLLIMPORT void objc_msgSendSuper_stret(void);
__OBJC_RW_DLLIMPORT void objc_msgSend_fpret(void);
__OBJC_RW_DLLIMPORT struct objc_class *objc_getClass(const char *);
__OBJC_RW_DLLIMPORT struct objc_class *class_getSuperclass(struct objc_class *);
__OBJC_RW_DLLIMPORT struct objc_class *objc_getMetaClass(const char *);
__OBJC_RW_DLLIMPORT void objc_exception_throw( struct objc_object *);
__OBJC_RW_DLLIMPORT int objc_sync_enter( struct objc_object *);
__OBJC_RW_DLLIMPORT int objc_sync_exit( struct objc_object *);
__OBJC_RW_DLLIMPORT Protocol *objc_getProtocol(const char *);

然后定义了NSUInteger的类型。

1
2
3
4
5
#ifdef _WIN64
typedef unsigned long long _WIN_NSUInteger;
#else
typedef unsigned int _WIN_NSUInteger;
#endif

接着是对枚举的定义。

1
2
3
4
5
6
7
8
9
10
#ifndef __FASTENUMERATIONSTATE
struct __objcFastEnumerationState {
unsigned long state;
void **itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
};
__OBJC_RW_DLLIMPORT void objc_enumerationMutation(struct objc_object *);
#define __FASTENUMERATIONSTATE
#endif

然后是对常量字符串的定义,它的isa取值为__CFConstantStringClassReference,代码中出现的所有常量字符串类型都会转换成该类型,并且放在内存中的数据区域(.data区)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#ifndef __NSCONSTANTSTRINGIMPL
struct __NSConstantStringImpl {
int *isa;
int flags;
char *str;
#if _WIN64
long long length;
#else
long length;
#endif
};
#ifdef CF_EXPORT_CONSTANT_STRING
extern "C" __declspec(dllexport) int __CFConstantStringClassReference[];
#else
__OBJC_RW_DLLIMPORT int __CFConstantStringClassReference[];
#endif
#define __NSCONSTANTSTRINGIMPL
#endif

接着是对Block的定义,可以看到Block实际上是void *类型,它的取值这里只导入了两个_NSConcreteGlobalBlock_NSConcreteStackBlock,其实还有一个_NSConcreteMallocBlock_Block_object_assign是对一个Block进行copy时调用的,_Block_object_dispose就是销毁的时候了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#ifndef BLOCK_IMPL
#define BLOCK_IMPL
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// Runtime copy/destroy helper functions (from Block_private.h)
#ifdef __OBJC_EXPORT_BLOCKS
extern "C" __declspec(dllexport) void _Block_object_assign(void *, const void *, const int);
extern "C" __declspec(dllexport) void _Block_object_dispose(const void *, const int);
extern "C" __declspec(dllexport) void *_NSConcreteGlobalBlock[32];
extern "C" __declspec(dllexport) void *_NSConcreteStackBlock[32];
#else
__OBJC_RW_DLLIMPORT void _Block_object_assign(void *, const void *, const int);
__OBJC_RW_DLLIMPORT void _Block_object_dispose(const void *, const int);
__OBJC_RW_DLLIMPORT void *_NSConcreteGlobalBlock[32];
__OBJC_RW_DLLIMPORT void *_NSConcreteStackBlock[32];
#endif
#endif
#define __block
#define __weak

下面是对OC中容器类型的定义。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <stdarg.h>
struct __NSContainer_literal {
void * *arr;
__NSContainer_literal (unsigned int count, ...) {
va_list marker;
va_start(marker, count);
arr = new void *[count];
for (unsigned i = 0; i < count; i++)
arr[i] = va_arg(marker, void *);
va_end( marker );
};
~__NSContainer_literal() {
delete[] arr;
}
};

接着是与autoreleasePool相关的方法导入和定义。

1
2
3
4
5
6
7
8
extern "C" __declspec(dllimport) void * objc_autoreleasePoolPush(void);
extern "C" __declspec(dllimport) void objc_autoreleasePoolPop(void *);

struct __AtAutoreleasePool {
__AtAutoreleasePool() {atautoreleasepoolobj = objc_autoreleasePoolPush();}
~__AtAutoreleasePool() {objc_autoreleasePoolPop(atautoreleasepoolobj);}
void * atautoreleasepoolobj;
};

然后是定义一个宏,来计算struct结构中变量的偏移量。

1
#define __OFFSETOFIVAR__(TYPE, MEMBER) ((long long) &((TYPE *)0)->MEMBER)

到此对OC中的一些基本结构的定义就结束了。下面一段是对我们自己写的代码中的字符串的定义。通过section ("__DATA, __cfstring")可以看到,常量字符串放置在了内存中的数据区域。

1
static __NSConstantStringImpl __NSConstantStringImpl__var_folders_nx_dxppf5lj151119zfqd6lrc_m0000gn_T_main_obj_80f1ba_mi_0 __attribute__ ((section ("__DATA, __cfstring"))) = {__CFConstantStringClassReference,0x000007c8,"I'm working!",12};

接下来有很长一段的空行,然后应该是我们OC中导入的头文件的转换。可以直接跳过,这一部分对于分析我们自己的代码作用不是很大。全局搜索#pragma clang assume_nonnull end,最后一次出现的地方的下方就是对应我们上面写的OC代码的转换了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#ifndef _REWRITER_typedef_EOCObject
#define _REWRITER_typedef_EOCObject
typedef struct objc_object EOCObject;
typedef struct {} _objc_exc_EOCObject;
#endif

extern "C" unsigned long OBJC_IVAR_$_EOCObject$_name;
struct EOCObject_IMPL {
struct NSObject_IMPL NSObject_IVARS;
NSString *__strong _name;
};

// @property (nonatomic, copy) NSString *name;
/* @end */

声明一个struct objc_object类型的变量EOCObject,已经定义它的内部变量_name

1
2
3
4
5
6
7
8
9
10
11
// @implementation EOCObject

static void _I_EOCObject_work(EOCObject * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_nx_dxppf5lj151119zfqd6lrc_m0000gn_T_main_obj_f18233_mi_0);
}

static NSString * _I_EOCObject_name(EOCObject * self, SEL _cmd) { return (*(NSString *__strong *)((char *)self + OBJC_IVAR_$_EOCObject$_name)); }
extern "C" __declspec(dllimport) void objc_setProperty (id, SEL, long, id, bool, bool);

static void _I_EOCObject_setName_(EOCObject * self, SEL _cmd, NSString *name) { objc_setProperty (self, _cmd, __OFFSETOFIVAR__(struct EOCObject, _name), (id)name, 0, 1); }
// @end

EOCObject中的3个函数实现,后面会用到此处的函数指针。

1
2
3
4
5
6
7
8

int main(int argc, char * argv[]) {
/* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool;
EOCObject *obj = ((EOCObject *(*)(id, SEL))(void *)objc_msgSend)((id)((EOCObject *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("EOCObject"), sel_registerName("alloc")), sel_registerName("init"));
((void (*)(id, SEL))(void *)objc_msgSend)((id)obj, sel_registerName("work"));
return 0;
}
}

对应函数main中代码的转换,方法调用都转成了用objc_msgSend调用。因为objc_msgSend返回的是void *,所以在使用时需要对其强制转换,所以看起起来啰嗦了一些。把((void (*)(id, SEL))(void *)objc_msgSend)看成一个整体就好。

再接下来,又是一堆结构体的定义,包括_prop_t_objc_method_protocol_t_ivar_t_class_ro_t_class_t_category_t,这一段代码比较简单,不贴代码出来了,太长了…

然后就是对我们在OC中定义的对象的处理过程,给各种struct赋值的一个过程。仔细阅读可以对我们平时在写的OC代码有一个更清晰的认识。下面依次说一下这段里的每一部分。

1
extern "C" unsigned long int OBJC_IVAR_$_EOCObject$_name __attribute__ ((used, section ("__DATA,__objc_ivar"))) = __OFFSETOFIVAR__(struct EOCObject, _name);

利用上面的宏OFFSETOFIVAR计算_name的偏移量,赋值给变量OBJC_IVAR_$_EOCObject$_name,然后存在内存中的数据区域。

1
2
3
4
5
6
7
8
9
static struct /*_ivar_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count;
struct _ivar_t ivar_list[1];
} _OBJC_$_INSTANCE_VARIABLES_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_ivar_t),
1,
{{(unsigned long int *)&OBJC_IVAR_$_EOCObject$_name, "_name", "@\"NSString\"", 3, 8}}
};

拿到EOCObject的内部成员变量信息,赋值给变量_OBJC_$_INSTANCE_VARIABLES_EOCObject,每个成员变量的信息存在了_ivar_t类型的数组ivar_list中。

1
2
3
4
5
6
7
8
9
10
11
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_INSTANCE_METHODS_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"work", "v16@0:8", (void *)_I_EOCObject_work},
{(struct objc_selector *)"name", "@16@0:8", (void *)_I_EOCObject_name},
{(struct objc_selector *)"setName:", "v24@0:8@16", (void *)_I_EOCObject_setName_}}
};

拿到EOCObject的方法信息,赋值给变量_OBJC_$_INSTANCE_METHODS_EOCObject,每个方法的信息存在_objc_method类型的数组method_list中。_objc_method由方法的名字,如(struct objc_selector *)"work",和方法类型,如v16@0:8(冒号前面表示返回值,后面表示参数),还有指向实际去执行的函数的指针,如(void *)_I_EOCObject_work_I_EOCObject_work在main的上面定义过。

1
2
3
4
5
6
7
8
9
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"name","T@\"NSString\",C,N,V_name"}}
};

拿到EOCObject定义在property中的属性信息,赋值给_OBJC_$_PROP_LIST_EOCObject,每个属性的信息存在_prop_t类型的数组prop_list中。

1
2
3
4
5
6
7
8
9
10
11
static struct _class_ro_t _OBJC_METACLASS_RO_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1, sizeof(struct _class_t), sizeof(struct _class_t),
(unsigned int)0,
0,
"EOCObject",
0,
0,
0,
0,
0,
};

拿到EOCObject的元类信息,赋值给_OBJC_METACLASS_RO_$_EOCObject,可以看到EOCObject的元类也是EOCObject,但是方法列表等都是空的,因为我们没有在OC中给EOCObject定义类方法等。

1
2
3
4
5
6
7
8
9
10
11
static struct _class_ro_t _OBJC_CLASS_RO_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_const"))) = {
0, __OFFSETOFIVAR__(struct EOCObject, _name), sizeof(struct EOCObject_IMPL),
(unsigned int)0,
0,
"EOCObject",
(const struct _method_list_t *)&_OBJC_$_INSTANCE_METHODS_EOCObject,
0,
(const struct _ivar_list_t *)&_OBJC_$_INSTANCE_VARIABLES_EOCObject,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_EOCObject,
};

那到EOCObject类本身的一些信息,赋值给_OBJC_CLASS_RO_$_EOCObject,这里就把刚刚准备好的_OBJC_$_INSTANCE_METHODS_EOCObject_OBJC_$_INSTANCE_VARIABLES_EOCObject_OBJC_$_PROP_LIST_EOCObject关联了起来。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
extern "C" __declspec(dllimport) struct _class_t OBJC_METACLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_METACLASS_$_EOCObject __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_$_EOCObject,
};

extern "C" __declspec(dllimport) struct _class_t OBJC_CLASS_$_NSObject;

extern "C" __declspec(dllexport) struct _class_t OBJC_CLASS_$_EOCObject __attribute__ ((used, section ("__DATA,__objc_data"))) = {
0, // &OBJC_METACLASS_$_EOCObject,
0, // &OBJC_CLASS_$_NSObject,
0, // (void *)&_objc_empty_cache,
0, // unused, was (void *)&_objc_empty_vtable,
&_OBJC_CLASS_RO_$_EOCObject,
};

_class_ro_t只是存了类的基本信息,但是没有存它的isa指向,和它的父类,于是结构体_class_t又封装了一次,并且加上缓存。所以上面这段得到的就是元类NSObject、元类EOCObject、类NSObject、类EOCObject

1
2
3
4
5
6
7
8
static void OBJC_CLASS_SETUP_$_EOCObject(void ) {
OBJC_METACLASS_$_EOCObject.isa = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_EOCObject.superclass = &OBJC_METACLASS_$_NSObject;
OBJC_METACLASS_$_EOCObject.cache = &_objc_empty_cache;
OBJC_CLASS_$_EOCObject.isa = &OBJC_METACLASS_$_EOCObject;
OBJC_CLASS_$_EOCObject.superclass = &OBJC_CLASS_$_NSObject;
OBJC_CLASS_$_EOCObject.cache = &_objc_empty_cache;
}

该方法就把上面的各种类之间的关系串起来了,元类EOCObjectisa和父类都指向元类NSObject,类EOCObjectisa指向元类EOCObject,它的父类指向类NSObject

1
2
3
4
#pragma section(".objc_inithooks$B", long, read, write)
__declspec(allocate(".objc_inithooks$B")) static void *OBJC_CLASS_SETUP[] = {
(void *)&OBJC_CLASS_SETUP_$_EOCObject,
};

此处应该是一个钩子,在初始化时执行上面的OBJC_CLASS_SETUP_$_EOCObject函数,对类进行设置。

1
2
3
static struct _class_t *L_OBJC_LABEL_CLASS_$ [1] __attribute__((used, section ("__DATA, __objc_classlist,regular,no_dead_strip")))= {
&OBJC_CLASS_$_EOCObject,
};

拿到当前的文件中所有的自定义类,暂时还没有看到变量L_OBJC_LABEL_CLASS_$的用处。

本文作者:意林
本文链接:http://www.shinancao.cn/2019/06/03/iOS-clang-1
版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 3.0 许可协议。转载请注明出处!