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 /*
30  * Block comment which describes the contents of this file.
31  */
32 
33 #include <_synonyms.h>
34 #include <string.h>
35 #include <dlfcn.h>
36 #include <debug.h>
37 #include <_rtld.h>
38 #include <_elf.h>
39 #include <msg.h>
40 
41 #include <stdio.h>
42 
43 static Dl_amd64_unwindinfo *
44 getunwind_core(Lm_list *lml, void *pc, Dl_amd64_unwindinfo *unwindinfo)
45 {
46 	Rt_map	*lmp;
47 
48 	/*
49 	 * Validate the version information.
50 	 */
51 	if (unwindinfo == NULL) {
52 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLVAL));
53 		return (0);
54 	}
55 	if ((unwindinfo->dlui_version < DLUI_VERS_1) ||
56 	    (unwindinfo->dlui_version > DLUI_VERS_CURRENT)) {
57 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_UNW_BADVERS),
58 		    unwindinfo->dlui_version, DLUI_VERS_CURRENT);
59 		return (0);
60 	}
61 
62 	/*
63 	 * Clean out the structure.
64 	 */
65 	unwindinfo->dlui_flags = 0;
66 	unwindinfo->dlui_objname = 0;
67 	unwindinfo->dlui_unwindstart = 0;
68 	unwindinfo->dlui_unwindend = 0;
69 	unwindinfo->dlui_segstart = 0;
70 	unwindinfo->dlui_segend = 0;
71 
72 	/*
73 	 * Identify the link-map associated with the exception "pc".  Note,
74 	 * the "pc" might not correspond to a link-map (as can happen with a
75 	 * "pc" fabricated by a debugger such as dbx).  In this case, the
76 	 * unwind data buffer will be filled with flags set to indicate an
77 	 * unknown caller.
78 	 */
79 	lmp = _caller(pc, CL_NONE);
80 
81 	if (lmp) {
82 		Mmap	*immap;
83 
84 		unwindinfo->dlui_objname = PATHNAME(lmp);
85 
86 		/*
87 		 * Scan through the mmaps of this object to get the specific
88 		 * segment found.
89 		 */
90 		for (immap = MMAPS(lmp); immap->m_vaddr; immap++) {
91 			if (((caddr_t)pc >= immap->m_vaddr) &&
92 			    ((caddr_t)pc < (immap->m_vaddr + immap->m_msize))) {
93 				break;
94 			}
95 		}
96 		unwindinfo->dlui_segstart = immap->m_vaddr;
97 		unwindinfo->dlui_segend = immap->m_vaddr + immap->m_msize;
98 
99 		if (PTUNWIND(lmp) && (immap->m_vaddr)) {
100 			uintptr_t   base;
101 
102 			if (FLAGS(lmp) & FLG_RT_FIXED)
103 				base = 0;
104 			else
105 				base = ADDR(lmp);
106 
107 			unwindinfo->dlui_unwindstart =
108 			    (void *)(PTUNWIND(lmp)->p_vaddr + base);
109 			unwindinfo->dlui_unwindend =
110 			    (void *)(PTUNWIND(lmp)->p_vaddr +
111 			    PTUNWIND(lmp)->p_memsz + base);
112 
113 		} else if (immap->m_vaddr)
114 			unwindinfo->dlui_flags |= DLUI_FLG_NOUNWIND;
115 		else
116 			unwindinfo->dlui_flags |=
117 			    DLUI_FLG_NOUNWIND | DLUI_FLG_NOOBJ;
118 	} else {
119 		/*
120 		 * No object found.
121 		 */
122 		unwindinfo->dlui_flags = DLUI_FLG_NOOBJ | DLUI_FLG_NOUNWIND;
123 	}
124 	return (unwindinfo);
125 }
126 
127 #pragma weak dlamd64getunwind = _dlamd64getunwind
128 
129 Dl_amd64_unwindinfo *
130 _dlamd64getunwind(void *pc, Dl_amd64_unwindinfo *unwindinfo)
131 {
132 	Rt_map	*lmp;
133 	Lm_list	*lml;
134 	int	entry = enter(0);
135 
136 	lmp = _caller(caller(), CL_EXECDEF);
137 	lml = LIST(lmp);
138 
139 	unwindinfo = getunwind_core(lml, pc, unwindinfo);
140 
141 	if (entry)
142 		leave(lml, 0);
143 	return (unwindinfo);
144 }
145