2013-10-18 28 views
5

Celowo powoduję EXC_BAD_ACCESS. Wywołując zapis do NSObject na stronie pamięci wirtualnej tylko do odczytu. Idealnie, chciałbym złapać EXC_BAD_ACCESS, oznaczyć stronę pamięci wirtualnej jako odczyt-zapis i kontynuować wykonanie tak jak zwykle. Czy to możliwe? Kod, który napisałem, powoduje, że EXC_BAD_ACCESS jest poniżej.Jak mogę odzyskać od 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); 
} 

Odpowiedz

2

Znalazłem darwin-dev post, który ma odpowiedź!

UWAGA

Odpowiedź ta ma ogromny potencjał spadkowy. Mój debugger nie działałby w żadnym wątku innym niż wątek wyjątku mach. Umieszczenie punktu przerwania w dowolnym innym wątku spowodowało zawieszenie Xcode5. Musiałem to wymusić. Wewnątrz mojej funkcji catch_exception_raise wszystko działało dobrze. I asked the LLDB folks about this.

END UWAGA

Ten kod jest szkielet odpowiedź. Będzie to nieskończona pętla, ponieważ (zgodnie z follow-up) musisz zrobić coś, aby błąd można było odzyskać. W moim przypadku muszę oznaczyć stronę jako odczyt-zapis.

#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; 
} 

To jest mój nowy kod, który działa:

WeakTargetObject.h (ARC)

@interface WeakTargetObject : NSObject 
@property (nonatomic, weak) NSObject *target; 
@end 

WeakTargetObject.m (ARC)

@implementation WeakTargetObject 
@end 

mai n.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); 
}