summaryrefslogtreecommitdiff
path: root/usr/src/lib/libproc/common/Psymtab.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/lib/libproc/common/Psymtab.c')
-rw-r--r--usr/src/lib/libproc/common/Psymtab.c215
1 files changed, 179 insertions, 36 deletions
diff --git a/usr/src/lib/libproc/common/Psymtab.c b/usr/src/lib/libproc/common/Psymtab.c
index 35107c546b..0b98622759 100644
--- a/usr/src/lib/libproc/common/Psymtab.c
+++ b/usr/src/lib/libproc/common/Psymtab.c
@@ -171,13 +171,13 @@ get_saddrs(struct ps_prochandle *P, uintptr_t ehdr_start, uint_t *n)
/*
* Allocation function for a new file_info_t
*/
-static file_info_t *
+file_info_t *
file_info_new(struct ps_prochandle *P, map_info_t *mptr)
{
file_info_t *fptr;
map_info_t *mp;
- uintptr_t addr;
- uint_t i, j;
+ uintptr_t mstart, mend, sstart, send;
+ uint_t i;
if ((fptr = calloc(1, sizeof (file_info_t))) == NULL)
return (NULL);
@@ -201,22 +201,38 @@ file_info_new(struct ps_prochandle *P, map_info_t *mptr)
&fptr->file_nsaddrs)) == NULL)
return (fptr);
- i = j = 0;
mp = P->mappings;
- while (j < P->map_count && i < fptr->file_nsaddrs) {
- addr = fptr->file_saddrs[i];
- if (addr >= mp->map_pmap.pr_vaddr &&
- addr < mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size &&
- mp->map_file == NULL) {
- mp->map_file = fptr;
- fptr->file_ref++;
- }
+ i = 0;
+ while (mp < P->mappings + P->map_count && i < fptr->file_nsaddrs) {
- if (addr < mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size) {
- i++;
+ /* Calculate the start and end of the mapping and section */
+ mstart = mp->map_pmap.pr_vaddr;
+ mend = mp->map_pmap.pr_vaddr + mp->map_pmap.pr_size;
+ sstart = fptr->file_saddrs[i];
+ send = fptr->file_saddrs[i + 1];
+
+ if (mend <= sstart) {
+ /* This mapping is below the current section */
+ mp++;
+ } else if (mstart >= send) {
+ /* This mapping is above the current section */
+ i += 2;
} else {
+ /* This mapping overlaps the current section */
+ if (mp->map_file == NULL) {
+ dprintf("file_info_new: associating "
+ "segment at %p\n",
+ (void *)mp->map_pmap.pr_vaddr);
+ mp->map_file = fptr;
+ fptr->file_ref++;
+ } else {
+ dprintf("file_info_new: segment at %p "
+ "already associated with %s\n",
+ (void *)mp->map_pmap.pr_vaddr,
+ (mp == mptr ? "this file" :
+ mp->map_file->file_pname));
+ }
mp++;
- j++;
}
}
@@ -596,9 +612,17 @@ Paddr_to_text_map(struct ps_prochandle *P, uintptr_t addr)
file_info_t *fptr = build_map_symtab(P, mptr);
const prmap_t *pmp = &mptr->map_pmap;
+ /*
+ * Assume that if rl_data_base is NULL, it means that no
+ * data section was found for this load object, and that
+ * a section must be text. Otherwise, a section will be
+ * text unless it ends above the start of the data
+ * section.
+ */
if (fptr != NULL && fptr->file_lo != NULL &&
- fptr->file_lo->rl_base >= pmp->pr_vaddr &&
- fptr->file_lo->rl_base < pmp->pr_vaddr + pmp->pr_size)
+ (fptr->file_lo->rl_data_base == NULL ||
+ pmp->pr_vaddr + pmp->pr_size <
+ fptr->file_lo->rl_data_base))
return (pmp);
}
@@ -906,6 +930,7 @@ is_mapping_in_file(struct ps_prochandle *P, map_info_t *mptr, file_info_t *fptr)
prmap_t *pmap = &mptr->map_pmap;
rd_loadobj_t *lop = fptr->file_lo;
uint_t i;
+ uintptr_t mstart, mend, sstart, send;
/*
* We can get for free the start address of the text and data
@@ -927,7 +952,7 @@ is_mapping_in_file(struct ps_prochandle *P, map_info_t *mptr, file_info_t *fptr)
* only one will be seen to enclose that section's start address.
* Thus, to be rigorous, we ask not whether this mapping encloses
* the start of a section, but whether there exists a section that
- * encloses the start of this mapping.
+ * overlaps this mapping.
*
* If we don't already have the section addresses, and we successfully
* get them, then we cache them in case we come here again.
@@ -936,10 +961,14 @@ is_mapping_in_file(struct ps_prochandle *P, map_info_t *mptr, file_info_t *fptr)
(fptr->file_saddrs = get_saddrs(P,
fptr->file_map->map_pmap.pr_vaddr, &fptr->file_nsaddrs)) == NULL)
return (0);
+
+ mstart = mptr->map_pmap.pr_vaddr;
+ mend = mptr->map_pmap.pr_vaddr + mptr->map_pmap.pr_size;
for (i = 0; i < fptr->file_nsaddrs; i += 2) {
- /* Does this section enclose the start of the mapping? */
- if (fptr->file_saddrs[i] <= pmap->pr_vaddr &&
- fptr->file_saddrs[i + 1] > pmap->pr_vaddr)
+ /* Does this section overlap the mapping? */
+ sstart = fptr->file_saddrs[i];
+ send = fptr->file_saddrs[i + 1];
+ if (!(mend <= sstart || mstart >= send))
return (1);
}
@@ -1513,18 +1542,31 @@ optimize_symtab(sym_tbl_t *symtab)
}
/*
- * Sort the two tables according to the appropriate criteria.
+ * Sort the two tables according to the appropriate criteria,
+ * unless the user has overridden this behaviour.
+ *
+ * An example where we might not sort the tables is the relatively
+ * unusual case of a process with very large symbol tables in which
+ * we perform few lookups. In such a case the total time would be
+ * dominated by the sort. It is difficult to determine a priori
+ * how many lookups an arbitrary client will perform, and
+ * hence whether the symbol tables should be sorted. We therefore
+ * sort the tables by default, but provide the user with a
+ * "chicken switch" in the form of the LIBPROC_NO_QSORT
+ * environment variable.
*/
- (void) mutex_lock(&sort_mtx);
- sort_strs = symtab->sym_strs;
- sort_syms = syms;
+ if (!_libproc_no_qsort) {
+ (void) mutex_lock(&sort_mtx);
+ sort_strs = symtab->sym_strs;
+ sort_syms = syms;
- qsort(symtab->sym_byaddr, count, sizeof (uint_t), byaddr_cmp);
- qsort(symtab->sym_byname, count, sizeof (uint_t), byname_cmp);
+ qsort(symtab->sym_byaddr, count, sizeof (uint_t), byaddr_cmp);
+ qsort(symtab->sym_byname, count, sizeof (uint_t), byname_cmp);
- sort_strs = NULL;
- sort_syms = NULL;
- (void) mutex_unlock(&sort_mtx);
+ sort_strs = NULL;
+ sort_syms = NULL;
+ (void) mutex_unlock(&sort_mtx);
+ }
free(syms);
}
@@ -2084,12 +2126,11 @@ sym_prefer(GElf_Sym *sym1, char *name1, GElf_Sym *sym2, char *name2)
}
/*
- * Look up a symbol by address in the specified symbol table.
- * Adjustment to 'addr' must already have been made for the
- * offset of the symbol if this is a dynamic library symbol table.
+ * Use a binary search to do the work of sym_by_addr().
*/
static GElf_Sym *
-sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
+sym_by_addr_binary(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp,
+ uint_t *idp)
{
GElf_Sym sym, osym;
uint_t i, oid, *byaddr = symtab->sym_byaddr;
@@ -2154,10 +2195,70 @@ sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
}
/*
- * Look up a symbol by name in the specified symbol table.
+ * Use a linear search to do the work of sym_by_addr().
*/
static GElf_Sym *
-sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
+sym_by_addr_linear(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symbolp,
+ uint_t *idp)
+{
+ size_t symn = symtab->sym_symn;
+ char *strs = symtab->sym_strs;
+ GElf_Sym sym, *symp = NULL;
+ GElf_Sym osym, *osymp = NULL;
+ int i, id;
+
+ if (symtab->sym_data_pri == NULL || symn == 0 || strs == NULL)
+ return (NULL);
+
+ for (i = 0; i < symn; i++) {
+ if ((symp = symtab_getsym(symtab, i, &sym)) != NULL) {
+ if (addr >= sym.st_value &&
+ addr < sym.st_value + sym.st_size) {
+ if (osymp)
+ symp = sym_prefer(
+ symp, strs + symp->st_name,
+ osymp, strs + osymp->st_name);
+ if (symp != osymp) {
+ osym = sym;
+ osymp = &osym;
+ id = i;
+ }
+ }
+ }
+ }
+ if (osymp) {
+ *symbolp = osym;
+ if (idp)
+ *idp = id;
+ return (symbolp);
+ }
+ return (NULL);
+}
+
+/*
+ * Look up a symbol by address in the specified symbol table.
+ * Adjustment to 'addr' must already have been made for the
+ * offset of the symbol if this is a dynamic library symbol table.
+ *
+ * Use a linear or a binary search depending on whether or not we
+ * chose to sort the table in optimize_symtab().
+ */
+static GElf_Sym *
+sym_by_addr(sym_tbl_t *symtab, GElf_Addr addr, GElf_Sym *symp, uint_t *idp)
+{
+ if (_libproc_no_qsort) {
+ return (sym_by_addr_linear(symtab, addr, symp, idp));
+ } else {
+ return (sym_by_addr_binary(symtab, addr, symp, idp));
+ }
+}
+
+/*
+ * Use a binary search to do the work of sym_by_name().
+ */
+static GElf_Sym *
+sym_by_name_binary(sym_tbl_t *symtab, const char *name, GElf_Sym *symp,
+ uint_t *idp)
{
char *strs = symtab->sym_strs;
uint_t i, *byname = symtab->sym_byname;
@@ -2192,6 +2293,48 @@ sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
}
/*
+ * Use a linear search to do the work of sym_by_name().
+ */
+static GElf_Sym *
+sym_by_name_linear(sym_tbl_t *symtab, const char *name, GElf_Sym *symp,
+ uint_t *idp)
+{
+ size_t symn = symtab->sym_symn;
+ char *strs = symtab->sym_strs;
+ int i;
+
+ if (symtab->sym_data_pri == NULL || symn == 0 || strs == NULL)
+ return (NULL);
+
+ for (i = 0; i < symn; i++) {
+ if (symtab_getsym(symtab, i, symp) &&
+ strcmp(name, strs + symp->st_name) == 0) {
+ if (idp)
+ *idp = i;
+ return (symp);
+ }
+ }
+
+ return (NULL);
+}
+
+/*
+ * Look up a symbol by name in the specified symbol table.
+ *
+ * Use a linear or a binary search depending on whether or not we
+ * chose to sort the table in optimize_symtab().
+ */
+static GElf_Sym *
+sym_by_name(sym_tbl_t *symtab, const char *name, GElf_Sym *symp, uint_t *idp)
+{
+ if (_libproc_no_qsort) {
+ return (sym_by_name_linear(symtab, name, symp, idp));
+ } else {
+ return (sym_by_name_binary(symtab, name, symp, idp));
+ }
+}
+
+/*
* Search the process symbol tables looking for a symbol whose
* value to value+size contain the address specified by addr.
* Return values are: