mirror of
https://github.com/DKJone/DKWechatHelper.git
synced 2026-07-30 06:44:32 +08:00
init project
添加初始项目
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// AntiAntiDebug.m
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2016/12/10.
|
||||
// Copyright © 2017年 MonkeyDev. All rights reserved.
|
||||
//
|
||||
|
||||
#if TARGET_OS_SIMULATOR
|
||||
#error Do not support the simulator, please use the real iPhone Device.
|
||||
#endif
|
||||
|
||||
#import "fishhook.h"
|
||||
#import <Foundation/Foundation.h>
|
||||
#import <sys/sysctl.h>
|
||||
|
||||
typedef int (*ptrace_ptr_t)(int _request,pid_t _pid, caddr_t _addr,int _data);
|
||||
typedef void* (*dlsym_ptr_t)(void * __handle, const char* __symbol);
|
||||
typedef int (*syscall_ptr_t)(int, ...);
|
||||
typedef int (*sysctl_ptr_t)(int *,u_int, void*, size_t*,void*, size_t);
|
||||
|
||||
|
||||
static ptrace_ptr_t orig_ptrace = NULL;
|
||||
static dlsym_ptr_t orig_dlsym = NULL;
|
||||
static sysctl_ptr_t orig_sysctl = NULL;
|
||||
static syscall_ptr_t orig_syscall = NULL;
|
||||
|
||||
int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data);
|
||||
void* my_dlsym(void* __handle, const char* __symbol);
|
||||
int my_sysctl(int * name, u_int namelen, void * info, size_t * infosize, void * newinfo, size_t newinfosize);
|
||||
int my_syscall(int code, va_list args);
|
||||
|
||||
int my_ptrace(int _request, pid_t _pid, caddr_t _addr, int _data){
|
||||
if(_request != 31){
|
||||
return orig_ptrace(_request,_pid,_addr,_data);
|
||||
}
|
||||
|
||||
NSLog(@"[AntiAntiDebug] - ptrace request is PT_DENY_ATTACH");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void* my_dlsym(void* __handle, const char* __symbol){
|
||||
if(strcmp(__symbol, "ptrace") != 0){
|
||||
return orig_dlsym(__handle, __symbol);
|
||||
}
|
||||
|
||||
NSLog(@"[AntiAntiDebug] - dlsym get ptrace symbol");
|
||||
|
||||
return my_ptrace;
|
||||
}
|
||||
|
||||
typedef struct kinfo_proc _kinfo_proc;
|
||||
|
||||
int my_sysctl(int * name, u_int namelen, void * info, size_t * infosize, void * newinfo, size_t newinfosize){
|
||||
if(namelen == 4 && name[0] == CTL_KERN && name[1] == KERN_PROC && name[2] == KERN_PROC_PID && info && infosize && ((int)*infosize == sizeof(_kinfo_proc))){
|
||||
int ret = orig_sysctl(name, namelen, info, infosize, newinfo, newinfosize);
|
||||
struct kinfo_proc *info_ptr = (struct kinfo_proc *)info;
|
||||
if(info_ptr && (info_ptr->kp_proc.p_flag & P_TRACED) != 0){
|
||||
NSLog(@"[AntiAntiDebug] - sysctl query trace status.");
|
||||
info_ptr->kp_proc.p_flag ^= P_TRACED;
|
||||
if((info_ptr->kp_proc.p_flag & P_TRACED) == 0){
|
||||
NSLog(@"trace status reomve success!");
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return orig_sysctl(name, namelen, info, infosize, newinfo, newinfosize);
|
||||
}
|
||||
|
||||
int my_syscall(int code, va_list args){
|
||||
int request;
|
||||
va_list newArgs;
|
||||
va_copy(newArgs, args);
|
||||
if(code == 26){
|
||||
#ifdef __LP64__
|
||||
__asm__(
|
||||
"ldr %w[result], [fp, #0x10]\n"
|
||||
: [result] "=r" (request)
|
||||
:
|
||||
:
|
||||
);
|
||||
#else
|
||||
request = va_arg(args, int);
|
||||
#endif
|
||||
if(request == 31){
|
||||
NSLog(@"[AntiAntiDebug] - syscall call ptrace, and request is PT_DENY_ATTACH");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return orig_syscall(code, newArgs);
|
||||
}
|
||||
|
||||
__attribute__((constructor)) static void entry(){
|
||||
NSLog(@"[AntiAntiDebug Init]");
|
||||
|
||||
rebind_symbols((struct rebinding[1]){{"ptrace", my_ptrace, (void*)&orig_ptrace}},1);
|
||||
|
||||
rebind_symbols((struct rebinding[1]){{"dlsym", my_dlsym, (void*)&orig_dlsym}},1);
|
||||
|
||||
//some app will crash with _dyld_debugger_notification
|
||||
// rebind_symbols((struct rebinding[1]){{"sysctl", my_sysctl, (void*)&orig_sysctl}},1);
|
||||
|
||||
rebind_symbols((struct rebinding[1]){{"syscall", my_syscall, (void*)&orig_syscall}},1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// MDConfigManager.h
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2018/4/24.
|
||||
// Copyright © 2018年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define MDCONFIG_CYCRIPT_KEY @"Cycript"
|
||||
#define MDCONFIG_LOADATLAUNCH_KEY @"LoadAtLaunch"
|
||||
|
||||
#define MDCONFIG_TRACE_KEY @"MethodTrace"
|
||||
#define MDCONFIG_LOG_LEVEL_KEY @"LogLevel"
|
||||
#define MDCONFIG_LOG_WHEN_KEY @"LogWhen"
|
||||
#define MDCONFIG_LOG_REGEX_STRING_KEY @"LogRegexString" // 仅LogWhen=MDTraceLogWhenRegexString有效
|
||||
#define MDCONFIG_TRACE_FLAG_KEY @"TraceFlag"
|
||||
#define MDCONFIG_TRACE_OBJECT_KEY @"TraceObject"
|
||||
#define MDCONFIG_CLASS_REGEX_STRING_KEY @"ClassRegexString" // 仅TraceObject=MDTraceObjectRegexClass有效
|
||||
#define MDCONFIG_CORE_CLASS_LIST @"CORE_CLASS_LIST"
|
||||
#define MDCONFIG_USER_CLASS_LIST @"USER_CLASS_LIST"
|
||||
#define MDCONFIG_TRACE_MODE_KEY @"TraceMode"
|
||||
#define MDCONFIG_METHOD_WHITE_LIST_KEY @"MethodWhiteList"
|
||||
#define MDCONFIG_METHOD_BLACK_LIST_KEY @"MethodBlackList"
|
||||
|
||||
// Trace日志级别
|
||||
typedef NS_ENUM(NSUInteger, MDTraceLogLevel) {
|
||||
MDTraceLogLeveError = 0, // 错误
|
||||
MDTraceLogLeveDebug = 1, // 调试
|
||||
MDTraceLogLeveMax
|
||||
};
|
||||
|
||||
// Trace日志输出时机
|
||||
typedef NS_ENUM(NSUInteger, MDTraceLogWhen) {
|
||||
MDTraceLogWhenStartup = 0, // 启动即输出日志
|
||||
MDTraceLogWhenVolume = 1, // 根据音量键控制输出日志(增加音量:输出日志;降低音量:关闭日志;默认时关闭日志)
|
||||
MDTraceLogWhenRegexString = 2, // 日志包含指定正则字符串才输出日志
|
||||
MDTraceLogWhenMax
|
||||
};
|
||||
|
||||
// Trace控制位(尽量在该处扩展)
|
||||
typedef NS_OPTIONS(NSUInteger, MDTraceFlag) {
|
||||
MDTraceFlagDoesNotUseDescription = 1 << 0, // 跳过调用对象description方法,避免不正规的description实现导致递归
|
||||
MDTraceFlagDumpClassListInfo = 1 << 1, // 打印类列表信息,便于调试
|
||||
MDTraceFlagDumpClassMethod = 1 << 2, // 打印某个类的方法(不包括父类方法),便于调试
|
||||
MDTraceFlagDumpSuperClassMethod = 1 << 3, // 打印某个类的父类方法(包括递归父类的方法),便于调试
|
||||
MDTraceFlagMask = 0xF,
|
||||
|
||||
MDTraceFlagDefault = 0,
|
||||
};
|
||||
|
||||
// Trace对象
|
||||
typedef NS_ENUM(NSUInteger, MDTraceObject) {
|
||||
// 屏蔽trace所有类
|
||||
MDTraceObjectNone = 0,
|
||||
// trace引擎指定类的方法(仅测试验证使用),仅需要考虑CORE_CLASS_LIST
|
||||
MDTraceObjectCoreClass = 1,
|
||||
// trace用户指定类的方法,需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集"
|
||||
MDTraceObjectUserClass = 2,
|
||||
// trace用户指定类 + 正则匹配类的方法,需要考虑USER_CLASS_LIST + "USER_CLASS_LIST和CORE_CLASS_LIST交集" +
|
||||
// "匹配ClassRegexString的CLASS_LIST和CORE_CLASS_LIST交集"
|
||||
MDTraceObjectRegexClass = 3,
|
||||
|
||||
MDTraceObjectMax
|
||||
};
|
||||
|
||||
// Trace模式
|
||||
typedef NS_ENUM(NSUInteger, MDTraceMode) {
|
||||
MDTraceModeOff = 0, // 屏蔽trace方法
|
||||
MDTraceModeAll = 1, // trace所有方法
|
||||
MDTraceModeIncludeWhiteList = 2, // trace包含"白名单方法列表"的方法
|
||||
MDTraceModeExcludeBlackList = 3, // trace排除"黑名单方法列表"的方法
|
||||
MDTraceModeMax
|
||||
};
|
||||
|
||||
@interface MDConfigManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (NSDictionary*)readConfigByKey:(NSString*) key;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,48 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// MDConfigManager.m
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2018/4/24.
|
||||
// Copyright © 2018年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#define CONFIG_FILE_NAME @"MDConfig"
|
||||
|
||||
#import "MDConfigManager.h"
|
||||
|
||||
@implementation MDConfigManager{
|
||||
NSString* _filepath;
|
||||
}
|
||||
|
||||
+ (instancetype)sharedInstance{
|
||||
static MDConfigManager *sharedInstance = nil;
|
||||
if (!sharedInstance){
|
||||
sharedInstance = [[MDConfigManager alloc] init];
|
||||
}
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (BOOL)isActive{
|
||||
_filepath = [[NSBundle mainBundle] pathForResource:CONFIG_FILE_NAME ofType:@"plist"];
|
||||
if(_filepath == nil){
|
||||
return NO;
|
||||
}
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (NSDictionary*) readConfigByKey:(NSString*) key{
|
||||
if([self isActive]){
|
||||
NSDictionary* contentDict = [NSDictionary dictionaryWithContentsOfFile:_filepath];
|
||||
if([contentDict.allKeys containsObject:key]){
|
||||
return contentDict[key];
|
||||
}else{
|
||||
return nil;
|
||||
}
|
||||
}else{
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,40 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// MDCycriptManager.h
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2018/3/8.
|
||||
// Copyright © 2018年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define PORT 6666
|
||||
|
||||
@interface MDCycriptManager : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
|
||||
/**
|
||||
Download script by config.plist
|
||||
|
||||
@param update Force update of all scripts
|
||||
*/
|
||||
-(void)loadCycript:(BOOL) update;
|
||||
|
||||
/**
|
||||
eval javascript by cycript
|
||||
|
||||
@param cycript javascript string
|
||||
@param error error happened
|
||||
@return eval result
|
||||
*/
|
||||
-(NSString*)evaluateCycript:(NSString*) cycript error:(NSError **) error;
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,306 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// MDCycriptManager.m
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2018/3/8.
|
||||
// Copyright © 2018年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
|
||||
#import "MDCycriptManager.h"
|
||||
#import "MDConfigManager.h"
|
||||
#import <ifaddrs.h>
|
||||
#import <arpa/inet.h>
|
||||
#import <net/if.h>
|
||||
#import <pthread.h>
|
||||
#import <JavaScriptCore/JavaScriptCore.h>
|
||||
|
||||
#define IOS_CELLULAR @"pdp_ip0"
|
||||
#define IOS_WIFI @"en0"
|
||||
#define IP_ADDR_IPv4 @"ipv4"
|
||||
#define IP_ADDR_IPv6 @"ipv6"
|
||||
#define MDLog(fmt, ...) NSLog((@"[Cycript] " fmt), ##__VA_ARGS__)
|
||||
|
||||
extern JSGlobalContextRef CYGetJSContext(void);
|
||||
extern void CydgetMemoryParse(const uint16_t **data, size_t *size);
|
||||
|
||||
NSString * const CYErrorLineKey = @"CYErrorLineKey";
|
||||
NSString * const CYErrorNameKey = @"CYErrorNameKey";
|
||||
NSString * const CYErrorMessageKey = @"CYErrorMessageKey";
|
||||
|
||||
@interface MDSettingObject : NSObject
|
||||
|
||||
@property (nonatomic, assign) NSInteger priority;
|
||||
@property (nonatomic, copy) NSString* url;
|
||||
@property (nonatomic, copy) NSString* content;
|
||||
@property (nonatomic, assign) BOOL loadAtLaunch;
|
||||
|
||||
-(instancetype)initWithDicationary:(NSDictionary*) dictionary;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MDSettingObject
|
||||
|
||||
-(instancetype)initWithDicationary:(NSDictionary *)dictionary{
|
||||
self = [super init];
|
||||
if(self){
|
||||
self.priority = [dictionary[@"priority"] integerValue];
|
||||
self.url = dictionary[@"url"];
|
||||
self.content = dictionary[@"content"];
|
||||
self.loadAtLaunch = [dictionary objectForKey:MDCONFIG_LOADATLAUNCH_KEY] && [dictionary[MDCONFIG_LOADATLAUNCH_KEY] boolValue];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@interface MDCycriptManager()
|
||||
|
||||
@property (nonatomic, strong) NSDictionary* configItem;
|
||||
@property (nonatomic, copy) NSString* cycriptDirectory;
|
||||
@property (nonatomic, strong) NSMutableArray* downloading;
|
||||
@property (nonatomic, strong) NSMutableDictionary* loadAtLaunchModules;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MDCycriptManager
|
||||
|
||||
+ (instancetype)sharedInstance{
|
||||
static MDCycriptManager *sharedInstance = nil;
|
||||
if (!sharedInstance){
|
||||
sharedInstance = [[MDCycriptManager alloc] init];
|
||||
}
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
_loadAtLaunchModules = [NSMutableDictionary dictionary];
|
||||
_downloading = [NSMutableArray array];
|
||||
[self check];
|
||||
[self readConfigFile];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
-(void)check{
|
||||
NSString* ip = [self getIPAddress];
|
||||
if(ip != nil){
|
||||
printf("\nDownload cycript(https://cydia.saurik.com/api/latest/3) then run: ./cycript -r %s:%d\n\n", [ip UTF8String], PORT);
|
||||
}else{
|
||||
printf("\nPlease connect wifi before using cycript!\n\n");
|
||||
}
|
||||
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
NSString *documentsPath =[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask,YES) firstObject];
|
||||
_cycriptDirectory = [documentsPath stringByAppendingPathComponent:@"cycript"];
|
||||
[fileManager createDirectoryAtPath:_cycriptDirectory withIntermediateDirectories:YES attributes:nil error:nil];
|
||||
}
|
||||
|
||||
-(NSArray*)sortedArray:(NSDictionary*) dictionary{
|
||||
NSMutableArray* result = [NSMutableArray arrayWithCapacity:10];
|
||||
|
||||
NSArray* sortedArray = [dictionary.allKeys sortedArrayUsingComparator:^NSComparisonResult(NSNumber* _Nonnull number1, NSNumber* _Nonnull number2) {
|
||||
if ([number1 integerValue] > [number2 integerValue])
|
||||
return NSOrderedDescending;
|
||||
return NSOrderedAscending;
|
||||
}];
|
||||
|
||||
for (NSNumber* item in sortedArray) {
|
||||
[result addObject:dictionary[item]];
|
||||
}
|
||||
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
-(void)readConfigFile{
|
||||
MDConfigManager * configManager = [MDConfigManager sharedInstance];
|
||||
_configItem = [configManager readConfigByKey:MDCONFIG_CYCRIPT_KEY];
|
||||
}
|
||||
|
||||
-(void)loadCycript:(BOOL) update{
|
||||
NSFileManager *fileManager = [NSFileManager defaultManager];
|
||||
|
||||
if(_configItem && _configItem.count > 0){
|
||||
|
||||
BOOL download = NO;
|
||||
|
||||
for (NSString* moduleName in _configItem.allKeys) {
|
||||
MDSettingObject * item = [[MDSettingObject alloc] initWithDicationary:_configItem[moduleName]];
|
||||
NSString *fullPath = [[_cycriptDirectory stringByAppendingPathComponent:moduleName] stringByAppendingPathExtension:@"cy"];
|
||||
|
||||
if(item.url){
|
||||
if(![fileManager fileExistsAtPath:fullPath] || update){
|
||||
download = YES;
|
||||
[self.downloading addObject:moduleName];
|
||||
[self downLoadUrl:item.url saveName:moduleName];
|
||||
}
|
||||
}
|
||||
|
||||
if(item.content){
|
||||
if(![fileManager fileExistsAtPath:fullPath] || update){
|
||||
NSString* writeContent = [NSString stringWithFormat:@"(function(exports) { %@ })(exports);", item.content];
|
||||
[writeContent writeToFile:fullPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
|
||||
}
|
||||
}
|
||||
|
||||
if(item.loadAtLaunch){
|
||||
[_loadAtLaunchModules setObject:fullPath forKey:@(item.priority)];
|
||||
}
|
||||
}
|
||||
|
||||
if(!download){
|
||||
[self finishDownload];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)finishDownload{
|
||||
MDLog(@"Finish download all script!");
|
||||
NSArray* sortedArray = [self sortedArray:_loadAtLaunchModules];
|
||||
for (NSString* fullPath in sortedArray) {
|
||||
NSError* error;
|
||||
[self evaluateCycript:[NSString stringWithFormat:@"require('%@');",fullPath] error:&error];
|
||||
if(error.code != 0){
|
||||
MDLog(@"%@", error.localizedDescription);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-(void)downLoadUrl:(NSString*) urlString saveName:(NSString*) filename{
|
||||
__weak typeof(self) weakSelf = self;
|
||||
NSURLSession *session = [NSURLSession sharedSession];
|
||||
NSURL *url = [NSURL URLWithString:urlString];
|
||||
NSURLSessionDownloadTask *downloadTask = [session downloadTaskWithURL:url completionHandler:^(NSURL * _Nullable location, NSURLResponse * _Nullable response, NSError * _Nullable error) {
|
||||
|
||||
if(error){
|
||||
MDLog(@"Failed download script [%@]: %@", filename, error.localizedDescription);
|
||||
}else{
|
||||
NSString *fullPath = [[weakSelf.cycriptDirectory stringByAppendingPathComponent:filename] stringByAppendingPathExtension:@"cy"];
|
||||
[[NSFileManager defaultManager] moveItemAtURL:location toURL:[NSURL fileURLWithPath:fullPath] error:nil];
|
||||
|
||||
MDLog(@"Successful download script [%@]", filename);
|
||||
}
|
||||
|
||||
[weakSelf.downloading removeObject:filename];
|
||||
|
||||
if(!weakSelf.downloading.count){
|
||||
[weakSelf finishDownload];
|
||||
}
|
||||
}];
|
||||
[downloadTask resume];
|
||||
}
|
||||
|
||||
-(NSString *)evaluateCycript:(NSString *)cycript error:(NSError *__autoreleasing *)error{
|
||||
NSString *resultString = nil;
|
||||
|
||||
static pthread_mutex_t cycript_metex = PTHREAD_MUTEX_INITIALIZER;
|
||||
pthread_mutex_lock(&cycript_metex); {
|
||||
JSGlobalContextRef context = CYGetJSContext();
|
||||
|
||||
size_t length = cycript.length;
|
||||
unichar *buffer = malloc(length * sizeof(unichar));
|
||||
[cycript getCharacters:buffer range:NSMakeRange(0, length)];
|
||||
const uint16_t *characters = buffer;
|
||||
CydgetMemoryParse(&characters, &length);
|
||||
JSStringRef expression = JSStringCreateWithCharacters(characters, length);
|
||||
|
||||
// Evaluate the Javascript
|
||||
JSValueRef exception = NULL;
|
||||
JSValueRef result = JSEvaluateScript(context, expression, NULL, NULL, 0, &exception);
|
||||
JSStringRelease(expression);
|
||||
|
||||
// If a result was returned, convert it into an NSString
|
||||
if (result) {
|
||||
JSStringRef string = JSValueToStringCopy(context, result, &exception);
|
||||
if (string) {
|
||||
resultString = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
JSStringRelease(string);
|
||||
}
|
||||
}
|
||||
|
||||
// If an exception was thrown, convert it into an NSError
|
||||
if (exception && error) {
|
||||
JSObjectRef exceptionObject = JSValueToObject(context, exception, NULL);
|
||||
|
||||
NSInteger line = (NSInteger)JSValueToNumber(context, JSObjectGetProperty(context, exceptionObject, JSStringCreateWithUTF8CString("line"), NULL), NULL);
|
||||
JSStringRef string = JSValueToStringCopy(context, JSObjectGetProperty(context, exceptionObject, JSStringCreateWithUTF8CString("name"), NULL), NULL);
|
||||
NSString *name = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
JSStringRelease(string);
|
||||
string = JSValueToStringCopy(context, JSObjectGetProperty(context, exceptionObject, JSStringCreateWithUTF8CString("message"), NULL), NULL);
|
||||
NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
JSStringRelease(string);
|
||||
string = JSValueToStringCopy(context, exception, NULL);
|
||||
NSString *description = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
JSStringRelease(string);
|
||||
|
||||
NSMutableDictionary *userInfo = [NSMutableDictionary dictionary];
|
||||
[userInfo setValue:@(line) forKey:CYErrorLineKey];
|
||||
[userInfo setValue:name forKey:CYErrorNameKey];
|
||||
[userInfo setValue:message forKey:CYErrorMessageKey];
|
||||
[userInfo setValue:description forKey:NSLocalizedDescriptionKey];
|
||||
*error = [NSError errorWithDomain:@"CYContextDomain" code:0 userInfo:userInfo];
|
||||
}
|
||||
}pthread_mutex_unlock(&cycript_metex);
|
||||
|
||||
return resultString;
|
||||
}
|
||||
|
||||
- (NSString *)getIPAddress{
|
||||
|
||||
NSDictionary *addresses = [self getIPAddresses];
|
||||
|
||||
if([addresses.allKeys containsObject:IOS_WIFI @"/" IP_ADDR_IPv4]){
|
||||
return addresses[IOS_WIFI @"/" IP_ADDR_IPv4];
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
- (NSDictionary *)getIPAddresses{
|
||||
NSMutableDictionary *addresses = [NSMutableDictionary dictionaryWithCapacity:8];
|
||||
|
||||
// retrieve the current interfaces - returns 0 on success
|
||||
struct ifaddrs *interfaces;
|
||||
if(!getifaddrs(&interfaces)) {
|
||||
// Loop through linked list of interfaces
|
||||
struct ifaddrs *interface;
|
||||
for(interface=interfaces; interface; interface=interface->ifa_next) {
|
||||
if(!(interface->ifa_flags & IFF_UP) /* || (interface->ifa_flags & IFF_LOOPBACK) */ ) {
|
||||
continue; // deeply nested code harder to read
|
||||
}
|
||||
const struct sockaddr_in *addr = (const struct sockaddr_in*)interface->ifa_addr;
|
||||
char addrBuf[ MAX(INET_ADDRSTRLEN, INET6_ADDRSTRLEN) ];
|
||||
if(addr && (addr->sin_family==AF_INET || addr->sin_family==AF_INET6)) {
|
||||
NSString *name = [NSString stringWithUTF8String:interface->ifa_name];
|
||||
NSString *type;
|
||||
if(addr->sin_family == AF_INET) {
|
||||
if(inet_ntop(AF_INET, &addr->sin_addr, addrBuf, INET_ADDRSTRLEN)) {
|
||||
type = IP_ADDR_IPv4;
|
||||
}
|
||||
} else {
|
||||
const struct sockaddr_in6 *addr6 = (const struct sockaddr_in6*)interface->ifa_addr;
|
||||
if(inet_ntop(AF_INET6, &addr6->sin6_addr, addrBuf, INET6_ADDRSTRLEN)) {
|
||||
type = IP_ADDR_IPv6;
|
||||
}
|
||||
}
|
||||
if(type) {
|
||||
NSString *key = [NSString stringWithFormat:@"%@/%@", name, type];
|
||||
addresses[key] = [NSString stringWithUTF8String:addrBuf];
|
||||
}
|
||||
}
|
||||
}
|
||||
// Free memory
|
||||
freeifaddrs(interfaces);
|
||||
}
|
||||
return [addresses count] ? addresses : nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,30 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// MDMethodTrace.h
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2017/9/7.
|
||||
// Copyright © 2017年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef MethodTrace_h
|
||||
#define MethodTrace_h
|
||||
|
||||
#define TRACE_README @"\n📚--------------------OCMethodTrace(Usage)-------------------📚\nhttps://github.com/omxcodec/OCMethodTrace/blob/master/README.md\n📚--------------------OCMethodTrace(Usage)-------------------📚"
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface MDMethodTrace : NSObject
|
||||
|
||||
+ (instancetype)sharedInstance;
|
||||
|
||||
- (void)addClassTrace:(NSString *) className;
|
||||
|
||||
- (void)addClassTrace:(NSString *)className methodName:(NSString *)methodName;
|
||||
|
||||
- (void)addClassTrace:(NSString *)className methodList:(NSArray *)methodList;
|
||||
|
||||
@end
|
||||
|
||||
#endif /* MethodTrace_h */
|
||||
@@ -0,0 +1,923 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// MDMethodTrace.m
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2017/9/6.
|
||||
// Copyright © 2017年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#import "MDMethodTrace.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <AVFoundation/AVFoundation.h>
|
||||
#import "MDConfigManager.h"
|
||||
#import "OCMethodTrace.h"
|
||||
|
||||
//#define USE_DDLOG // 使用外部日志系统,日志多的时候特别有用
|
||||
#ifdef USE_DDLOG
|
||||
#import "CocoaLumberjack.h"
|
||||
static const int ddLogLevel = DDLogLevelVerbose;
|
||||
#define MDLog(fmt, ...) DDLogDebug((@"[MethodTrace] " fmt), ##__VA_ARGS__)
|
||||
#else
|
||||
//#define MDLog(fmt, ...) NSLog((@"[MethodTrace] " fmt), ##__VA_ARGS__)
|
||||
#define MDLog(fmt, ...) printf("[MethodTrace] %s\n",[[NSString stringWithFormat:fmt, ##__VA_ARGS__] UTF8String]);
|
||||
#endif
|
||||
|
||||
#define SAFE_CHECK(_object, _type) (_type *)[[self class] safeCheck:_object class:[_type class]]
|
||||
#define MDFatal(fmt, ...) [NSException raise:@"MDMethodTrace" format:fmt, ##__VA_ARGS__]
|
||||
|
||||
// 指定ClassInfo源
|
||||
typedef NS_ENUM(NSUInteger, MDTraceSource) {
|
||||
MDTraceSourceCore = 0, // 引擎指定(引擎指OCMethodTrace内部实现框架)
|
||||
MDTraceSourceUser = 1, // 用户指定
|
||||
MDTraceSourceMerge = 2, // 引擎和用户合并
|
||||
MDTraceSourceMax
|
||||
};
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Class Define
|
||||
|
||||
@interface MDTraceClassInfo : NSObject
|
||||
|
||||
@property (nonatomic, strong) NSString *name; // 类名
|
||||
@property (nonatomic, assign) MDTraceSource source; // 指定源
|
||||
@property (nonatomic, assign) MDTraceMode mode; // trace模式
|
||||
@property (nonatomic, assign) MDTraceFlag flag; // flag
|
||||
@property (nonatomic, strong) NSMutableArray *methodList; // 黑名单或者白名单,根据mode决定
|
||||
|
||||
// 解析类配置,无key则加key,设置默认值,并校验值类型
|
||||
+ (instancetype)infoWithName:(NSString *)name source:(MDTraceSource)source config:(NSDictionary *)config;
|
||||
// 根据类型返回引擎指定的默认ClassInfo
|
||||
+ (instancetype)defaultCoreClassInfo:(NSString *)name;
|
||||
// 根据类型返回用户指定的默认ClassInfo
|
||||
+ (instancetype)defaultUserClassInfo:(NSString *)name;
|
||||
// 安全检查
|
||||
+ (id)safeCheck:(id)content class:(Class)cls;
|
||||
// 数组相减: arrayA - arrayB
|
||||
+ (NSArray *)minusWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB;
|
||||
// 数组相加: arrayA + arrayB,可去重,相同元素只保留一个
|
||||
+ (NSArray *)unionWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB;
|
||||
// 合并引擎和用户ClassInfo,合并时core的优先级比user大,具体见函数内部实现
|
||||
+ (MDTraceClassInfo *)mergeInfoWithCoreInfo:(MDTraceClassInfo *)coreInfo userInfo:(MDTraceClassInfo *)userInfo;
|
||||
// 合并引擎和用户config
|
||||
+ (MDTraceClassInfo *)mergeInfoWithName:(NSString *)name coreConfig:(NSDictionary *)coreConfig userConfig:(NSDictionary *)userConfig;
|
||||
|
||||
@end
|
||||
|
||||
@interface MDMethodTrace () <OCMethodTraceDelegate>
|
||||
|
||||
@property (nonatomic, strong) NSMutableDictionary *config;
|
||||
@property (nonatomic, assign) MDTraceLogLevel logLevel; // 日志级别
|
||||
@property (nonatomic, assign) MDTraceLogWhen logWhen; // 日志输出时机
|
||||
@property (nonatomic, strong) NSString *logRegexString; // 日志正则匹配字符串,仅当logWhen=MDTraceLogWhenRegexString有效
|
||||
@property (nonatomic, assign) NSInteger numberOfPendingLog; // logRegexString匹配后,待输出afer日志个数
|
||||
@property (nonatomic, assign) CGFloat lastSystemVolume; // 上一次系统音量
|
||||
@property (nonatomic, assign) MDTraceFlag traceFlag; // 控制trace行为的一些特殊flag
|
||||
@property (nonatomic, assign) MDTraceObject traceObject; // trace对象
|
||||
@property (nonatomic, strong) NSString *classRegexString; // 类正则匹配字符串
|
||||
@property (nonatomic, strong) NSMutableArray *coreClassInfoList; // 引擎类信息列表
|
||||
@property (nonatomic, strong) NSMutableArray *userClassInfoList; // 用户类信息列表
|
||||
@property (nonatomic, strong) NSMutableArray *regexClassInfoList; // 正则类信息列表
|
||||
|
||||
// 解析配置文件
|
||||
- (void)parseConfig:(NSDictionary *)config;
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - MDTraceClassInfo
|
||||
|
||||
@implementation MDTraceClassInfo
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.name = @"NoName";
|
||||
self.source = MDTraceSourceUser;
|
||||
self.mode = MDTraceModeAll;
|
||||
self.flag = MDTraceFlagDefault;
|
||||
self.methodList = [NSMutableArray array];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSString *)description
|
||||
{
|
||||
return [NSString stringWithFormat:@"<%@: %p name: %@ source: %tu mode: %tu flag: %tu methodList: %@>",
|
||||
NSStringFromClass([self class]), self, _name, _source, _mode, _flag, _methodList];
|
||||
}
|
||||
|
||||
+ (instancetype)infoWithName:(NSString *)name source:(MDTraceSource)source config:(NSDictionary *)config
|
||||
{
|
||||
// 无对应字典说明用户没有指定类,不处理
|
||||
if (nil == config) {
|
||||
return nil;
|
||||
}
|
||||
|
||||
MDTraceClassInfo *info = [[MDTraceClassInfo alloc] init];
|
||||
info.name = name;
|
||||
info.source = source;
|
||||
// 1 mode
|
||||
info.mode = info.source == MDTraceSourceCore ? MDTraceModeOff: MDTraceModeAll;
|
||||
if (![config valueForKey:MDCONFIG_TRACE_MODE_KEY]) {
|
||||
// MDLog(@"Cannot find class %@ %@, set it to default %d", info.name, MDCONFIG_TRACE_MODE_KEY, (int)info.mode);
|
||||
} else {
|
||||
info.mode = [SAFE_CHECK(config[MDCONFIG_TRACE_MODE_KEY], NSNumber) integerValue];
|
||||
}
|
||||
|
||||
// 2 flag
|
||||
info.flag = MDTraceFlagDefault;
|
||||
if (![config valueForKey:MDCONFIG_TRACE_FLAG_KEY]) {
|
||||
// MDLog(@"Cannot find class %@ %@, set it to default %d", info.name, MDCONFIG_TRACE_FLAG_KEY, (int)info.flag);
|
||||
} else {
|
||||
info.flag = [SAFE_CHECK(config[MDCONFIG_TRACE_FLAG_KEY], NSNumber) integerValue];
|
||||
}
|
||||
|
||||
// 3 whiteList
|
||||
if ([config valueForKey:MDCONFIG_METHOD_WHITE_LIST_KEY]) {
|
||||
id methodList = config[MDCONFIG_METHOD_WHITE_LIST_KEY];
|
||||
if (![methodList isKindOfClass:[NSArray class]]) {
|
||||
MDFatal(@"Class %@ %@ should be array", info.name, MDCONFIG_METHOD_WHITE_LIST_KEY);
|
||||
} else if (info.mode == MDTraceModeIncludeWhiteList) {
|
||||
info.methodList = methodList;
|
||||
}
|
||||
}
|
||||
|
||||
// 4 blackList
|
||||
if ([config valueForKey:MDCONFIG_METHOD_BLACK_LIST_KEY]) {
|
||||
id methodList = config[MDCONFIG_METHOD_BLACK_LIST_KEY];
|
||||
if (![methodList isKindOfClass:[NSArray class]]) {
|
||||
MDFatal(@"Class %@ %@ should be array", info.name, MDCONFIG_METHOD_BLACK_LIST_KEY);
|
||||
} else if (info.mode == MDTraceModeExcludeBlackList) {
|
||||
info.methodList = methodList;
|
||||
}
|
||||
}
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultCoreClassInfo:(NSString *)name
|
||||
{
|
||||
MDTraceClassInfo *info = [[MDTraceClassInfo alloc] init];
|
||||
info.name = name;
|
||||
info.source = MDTraceSourceCore;
|
||||
info.mode = MDTraceModeOff;
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (instancetype)defaultUserClassInfo:(NSString *)name
|
||||
{
|
||||
MDTraceClassInfo *info = [[MDTraceClassInfo alloc] init];
|
||||
info.name = name;
|
||||
info.source = MDTraceSourceUser;
|
||||
info.mode = MDTraceModeAll;
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (id)safeCheck:(id)content class:(Class)cls
|
||||
{
|
||||
if ([content isKindOfClass:cls]) {
|
||||
return content;
|
||||
}
|
||||
MDFatal(@"safeCheck failed, content(%@) should be class(%@) type", content, NSStringFromClass(cls));
|
||||
return nil;
|
||||
}
|
||||
|
||||
+ (NSArray *)minusWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB
|
||||
{
|
||||
NSMutableOrderedSet *setA = [NSMutableOrderedSet orderedSetWithArray:arrayA];
|
||||
NSMutableOrderedSet *setB = [NSMutableOrderedSet orderedSetWithArray:arrayB];
|
||||
[setA minusOrderedSet:setB];
|
||||
return [setA array];
|
||||
}
|
||||
|
||||
+ (NSArray *)unionWithArrayA:(NSArray *)arrayA arrayB:(NSArray *)arrayB
|
||||
{
|
||||
NSMutableOrderedSet *setA = [NSMutableOrderedSet orderedSetWithArray:arrayA];
|
||||
NSMutableOrderedSet *setB = [NSMutableOrderedSet orderedSetWithArray:arrayB];
|
||||
[setA unionOrderedSet:setB];
|
||||
return [setA array];
|
||||
}
|
||||
|
||||
- (void)setSource:(MDTraceSource)source
|
||||
{
|
||||
NSAssert(source >= MDTraceSourceCore && source < MDTraceSourceMax, @"invalid TraceSource");
|
||||
_source = source;
|
||||
}
|
||||
|
||||
- (void)setMode:(MDTraceMode)mode
|
||||
{
|
||||
NSAssert(mode >= MDTraceModeOff && mode < MDTraceModeMax, @"invalid TraceMode");
|
||||
_mode = mode;
|
||||
}
|
||||
|
||||
- (void)setFlag:(MDTraceFlag)flag
|
||||
{
|
||||
NSAssert(flag >= MDTraceModeOff && flag <= MDTraceFlagMask, @"invalid TraceFlag");
|
||||
_flag = flag;
|
||||
}
|
||||
|
||||
- (id)copyWithZone:(NSZone *)zone
|
||||
{
|
||||
MDTraceClassInfo *info = [[self.class allocWithZone:zone] init];
|
||||
|
||||
info.name = self.name;
|
||||
info.source = self.source;
|
||||
info.mode = self.mode;
|
||||
info.flag = self.flag;
|
||||
info.methodList = [NSMutableArray arrayWithArray:self.methodList];
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
+ (MDTraceClassInfo *)mergeInfoWithCoreInfo:(MDTraceClassInfo *)coreInfo userInfo:(MDTraceClassInfo *)userInfo
|
||||
{
|
||||
if (nil == coreInfo && nil == userInfo) {
|
||||
NSAssert(0, @"invalid info");
|
||||
return nil;
|
||||
}
|
||||
|
||||
MDTraceClassInfo *mergeInfo = nil;
|
||||
if (nil != coreInfo && nil == userInfo) {
|
||||
NSAssert(coreInfo.source == MDTraceSourceCore, @"invalid core source");
|
||||
mergeInfo = [coreInfo copy];
|
||||
} else if (nil == coreInfo && nil != userInfo) {
|
||||
NSAssert(userInfo.source == MDTraceSourceUser, @"invalid user source");
|
||||
mergeInfo = [userInfo copy];
|
||||
} else {
|
||||
NSAssert([coreInfo.name isEqualToString:userInfo.name], @"invalid name");
|
||||
NSAssert(coreInfo.source == MDTraceSourceCore, @"invalid core source");
|
||||
NSAssert(coreInfo.mode == MDTraceModeOff || coreInfo.mode == MDTraceModeExcludeBlackList, @"invalid core mode");
|
||||
NSAssert(userInfo.source == MDTraceSourceUser, @"invalid user source");
|
||||
|
||||
mergeInfo = [[MDTraceClassInfo alloc] init];
|
||||
mergeInfo.name = coreInfo.name;
|
||||
mergeInfo.source = MDTraceSourceMerge;
|
||||
mergeInfo.flag = coreInfo.flag | userInfo.flag;
|
||||
// core优先级比user优先级高
|
||||
if (coreInfo.mode == MDTraceModeOff) {
|
||||
// core关闭则无需关心user配置
|
||||
mergeInfo.mode = MDTraceModeOff;
|
||||
} else if (coreInfo.mode == MDTraceModeExcludeBlackList) {
|
||||
switch (userInfo.mode) {
|
||||
case MDTraceModeOff:
|
||||
// user关闭就不trace这个类
|
||||
mergeInfo.mode = MDTraceModeOff;
|
||||
break;
|
||||
case MDTraceModeAll:
|
||||
// 排除core指定的黑名单
|
||||
mergeInfo.mode = MDTraceModeExcludeBlackList;
|
||||
[mergeInfo.methodList addObjectsFromArray:coreInfo.methodList];
|
||||
break;
|
||||
case MDTraceModeIncludeWhiteList:
|
||||
// user白名单排除掉core黑名单
|
||||
mergeInfo.mode = MDTraceModeIncludeWhiteList;
|
||||
[mergeInfo.methodList addObjectsFromArray:[[self class] minusWithArrayA:userInfo.methodList arrayB:coreInfo.methodList]];
|
||||
break;
|
||||
case MDTraceModeExcludeBlackList:
|
||||
// user黑名单加上core黑名单
|
||||
mergeInfo.mode = MDTraceModeExcludeBlackList;
|
||||
[mergeInfo.methodList addObjectsFromArray:[[self class] unionWithArrayA:userInfo.methodList arrayB:coreInfo.methodList]];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return mergeInfo;
|
||||
}
|
||||
|
||||
+ (MDTraceClassInfo *)mergeInfoWithName:(NSString *)name coreConfig:(NSDictionary *)coreConfig userConfig:(NSDictionary *)userConfig;
|
||||
{
|
||||
MDTraceClassInfo *coreInfo = [MDTraceClassInfo infoWithName:name source:MDTraceSourceCore config:coreConfig];
|
||||
MDTraceClassInfo *userInfo = [MDTraceClassInfo infoWithName:name source:MDTraceSourceUser config:userConfig];
|
||||
return [MDTraceClassInfo mergeInfoWithCoreInfo:coreInfo userInfo:userInfo];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - MDMethodTrace
|
||||
|
||||
@implementation MDMethodTrace
|
||||
|
||||
+ (instancetype)sharedInstance {
|
||||
static MDMethodTrace *sharedInstance = nil;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
sharedInstance = [[MDMethodTrace alloc] init];
|
||||
});
|
||||
return sharedInstance;
|
||||
}
|
||||
|
||||
- (instancetype)init
|
||||
{
|
||||
self = [super init];
|
||||
if (self) {
|
||||
self.logLevel = MDTraceLogLeveDebug;
|
||||
self.logWhen = MDTraceLogWhenStartup;
|
||||
self.traceFlag = MDTraceFlagDefault;
|
||||
self.traceObject = MDTraceObjectNone;
|
||||
self.coreClassInfoList = [[NSMutableArray alloc] init];
|
||||
self.userClassInfoList = [[NSMutableArray alloc] init];
|
||||
self.regexClassInfoList = [[NSMutableArray alloc] init];
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
#if !TARGET_OS_SIMULATOR
|
||||
[self removeVolumeObserver];
|
||||
#endif
|
||||
}
|
||||
|
||||
#pragma mark - Trace utils
|
||||
|
||||
+ (NSString *)docPath
|
||||
{
|
||||
return [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
|
||||
}
|
||||
|
||||
// 安全检查
|
||||
+ (id)safeCheck:(id)content class:(Class)cls
|
||||
{
|
||||
if ([content isKindOfClass:cls]) {
|
||||
return content;
|
||||
}
|
||||
MDFatal(@"safeCheck failed, content(%@) should be class(%@) type", content, NSStringFromClass(cls));
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 正则匹配 optionss是否需要更多参数???
|
||||
// FIXME -[NSString rangeOfString:options:NSRegularExpressionSearch]在实际运行中会死循环,不太明白原因
|
||||
+ (BOOL)isMatchRegexString:(NSString *)regexString inputString:(NSString *)inputString
|
||||
{
|
||||
NSError *error = NULL;
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:regexString options:NSRegularExpressionDotMatchesLineSeparators | NSRegularExpressionAnchorsMatchLines error:&error];
|
||||
NSTextCheckingResult *result = [regex firstMatchInString:inputString options:0 range:NSMakeRange(0, [inputString length])];
|
||||
return nil != result;
|
||||
}
|
||||
|
||||
// 获取object名称
|
||||
+ (NSString *)NSStringFromTraceObject:(MDTraceObject)traceObject
|
||||
{
|
||||
NSString *name;
|
||||
switch (traceObject) {
|
||||
case MDTraceObjectNone:
|
||||
name = @"未指定类";
|
||||
break;
|
||||
case MDTraceObjectCoreClass:
|
||||
name = @"引擎类";
|
||||
break;
|
||||
case MDTraceObjectUserClass:
|
||||
name = @"用户类";
|
||||
break;
|
||||
case MDTraceObjectRegexClass:
|
||||
name = @"正则类";
|
||||
break;
|
||||
|
||||
default:
|
||||
NSAssert1(0, @"Unkown trace object: %tu", traceObject);
|
||||
break;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
// 判断object是否属于测试模式
|
||||
+ (BOOL)isTestTraceObject:(MDTraceObject)traceObject
|
||||
{
|
||||
return (traceObject == MDTraceObjectCoreClass);
|
||||
}
|
||||
|
||||
// 获取当前classInfoList
|
||||
- (NSArray *)classInfoList
|
||||
{
|
||||
if (self.traceObject == MDTraceObjectCoreClass) {
|
||||
return self.coreClassInfoList;
|
||||
} else if (self.traceObject == MDTraceObjectUserClass) {
|
||||
return self.userClassInfoList;
|
||||
} else if (self.traceObject == MDTraceObjectRegexClass) {
|
||||
return self.regexClassInfoList;
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// 查找某个类是否存在classInfoList中
|
||||
- (MDTraceClassInfo *)infoInClassInfoList:(NSString *)className
|
||||
{
|
||||
for (MDTraceClassInfo *info in [self classInfoList]) {
|
||||
if ([info.name isEqualToString:className]) {
|
||||
return info;
|
||||
}
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
||||
// dump输出ClassListInfo
|
||||
- (void)dumpClassListInfo
|
||||
{
|
||||
if (!(self.traceFlag & MDTraceFlagDumpClassListInfo)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MDLog(@"Dump %@ class list info: ", [[self class] NSStringFromTraceObject:self.traceObject]);
|
||||
MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
NSArray *classInfoList = [self classInfoList];
|
||||
for (int i = 0; i < classInfoList.count; i++) {
|
||||
MDLog(@"ClassList[%05d]: %@", i, classInfoList[i]);
|
||||
}
|
||||
MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
}
|
||||
|
||||
// dump类所有方法信息
|
||||
- (void)dumpClassMethodInfo:(MDTraceClassInfo *)classInfo
|
||||
{
|
||||
if (!(self.traceFlag & MDTraceFlagDumpClassMethod || classInfo.flag & MDTraceFlagDumpClassMethod)) {
|
||||
return;
|
||||
}
|
||||
|
||||
MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
|
||||
Class cls = NSClassFromString(classInfo.name);
|
||||
while (nil != cls) {
|
||||
unsigned int numMethods = 0;
|
||||
Method *methodList = class_copyMethodList(cls, &numMethods);
|
||||
if (NULL != methodList) {
|
||||
for (unsigned int i = 0; i < numMethods; i ++) {
|
||||
MDLog(@"-Class[%@] method[%03d]: %@", cls, i, [[self class] methodInfo:methodList[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
Class metaClass = object_getClass(cls);
|
||||
methodList = class_copyMethodList(metaClass, &numMethods);
|
||||
if (NULL != methodList) {
|
||||
for (unsigned int i = 0; i < numMethods; i ++) {
|
||||
MDLog(@"+Class[%@] method[%03d]: %@", cls, i, [[self class] methodInfo:methodList[i]]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!(self.traceFlag & MDTraceFlagDumpSuperClassMethod || classInfo.flag & MDTraceFlagDumpSuperClassMethod)) {
|
||||
break;
|
||||
}
|
||||
cls = [cls superclass];
|
||||
}
|
||||
|
||||
MDLog(@"~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
|
||||
}
|
||||
|
||||
// 某方法信息
|
||||
+ (NSString *)methodInfo:(Method)method
|
||||
{
|
||||
return [NSString stringWithFormat:@"name[%s] IMP[%p]", sel_getName(method_getName(method)), method_getImplementation(method)];
|
||||
}
|
||||
|
||||
#pragma mark - Trace log
|
||||
|
||||
// 初始化日志系统
|
||||
- (void)initLogger
|
||||
{
|
||||
#ifdef USE_DDLOG
|
||||
// 外部Lumberjack logger, 日志存放于目录"~/Library/Caches/Logs"
|
||||
[DDLog addLogger:[DDTTYLogger sharedInstance]]; // TTY = Xcode 控制台
|
||||
// [[DDTTYLogger sharedInstance] setColorsEnabled:YES];
|
||||
// [DDLog addLogger:[DDASLLogger sharedInstance]]; // ASL = Apple System Logs 苹果系统日志
|
||||
|
||||
DDFileLogger *fileLogger = [[DDFileLogger alloc] init]; // 本地文件日志
|
||||
fileLogger.doNotReuseLogFiles = YES;
|
||||
fileLogger.logFileManager.maximumNumberOfLogFiles = 1; // 永远创建一个文件,便于观察日志
|
||||
fileLogger.maximumFileSize = 0;
|
||||
fileLogger.rollingFrequency = 0;
|
||||
[DDLog addLogger:fileLogger];
|
||||
#endif
|
||||
|
||||
// 内部logger
|
||||
[[OCMethodTrace sharedInstance] setLogLevel:self.logLevel == MDTraceLogLeveError ? OMTLogLevelError : OMTLogLevelDebug];
|
||||
[[OCMethodTrace sharedInstance] setDelegate:self];
|
||||
}
|
||||
|
||||
// trace开关
|
||||
- (void)enableTrace:(BOOL)enable
|
||||
{
|
||||
if (!enable) {
|
||||
[OCMethodTrace sharedInstance].disableTrace = YES;
|
||||
} else {
|
||||
if (self.logWhen == MDTraceLogWhenVolume) {
|
||||
#if TARGET_OS_SIMULATOR
|
||||
[OCMethodTrace sharedInstance].disableTrace = NO;
|
||||
self.logWhen = MDTraceLogWhenStartup;
|
||||
MDLog(@"Volume control log is not supported when simulator, reset logWhen to %tu", self.logWhen);
|
||||
#else
|
||||
self.lastSystemVolume = [[AVAudioSession sharedInstance] outputVolume];
|
||||
// 需要异步注册通知,否则无法工作
|
||||
dispatch_async(dispatch_get_main_queue(), ^{
|
||||
[self addVolumeObserver];
|
||||
});
|
||||
#endif
|
||||
} else {
|
||||
[OCMethodTrace sharedInstance].disableTrace = NO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#if !TARGET_OS_SIMULATOR
|
||||
- (void)addVolumeObserver
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(onSystemVolumeChanged:) name:@"AVSystemController_SystemVolumeDidChangeNotification"
|
||||
object:nil];
|
||||
[[UIApplication sharedApplication] beginReceivingRemoteControlEvents];
|
||||
}
|
||||
|
||||
- (void)removeVolumeObserver
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self
|
||||
name:@"AVSystemController_SystemVolumeDidChangeNotification"
|
||||
object:nil];
|
||||
[[UIApplication sharedApplication] endReceivingRemoteControlEvents];
|
||||
}
|
||||
|
||||
- (void)onSystemVolumeChanged:(NSNotification *)notification
|
||||
{
|
||||
NSString *category = notification.userInfo[@"AVSystemController_AudioCategoryNotificationParameter"];
|
||||
NSString *changeReason = notification.userInfo[@"AVSystemController_AudioVolumeChangeReasonNotificationParameter"];
|
||||
if (![category isEqualToString:@"Audio/Video"] || ![changeReason isEqualToString:@"ExplicitVolumeChange"]) {
|
||||
return;
|
||||
}
|
||||
CGFloat volume = [[notification userInfo][@"AVSystemController_AudioVolumeNotificationParameter"] floatValue];
|
||||
[OCMethodTrace sharedInstance].disableTrace = volume < self.lastSystemVolume; // 音量升高触发日志输出
|
||||
self.lastSystemVolume = volume;
|
||||
}
|
||||
#endif
|
||||
|
||||
#pragma mark - Trace parse config
|
||||
|
||||
- (void)parseConfig:(NSDictionary *)config
|
||||
{
|
||||
if (nil == config) {
|
||||
return;
|
||||
}
|
||||
|
||||
MDLog(TRACE_README);
|
||||
|
||||
self.config = [NSMutableDictionary dictionaryWithDictionary:config];
|
||||
self.logLevel = [SAFE_CHECK(self.config[MDCONFIG_LOG_LEVEL_KEY], NSNumber) unsignedIntegerValue];
|
||||
self.logWhen = [SAFE_CHECK(self.config[MDCONFIG_LOG_WHEN_KEY], NSNumber) unsignedIntegerValue];
|
||||
self.traceFlag = [SAFE_CHECK(self.config[MDCONFIG_TRACE_FLAG_KEY], NSNumber) unsignedIntegerValue];
|
||||
self.traceObject = [SAFE_CHECK(self.config[MDCONFIG_TRACE_OBJECT_KEY], NSNumber) unsignedIntegerValue];
|
||||
|
||||
MDLog(@"logLevel: %tu: logWhen: %tu traceFlag: %tu traceObject: %tu(%@%@)",
|
||||
self.logLevel, self.logWhen, self.traceFlag, self.traceObject,
|
||||
[[self class] NSStringFromTraceObject:self.traceObject],
|
||||
[[self class] isTestTraceObject:self.traceObject] ? @"(仅测试验证使用,不建议开启)" : @"");
|
||||
|
||||
// 检查配置有效性
|
||||
[self checkConfig];
|
||||
|
||||
// 初始化日志
|
||||
[self initLogger];
|
||||
|
||||
// 屏蔽trace输出。避免hook准备过程中导致的递归调用
|
||||
[self enableTrace:NO];
|
||||
|
||||
// 实际hook类的各种方法
|
||||
[self traceClassObject];
|
||||
|
||||
// 恢复trace输出
|
||||
[self enableTrace:YES];
|
||||
}
|
||||
|
||||
- (void)checkConfig
|
||||
{
|
||||
NSAssert(self.logLevel >= MDTraceLogLeveError && self.logLevel < MDTraceLogLeveMax, @"invalid loglevel");
|
||||
NSAssert(self.logWhen >= MDTraceLogWhenStartup && self.logWhen < MDTraceLogWhenMax, @"invalid logWhen");
|
||||
NSAssert(self.traceFlag >= 0 && self.traceFlag <= MDTraceFlagMask, @"invalid traceFlag");
|
||||
NSAssert(self.traceObject >= MDTraceObjectNone && self.traceObject < MDTraceObjectMax, @"invalid traceObject");
|
||||
|
||||
if (self.logWhen == MDTraceLogWhenRegexString) {
|
||||
self.logRegexString = SAFE_CHECK(self.config[MDCONFIG_LOG_REGEX_STRING_KEY], NSString);
|
||||
if (self.logRegexString.length == 0) {
|
||||
MDFatal(@"LogRegexString is nil");
|
||||
}
|
||||
MDLog(@"LogRegexString: %@", self.logRegexString);
|
||||
}
|
||||
|
||||
if (self.traceObject == MDTraceObjectRegexClass) {
|
||||
self.classRegexString = SAFE_CHECK(self.config[MDCONFIG_CLASS_REGEX_STRING_KEY], NSString);
|
||||
if (self.classRegexString.length == 0) {
|
||||
MDFatal(@"ClassRegexString is nil");
|
||||
}
|
||||
MDLog(@"ClassRegexString: %@", self.classRegexString);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traceClassObject
|
||||
{
|
||||
if (self.traceObject == MDTraceObjectNone) {
|
||||
MDLog(@"Method Trace is disabled");
|
||||
return;
|
||||
}
|
||||
|
||||
[self parseClassListInfo];
|
||||
[self dumpClassListInfo];
|
||||
[self traceClassListInfo];
|
||||
}
|
||||
|
||||
- (void)parseClassListInfo
|
||||
{
|
||||
id object = nil;
|
||||
NSDictionary *coreClassListDict = nil;
|
||||
NSDictionary *userClassListDict = nil;
|
||||
NSMutableArray *classList = [[NSMutableArray alloc] init];
|
||||
NSMutableArray *regexClassList = [[NSMutableArray alloc] init];
|
||||
|
||||
object = [self.config valueForKey:MDCONFIG_CORE_CLASS_LIST];
|
||||
if (nil != object) {
|
||||
coreClassListDict = SAFE_CHECK(object, NSDictionary);
|
||||
}
|
||||
object = [self.config valueForKey:MDCONFIG_USER_CLASS_LIST];
|
||||
if (nil != object) {
|
||||
userClassListDict = SAFE_CHECK(object, NSDictionary);
|
||||
}
|
||||
|
||||
// 1 获取真实存在的类
|
||||
int numberOfClasses = objc_getClassList(NULL, 0);
|
||||
Class *tmpClassList = (Class *)malloc(sizeof(Class) * numberOfClasses);
|
||||
if (NULL != tmpClassList) {
|
||||
objc_getClassList(tmpClassList, numberOfClasses);
|
||||
for (int i = 0; i < numberOfClasses; i++) {
|
||||
NSString *className = NSStringFromClass(tmpClassList[i]);
|
||||
[classList addObject:className];
|
||||
if (self.traceObject == MDTraceObjectRegexClass && [[self class] isMatchRegexString:self.classRegexString inputString:className]) {
|
||||
// 多次匹配可能是重复类,需要去重
|
||||
if (![regexClassList containsObject:className]) {
|
||||
[regexClassList addObject:className];
|
||||
}
|
||||
}
|
||||
}
|
||||
free(tmpClassList);
|
||||
}
|
||||
|
||||
// 2 获取classInfo
|
||||
if (self.traceObject == MDTraceObjectCoreClass) {
|
||||
// 2.1 core
|
||||
for (NSString *className in coreClassListDict.allKeys) {
|
||||
if (![classList containsObject:className]) {
|
||||
MDLog(@"Cannot find class %@", className);
|
||||
continue;
|
||||
}
|
||||
|
||||
NSDictionary *coreConfig = coreClassListDict[className];
|
||||
MDTraceClassInfo *coreInfo = [MDTraceClassInfo infoWithName:className source:MDTraceSourceCore config:coreConfig];
|
||||
[self.coreClassInfoList addObject:coreInfo];
|
||||
}
|
||||
} else if (self.traceObject == MDTraceObjectUserClass) {
|
||||
// 2.2 user,注意,user需要跟core合并,优先级:core > user
|
||||
for (NSString *className in userClassListDict.allKeys) {
|
||||
if (![classList containsObject:className]) {
|
||||
MDLog(@"Cannot find class %@", className);
|
||||
continue;
|
||||
}
|
||||
|
||||
NSDictionary *coreConfig = coreClassListDict[className];
|
||||
NSDictionary *userConfig = userClassListDict[className];
|
||||
MDTraceClassInfo *mergeinfo = [MDTraceClassInfo mergeInfoWithName:className coreConfig:coreConfig userConfig:userConfig];
|
||||
[self.userClassInfoList addObject:mergeinfo];
|
||||
}
|
||||
} else if (self.traceObject == MDTraceObjectRegexClass) {
|
||||
// 2.3 regex, 优先级:core > user > regex
|
||||
|
||||
// 2.3.1 user需要跟core合并,优先级:core > user
|
||||
for (NSString *className in userClassListDict.allKeys) {
|
||||
if (![classList containsObject:className]) {
|
||||
MDLog(@"Cannot find class %@", className);
|
||||
continue;
|
||||
}
|
||||
|
||||
NSDictionary *coreConfig = coreClassListDict[className];
|
||||
NSDictionary *userConfig = userClassListDict[className];
|
||||
MDTraceClassInfo *mergeinfo = [MDTraceClassInfo mergeInfoWithName:className coreConfig:coreConfig userConfig:userConfig];
|
||||
[self.regexClassInfoList addObject:mergeinfo];
|
||||
}
|
||||
|
||||
// 2.3.2 regex需要跟core合并,优先级:core > regex。regex匹配的类可理解成更低优先级的user类
|
||||
NSDictionary *defaultUserConfig = [[NSDictionary alloc] init];
|
||||
for (NSString *className in regexClassList) {
|
||||
if (nil == [self infoInClassInfoList:className]) {
|
||||
NSDictionary *coreConfig = coreClassListDict[className];
|
||||
MDTraceClassInfo *mergeinfo = [MDTraceClassInfo mergeInfoWithName:className coreConfig:coreConfig userConfig:defaultUserConfig];
|
||||
[self.regexClassInfoList addObject:mergeinfo];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)traceClassListInfo
|
||||
{
|
||||
MDLog(@" ");
|
||||
MDLog(@"////////////////////////////////////////////////////////////////////////////////");
|
||||
MDLog(@" ");
|
||||
|
||||
NSArray *classInfoList = [self classInfoList];
|
||||
for (int i = 0; i < classInfoList.count; i++) {
|
||||
MDTraceClassInfo *info = classInfoList[i];
|
||||
if (nil != objc_getClass([info.name UTF8String])) {
|
||||
MDLog(@"ClassList[%05d]: %@", i, info.name);
|
||||
[self traceClass:info];
|
||||
} else {
|
||||
MDLog(@"Cannot find class %@", info.name);
|
||||
}
|
||||
}
|
||||
|
||||
MDLog(@" ");
|
||||
MDLog(@"////////////////////////////////////////////////////////////////////////////////");
|
||||
MDLog(@" ");
|
||||
}
|
||||
|
||||
- (void)traceClass:(MDTraceClassInfo *)classInfo
|
||||
{
|
||||
if (nil == classInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
[self dumpClassMethodInfo:classInfo];
|
||||
|
||||
if (classInfo.mode == MDTraceModeOff) {
|
||||
} else if (classInfo.mode == MDTraceModeAll) {
|
||||
[self addClassTrace:classInfo.name];
|
||||
} else if (classInfo.mode == MDTraceModeIncludeWhiteList) {
|
||||
[self addClassTrace:classInfo.name methodList:classInfo.methodList white:YES];
|
||||
} else if (classInfo.mode == MDTraceModeExcludeBlackList) {
|
||||
[self addClassTrace:classInfo.name methodList:classInfo.methodList white:NO];
|
||||
}
|
||||
|
||||
[self dumpClassMethodInfo:classInfo];
|
||||
}
|
||||
|
||||
#pragma mark - Trace method
|
||||
|
||||
- (void)addClassTrace:(NSString *)className{
|
||||
[self addClassTrace:className methodList:nil];
|
||||
}
|
||||
|
||||
- (void)addClassTrace:(NSString *)className methodName:(NSString *)methodName {
|
||||
[self addClassTrace:className methodList:@[methodName]];
|
||||
}
|
||||
|
||||
- (void)addClassTrace:(NSString *)className methodList:(NSArray*)methodList {
|
||||
[self addClassTrace:className methodList:methodList white:YES];
|
||||
}
|
||||
|
||||
- (void)addClassTrace:(NSString *)className methodList:(NSArray *)methodList white:(BOOL)white {
|
||||
Class targetClass = objc_getClass([className UTF8String]);
|
||||
if (targetClass != nil) {
|
||||
[[OCMethodTrace sharedInstance] traceMethodWithClass:NSClassFromString(className) condition:^BOOL(SEL sel) {
|
||||
if (methodList == nil || methodList.count == 0) {
|
||||
return YES;
|
||||
}
|
||||
for (id object in methodList) {
|
||||
NSString *methodName = SAFE_CHECK(object, NSString);
|
||||
// 方法可以是正则表达式
|
||||
if ([[self class] isMatchRegexString:methodName inputString:NSStringFromSelector(sel)]) {
|
||||
return white;
|
||||
}
|
||||
}
|
||||
return !white;
|
||||
} before:^(id target, Class cls, SEL sel, NSArray *args, int deep) {
|
||||
NSString *selector = NSStringFromSelector(sel);
|
||||
NSMutableString *selectorString = [NSMutableString new];
|
||||
if ([selector containsString:@":"]) {
|
||||
NSArray *selectorArrary = [selector componentsSeparatedByString:@":"];
|
||||
selectorArrary = [selectorArrary filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:@"length > 0"]];
|
||||
for (int i = 0; i < selectorArrary.count; i++) {
|
||||
[selectorString appendFormat:@"%@:%@ ", selectorArrary[i], args[i]];
|
||||
}
|
||||
} else {
|
||||
[selectorString appendString:selector];
|
||||
}
|
||||
|
||||
NSMutableString *deepString = [NSMutableString new];
|
||||
for (int i = 0; i < deep; i++) {
|
||||
[deepString appendString:@"-"];
|
||||
}
|
||||
|
||||
// [obj class]则分两种情况:
|
||||
// 1 当obj为实例对象时,[obj class]中class是实例方法:- (Class)class,返回的obj对象中的isa指针;
|
||||
// 2 当obj为类对象(包括元类和根类以及根元类)时,调用的是类方法:+ (Class)class,返回的结果为其本身。
|
||||
NSString *prefix = target == [target class] ? @"+" : @"-";
|
||||
// target不是强引用,如果打印接口异步,可能未实际调用description就被释放了,所以提前获取desc,保证线程安全
|
||||
NSString *description = [self descriptionWithTarget:target class:cls selector:sel targetPosition:OMTTargetPositionBeforeSelf];
|
||||
NSString *logString = nil;
|
||||
if ([target class] != [cls class]) {
|
||||
// 如果是子类调用基类方法,则()内打印基类名
|
||||
logString = [NSString stringWithFormat:@"%@%@[%@(%@) %@]", deepString, prefix, description, NSStringFromClass(cls), selectorString];
|
||||
} else {
|
||||
logString = [NSString stringWithFormat:@"%@%@[%@ %@]", deepString, prefix, description, selectorString];
|
||||
}
|
||||
|
||||
if (self.logWhen == MDTraceLogWhenStartup ||
|
||||
self.logWhen == MDTraceLogWhenVolume ||
|
||||
(self.logWhen == MDTraceLogWhenRegexString && [[self class] isMatchRegexString:self.logRegexString inputString:logString])) {
|
||||
self.numberOfPendingLog++;
|
||||
MDLog(@"%@", logString);
|
||||
}
|
||||
} after:^(id target, Class cls, SEL sel, id ret, int deep, NSTimeInterval interval) {
|
||||
// 因为多线程并发,numberOfPendingLog变量维护的输出状态有可能并不是那么准,但是打印调试可以容忍
|
||||
if (self.numberOfPendingLog > 0) {
|
||||
self.numberOfPendingLog--;
|
||||
NSMutableString *deepString = [NSMutableString new];
|
||||
for (int i = 0; i < deep; i++) {
|
||||
[deepString appendString:@"-"];
|
||||
}
|
||||
|
||||
NSString *prefix = target == [target class] ? @"+" : @"-";
|
||||
MDLog(@"%@%@ret:%@", deepString, prefix, ret);
|
||||
}
|
||||
}];
|
||||
} else {
|
||||
MDLog(@"Canot find class %@", className);
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - OCMethodTraceDelegate
|
||||
|
||||
- (NSString *)descriptionWithTarget:(id)target class:(Class)cls selector:(SEL)sel targetPosition:(OMTTargetPosition)targetPosition
|
||||
{
|
||||
if (nil == target) {
|
||||
return @"nil";
|
||||
}
|
||||
|
||||
NSString *targetClassName = NSStringFromClass([target class]);
|
||||
|
||||
// 全局跳过对象description方法
|
||||
if (self.traceFlag & MDTraceFlagDoesNotUseDescription) {
|
||||
return [NSString stringWithFormat:@"<%@: %p>", targetClassName, target];
|
||||
}
|
||||
|
||||
// 类跳过对象description方法,粒度更小一点
|
||||
MDTraceClassInfo *info = [self infoInClassInfoList:targetClassName];
|
||||
BOOL doesNotUseDescription = (nil != info && info.flag & MDTraceFlagDoesNotUseDescription);
|
||||
if (!doesNotUseDescription) {
|
||||
// 构造初始化函数特殊处理, 系统类初始化比较喜欢"_init"这样的方式
|
||||
NSString *selectorName = NSStringFromSelector(sel);
|
||||
BOOL isAllocFunc = [selectorName hasPrefix:@"new"] || [selectorName hasPrefix:@"alloc"];
|
||||
BOOL maybeInitFunc = [selectorName hasPrefix:@"init"] || [selectorName hasPrefix:@"_init"];
|
||||
BOOL isDeallocFunc = [selectorName isEqualToString:@"dealloc"];
|
||||
if (isAllocFunc || maybeInitFunc) {
|
||||
switch (targetPosition) {
|
||||
case OMTTargetPositionBeforeSelf:
|
||||
// 调用构造函数时,此时实例还没初始化完全,不能调用description
|
||||
doesNotUseDescription = YES;
|
||||
break;
|
||||
case OMTTargetPositionBeforeArgument:
|
||||
break;
|
||||
case OMTTargetPositionAfterSelf:
|
||||
case OMTTargetPositionAfterReturnValue:
|
||||
if (isAllocFunc) {
|
||||
doesNotUseDescription = YES;
|
||||
} else if (maybeInitFunc) {
|
||||
// 调用构造函数时,可能是调用父类的构造函数,此时实例还没初始化完全,不能调用description
|
||||
if (![targetClassName isEqualToString:NSStringFromClass(cls)]) {
|
||||
doesNotUseDescription = YES;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
} else if (isDeallocFunc) {
|
||||
// 析构函数所有只打印指针,因为dealloc after时,对象已被释放
|
||||
doesNotUseDescription = YES;
|
||||
}
|
||||
}
|
||||
|
||||
return doesNotUseDescription ? [NSString stringWithFormat:@"<%@: %p>", targetClassName, target] : [target description];
|
||||
}
|
||||
|
||||
- (void)log:(OMTLogLevel)level format:(NSString *)format, ...
|
||||
{
|
||||
va_list args;
|
||||
if (format) {
|
||||
va_start(args, format);
|
||||
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
MDLog(@"%@", message);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
#pragma mark - Entry
|
||||
|
||||
static __attribute__((constructor)) void entry()
|
||||
{
|
||||
NSDictionary *config = [[MDConfigManager sharedInstance] readConfigByKey:MDCONFIG_TRACE_KEY];
|
||||
[[MDMethodTrace sharedInstance] parseConfig:config];
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
// *** DO NOT EDIT THIS FILE! ***
|
||||
// This file is generated by Logos processing using dkhelperDylib.xm during each build.
|
||||
|
||||
// Logos by Dustin Howett
|
||||
// See http://iphonedevwiki.net/index.php/Logos
|
||||
@@ -0,0 +1,60 @@
|
||||
// See http://iphonedevwiki.net/index.php/Logos
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface CustomViewController
|
||||
|
||||
@property (nonatomic, copy) NSString* newProperty;
|
||||
|
||||
+ (void)classMethod;
|
||||
|
||||
- (NSString*)getMyName;
|
||||
|
||||
- (void)newMethod:(NSString*) output;
|
||||
|
||||
@end
|
||||
|
||||
%hook CustomViewController
|
||||
|
||||
+ (void)classMethod
|
||||
{
|
||||
%log;
|
||||
|
||||
%orig;
|
||||
}
|
||||
|
||||
%new
|
||||
-(void)newMethod:(NSString*) output{
|
||||
NSLog(@"This is a new method : %@", output);
|
||||
}
|
||||
|
||||
%new
|
||||
- (id)newProperty {
|
||||
return objc_getAssociatedObject(self, @selector(newProperty));
|
||||
}
|
||||
|
||||
%new
|
||||
- (void)setNewProperty:(id)value {
|
||||
objc_setAssociatedObject(self, @selector(newProperty), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
|
||||
}
|
||||
|
||||
- (NSString*)getMyName
|
||||
{
|
||||
%log;
|
||||
|
||||
NSString* password = MSHookIvar<NSString*>(self,"_password");
|
||||
|
||||
NSLog(@"password:%@", password);
|
||||
|
||||
[%c(CustomViewController) classMethod];
|
||||
|
||||
[self newMethod:@"output"];
|
||||
|
||||
self.newProperty = @"newProperty";
|
||||
|
||||
NSLog(@"newProperty : %@", self.newProperty);
|
||||
|
||||
return %orig();
|
||||
}
|
||||
|
||||
%end
|
||||
@@ -0,0 +1,30 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// LLDBTools.h
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2018/3/8.
|
||||
// Copyright © 2018年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <mach/vm_types.h>
|
||||
|
||||
//(lldb) po pviews()
|
||||
|
||||
NSString* pvc(void);
|
||||
|
||||
NSString* pviews(void);
|
||||
|
||||
NSString* pactions(vm_address_t address);
|
||||
|
||||
NSString* pblock(vm_address_t address);
|
||||
|
||||
NSString* methods(const char * classname);
|
||||
|
||||
NSString* ivars(vm_address_t address);
|
||||
|
||||
NSString* choose(const char* classname);
|
||||
|
||||
NSString* vmmap();
|
||||
@@ -0,0 +1,432 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// LLDBTools.m
|
||||
// MonkeyDev
|
||||
//
|
||||
// Created by AloneMonkey on 2018/3/8.
|
||||
// Copyright © 2018年 AloneMonkey. All rights reserved.
|
||||
//
|
||||
|
||||
#pragma GCC diagnostic ignored "-Wundeclared-selector"
|
||||
|
||||
#import "LLDBTools.h"
|
||||
#import <set>
|
||||
#import <mach/mach_types.h>
|
||||
#import <malloc/malloc.h>
|
||||
#import <objc/runtime.h>
|
||||
#import <mach/mach_init.h>
|
||||
#import <mach/mach_error.h>
|
||||
|
||||
enum {
|
||||
BLOCK_HAS_COPY_DISPOSE = (1 << 25),
|
||||
BLOCK_HAS_CTOR = (1 << 26), // helpers have C++ code
|
||||
BLOCK_IS_GLOBAL = (1 << 28),
|
||||
BLOCK_HAS_STRET = (1 << 29), // IFF BLOCK_HAS_SIGNATURE
|
||||
BLOCK_HAS_SIGNATURE = (1 << 30),
|
||||
};
|
||||
|
||||
struct Block_literal_1 {
|
||||
void *isa; // initialized to &_NSConcreteStackBlock or &_NSConcreteGlobalBlock
|
||||
int flags;
|
||||
int reserved;
|
||||
void (*invoke)(void *, ...);
|
||||
struct Block_descriptor_1 {
|
||||
unsigned long int reserved; // NULL
|
||||
unsigned long int size; // sizeof(struct Block_literal_1)
|
||||
// optional helper functions
|
||||
void (*copy_helper)(void *dst, void *src); // IFF (1<<25)
|
||||
void (*dispose_helper)(void *src); // IFF (1<<25)
|
||||
// required ABI.2010.3.16
|
||||
const char *signature; // IFF (1<<30)
|
||||
} *descriptor;
|
||||
// imported variables
|
||||
};
|
||||
|
||||
NSString* decode(NSString* code);
|
||||
NSArray* choose_inner(const char * classname);
|
||||
char * protection_bits_to_rwx (vm_prot_t p);
|
||||
const char * unparse_inheritance (vm_inherit_t i);
|
||||
char * behavior_to_text (vm_behavior_t b);
|
||||
|
||||
NSString* decode(NSString* code){
|
||||
NSDictionary * encodeMap = @{
|
||||
@"c": @"char",
|
||||
@"i": @"int",
|
||||
@"s": @"short",
|
||||
@"l": @"long",
|
||||
@"q": @"long long",
|
||||
|
||||
@"C": @"unsigned char",
|
||||
@"I": @"unsigned int",
|
||||
@"S": @"unsigned short",
|
||||
@"L": @"unsigned long",
|
||||
@"Q": @"unsigned long long",
|
||||
|
||||
@"f": @"float",
|
||||
@"d": @"double",
|
||||
@"B": @"bool",
|
||||
@"v": @"void",
|
||||
@"*": @"char *",
|
||||
@"@": @"id",
|
||||
@"#": @"Class",
|
||||
@":": @"SEL"
|
||||
};
|
||||
|
||||
if(encodeMap[code]){
|
||||
return encodeMap[code];
|
||||
}else if([code characterAtIndex:0] == '@'){
|
||||
if([code characterAtIndex:1] == '?'){
|
||||
return code;
|
||||
}else if([code characterAtIndex:2] == '<'){
|
||||
return [NSString stringWithFormat:@"id%@", [[code substringWithRange:NSMakeRange(2, code.length-3)] stringByReplacingOccurrencesOfString:@"><" withString:@", "]];
|
||||
}else{
|
||||
return [NSString stringWithFormat:@"%@ *", [code substringWithRange:NSMakeRange(2, code.length-3)]];
|
||||
}
|
||||
}else if([code characterAtIndex:0] == '^'){
|
||||
return [NSString stringWithFormat:@"%@ *", decode([code substringFromIndex:1])];
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
NSString* pvc(){
|
||||
return [[[UIWindow performSelector:@selector(keyWindow)] performSelector:@selector(rootViewController)] performSelector:@selector(_printHierarchy)];
|
||||
}
|
||||
|
||||
NSString* pviews(){
|
||||
return [[[UIApplication sharedApplication] keyWindow] performSelector:@selector(recursiveDescription)];
|
||||
}
|
||||
|
||||
NSString* pactions(vm_address_t address){
|
||||
NSMutableString* result = [NSMutableString new];
|
||||
UIControl* control = (__bridge UIControl*)(void*)address;
|
||||
NSArray* targets = [[control allTargets] allObjects];
|
||||
for (id item in targets) {
|
||||
NSArray* actions = [control actionsForTarget:item forControlEvent:0];
|
||||
[result appendFormat:@"<%s: 0x%lx>: %@\n", object_getClassName(item), (unsigned long)item, [actions componentsJoinedByString:@","]];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NSString* pblock(vm_address_t address){
|
||||
struct Block_literal_1 real = *((struct Block_literal_1 *)(void*)address);
|
||||
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
|
||||
[dict setObject:[NSNumber numberWithLong:(long)real.invoke] forKey:@"invoke"];
|
||||
if (real.flags & BLOCK_HAS_SIGNATURE) {
|
||||
char *signature;
|
||||
if (real.flags & BLOCK_HAS_COPY_DISPOSE) {
|
||||
signature = (char *)(real.descriptor)->signature;
|
||||
} else {
|
||||
signature = (char *)(real.descriptor)->copy_helper;
|
||||
}
|
||||
|
||||
NSMethodSignature *sig = [NSMethodSignature signatureWithObjCTypes:signature];
|
||||
NSMutableArray *types = [NSMutableArray array];
|
||||
|
||||
[types addObject:[NSString stringWithUTF8String:(char *)[sig methodReturnType]]];
|
||||
|
||||
for (NSUInteger i = 0; i < sig.numberOfArguments; i++) {
|
||||
char *type = (char *)[sig getArgumentTypeAtIndex:i];
|
||||
[types addObject:[NSString stringWithUTF8String:type]];
|
||||
}
|
||||
|
||||
[dict setObject:types forKey:@"signature"];
|
||||
}
|
||||
|
||||
NSMutableArray* sigArr = dict[@"signature"];
|
||||
|
||||
if(!sigArr){
|
||||
return [NSString stringWithFormat:@"Imp: 0x%lx", [dict[@"invoke"] longValue]];
|
||||
}else{
|
||||
NSMutableString* sig = [NSMutableString stringWithFormat:@"%@ ^(", decode(sigArr[0])];
|
||||
for (int i = 2; i < sigArr.count; i++) {
|
||||
if(i == sigArr.count - 1){
|
||||
[sig appendFormat:@"%@", decode(sigArr[i])];
|
||||
}else{
|
||||
[sig appendFormat:@"%@ ,", decode(sigArr[i])];
|
||||
}
|
||||
}
|
||||
[sig appendString:@");"];
|
||||
return [NSString stringWithFormat:@"Imp: 0x%lx Signature: %s", [dict[@"invoke"] longValue], [sig UTF8String]];
|
||||
}
|
||||
}
|
||||
|
||||
struct CYChoice {
|
||||
std::set<Class> query_;
|
||||
std::set<id> results_;
|
||||
};
|
||||
|
||||
struct CYObjectStruct {
|
||||
Class isa_;
|
||||
};
|
||||
|
||||
static void choose_(task_t task, void *baton, unsigned type, vm_range_t *ranges, unsigned count) {
|
||||
CYChoice *choice(reinterpret_cast<CYChoice *>(baton));
|
||||
|
||||
for (unsigned i(0); i != count; ++i) {
|
||||
vm_range_t &range(ranges[i]);
|
||||
void *data(reinterpret_cast<void *>(range.address));
|
||||
size_t size(range.size);
|
||||
|
||||
if (size < sizeof(CYObjectStruct))
|
||||
continue;
|
||||
|
||||
uintptr_t *pointers(reinterpret_cast<uintptr_t *>(data));
|
||||
#ifdef __arm64__
|
||||
Class isa = (__bridge Class)((void *)(pointers[0] & 0x1fffffff8));
|
||||
#else
|
||||
Class isa =(__bridge Class)(void *)pointers[0];
|
||||
#endif
|
||||
std::set<Class>::const_iterator result(choice->query_.find(isa));
|
||||
if (result == choice->query_.end())
|
||||
continue;
|
||||
|
||||
size_t needed(class_getInstanceSize(*result));
|
||||
// XXX: if (size < needed)
|
||||
|
||||
size_t boundary(496);
|
||||
#ifdef __LP64__
|
||||
boundary *= 2;
|
||||
#endif
|
||||
if ((needed <= boundary && (needed + 15) / 16 * 16 != size) || (needed > boundary && (needed + 511) / 512 * 512 != size))
|
||||
continue;
|
||||
choice->results_.insert((__bridge id)(data));
|
||||
}
|
||||
}
|
||||
|
||||
static Class *CYCopyClassList(size_t &size) {
|
||||
size = objc_getClassList(NULL, 0);
|
||||
Class *data(reinterpret_cast<Class *>(malloc(sizeof(Class) * size)));
|
||||
|
||||
for (;;) {
|
||||
size_t writ(objc_getClassList(data, (int)size));
|
||||
if (writ <= size) {
|
||||
size = writ;
|
||||
return data;
|
||||
}
|
||||
|
||||
Class *copy(reinterpret_cast<Class *>(realloc(data, sizeof(Class) * writ)));
|
||||
if (copy == NULL) {
|
||||
free(data);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
data = copy;
|
||||
size = writ;
|
||||
}
|
||||
}
|
||||
|
||||
static kern_return_t CYReadMemory(task_t task, vm_address_t address, vm_size_t size, void **data) {
|
||||
*data = reinterpret_cast<void *>(address);
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
NSArray* choose_inner(const char * classname){
|
||||
|
||||
Class _class = NSClassFromString([NSString stringWithUTF8String:classname]);
|
||||
|
||||
vm_address_t *zones = NULL;
|
||||
unsigned size = 0;
|
||||
//获取所有的zone信息 堆上的区域
|
||||
kern_return_t error = malloc_get_all_zones(mach_task_self(), &CYReadMemory, &zones, &size);
|
||||
assert(error == KERN_SUCCESS);
|
||||
|
||||
size_t number;
|
||||
Class *classes(CYCopyClassList(number));
|
||||
assert(classes != NULL);
|
||||
|
||||
CYChoice choice;
|
||||
|
||||
//找到目标Class
|
||||
for (size_t i(0); i != number; ++i)
|
||||
for (Class current(classes[i]); current != Nil; current = class_getSuperclass(current))
|
||||
if (current == _class) {
|
||||
choice.query_.insert(classes[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
for (unsigned i(0); i != size; ++i) {
|
||||
const malloc_zone_t *zone(reinterpret_cast<const malloc_zone_t *>(zones[i]));
|
||||
if (zone == NULL || zone->introspect == NULL)
|
||||
continue;
|
||||
|
||||
//枚举堆上的对象
|
||||
zone->introspect->enumerator(mach_task_self(), &choice, MALLOC_PTR_IN_USE_RANGE_TYPE, zones[i], &CYReadMemory, &choose_);
|
||||
}
|
||||
NSMutableArray * result = [[NSMutableArray alloc] init];
|
||||
|
||||
for (auto iter = choice.results_.begin(); iter != choice.results_.end(); iter++) {
|
||||
[result addObject:(id)*iter];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NSString* choose(const char* classname){
|
||||
NSMutableString* result = [NSMutableString new];
|
||||
NSArray* results = choose_inner(classname);
|
||||
[result appendFormat:@"Find %lu instance objects in memory!\n" , (unsigned long)results.count];
|
||||
for (id item in results) {
|
||||
[result appendFormat:@"<%s: 0x%llx>\n", object_getClassName(item), (long long)item];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
NSString* methods(const char * classname){
|
||||
return [objc_getClass(classname) performSelector:@selector(_shortMethodDescription)];
|
||||
}
|
||||
|
||||
NSString* ivars(vm_address_t address){
|
||||
id target = (__bridge id)(void*)address;
|
||||
return [target performSelector:@selector(_ivarDescription)];
|
||||
}
|
||||
|
||||
char * protection_bits_to_rwx (vm_prot_t p){
|
||||
static char returned[4];
|
||||
|
||||
returned[0] = (p & VM_PROT_READ ? 'r' : '-');
|
||||
returned[1] = (p & VM_PROT_WRITE ? 'w' : '-');
|
||||
returned[2] = (p & VM_PROT_EXECUTE ? 'x' : '-');
|
||||
returned[3] = '\0';
|
||||
|
||||
// memory leak here. No biggy
|
||||
return (strdup(returned));
|
||||
}
|
||||
|
||||
const char * unparse_inheritance (vm_inherit_t i){
|
||||
switch (i){
|
||||
case VM_INHERIT_SHARE:
|
||||
return "share";
|
||||
case VM_INHERIT_COPY:
|
||||
return "copy";
|
||||
case VM_INHERIT_NONE:
|
||||
return "none";
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
char * behavior_to_text (vm_behavior_t b){
|
||||
switch (b){
|
||||
case VM_BEHAVIOR_DEFAULT: return((char*)"default");
|
||||
case VM_BEHAVIOR_RANDOM: return((char*)"random");
|
||||
case VM_BEHAVIOR_SEQUENTIAL: return((char*)"fwd-seq");
|
||||
case VM_BEHAVIOR_RSEQNTL: return((char*)"rev-seq");
|
||||
case VM_BEHAVIOR_WILLNEED: return((char*)"will-need");
|
||||
case VM_BEHAVIOR_DONTNEED: return((char*)"will-need");
|
||||
case VM_BEHAVIOR_FREE: return((char*)"free-nowb");
|
||||
case VM_BEHAVIOR_ZERO_WIRED_PAGES: return((char*)"zero-wire");
|
||||
case VM_BEHAVIOR_REUSABLE: return((char*)"reusable");
|
||||
case VM_BEHAVIOR_REUSE: return((char*)"reuse");
|
||||
case VM_BEHAVIOR_CAN_REUSE: return((char*)"canreuse");
|
||||
default: return ((char*)"?");
|
||||
}
|
||||
}
|
||||
|
||||
__BEGIN_DECLS
|
||||
|
||||
extern kern_return_t mach_vm_region
|
||||
(
|
||||
vm_map_t target_task,
|
||||
mach_vm_address_t *address,
|
||||
mach_vm_size_t *size,
|
||||
vm_region_flavor_t flavor,
|
||||
vm_region_info_t info,
|
||||
mach_msg_type_number_t *infoCnt,
|
||||
mach_port_t *object_name
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
NSString* vmmap(){
|
||||
vm_region_basic_info_data_t info, prev_info;
|
||||
mach_vm_address_t address = 1, prev_address;
|
||||
mach_vm_size_t size, prev_size;
|
||||
mach_msg_type_number_t count = VM_REGION_BASIC_INFO_COUNT_64;
|
||||
mach_port_t object_name;
|
||||
|
||||
int nsubregions = 0;
|
||||
kern_return_t kr = mach_vm_region(mach_task_self(), &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object_name);
|
||||
|
||||
NSMutableString* result = [[NSMutableString alloc] init];
|
||||
|
||||
if(kr != KERN_SUCCESS){
|
||||
[result appendFormat:@"mach_vm_region: Error %d - %s", kr, mach_error_string(kr)];
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
//保存之前查到的info
|
||||
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t));
|
||||
prev_address = address;
|
||||
prev_size = size;
|
||||
nsubregions = 1;
|
||||
|
||||
while (true) {
|
||||
int print = 0, done = 0;
|
||||
|
||||
address = prev_address + prev_size;
|
||||
|
||||
if (address == 0){
|
||||
print = done = 1;
|
||||
}
|
||||
|
||||
if (!done){
|
||||
kr = mach_vm_region (mach_task_self(), &address, &size, VM_REGION_BASIC_INFO, (vm_region_info_t)&info, &count, &object_name);
|
||||
|
||||
if (kr != KERN_SUCCESS){
|
||||
[result appendFormat:@"mach_vm_region failed for address %llu - Error: %x\n",address, (kr)];
|
||||
print = done = 1;
|
||||
}
|
||||
}
|
||||
|
||||
//等于才是连续的内存,不等于才打印
|
||||
if (address != prev_address + prev_size)
|
||||
print = 1;
|
||||
|
||||
//或者权限信息改变了也打印
|
||||
if ((info.protection != prev_info.protection)
|
||||
|| (info.max_protection != prev_info.max_protection)
|
||||
|| (info.inheritance != prev_info.inheritance)
|
||||
|| (info.shared != prev_info.reserved)
|
||||
|| (info.reserved != prev_info.reserved))
|
||||
print = 1;
|
||||
|
||||
if (print){
|
||||
char *print_size_unit = NULL;
|
||||
|
||||
mach_vm_size_t print_size = prev_size;
|
||||
if (print_size > 1024) { print_size /= 1024; print_size_unit = (char*)"K"; }
|
||||
if (print_size > 1024) { print_size /= 1024; print_size_unit = (char*)"M"; }
|
||||
if (print_size > 1024) { print_size /= 1024; print_size_unit = (char*)"G"; }
|
||||
|
||||
[result appendFormat:@" %p-%p [%llu%s](%s/%s; %s, %s, %s) %s",
|
||||
(void*)(prev_address),
|
||||
(void*)(prev_address + prev_size),
|
||||
print_size,
|
||||
print_size_unit,
|
||||
protection_bits_to_rwx (prev_info.protection),
|
||||
protection_bits_to_rwx (prev_info.max_protection),
|
||||
unparse_inheritance (prev_info.inheritance),
|
||||
prev_info.shared ? "shared" : "private",
|
||||
prev_info.reserved ? "reserved" : "not-reserved",
|
||||
behavior_to_text (prev_info.behavior)];
|
||||
|
||||
if (nsubregions > 1)
|
||||
[result appendFormat:@" (%d sub-regions)", nsubregions];
|
||||
|
||||
[result appendFormat:@"\n"];
|
||||
prev_address = address;
|
||||
prev_size = size;
|
||||
memcpy (&prev_info, &info, sizeof (vm_region_basic_info_data_t));
|
||||
nsubregions = 1;
|
||||
}else{
|
||||
prev_size += size;
|
||||
nsubregions++;
|
||||
}
|
||||
|
||||
if (done)
|
||||
break;
|
||||
}
|
||||
return [result copy];
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* OCMethodTrace.h
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
// 方法跟踪条件block
|
||||
// @param sel 方法名
|
||||
typedef BOOL (^OMTConditionBlock)(SEL sel);
|
||||
|
||||
// 方法调用前会调用该block
|
||||
// @param target 跟踪目标对象
|
||||
// @param cls 调用方法所在的类(可以是target所在的类,也可以是target的父类)
|
||||
// @param sel 方法名
|
||||
// @param args 参数列表
|
||||
// @param deep 调用层次
|
||||
typedef void (^OMTBeforeBlock)(id target, Class cls, SEL sel, NSArray *args, int deep);
|
||||
|
||||
// 方法调用后会调用该block
|
||||
// @param target 跟踪目标对象
|
||||
// @param cls 调用方法所在的类(可以是target所在的类,也可以是target的父类)
|
||||
// @param sel 方法名
|
||||
// @param ret 返回值
|
||||
// @param deep 调用层次
|
||||
// @param interval 执行方法的ms耗时
|
||||
typedef void (^OMTAfterBlock)(id target, Class cls, SEL sel, id ret, int deep, NSTimeInterval interval);
|
||||
|
||||
// target位置
|
||||
typedef NS_ENUM(NSUInteger, OMTTargetPosition) {
|
||||
OMTTargetPositionBeforeSelf = 0, // before-调用者self
|
||||
OMTTargetPositionBeforeArgument, // before-参数
|
||||
OMTTargetPositionAfterSelf, // after-调用者self
|
||||
OMTTargetPositionAfterReturnValue, // after-返回值
|
||||
OMTTargetPositionMax,
|
||||
};
|
||||
|
||||
// 日志级别
|
||||
typedef NS_ENUM(NSUInteger, OMTLogLevel) {
|
||||
OMTLogLevelError = 0,
|
||||
OMTLogLevelDebug,
|
||||
OMTLogLevelMax,
|
||||
};
|
||||
|
||||
@protocol OCMethodTraceDelegate <NSObject>
|
||||
|
||||
@optional
|
||||
// 获取target的description回调
|
||||
- (NSString *)descriptionWithTarget:(id)target class:(Class)cls selector:(SEL)sel targetPosition:(OMTTargetPosition)targetPosition;
|
||||
// 日志回调
|
||||
- (void)log:(OMTLogLevel)level format:(NSString *)format, ... NS_FORMAT_FUNCTION(2,3);
|
||||
|
||||
@end
|
||||
|
||||
@interface OCMethodTrace : NSObject
|
||||
|
||||
@property (nonatomic, assign) BOOL disableTrace; // 屏蔽before和after调用,hook完成后默认打开
|
||||
@property (nonatomic, weak) id<OCMethodTraceDelegate> delegate; // 回调
|
||||
@property (nonatomic, assign) OMTLogLevel logLevel; // 日志级别,默认OMTLogLevelDebug
|
||||
|
||||
+ (OCMethodTrace *)sharedInstance;
|
||||
|
||||
// 跟踪打印目标(实例或类)方法调用
|
||||
// @param cls 跟踪打印目标类
|
||||
// @param condition 判断是否跟踪方法的block
|
||||
// @param before 被跟踪的方法调用前会调用该block
|
||||
// @param after 被跟踪的方法调用后会调用该block
|
||||
- (void)traceMethodWithClass:(Class)cls
|
||||
condition:(OMTConditionBlock)condition
|
||||
before:(OMTBeforeBlock)before
|
||||
after:(OMTAfterBlock)after;
|
||||
|
||||
@end
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* OCSelectorTrampolines.mm
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
FOUNDATION_EXTERN IMP imp_implementationWithSelector(SEL aSel, const char *signature);
|
||||
FOUNDATION_EXTERN SEL imp_getSelector(IMP anImp);
|
||||
FOUNDATION_EXTERN BOOL imp_removeSelector(IMP anImp);
|
||||
@@ -0,0 +1,449 @@
|
||||
/*
|
||||
* OCSelectorTrampolines.mm
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#import "OCSelectorTrampolines.h"
|
||||
#import <objc/runtime.h>
|
||||
#import <objc/message.h>
|
||||
#import <mach/mach.h>
|
||||
#import <pthread.h>
|
||||
|
||||
// Define SUPPORT_STRET on architectures that need separate struct-return ABI.
|
||||
#if defined(__arm64__)
|
||||
# define SUPPORT_STRET 0
|
||||
#else
|
||||
# define SUPPORT_STRET 1
|
||||
#endif
|
||||
|
||||
#define _ost_fatal(fmt, ...) [NSException raise:@"OCSelectorTrampolines" format:fmt, ##__VA_ARGS__]
|
||||
|
||||
// symbols defined in assembly files
|
||||
// Don't use the symbols directly; they're thumb-biased on some ARM archs.
|
||||
#define TRAMP(tramp) \
|
||||
static inline __unused uintptr_t tramp(void) { \
|
||||
extern void *_##tramp; \
|
||||
return ((uintptr_t)&_##tramp) & ~1UL; \
|
||||
}
|
||||
// Scalar return
|
||||
TRAMP(a1a2_selectorTrampHead); // trampoline header code
|
||||
TRAMP(a1a2_firstSelectorTramp); // first trampoline
|
||||
TRAMP(a1a2_selectorTrampEnd); // after the last trampoline
|
||||
|
||||
#if SUPPORT_STRET
|
||||
// Struct return
|
||||
TRAMP(a2a3_selectorTrampHead);
|
||||
TRAMP(a2a3_firstSelectorTramp);
|
||||
TRAMP(a2a3_selectorTrampEnd);
|
||||
#endif
|
||||
|
||||
// argument mode identifier
|
||||
typedef enum {
|
||||
ReturnValueInRegisterArgumentMode,
|
||||
#if SUPPORT_STRET
|
||||
ReturnValueOnStackArgumentMode,
|
||||
#endif
|
||||
|
||||
ArgumentModeCount
|
||||
} ArgumentMode;
|
||||
|
||||
|
||||
// We must take care with our data layout on architectures that support
|
||||
// multiple page sizes.
|
||||
//
|
||||
// The trampoline template in __TEXT is sized and aligned with PAGE_MAX_SIZE.
|
||||
// On some platforms this requires additional linker flags.
|
||||
//
|
||||
// When we allocate a page pair, we use PAGE_MAX_SIZE size.
|
||||
// This allows trampoline code to find its data by subtracting PAGE_MAX_SIZE.
|
||||
//
|
||||
// When we allocate a page pair, we use the process's page alignment.
|
||||
// This simplifies allocation because we don't need to force greater than
|
||||
// default alignment when running with small pages, but it also means
|
||||
// the trampoline code MUST NOT look for its data by masking with PAGE_MAX_MASK.
|
||||
|
||||
struct TrampolineSelectorPagePair
|
||||
{
|
||||
IMP msgSend; // msg send hander
|
||||
|
||||
TrampolineSelectorPagePair *nextPagePair; // linked list of all pages
|
||||
TrampolineSelectorPagePair *nextAvailablePage; // linked list of pages with available slots
|
||||
|
||||
uintptr_t nextAvailable; // index of next available slot, endIndex() if no more available
|
||||
|
||||
// Payload data: selector and free list.
|
||||
// Bytes parallel with trampoline header code are the fields above or unused
|
||||
// uint8_t selectors[ PAGE_MAX_SIZE - sizeof(TrampolineSelectorPagePair) ]
|
||||
|
||||
// Code: trampoline header followed by trampolines.
|
||||
// uint8_t trampolines[PAGE_MAX_SIZE];
|
||||
|
||||
// Per-trampoline selector data format:
|
||||
// initial value is 0 while page data is filled sequentially
|
||||
// when filled, value is reference to selector
|
||||
// when empty, value is index of next available slot OR 0 if never used yet
|
||||
|
||||
union Payload {
|
||||
SEL selector;
|
||||
uintptr_t nextAvailable; // free list
|
||||
};
|
||||
|
||||
static uintptr_t headerSize() {
|
||||
return (uintptr_t) (a1a2_firstSelectorTramp() - a1a2_selectorTrampHead());
|
||||
}
|
||||
|
||||
static uintptr_t slotSize() {
|
||||
return 8;
|
||||
}
|
||||
|
||||
static uintptr_t startIndex() {
|
||||
// headerSize is assumed to be slot-aligned
|
||||
return headerSize() / slotSize();
|
||||
}
|
||||
|
||||
static uintptr_t endIndex() {
|
||||
return (uintptr_t)PAGE_MAX_SIZE / slotSize();
|
||||
}
|
||||
|
||||
static bool validIndex(uintptr_t index) {
|
||||
return (index >= startIndex() && index < endIndex());
|
||||
}
|
||||
|
||||
Payload *payload(uintptr_t index) {
|
||||
assert(validIndex(index));
|
||||
return (Payload *)((char *)this + index*slotSize());
|
||||
}
|
||||
|
||||
IMP trampoline(uintptr_t index) {
|
||||
assert(validIndex(index));
|
||||
char *imp = (char *)this + index*slotSize() + PAGE_MAX_SIZE;
|
||||
#if __arm__
|
||||
imp++; // trampoline is Thumb instructions
|
||||
#endif
|
||||
return (IMP)imp;
|
||||
}
|
||||
|
||||
uintptr_t indexForTrampoline(IMP tramp) {
|
||||
uintptr_t tramp0 = (uintptr_t)this + PAGE_MAX_SIZE;
|
||||
uintptr_t start = tramp0 + headerSize();
|
||||
uintptr_t end = tramp0 + PAGE_MAX_SIZE;
|
||||
uintptr_t address = (uintptr_t)tramp;
|
||||
if (address >= start && address < end) {
|
||||
return (uintptr_t)(address - tramp0) / slotSize();
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void check() {
|
||||
assert(TrampolineSelectorPagePair::slotSize() == 8);
|
||||
assert(TrampolineSelectorPagePair::headerSize() >= sizeof(TrampolineSelectorPagePair));
|
||||
assert(TrampolineSelectorPagePair::headerSize() % TrampolineSelectorPagePair::slotSize() == 0);
|
||||
|
||||
// _objc_inform("%p %p %p", a1a2_selectorTrampHead(), a1a2_firstSelectorTramp(),
|
||||
// a1a2_selectorTrampEnd());
|
||||
assert(a1a2_selectorTrampHead() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE
|
||||
assert(a1a2_selectorTrampHead() + PAGE_MAX_SIZE == a1a2_selectorTrampEnd());
|
||||
#if SUPPORT_STRET
|
||||
// _objc_inform("%p %p %p", a2a3_selectorTrampHead(), a2a3_firstSelectorTramp(),
|
||||
// a2a3_selectorTrampEnd());
|
||||
assert(a2a3_selectorTrampHead() % PAGE_SIZE == 0); // not PAGE_MAX_SIZE
|
||||
assert(a2a3_selectorTrampHead() + PAGE_MAX_SIZE == a2a3_selectorTrampEnd());
|
||||
#endif
|
||||
|
||||
#if __arm__
|
||||
// make sure trampolines are Thumb
|
||||
extern void *_a1a2_firstSelectorTramp;
|
||||
extern void *_a2a3_firstSelectorTramp;
|
||||
assert(((uintptr_t)&_a1a2_firstSelectorTramp) % 2 == 1);
|
||||
assert(((uintptr_t)&_a2a3_firstSelectorTramp) % 2 == 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
// two sets of trampoline pages; one for stack returns and one for register returns
|
||||
static TrampolineSelectorPagePair *headPagePairs[ArgumentModeCount];
|
||||
|
||||
#pragma mark Utility Functions
|
||||
|
||||
static pthread_rwlock_t trampolinesLock = PTHREAD_RWLOCK_INITIALIZER;
|
||||
|
||||
static inline void _lock() {
|
||||
int err = pthread_rwlock_wrlock(&trampolinesLock);
|
||||
if (err) _ost_fatal(@"pthread_rwlock_wrlock failed (%d)", err);
|
||||
}
|
||||
|
||||
static inline void _unlock() {
|
||||
int err = pthread_rwlock_unlock(&trampolinesLock);
|
||||
if (err) _ost_fatal(@"pthread_rwlock_unlock failed (%d)", err);
|
||||
}
|
||||
|
||||
static inline void _assert_locked() {
|
||||
}
|
||||
|
||||
#pragma mark Trampoline Management Functions
|
||||
static TrampolineSelectorPagePair *_allocateTrampolinesAndData(ArgumentMode aMode)
|
||||
{
|
||||
_assert_locked();
|
||||
|
||||
vm_address_t dataAddress;
|
||||
|
||||
TrampolineSelectorPagePair::check();
|
||||
|
||||
TrampolineSelectorPagePair *headPagePair = headPagePairs[aMode];
|
||||
|
||||
assert(headPagePair == nil || headPagePair->nextAvailablePage == nil);
|
||||
|
||||
kern_return_t result;
|
||||
result = vm_allocate(mach_task_self(), &dataAddress, PAGE_MAX_SIZE * 2,
|
||||
VM_FLAGS_ANYWHERE | VM_MAKE_TAG(VM_MEMORY_FOUNDATION));
|
||||
if (result != KERN_SUCCESS) {
|
||||
_ost_fatal(@"vm_allocate trampolines failed (%d)", result);
|
||||
}
|
||||
|
||||
vm_address_t codeAddress = dataAddress + PAGE_MAX_SIZE;
|
||||
|
||||
uintptr_t codePage = NULL;
|
||||
IMP sendImp = NULL;
|
||||
switch(aMode) {
|
||||
case ReturnValueInRegisterArgumentMode:
|
||||
codePage = a1a2_selectorTrampHead();
|
||||
sendImp = objc_msgSend;
|
||||
break;
|
||||
#if SUPPORT_STRET
|
||||
case ReturnValueOnStackArgumentMode:
|
||||
codePage = a2a3_selectorTrampHead();
|
||||
sendImp = objc_msgSend_stret;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
_ost_fatal(@"unknown return mode %d", (int)aMode);
|
||||
break;
|
||||
}
|
||||
|
||||
vm_prot_t currentProtection, maxProtection;
|
||||
result = vm_remap(mach_task_self(), &codeAddress, PAGE_MAX_SIZE,
|
||||
0, VM_FLAGS_FIXED | VM_FLAGS_OVERWRITE,
|
||||
mach_task_self(), codePage, TRUE,
|
||||
¤tProtection, &maxProtection, VM_INHERIT_SHARE);
|
||||
if (result != KERN_SUCCESS) {
|
||||
// vm_deallocate(mach_task_self(), dataAddress, PAGE_MAX_SIZE * 2);
|
||||
_ost_fatal(@"vm_remap trampolines failed (%d)", result);
|
||||
}
|
||||
|
||||
TrampolineSelectorPagePair *pagePair = (TrampolineSelectorPagePair *) dataAddress;
|
||||
pagePair->nextAvailable = pagePair->startIndex();
|
||||
pagePair->nextPagePair = nil;
|
||||
pagePair->nextAvailablePage = nil;
|
||||
pagePair->msgSend = sendImp;
|
||||
|
||||
if (headPagePair) {
|
||||
TrampolineSelectorPagePair *lastPagePair = headPagePair;
|
||||
while(lastPagePair->nextPagePair) {
|
||||
lastPagePair = lastPagePair->nextPagePair;
|
||||
}
|
||||
lastPagePair->nextPagePair = pagePair;
|
||||
headPagePairs[aMode]->nextAvailablePage = pagePair;
|
||||
} else {
|
||||
headPagePairs[aMode] = pagePair;
|
||||
}
|
||||
|
||||
return pagePair;
|
||||
}
|
||||
|
||||
static TrampolineSelectorPagePair *
|
||||
_getOrAllocatePagePairWithNextAvailable(ArgumentMode aMode)
|
||||
{
|
||||
_assert_locked();
|
||||
|
||||
TrampolineSelectorPagePair *headPagePair = headPagePairs[aMode];
|
||||
|
||||
if (!headPagePair)
|
||||
return _allocateTrampolinesAndData(aMode);
|
||||
|
||||
// make sure head page is filled first
|
||||
if (headPagePair->nextAvailable != headPagePair->endIndex())
|
||||
return headPagePair;
|
||||
|
||||
if (headPagePair->nextAvailablePage) // check if there is a page w/a hole
|
||||
return headPagePair->nextAvailablePage;
|
||||
|
||||
return _allocateTrampolinesAndData(aMode); // tack on a new one
|
||||
}
|
||||
|
||||
static TrampolineSelectorPagePair *
|
||||
_pageAndIndexContainingIMP(IMP anImp, uintptr_t *outIndex,
|
||||
TrampolineSelectorPagePair **outHeadPagePair)
|
||||
{
|
||||
_assert_locked();
|
||||
|
||||
for (int arg = 0; arg < ArgumentModeCount; arg++) {
|
||||
for (TrampolineSelectorPagePair *pagePair = headPagePairs[arg];
|
||||
pagePair;
|
||||
pagePair = pagePair->nextPagePair)
|
||||
{
|
||||
uintptr_t index = pagePair->indexForTrampoline(anImp);
|
||||
if (index) {
|
||||
if (outIndex) *outIndex = index;
|
||||
if (outHeadPagePair) *outHeadPagePair = headPagePairs[arg];
|
||||
return pagePair;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil;
|
||||
}
|
||||
|
||||
static ArgumentMode
|
||||
_argumentModeForSignature(const char *signature)
|
||||
{
|
||||
ArgumentMode aMode = ReturnValueInRegisterArgumentMode;
|
||||
|
||||
#if SUPPORT_STRET
|
||||
if (signature && signature[0] == '{') {
|
||||
@try {
|
||||
// In some cases that returns struct, we should use the '_stret' API:
|
||||
// http://sealiesoftware.com/blog/archive/2008/10/30/objc_explain_objc_msgSend_stret.html
|
||||
// NSMethodSignature knows the detail but has no API to return, we can only get the info from debugDescription.
|
||||
NSMethodSignature *methodSignature = [NSMethodSignature signatureWithObjCTypes:signature];
|
||||
if ([methodSignature.debugDescription rangeOfString:@"is special struct return? YES"].location != NSNotFound) {
|
||||
aMode = ReturnValueOnStackArgumentMode;
|
||||
}
|
||||
} @catch (__unused NSException *e) {}
|
||||
}
|
||||
#endif
|
||||
|
||||
return aMode;
|
||||
}
|
||||
|
||||
#pragma mark Public API
|
||||
IMP imp_implementationWithSelector(SEL aSel, const char *signature)
|
||||
{
|
||||
IMP returnIMP;
|
||||
|
||||
_lock();
|
||||
|
||||
ArgumentMode aMode = _argumentModeForSignature(signature);
|
||||
|
||||
TrampolineSelectorPagePair *pagePair =
|
||||
_getOrAllocatePagePairWithNextAvailable(aMode);
|
||||
if (!headPagePairs[aMode])
|
||||
headPagePairs[aMode] = pagePair;
|
||||
|
||||
uintptr_t index = pagePair->nextAvailable;
|
||||
assert(index >= pagePair->startIndex() && index < pagePair->endIndex());
|
||||
TrampolineSelectorPagePair::Payload *payload = pagePair->payload(index);
|
||||
|
||||
uintptr_t nextAvailableIndex = payload->nextAvailable;
|
||||
if (nextAvailableIndex == 0) {
|
||||
// First time through (unused slots are zero). Fill sequentially.
|
||||
// If the page is now full this will now be endIndex(), handled below.
|
||||
nextAvailableIndex = index + 1;
|
||||
}
|
||||
pagePair->nextAvailable = nextAvailableIndex;
|
||||
if (nextAvailableIndex == pagePair->endIndex()) {
|
||||
// PagePair is now full (free list or wilderness exhausted)
|
||||
// Remove from available page linked list
|
||||
TrampolineSelectorPagePair *iterator = headPagePairs[aMode];
|
||||
while(iterator && (iterator->nextAvailablePage != pagePair)) {
|
||||
iterator = iterator->nextAvailablePage;
|
||||
}
|
||||
if (iterator) {
|
||||
iterator->nextAvailablePage = pagePair->nextAvailablePage;
|
||||
pagePair->nextAvailablePage = nil;
|
||||
}
|
||||
}
|
||||
|
||||
payload->selector = aSel;
|
||||
returnIMP = pagePair->trampoline(index);
|
||||
|
||||
_unlock();
|
||||
|
||||
return returnIMP;
|
||||
}
|
||||
|
||||
SEL imp_getSelector(IMP anImp) {
|
||||
uintptr_t index;
|
||||
TrampolineSelectorPagePair *pagePair;
|
||||
|
||||
if (!anImp) return nil;
|
||||
|
||||
_lock();
|
||||
|
||||
pagePair = _pageAndIndexContainingIMP(anImp, &index, nil);
|
||||
|
||||
if (!pagePair) {
|
||||
_unlock();
|
||||
return nil;
|
||||
}
|
||||
|
||||
TrampolineSelectorPagePair::Payload *payload = pagePair->payload(index);
|
||||
|
||||
if (payload->nextAvailable <= TrampolineSelectorPagePair::endIndex()) {
|
||||
// unallocated
|
||||
_unlock();
|
||||
return nil;
|
||||
}
|
||||
|
||||
_unlock();
|
||||
|
||||
return payload->selector;
|
||||
}
|
||||
|
||||
BOOL imp_removeSelector(IMP anImp) {
|
||||
TrampolineSelectorPagePair *pagePair;
|
||||
TrampolineSelectorPagePair *headPagePair;
|
||||
uintptr_t index;
|
||||
|
||||
if (!anImp) return NO;
|
||||
|
||||
_lock();
|
||||
pagePair = _pageAndIndexContainingIMP(anImp, &index, &headPagePair);
|
||||
|
||||
if (!pagePair) {
|
||||
_unlock();
|
||||
return NO;
|
||||
}
|
||||
|
||||
TrampolineSelectorPagePair::Payload *payload = pagePair->payload(index);
|
||||
|
||||
payload->nextAvailable = pagePair->nextAvailable;
|
||||
pagePair->nextAvailable = index;
|
||||
|
||||
// make sure this page is on available linked list
|
||||
TrampolineSelectorPagePair *pagePairIterator = headPagePair;
|
||||
|
||||
// see if page is the next available page for any existing pages
|
||||
while (pagePairIterator->nextAvailablePage &&
|
||||
pagePairIterator->nextAvailablePage != pagePair)
|
||||
{
|
||||
pagePairIterator = pagePairIterator->nextAvailablePage;
|
||||
}
|
||||
|
||||
if (! pagePairIterator->nextAvailablePage) {
|
||||
// if iteration stopped because nextAvail was nil
|
||||
// add to end of list.
|
||||
pagePairIterator->nextAvailablePage = pagePair;
|
||||
pagePair->nextAvailablePage = nil;
|
||||
}
|
||||
|
||||
_unlock();
|
||||
|
||||
return YES;
|
||||
}
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* a1a2-selectortramps-arm.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if __arm__
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
.private_extern __a1a2_selectorTrampHead
|
||||
.private_extern __a1a2_firstSelectorTramp
|
||||
.private_extern __a1a2_selectorTrampEnd
|
||||
|
||||
// Trampoline machinery assumes the trampolines are Thumb function pointers
|
||||
#if !__thumb2__
|
||||
# error sorry
|
||||
#endif
|
||||
|
||||
.thumb
|
||||
.thumb_func __a1a2_selectorTrampHead
|
||||
.thumb_func __a1a2_firstSelectorTramp
|
||||
.thumb_func __a1a2_selectorTrampEnd
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__a1a2_selectorTrampHead:
|
||||
// Trampoline's data is one page before the trampoline text.
|
||||
// Also correct PC bias of 4 bytes.
|
||||
// 1. selector
|
||||
sub r12, #PAGE_MAX_SIZE
|
||||
ldr r1, [r12, #-4] // selector -> _cmd
|
||||
// 2. msgSend
|
||||
mov r12, pc
|
||||
sub r12, #PAGE_MAX_SIZE
|
||||
ldr pc, [r12, #-12] // tail call msgSend
|
||||
// not reached
|
||||
nop
|
||||
|
||||
// Align trampolines to 8 bytes
|
||||
.align 3
|
||||
|
||||
.macro TrampolineEntry
|
||||
mov r12, pc
|
||||
b __a1a2_selectorTrampHead
|
||||
.align 3
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
.endmacro
|
||||
|
||||
.private_extern __a1a2_firstSelectorTramp
|
||||
__a1a2_firstSelectorTramp:
|
||||
// 2048-3 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
.private_extern __a1a2_selectorTrampEnd
|
||||
__a1a2_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,156 @@
|
||||
/*
|
||||
* a1a2-selectortramps-arm64.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if __arm64__
|
||||
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.text
|
||||
|
||||
.private_extern __a1a2_selectorTrampHead
|
||||
.private_extern __a1a2_firstSelectorTramp
|
||||
.private_extern __a1a2_selectorTrampEnd
|
||||
|
||||
msgSend:
|
||||
.quad 0
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__a1a2_selectorTrampHead:
|
||||
L_a1a2_selectorTrampHead:
|
||||
// 1. selector
|
||||
ldr x1, [x17] // selector -> _cmd
|
||||
// 2. msgSend
|
||||
adr x17, L_a1a2_selectorTrampHead
|
||||
sub x17, x17, #PAGE_MAX_SIZE
|
||||
ldr x16, [x17]
|
||||
br x16 // tail call msgSend
|
||||
|
||||
// pad up to TrampolineSelectorPagePair header size
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
|
||||
.macro TrampolineEntry
|
||||
// load address of trampoline data (one page before this instruction)
|
||||
adr x17, -PAGE_MAX_SIZE
|
||||
b L_a1a2_selectorTrampHead
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
.endmacro
|
||||
|
||||
.align 3
|
||||
.private_extern __a1a2_firstSelectorTramp
|
||||
__a1a2_firstSelectorTramp:
|
||||
// 2048-4 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
.private_extern __a1a2_selectorTrampEnd
|
||||
__a1a2_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* a1a2-selectortramps-i386.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include "selectortramps.mac"
|
||||
|
||||
.text
|
||||
.private_extern __a1a2_selectorTrampHead
|
||||
.private_extern __a1a2_firstSelectorTramp
|
||||
.private_extern __a1a2_nextSelectorTramp
|
||||
.private_extern __a1a2_selectorTrampEnd
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a1a2_selectorTrampHead:
|
||||
// 1. selector
|
||||
popl %eax
|
||||
andl $0xFFFFFFF8, %eax
|
||||
subl $ PAGE_SIZE, %eax
|
||||
movl (%eax), %ecx // selectorPtr -> ecx
|
||||
movl %ecx, 8(%esp) // ecx -> _cmd
|
||||
// 2. msgSend
|
||||
INIT_PIC(__a1a2_selectorTrampHead)
|
||||
PRELOAD(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead)
|
||||
subl $ PAGE_SIZE, LABEL_ADDR(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead)
|
||||
END_PIC()
|
||||
jmp *0(%eax) // tail call msgSend
|
||||
|
||||
.macro TrampolineEntry
|
||||
call __a1a2_selectorTrampHead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a1a2_firstSelectorTramp:
|
||||
TrampolineEntry
|
||||
__a1a2_nextSelectorTramp: // used to calculate size of each trampoline
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
__a1a2_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* a1a2-selectortramps-x86_64.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include "selectortramps.mac"
|
||||
|
||||
.text
|
||||
.private_extern __a1a2_selectorTrampHead
|
||||
.private_extern __a1a2_firstSelectorTramp
|
||||
.private_extern __a1a2_nextSelectorTramp
|
||||
.private_extern __a1a2_selectorTrampEnd
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a1a2_selectorTrampHead:
|
||||
// 1. selector
|
||||
popq %r10
|
||||
andq $0xFFFFFFFFFFFFFFF8, %r10
|
||||
subq $ PAGE_SIZE, %r10
|
||||
movq (%r10), %rsi // selector -> _cmd
|
||||
// 2. msgSend
|
||||
INIT_PIC(__a1a2_selectorTrampHead)
|
||||
PRELOAD(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead)
|
||||
subq $ PAGE_SIZE, LABEL_ADDR(__a1a2_selectorTrampHead, __a1a2_selectorTrampHead)
|
||||
END_PIC()
|
||||
jmp *0(%r10) // tail call msgSend
|
||||
|
||||
.macro TrampolineEntry
|
||||
callq __a1a2_selectorTrampHead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a1a2_firstSelectorTramp:
|
||||
TrampolineEntry
|
||||
__a1a2_nextSelectorTramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
__a1a2_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,166 @@
|
||||
/*
|
||||
* a2a3-selectortramps-arm.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#if __arm__
|
||||
|
||||
#include <arm/arch.h>
|
||||
#include <mach/vm_param.h>
|
||||
|
||||
.syntax unified
|
||||
|
||||
.text
|
||||
|
||||
.private_extern __a2a3_selectorTrampHead
|
||||
.private_extern __a2a3_firstSelectorTramp
|
||||
.private_extern __a2a3_selectorTrampEnd
|
||||
|
||||
// Trampoline machinery assumes the trampolines are Thumb function pointers
|
||||
#if !__thumb2__
|
||||
# error sorry
|
||||
#endif
|
||||
|
||||
.thumb
|
||||
.thumb_func __a2a3_selectorTrampHead
|
||||
.thumb_func __a2a3_firstSelectorTramp
|
||||
.thumb_func __a2a3_selectorTrampEnd
|
||||
|
||||
.align PAGE_MAX_SHIFT
|
||||
__a2a3_selectorTrampHead:
|
||||
// Trampoline's data is one page before the trampoline text.
|
||||
// Also correct PC bias of 4 bytes.
|
||||
// 1. selector
|
||||
sub r12, #PAGE_MAX_SIZE
|
||||
ldr r2, [r12, #-4] // _cmd = selector
|
||||
// 2. msgSend. Can't "ldr r12, msgSend", error: out of range pc-relative fixup value
|
||||
mov r12, pc
|
||||
sub r12, #PAGE_MAX_SIZE
|
||||
ldr pc, [r12, #-12]
|
||||
// not reached
|
||||
nop
|
||||
|
||||
// Align trampolines to 8 bytes
|
||||
.align 3
|
||||
|
||||
.macro TrampolineEntry
|
||||
mov r12, pc
|
||||
b __a2a3_selectorTrampHead
|
||||
.align 3
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX16
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
.endmacro
|
||||
|
||||
.macro TrampolineEntryX256
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
.endmacro
|
||||
|
||||
.private_extern __a2a3_firstSelectorTramp
|
||||
__a2a3_firstSelectorTramp:
|
||||
// 2048-2 trampolines to fill 16K page
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
TrampolineEntryX256
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
TrampolineEntryX16
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
.private_extern __a2a3_selectorTrampEnd
|
||||
__a2a3_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,569 @@
|
||||
/*
|
||||
* a2a3-selectortramps-i386.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef __i386__
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include "selectortramps.mac"
|
||||
|
||||
.text
|
||||
.private_extern __a2a3_selectorTrampHead
|
||||
.private_extern __a2a3_firstSelectorTramp
|
||||
.private_extern __a2a3_nextSelectorTramp
|
||||
.private_extern __a2a3_selectorTrampEnd
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a2a3_selectorTrampHead:
|
||||
// 1. selector
|
||||
popl %eax
|
||||
andl $0xFFFFFFF8, %eax
|
||||
subl $ PAGE_SIZE, %eax
|
||||
movl (%eax), %ecx // selectorPtr -> ecx
|
||||
movl %ecx, 12(%esp) // ecx -> _cmd
|
||||
// 2. msgSend
|
||||
INIT_PIC(__a2a3_selectorTrampHead)
|
||||
PRELOAD(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead)
|
||||
subl $ PAGE_SIZE, LABEL_ADDR(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead)
|
||||
END_PIC()
|
||||
jmp *0(%eax) // tail call msgSend
|
||||
|
||||
.macro TrampolineEntry
|
||||
call __a2a3_selectorTrampHead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a2a3_firstSelectorTramp:
|
||||
TrampolineEntry
|
||||
__a2a3_nextSelectorTramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
__a2a3_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,568 @@
|
||||
/*
|
||||
* a2a3-selectortramps-x86_64.s
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
|
||||
#include <mach/mach.h>
|
||||
#include "selectortramps.mac"
|
||||
|
||||
.text
|
||||
.private_extern __a2a3_selectorTrampHead
|
||||
.private_extern __a2a3_firstSelectorTramp
|
||||
.private_extern __a2a3_nextSelectorTramp
|
||||
.private_extern __a2a3_selectorTrampEnd
|
||||
|
||||
.align PAGE_SHIFT
|
||||
__a2a3_selectorTrampHead:
|
||||
// 1. selector
|
||||
popq %r10
|
||||
andq $0xFFFFFFFFFFFFFFF8, %r10
|
||||
subq $ PAGE_SIZE, %r10
|
||||
movq (%r10), %rdx // selector -> _cmd
|
||||
// 2. msgSend
|
||||
INIT_PIC(__a2a3_selectorTrampHead)
|
||||
PRELOAD(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead)
|
||||
subq $ PAGE_SIZE, LABEL_ADDR(__a2a3_selectorTrampHead, __a2a3_selectorTrampHead)
|
||||
END_PIC()
|
||||
jmp *0(%r10) // tail call msgSend
|
||||
|
||||
.macro TrampolineEntry
|
||||
callq __a2a3_selectorTrampHead
|
||||
nop
|
||||
nop
|
||||
nop
|
||||
.endmacro
|
||||
|
||||
.align 5
|
||||
__a2a3_firstSelectorTramp:
|
||||
TrampolineEntry
|
||||
__a2a3_nextSelectorTramp:
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
// TrampolineEntry
|
||||
|
||||
__a2a3_selectorTrampEnd:
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* selectortramps.mac
|
||||
* OCMethodTrace
|
||||
*
|
||||
* https://github.com/omxcodec/OCMethodTrace.git
|
||||
*
|
||||
* Copyright (C) 2018 Michael Chen <omxcodec@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifdef __x86_64__
|
||||
#define PRELOAD(x,f) movq x@GOTPCREL(%rip), %r10;
|
||||
#define LABEL_ADDR(x,f) %r10
|
||||
#define LABEL_VALUE(x,f) (%r10)
|
||||
#define INIT_PIC(f)
|
||||
#define END_PIC(f)
|
||||
#elif __i386__
|
||||
#define PRELOAD(x,f) leal x-L0## f ##$pb(%ebx), %eax;
|
||||
#define LABEL_ADDR(x,f) %eax
|
||||
#define LABEL_VALUE(x,f) (%eax)
|
||||
#define INIT_PIC(x) \
|
||||
push %ebx; \
|
||||
call L0## x ##$pb; \
|
||||
L0## x ##$pb:; \
|
||||
pop %ebx;
|
||||
#define END_PIC(x) pop %ebx
|
||||
#endif
|
||||
@@ -0,0 +1,8 @@
|
||||
//
|
||||
// Prefix header for all source files of the 'dkhelperDylib' target in the 'dkhelperDylib' project
|
||||
//
|
||||
|
||||
#ifdef __OBJC__
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "/opt/theos/Prefix.pch" //path/to/theos/Prefix.pch
|
||||
#endif
|
||||
@@ -0,0 +1,25 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// dkhelperDylib.h
|
||||
// dkhelperDylib
|
||||
//
|
||||
// Created by 朱德坤 on 2019/1/23.
|
||||
// Copyright (c) 2019 DKJone. All rights reserved.
|
||||
//
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#define INSERT_SUCCESS_WELCOME " 🎉!!!congratulations!!!🎉\n👍----------------insert dylib success----------------👍\n"
|
||||
|
||||
@interface CustomViewController
|
||||
|
||||
@property (nonatomic, copy) NSString* newProperty;
|
||||
|
||||
+ (void)classMethod;
|
||||
|
||||
- (NSString*)getMyName;
|
||||
|
||||
- (void)newMethod:(NSString*) output;
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,91 @@
|
||||
// weibo: http://weibo.com/xiaoqing28
|
||||
// blog: http://www.alonemonkey.com
|
||||
//
|
||||
// dkhelperDylib.m
|
||||
// dkhelperDylib
|
||||
//
|
||||
// Created by 朱德坤 on 2019/1/23.
|
||||
// Copyright (c) 2019 DKJone. All rights reserved.
|
||||
//
|
||||
|
||||
#import "dkhelperDylib.h"
|
||||
#import <CaptainHook/CaptainHook.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <Cycript/Cycript.h>
|
||||
#import <MDCycriptManager.h>
|
||||
|
||||
CHConstructor{
|
||||
printf(INSERT_SUCCESS_WELCOME);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationDidFinishLaunchingNotification object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification * _Nonnull note) {
|
||||
|
||||
#ifndef __OPTIMIZE__
|
||||
CYListenServer(6666);
|
||||
|
||||
MDCycriptManager* manager = [MDCycriptManager sharedInstance];
|
||||
[manager loadCycript:NO];
|
||||
|
||||
NSError* error;
|
||||
NSString* result = [manager evaluateCycript:@"UIApp" error:&error];
|
||||
NSLog(@"result: %@", result);
|
||||
if(error.code != 0){
|
||||
NSLog(@"error: %@", error.localizedDescription);
|
||||
}
|
||||
#endif
|
||||
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
CHDeclareClass(CustomViewController)
|
||||
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wstrict-prototypes"
|
||||
|
||||
//add new method
|
||||
CHDeclareMethod1(void, CustomViewController, newMethod, NSString*, output){
|
||||
NSLog(@"This is a new method : %@", output);
|
||||
}
|
||||
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
CHOptimizedClassMethod0(self, void, CustomViewController, classMethod){
|
||||
NSLog(@"hook class method");
|
||||
CHSuper0(CustomViewController, classMethod);
|
||||
}
|
||||
|
||||
CHOptimizedMethod0(self, NSString*, CustomViewController, getMyName){
|
||||
//get origin value
|
||||
NSString* originName = CHSuper(0, CustomViewController, getMyName);
|
||||
|
||||
NSLog(@"origin name is:%@",originName);
|
||||
|
||||
//get property
|
||||
NSString* password = CHIvar(self,_password,__strong NSString*);
|
||||
|
||||
NSLog(@"password is %@",password);
|
||||
|
||||
[self newMethod:@"output"];
|
||||
|
||||
//set new property
|
||||
self.newProperty = @"newProperty";
|
||||
|
||||
NSLog(@"newProperty : %@", self.newProperty);
|
||||
|
||||
//change the value
|
||||
return @"朱德坤";
|
||||
|
||||
}
|
||||
|
||||
//add new property
|
||||
CHPropertyRetainNonatomic(CustomViewController, NSString*, newProperty, setNewProperty);
|
||||
|
||||
CHConstructor{
|
||||
CHLoadLateClass(CustomViewController);
|
||||
CHClassHook0(CustomViewController, getMyName);
|
||||
CHClassHook0(CustomViewController, classMethod);
|
||||
|
||||
CHHook0(CustomViewController, newProperty);
|
||||
CHHook1(CustomViewController, setNewProperty);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright (c) 2013, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name Facebook nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#import "fishhook.h"
|
||||
|
||||
#import <dlfcn.h>
|
||||
#import <stdlib.h>
|
||||
#import <string.h>
|
||||
#import <sys/types.h>
|
||||
#import <mach-o/dyld.h>
|
||||
#import <mach-o/loader.h>
|
||||
#import <mach-o/nlist.h>
|
||||
|
||||
#ifdef __LP64__
|
||||
typedef struct mach_header_64 mach_header_t;
|
||||
typedef struct segment_command_64 segment_command_t;
|
||||
typedef struct section_64 section_t;
|
||||
typedef struct nlist_64 nlist_t;
|
||||
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT_64
|
||||
#else
|
||||
typedef struct mach_header mach_header_t;
|
||||
typedef struct segment_command segment_command_t;
|
||||
typedef struct section section_t;
|
||||
typedef struct nlist nlist_t;
|
||||
#define LC_SEGMENT_ARCH_DEPENDENT LC_SEGMENT
|
||||
#endif
|
||||
|
||||
#ifndef SEG_DATA_CONST
|
||||
#define SEG_DATA_CONST "__DATA_CONST"
|
||||
#endif
|
||||
|
||||
struct rebindings_entry {
|
||||
struct rebinding *rebindings;
|
||||
size_t rebindings_nel;
|
||||
struct rebindings_entry *next;
|
||||
};
|
||||
|
||||
static struct rebindings_entry *_rebindings_head;
|
||||
|
||||
static int prepend_rebindings(struct rebindings_entry **rebindings_head,
|
||||
struct rebinding rebindings[],
|
||||
size_t nel) {
|
||||
struct rebindings_entry *new_entry = (struct rebindings_entry *) malloc(sizeof(struct rebindings_entry));
|
||||
if (!new_entry) {
|
||||
return -1;
|
||||
}
|
||||
new_entry->rebindings = (struct rebinding *) malloc(sizeof(struct rebinding) * nel);
|
||||
if (!new_entry->rebindings) {
|
||||
free(new_entry);
|
||||
return -1;
|
||||
}
|
||||
memcpy(new_entry->rebindings, rebindings, sizeof(struct rebinding) * nel);
|
||||
new_entry->rebindings_nel = nel;
|
||||
new_entry->next = *rebindings_head;
|
||||
*rebindings_head = new_entry;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void perform_rebinding_with_section(struct rebindings_entry *rebindings,
|
||||
section_t *section,
|
||||
intptr_t slide,
|
||||
nlist_t *symtab,
|
||||
char *strtab,
|
||||
uint32_t *indirect_symtab) {
|
||||
uint32_t *indirect_symbol_indices = indirect_symtab + section->reserved1;
|
||||
void **indirect_symbol_bindings = (void **)((uintptr_t)slide + section->addr);
|
||||
for (uint i = 0; i < section->size / sizeof(void *); i++) {
|
||||
uint32_t symtab_index = indirect_symbol_indices[i];
|
||||
if (symtab_index == INDIRECT_SYMBOL_ABS || symtab_index == INDIRECT_SYMBOL_LOCAL ||
|
||||
symtab_index == (INDIRECT_SYMBOL_LOCAL | INDIRECT_SYMBOL_ABS)) {
|
||||
continue;
|
||||
}
|
||||
uint32_t strtab_offset = symtab[symtab_index].n_un.n_strx;
|
||||
char *symbol_name = strtab + strtab_offset;
|
||||
if (strnlen(symbol_name, 2) < 2) {
|
||||
continue;
|
||||
}
|
||||
struct rebindings_entry *cur = rebindings;
|
||||
while (cur) {
|
||||
for (uint j = 0; j < cur->rebindings_nel; j++) {
|
||||
if (strcmp(&symbol_name[1], cur->rebindings[j].name) == 0) {
|
||||
if (cur->rebindings[j].replaced != NULL &&
|
||||
indirect_symbol_bindings[i] != cur->rebindings[j].replacement) {
|
||||
*(cur->rebindings[j].replaced) = indirect_symbol_bindings[i];
|
||||
}
|
||||
indirect_symbol_bindings[i] = cur->rebindings[j].replacement;
|
||||
goto symbol_loop;
|
||||
}
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
symbol_loop:;
|
||||
}
|
||||
}
|
||||
|
||||
static void rebind_symbols_for_image(struct rebindings_entry *rebindings,
|
||||
const struct mach_header *header,
|
||||
intptr_t slide) {
|
||||
Dl_info info;
|
||||
if (dladdr(header, &info) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
segment_command_t *cur_seg_cmd;
|
||||
segment_command_t *linkedit_segment = NULL;
|
||||
struct symtab_command* symtab_cmd = NULL;
|
||||
struct dysymtab_command* dysymtab_cmd = NULL;
|
||||
|
||||
uintptr_t cur = (uintptr_t)header + sizeof(mach_header_t);
|
||||
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
|
||||
cur_seg_cmd = (segment_command_t *)cur;
|
||||
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
|
||||
if (strcmp(cur_seg_cmd->segname, SEG_LINKEDIT) == 0) {
|
||||
linkedit_segment = cur_seg_cmd;
|
||||
}
|
||||
} else if (cur_seg_cmd->cmd == LC_SYMTAB) {
|
||||
symtab_cmd = (struct symtab_command*)cur_seg_cmd;
|
||||
} else if (cur_seg_cmd->cmd == LC_DYSYMTAB) {
|
||||
dysymtab_cmd = (struct dysymtab_command*)cur_seg_cmd;
|
||||
}
|
||||
}
|
||||
|
||||
if (!symtab_cmd || !dysymtab_cmd || !linkedit_segment ||
|
||||
!dysymtab_cmd->nindirectsyms) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find base symbol/string table addresses
|
||||
uintptr_t linkedit_base = (uintptr_t)slide + linkedit_segment->vmaddr - linkedit_segment->fileoff;
|
||||
nlist_t *symtab = (nlist_t *)(linkedit_base + symtab_cmd->symoff);
|
||||
char *strtab = (char *)(linkedit_base + symtab_cmd->stroff);
|
||||
|
||||
// Get indirect symbol table (array of uint32_t indices into symbol table)
|
||||
uint32_t *indirect_symtab = (uint32_t *)(linkedit_base + dysymtab_cmd->indirectsymoff);
|
||||
|
||||
cur = (uintptr_t)header + sizeof(mach_header_t);
|
||||
for (uint i = 0; i < header->ncmds; i++, cur += cur_seg_cmd->cmdsize) {
|
||||
cur_seg_cmd = (segment_command_t *)cur;
|
||||
if (cur_seg_cmd->cmd == LC_SEGMENT_ARCH_DEPENDENT) {
|
||||
if (strcmp(cur_seg_cmd->segname, SEG_DATA) != 0 &&
|
||||
strcmp(cur_seg_cmd->segname, SEG_DATA_CONST) != 0) {
|
||||
continue;
|
||||
}
|
||||
for (uint j = 0; j < cur_seg_cmd->nsects; j++) {
|
||||
section_t *sect =
|
||||
(section_t *)(cur + sizeof(segment_command_t)) + j;
|
||||
if ((sect->flags & SECTION_TYPE) == S_LAZY_SYMBOL_POINTERS) {
|
||||
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
|
||||
}
|
||||
if ((sect->flags & SECTION_TYPE) == S_NON_LAZY_SYMBOL_POINTERS) {
|
||||
perform_rebinding_with_section(rebindings, sect, slide, symtab, strtab, indirect_symtab);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void _rebind_symbols_for_image(const struct mach_header *header,
|
||||
intptr_t slide) {
|
||||
rebind_symbols_for_image(_rebindings_head, header, slide);
|
||||
}
|
||||
|
||||
int rebind_symbols_image(void *header,
|
||||
intptr_t slide,
|
||||
struct rebinding rebindings[],
|
||||
size_t rebindings_nel) {
|
||||
struct rebindings_entry *rebindings_head = NULL;
|
||||
int retval = prepend_rebindings(&rebindings_head, rebindings, rebindings_nel);
|
||||
rebind_symbols_for_image(rebindings_head, (const struct mach_header *) header, slide);
|
||||
free(rebindings_head);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel) {
|
||||
int retval = prepend_rebindings(&_rebindings_head, rebindings, rebindings_nel);
|
||||
if (retval < 0) {
|
||||
return retval;
|
||||
}
|
||||
// If this was the first call, register callback for image additions (which is also invoked for
|
||||
// existing images, otherwise, just run on existing images
|
||||
if (!_rebindings_head->next) {
|
||||
_dyld_register_func_for_add_image(_rebind_symbols_for_image);
|
||||
} else {
|
||||
uint32_t c = _dyld_image_count();
|
||||
for (uint32_t i = 0; i < c; i++) {
|
||||
_rebind_symbols_for_image(_dyld_get_image_header(i), _dyld_get_image_vmaddr_slide(i));
|
||||
}
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright (c) 2013, Facebook, Inc.
|
||||
// All rights reserved.
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions are met:
|
||||
// * Redistributions of source code must retain the above copyright notice,
|
||||
// this list of conditions and the following disclaimer.
|
||||
// * Redistributions in binary form must reproduce the above copyright notice,
|
||||
// this list of conditions and the following disclaimer in the documentation
|
||||
// and/or other materials provided with the distribution.
|
||||
// * Neither the name Facebook nor the names of its contributors may be used to
|
||||
// endorse or promote products derived from this software without specific
|
||||
// prior written permission.
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
#ifndef fishhook_h
|
||||
#define fishhook_h
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#if !defined(FISHHOOK_EXPORT)
|
||||
#define FISHHOOK_VISIBILITY __attribute__((visibility("hidden")))
|
||||
#else
|
||||
#define FISHHOOK_VISIBILITY __attribute__((visibility("default")))
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif //__cplusplus
|
||||
|
||||
/*
|
||||
* A structure representing a particular intended rebinding from a symbol
|
||||
* name to its replacement
|
||||
*/
|
||||
struct rebinding {
|
||||
const char *name;
|
||||
void *replacement;
|
||||
void **replaced;
|
||||
};
|
||||
|
||||
/*
|
||||
* For each rebinding in rebindings, rebinds references to external, indirect
|
||||
* symbols with the specified name to instead point at replacement for each
|
||||
* image in the calling process as well as for all future images that are loaded
|
||||
* by the process. If rebind_functions is called more than once, the symbols to
|
||||
* rebind are added to the existing list of rebindings, and if a given symbol
|
||||
* is rebound more than once, the later rebinding will take precedence.
|
||||
*/
|
||||
FISHHOOK_VISIBILITY
|
||||
int rebind_symbols(struct rebinding rebindings[], size_t rebindings_nel);
|
||||
|
||||
/*
|
||||
* Rebinds as above, but only in the specified image. The header should point
|
||||
* to the mach-o header, the slide should be the slide offset. Others as above.
|
||||
*/
|
||||
FISHHOOK_VISIBILITY
|
||||
int rebind_symbols_image(void *header,
|
||||
intptr_t slide,
|
||||
struct rebinding rebindings[],
|
||||
size_t rebindings_nel);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif //__cplusplus
|
||||
|
||||
#endif //fishhook_h
|
||||
|
||||
Reference in New Issue
Block a user