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 2007 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, Rt_map *lmp, void *pc,
45     Dl_amd64_unwindinfo *unwindinfo)
46 {
47 	/*
48 	 * Validate the version information.
49 	 */
50 	if (unwindinfo == NULL) {
51 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLVAL));
52 		return (0);
53 	}
54 	if ((unwindinfo->dlui_version < DLUI_VERS_1) ||
55 	    (unwindinfo->dlui_version > DLUI_VERS_CURRENT)) {
56 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_UNW_BADVERS),
57 		    unwindinfo->dlui_version, DLUI_VERS_CURRENT);
58 		return (0);
59 	}
60 
61 	/*
62 	 * Clean out the structure.
63 	 */
64 	unwindinfo->dlui_flags = 0;
65 	unwindinfo->dlui_objname = 0;
66 	unwindinfo->dlui_unwindstart = 0;
67 	unwindinfo->dlui_unwindend = 0;
68 	unwindinfo->dlui_segstart = 0;
69 	unwindinfo->dlui_segend = 0;
70 
71 	if (lmp) {
72 		Mmap	*immap;
73 
74 		unwindinfo->dlui_objname = PATHNAME(lmp);
75 
76 		/*
77 		 * Scan through the mmaps of this object to get the specific
78 		 * segment found.
79 		 */
80 		for (immap = MMAPS(lmp); immap->m_vaddr; immap++) {
81 			if (((caddr_t)pc >= immap->m_vaddr) &&
82 			    ((caddr_t)pc < (immap->m_vaddr + immap->m_msize))) {
83 				break;
84 			}
85 		}
86 		unwindinfo->dlui_segstart = immap->m_vaddr;
87 		unwindinfo->dlui_segend = immap->m_vaddr + immap->m_msize;
88 
89 		if (PTUNWIND(lmp) && (immap->m_vaddr)) {
90 			uintptr_t   base;
91 
92 			if (FLAGS(lmp) & FLG_RT_FIXED)
93 				base = 0;
94 			else
95 				base = ADDR(lmp);
96 
97 			unwindinfo->dlui_unwindstart =
98 			    (void *)(PTUNWIND(lmp)->p_vaddr + base);
99 			unwindinfo->dlui_unwindend =
100 			    (void *)(PTUNWIND(lmp)->p_vaddr +
101 			    PTUNWIND(lmp)->p_memsz + base);
102 
103 		} else if (immap->m_vaddr)
104 			unwindinfo->dlui_flags |= DLUI_FLG_NOUNWIND;
105 		else
106 			unwindinfo->dlui_flags |=
107 			    DLUI_FLG_NOUNWIND | DLUI_FLG_NOOBJ;
108 	} else {
109 		/*
110 		 * No object found.
111 		 */
112 		unwindinfo->dlui_flags = DLUI_FLG_NOOBJ | DLUI_FLG_NOUNWIND;
113 	}
114 	return (unwindinfo);
115 }
116 
117 #pragma weak dlamd64getunwind = _dlamd64getunwind
118 
119 Dl_amd64_unwindinfo *
120 _dlamd64getunwind(void *pc, Dl_amd64_unwindinfo *unwindinfo)
121 {
122 	Rt_map	*lmp;
123 	Lm_list	*lml;
124 	int	entry = enter();
125 
126 	/*
127 	 * Identify the link-map associated with the exception "pc".  Note,
128 	 * this is not the actual caller of _dlamd64getunwind(), which is
129 	 * probably one of the libC libraries.  However, the caller and
130 	 * exception object are both on the same link-map list.  In doing this,
131 	 * we must guard against being given a pc that does not correspond to a
132 	 * link-map (as can happen with a pc fabricated by a debugger such as
133 	 * dbx).  In this case, getunwind_core() will fill the unwind data
134 	 * buffer with flags set to indicate an unknown caller.
135 	 */
136 	if ((lmp = _caller(pc, CL_NONE)) != 0)
137 		lml = LIST(lmp);
138 	else
139 		lml = 0;
140 
141 	unwindinfo = getunwind_core(lml, lmp, pc, unwindinfo);
142 
143 	if (entry)
144 		leave(lml);
145 	return (unwindinfo);
146 }
147