xref: /illumos-gate/usr/src/cmd/sgs/rtld/amd64/dlamd64getunwind.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2009 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 #pragma ident	"@(#)dlamd64getunwind.c	1.10	08/07/30 SMI"
27 
28 #include	<string.h>
29 #include	<dlfcn.h>
30 #include	<stdio.h>
31 #include	<debug.h>
32 #include	"_rtld.h"
33 #include	"_elf.h"
34 #include	"_inline.h"
35 #include	"msg.h"
36 
37 
38 static Dl_amd64_unwindinfo *
39 getunwind_core(Lm_list *lml, void *pc, Dl_amd64_unwindinfo *unwindinfo)
40 {
41 	Rt_map	*lmp;
42 
43 	/*
44 	 * Validate the version information.
45 	 */
46 	if (unwindinfo == NULL) {
47 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_ARG_ILLVAL));
48 		return (0);
49 	}
50 	if ((unwindinfo->dlui_version < DLUI_VERS_1) ||
51 	    (unwindinfo->dlui_version > DLUI_VERS_CURRENT)) {
52 		eprintf(lml, ERR_FATAL, MSG_INTL(MSG_UNW_BADVERS),
53 		    unwindinfo->dlui_version, DLUI_VERS_CURRENT);
54 		return (0);
55 	}
56 
57 	/*
58 	 * Clean out the structure.
59 	 */
60 	unwindinfo->dlui_flags = 0;
61 	unwindinfo->dlui_objname = 0;
62 	unwindinfo->dlui_unwindstart = 0;
63 	unwindinfo->dlui_unwindend = 0;
64 	unwindinfo->dlui_segstart = 0;
65 	unwindinfo->dlui_segend = 0;
66 
67 	/*
68 	 * Identify the link-map associated with the exception "pc".  Note,
69 	 * the "pc" might not correspond to a link-map (as can happen with a
70 	 * "pc" fabricated by a debugger such as dbx).  In this case, the
71 	 * unwind data buffer will be filled with flags set to indicate an
72 	 * unknown caller.
73 	 */
74 	lmp = _caller(pc, CL_NONE);
75 
76 	if (lmp) {
77 		mmapobj_result_t	*mpp;
78 
79 		/*
80 		 * Determine the associated segment.
81 		 */
82 		if ((mpp = find_segment(pc, lmp)) == NULL)
83 			return (0);
84 
85 		unwindinfo->dlui_objname = (char *)PATHNAME(lmp);
86 		unwindinfo->dlui_segstart = mpp->mr_addr;
87 		unwindinfo->dlui_segend = mpp->mr_addr + mpp->mr_msize;
88 
89 		if (PTUNWIND(lmp) && (mpp->mr_addr)) {
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 (mpp->mr_addr)
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(0);
125 
126 	lmp = _caller(caller(), CL_EXECDEF);
127 	lml = LIST(lmp);
128 
129 	unwindinfo = getunwind_core(lml, pc, unwindinfo);
130 
131 	if (entry)
132 		leave(lml, 0);
133 	return (unwindinfo);
134 }
135