Author: markj
Date: Tue Nov 21 16:03:21 2017
New Revision: 326064
URL: https://svnweb.freebsd.org/changeset/base/326064

Log:
  Refine symtab sorting in libproc.
  
  Add some rules to more closely match what illumos does when an address
  resolves to multiple symbols:
  - prefer non-local symbols
  - prefer symbols with fewer leading underscores and no leading '$'
  
  Add some regression tests to verify these rules.

Modified:
  head/lib/libproc/proc_sym.c
  head/lib/libproc/tests/proc_test.c
  head/lib/libproc/tests/target_prog.c

Modified: head/lib/libproc/proc_sym.c
==============================================================================
--- head/lib/libproc/proc_sym.c Tue Nov 21 16:00:18 2017        (r326063)
+++ head/lib/libproc/proc_sym.c Tue Nov 21 16:03:21 2017        (r326064)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2016 Mark Johnston <ma...@freebsd.org>
+ * Copyright (c) 2016-2017 Mark Johnston <ma...@freebsd.org>
  * Copyright (c) 2010 The FreeBSD Foundation
  * Copyright (c) 2008 John Birrell (j...@freebsd.org)
  * All rights reserved.
@@ -100,27 +100,59 @@ fail:
        strlcpy(buf, symbol, len);
 }
 
+struct symsort_thunk {
+       Elf *e;
+       struct symtab *symtab;
+};
+
 static int
-symvalcomp(void *thunk, const void *a1, const void *a2)
+symvalcmp(void *_thunk, const void *a1, const void *a2)
 {
-       struct symtab *symtab;
        GElf_Sym sym1, sym2;
+       struct symsort_thunk *thunk;
+       const char *s1, *s2;
        u_int i1, i2;
-       int ret;
+       int bind1, bind2;
 
        i1 = *(const u_int *)a1;
        i2 = *(const u_int *)a2;
-       symtab = thunk;
+       thunk = _thunk;
 
-       (void)gelf_getsym(symtab->data, i1, &sym1);
-       (void)gelf_getsym(symtab->data, i2, &sym2);
-       if (sym1.st_value < sym2.st_value)
-               ret = -1;
-       else if (sym1.st_value == sym2.st_value)
-               ret = 0;
-       else
-               ret = 1;
-       return (ret);
+       (void)gelf_getsym(thunk->symtab->data, i1, &sym1);
+       (void)gelf_getsym(thunk->symtab->data, i2, &sym2);
+
+       if (sym1.st_value != sym2.st_value)
+               return (sym1.st_value < sym2.st_value ? -1 : 1);
+
+       /* Prefer non-local symbols. */
+       bind1 = GELF_ST_BIND(sym1.st_info);
+       bind2 = GELF_ST_BIND(sym2.st_info);
+       if (bind1 != bind2) {
+               if (bind1 == STB_LOCAL && bind2 != STB_LOCAL)
+                       return (-1);
+               if (bind1 != STB_LOCAL && bind2 == STB_LOCAL)
+                       return (1);
+       }
+
+       s1 = elf_strptr(thunk->e, thunk->symtab->stridx, sym1.st_name);
+       s2 = elf_strptr(thunk->e, thunk->symtab->stridx, sym2.st_name);
+       if (s1 != NULL && s2 != NULL) {
+               /* Prefer symbols without a leading '$'. */
+               if (*s1 == '$')
+                       return (-1);
+               if (*s2 == '$')
+                       return (1);
+
+               /* Prefer symbols with fewer leading underscores. */
+               for (; *s1 == '_' && *s2 == '_'; s1++, s2++)
+                       ;
+               if (*s1 == '_')
+                       return (-1);
+               if (*s2 == '_')
+                       return (1);
+       }
+
+       return (0);
 }
 
 static int
