在前面的文章中,笔者有讲解如何设置以及获取一个section的数据,demo以下:html
#import <Foundation/Foundation.h>
#import <dlfcn.h>
#include <mach-o/loader.h>
#include <mach-o/getsect.h>
#ifndef __LP64__
#define mach_header mach_header
#else
#define mach_header mach_header_64
#endif
const struct mach_header *machHeader = NULL;
static NSString *configuration = @"";
//设置"__DATA,__customSection"的数据为kyson
char *kString __attribute__((section("__DATA,__customSection"))) = (char *)"kyson";
int main(int argc, const char * argv[]) {
@autoreleasepool {
//设置machheader信息
if (machHeader == NULL)
{
Dl_info info;
dladdr((__bridge const void *)(configuration), &info);
machHeader = (struct mach_header_64*)info.dli_fbase;
}
unsigned long byteCount = 0;
uintptr_t* data = (uintptr_t *) getsectiondata(machHeader, "__DATA", "__customSection", &byteCount);
NSUInteger counter = byteCount/sizeof(void*);
for(NSUInteger idx = 0; idx < counter; ++idx)
{
char *string = (char*)data[idx];
NSString *str = [NSString stringWithUTF8String:string];
NSLog(@"%@",str);
}
}
return 0;
}
复制代码
本文就带你们详细分析一下,这段代码的含义。本文您将了解到ios
__attribute__
dladdr
uintptr_t
等含义__attribute__
关于__attribute__
,其实前面的文章有说过其中的一种用法:objective-c
__attribute__((constructor)) void myentry(){
NSLog(@"constructor");
}
复制代码
这段代码会优先于main方法执行。 很显然,其于通常方法不同的地方在于有修饰符:安全
__attribute__((constructor))
复制代码
那么__attribute__
修饰符有什么做用呢,这里引用一段:bash
This section describes the syntax with which
__attribute__
may be used, and the constructs to which attribute specifiers bind, for the C language. Some details may vary for C++ and Objective-C. Because of infelicities in the grammar for attributes, some forms described here may not be successfully parsed in all cases. There are some problems with the semantics of attributes in C++. For example, there are no manglings for attributes, although they may affect code generation, so problems may arise when attributed types are used in conjunction with templates or overloading. Similarly, typeid does not distinguish between types with different attributes. Support for attributes in C++ may be restricted in future to attributes on declarations only, but not on nested declarators.函数
以上摘自gcc:gcc.gnu.org/onlinedocs/… 这里笔者也摘抄了其余博客的一些论述:post
__attribute__
能够设置函数属性(Function Attribute)、变量属性(Variable Attribute)和类型属性(Type Attribute)。它的书写特征是:__attribute__
先后都有两个下划线,并切后面会紧跟一对原括弧,括弧里面是相应的__attribute__
参数,语法格式以下:__attribute__
((attribute-list)) 另外,它必须放于声明的尾部“;”以前。ui
这里再介绍一篇文章,用于讲解__attribute__
的做用: 不使用 NSOBJECT 的 OBJECTIVE-C CLASS 这篇文章讲解了atom
__attribute__((objc_root_class))
复制代码
的用法,完整的代码以下:spa
#import <stdio.h>
#import <stdlib.h>
#import <objc/runtime.h>
__attribute__((objc_root_class))
@interface Answer
{
Class isa;
}
+ (id)instantiate;
- (void)die;
@property(assign, nonatomic) int value;
@end
@implementation Answer
+ (id)instantiate
{
Answer *result = malloc(class_getInstanceSize(self));
result->isa = self;
return result;
}
- (void)die
{
free(self);
}
@end
int main(int argc, char const *argv[])
{
Answer *answer = [Answer instantiate];
answer.value = 42;
printf("The answer is: %d\n", answer.value);
[answer die];
return 0;
}
复制代码
回到本文开头的Demo,使用的是另一个属性:
__attribute__((section("__DATA,__customSection")))
复制代码
明显看出,是声明的一个变量属性。 这个变量要“被放到” section为“__DATA,__customSection”里面。这么一来就不难理解了。
dladdr
使用dladdr方法能够得到一个函数所在模块,名称以及地址。
下面咱们经过一个实例来讲明:
#include <dlfcn.h>
#include <objc/objc.h>
#include <objc/runtime.h>
#include <stdio.h>
#include <string.h>
int main()
{
Dl_info info;
IMP imp = class_getMethodImplementation(objc_getClass("NSArray"),sel_registerName("description"));
printf("pointer %p\n", imp);
if (dladdr(imp,&info))
{
printf("dli_fname: %s\n", info.dli_fname);
printf("dli_sname: %s\n", info.dli_sname);
printf("dli_fbase: %p\n", info.dli_fbase);
printf("dli_saddr: %p\n", info.dli_saddr);
} else
{
printf("error: can't find that symbol.\n");
}
}
复制代码
运行结果以下:
为了更好说明函数dladdr
做用,这里笔者再举个Demo:
static inline BOOL validate_methods(const char *cls,const char *fname) __attribute__ ((always_inline));
BOOL validate_methods(const char *cls,const char *fname){
Class aClass = objc_getClass(cls);
Method *methods;
unsigned int nMethods;
Dl_info info;
IMP imp;
char buf[128];
Method m;
if(!aClass)
return NO;
methods = class_copyMethodList(aClass, &nMethods);
while (nMethods--) {
m = methods[nMethods];
printf("validating [%s %s]\n",(const char *)class_getName(aClass),(const char *)method_getName(m));
imp = method_getImplementation(m);
//imp = class_getMethodImplementation(aClass, sel_registerName("allObjects"));
if(!imp){
printf("error:method_getImplementation(%s) failed\n",(const char *)method_getName(m));
free(methods);
return NO;
}
if(!dladdr(imp, &info)){
printf("error:dladdr() failed for %s\n",(const char *)method_getName(m));
free(methods);
return NO;
}
/*Validate image path*/
if(strcmp(info.dli_fname, fname))
goto FAIL;
if (info.dli_sname != NULL && strcmp(info.dli_sname, "<redacted>") != 0) {
/*Validate class name in symbol*/
snprintf(buf, sizeof(buf), "[%s ",(const char *) class_getName(aClass));
if(strncmp(info.dli_sname + 1, buf, strlen(buf))){
snprintf(buf, sizeof(buf),"[%s(",(const char *)class_getName(aClass));
if(strncmp(info.dli_sname + 1, buf, strlen(buf)))
goto FAIL;
}
/*Validate selector in symbol*/
snprintf(buf, sizeof(buf), " %s]",(const char*)method_getName(m));
if(strncmp(info.dli_sname + (strlen(info.dli_sname) - strlen(buf)), buf, strlen(buf))){
goto FAIL;
}
}else{
printf("<redacted> \n");
}
}
return YES;
FAIL:
printf("method %s failed integrity test:\n",
(const char *)method_getName(m));
printf(" dli_fname:%s\n",info.dli_fname);
printf(" dli_sname:%s\n",info.dli_sname);
printf(" dli_fbase:%p\n",info.dli_fbase);
printf(" dli_saddr:%p\n",info.dli_saddr);
free(methods);
return NO;
}
复制代码
回到本文开头的Demo,不难看出,其实
if (machHeader == NULL)
{
Dl_info info;
dladdr((__bridge const void *)(configuration), &info);
machHeader = (struct mach_header_64*)info.dli_fbase;
}
复制代码
这段代码的用途仅仅是为了获取header。至于header前面的 文章也提到过了,这里很少作讲解了,拿到的header做为函数getsectiondata
的参数:
uintptr_t* data = (uintptr_t *) getsectiondata(machHeader, "__DATA", "__customSection", &byteCount);
复制代码
这里须要注意的是getsectiondata
的定义以下:
extern uint8_t *getsectiondata(
const struct mach_header_64 *mhp,
const char *segname,
const char *sectname,
unsigned long *size);
复制代码
因此一开始笔者在返回类型的时候,使用了uint8_t
结果发现无论怎么操做都不能打印出想要的数据。改为uintptr_t
才能打印成功。缘由你们能够猜测一下。 最后咱们查看一下打印的结果: