xref: /illumos-gate/usr/src/cmd/sgs/rtld/amd64/dlamd64getunwind.c (revision a73c0fe4e90b82a478f821ef3adb5cf34f6a9346)
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 <string.h>
34 #include <dlfcn.h>
35 #include <debug.h>
36 #include <_rtld.h>
37 #include <_elf.h>
38 #include <msg.h>
39 
40 #include <stdio.h>
41 
42 static Dl_amd64_unwindinfo *
43 getunwind_core(Lm_list *lml, void *pc, Dl_amd64_unwindinfo *unwindinfo)
44 {
45 	Rt_map	*lmp;
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 	/*
72 	 * Identify the link-map associated with the exception "pc".  Note,
73 	 * the "pc" might not correspond to a link-map (as can happen with a
74 	 * "pc" fabricated by a debugger such as dbx).  In this case, the
75 	 * unwind data buffer will be filled with flags set to indicate an
76 	 * unknown caller.
77 	 */
78 	lmp = _caller(pc, CL_NONE);
79 
80 	if (lmp) {
81 		Mmap	*immap;
82 
83 		unwindinfo->dlui_objname = PATHNAME(lmp);
84 
85 		/*
86 		 * Scan through the mmaps of this object to get the specific
87 		 * segment found.
88 		 */
89 		for (immap = MMAPS(lmp); immap->m_vaddr; immap++) {
90 			if (((caddr_t)pc >= immap->m_vaddr) &&
91 			    ((caddr_t)pc < (immap->m_vaddr + immap->m_msize))) {
92 				break;
93 			}
94 		}
95 		unwindinfo->dlui_segstart = immap->m_vaddr;
96 		unwindinfo->dlui_segend = immap->m_vaddr + immap->m_msize;
97 
98 		if (PTUNWIND(lmp) && (immap->m_vaddr)) {
99 			uintptr_t   base;
100 
101 			if (FLAGS(lmp) & FLG_RT_FIXED)
102 				base = 0;
103 			else
104 				base = ADDR(lmp);
105 
106 			unwindinfo->dlui_unwindstart =
107 			    (void *)(PTUNWIND(lmp)->p_vaddr + base);
108 			unwindinfo->dlui_unwindend =
109 			    (void *)(PTUNWIND(lmp)->p_vaddr +
110 			    PTUNWIND(lmp)->p_memsz + base);
111 
112 		} else if (immap->m_vaddr)
113 			unwindinfo->dlui_flags |= DLUI_FLG_NOUNWIND;
114 		else
115 			unwindinfo->dlui_flags |=
116 			    DLUI_FLG_NOUNWIND | DLUI_FLG_NOOBJ;
117 	} else {
118 		/*
119 		 * No object found.
120 		 */
121 		unwindinfo->dlui_flags = DLUI_FLG_NOOBJ | DLUI_FLG_NOUNWIND;
122 	}
123 	return (unwindinfo);
124 }
125 
126 #pragma weak _dlamd64getunwind = dlamd64getunwind
127 
128 Dl_amd64_unwindinfo *
129 dlamd64getunwind(void *pc, Dl_amd64_unwindinfo *unwindinfo)
130 {
131 	Rt_map	*lmp;
132 	Lm_list	*lml;
133 	int	entry = enter(0);
134 
135 	lmp = _caller(caller(), CL_EXECDEF);
136 	lml = LIST(lmp);
137 
138 	unwindinfo = getunwind_core(lml, pc, unwindinfo);
139 
140 	if (entry)
141 		leave(lml, 0);
142 	return (unwindinfo);
143 }
144