@@ -128,6 +160,7 @@ load_symtab(Elf *e, struct symtab *symtab, u_long sh_t
 {
        GElf_Ehdr ehdr;
        GElf_Shdr shdr;
+       struct symsort_thunk thunk;
        Elf_Scn *scn;
        u_int nsyms;
 
@@ -155,9 +188,13 @@ load_symtab(Elf *e, struct symtab *symtab, u_long sh_t
                return (-1);
        for (u_int i = 0; i < nsyms; i++)
                symtab->index[i] = i;
-       qsort_r(symtab->index, nsyms, sizeof(u_int), symtab, symvalcomp);
        symtab->nsyms = nsyms;
        symtab->stridx = shdr.sh_link;
+
+       thunk.e = e;
+       thunk.symtab = symtab;
+       qsort_r(symtab->index, nsyms, sizeof(u_int), &thunk, symvalcmp);
+
        return (0);
 }
 
@@ -416,34 +453,48 @@ proc_addr2map(struct proc_handle *p, uintptr_t addr)
  * symbol and its name.
  */
 static int
-lookup_symbol_by_addr(Elf *elf, struct symtab *symtab, uintptr_t addr,
-    const char **namep, GElf_Sym *sym)
+lookup_symbol_by_addr(Elf *e, struct symtab *symtab, uintptr_t addr,
+    const char **namep, GElf_Sym *symp)
 {
+       GElf_Sym sym;
        Elf_Data *data;
        const char *s;
-       int min, max, mid;
+       u_int i, min, max, mid;
 
+       if (symtab->nsyms == 0)
+               return (ENOENT);
+
        data = symtab->data;
        min = 0;
        max = symtab->nsyms - 1;
 
        while (min <= max) {
                mid = (max + min) / 2;
-               (void)gelf_getsym(data, symtab->index[mid], sym);
-               if (addr >= sym->st_value &&
-                   addr < sym->st_value + sym->st_size) {
-                       s = elf_strptr(elf, symtab->stridx, sym->st_name);
-                       if (s != NULL && namep != NULL)
-                               *namep = s;
-                       return (0);
-               }
+               (void)gelf_getsym(data, symtab->index[mid], &sym);
+               if (addr >= sym.st_value && addr < sym.st_value + sym.st_size)
+                       break;
 
-               if (addr < sym->st_value)
+               if (addr < sym.st_value)
                        max = mid - 1;
                else
                        min = mid + 1;
        }
-       return (ENOENT);
+       if (min > max)
+               return (ENOENT);
+
+       /*
+        * Advance until we find the matching symbol with largest index.
+        */
+       for (i = mid; i < symtab->nsyms; i++) {
+               (void)gelf_getsym(data, symtab->index[i], &sym);
+               if (addr < sym.st_value || addr >= sym.st_value + sym.st_size)
+                       break;
+       }
+       (void)gelf_getsym(data, symtab->index[i - 1], symp);
+       s = elf_strptr(e, symtab->stridx, symp->st_name);
+       if (s != NULL && namep != NULL)
+               *namep = s;
+       return (0);
 }
 
 int

Modified: head/lib/libproc/tests/proc_test.c
==============================================================================
--- head/lib/libproc/tests/proc_test.c  Tue Nov 21 16:00:18 2017        
(r326063)
+++ head/lib/libproc/tests/proc_test.c  Tue Nov 21 16:03:21 2017        
(r326064)
@@ -1,5 +1,5 @@
 /*-
- * Copyright (c) 2014-2016 Mark Johnston <ma...@freebsd.org>
+ * Copyright (c) 2014-2017 Mark Johnston <ma...@freebsd.org>
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
@@ -370,6 +370,93 @@ ATF_TC_BODY(signal_forward, tc)
        proc_free(phdl);
 }
 
+ATF_TC(symbol_sort_local);
+ATF_TC_HEAD(symbol_sort_local, tc)
+{
+       atf_tc_set_md_var(tc, "descr",
+           "Ensure that proc_addr2sym() returns the non-local alias when "
+           "the address resolves to multiple symbols.");
+}
+ATF_TC_BODY(symbol_sort_local, tc)
+{
+       char symname[32];
+       GElf_Sym bar_sym;
+       struct proc_handle *phdl;
+       int error;
+
+       phdl = start_prog(tc, true);
+
+       error = proc_name2sym(phdl, target_prog_file, "bar", &bar_sym, NULL);
+       ATF_REQUIRE_MSG(error == 0, "failed to look up 'bar' in %s",
+           target_prog_file);
+       ATF_REQUIRE(GELF_ST_BIND(bar_sym.st_info) == STB_LOCAL);
+
+       error = proc_addr2sym(phdl, bar_sym.st_value, symname, sizeof(symname),
+           &bar_sym);
+       ATF_REQUIRE_MSG(error == 0, "failed to resolve 'bar' by addr");
+
+       ATF_REQUIRE_MSG(strcmp(symname, "baz") == 0,
+           "unexpected symbol name '%s'", symname);
+       ATF_REQUIRE(GELF_ST_BIND(bar_sym.st_info) == STB_GLOBAL);
+}
+
+ATF_TC(symbol_sort_prefix);
+ATF_TC_HEAD(symbol_sort_prefix, tc)
+{
+       atf_tc_set_md_var(tc, "descr",
+           "Ensure that proc_addr2sym() returns the alias whose name is not "
+           "prefixed with '$' if one exists.");
+}
+ATF_TC_BODY(symbol_sort_prefix, tc)
+{
+       char symname[32];
+       GElf_Sym qux_sym;
+       struct proc_handle *phdl;
+       int error;
+
+       phdl = start_prog(tc, true);
+
+       error = proc_name2sym(phdl, target_prog_file, "$qux", &qux_sym, NULL);
+       ATF_REQUIRE_MSG(error == 0, "failed to look up '$qux' in %s",
+           target_prog_file);
+
+       error = proc_addr2sym(phdl, qux_sym.st_value, symname, sizeof(symname),
+           &qux_sym);
+       ATF_REQUIRE_MSG(error == 0, "failed to resolve 'qux' by addr");
+
+       ATF_REQUIRE_MSG(strcmp(symname, "qux") == 0,
+           "unexpected symbol name '%s'", symname);
+}
+
+ATF_TC(symbol_sort_underscore);
+ATF_TC_HEAD(symbol_sort_underscore, tc)
+{
+       atf_tc_set_md_var(tc, "descr",
+           "Ensure that proc_addr2sym() returns the alias with fewest leading "
+           "underscores in the name when the address resolves to multiple "
+           "symbols.");
+}
+ATF_TC_BODY(symbol_sort_underscore, tc)
+{
+       char symname[32];
+       GElf_Sym foo_sym;
+       struct proc_handle *phdl;
+       int error;
+
+       phdl = start_prog(tc, true);
+
+       error = proc_name2sym(phdl, target_prog_file, "foo", &foo_sym, NULL);
+       ATF_REQUIRE_MSG(error == 0, "failed to look up 'foo' in %s",
+           target_prog_file);
+
+       error = proc_addr2sym(phdl, foo_sym.st_value, symname, sizeof(symname),
+           &foo_sym);
+       ATF_REQUIRE_MSG(error == 0, "failed to resolve 'foo' by addr");
+
+       ATF_REQUIRE_MSG(strcmp(symname, "foo") == 0,
+           "unexpected symbol name '%s'", symname);
+}
+
 ATF_TP_ADD_TCS(tp)
 {
 
@@ -379,6 +466,9 @@ ATF_TP_ADD_TCS(tp)
        ATF_TP_ADD_TC(tp, symbol_lookup);
        ATF_TP_ADD_TC(tp, symbol_lookup_fail);
        ATF_TP_ADD_TC(tp, signal_forward);
+       ATF_TP_ADD_TC(tp, symbol_sort_local);
+       ATF_TP_ADD_TC(tp, symbol_sort_prefix);
+       ATF_TP_ADD_TC(tp, symbol_sort_underscore);
 
        return (atf_no_error());
 }

Modified: head/lib/libproc/tests/target_prog.c
==============================================================================
--- head/lib/libproc/tests/target_prog.c        Tue Nov 21 16:00:18 2017        
(r326063)
+++ head/lib/libproc/tests/target_prog.c        Tue Nov 21 16:03:21 2017        
(r326064)
@@ -42,9 +42,32 @@ usr1(int sig __unused)
        saw = 1;
 }
 
+void   foo(void);
+void   qux(void);
+
+void
+foo(void)
+{
+}
+__weak_reference(foo, _foo);
+
+static void
+bar(void)
+{
+}
+__strong_reference(bar, baz);
+
+void
+qux(void)
+{
+}
+__strong_reference(qux, $qux);
+
 int
 main(int argc, char **argv)
 {
+
+       bar(); /* force the symbol to be emitted */
 
        if (argc == 1)
                return (EXIT_SUCCESS);
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to