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) 1995, 2010, Oracle and/or its affiliates. All rights reserved.
24 */
25
26 #include <stdio.h>
27 #include <string.h>
28 #include <libelf.h>
29 #include "rdb.h"
30
31 /*
32 * Given a symbol index, look up the corresponding symbol from the
33 * given symbol table.
34 *
35 * This function allows the caller to treat the symbol table as a single
36 * logical entity even though there may be 2 actual ELF symbol tables
37 * involved. See the comments in Pcontrol.h for details.
38 */
39 static GElf_Sym *
symtab_getsym(sym_tbl_t * symtab,int ndx,GElf_Sym * dst)40 symtab_getsym(sym_tbl_t *symtab, int ndx, GElf_Sym *dst)
41 {
42 /* If index is in range of primary symtab, look it up there */
43 if (ndx >= symtab->st_symn_aux) {
44 return (gelf_getsym(symtab->st_syms_pri,
45 ndx - symtab->st_symn_aux, dst));
46 }
47
48 /* Not in primary: Look it up in the auxiliary symtab */
49 return (gelf_getsym(symtab->st_syms_aux, ndx, dst));
50 }
51
52 retc_t
str_map_sym(const char * symname,map_info_t * mp,GElf_Sym * symptr,char ** str)53 str_map_sym(const char *symname, map_info_t *mp, GElf_Sym *symptr, char **str)
54 {
55 sym_tbl_t *symp;
56 char *strs;
57 int i;
58
59 if (mp->mi_symtab.st_syms_pri)
60 symp = &(mp->mi_symtab);
61 else if (mp->mi_dynsym.st_syms_pri)
62 symp = &(mp->mi_dynsym);
63 else
64 return (RET_FAILED);
65
66 strs = symp->st_strs;
67
68 for (i = 0; i < (int)symp->st_symn; i++) {
69 GElf_Sym sym;
70
71 if (symtab_getsym(symp, i, &sym) == NULL) {
72 (void) printf("symtab_getsym(): %s\n", elf_errmsg(-1));
73 return (RET_FAILED);
74 }
75
76 if (sym.st_name == 0)
77 continue;
78 if ((sym.st_shndx == SHN_UNDEF) ||
79 (strcmp(strs + sym.st_name, symname) != 0))
80 continue;
81 *symptr = sym;
82 if (str != NULL)
83 *str = (char *)strs + symptr->st_name;
84 if ((mp->mi_flags & FLG_MI_EXEC) == 0)
85 symptr->st_value += (GElf_Addr)(mp->mi_addr);
86 return (RET_OK);
87 }
88
89 return (RET_FAILED);
90 }
91
92 /*
93 * If two syms are of equal value this routine will
94 * favor one over the other based off of it's symbol
95 * type.
96 */
97 static GElf_Sym
sym_swap(GElf_Sym * s1,GElf_Sym * s2)98 sym_swap(GElf_Sym * s1, GElf_Sym * s2)
99 {
100 int t1 = GELF_ST_TYPE(s1->st_info);
101 int t2 = GELF_ST_TYPE(s2->st_info);
102
103 if ((t1 == STT_FUNC) || (t2 == STT_FUNC)) {
104 if (t1 == STT_FUNC)
105 return (*s1);
106 return (*s2);
107 }
108
109 if ((t1 == STT_OBJECT) || (t2 == STT_OBJECT)) {
110 if (t1 == STT_OBJECT)
111 return (*s1);
112 return (*s2);
113 }
114
115 if ((t1 == STT_OBJECT) || (t2 == STT_OBJECT)) {
116 if (t1 == STT_OBJECT)
117 return (*s1);
118 return (*s2);
119 }
120 return (*s1);
121 }
122
123 static retc_t
addr_map_sym(map_info_t * mp,ulong_t addr,GElf_Sym * symptr,char ** str)124 addr_map_sym(map_info_t *mp, ulong_t addr, GElf_Sym *symptr, char **str)
125 {
126 sym_tbl_t *symp;
127 GElf_Sym sym;
128 GElf_Sym *symr = NULL;
129 GElf_Sym *lsymr = NULL;
130 GElf_Sym rsym;
131 GElf_Sym lsym;
132 ulong_t baseaddr = 0;
133 int i;
134
135 if ((mp->mi_flags & FLG_MI_EXEC) == 0)
136 baseaddr = (ulong_t)mp->mi_addr;
137
138 if (mp->mi_symtab.st_syms_pri)
139 symp = &(mp->mi_symtab);
140 else if (mp->mi_dynsym.st_syms_pri)
141 symp = &(mp->mi_dynsym);
142 else
143 return (RET_FAILED);
144
145 /*
146 * normalize address
147 */
148 addr -= baseaddr;
149 for (i = 0; i < (int)symp->st_symn; i++) {
150 ulong_t svalue;
151
152 if (symtab_getsym(symp, i, &sym) == NULL) {
153 (void) printf("symtab_getsym(): %s\n", elf_errmsg(-1));
154 return (RET_FAILED);
155 }
156 if ((sym.st_name == 0) || (sym.st_shndx == SHN_UNDEF))
157 continue;
158
159 svalue = (ulong_t)sym.st_value;
160
161 if (svalue <= addr) {
162 /*
163 * track both the best local and best
164 * global fit for this address. Later
165 * we will favor the global over the local
166 */
167 if ((GELF_ST_BIND(sym.st_info) == STB_LOCAL) &&
168 ((lsymr == NULL) ||
169 (svalue >= (ulong_t)lsymr->st_value))) {
170 if (lsymr && (lsymr->st_value == svalue))
171 *lsymr = sym_swap(lsymr, &sym);
172 else {
173 lsymr = &lsym;
174 *lsymr = sym;
175 }
176 } else if ((symr == NULL) ||
177 (svalue >= (ulong_t)symr->st_value)) {
178 if (symr && (symr->st_value == svalue))
179 *symr = sym_swap(symr, &sym);
180 else {
181 symr = &rsym;
182 *symr = sym;
183 }
184 }
185 }
186 }
187 if ((symr == NULL) && (lsymr == NULL))
188 return (RET_FAILED);
189
190 if (lsymr) {
191 /*
192 * If a possible local symbol was found should
193 * we use it.
194 */
195 if (symr && (lsymr->st_value > symr->st_value))
196 symr = lsymr;
197 else if (symr == NULL)
198 symr = lsymr;
199 }
200
201 *symptr = *symr;
202 *str = (char *)(symp->st_strs + symptr->st_name);
203 symptr->st_value += baseaddr;
204 return (RET_OK);
205 }
206
207 retc_t
addr_to_sym(struct ps_prochandle * ph,ulong_t addr,GElf_Sym * symp,char ** str)208 addr_to_sym(struct ps_prochandle *ph, ulong_t addr,
209 GElf_Sym *symp, char **str)
210 {
211 map_info_t *mip;
212
213 if ((mip = addr_to_map(ph, addr)) == NULL)
214 return (RET_FAILED);
215
216 return (addr_map_sym(mip, addr, symp, str));
217 }
218
219 retc_t
str_to_sym(struct ps_prochandle * ph,const char * name,GElf_Sym * symp)220 str_to_sym(struct ps_prochandle *ph, const char *name, GElf_Sym *symp)
221 {
222 map_info_t *mip;
223
224 if (ph->pp_lmaplist.ml_head == NULL) {
225 if (str_map_sym(name, &(ph->pp_ldsomap), symp, NULL) == RET_OK)
226 return (RET_OK);
227
228 return (str_map_sym(name, &(ph->pp_execmap), symp, NULL));
229 }
230
231 for (mip = ph->pp_lmaplist.ml_head; mip; mip = mip->mi_next)
232 if (str_map_sym(name, mip, symp, NULL) == RET_OK)
233 return (RET_OK);
234
235 return (RET_FAILED);
236 }
237