* elf32-ppc.c (section_covers_vma): New function.

(ppc_elf_get_synthetic_symtab): New function.
	(bfd_elf32_get_synthetic_symtab): Define.
	* elf64-ppc.c (section_covers_vma): New function.
	(ppc64_elf_get_synthetic_symtab): Generate sym@plt on glink branch
	table entries, and __glink_PLTresolve on resolver stub.
	(ppc64_elf_build_stubs): Rename __glink sym to __glink_PLTresolve.
This commit is contained in:
Alan Modra 2008-05-14 02:21:19 +00:00
parent b091ad0577
commit 468392fb66
3 changed files with 371 additions and 5 deletions

View File

@ -1,3 +1,14 @@
2008-05-14 Ulrich Weigand <uweigand@de.ibm.com>
Alan Modra <amodra@bigpond.net.au>
* elf32-ppc.c (section_covers_vma): New function.
(ppc_elf_get_synthetic_symtab): New function.
(bfd_elf32_get_synthetic_symtab): Define.
* elf64-ppc.c (section_covers_vma): New function.
(ppc64_elf_get_synthetic_symtab): Generate sym@plt on glink branch
table entries, and __glink_PLTresolve on resolver stub.
(ppc64_elf_build_stubs): Rename __glink sym to __glink_PLTresolve.
2008-05-12 Alan Modra <amodra@bigpond.net.au>
PR 6443

View File

