#include "libdwflP.h"
-/* Returns the name of the symbol "closest" to ADDR.
- Never returns symbols at addresses above ADDR. */
-
-const char *
-internal_function
-__libdwfl_addrsym (Dwfl_Module *mod, GElf_Addr addr, GElf_Off *off,
- GElf_Sym *closest_sym, GElf_Word *shndxp,
- Elf **elfp, Dwarf_Addr *biasp, bool adjust_st_value)
+struct search_state
{
- int syments = INTUSE(dwfl_module_getsymtab) (mod);
- if (syments < 0)
- return NULL;
+ Dwfl_Module *mod;
+ GElf_Addr addr;
- /* Return true iff we consider ADDR to lie in the same section as SYM. */
- GElf_Word addr_shndx = SHN_UNDEF;
- Elf *addr_symelf = NULL;
- inline bool same_section (GElf_Addr value, Elf *symelf, GElf_Word shndx)
- {
- /* For absolute symbols and the like, only match exactly. */
- if (shndx >= SHN_LORESERVE)
- return value == addr;
+ GElf_Sym *closest_sym;
+ bool adjust_st_value;
+ GElf_Word addr_shndx;
+ Elf *addr_symelf;
- /* If value might not be st_value, the shndx of the symbol might
- not match the section of the value. Explicitly look both up. */
- if (! adjust_st_value)
- {
- Dwarf_Addr v;
- if (addr_shndx == SHN_UNDEF)
- {
- v = addr;
- addr_shndx = __libdwfl_find_section_ndx (mod, &v);
- }
+ /* Keep track of the closest symbol we have seen so far.
+ Here we store only symbols with nonzero st_size. */
+ const char *closest_name;
+ GElf_Addr closest_value;
+ GElf_Word closest_shndx;
+ Elf *closest_elf;
- v = value;
- return addr_shndx == __libdwfl_find_section_ndx (mod, &v);
- }
+ /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
+ const char *sizeless_name;
+ GElf_Sym sizeless_sym;
+ GElf_Addr sizeless_value;
+ GElf_Word sizeless_shndx;
+ Elf *sizeless_elf;
- /* Figure out what section ADDR lies in. */
- if (addr_shndx == SHN_UNDEF || addr_symelf != symelf)
- {
- GElf_Addr mod_addr = dwfl_deadjust_st_value (mod, symelf, addr);
- Elf_Scn *scn = NULL;
- addr_shndx = SHN_ABS;
- addr_symelf = symelf;
- while ((scn = elf_nextscn (symelf, scn)) != NULL)
- {
- GElf_Shdr shdr_mem;
- GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
- if (likely (shdr != NULL)
- && mod_addr >= shdr->sh_addr
- && mod_addr < shdr->sh_addr + shdr->sh_size)
- {
- addr_shndx = elf_ndxscn (scn);
- break;
- }
- }
- }
+ /* Keep track of the lowest address a relevant sizeless symbol could have. */
+ GElf_Addr min_label;
+};
- return shndx == addr_shndx && addr_symelf == symelf;
+/* Return true iff we consider ADDR to lie in the same section as SYM. */
+static inline bool
+same_section (struct search_state *state,
+ GElf_Addr value, Elf *symelf, GElf_Word shndx)
+{
+ /* For absolute symbols and the like, only match exactly. */
+ if (shndx >= SHN_LORESERVE)
+ return value == state->addr;
+
+ /* If value might not be st_value, the shndx of the symbol might
+ not match the section of the value. Explicitly look both up. */
+ if (! state->adjust_st_value)
+ {
+ Dwarf_Addr v;
+ if (state->addr_shndx == SHN_UNDEF)
+ {
+ v = state->addr;
+ state->addr_shndx = __libdwfl_find_section_ndx (state->mod, &v);
+ }
+
+ v = value;
+ return state->addr_shndx == __libdwfl_find_section_ndx (state->mod, &v);
}
- /* Keep track of the closest symbol we have seen so far.
- Here we store only symbols with nonzero st_size. */
- const char *closest_name = NULL;
- GElf_Addr closest_value = 0;
- GElf_Word closest_shndx = SHN_UNDEF;
- Elf *closest_elf = NULL;
+ /* Figure out what section ADDR lies in. */
+ if (state->addr_shndx == SHN_UNDEF || state->addr_symelf != symelf)
+ {
+ GElf_Addr mod_addr = dwfl_deadjust_st_value (state->mod, symelf,
+ state->addr);
+ Elf_Scn *scn = NULL;
+ state->addr_shndx = SHN_ABS;
+ state->addr_symelf = symelf;
+ while ((scn = elf_nextscn (symelf, scn)) != NULL)
+ {
+ GElf_Shdr shdr_mem;
+ GElf_Shdr *shdr = gelf_getshdr (scn, &shdr_mem);
+ if (likely (shdr != NULL)
+ && mod_addr >= shdr->sh_addr
+ && mod_addr < shdr->sh_addr + shdr->sh_size)
+ {
+ state->addr_shndx = elf_ndxscn (scn);
+ break;
+ }
+ }
+ }
- /* Keep track of an eligible symbol with st_size == 0 as a fallback. */
- const char *sizeless_name = NULL;
- GElf_Sym sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF };
- GElf_Addr sizeless_value = 0;
- GElf_Word sizeless_shndx = SHN_UNDEF;
- Elf *sizeless_elf = NULL;
+ return shndx == state->addr_shndx && state->addr_symelf == symelf;
+}
- /* Keep track of the lowest address a relevant sizeless symbol could have. */
- GElf_Addr min_label = 0;
+/* Return GELF_ST_BIND as higher-is-better integer. */
+static inline int
+binding_value (const GElf_Sym *symp)
+{
+ switch (GELF_ST_BIND (symp->st_info))
+ {
+ case STB_GLOBAL:
+ return 3;
+ case STB_WEAK:
+ return 2;
+ case STB_LOCAL:
+ return 1;
+ default:
+ return 0;
+ }
+}
- /* Try one symbol and associated value from the search table. */
- inline void try_sym_value (GElf_Addr value, GElf_Sym *sym,
- const char *name, GElf_Word shndx,
- Elf *elf, bool resolved)
- {
+/* Try one symbol and associated value from the search table. */
+static inline void
+try_sym_value (struct search_state *state,
+ GElf_Addr value, GElf_Sym *sym,
+ const char *name, GElf_Word shndx,
+ Elf *elf, bool resolved)
+{
/* Even if we don't choose this symbol, its existence excludes
any sizeless symbol (assembly label) that is below its upper
bound. */
- if (value + sym->st_size > min_label)
- min_label = value + sym->st_size;
+ if (value + sym->st_size > state->min_label)
+ state->min_label = value + sym->st_size;
- if (sym->st_size == 0 || addr - value < sym->st_size)
+ if (sym->st_size == 0 || state->addr - value < sym->st_size)
{
- /* Return GELF_ST_BIND as higher-is-better integer. */
- inline int binding_value (const GElf_Sym *symp)
- {
- switch (GELF_ST_BIND (symp->st_info))
- {
- case STB_GLOBAL:
- return 3;
- case STB_WEAK:
- return 2;
- case STB_LOCAL:
- return 1;
- default:
- return 0;
- }
- }
-
/* This symbol is a better candidate than the current one
if it's closer to ADDR or is global when it was local. */
- if (closest_name == NULL
- || closest_value < value
- || binding_value (closest_sym) < binding_value (sym))
+ if (state->closest_name == NULL
+ || state->closest_value < value
+ || binding_value (state->closest_sym) < binding_value (sym))
{
if (sym->st_size != 0)
{
- *closest_sym = *sym;
- closest_value = value;
- closest_shndx = shndx;
- closest_elf = elf;
- closest_name = name;
+ *state->closest_sym = *sym;
+ state->closest_value = value;
+ state->closest_shndx = shndx;
+ state->closest_elf = elf;
+ state->closest_name = name;
}
- else if (closest_name == NULL
- && value >= min_label
- && same_section (value,
- resolved ? mod->main.elf : elf, shndx))
+ else if (state->closest_name == NULL
+ && value >= state->min_label
+ && same_section (state, value,
+ resolved ? state->mod->main.elf : elf,
+ shndx))
{
/* Handwritten assembly symbols sometimes have no
st_size. If no symbol with proper size includes
the address, we'll use the closest one that is in
the same section as ADDR. */
- sizeless_sym = *sym;
- sizeless_value = value;
- sizeless_shndx = shndx;
- sizeless_elf = elf;
- sizeless_name = name;
+ state->sizeless_sym = *sym;
+ state->sizeless_value = value;
+ state->sizeless_shndx = shndx;
+ state->sizeless_elf = elf;
+ state->sizeless_name = name;
}
}
/* When the beginning of its range is no closer,
GELF_ST_BIND preference. If all are equal prefer
the first symbol found. */
else if (sym->st_size != 0
- && closest_value == value
- && ((closest_sym->st_size > sym->st_size
- && (binding_value (closest_sym)
+ && state->closest_value == value
+ && ((state->closest_sym->st_size > sym->st_size
+ && (binding_value (state->closest_sym)
<= binding_value (sym)))
- || (closest_sym->st_size >= sym->st_size
- && (binding_value (closest_sym)
+ || (state->closest_sym->st_size >= sym->st_size
+ && (binding_value (state->closest_sym)
< binding_value (sym)))))
{
- *closest_sym = *sym;
- closest_value = value;
- closest_shndx = shndx;
- closest_elf = elf;
- closest_name = name;
+ *state->closest_sym = *sym;
+ state->closest_value = value;
+ state->closest_shndx = shndx;
+ state->closest_elf = elf;
+ state->closest_name = name;
}
}
- }
+}
- /* Look through the symbol table for a matching symbol. */
- inline void search_table (int start, int end)
- {
+/* Look through the symbol table for a matching symbol. */
+static inline void
+search_table (struct search_state *state, int start, int end)
+{
for (int i = start; i < end; ++i)
{
GElf_Sym sym;
GElf_Word shndx;
Elf *elf;
bool resolved;
- const char *name = __libdwfl_getsym (mod, i, &sym, &value,
+ const char *name = __libdwfl_getsym (state->mod, i, &sym, &value,
&shndx, &elf, NULL,
- &resolved, adjust_st_value);
+ &resolved,
+ state->adjust_st_value);
if (name != NULL && name[0] != '\0'
&& sym.st_shndx != SHN_UNDEF
- && value <= addr
+ && value <= state->addr
&& GELF_ST_TYPE (sym.st_info) != STT_SECTION
&& GELF_ST_TYPE (sym.st_info) != STT_FILE
&& GELF_ST_TYPE (sym.st_info) != STT_TLS)
{
- try_sym_value (value, &sym, name, shndx, elf, resolved);
+ try_sym_value (state, value, &sym, name, shndx, elf, resolved);
/* If this is an addrinfo variant and the value could be
resolved then also try matching the (adjusted) st_value. */
- if (resolved && mod->e_type != ET_REL)
+ if (resolved && state->mod->e_type != ET_REL)
{
GElf_Addr adjusted_st_value;
- adjusted_st_value = dwfl_adjusted_st_value (mod, elf,
+ adjusted_st_value = dwfl_adjusted_st_value (state->mod, elf,
sym.st_value);
- if (value != adjusted_st_value && adjusted_st_value <= addr)
- try_sym_value (adjusted_st_value, &sym, name, shndx,
+ if (value != adjusted_st_value
+ && adjusted_st_value <= state->addr)
+ try_sym_value (state, adjusted_st_value, &sym, name, shndx,
elf, false);
}
}
}
- }
+}
+
+/* Returns the name of the symbol "closest" to ADDR.
+ Never returns symbols at addresses above ADDR. */
+const char *
+internal_function
+__libdwfl_addrsym (Dwfl_Module *_mod, GElf_Addr _addr, GElf_Off *off,
+ GElf_Sym *_closest_sym, GElf_Word *shndxp,
+ Elf **elfp, Dwarf_Addr *biasp, bool _adjust_st_value)
+{
+ int syments = INTUSE(dwfl_module_getsymtab) (_mod);
+ if (syments < 0)
+ return NULL;
+
+ struct search_state state =
+ {
+ .addr = _addr,
+ .mod = _mod,
+ .closest_sym = _closest_sym,
+ .adjust_st_value = _adjust_st_value,
+ .addr_shndx = SHN_UNDEF,
+ .addr_symelf = NULL,
+ .closest_name = NULL,
+ .closest_value = 0,
+ .closest_shndx = SHN_UNDEF,
+ .closest_elf = NULL,
+ .sizeless_name = NULL,
+ .sizeless_sym = { 0, 0, 0, 0, 0, SHN_UNDEF },
+ .sizeless_value = 0,
+ .sizeless_shndx = SHN_UNDEF,
+ .sizeless_elf = NULL,
+ .min_label = 0
+ };
/* First go through global symbols. mod->first_global and
mod->aux_first_global are setup by dwfl_module_getsymtab to the
come first in the symbol table, then all globals. The zeroth,
null entry, in the auxiliary table is skipped if there is a main
table. */
- int first_global = INTUSE (dwfl_module_getsymtab_first_global) (mod);
+ int first_global = INTUSE (dwfl_module_getsymtab_first_global) (state.mod);
if (first_global < 0)
return NULL;
- search_table (first_global == 0 ? 1 : first_global, syments);
+ search_table (&state, first_global == 0 ? 1 : first_global, syments);
/* If we found nothing searching the global symbols, then try the locals.
Unless we have a global sizeless symbol that matches exactly. */
- if (closest_name == NULL && first_global > 1
- && (sizeless_name == NULL || sizeless_value != addr))
- search_table (1, first_global);
+ if (state.closest_name == NULL && first_global > 1
+ && (state.sizeless_name == NULL || state.sizeless_value != state.addr))
+ search_table (&state, 1, first_global);
/* If we found no proper sized symbol to use, fall back to the best
candidate sizeless symbol we found, if any. */
- if (closest_name == NULL
- && sizeless_name != NULL && sizeless_value >= min_label)
+ if (state.closest_name == NULL
+ && state.sizeless_name != NULL
+ && state.sizeless_value >= state.min_label)
{
- *closest_sym = sizeless_sym;
- closest_value = sizeless_value;
- closest_shndx = sizeless_shndx;
- closest_elf = sizeless_elf;
- closest_name = sizeless_name;
+ *state.closest_sym = state.sizeless_sym;
+ state.closest_value = state.sizeless_value;
+ state.closest_shndx = state.sizeless_shndx;
+ state.closest_elf = state.sizeless_elf;
+ state.closest_name = state.sizeless_name;
}
- *off = addr - closest_value;
+ *off = state.addr - state.closest_value;
if (shndxp != NULL)
- *shndxp = closest_shndx;
+ *shndxp = state.closest_shndx;
if (elfp != NULL)
- *elfp = closest_elf;
+ *elfp = state.closest_elf;
if (biasp != NULL)
- *biasp = dwfl_adjusted_st_value (mod, closest_elf, 0);
- return closest_name;
+ *biasp = dwfl_adjusted_st_value (state.mod, state.closest_elf, 0);
+ return state.closest_name;
}