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