PPC64: always make synthetic .text symbols for GNU ifunc symbols

If you create an ifunc using GCC's __attribute__ ifunc, like:

 extern int gnu_ifunc (int arg);
 static int gnu_ifunc_target (int arg) { return 0; }
 __typeof (gnu_ifunc) *gnu_ifunc_resolver (unsigned long hwcap) { return gnu_ifunc_target; }
 __typeof (gnu_ifunc) gnu_ifunc __attribute__ ((ifunc ("gnu_ifunc_resolver")));

then you end up with two (function descriptor) symbols, one for the
ifunc itself, and another for the resolver:

   (...)
   12: 0000000000020060    104 FUNC    GLOBAL DEFAULT       18 gnu_ifunc_resolver
   (...)
   16: 0000000000020060    104 GNU_IFUNC GLOBAL DEFAULT       18 gnu_ifunc
   (...)

Both ifunc and resolver symbols have the same address/value, so
ppc64_elf_get_synthetic_symtab only creates a synthetic text symbol
for one of them.  In the case above, it ends up being created for the
resolver, only:

  (gdb) maint print msymbols
  (...)
  [ 7] t 0x980 .frame_dummy section .text
  [ 8] T 0x9e4 .gnu_ifunc_resolver section .text
  [ 9] T 0xa58 __glink_PLTresolve section .text
  (...)

GDB needs to know when a program stepped into an ifunc resolver, so
that it can know whether to step past the resolver into the target
function without the user noticing.  The way GDB does it is my
checking whether the current PC points to an ifunc symbol (since
resolver and ifunc have the same address by design).

The problem is then that ppc64_elf_get_synthetic_symtab never creates
the synchetic symbol for the ifunc, so GDB stops stepping at the
resolver (in a test added by the following patch):

  (gdb) step
  gnu_ifunc_resolver (hwcap=21) at gdb/testsuite/gdb.base/gnu-ifunc-lib.c:33
  33      {
  (gdb) FAIL: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1: final_debug=0: step

After this commit, we get:

  [ 8] i 0x9e4 .gnu_ifunc section .text
  [ 9] T 0x9e4 .gnu_ifunc_resolver section .text

And stepping an ifunc call takes to the final function:
  (gdb) step
  0x00000000100009e8 in .final ()
  (gdb) PASS: gdb.base/gnu-ifunc.exp: resolver_attr=1: resolver_debug=1: final_debug=0: step

An alternative to touching bfd I considered was for GDB to check
whether there's an ifunc data symbol / function descriptor that points
to the current PC, whenever the program stops, but discarded it
because we'd have to do a linear scan over .opd over an over to find a
matching function descriptor for the current PC.  At that point I
considered caching that info, but quickly dismissed it as then that
has no advantage (memory or performance) over just creating the
synthetic ifunc text symbol in the first place.

I ran the binutils and ld testsuites on PPC64 ELFv1 (machine gcc110 on
the GCC compile farm), and saw no regressions.  This commit is part of
a GDB patch series that includes GDB tests that fail without this fix.

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

	* elf64-ppc.c (ppc64_elf_get_synthetic_symtab): Don't consider
	ifunc and non-ifunc symbols duplicates.
This commit is contained in:
Pedro Alves 2018-04-26 13:01:27 +01:00
parent f50776aad5
commit bfa5bd2ab3
2 changed files with 21 additions and 6 deletions

View File

@ -1,3 +1,8 @@
2018-04-26 Pedro Alves <palves@redhat.com>
* elf64-ppc.c (ppc64_elf_get_synthetic_symtab): Don't consider
ifunc and non-ifunc symbols duplicates.
2018-04-25 Christophe Lyon <christophe.lyon@st.com>
Mickaël Guêné <mickael.guene@st.com>

View File

@ -3366,13 +3366,23 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
if (!relocatable && symcount > 1)
{
/* Trim duplicate syms, since we may have merged the normal and
dynamic symbols. Actually, we only care about syms that have
different values, so trim any with the same value. */
/* Trim duplicate syms, since we may have merged the normal
and dynamic symbols. Actually, we only care about syms
that have different values, so trim any with the same
value. Don't consider ifunc and ifunc resolver symbols
duplicates however, because GDB wants to know whether a
text symbol is an ifunc resolver. */
for (i = 1, j = 1; i < symcount; ++i)
if (syms[i - 1]->value + syms[i - 1]->section->vma
!= syms[i]->value + syms[i]->section->vma)
syms[j++] = syms[i];
{
const asymbol *s0 = syms[i - 1];
const asymbol *s1 = syms[i];
if ((s0->value + s0->section->vma
!= s1->value + s1->section->vma)
|| ((s0->flags & BSF_GNU_INDIRECT_FUNCTION)
!= (s1->flags & BSF_GNU_INDIRECT_FUNCTION)))
syms[j++] = syms[i];
}
symcount = j;
}