前面主要是看下xnu的源码,要是对源码不感兴趣的童鞋,可以直接看捕捉。
什么是signal
POSIX
POSIX表示可移植操作系统接口(Portable Operating System Interface of UNIX,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准。
POSIX标准意在期望获得源代码级别的软件可移植性。换句话说,为一个POSIX兼容的操作系统编写的程序,应该可以在任何其它的POSIX操作系统(即使是来自另一个厂商)上编译执行。
很明显iOS和macOS也是兼容这个标准的。这个标准中有一块信号机制就是signal。
和Mach的关系
Mach已经通过异常机制提供了底层的陷阱处理,详见《iOS_Crash收集之Mach》。而BSD则在异常机制之上建立了信号处理机制。硬件产生的信号被Mach层捕捉,然后转换为对应的Unix信号。为了维护一个统一的机制,操作系统和用户产生的信号首先被转换为Mach异常,然后再转换为信号。
以上的东西讲的太宽泛,我们可以查看下xnu的源码。
//bsd/uxkern/ux_exception.c
void
ux_handler_init(void)
{
thread_t thread = THREAD_NULL;
ux_exception_port = MACH_PORT_NULL;
//启动一个新线程,开启ux_handler任务
(void) kernel_thread_start((thread_continue_t)ux_handler, NULL, &thread);
thread_deallocate(thread);
//加锁,等待ux_exception_port注册成功
proc_list_lock();
if (ux_exception_port == MACH_PORT_NULL) {
(void)msleep(&ux_exception_port, proc_list_mlock, 0, "ux_handler_wait", 0);
}
proc_list_unlock();
}
//bsd/kern/bsd_init.c
void
bsdinit_task(void)
{
proc_t p = current_proc();
struct uthread *ut;
thread_t thread;
//设置进程名称为 init
process_name("init", p);
//开启 Unix异常处理线程,注册ux_exception_port
ux_handler_init();
thread = current_thread();
//设置exception监听端口,这里不就是mach中的 XXXX_set_exception_ports么?
(void) host_set_exception_ports(host_priv_self(),
EXC_MASK_ALL & ~(EXC_MASK_RPC_ALERT),//pilotfish (shark) needs this port
(mach_port_t) ux_exception_port,
EXCEPTION_DEFAULT| MACH_EXCEPTION_CODES,
0);
ut = (uthread_t)get_bsdthread_info(thread);
#if CONFIG_MACF
mac_cred_label_associate_user(p->p_ucred);
#endif
vm_init_before_launchd();
bsd_init_kprintf("bsd_do_post - done");
//加载程序
load_init_program(p);
lock_trace = 1
}
通过调用host_set_exception_ports函数,bsdinit_task将所有的Mach异常消息都重定向到
ux_exception_port,这个端口被ux_handler持有,并且在ux_handler中处理异常消息。
接下来看看ux_handler是个啥?
__attribute__((noreturn))
static void
ux_handler(void)
{
task_t self = current_task();
mach_port_name_t exc_port_name;
mach_port_name_t exc_set_name;
/* self->kernel_vm_space = TRUE; */
ux_handler_self = self;
/*
* 分配端口,通过这个端口接受异常信息
*/
if (mach_port_allocate(get_task_ipcspace(ux_handler_self), MACH_PORT_RIGHT_PORT_SET, &exc_set_name) != MACH_MSG_SUCCESS)
panic("ux_handler: port_set_allocate failed");
/*
* 分配异常端口,并且通过ipc_object_copyin将端口转换成全局的名称
* 将端口放入集合
*/
if (mach_port_allocate(get_task_ipcspace(ux_handler_self), MACH_PORT_RIGHT_RECEIVE, &exc_port_name) != MACH_MSG_SUCCESS)
panic("ux_handler: port_allocate failed");
if (mach_port_move_member(get_task_ipcspace(ux_handler_self),
exc_port_name, exc_set_name) != MACH_MSG_SUCCESS)
panic("ux_handler: port_set_add failed");
if (ipc_object_copyin(get_task_ipcspace(self), exc_port_name,
MACH_MSG_TYPE_MAKE_SEND,
(void *) &ux_exception_port) != MACH_MSG_SUCCESS)
panic("ux_handler: object_copyin(ux_exception_port) failed");
proc_list_lock();
thread_wakeup(&ux_exception_port);
proc_list_unlock();
/*消息处理循环 */
for (;;) {
struct rep_msg {
mach_msg_header_t Head;
NDR_record_t NDR;
kern_return_t RetCode;
} rep_msg;
struct exc_msg {
mach_msg_header_t Head;
/* start of the kernel processed data */
mach_msg_body_t msgh_body;
mach_msg_port_descriptor_t thread;
mach_msg_port_descriptor_t task;
/* end of the kernel processed data */
NDR_record_t NDR;
exception_type_t exception;
mach_msg_type_number_t codeCnt;
mach_exception_data_t code;
/* some times RCV_TO_LARGE probs */
char pad[512];
} exc_msg;
mach_port_name_t reply_port;
kern_return_t result;
exc_msg.Head.msgh_local_port = CAST_MACH_NAME_TO_PORT(exc_set_name);
exc_msg.Head.msgh_size = sizeof (exc_msg);
#if 0
result = mach_msg_receive(&exc_msg.Head);
#else
result = mach_msg_receive(&exc_msg.Head, MACH_RCV_MSG,
sizeof (exc_msg), exc_set_name,
MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL,
0);
#endif
if (result == MACH_MSG_SUCCESS) {
reply_port = CAST_MACH_PORT_TO_NAME(exc_msg.Head.msgh_remote_port);
// mach_exc_server 会调用 mach_exception_raise,
// 然后会被 mach_catch_exception_raise捕获,
//信号处理逻辑就在mach_catch_exception_raise
if (mach_exc_server(&exc_msg.Head, &rep_msg.Head)) {
result = mach_msg_send(&rep_msg.Head, MACH_SEND_MSG,
sizeof (rep_msg),MACH_MSG_TIMEOUT_NONE,MACH_PORT_NULL);
if (reply_port != 0 && result != MACH_MSG_SUCCESS)
mach_port_deallocate(get_task_ipcspace(ux_handler_self), reply_port);
}
}
else if (result == MACH_RCV_TOO_LARGE)
/* ignore oversized messages */;
else
panic("exception_handler");
}
}
消息被mach_catch_exception_raise 捕捉到,mach_catch_exception_raise主要就是将异常转换成signal,那就看看源码吧。
kern_return_t
catch_mach_exception_raise(
__unused mach_port_t exception_port,
mach_port_t thread,
mach_port_t task,
exception_type_t exception,
mach_exception_data_t code,
__unused mach_msg_type_number_t codeCnt
)
{
task_t self = current_task();
thread_t th_act;
ipc_port_t thread_port;
struct proc *p;
kern_return_t result = MACH_MSG_SUCCESS;
int ux_signal = 0;
mach_exception_code_t ucode = 0;
struct uthread *ut;
mach_port_name_t thread_name = CAST_MACH_PORT_TO_NAME(thread);
mach_port_name_t task_name = CAST_MACH_PORT_TO_NAME(task);
/*
* Convert local thread name to global port.
*/
if (MACH_PORT_VALID(thread_name) &&
(ipc_object_copyin(get_task_ipcspace(self), thread_name,
MACH_MSG_TYPE_PORT_SEND,
(void *) &thread_port) == MACH_MSG_SUCCESS)) {
if (IPC_PORT_VALID(thread_port)) {
th_act = convert_port_to_thread(thread_port);
ipc_port_release_send(thread_port);
} else {
th_act = THREAD_NULL;
}
/*
* Catch bogus ports
*/
if (th_act != THREAD_NULL) {
/*
* 将异常转换成signal和code
*/
ux_exception(exception, code[0], code[1], &ux_signal, &ucode);
ut = get_bsdthread_info(th_act);
p = proc_findthread(th_act);
/* Can't deliver a signal without a bsd process reference */
if (p == NULL) {
ux_signal = 0;
result = KERN_FAILURE;
}
/*
* 堆栈溢出需要特别处理
*/
if (code[0] == KERN_PROTECTION_FAILURE &&
ux_signal == SIGBUS) {
user_addr_t sp, stack_min, stack_max;
int mask;
struct sigacts *ps;
sp = code[1];
stack_max = p->user_stack;
stack_min = p->user_stack - MAXSSIZ;
if (sp >= stack_min &&
sp < stack_max) {
/*
* This is indeed a stack overflow. Deliver a
* SIGSEGV signal.
*/
ux_signal = SIGSEGV;
/*
* If the thread/process is not ready to handle
* SIGSEGV on an alternate stack, force-deliver
* SIGSEGV with a SIG_DFL handler.
*/
mask = sigmask(ux_signal);
ps = p->p_sigacts;
if ((p->p_sigignore & mask) ||
(ut->uu_sigwait & mask) ||
(ut->uu_sigmask & mask) ||
(ps->ps_sigact[SIGSEGV] == SIG_IGN) ||
(! (ps->ps_sigonstack & mask))) {
p->p_sigignore &= ~mask;
p->p_sigcatch &= ~mask;
ps->ps_sigact[SIGSEGV] = SIG_DFL;
ut->uu_sigwait &= ~mask;
ut->uu_sigmask &= ~mask;
}
}
}
/*
* 发送信号
*/
if (ux_signal != 0) {
ut->uu_exception = exception;
//ut->uu_code = code[0]; // filled in by threadsignal
ut->uu_subcode = code[1];
threadsignal(th_act, ux_signal, code[0], TRUE);
}
if (p != NULL)
proc_rele(p);
thread_deallocate(th_act);
}
else
result = KERN_INVALID_ARGUMENT;
}
else
result = KERN_INVALID_ARGUMENT;
/*
* Delete our send rights to the task port.
*/
(void)mach_port_deallocate(get_task_ipcspace(ux_handler_self), task_name);
return (result);
}
到这里源码上面的mach exception 转 signal 的流程就走完了。看图大概更直观:
signal类型
signal异常捕获
- 获取已存在的异常处理句柄
- 设置新的异常处理句柄
//用于储存已存在的异常处理句柄
static struct sigaction* dt_previousSignalHandlers = NULL;
#define PreviousSignalHandlersSize sizeof(*dt_previousSignalHandlers)
int getPreviousSignalHandlers() {
//初始化内存。
/*
self.signals = @[@SIGABRT,
@SIGBUS,
@SIGFPE,
@SIGILL,
@SIGPIPE,
@SIGSEGV,
@SIGSYS,
@SIGTRAP];
*/
dt_previousSignalHandlers = malloc(PreviousSignalHandlersSize * self.signals.count);
struct sigaction action = {{0}};
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
#if defined(__LP64__)
action.sa_flags |= SA_64REGSET;
#endif
sigemptyset(&action.sa_mask);
action.sa_sigaction = &handleSignal;
for (int i = 0; i < self.signals.count; i++) {
int signum =((NSNumber *)self.signals[i]).intValue;
//保存原来的异常处理句柄,设置新的异常处理句柄
int result = sigaction(signum,
&action,
&dt_previousSignalHandlers[i]);
if (result != 0) {
for (i -- ; i >= 0 ; i --) {
sigaction(signum,
&dt_previousSignalHandlers[i],
NULL);
}
return -1;
}
return 0;
}
- 实现异常处理方法
static void handleSignal(int sigNum, siginfo_t* signalInfo, void* userContext) {
#ifdef __arm64__
#define UC_MCONTEXT uc_mcontext64
typedef ucontext64_t SignalUserContext;
#else
#define UC_MCONTEXT uc_mcontext
typedef ucontext_t SignalUserContext;
#endif
//可以从uc中拿到backtrace
_STRUCT_MCONTEXT *uc = ((SignalUserContext*)userContext)->UC_MCONTEXT;
int signalNum = signalInfo->si_signo;
int signalCode = signalInfo->si_code;
uintptr_t signalAddress = (uintptr_t)signalInfo->si_addr;
raise(sigNum);
}
-
重置异常处理句柄
void resetSignalHandler() { for (int i = 0; i < self.signals.count; ++ i) { int signum =((NSNumber *)self.signals[i]).intValue; int result = sigaction(signum, &cuckoo_previousSignalHandlers[i], NULL); if (result != 0) { CuckooError(@"Sigaction uninstall failure:using default signal %d",signum); struct sigaction sa; memset(&sa, 0, sizeof(sa)); sa.sa_handler = SIG_ERR; sigemptyset(&sa.sa_mask); sigaction(signum, &sa, NULL); } } }
堆栈解析###
详见堆栈解析