1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9  * or http://www.opensolaris.org/os/licensing.
10  * See the License for the specific language governing permissions
11  * and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL HEADER in each
14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15  * If applicable, add the following below this CDDL HEADER, with the
16  * fields enclosed by brackets "[]" replaced with your own identifying
17  * information: Portions Copyright [yyyy] [name of copyright owner]
18  *
19  * CDDL HEADER END
20  */
21 
22 /*
23  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 #include "lint.h"
30 #include "thr_uberdata.h"
31 #include "stack_unwind.h"
32 #include "reg_num.h"
33 #include <dlfcn.h>
34 
35 /*
36  * Due to the subtle mysteries of the amd64 unwind interfaces, the
37  * "Canonical Frame Address" is 16 bytes higher in memory than the
38  * value of the frame pointer (%fp).
39  */
40 #define	CFA_ADJUST	16
41 
42 /* ARGSUSED */
43 static _Unwind_Reason_Code
posix_stop_func(int version,_Unwind_Action _Unwind_actions,uint64_t exceptionClass,struct _Unwind_Exception * exceptionObject,struct _Unwind_Context * context,void * func_arg)44 posix_stop_func(
45 	int version,
46 	_Unwind_Action _Unwind_actions,
47 	uint64_t exceptionClass,
48 	struct _Unwind_Exception *exceptionObject,
49 	struct _Unwind_Context *context,
50 	void *func_arg)
51 {
52 	__cleanup_t **headp = (__cleanup_t **)func_arg;
53 	__cleanup_t *head;
54 	uint64_t cfa;
55 
56 	/*
57 	 * If we have reached the origin of the stack, exit now.
58 	 */
59 	cfa = _Unwind_GetCFA(context);
60 	if (cfa == 0 || _Unwind_GetGR(context, RET_ADD) == 0) {
61 		_Unwind_DeleteException(exceptionObject);
62 		_thrp_exit();
63 		thr_panic("posix_stop_func(): _thrp_exit() returned");
64 	}
65 
66 	/*
67 	 * Call all Posix cleanup handlers for this frame.
68 	 */
69 	while ((head = *headp) != NULL &&
70 	    (caddr_t)cfa == head->fp + CFA_ADJUST) {
71 		*headp = head->next;
72 		(*head->func)(head->arg);
73 	}
74 
75 	return (_URC_NO_REASON);
76 }
77 
78 /*
79  * _ex_unwind() is provided by libCrun to perform stack unwinding
80  * and calling C++ destructors as needed, interleaved with calling
81  * Posix cleanup handlers along the way.  If libCrun is not present
82  * we just need to call the Posix cleanup handlers.
83  */
84 
85 /* ARGSUSED */
86 void
_thrp_unwind(void * dummy)87 _thrp_unwind(void *dummy)
88 {
89 	ulwp_t *self = curthread;
90 	__cleanup_t **headp = &self->ul_clnup_hdr;
91 	__cleanup_t *head;
92 	void (*fptr)(_Unwind_Stop_Fn, void *);
93 
94 	/* Do this once per thread exit, not once per unwind frame */
95 	if (self->ul_ex_unwind == NULL &&
96 	    (self->ul_ex_unwind = dlsym(RTLD_PROBE, "_ex_unwind")) == NULL)
97 		self->ul_ex_unwind = (void *)-1;
98 
99 	if (self->ul_ex_unwind == (void *)-1)
100 		fptr = NULL;
101 	else
102 		fptr = (void (*)())self->ul_ex_unwind;
103 
104 	/*
105 	 * Call _ex_unwind() if it is present (C++ loaded),
106 	 * else just call the Posix cleanup handlers.
107 	 */
108 	if (fptr != NULL)
109 		(*fptr)(posix_stop_func, headp);
110 
111 	/*
112 	 * Call all remaining Posix cleanup handlers.
113 	 */
114 	while ((head = *headp) != NULL) {
115 		*headp = head->next;
116 		(*head->func)(head->arg);
117 	}
118 
119 	_thrp_exit();
120 	thr_panic("_thrp_unwind(): _thrp_exit() returned");
121 }
122