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 #include "lint.h"
28 #include "thr_uberdata.h"
29 #include "stack_unwind.h"
30 #include "reg_num.h"
31 #include <dlfcn.h>
32 
33 /*
34  * Due to the subtle mysteries of the amd64 unwind interfaces, the
35  * "Canonical Frame Address" is 16 bytes higher in memory than the
36  * value of the frame pointer (%fp).
37  */
38 #define	CFA_ADJUST	16
39 
40 static _Unwind_Reason_Code
posix_stop_func(int version __unused,_Unwind_Action _Unwind_actions __unused,uint64_t exceptionClass __unused,struct _Unwind_Exception * exceptionObject,struct _Unwind_Context * context,void * func_arg)41 posix_stop_func(int version __unused,
42     _Unwind_Action _Unwind_actions __unused,
43     uint64_t exceptionClass __unused,
44     struct _Unwind_Exception *exceptionObject,
45     struct _Unwind_Context *context,
46     void *func_arg)
47 {
48 	__cleanup_t **headp = (__cleanup_t **)func_arg;
49 	__cleanup_t *head;
50 	uint64_t cfa;
51 
52 	/*
53 	 * If we have reached the origin of the stack, exit now.
54 	 */
55 	cfa = _Unwind_GetCFA(context);
56 	if (cfa == 0 || _Unwind_GetGR(context, RET_ADD) == 0) {
57 		_Unwind_DeleteException(exceptionObject);
58 		_thrp_exit();
59 		thr_panic("posix_stop_func(): _thrp_exit() returned");
60 	}
61 
62 	/*
63 	 * Call all Posix cleanup handlers for this frame.
64 	 */
65 	while ((head = *headp) != NULL &&
66 	    (caddr_t)cfa == head->fp + CFA_ADJUST) {
67 		*headp = head->next;
68 		(*head->func)(head->arg);
69 	}
70 
71 	return (_URC_NO_REASON);
72 }
73 
74 /*
75  * _ex_unwind() is provided by libCrun to perform stack unwinding
76  * and calling C++ destructors as needed, interleaved with calling
77  * Posix cleanup handlers along the way.  If libCrun is not present
78  * we just need to call the Posix cleanup handlers.
79  */
80 
81 void
_thrp_unwind(void * dummy __unused)82 _thrp_unwind(void *dummy __unused)
83 {
84 	ulwp_t *self = curthread;
85 	__cleanup_t **headp = &self->ul_clnup_hdr;
86 	__cleanup_t *head;
87 	void (*fptr)(_Unwind_Stop_Fn, void *);
88 
89 	/* Do this once per thread exit, not once per unwind frame */
90 	if (self->ul_ex_unwind == NULL &&
91 	    (self->ul_ex_unwind = dlsym(RTLD_PROBE, "_ex_unwind")) == NULL)
92 		self->ul_ex_unwind = (void *)-1;
93 
94 	if (self->ul_ex_unwind == (void *)-1)
95 		fptr = NULL;
96 	else
97 		fptr = (void (*)())self->ul_ex_unwind;
98 
99 	/*
100 	 * Call _ex_unwind() if it is present (C++ loaded),
101 	 * else just call the Posix cleanup handlers.
102 	 */
103 	if (fptr != NULL)
104 		(*fptr)(posix_stop_func, headp);
105 
106 	/*
107 	 * Call all remaining Posix cleanup handlers.
108 	 */
109 	while ((head = *headp) != NULL) {
110 		*headp = head->next;
111 		(*head->func)(head->arg);
112 	}
113 
114 	_thrp_exit();
115 	thr_panic("_thrp_unwind(): _thrp_exit() returned");
116 }
117