250 строки
10 KiB
Objective-C
250 строки
10 KiB
Objective-C
/*
|
|
* Author: Landon Fuller <landonf@bikemonkey.org>
|
|
*
|
|
* Copyright (c) 2012-2013 Plausible Labs Cooperative, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person
|
|
* obtaining a copy of this software and associated documentation
|
|
* files (the "Software"), to deal in the Software without
|
|
* restriction, including without limitation the rights to use,
|
|
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the
|
|
* Software is furnished to do so, subject to the following
|
|
* conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be
|
|
* included in all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
|
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
|
* OTHER DEALINGS IN THE SOFTWARE.
|
|
*/
|
|
|
|
#import "PLCrashCompatConstants.h"
|
|
#import "PLCrashMachExceptionPort.h"
|
|
#import "PLCrashReporterNSError.h"
|
|
|
|
#if PLCRASH_FEATURE_MACH_EXCEPTIONS
|
|
|
|
|
|
/**
|
|
* @internal
|
|
*
|
|
* This class manages a reference to a Mach exception server port, and the associated
|
|
* mask, behavior, and thread state flavor expected by the given Mach exception server.
|
|
*/
|
|
@implementation PLCrashMachExceptionPort {
|
|
|
|
/** Exception mask. */
|
|
exception_mask_t _mask;
|
|
|
|
/** Exception server port. */
|
|
mach_port_t _port;
|
|
|
|
/** Exception behavior. */
|
|
exception_behavior_t _behavior;
|
|
|
|
/** Exception thread flavor. */
|
|
thread_state_flavor_t _flavor;
|
|
}
|
|
|
|
@synthesize server_port = _port;
|
|
@synthesize mask = _mask;
|
|
@synthesize behavior = _behavior;
|
|
@synthesize flavor = _flavor;
|
|
|
|
/**
|
|
* Return the current PLCrashMachExceptionPortState set registered for a @a task and @a mask.
|
|
*
|
|
* @param task The task for which exception port state will be retrieved.
|
|
* @param mask The exception mask for which exception port state will be retrieved.
|
|
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
|
|
* will contain an error object indicating why the Mach exception port state could not be fetched. If no error
|
|
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
|
|
* will be provided.
|
|
*
|
|
* @return Returns the set of registered port states on success, or nil on failure.
|
|
*/
|
|
+ (PLCrashMachExceptionPortSet *) exceptionPortsForTask: (task_t) task mask: (exception_mask_t) mask error: (NSError **) outError {
|
|
plcrash_mach_exception_port_set_t states;
|
|
|
|
kern_return_t kr;
|
|
|
|
/* Fetch the current ports */
|
|
kr = task_get_exception_ports(task,
|
|
mask,
|
|
states.masks,
|
|
&states.count,
|
|
states.ports,
|
|
states.behaviors,
|
|
states.flavors);
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
plcrash_populate_mach_error(outError, kr, @"Failed to fetch mach exception ports");
|
|
return nil;
|
|
}
|
|
|
|
return [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: states];
|
|
}
|
|
|
|
/**
|
|
* Return the current PLCrashMachExceptionPortState set registered for a @a thread and @a mask.
|
|
*
|
|
* @param thread The thread for which exception port state will be retrieved.
|
|
* @param mask The exception mask for which exception port state will be retrieved.
|
|
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
|
|
* will contain an error object indicating why the Mach exception port state could not be fetched. If no error
|
|
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
|
|
* will be provided.
|
|
*
|
|
* @return Returns the set of registered port states on success, or nil on failure.
|
|
*/
|
|
+ (PLCrashMachExceptionPortSet *) exceptionPortsForThread: (thread_t) thread mask: (exception_mask_t) mask error: (NSError **) outError {
|
|
plcrash_mach_exception_port_set_t states;
|
|
|
|
kern_return_t kr;
|
|
|
|
/* Fetch the current ports */
|
|
kr = thread_get_exception_ports(thread,
|
|
mask,
|
|
states.masks,
|
|
&states.count,
|
|
states.ports,
|
|
states.behaviors,
|
|
states.flavors);
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
plcrash_populate_mach_error(outError, kr, @"Failed to swap mach exception ports");
|
|
return nil;
|
|
}
|
|
|
|
return [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: states];
|
|
}
|
|
|
|
/**
|
|
* Initialize a new instance.
|
|
*
|
|
* @param port The Mach exception server's port, for which a MACH_PORT_RIGHT_SEND will be retained (and released on dealloc). MACH_PORT_NULL may be specified.
|
|
* @param mask The exception masks for which @a port should be (or was) registered.
|
|
* @param behavior The exception behavior expected by the server on @a port.
|
|
* @param flavor The thread flavor expected by the server on @a port.
|
|
*/
|
|
- (instancetype) initWithServerPort: (mach_port_t) port
|
|
mask: (exception_mask_t) mask
|
|
behavior: (exception_behavior_t) behavior
|
|
flavor: (thread_state_flavor_t) flavor
|
|
{
|
|
kern_return_t kt;
|
|
|
|
if ((self = [super init]) == nil)
|
|
return nil;
|
|
|
|
_port = port;
|
|
_mask = mask;
|
|
_behavior = behavior;
|
|
_flavor = flavor;
|
|
|
|
/* Retain the port if it's not MACH_PORT_(NULL|INVALID) */
|
|
if (MACH_PORT_VALID(_port) && (kt = mach_port_mod_refs(mach_task_self(), _port, MACH_PORT_RIGHT_SEND, 1)) != KERN_SUCCESS) {
|
|
PLCR_LOG("Unexpected error incrementing mach port reference: %d", kt);
|
|
}
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc {
|
|
kern_return_t kt;
|
|
|
|
/* Release the port if it's not MACH_PORT_(NULL|INVALID) */
|
|
if (MACH_PORT_VALID(_port) && (kt = mach_port_mod_refs(mach_task_self(), _port, MACH_PORT_RIGHT_SEND, -1)) != KERN_SUCCESS) {
|
|
PLCR_LOG("Unexpected error incrementing mach port reference: %d", kt);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Atomically set the Mach exception server port managed by the receiver as the @a task's Mach exception server, returning
|
|
* the previously configured ports in @a portStates.
|
|
*
|
|
* @param task The task for which the Mach exception server should be set.
|
|
* @param ports On success, will contain a set of previously registered ports for the exception masks claimed
|
|
* by the receiver. If NULL, the previous ports will not be provided.
|
|
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
|
|
* will contain an error object indicating why the Mach exception port could not be registered. If no error
|
|
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
|
|
* will be provided.
|
|
* @return YES if the mach exception port state was successfully registered for @a task, NO on error.
|
|
*/
|
|
- (BOOL) registerForTask: (task_t) task previousPortSet: (__strong PLCrashMachExceptionPortSet **) ports error: (NSError **) outError {
|
|
plcrash_mach_exception_port_set_t prev;
|
|
|
|
kern_return_t kr;
|
|
kr = task_swap_exception_ports(task,
|
|
_mask,
|
|
_port,
|
|
_behavior,
|
|
_flavor,
|
|
prev.masks,
|
|
&prev.count,
|
|
prev.ports,
|
|
prev.behaviors,
|
|
prev.flavors);
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
plcrash_populate_mach_error(outError, kr, @"Failed to swap mach exception ports");
|
|
return NO;
|
|
}
|
|
|
|
if (ports != NULL)
|
|
*ports = [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: prev];
|
|
return YES;
|
|
}
|
|
|
|
/**
|
|
* Atomically set the Mach exception server port managed by the receiver as the @a thread's Mach exception server, returning
|
|
* the previously configured ports in @a portStates.
|
|
*
|
|
* @param thread The thread for which the Mach exception server should be set.
|
|
* @param ports On success, will contain a set of previously registered ports for the exception masks claimed
|
|
* by the receiver.
|
|
* @param outError A pointer to an NSError object variable. If an error occurs, this pointer
|
|
* will contain an error object indicating why the Mach exception port could not be registered. If no error
|
|
* occurs, this parameter will be left unmodified. You may specify nil for this parameter, and no error information
|
|
* will be provided.
|
|
* @return YES if the mach exception port state was successfully registered for @a thread, NO on error.
|
|
*/
|
|
- (BOOL) registerForThread: (thread_t) thread previousPortSet: (__strong PLCrashMachExceptionPortSet **) ports error: (NSError **) outError {
|
|
plcrash_mach_exception_port_set_t prev;
|
|
|
|
kern_return_t kr;
|
|
kr = thread_swap_exception_ports(thread,
|
|
_mask,
|
|
_port,
|
|
_behavior,
|
|
_flavor,
|
|
prev.masks,
|
|
&prev.count,
|
|
prev.ports,
|
|
prev.behaviors,
|
|
prev.flavors);
|
|
|
|
if (kr != KERN_SUCCESS) {
|
|
plcrash_populate_mach_error(outError, kr, @"Failed to swap mach exception ports");
|
|
return NO;
|
|
}
|
|
|
|
if (ports != NULL)
|
|
*ports = [[PLCrashMachExceptionPortSet alloc] initWithAsyncSafeRepresentation: prev];
|
|
|
|
return YES;
|
|
}
|
|
|
|
@end
|
|
|
|
#endif /* PLCRASH_FEATURE_MACH_EXCEPTIONS */
|