Fix breakpoints in ifunc after inferior resolved it (@got.plt symbol creation)

Setting a breakpoint on an ifunc symbol after the ifunc has already
been resolved by the inferior should result in creating a breakpoint
location at the ifunc target.  However, that's not what happens on
current Fedora:

  (gdb) n
  53        i = gnu_ifunc (1);    /* break-at-call */
  (gdb)
  54        assert (i == 2);
  (gdb) b gnu_ifunc
  Breakpoint 2 at gnu-indirect-function resolver at 0x7ffff7bd36ee
  (gdb) info breakpoints
  Num     Type                   Disp Enb Address            What
  2       STT_GNU_IFUNC resolver keep y   0x00007ffff7bd36ee <gnu_ifunc+4>

The problem is that elf_gnu_ifunc_resolve_by_got never manages to
resolve an ifunc target.  The reason is that GDB never actually
creates the internal got.plt symbols:

 (gdb) p 'gnu_ifunc@got.plt'
 No symbol "gnu_ifunc@got.plt" in current context.

and this is because GDB expects that rela.plt has relocations for
.plt, while it actually has relocations for .got.plt:

 Relocation section [10] '.rela.plt' for section [22] '.got.plt' at offset 0x570 contains 2 entries:
   Offset              Type            Value               Addend Name
   0x0000000000601018  X86_64_JUMP_SLOT 000000000000000000      +0 __assert_fail
   0x0000000000601020  X86_64_JUMP_SLOT 000000000000000000      +0 gnu_ifunc


Using an older system on the GCC compile farm (machine gcc15, an
x86-64 running Debian 6.0.8, with GNU ld 2.20.1), we see that it used
to be that we'd get a .rela.plt section for .plt:

 Relocation section [ 9] '.rela.plt' for section [11] '.plt' at offset 0x578 contains 3 entries:
   Offset              Type            Value               Addend Name
   0x0000000000600cc0  X86_64_JUMP_SLOT 000000000000000000      +0 __assert_fail
   0x0000000000600cc8  X86_64_JUMP_SLOT 000000000000000000      +0 __libc_start_main
   0x0000000000600cd0  X86_64_JUMP_SLOT 000000000000000000      +0 gnu_ifunc

Those offsets did point into .got.plt, as seen with objdump -h:

  20 .got.plt      00000030  0000000000600ca8  0000000000600ca8  00000ca8  2**3
     		   CONTENTS, ALLOC, LOAD, DATA

I also tested on gcc110 on the compile farm (PPC64 running CentOS
7.4.1708, with GNU ld 2.25.1), and there we see instead:

 Relocation section [ 9] '.rela.plt' for section [23] '.plt' at offset 0x5d0 contains 4 entries:
   Offset              Type            Value               Addend Name
   0x0000000010020148  PPC64_JMP_SLOT  000000000000000000      +0 __libc_start_main
   0x0000000010020160  PPC64_JMP_SLOT  000000000000000000      +0 __gmon_start__
   0x0000000010020178  PPC64_JMP_SLOT  000000000000000000      +0 __assert_fail
   0x0000000010020190  PPC64_JMP_SLOT  000000000000000000      +0 gnu_ifunc

But note that those offsets point into .plt, not .got.plt, as seen
with objdump -h:

 22 .plt          00000078  0000000010020130  0000000010020130  00010130  2**3
                  ALLOC

This commit makes us support all the different combinations above.

With that addressed, we now get:

 (gdb) p 'gnu_ifunc@got.plt'
 $1 = (<text from jump slot in .got.plt, no debug info>) 0x400753 <final>

And setting a breakpoint on the ifunc finds the ifunc target:

 (gdb) b gnu_ifunc
 Breakpoint 2 at 0x400753
 (gdb) info breakpoints
 Num     Type           Disp Enb Address            What
 2       breakpoint     keep y   0x0000000000400753 <final>

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

	* elfread.c (elf_rel_plt_read): Look for relocations for .got.plt too.
This commit is contained in:
Pedro Alves 2018-04-26 13:02:26 +01:00
parent e2195274d4
commit 02e169e2da
2 changed files with 44 additions and 17 deletions

