跟Java一样,iOS也有try catch,抓取某些代码的异常,防止某些crash
Mach是Mac OS和iOS操作系统的微内核核心,Mach异常是指最底层的内核级异常 。每个thread,task都有一个异常端口数组,Mach的部分API暴露给了开发者,开发者可以直接通过Mach API设置thread,task,host的异常端口,来监听捕获Mach异常,抓取Crash事件。所以当APP中产生异常时,最先能监听到异常的就是Mach
iOS的异常也称为Mach异常
比如数组越界异常NSRangeException([__NSArrayI objectAtIndexedSubscript:]: index 3 beyond bounds [0 .. 1]'),
可变字典中插入空对象异常NSInvalidArgumentException([__NSDictionaryM setObject:forKey:]: object cannot be nil (key: aa))等等,
像这样的NSException是可以被try catch住的,在iOS中除了使用try catch以外,最典型的是使用UncaughtExceptionHandler机制捕获,
具体调用如下
void HandleException(NSException *exception){
}
这个可以捕获整个进程的异常,不需要像try catch那样分批捕获,这也是很多崩溃统计sdk的原理
当前iOS中很多crash是不能被try catch,比如内存原因产生的crash,最典型的是使用一个已经释放的内存,会产生EXC_BAD_ACCESS的错误,并且发出产生SIGSEGV(信号)这样的信号,当这样crash是可以用另一种方式捕获的,那就是signal机制,它来捕获Crash发生时的错误内容,具体用法
signal(SIGSEGV, SignalHandler);
void SignalHandler(int signal){
//处理异常信号
}
注册对SIGSEGV监听,当SIGSEGV信号产生时,触发SignalHandler函数的调用
说明:上面也说过,在捕获Crash事件时,如果mach可以处理crash,NSExcetion导致的crash,优选Mach异常。因为Mach异常处理会先于Unix信号处理发生,如果Mach异常的handler让程序exit了,那么Unix信号就永远不会到达这个进程了,如果mach不处理的话,那么NSExcetion会转化为相应的信号SIGABRT,传递出去产生crash,
iOS开发者常见的 UNIX 信号 如下:
UNIX 信号 | 注释 |
---|---|
SIGSEGV | 访问无效的内存地址。地址存在,但是应用程序无法访问。 |
SIGABRT | 程序崩溃。由 C函数 abort() 初始化。通常意味着系统检测到某些事务出错,例如 assert() 或者 NSAssert() 校验失败。 |
SIGBUS | 访问无效的内存地址。地址不存在,或对齐无效。(The address does not exist, or the alignment is invalid.) |
SIGTRAP | 调试器相关 |
SIGILL | 尝试执行非法的、有缺陷、未知的或者需要权限的指令。 |
SIGKILL | 程序结束接收中止信号。 |
下面举例说明常见的一种crash,以下场景均未对异常和信号进行捕获
- 数组越界
NSArray *testArray = @[@"11",@"22"];
NSString *value = testArray[3];
异常日志如下
Exception Type: EXC_CRASH (SIGABRT)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Triggered by Thread: 0
Last Exception Backtrace:
0 CoreFoundation 0x18eb6ea48 __exceptionPreprocess + 220
1 libobjc.A.dylib 0x18e895fa4 objc_exception_throw + 55
2 CoreFoundation 0x18ebc4360 _CFThrowFormattedException + 111
3 CoreFoundation 0x18ea652a0 -[__NSArrayI objectAtIndexedSubscript:] + 127
4 DSCrashDemo 0x10256c4b8 -[ExceptionViewController testArrayOutOfIndex] + 50360 (ExceptionViewController.m:54)
说明
Exception Type:表示异常类型以及信号值,这里EXC_CRASH一般就是NSExcetion导致的crash,SIGABRT
Exception Codes: 异常码,暂时不知具体的值表示什么含义
Triggered by Thread: 发生crash的线程
- 访问已经释放的内存crash
MRC下
init_safe_free的实现见最底部
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//AppDelegate里开启了init_safe_free(),表示释放的内存不会被覆盖,所以tempView调用setNeedsDisplay肯定会崩溃
init_safe_free();
}
MyMrcView.m
- (instancetype)init{
UILabel *tempView = [UILabel new];
[tempView release];
NSLog(@"text=%@",tempView.text);
}
异常日志如下
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000555555560
VM Region Info: 0x555555560 is not in any region. Bytes after previous region: 11632203105
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
MALLOC_NANO 0000000280000000-00000002a0000000 [512.0M] rw-/rwx SM=PRV
--->
UNUSED SPACE AT END
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [1817]
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libobjc.A.dylib 0x000000018e892090 objc_msgSend + 16
1 DSCrashDemo 0x00000001007d6400 -[MyMrcView init] + 25600 (MyMrcView.m:30)
2 DSCrashDemo 0x00000001007d8cf0 -[MRCOneViewController crashSignalEGVClick] + 36080 (MRCOneViewController.m:37)
3 UIKitCore 0x0000000192bf59ac -[UIApplication sendAction:to:from:forEvent:] + 96
说明
Exception Type:表示异常类型以及信号值,这里EXC_BAD_ACCESS表示内存访问错误,SIGSEG表示内存段异常信号
Exception Subtype: 表示不合法内存地址
Termination Signal:同SIGSEG
Terminating Process: 进程id
Triggered by Thread: 发生crash的线程
如果将代码改为下面代码
AppDelegate.m
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//AppDelegate里开启了init_safe_free(),表示释放的内存不会被覆盖,所以tempView调用setNeedsDisplay肯定会崩溃
//init_safe_free();
}
MyMrcView.m
- (instancetype)init{
UILabel *tempView = [UILabel new];
NSLog(@"tempView=%@",tempView);
[tempView release];
//AppDelegate里开启了init_safe_free(),表示释放的内存不会被覆盖,所以tempView调用setNeedsDisplay肯定会崩溃
//如果不开启init_safe_free(),下面创建了10个UIView,那块被释放的内存可能会被覆盖,所以tempView调用setNeedsDisplay不一定会崩溃
for (int i = 0; i < 5; i ++) {
UILabel *temp = [[[UILabel alloc]init] autorelease];
NSLog(@"temp=%@",temp);
temp.text = [NSString stringWithFormat:@"UILabel_%d",i];
[self addSubview:temp];
}
NSLog(@"text=%@",tempView.text);
}
测试了几次,其中存在crash的,也存在没crash
没crash的日志如下,一目了然
2020-04-22 21:42:13.999612+0800 DSCrashDemo[1910:563205] tempView=<UILabel: 0x103400480; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282380550>>
2020-04-22 21:42:14.000465+0800 DSCrashDemo[1910:563205] temp=<UILabel: 0x103400480; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x2823805f0>>
2020-04-22 21:42:14.001157+0800 DSCrashDemo[1910:563205] temp=<UILabel: 0x103403b50; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282380690>>
2020-04-22 21:42:14.001728+0800 DSCrashDemo[1910:563205] temp=<UILabel: 0x103403dc0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282380780>>
2020-04-22 21:42:14.002311+0800 DSCrashDemo[1910:563205] temp=<UILabel: 0x103404030; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282380870>>
2020-04-22 21:42:14.002865+0800 DSCrashDemo[1910:563205] temp=<UILabel: 0x1034042a0; frame = (0 0; 0 0); userInteractionEnabled = NO; layer = <_UILabelLayer: 0x282380960>>
2020-04-22 21:42:14.014872+0800 DSCrashDemo[1910:563205] text=UILabel_0
- 中止进程信号
代码如下,模拟主线程阻塞,然后杀死进程,有点像Android的ANR,因为iOS如果主线程阻塞的话不会ANR,所以当成crash处理
82 - (void)bugThreeButtonClick{
83 while (1) {
84 }
异常日志如下
Exception Type: EXC_CRASH (SIGKILL)
Exception Codes: 0x0000000000000000, 0x0000000000000000
Exception Note: EXC_CORPSE_NOTIFY
Termination Reason: Namespace SPRINGBOARD, Code 0x8badf00d
Termination Description: SPRINGBOARD, process-exit watchdog transgression: application<dasheng.DSCrashDemo.aa>:2094 exhausted real (wall clock) time allowance of 5.00 seconds | ProcessVisibility: Foreground | ProcessState: Running | WatchdogEvent: process-exit | WatchdogVisibility: Foreground | WatchdogCPUStatistics: ( | "Elapsed total CPU time (seconds): 7.390 (user 7.390, system 0.000), 25% CPU", | "Elapsed application CPU time (seconds): 4.988, 17% CPU" | )
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 DSCrashDemo 0x000000010072088c -[DeadLockViewController bugThreeButtonClick] + 34956 (DeadLockViewController.m:83)
1 UIKitCore 0x0000000192bf59ac -[UIApplication sendAction:to:from:forEvent:] + 96
2 UIKitCore 0x000000019262bfbc -[UIControl sendAction:to:forEvent:] + 240
3 UIKitCore 0x000000019262c320 -[UIControl _sendActionsForEvents:withEvent:] + 408
4 UIKitCore 0x000000019262b33c -[UIControl touchesEnded:withEvent:] + 520
5 UIKitCore 0x00000001927dca58 _UIGestureEnvironmentUpdate + 7636
- 主线程死锁
44 - (void)bugButtonClick{
45 dispatch_sync(dispatch_get_main_queue(), ^{
46 NSLog(@"任务2 - %@", [NSThread currentThread]); // 任务2
47 });
}
异常日志如下
xception Type: EXC_BREAKPOINT (SIGTRAP)
Exception Codes: 0x0000000000000001, 0x000000018e81f0e4
Termination Signal: Trace/BPT trap: 5
Termination Reason: Namespace SIGNAL, Code 0x5
Terminating Process: exc handler [2145]
Triggered by Thread: 0
Application Specific Information:
BUG IN CLIENT OF LIBDISPATCH: dispatch_sync called on queue already owned by current thread
Abort Cause 9005618706777092
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 libdispatch.dylib 0x000000018e81f0e4 __DISPATCH_WAIT_FOR_QUEUE__ + 448
1 libdispatch.dylib 0x000000018e81ec74 _dispatch_sync_f_slow + 140
2 libdispatch.dylib 0x000000018e81ec74 _dispatch_sync_f_slow + 140
3 DSCrashDemo 0x00000001001bc3bc -[DeadLockViewController bugButtonClick] + 33724 (DeadLockViewController.m:45)
4 UIKitCore 0x0000000192bf59ac -[UIApplication sendAction:to:from:forEvent:] + 96
其中与“EXC_CRASH”非常相似,EXC_BREAKPOINT 也往往与调试器一起发挥作用,在测试阶段被捕获
- 非法内存
代码如下
- (void)crashSignalEGVClick{
//EXC_BAD_ACCESS(code=1,address=0x1111):表示0x00001111此内存并不合法,SIGSEGV类型崩溃
int *pi = (int*)0x00001111;
*pi = 17;
}
异常日志
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Subtype: KERN_INVALID_ADDRESS at 0x0000000000001111
VM Region Info: 0x1111 is not in any region. Bytes before following region: 4365446895
REGION TYPE START - END [ VSIZE] PRT/MAX SHRMOD REGION DETAIL
UNUSED SPACE AT START
--->
__TEXT 0000000104338000-0000000104348000 [ 64K] r-x/r-x SM=COW ...p/DSCrashDemo
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [2051]
Triggered by Thread: 0
Thread 0 name: Dispatch queue: com.apple.main-thread
Thread 0 Crashed:
0 DSCrashDemo 0x000000010433ef28 -[SEGVViewController crashSignalEGVClick] + 28456 (SEGVViewController.m:36)
1 UIKitCore 0x0000000192bf59ac -[UIApplication sendAction:to:from:forEvent:] + 96
说明跟上面的访问已经释放的内存crash一样
- 内存地址未对齐,修改一个保护的地址
- (void)crashSignalBUSClick{
//SIGBUS,内存地址未对齐
//EXC_BAD_ACCESS(code=1,address=0x1000dba58)
//常量字符串不能被修改
char *s = "hello";
*ss = 'a';
}
异常日志
Exception Type: EXC_BAD_ACCESS (SIGBUS)
Exception Codes: KERN_PROTECTION_FAILURE at 0x000000010b2638b1
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Bus error: 10
Termination Reason: Namespace SIGNAL, Code 0xa
Terminating Process: exc handler [67982]
VM Regions Near 0x10b2638b1:
--> __TEXT 000000010b259000-000000010b266000 [ 52K] r-x/r-x SM=COW /Users/USER/Library/Developer/CoreSimulator/Devices/9D2C4E72-3C0E-45C7-B00C-77E0F2882038/data/Containers/Bundle/Application/081D3E2E-0AE4-4796-815D-AA443B8534DE/DSCrashDemo.app/DSCrashDemo
__DATA 000000010b266000-000000010b26a000 [ 16K] rw-/rw- SM=COW /Users/USER/Library/Developer/CoreSimulator/Devices/9D2C4E72-3C0E-45C7-B00C-77E0F2882038/data/Containers/Bundle/Application/081D3E2E-0AE4-4796-815D-AA443B8534DE/DSCrashDemo.app/DSCrashDemo
Application Specific Information:
CoreSimulator 704.12.1 - Device: iPhone 11 Pro Max (9D2C4E72-3C0E-45C7-B00C-77E0F2882038) - Runtime: iOS 13.4 (17E8260) - DeviceType: iPhone 11 Pro Max
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 dasheng.DSCrashDemo.aa 0x000000010b262d5f -[BusViewController crashSignalBUSClick] + 31 (BusViewController.m:33)
1 com.apple.UIKitCore 0x00007fff48bc9235 -[UIApplication sendAction:to:from:forEvent:] + 83
说明字符串常量”hello”出现在一个表达式中时,表达式使用的值就是这些字符所存储的地址,而不是这些字符本身。这个常量对应的地址是无不可写的,是被保护的,想想看,如果ss = 'a'生效,那么该地址存储的值改变了,那么常量是不是也被改变了,不合法啊
- 内存错误,将一个对象类型的属性声明为assign
说明一个属性是assign时,不管是ARC还是MRC,当给这个属性对象赋值时,引用计数都不会+1,可以了理解为weak
代码如下
@interface ExceptionViewController ()
@property(nonatomic,assign)NSObject *testObj;
@end
- (void)viewDidLoad {
[super viewDidLoad];
UIButton *btn3 = [UIButton new];
btn3.frame = CGRectMake(100,320,100,100);
[btn3 setTitle:@"ARC Assgin" forState:UIControlStateNormal];
[btn3 addTarget:self action:@selector(testArcAssgin) forControlEvents:UIControlEventTouchUpInside];
[self.view addSubview:btn3];
47 NSObject *Object = [NSObject new];
48 self.testObj = Object;
49 }
.....
64 -(void)testArcAssgin{
65 NSLog(@"testArcAssgin testObj=%p",self.testObj);
66 }
异常日志:
Exception Type: EXC_BAD_ACCESS (SIGSEGV)
Exception Codes: EXC_I386_GPFLT
Exception Note: EXC_CORPSE_NOTIFY
Termination Signal: Segmentation fault: 11
Termination Reason: Namespace SIGNAL, Code 0xb
Terminating Process: exc handler [63007]
Application Specific Information:
CoreSimulator 704.12.1 - Device: iPhone 11 Pro Max (9D2C4E72-3C0E-45C7-B00C-77E0F2882038) - Runtime: iOS 13.4 (17E8260) - DeviceType: iPhone 11 Pro Max
Thread 0 Crashed:: Dispatch queue: com.apple.main-thread
0 libobjc.A.dylib 0x00007fff50aedc6a objc_retain + 10
1 dasheng.DSCrashDemo.aa 0x000000010bca734c -[ExceptionViewController testArcAssgin] + 44 (ExceptionViewController.m:65)
2 com.apple.UIKitCore 0x00007fff48bc9235 -[UIApplication sendAction:to:from:forEvent:] + 83
3 com.apple.UIKitCore 0x00007fff4857a9d3 -[UIControl sendAction:to:forEvent:] + 223
4 com.apple.UIKitCore 0x00007fff4857ad1b -[UIControl _sendActionsForEvents:withEvent:] + 396
5 com.apple.UIKitCore 0x00007fff48579c8c -[UIControl touchesEnded:withEvent:] + 497
说明:因为testObj是assign的属性,所以当48行代码,Object给testObj赋值时,Object的对应的引用计数并不增加,依然是1
,当viewDidLoad结束调用,函数出栈,释放局部变量Object,其引用计数-1,变成0了,所以当点击触发testArcAssgin函数时self.testObj的引用计数也为0了,导致crash
- 内存错误,将一个分类的对象类型的属性在getter和setter的时候设为OBJC_ASSOCIATION_ASSIGN
UIView+TestName.h
@class RootViewController;
@interface UIView (TestName)
@property (nonatomic, strong) RootViewController *vc;
@end
UIView+TestName.m
-(void)setVc:(RootViewController *)vc
{
objc_setAssociatedObject(self, _cmd, vc, OBJC_ASSOCIATION_ASSIGN);
}
-(RootViewController *)vc
{
return objc_getAssociatedObject(self, @selector(setVc:));
}
异常日志:同8,这里就不细说了
init_safe_free()具体实现
DSSafeFree.m
#import "DSSafeFree.h"
#import "fishhook.h"
#import <malloc/malloc.h>
#import "TYLQueue.h"
#import <libkern/OSAtomic.h>
long unfreeSize = 0;
TYLQueue *_unfreeQueue = NULL;
OSSpinLock spinLock = OS_SPINLOCK_INIT;
#define MAX_SAVE_MEM_SIZE 1024*1024*100 //最多存这么多值,大于这个值就释放一部分
#define MAX_SAVE_MEM_NUM 1024*1024*10 //最多保留这么多指针,再多就释放一部分
#define PER_FREE_NUM 100 //每次释放指针数量
static int (*orig_free)(const void *);
void free_some_mem(size_t freeNum){
size_t count = tyl_queue_length(_unfreeQueue);
freeNum = freeNum > count ? count : freeNum;
for (int i = 0; i < freeNum; i++) {
void *unfreePoint = tyl_queue_dequeue(_unfreeQueue);
size_t memSize = malloc_size(unfreePoint);
OSSpinLockLock(&spinLock);
unfreeSize -= memSize;
OSSpinLockUnlock(&spinLock);
orig_free(unfreePoint);
}
}
void safe_free(void* p){
#if 0
size_t memSize = malloc_size(p);
memset(p, 0x55, memSize);
orig_free(p);
#else
int unfreeCount = tyl_queue_length(_unfreeQueue);
if (unfreeCount > MAX_SAVE_MEM_NUM * 0.9 || unfreeSize > MAX_SAVE_MEM_SIZE) {
free_some_mem(PER_FREE_NUM);
}
size_t memSize = malloc_size(p);
memset(p, 0x55, memSize);
OSSpinLockLock(&spinLock);
unfreeSize += memSize;
OSSpinLockUnlock(&spinLock);
tyl_queue_enqueue(_unfreeQueue, p);
#endif
return;
}
bool init_safe_free(){
_unfreeQueue = tyl_queue_create_capacity(MAX_SAVE_MEM_NUM);
rebind_symbols((struct rebinding[1]){{"free", safe_free,(void *)&orig_free},},1);
return true;
}
@implementation DSSafeFree
@end
TYLQueue.c
// TYLQueue.c
#include "TYLQueue.h"
static void tyl_queue_realloc(TYLQueue *q);
int tyl_queue_length(TYLQueue *q){
return q->_size;
}
TYLQueue *tyl_queue_create(int priority){
TYLQueue *q = (TYLQueue *)malloc(sizeof(TYLQueue));
q->_priority = priority;
q->_size = 0;
q->_capacity = 11;//default initial value
q->_node = (void **)malloc(sizeof(void*)*q->_capacity);
return q;
}
TYLQueue *tyl_queue_create_capacity(long capacity){
TYLQueue *q = (TYLQueue *)malloc(sizeof(TYLQueue));
q->_priority = TYL_PRIORITY_DEFAULT;
q->_size = 0;
if (capacity > 0) {
q->_capacity = capacity;
}else{
q->_capacity = 11;//default initial value
}
q->_node = (void **)malloc(sizeof(void*)*q->_capacity);
return q;
}
void tyl_queue_free(TYLQueue *q){
int i;
if (q) {
for (i = 0; i < q->_size; i ++) {
free(q->_node[i]);
}
free(q->_node);
free(q);
}
}
void tyl_queue_enqueue(TYLQueue *q,void *p){
q->_node[q->_size] = p;
q->_size++;
if (q->_size >= q->_capacity) {
tyl_queue_realloc(q);
}
}
void *tyl_queue_dequeue(TYLQueue *q){
void *p = NULL;
if (q->_size > 0) {
p = q->_node[0];
}
return p;
}
#pragma mark - internal method
static void tyl_queue_realloc(TYLQueue *q){
q->_capacity = q->_capacity*2;
q->_node = realloc(q->_node, sizeof(void *) * q->_capacity);
}
fishhook.c
#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 = malloc(sizeof(struct rebindings_entry));
if (!new_entry) {
return -1;
}
new_entry->rebindings = 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 bool str_longer(const char *symbol, size_t len) {
for (size_t i = 0; i <= len; i++) {
if (symbol[i] == 0) {
return false;
}
}
return true;
}
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;
struct rebindings_entry *cur = rebindings;
while (cur) {
for (uint j = 0; j < cur->rebindings_nel; j++) {
if (str_longer(symbol_name, 1) &&
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, 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;
}
网友评论