xref: /illumos-gate/usr/src/cmd/sgs/demo_rdb/common/syms.c (revision 379728489ed47862c4927c75771e767b9476c9c4)
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 *
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
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
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
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
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
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