unwind.c revision 7257d1b4d25bfac0c802847390e98a464fd787ac
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* or http://www.opensolaris.org/os/licensing.
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright 2008 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "lint.h"
#include "thr_uberdata.h"
#include <dlfcn.h>
/*
* This is common code for sparc, sparcv9, and i386.
* The amd64 unwind code is vastly different from this.
* Look under the amd64-specific directory structure for details.
*/
/*
* _ex_unwind() is provided by libC, but if libC is not loaded we
* need to call a local version of _ex_unwind() which does exactly
* the same thing except for calling C++ destructors.
*/
extern void _ex_clnup_handler(void *, void (*)(void *));
extern void _ex_unwind_local(void);
#pragma unknown_control_flow(_ex_clnup_handler)
#pragma unknown_control_flow(_ex_unwind_local)
/*
* _t_cancel(fp):calls cleanup handlers if there are any in
* frame (fp), and calls _ex_unwind() to call
* destructors if libC has been linked.
*
* Control comes here from _thrp_unwind. Logically:
*
* _thrp_unwind: first arg = current fp;
* jump _t_cancel;
*
* We could have called _t_cancel(_getfp) from thr_exit()
* but _ex_unwind() also calls _t_cancel() and it does after
* poping out the two frames. If _ex_unwind() passes the current
* fp, then it will be invalid. For a caller of _thrp_unwind()
* it looks as if it is calling _t_cancel(fp).
*
* _t_cancel will eventually call _thrp_exit().
* It never returns from _t_cancel().
*
*/
void
_t_cancel(void *fp)
{
ulwp_t *self = curthread;
__cleanup_t *head;
void (*fptr)(void (*func)(void *), void *arg);
/* Do this once per thread exit, not once per unwind frame */
if (self->ul_ex_unwind == NULL &&
(self->ul_ex_unwind = dlsym(RTLD_PROBE, "_ex_unwind")) == NULL)
self->ul_ex_unwind = (void *)-1;
if (self->ul_ex_unwind == (void *)-1)
fptr = NULL;
else
fptr = (void (*)())self->ul_ex_unwind;
if (fp == NULL) {
_thrp_exit();
thr_panic("_t_cancel(): _thrp_exit() returned");
}
if ((head = self->ul_clnup_hdr) != NULL && fp == head->fp) {
self->ul_clnup_hdr = head->next;
/* execute the cleanup handler */
_ex_clnup_handler(head->arg, head->func);
thr_panic("_t_cancel(): _ex_clnup_handler() returned");
}
if (fptr != NULL && self->ul_unwind) {
/* libC is loaded and thread is canceled, call libC version */
(*fptr)(_thrp_unwind, NULL);
thr_panic("_t_cancel(): _ex_unwind() returned");
} else if (head != NULL) {
/* libC not present, call local version */
_ex_unwind_local();
thr_panic("_t_cancel(): _ex_unwind_local() returned");
} else {
/* libC not present and no cleanup handlers, exit here */
_thrp_exit();
thr_panic("_t_cancel(): _thrp_exit() returned");
}
/* never returns here */
}