Breakpoints, don't skip prologue of ifunc resolvers with debug info
authorPedro Alves <palves@redhat.com>
Thu, 26 Apr 2018 12:01:26 +0000 (13:01 +0100)
committerPedro Alves <palves@redhat.com>
Thu, 26 Apr 2018 12:06:53 +0000 (13:06 +0100)
Without this patch, some of the tests added to gdb.base/gnu-ifunc.exp
by a following patch fail like so:

  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=0: set-break: before resolving: break gnu_ifunc
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=0: set-break: before resolving: info breakpoints
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=0: set-break: after resolving: break gnu_ifunc
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=0: set-break: after resolving: info breakpoints
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=1: set-break: before resolving: break gnu_ifunc
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=1: set-break: before resolving: info breakpoints
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=1: set-break: after resolving: break gnu_ifunc
  FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=0: resolver_debug=1: resolved_debug=1: set-break: after resolving: info breakpoints

All of them trigger iff:

 - you have debug info for the ifunc resolver.
 - the resolver and the user-visible symbol have the same name.

If you have an ifunc that has a resolver with the same name as the
user visible symbol, debug info for the resolver masks out the ifunc
minsym.  When you set a breakpoint by name on the user visible symbol,
GDB finds the DWARF symbol for the resolver, and thinking that it's a
regular function, sets a breakpoint location past its prologue.

Like so, location 1.2, before the ifunc is resolved by the inferior:

  (gdb) break gnu_ifunc
  Breakpoint 2 at 0x7ffff7bd36ea (2 locations)
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  1       breakpoint     keep y   <MULTIPLE>
  1.1                         y     0x00007ffff7bd36ea <gnu_ifunc>
  1.2                         y     0x00007ffff7bd36f2 in gnu_ifunc at src/gdb/testsuite/gdb.base/gnu-ifunc-lib.c:34
  (gdb)

And like so, location 2.2, if you set the breakpoint after the ifunc
is resolved by the inferior (to "final"):

  (gdb) break gnu_ifunc
  Breakpoint 5 at 0x400757 (2 locations)
  (gdb) info breakpoints
  Num     Type           Disp Enb Address            What
  2       breakpoint     keep y   <MULTIPLE>
  2.1                         y     0x000000000040075a in final at src/gdb/testsuite/gdb.base/gnu-ifunc-resd.c:21
  2.2                         y     0x00007ffff7bd36f2 in gnu_ifunc at src/gdb/testsuite/gdb.base/gnu-ifunc-lib.c:34
  (gdb)

I don't think this is right because when users set a breakpoint at an
ifunc, they don't care about debugging the resolver.  Instead what you
should is a single location for the ifunc in the first case, and a
single location of the ifunc target in the second case.

gdb/ChangeLog:
2018-04-26  Pedro Alves  <palves@redhat.com>

* linespec.c (struct bound_minimal_symbol_search_key): New.
(convert_linespec_to_sals): Sort minimal symbols earlier.  Don't
skip first line if we found a GNU ifunc minimal symbol by name.
(compare_msymbols): Change parameters to work with a destructured
lhs minsym.
(compare_msymbols_for_qsort, compare_msymbols_for_bsearch): New
functions.

gdb/ChangeLog
gdb/linespec.c

index 5901a15..6a94e75 100644 (file)
@@ -1,5 +1,15 @@
 2018-04-26  Pedro Alves  <palves@redhat.com>
 
+       * linespec.c (struct bound_minimal_symbol_search_key): New.
+       (convert_linespec_to_sals): Sort minimal symbols earlier.  Don't
+       skip first line if we found a GNU ifunc minimal symbol by name.
+       (compare_msymbols): Change parameters to work with a destructured
+       lhs minsym.
+       (compare_msymbols_for_qsort, compare_msymbols_for_bsearch): New
+       functions.
+
+2018-04-26  Pedro Alves  <palves@redhat.com>
+
        * breakpoint.c (set_breakpoint_location_function): Don't resolve
        ifunc targets here.  Instead, if we have an ifunc minsym, use its
        address/name.
