理论
编译器编译后生成的文件叫目标文件,从文件结构来说,它已经是编译后可执行的文件,只是还没有经过链接的过程。我们程序编译后的代码在.text,数据.data中。编译器函数在编译时把数据写入可执行文件的.data段中,运行时再从.data段中取出数据进行相应的操作。借助.data段,能够覆盖所有的启动阶段,例如main()之前的阶段。
Clang提供了很多的编译器函数,它们可以完成不同的功能。其中一种是section()函数,section()函数提供了二进制段的读写能力,它可以将一些编译期就可以确定的常量写入数据段。在具体的实现中,主要分为编译期和运行时两个部分。在编译期,编译器会将标记了attribute((section()))的数据写到指定的数据段,例如写一个{key(key代表不同的启动阶段), *pointer}对到数据段。到运行时,在合适的时间节点,再根据key读取出函数指针,完成函数的调用。
使用_ _attribute方法注入
/**
被used修饰以后,意味着即使函数没有被引用,在Release下也不会被优化。如果不加这个修饰,那么Release环境链接器下会去掉没有被引用的段
*/
#define HMAnnotationDATA __attribute((used, section("__DATA,HMSectionStore")))
/**
* Use this to annotation a `module`
* like this: HMSectionStoreModule()
*/
#define HMSectionStoreModule(modName) \
char * kHMSectionStoreModule_##modName HMAnnotationDATA = "M:"#modName"";
/**
调用上面定义好的宏
*/
HMSectionStoreModule(HMSectionStoreViewController)
获取存储数据
NSArray<NSString *>* HMSectionStoreReadConfigFromSection(const char *sectionName){
#ifndef __LP64__
const struct mach_header *mhp = NULL;
#else
const struct mach_header_64 *mhp = NULL;
#endif
NSMutableArray *configs = [NSMutableArray array];
Dl_info info;
if (mhp == NULL) {
dladdr(HMSectionStoreReadConfigFromSection, &info);
#ifndef __LP64__
mhp = (struct mach_header*)info.dli_fbase;
#else
mhp = (struct mach_header_64*)info.dli_fbase;
#endif
}
#ifndef __LP64__
unsigned long size = 0;
// 找到之前存储的数据段的一片内存
uint32_t *memory = (uint32_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
#else /* defined(__LP64__) */
unsigned long size = 0;
uint64_t *memory = (uint64_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
#endif /* defined(__LP64__) */
for(int idx = 0; idx < size/sizeof(void*); ++idx){
char *string = (char*)memory[idx];
// 把特殊段里面的数据都转换成字符串存入数组中
NSString *str = [NSString stringWithUTF8String:string];
if(!str)continue;
if(str) [configs addObject:str];
}
return configs;
}
完整的代码
#import "HMSectionStoreViewController.h"
#include <mach-o/getsect.h>
#include <mach-o/loader.h>
#include <mach-o/dyld.h>
#include <dlfcn.h>
#define HMAnnotationDATA __attribute((used, section("__DATA,HMSectionStore")))
/**
* Use this to annotation a `module`
* like this: @HMSectionStoreModule()
*/
#define HMSectionStoreModule(modName) \
char * kHMSectionStoreModule_##modName HMAnnotationDATA = "M:"#modName"";
NSArray<NSString *>* HMSectionStoreReadConfigFromSection(const char *sectionName){
#ifndef __LP64__
const struct mach_header *mhp = NULL;
#else
const struct mach_header_64 *mhp = NULL;
#endif
NSMutableArray *configs = [NSMutableArray array];
Dl_info info;
if (mhp == NULL) {
dladdr(HMSectionStoreReadConfigFromSection, &info);
#ifndef __LP64__
mhp = (struct mach_header*)info.dli_fbase;
#else
mhp = (struct mach_header_64*)info.dli_fbase;
#endif
}
#ifndef __LP64__
unsigned long size = 0;
// 找到之前存储的数据段的一片内存
uint32_t *memory = (uint32_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
#else /* defined(__LP64__) */
unsigned long size = 0;
uint64_t *memory = (uint64_t*)getsectiondata(mhp, SEG_DATA, sectionName, & size);
#endif /* defined(__LP64__) */
for(int idx = 0; idx < size/sizeof(void*); ++idx){
char *string = (char*)memory[idx];
// 把特殊段里面的数据都转换成字符串存入数组中
NSString *str = [NSString stringWithUTF8String:string];
if(!str)continue;
if(str) [configs addObject:str];
}
return configs;
}
/**
编译的时候,存储数据到.data中
*/
HMSectionStoreModule(HMSectionStoreViewController)
@interface HMSectionStoreViewController ()
@end
@implementation HMSectionStoreViewController
- (void)viewDidLoad {
[super viewDidLoad];
//获取注入的数据
NSArray<NSString *> *dataListInSection = HMSectionStoreReadConfigFromSection("HMSectionStore");
for (NSString *dataString in dataListInSection) {
NSLog(@"获取section中存储的数据--> %@",dataString);
}
}
@end
运行结果
运用
路由协议中,模块类容加载到内存中。可参考
AppLord
BeeHive