1da0c48c4Sopenharmony_ci/* Find debugging and symbol information for a module in libdwfl. 2da0c48c4Sopenharmony_ci Copyright (C) 2005-2013 Red Hat, Inc. 3da0c48c4Sopenharmony_ci This file is part of elfutils. 4da0c48c4Sopenharmony_ci 5da0c48c4Sopenharmony_ci This file is free software; you can redistribute it and/or modify 6da0c48c4Sopenharmony_ci it under the terms of either 7da0c48c4Sopenharmony_ci 8da0c48c4Sopenharmony_ci * the GNU Lesser General Public License as published by the Free 9da0c48c4Sopenharmony_ci Software Foundation; either version 3 of the License, or (at 10da0c48c4Sopenharmony_ci your option) any later version 11da0c48c4Sopenharmony_ci 12da0c48c4Sopenharmony_ci or 13da0c48c4Sopenharmony_ci 14da0c48c4Sopenharmony_ci * the GNU General Public License as published by the Free 15da0c48c4Sopenharmony_ci Software Foundation; either version 2 of the License, or (at 16da0c48c4Sopenharmony_ci your option) any later version 17da0c48c4Sopenharmony_ci 18da0c48c4Sopenharmony_ci or both in parallel, as here. 19da0c48c4Sopenharmony_ci 20da0c48c4Sopenharmony_ci elfutils is distributed in the hope that it will be useful, but 21da0c48c4Sopenharmony_ci WITHOUT ANY WARRANTY; without even the implied warranty of 22da0c48c4Sopenharmony_ci MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23da0c48c4Sopenharmony_ci General Public License for more details. 24da0c48c4Sopenharmony_ci 25da0c48c4Sopenharmony_ci You should have received copies of the GNU General Public License and 26da0c48c4Sopenharmony_ci the GNU Lesser General Public License along with this program. If 27da0c48c4Sopenharmony_ci not, see <http://www.gnu.org/licenses/>. */ 28da0c48c4Sopenharmony_ci 29da0c48c4Sopenharmony_ci#ifdef HAVE_CONFIG_H 30da0c48c4Sopenharmony_ci# include <config.h> 31da0c48c4Sopenharmony_ci#endif 32da0c48c4Sopenharmony_ci 33da0c48c4Sopenharmony_ci#include "libdwflP.h" 34da0c48c4Sopenharmony_ci 35da0c48c4Sopenharmony_cistruct search_state 36da0c48c4Sopenharmony_ci{ 37da0c48c4Sopenharmony_ci Dwfl_Module *mod; 38da0c48c4Sopenharmony_ci GElf_Addr addr; 39da0c48c4Sopenharmony_ci 40da0c48c4Sopenharmony_ci GElf_Sym *closest_sym; 41da0c48c4Sopenharmony_ci bool adjust_st_value; 42da0c48c4Sopenharmony_ci GElf_Word addr_shndx; 43da0c48c4Sopenharmony_ci Elf *addr_symelf; 44da0c48c4Sopenharmony_ci 45da0c48c4Sopenharmony_ci /* Keep track of the closest symbol we have seen so far. 46da0c48c4Sopenharmony_ci Here we store only symbols with nonzero st_size. */ 47da0c48c4Sopenharmony_ci const char *closest_name; 48da0c48c4Sopenharmony_ci GElf_Addr closest_value; 49da0c48c4Sopenharmony_ci GElf_Word closest_shndx; 50da0c48c4Sopenharmony_ci Elf *closest_elf; 51da0c48c4Sopenharmony_ci 52da0c48c4Sopenharmony_ci /* Keep track of an eligible symbol with st_size == 0 as a fallback. */ 53da0c48c4Sopenharmony_ci const char *sizeless_name; 54da0c48c4Sopenharmony_ci GElf_Sym sizeless_sym; 55da0c48c4Sopenharmony_ci GElf_Addr sizeless_value; 56da0c48c4Sopenharmony_ci GElf_Word sizeless_shndx; 57da0c48c4Sopenharmony_ci Elf *sizeless_elf; 58da0c48c4Sopenharmony_ci 59da0c48c4Sopenharmony_ci /* Keep track of the lowest address a relevant sizeless symbol could have. */ 60da0c48c4Sopenharmony_ci GElf_Addr min_label; 61da0c48c4Sopenharmony_ci}; 62da0c48c4Sopenharmony_ci 63da0c48c4Sopenharmony_ci/* Return true iff we consider ADDR to lie in the same section as SYM. */ 64da0c48c4Sopenharmony_cistatic inline bool 65da0c48c4Sopenharmony_cisame_section (struct search_state *state, 66da0c48c4Sopenharmony_ci GElf_Addr value, Elf *symelf, GElf_Word shndx) 67da0c48c4Sopenharmony_ci{ 68da0c48c4Sopenharmony_ci /* For absolute symbols and the like, only match exactly. */ 69da0c48c4Sopenharmony_ci if (shndx >= SHN_LORESERVE) 70da0c48c4Sopenharmony_ci return value == state->addr; 71da0c48c4Sopenharmony_ci 72da0c48c4Sopenharmony_ci /* If value might not be st_value, the shndx of the symbol might 73da0c48c4Sopenharmony_ci not match the section of the value. Explicitly look both up. */ 74da0c48c4Sopenharmony_ci if (! state->adjust_st_value) 75da0c48c4Sopenharmony_ci { 76da0c48c4Sopenharmony_ci Dwarf_Addr v; 77da0c48c4Sopenharmony_ci if (state->addr_shndx == SHN_UNDEF) 78da0c48c4Sopenharmony_ci { 79da0c48c4Sopenharmony_ci v = state->addr; 80da0c48c4Sopenharmony_ci state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v); 81da0c48c4Sopenharmony_ci } 82da0c48c4Sopenharmony_ci 83da0c48c4Sopenharmony_ci v = value; 84da0c48c4Sopenharmony_ci return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v); 85da0c48c4Sopenharmony_ci } 86da0c48c4Sopenharmony_ci 87da0c48c4Sopenharmony_ci /* Figure out what section ADDR lies in. */ 88da0c48c4Sopenharmony_ci if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf) 89da0c48c4Sopenharmony_ci { 90da0c48c4Sopenharmony_ci GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf, 91da0c48c4Sopenharmony_ci state->addr); 92da0c48c4Sopenharmony_ci Elf_Scn *scn = NULL; 93da0c48c4Sopenharmony_ci state->addr_shndx = SHN_ABS; 94da0c48c4Sopenharmony_ci state->addr_symelf = symelf; 95da0c48c4Sopenharmony_ci while ((scn = elf_nextscn (symelf, scn)) != NULL) 96da0c48c4Sopenharmony_ci { 97da0c48c4Sopenharmony_ci GElf_Shdr shdr_mem; 98da0c48c4Sopenharmony_ci GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem); 99da0c48c4Sopenharmony_ci if (likely (shdr != NULL) 100da0c48c4Sopenharmony_ci && mod_addr >= shdr->sh_addr 101da0c48c4Sopenharmony_ci && mod_addr < shdr->sh_addr + shdr->sh_size) 102da0c48c4Sopenharmony_ci { 103da0c48c4Sopenharmony_ci state->addr_shndx = elf_ndxscn (scn); 104da0c48c4Sopenharmony_ci break; 105da0c48c4Sopenharmony_ci } 106da0c48c4Sopenharmony_ci } 107da0c48c4Sopenharmony_ci } 108da0c48c4Sopenharmony_ci 109da0c48c4Sopenharmony_ci return shndx == state->addr_shndx && state->addr_symelf == symelf; 110da0c48c4Sopenharmony_ci} 111da0c48c4Sopenharmony_ci 112da0c48c4Sopenharmony_ci/* Return GELF_ST_BIND as higher-is-better integer. */ 113da0c48c4Sopenharmony_cistatic inline int 114da0c48c4Sopenharmony_cibinding_value (const GElf_Sym *symp) 115da0c48c4Sopenharmony_ci{ 116da0c48c4Sopenharmony_ci switch (GELF_ST_BIND (symp->st_info)) 117da0c48c4Sopenharmony_ci { 118da0c48c4Sopenharmony_ci case STB_GLOBAL: 119da0c48c4Sopenharmony_ci return 3; 120da0c48c4Sopenharmony_ci case STB_WEAK: 121da0c48c4Sopenharmony_ci return 2; 122da0c48c4Sopenharmony_ci case STB_LOCAL: 123da0c48c4Sopenharmony_ci return 1; 124da0c48c4Sopenharmony_ci default: 125da0c48c4Sopenharmony_ci return 0; 126da0c48c4Sopenharmony_ci } 127da0c48c4Sopenharmony_ci} 128da0c48c4Sopenharmony_ci 129da0c48c4Sopenharmony_ci/* Try one symbol and associated value from the search table. */ 130da0c48c4Sopenharmony_cistatic inline void 131da0c48c4Sopenharmony_citry_sym_value (struct search_state *state, 132da0c48c4Sopenharmony_ci GElf_Addr value, GElf_Sym *sym, 133da0c48c4Sopenharmony_ci const char *name, GElf_Word shndx, 134da0c48c4Sopenharmony_ci Elf *elf, bool resolved) 135da0c48c4Sopenharmony_ci{ 136da0c48c4Sopenharmony_ci /* Even if we don't choose this symbol, its existence excludes 137da0c48c4Sopenharmony_ci any sizeless symbol (assembly label) that is below its upper 138da0c48c4Sopenharmony_ci bound. */ 139da0c48c4Sopenharmony_ci if (value + sym->st_size > state->min_label) 140da0c48c4Sopenharmony_ci state->min_label = value + sym->st_size; 141da0c48c4Sopenharmony_ci 142da0c48c4Sopenharmony_ci if (sym->st_size == 0 || state->addr - value < sym->st_size) 143da0c48c4Sopenharmony_ci { 144da0c48c4Sopenharmony_ci /* This symbol is a better candidate than the current one 145da0c48c4Sopenharmony_ci if it's closer to ADDR or is global when it was local. */ 146da0c48c4Sopenharmony_ci if (state->closest_name == NULL 147da0c48c4Sopenharmony_ci || state->closest_value < value 148da0c48c4Sopenharmony_ci || binding_value (state->closest_sym) < binding_value (sym)) 149da0c48c4Sopenharmony_ci { 150da0c48c4Sopenharmony_ci if (sym->st_size != 0) 151da0c48c4Sopenharmony_ci { 152da0c48c4Sopenharmony_ci *state->closest_sym = *sym; 153da0c48c4Sopenharmony_ci state->closest_value = value; 154da0c48c4Sopenharmony_ci state->closest_shndx = shndx; 155da0c48c4Sopenharmony_ci state->closest_elf = elf; 156da0c48c4Sopenharmony_ci state->closest_name = name; 157da0c48c4Sopenharmony_ci } 158da0c48c4Sopenharmony_ci else if (state->closest_name == NULL 159da0c48c4Sopenharmony_ci && value >= state->min_label 160da0c48c4Sopenharmony_ci && same_section (state, value, 161da0c48c4Sopenharmony_ci resolved ? state->mod->main.elf : elf, 162da0c48c4Sopenharmony_ci shndx)) 163da0c48c4Sopenharmony_ci { 164da0c48c4Sopenharmony_ci /* Handwritten assembly symbols sometimes have no 165da0c48c4Sopenharmony_ci st_size. If no symbol with proper size includes 166da0c48c4Sopenharmony_ci the address, we'll use the closest one that is in 167da0c48c4Sopenharmony_ci the same section as ADDR. */ 168da0c48c4Sopenharmony_ci state->sizeless_sym = *sym; 169da0c48c4Sopenharmony_ci state->sizeless_value = value; 170da0c48c4Sopenharmony_ci state->sizeless_shndx = shndx; 171da0c48c4Sopenharmony_ci state->sizeless_elf = elf; 172da0c48c4Sopenharmony_ci state->sizeless_name = name; 173da0c48c4Sopenharmony_ci } 174da0c48c4Sopenharmony_ci } 175da0c48c4Sopenharmony_ci /* When the beginning of its range is no closer, 176da0c48c4Sopenharmony_ci the end of its range might be. Otherwise follow 177da0c48c4Sopenharmony_ci GELF_ST_BIND preference. If all are equal prefer 178da0c48c4Sopenharmony_ci the first symbol found. */ 179da0c48c4Sopenharmony_ci else if (sym->st_size != 0 180da0c48c4Sopenharmony_ci && state->closest_value == value 181da0c48c4Sopenharmony_ci && ((state->closest_sym->st_size > sym->st_size 182da0c48c4Sopenharmony_ci && (binding_value (state->closest_sym) 183da0c48c4Sopenharmony_ci <= binding_value (sym))) 184da0c48c4Sopenharmony_ci || (state->closest_sym->st_size >= sym->st_size 185da0c48c4Sopenharmony_ci && (binding_value (state->closest_sym) 186da0c48c4Sopenharmony_ci < binding_value (sym))))) 187da0c48c4Sopenharmony_ci { 188da0c48c4Sopenharmony_ci *state->closest_sym = *sym; 189da0c48c4Sopenharmony_ci state->closest_value = value; 190da0c48c4Sopenharmony_ci state->closest_shndx = shndx; 191da0c48c4Sopenharmony_ci state->closest_elf = elf; 192da0c48c4Sopenharmony_ci state->closest_name = name; 193da0c48c4Sopenharmony_ci } 194da0c48c4Sopenharmony_ci } 195da0c48c4Sopenharmony_ci} 196da0c48c4Sopenharmony_ci 197da0c48c4Sopenharmony_ci/* Look through the symbol table for a matching symbol. */ 198da0c48c4Sopenharmony_cistatic inline void 199da0c48c4Sopenharmony_cisearch_table (struct search_state *state, int start, int end) 200da0c48c4Sopenharmony_ci{ 201da0c48c4Sopenharmony_ci for (int i = start; i < end; ++i) 202da0c48c4Sopenharmony_ci { 203da0c48c4Sopenharmony_ci GElf_Sym sym; 204da0c48c4Sopenharmony_ci GElf_Addr value; 205da0c48c4Sopenharmony_ci GElf_Word shndx; 206da0c48c4Sopenharmony_ci Elf *elf; 207da0c48c4Sopenharmony_ci bool resolved; 208da0c48c4Sopenharmony_ci const char *name = __libdwfl_getsym (state->mod, i, &sym, &value, 209da0c48c4Sopenharmony_ci &shndx, &elf, NULL, 210da0c48c4Sopenharmony_ci &resolved, 211da0c48c4Sopenharmony_ci state->adjust_st_value); 212da0c48c4Sopenharmony_ci if (name != NULL && name[0] != '\0' 213da0c48c4Sopenharmony_ci && sym.st_shndx != SHN_UNDEF 214da0c48c4Sopenharmony_ci && value <= state->addr 215da0c48c4Sopenharmony_ci && GELF_ST_TYPE (sym.st_info) != STT_SECTION 216da0c48c4Sopenharmony_ci && GELF_ST_TYPE (sym.st_info) != STT_FILE 217da0c48c4Sopenharmony_ci && GELF_ST_TYPE (sym.st_info) != STT_TLS) 218da0c48c4Sopenharmony_ci { 219da0c48c4Sopenharmony_ci try_sym_value (state, value, &sym, name, shndx, elf, resolved); 220da0c48c4Sopenharmony_ci 221da0c48c4Sopenharmony_ci /* If this is an addrinfo variant and the value could be 222da0c48c4Sopenharmony_ci resolved then also try matching the (adjusted) st_value. */ 223da0c48c4Sopenharmony_ci if (resolved && state->mod->e_type != ET_REL) 224da0c48c4Sopenharmony_ci { 225da0c48c4Sopenharmony_ci GElf_Addr adjusted_st_value; 226da0c48c4Sopenharmony_ci adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf, 227da0c48c4Sopenharmony_ci sym.st_value); 228da0c48c4Sopenharmony_ci if (value != adjusted_st_value 229da0c48c4Sopenharmony_ci && adjusted_st_value <= state->addr) 230da0c48c4Sopenharmony_ci try_sym_value (state, adjusted_st_value, &sym, name, shndx, 231da0c48c4Sopenharmony_ci elf, false); 232da0c48c4Sopenharmony_ci } 233da0c48c4Sopenharmony_ci } 234da0c48c4Sopenharmony_ci } 235da0c48c4Sopenharmony_ci} 236da0c48c4Sopenharmony_ci 237da0c48c4Sopenharmony_ci/* Returns the name of the symbol "closest" to ADDR. 238da0c48c4Sopenharmony_ci Never returns symbols at addresses above ADDR. 239da0c48c4Sopenharmony_ci 240da0c48c4Sopenharmony_ci Wrapper for old dwfl_module_addrsym and new dwfl_module_addrinfo. 241da0c48c4Sopenharmony_ci adjust_st_value set to true returns adjusted SYM st_value, set to false 242da0c48c4Sopenharmony_ci it will not adjust SYM at all, but does match against resolved values. */ 243da0c48c4Sopenharmony_cistatic const char * 244da0c48c4Sopenharmony_ci__libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off, 245da0c48c4Sopenharmony_ci GElf_Sym *_closest_sym, GElf_Word *shndxp, 246da0c48c4Sopenharmony_ci Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value) 247da0c48c4Sopenharmony_ci{ 248da0c48c4Sopenharmony_ci int syments = INTUSE(dwfl_module_getsymtab) (_mod); 249da0c48c4Sopenharmony_ci if (syments < 0) 250da0c48c4Sopenharmony_ci return NULL; 251da0c48c4Sopenharmony_ci 252da0c48c4Sopenharmony_ci struct search_state state = 253da0c48c4Sopenharmony_ci { 254da0c48c4Sopenharmony_ci .addr = _addr, 255da0c48c4Sopenharmony_ci .mod = _mod, 256da0c48c4Sopenharmony_ci .closest_sym = _closest_sym, 257da0c48c4Sopenharmony_ci .adjust_st_value = _adjust_st_value, 258da0c48c4Sopenharmony_ci .addr_shndx = SHN_UNDEF, 259da0c48c4Sopenharmony_ci .addr_symelf = NULL, 260da0c48c4Sopenharmony_ci .closest_name = NULL, 261da0c48c4Sopenharmony_ci .closest_value = 0, 262da0c48c4Sopenharmony_ci .closest_shndx = SHN_UNDEF, 263da0c48c4Sopenharmony_ci .closest_elf = NULL, 264da0c48c4Sopenharmony_ci .sizeless_name = NULL, 265da0c48c4Sopenharmony_ci .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF }, 266da0c48c4Sopenharmony_ci .sizeless_value = 0, 267da0c48c4Sopenharmony_ci .sizeless_shndx = SHN_UNDEF, 268da0c48c4Sopenharmony_ci .sizeless_elf = NULL, 269da0c48c4Sopenharmony_ci .min_label = 0 270da0c48c4Sopenharmony_ci }; 271da0c48c4Sopenharmony_ci 272da0c48c4Sopenharmony_ci /* First go through global symbols. mod->first_global and 273da0c48c4Sopenharmony_ci mod->aux_first_global are setup by dwfl_module_getsymtab to the 274da0c48c4Sopenharmony_ci index of the first global symbol in those symbol tables. Both 275da0c48c4Sopenharmony_ci are non-zero when the table exist, except when there is only a 276da0c48c4Sopenharmony_ci dynsym table loaded through phdrs, then first_global is zero and 277da0c48c4Sopenharmony_ci there will be no auxiliary table. All symbols with local binding 278da0c48c4Sopenharmony_ci come first in the symbol table, then all globals. The zeroth, 279da0c48c4Sopenharmony_ci null entry, in the auxiliary table is skipped if there is a main 280da0c48c4Sopenharmony_ci table. */ 281da0c48c4Sopenharmony_ci int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod); 282da0c48c4Sopenharmony_ci if (first_global < 0) 283da0c48c4Sopenharmony_ci return NULL; 284da0c48c4Sopenharmony_ci search_table (&state, first_global == 0 ? 1 : first_global, syments); 285da0c48c4Sopenharmony_ci 286da0c48c4Sopenharmony_ci /* If we found nothing searching the global symbols, then try the locals. 287da0c48c4Sopenharmony_ci Unless we have a global sizeless symbol that matches exactly. */ 288da0c48c4Sopenharmony_ci if (state.closest_name == NULL && first_global > 1 289da0c48c4Sopenharmony_ci && (state.sizeless_name == NULL || state.sizeless_value != state.addr)) 290da0c48c4Sopenharmony_ci search_table (&state, 1, first_global); 291da0c48c4Sopenharmony_ci 292da0c48c4Sopenharmony_ci /* If we found no proper sized symbol to use, fall back to the best 293da0c48c4Sopenharmony_ci candidate sizeless symbol we found, if any. */ 294da0c48c4Sopenharmony_ci if (state.closest_name == NULL 295da0c48c4Sopenharmony_ci && state.sizeless_name != NULL 296da0c48c4Sopenharmony_ci && state.sizeless_value >= state.min_label) 297da0c48c4Sopenharmony_ci { 298da0c48c4Sopenharmony_ci *state.closest_sym = state.sizeless_sym; 299da0c48c4Sopenharmony_ci state.closest_value = state.sizeless_value; 300da0c48c4Sopenharmony_ci state.closest_shndx = state.sizeless_shndx; 301da0c48c4Sopenharmony_ci state.closest_elf = state.sizeless_elf; 302da0c48c4Sopenharmony_ci state.closest_name = state.sizeless_name; 303da0c48c4Sopenharmony_ci } 304da0c48c4Sopenharmony_ci 305da0c48c4Sopenharmony_ci *off = state.addr - state.closest_value; 306da0c48c4Sopenharmony_ci 307da0c48c4Sopenharmony_ci if (shndxp != NULL) 308da0c48c4Sopenharmony_ci *shndxp = state.closest_shndx; 309da0c48c4Sopenharmony_ci if (elfp != NULL) 310da0c48c4Sopenharmony_ci *elfp = state.closest_elf; 311da0c48c4Sopenharmony_ci if (biasp != NULL) 312da0c48c4Sopenharmony_ci *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0); 313da0c48c4Sopenharmony_ci return state.closest_name; 314da0c48c4Sopenharmony_ci} 315da0c48c4Sopenharmony_ci 316da0c48c4Sopenharmony_ci 317da0c48c4Sopenharmony_ciconst char * 318da0c48c4Sopenharmony_cidwfl_module_addrsym (Dwfl_Module *mod, GElf_Addr addr, 319da0c48c4Sopenharmony_ci GElf_Sym *closest_sym, GElf_Word *shndxp) 320da0c48c4Sopenharmony_ci{ 321da0c48c4Sopenharmony_ci GElf_Off off; 322da0c48c4Sopenharmony_ci return __libdwfl_addrsym (mod, addr, &off, closest_sym, shndxp, 323da0c48c4Sopenharmony_ci NULL, NULL, true); 324da0c48c4Sopenharmony_ci} 325da0c48c4Sopenharmony_ciINTDEF (dwfl_module_addrsym) 326da0c48c4Sopenharmony_ci 327da0c48c4Sopenharmony_ciconst char 328da0c48c4Sopenharmony_ci*dwfl_module_addrinfo (Dwfl_Module *mod, GElf_Addr address, 329da0c48c4Sopenharmony_ci GElf_Off *offset, GElf_Sym *sym, 330da0c48c4Sopenharmony_ci GElf_Word *shndxp, Elf **elfp, Dwarf_Addr *bias) 331da0c48c4Sopenharmony_ci{ 332da0c48c4Sopenharmony_ci return __libdwfl_addrsym (mod, address, offset, sym, shndxp, elfp, bias, 333da0c48c4Sopenharmony_ci false); 334da0c48c4Sopenharmony_ci} 335da0c48c4Sopenharmony_ciINTDEF (dwfl_module_addrinfo) 336