@ -2291,6 +2291,208 @@ ppc_elf_final_write_processing (bfd *abfd, bfd_boolean linker ATTRIBUTE_UNUSED)
apuinfo_list_finish ();
}
static bfd_boolean
section_covers_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *section, void *ptr)
{
bfd_vma vma = *(bfd_vma *) ptr;
return ((section->flags & SEC_ALLOC) != 0
&& section->vma <= vma
&& vma < section->vma + section->size);
}
static long
ppc_elf_get_synthetic_symtab (bfd *abfd, long symcount, asymbol **syms,
long dynsymcount, asymbol **dynsyms,
asymbol **ret)
{
bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
asection *plt, *relplt, *dynamic, *glink;
bfd_vma glink_vma = 0;
bfd_vma resolv_vma = 0;
bfd_vma stub_vma;
asymbol *s;
arelent *p;
long count, i;
size_t size;
char *names;
bfd_byte buf[4];
*ret = NULL;
if ((abfd->flags & (DYNAMIC | EXEC_P)) == 0)
return 0;
if (dynsymcount <= 0)
return 0;
relplt = bfd_get_section_by_name (abfd, ".rela.plt");
if (relplt == NULL)
return 0;
plt = bfd_get_section_by_name (abfd, ".plt");
if (plt == NULL)
return 0;
/* Call common code to handle old-style executable PLTs. */
if (elf_section_flags (plt) & SHF_EXECINSTR)
return _bfd_elf_get_synthetic_symtab (abfd, symcount, syms,
dynsymcount, dynsyms, ret);
/* If this object was prelinked, the prelinker stored the address
of .glink at got[1]. If it wasn't prelinked, got[1] will be zero. */
dynamic = bfd_get_section_by_name (abfd, ".dynamic");
if (dynamic != NULL)
{
bfd_byte *dynbuf, *extdyn, *extdynend;
size_t extdynsize;
void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
if (!bfd_malloc_and_get_section (abfd, dynamic, &dynbuf))
return -1;
extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
extdyn = dynbuf;
extdynend = extdyn + dynamic->size;
for (; extdyn < extdynend; extdyn += extdynsize)
{
Elf_Internal_Dyn dyn;
(*swap_dyn_in) (abfd, extdyn, &dyn);
if (dyn.d_tag == DT_NULL)
break;
if (dyn.d_tag == DT_PPC_GOT)
{
unsigned int g_o_t = dyn.d_un.d_val;
asection *got = bfd_get_section_by_name (abfd, ".got");
if (got != NULL
&& bfd_get_section_contents (abfd, got, buf,
g_o_t - got->vma + 4, 4))
glink_vma = bfd_get_32 (abfd, buf);
break;
}
}
free (dynbuf);
}
/* Otherwise we read the first plt entry. */
if (glink_vma == 0)
{
if (bfd_get_section_contents (abfd, plt, buf, 0, 4))
glink_vma = bfd_get_32 (abfd, buf);
}
if (glink_vma == 0)
return 0;
/* The .glink section usually does not survive the final
link; search for the section (usually .text) where the
glink stubs now reside. */
glink = bfd_sections_find_if (abfd, section_covers_vma, &glink_vma);
if (glink == NULL)
return 0;
/* Determine glink PLT resolver by reading the relative branch
from the first glink stub. */
if (bfd_get_section_contents (abfd, glink, buf,
glink_vma - glink->vma, 4))
{
unsigned int insn = bfd_get_32 (abfd, buf);
/* The first glink stub may either branch to the resolver ... */
insn ^= B;
if ((insn & ~0x3fffffc) == 0)
resolv_vma = glink_vma + (insn ^ 0x2000000) - 0x2000000;
/* ... or fall through a bunch of NOPs. */
else if ((insn ^ B ^ NOP) == 0)
for (i = 4;
bfd_get_section_contents (abfd, glink, buf,
glink_vma - glink->vma + i, 4);
i += 4)
if (bfd_get_32 (abfd, buf) != NOP)
{
resolv_vma = glink_vma + i;
break;
}
}
slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
if (! (*slurp_relocs) (abfd, relplt, dynsyms, TRUE))
return -1;
count = relplt->size / sizeof (Elf32_External_Rela);
stub_vma = glink_vma - (bfd_vma) count * 16;
size = count * sizeof (asymbol);
p = relplt->relocation;
for (i = 0; i < count; i++, p++)
size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
size += sizeof (asymbol) + sizeof ("__glink");
if (resolv_vma)
size += sizeof (asymbol) + sizeof ("__glink_PLTresolve");
s = *ret = bfd_malloc (size);
if (s == NULL)
return -1;
names = (char *) (s + count + 1 + (resolv_vma != 0));
p = relplt->relocation;
for (i = 0; i < count; i++, p++)
{
size_t len;
*s = **p->sym_ptr_ptr;
/* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since
we are defining a symbol, ensure one of them is set. */
if ((s->flags & BSF_LOCAL) == 0)
s->flags |= BSF_GLOBAL;
s->section = glink;
s->value = stub_vma - glink->vma;
s->name = names;
s->udata.p = NULL;
len = strlen ((*p->sym_ptr_ptr)->name);
memcpy (names, (*p->sym_ptr_ptr)->name, len);
names += len;
memcpy (names, "@plt", sizeof ("@plt"));
names += sizeof ("@plt");
++s;
stub_vma += 16;
}
/* Add a symbol at the start of the glink branch table. */
memset (s, sizeof *s, 0);
s->the_bfd = abfd;
s->flags = BSF_GLOBAL;
s->section = glink;
s->value = glink_vma - glink->vma;
s->name = names;
memcpy (names, "__glink", sizeof ("__glink"));
names += sizeof ("__glink");
s++;
count++;
if (resolv_vma)
{
/* Add a symbol for the glink PLT resolver. */
memset (s, sizeof *s, 0);
s->the_bfd = abfd;
s->flags = BSF_GLOBAL;
s->section = glink;
s->value = resolv_vma - glink->vma;
s->name = names;
memcpy (names, "__glink_PLTresolve", sizeof ("__glink_PLTresolve"));
names += sizeof ("__glink_PLTresolve");
s++;
count++;
}
return count;
}
/* The following functions are specific to the ELF linker, while
functions above are used generally. They appear in this file more
or less in the order in which they are called. eg.
@ -7818,6 +8020,7 @@ ppc_elf_finish_dynamic_sections (bfd *output_bfd,
#define bfd_elf32_bfd_reloc_name_lookup ppc_elf_reloc_name_lookup
#define bfd_elf32_bfd_set_private_flags ppc_elf_set_private_flags
#define bfd_elf32_bfd_link_hash_table_create ppc_elf_link_hash_table_create
#define bfd_elf32_get_synthetic_symtab ppc_elf_get_synthetic_symtab
#define elf_backend_object_p ppc_elf_object_p
#define elf_backend_gc_mark_hook ppc_elf_gc_mark_hook
@ -7932,6 +8135,8 @@ ppc_elf_vxworks_final_write_processing (bfd *abfd, bfd_boolean linker)
#undef elf_backend_got_header_size
#define elf_backend_got_header_size 12
#undef bfd_elf32_get_synthetic_symtab
#undef bfd_elf32_bfd_link_hash_table_create
#define bfd_elf32_bfd_link_hash_table_create \
ppc_elf_vxworks_link_hash_table_create

View File

@ -2771,8 +2771,17 @@ sym_exists_at (asymbol **syms, long lo, long hi, int id, bfd_vma value)
return NULL;
}
static bfd_boolean
section_covers_vma (bfd *abfd ATTRIBUTE_UNUSED, asection *section, void *ptr)
{
bfd_vma vma = *(bfd_vma *) ptr;
return ((section->flags & SEC_ALLOC) != 0
&& section->vma <= vma
&& vma < section->vma + section->size);
}
/* Create synthetic symbols, effectively restoring "dot-symbol" function
entry syms. */
entry syms. Also generate @plt symbols for the glink branch table. */
static long
ppc64_elf_get_synthetic_symtab (bfd *abfd,
@ -2862,8 +2871,6 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
symcount = i;
count = 0;
if (opdsymend == secsymend)
goto done;
if (relocatable)
{
@ -2872,6 +2879,9 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
size_t size;
long relcount;
if (opdsymend == secsymend)
goto done;
slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
relcount = (opd->flags & SEC_RELOC) ? opd->reloc_count : 0;
if (relcount == 0)
@ -2960,8 +2970,13 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
}
else
{
bfd_boolean (*slurp_relocs) (bfd *, asection *, asymbol **, bfd_boolean);
bfd_byte *contents;
size_t size;
long plt_count = 0;
bfd_vma glink_vma = 0, resolv_vma = 0;
asection *dynamic, *glink = NULL, *relplt = NULL;
arelent *p;
if (!bfd_malloc_and_get_section (abfd, opd, &contents))
{
@ -2988,11 +3003,85 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
}
}
/* Get start of .glink stubs from DT_PPC64_GLINK. */
dynamic = bfd_get_section_by_name (abfd, ".dynamic");
if (dynamic != NULL)
{
bfd_byte *dynbuf, *extdyn, *extdynend;
size_t extdynsize;
void (*swap_dyn_in) (bfd *, const void *, Elf_Internal_Dyn *);
if (!bfd_malloc_and_get_section (abfd, dynamic, &dynbuf))
goto free_contents_and_exit;
extdynsize = get_elf_backend_data (abfd)->s->sizeof_dyn;
swap_dyn_in = get_elf_backend_data (abfd)->s->swap_dyn_in;
extdyn = dynbuf;
extdynend = extdyn + dynamic->size;
for (; extdyn < extdynend; extdyn += extdynsize)
{
Elf_Internal_Dyn dyn;
(*swap_dyn_in) (abfd, extdyn, &dyn);
if (dyn.d_tag == DT_NULL)
break;
if (dyn.d_tag == DT_PPC64_GLINK)
{
/* The first glink stub starts at offset 32; see comment in
ppc64_elf_finish_dynamic_sections. */
glink_vma = dyn.d_un.d_val + 32;
/* The .glink section usually does not survive the final
link; search for the section (usually .text) where the
glink stubs now reside. */
glink = bfd_sections_find_if (abfd, section_covers_vma,
&glink_vma);
break;
}
}
free (dynbuf);
}
if (glink != NULL)
{
/* Determine __glink trampoline by reading the relative branch
from the first glink stub. */
bfd_byte buf[4];
if (bfd_get_section_contents (abfd, glink, buf,
glink_vma + 4 - glink->vma, 4))
{
unsigned int insn = bfd_get_32 (abfd, buf);
insn ^= B_DOT;
if ((insn & ~0x3fffffc) == 0)
resolv_vma = glink_vma + 4 + (insn ^ 0x2000000) - 0x2000000;
}
if (resolv_vma)
size += sizeof (asymbol) + sizeof ("__glink_PLTresolve");
}
relplt = bfd_get_section_by_name (abfd, ".rela.plt");
if (glink != NULL && relplt != NULL)
{
slurp_relocs = get_elf_backend_data (abfd)->s->slurp_reloc_table;
if (! (*slurp_relocs) (abfd, relplt, dyn_syms, TRUE))
goto free_contents_and_exit;
plt_count = relplt->size / sizeof (Elf64_External_Rela);
size += plt_count * sizeof (asymbol);
p = relplt->relocation;
for (i = 0; i < plt_count; i++, p++)
size += strlen ((*p->sym_ptr_ptr)->name) + sizeof ("@plt");
}
s = *ret = bfd_malloc (size);
if (s == NULL)
goto free_contents_and_exit;
names = (char *) (s + count);
names = (char *) (s + count + plt_count + (resolv_vma != 0));
for (i = secsymend; i < opdsymend; ++i)
{
@ -3048,6 +3137,66 @@ ppc64_elf_get_synthetic_symtab (bfd *abfd,
}
}
free (contents);
if (glink != NULL && relplt != NULL)
{
if (resolv_vma)
{
/* Add a symbol for the main glink trampoline. */
memset (s, sizeof *s, 0);
s->the_bfd = abfd;
s->flags = BSF_GLOBAL;
s->section = glink;
s->value = resolv_vma - glink->vma;
s->name = names;
memcpy (names, "__glink_PLTresolve", sizeof ("__glink_PLTresolve"));
names += sizeof ("__glink_PLTresolve");
s++;
count++;
}
/* FIXME: It would be very much nicer to put sym@plt on the
stub rather than on the glink branch table entry. The
objdump disassembler would then use a sensible symbol
name on plt calls. The difficulty in doing so is
a) finding the stubs, and,
b) matching stubs against plt entries, and,
c) there can be multiple stubs for a given plt entry.
Solving (a) could be done by code scanning, but older
ppc64 binaries used different stubs to current code.
(b) is the tricky one since you need to known the toc
pointer for at least one function that uses a pic stub to
be able to calculate the plt address referenced.
(c) means gdb would need to set multiple breakpoints (or
find the glink branch itself) when setting breakpoints
for pending shared library loads. */
p = relplt->relocation;
for (i = 0; i < plt_count; i++, p++)
{
size_t len;
*s = **p->sym_ptr_ptr;
/* Undefined syms won't have BSF_LOCAL or BSF_GLOBAL set. Since
we are defining a symbol, ensure one of them is set. */
if ((s->flags & BSF_LOCAL) == 0)
s->flags |= BSF_GLOBAL;
s->section = glink;
s->value = glink_vma - glink->vma;
s->name = names;
s->udata.p = NULL;
len = strlen ((*p->sym_ptr_ptr)->name);
memcpy (names, (*p->sym_ptr_ptr)->name, len);
names += len;
memcpy (names, "@plt", sizeof ("@plt"));
names += sizeof ("@plt");
s++;
glink_vma += 8;
if (i >= 0x8000)
glink_vma += 4;
}
count += plt_count;
}
}
done:
@ -9802,7 +9951,8 @@ ppc64_elf_build_stubs (bfd_boolean emit_stub_syms,
if (htab->emit_stub_syms)
{
struct elf_link_hash_entry *h;
h = elf_link_hash_lookup (&htab->elf, "__glink", TRUE, FALSE, FALSE);
h = elf_link_hash_lookup (&htab->elf, "__glink_PLTresolve",
TRUE, FALSE, FALSE);
if (h == NULL)
return FALSE;
if (h->root.type == bfd_link_hash_new)