index 8a52f5e..b2deabe 100644 (file)
@@ -2258,12 +2258,6 @@ convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
   else if (ls->function_symbols != NULL || ls->minimal_symbols != NULL)
     {
       /* We have just a bunch of functions and/or methods.  */
-      int i;
-      struct symtab_and_line sal;
-      struct symbol *sym;
-      bound_minimal_symbol_d *elem;
-      struct program_space *pspace;
-
       if (ls->function_symbols != NULL)
        {
          /* Sort symbols so that symbols with the same program space are next
@@ -2272,30 +2266,66 @@ convert_linespec_to_sals (struct linespec_state *state, linespec_p ls)
                 VEC_length (symbolp, ls->function_symbols),
                 sizeof (symbolp), compare_symbols);
 
-         for (i = 0; VEC_iterate (symbolp, ls->function_symbols, i, sym); ++i)
+         struct symbol *sym;
+         for (int i = 0; VEC_iterate (symbolp, ls->function_symbols, i, sym); ++i)
            {
-             pspace = SYMTAB_PSPACE (symbol_symtab (sym));
+             program_space *pspace = SYMTAB_PSPACE (symbol_symtab (sym));
              set_current_program_space (pspace);
-             if (symbol_to_sal (&sal, state->funfirstline, sym)
-                 && maybe_add_address (state->addr_set, pspace, sal.pc))
-               add_sal_to_sals (state, &sals, &sal,
-                                SYMBOL_NATURAL_NAME (sym), 0);
+
+             /* Don't skip to the first line of the function if we
+                had found an ifunc minimal symbol for this function,
+                because that means that this function is an ifunc
+                resolver with the same name as the ifunc itself.  */
+             bool found_ifunc = false;
+
+             if (state->funfirstline
+                  && ls->minimal_symbols != NULL
+                  && SYMBOL_CLASS (sym) == LOC_BLOCK)
+               {
+                 const CORE_ADDR addr
+                   = BLOCK_START (SYMBOL_BLOCK_VALUE (sym));
+
+                 bound_minimal_symbol_d *elem;
+                 for (int m = 0;
+                      VEC_iterate (bound_minimal_symbol_d, ls->minimal_symbols,
+                                   m, elem);
+                      ++m)
+                   {
+                     if (MSYMBOL_TYPE (elem->minsym) == mst_text_gnu_ifunc
+                         && BMSYMBOL_VALUE_ADDRESS (*elem) == addr)
+                       {
+                         found_ifunc = true;
+                         break;
+                       }
+                   }
+               }
+
+             if (!found_ifunc)
+               {
+                 symtab_and_line sal;
+                 if (symbol_to_sal (&sal, state->funfirstline, sym)
+                     && maybe_add_address (state->addr_set, pspace, sal.pc))
+                   add_sal_to_sals (state, &sals, &sal,
+                                    SYMBOL_NATURAL_NAME (sym), 0);
+               }
            }
        }
 
       if (ls->minimal_symbols != NULL)
        {
-         /* Sort minimal symbols by program space, too.  */
+         /* Sort minimal symbols by program space, too  */
          qsort (VEC_address (bound_minimal_symbol_d, ls->minimal_symbols),
                 VEC_length (bound_minimal_symbol_d, ls->minimal_symbols),
                 sizeof (bound_minimal_symbol_d), compare_msymbols);
 
-         for (i = 0;
+         bound_minimal_symbol_d *elem;
+
+         for (int i = 0;
               VEC_iterate (bound_minimal_symbol_d, ls->minimal_symbols,
                            i, elem);
               ++i)
            {
-             pspace = elem->objfile->pspace;
+             program_space *pspace = elem->objfile->pspace;
              set_current_program_space (pspace);
              minsym_found (state, elem->objfile, elem->minsym, &sals);
            }