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