我故意造成EXC_BAD_ACCESS
。通过触发对只读虚拟内存页面中的NSObject
的写操作。理想情况下,我想捕获EXC_BAD_ACCESS
,将虚拟内存页标记为可读写,并像往常一样继续执行。这有可能吗?我编写的导致EXC_BAD_ACCESS
的代码如下。
WeakTargetObject.h(ARC)
@interface WeakTargetObject : NSObject
@property (nonatomic, weak) NSObject *target;
@end
WeakTargetObject.m(ARC)
@implementation WeakTargetObject
@end
main.m(MRR)
- (void)main {
char *mem = NULL;
vm_allocate(mach_task_self(), (vm_address_t *)&mem, vm_page_size, VM_FLAGS_ANYWHERE);
NSLog(@"mem: %p", mem);
WeakTargetObject *weakTargetObject = objc_constructInstance([WeakTargetObject class], (void *)mem);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *target = [[NSObject alloc] init];
weakTargetObject.target = target;
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
NSLog(@"expect non-nil. weakTargetObject.target: %@", weakTargetObject.target);
[pool drain];
vm_protect(mach_task_self(),
(vm_address_t)mem,
vm_page_size,
1,
VM_PROT_READ);
// triggers EXC_BAD_ACCESS when objc runtime
// tries to nil weakTargetObject.target
[weakTargetObject release];
NSLog(@"expect nil. weakTargetObject.target: %@", weakTargetObject.target);
}
最佳答案
我找到了有答案的darwin-dev post!
警告
这个答案有一个很大的缺点。除mach异常线程外,我的调试器无法在任何线程中工作。在其他任何线程中放置断点会导致Xcode5挂起。我不得不强行退出。在我的catch_exception_raise
函数中,它运行良好。 I asked the LLDB folks about this.
END警告
此代码是答案的框架。它将无限循环,因为(根据follow-up)您需要做一些事情以使错误可恢复。就我而言,我需要将该页面标记为可读写。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include <pthread.h>
#include <assert.h>
#include <mach/mach.h>
kern_return_t
catch_exception_raise(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code_vector,
mach_msg_type_number_t code_count)
{
fprintf(stderr, "catch_exception_raise %d\n", exception);
return KERN_SUCCESS; // loops infinitely...
}
void *exception_handler(void *arg)
{
extern boolean_t exc_server();
mach_port_t port = (mach_port_t) arg;
mach_msg_server(exc_server, 2048, port, 0);
abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns)
}
void setup_mach_exception_port()
{
static mach_port_t exception_port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
pthread_t returned_thread;
pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port);
}
void test_crash()
{
id *obj = NULL;
*obj = @"foo";
}
int main(int argc, char** argv)
{
setup_mach_exception_port();
test_crash();
return 0;
}
这是我的新代码,可以正常工作:
WeakTargetObject.h(ARC)
@interface WeakTargetObject : NSObject
@property (nonatomic, weak) NSObject *target;
@end
WeakTargetObject.m(ARC)
@implementation WeakTargetObject
@end
main.m(MRR)
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <stdarg.h>
#include <pthread.h>
#include <assert.h>
#include <mach/mach.h>
static char * mem = NULL;
kern_return_t
catch_exception_raise(mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
exception_data_t code_vector,
mach_msg_type_number_t code_count)
{
fprintf(stderr, "catch_exception_raise %d, mem: %p\n", exception, mem);
kern_return_t success = vm_protect(mach_task_self(),
(vm_address_t)mem,
vm_page_size,
0,
VM_PROT_DEFAULT);
fprintf(stderr, "switched to read-write: %d\n", success);
return KERN_SUCCESS;
}
void *exception_handler(void *arg)
{
extern boolean_t exc_server();
mach_port_t port = (mach_port_t) arg;
mach_msg_server(exc_server, 2048, port, 0);
abort(); // without this GCC complains (it doesn't know that mach_msg_server never returns)
}
void setup_mach_exception_port()
{
static mach_port_t exception_port = MACH_PORT_NULL;
mach_port_allocate(mach_task_self(), MACH_PORT_RIGHT_RECEIVE, &exception_port);
mach_port_insert_right(mach_task_self(), exception_port, exception_port, MACH_MSG_TYPE_MAKE_SEND);
task_set_exception_ports(mach_task_self(), EXC_MASK_BAD_ACCESS, exception_port, EXCEPTION_DEFAULT, MACHINE_THREAD_STATE);
pthread_t returned_thread;
pthread_create(&returned_thread, NULL, exception_handler, (void*) exception_port);
}
- (void)main {
setup_mach_exception_port();
vm_allocate(mach_task_self(), (vm_address_t *)&mem, vm_page_size, VM_FLAGS_ANYWHERE);
NSLog(@"mem: %p", mem);
WeakTargetObject *weakTargetObject = objc_constructInstance([WeakTargetObject class], (void *)mem);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSObject *target = [[NSObject alloc] init];
weakTargetObject.target = target;
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
NSLog(@"expect non-nil. weakTargetObject.target: %@", weakTargetObject.target);
[pool drain];
vm_protect(mach_task_self(),
(vm_address_t)mem,
vm_page_size,
// zero means don't set VM_PROT_READ as the maximum protection
// one means DO set VM_PROT_READ as the maximum protection
// we want zero because the if VM_PROT_READ is the maximum protection
// we won't be able to set it to VM_PROT_DEFAULT later
0,
VM_PROT_READ);
// triggers EXC_BAD_ACCESS when objc runtime
// tries to nil weakTargetObject.target
[weakTargetObject release];
NSLog(@"expect nil. weakTargetObject.target: %@", weakTargetObject.target);
}