View File

@ -1,3 +1,7 @@
2018-04-26 Pedro Alves <palves@redhat.com>
* elfread.c (elf_rel_plt_read): Look for relocations for .got.plt too.
2018-04-25 Pedro Alves <palves@redhat.com>
* infcmd.c (kill_command): Print the pid as string, not the whole

View File

@ -535,8 +535,7 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
{
bfd *obfd = objfile->obfd;
const struct elf_backend_data *bed = get_elf_backend_data (obfd);
asection *plt, *relplt, *got_plt;
int plt_elf_idx;
asection *relplt, *got_plt;
bfd_size_type reloc_count, reloc;
struct gdbarch *gdbarch = get_objfile_arch (objfile);
struct type *ptr_type = builtin_type (gdbarch)->builtin_data_ptr;
@ -545,11 +544,6 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
if (objfile->separate_debug_objfile_backlink)
return;
plt = bfd_get_section_by_name (obfd, ".plt");
if (plt == NULL)
return;
plt_elf_idx = elf_section_data (plt)->this_idx;
got_plt = bfd_get_section_by_name (obfd, ".got.plt");
if (got_plt == NULL)
{
@ -559,12 +553,25 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
return;
}
/* Depending on system, we may find jump slots in a relocation
section for either .got.plt or .plt. */
asection *plt = bfd_get_section_by_name (obfd, ".plt");
int plt_elf_idx = (plt != NULL) ? elf_section_data (plt)->this_idx : -1;
int got_plt_elf_idx = elf_section_data (got_plt)->this_idx;
/* This search algorithm is from _bfd_elf_canonicalize_dynamic_reloc. */
for (relplt = obfd->sections; relplt != NULL; relplt = relplt->next)
if (elf_section_data (relplt)->this_hdr.sh_info == plt_elf_idx
&& (elf_section_data (relplt)->this_hdr.sh_type == SHT_REL
|| elf_section_data (relplt)->this_hdr.sh_type == SHT_RELA))
break;
{
const auto &this_hdr = elf_section_data (relplt)->this_hdr;
if (this_hdr.sh_type == SHT_REL || this_hdr.sh_type == SHT_RELA)
{
if (this_hdr.sh_info == plt_elf_idx
|| this_hdr.sh_info == got_plt_elf_idx)
break;
}
}
if (relplt == NULL)
return;
@ -573,6 +580,17 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
std::string string_buffer;
/* Does ADDRESS reside in SECTION of OBFD? */
auto within_section = [obfd] (asection *section, CORE_ADDR address)
{
if (section == NULL)
return false;
return (bfd_get_section_vma (obfd, section) <= address
&& (address < bfd_get_section_vma (obfd, section)
+ bfd_get_section_size (section)));
};
reloc_count = relplt->size / elf_section_data (relplt)->this_hdr.sh_entsize;
for (reloc = 0; reloc < reloc_count; reloc++)
{
@ -585,10 +603,15 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
name = bfd_asymbol_name (*relplt->relocation[reloc].sym_ptr_ptr);
address = relplt->relocation[reloc].address;
/* Does the pointer reside in the .got.plt section? */
if (!(bfd_get_section_vma (obfd, got_plt) <= address
&& address < bfd_get_section_vma (obfd, got_plt)
+ bfd_get_section_size (got_plt)))
asection *msym_section;
/* Does the pointer reside in either the .got.plt or .plt
sections? */
if (within_section (got_plt, address))
msym_section = got_plt;
else if (within_section (plt, address))
msym_section = plt;
else
continue;
/* We cannot check if NAME is a reference to mst_text_gnu_ifunc as in
@ -600,8 +623,8 @@ elf_rel_plt_read (minimal_symbol_reader &reader,
msym = record_minimal_symbol (reader, string_buffer.c_str (),
string_buffer.size (),
true, address, mst_slot_got_plt, got_plt,
objfile);
true, address, mst_slot_got_plt,
msym_section, objfile);
if (msym)
SET_MSYMBOL_SIZE (msym, ptr_size);
}