ppc64 out-of-line register save/restore functions
Don't emit long branch or plt branch stubs to save/restore functions.
Copy them instead. The problem is that plt branch stubs currently
trash r12, one of the parameters to some of the save/restore
functions, and there is no free register available to use instead of
r12.
6f20ed8a
is prerequisite for this patch.
PR 18878
* elf64-ppc.c (ARRAY_SIZE): Define. Use throughout.
(enum ppc_stub_type): Add ppc_stub_save_res.
(struct map_stub): Add "next" and "needs_save_res".
(struct ppc_link_hash_entry): Add "save_res" flag.
(struct ppc_link_hash_table): Add "group".
(sfpr_define): Add stub_sec param. Define symbol in stub_sec if
stub_sec is non-null. Set "save_res".
(save_res_funcs): Make file scope, rename from funcs. Adjust uses.
(ppc64_elf_adjust_dynamic_symbol): Prohibit plt call to save_res syms.
(ppc_build_one_stub): Handle ppc_stub_save_res.
(ppc_size_one_stub): Set stub type to ppc_size_one_stub on finding
stub for linker defined save_res sym.
(group_sections): Init new fields of struct map_stub.
(ppc64_elf_size_stubs): Reserve space for save/restore func copy.
(ppc64_elf_build_stubs): Copy save/restore funcs to groups. Emit
alias syms too.
(ppc64_elf_relocate_section): Set destination for ppc_stub_save_res.
This commit is contained in:
parent
76b20b99f7
commit
a4b6fadd50
|
@ -1,3 +1,24 @@
|
|||
2015-09-01 Alan Modra <amodra@gmail.com>
|
||||
|
||||
PR 18878
|
||||
* elf64-ppc.c (ARRAY_SIZE): Define. Use throughout.
|
||||
(enum ppc_stub_type): Add ppc_stub_save_res.
|
||||
(struct map_stub): Add "next" and "needs_save_res".
|
||||
(struct ppc_link_hash_entry): Add "save_res" flag.
|
||||
(struct ppc_link_hash_table): Add "group".
|
||||
(sfpr_define): Add stub_sec param. Define symbol in stub_sec if
|
||||
stub_sec is non-null. Set "save_res".
|
||||
(save_res_funcs): Make file scope, rename from funcs. Adjust uses.
|
||||
(ppc64_elf_adjust_dynamic_symbol): Prohibit plt call to save_res syms.
|
||||
(ppc_build_one_stub): Handle ppc_stub_save_res.
|
||||
(ppc_size_one_stub): Set stub type to ppc_size_one_stub on finding
|
||||
stub for linker defined save_res sym.
|
||||
(group_sections): Init new fields of struct map_stub.
|
||||
(ppc64_elf_size_stubs): Reserve space for save/restore func copy.
|
||||
(ppc64_elf_build_stubs): Copy save/restore funcs to groups. Emit
|
||||
alias syms too.
|
||||
(ppc64_elf_relocate_section): Set destination for ppc_stub_save_res.
|
||||
|
||||
2015-08-31 Alan Modra <amodra@gmail.com>
|
||||
|
||||
* elf64-ppc.c (get_r2off): Return -1 on error.
|
||||
|
|
199
bfd/elf64-ppc.c
199
bfd/elf64-ppc.c
|
@ -238,6 +238,10 @@ static bfd_vma opd_entry_value
|
|||
#define NO_OPD_RELOCS 0
|
||||
#endif
|
||||
|
||||
#ifndef ARRAY_SIZE
|
||||
#define ARRAY_SIZE(a) (sizeof (a) / sizeof ((a)[0]))
|
||||
#endif
|
||||
|
||||
static inline int
|
||||
abiversion (bfd *abfd)
|
||||
{
|
||||
|
@ -2163,13 +2167,10 @@ ppc_howto_init (void)
|
|||
{
|
||||
unsigned int i, type;
|
||||
|
||||
for (i = 0;
|
||||
i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
|
||||
i++)
|
||||
for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++)
|
||||
{
|
||||
type = ppc64_elf_howto_raw[i].type;
|
||||
BFD_ASSERT (type < (sizeof (ppc64_elf_howto_table)
|
||||
/ sizeof (ppc64_elf_howto_table[0])));
|
||||
BFD_ASSERT (type < ARRAY_SIZE (ppc64_elf_howto_table));
|
||||
ppc64_elf_howto_table[type] = &ppc64_elf_howto_raw[i];
|
||||
}
|
||||
}
|
||||
|
@ -2428,9 +2429,7 @@ ppc64_elf_reloc_name_lookup (bfd *abfd ATTRIBUTE_UNUSED,
|
|||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0;
|
||||
i < sizeof (ppc64_elf_howto_raw) / sizeof (ppc64_elf_howto_raw[0]);
|
||||
i++)
|
||||
for (i = 0; i < ARRAY_SIZE (ppc64_elf_howto_raw); i++)
|
||||
if (ppc64_elf_howto_raw[i].name != NULL
|
||||
&& strcasecmp (ppc64_elf_howto_raw[i].name, r_name) == 0)
|
||||
return &ppc64_elf_howto_raw[i];
|
||||
|
@ -2451,8 +2450,7 @@ ppc64_elf_info_to_howto (bfd *abfd ATTRIBUTE_UNUSED, arelent *cache_ptr,
|
|||
ppc_howto_init ();
|
||||
|
||||
type = ELF64_R_TYPE (dst->r_info);
|
||||
if (type >= (sizeof (ppc64_elf_howto_table)
|
||||
/ sizeof (ppc64_elf_howto_table[0])))
|
||||
if (type >= ARRAY_SIZE (ppc64_elf_howto_table))
|
||||
{
|
||||
(*_bfd_error_handler) (_("%B: invalid relocation type %d"),
|
||||
abfd, (int) type);
|
||||
|
@ -3796,7 +3794,8 @@ enum ppc_stub_type {
|
|||
ppc_stub_plt_branch_r2off,
|
||||
ppc_stub_plt_call,
|
||||
ppc_stub_plt_call_r2save,
|
||||
ppc_stub_global_entry
|
||||
ppc_stub_global_entry,
|
||||
ppc_stub_save_res
|
||||
};
|
||||
|
||||
/* Information on stub grouping. */
|
||||
|
@ -3806,6 +3805,11 @@ struct map_stub
|
|||
asection *stub_sec;
|
||||
/* This is the section to which stubs in the group will be attached. */
|
||||
asection *link_sec;
|
||||
/* Next group. */
|
||||
struct map_stub *next;
|
||||
/* Whether to emit a copy of register save/restore functions in this
|
||||
group. */
|
||||
int needs_save_res;
|
||||
};
|
||||
|
||||
struct ppc_stub_hash_entry {
|
||||
|
@ -3893,6 +3897,10 @@ struct ppc_link_hash_entry
|
|||
/* Set if we twiddled this symbol to weak at some stage. */
|
||||
unsigned int was_undefined:1;
|
||||
|
||||
/* Set if this is an out-of-line register save/restore function,
|
||||
with non-standard calling convention. */
|
||||
unsigned int save_res:1;
|
||||
|
||||
/* Contexts in which symbol is used in the GOT (or TOC).
|
||||
TLS_GD .. TLS_EXPLICIT bits are or'd into the mask as the
|
||||
corresponding relocs are encountered during check_relocs.
|
||||
|
@ -3950,6 +3958,9 @@ struct ppc_link_hash_table
|
|||
} u;
|
||||
} *sec_info;
|
||||
|
||||
/* Linked list of groups. */
|
||||
struct map_stub *group;
|
||||
|
||||
/* Temp used when calculating TOC pointers. */
|
||||
bfd_vma toc_curr;
|
||||
bfd *toc_bfd;
|
||||
|
@ -6559,10 +6570,14 @@ struct sfpr_def_parms
|
|||
bfd_byte * (*write_tail) (bfd *, bfd_byte *, int);
|
||||
};
|
||||
|
||||
/* Auto-generate _save*, _rest* functions in .sfpr. */
|
||||
/* Auto-generate _save*, _rest* functions in .sfpr.
|
||||
If STUB_SEC is non-null, define alias symbols in STUB_SEC
|
||||
instead. */
|
||||
|
||||
static bfd_boolean
|
||||
sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm)
|
||||
sfpr_define (struct bfd_link_info *info,
|
||||
const struct sfpr_def_parms *parm,
|
||||
asection *stub_sec)
|
||||
{
|
||||
struct ppc_link_hash_table *htab = ppc_hash_table (info);
|
||||
unsigned int i;
|
||||
|
@ -6578,26 +6593,60 @@ sfpr_define (struct bfd_link_info *info, const struct sfpr_def_parms *parm)
|
|||
|
||||
for (i = parm->lo; i <= parm->hi; i++)
|
||||
{
|
||||
struct elf_link_hash_entry *h;
|
||||
struct ppc_link_hash_entry *h;
|
||||
|
||||
sym[len + 0] = i / 10 + '0';
|
||||
sym[len + 1] = i % 10 + '0';
|
||||
h = elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
|
||||
if (h != NULL
|
||||
&& !h->def_regular)
|
||||
h = (struct ppc_link_hash_entry *)
|
||||
elf_link_hash_lookup (&htab->elf, sym, FALSE, FALSE, TRUE);
|
||||
if (stub_sec != NULL)
|
||||
{
|
||||
h->root.type = bfd_link_hash_defined;
|
||||
h->root.u.def.section = htab->sfpr;
|
||||
h->root.u.def.value = htab->sfpr->size;
|
||||
h->type = STT_FUNC;
|
||||
h->def_regular = 1;
|
||||
_bfd_elf_link_hash_hide_symbol (info, h, TRUE);
|
||||
writing = TRUE;
|
||||
if (htab->sfpr->contents == NULL)
|
||||
if (h != NULL
|
||||
&& h->elf.root.type == bfd_link_hash_defined
|
||||
&& h->elf.root.u.def.section == htab->sfpr)
|
||||
{
|
||||
htab->sfpr->contents = bfd_alloc (htab->elf.dynobj, SFPR_MAX);
|
||||
if (htab->sfpr->contents == NULL)
|
||||
struct elf_link_hash_entry *s;
|
||||
char buf[32];
|
||||
sprintf (buf, "%08x.%s", stub_sec->id & 0xffffffff, sym);
|
||||
s = elf_link_hash_lookup (&htab->elf, buf, TRUE, TRUE, FALSE);
|
||||
if (s == NULL)
|
||||
return FALSE;
|
||||
if (s->root.type == bfd_link_hash_new
|
||||
|| (s->root.type = bfd_link_hash_defined
|
||||
&& s->root.u.def.section == stub_sec))
|
||||
{
|
||||
s->root.type = bfd_link_hash_defined;
|
||||
s->root.u.def.section = stub_sec;
|
||||
s->root.u.def.value = (stub_sec->size
|
||||
+ h->elf.root.u.def.value);
|
||||
s->ref_regular = 1;
|
||||
s->def_regular = 1;
|
||||
s->ref_regular_nonweak = 1;
|
||||
s->forced_local = 1;
|
||||
s->non_elf = 0;
|
||||
s->root.linker_def = 1;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (h != NULL)
|
||||
{
|
||||
h->save_res = 1;
|
||||
if (!h->elf.def_regular)
|
||||
{
|
||||
h->elf.root.type = bfd_link_hash_defined;
|
||||
h->elf.root.u.def.section = htab->sfpr;
|
||||
h->elf.root.u.def.value = htab->sfpr->size;
|
||||
h->elf.type = STT_FUNC;
|
||||
h->elf.def_regular = 1;
|
||||
_bfd_elf_link_hash_hide_symbol (info, &h->elf, TRUE);
|
||||
writing = TRUE;
|
||||
if (htab->sfpr->contents == NULL)
|
||||
{
|
||||
htab->sfpr->contents = bfd_alloc (htab->elf.dynobj, SFPR_MAX);
|
||||
if (htab->sfpr->contents == NULL)
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (writing)
|
||||
|
@ -6908,6 +6957,22 @@ func_desc_adjust (struct elf_link_hash_entry *h, void *inf)
|
|||
return TRUE;
|
||||
}
|
||||
|
||||
static const struct sfpr_def_parms save_res_funcs[] =
|
||||
{
|
||||
{ "_savegpr0_", 14, 31, savegpr0, savegpr0_tail },
|
||||
{ "_restgpr0_", 14, 29, restgpr0, restgpr0_tail },
|
||||
{ "_restgpr0_", 30, 31, restgpr0, restgpr0_tail },
|
||||
{ "_savegpr1_", 14, 31, savegpr1, savegpr1_tail },
|
||||
{ "_restgpr1_", 14, 31, restgpr1, restgpr1_tail },
|
||||
{ "_savefpr_", 14, 31, savefpr, savefpr0_tail },
|
||||
{ "_restfpr_", 14, 29, restfpr, restfpr0_tail },
|
||||
{ "_restfpr_", 30, 31, restfpr, restfpr0_tail },
|
||||
{ "._savef", 14, 31, savefpr, savefpr1_tail },
|
||||
{ "._restf", 14, 31, restfpr, restfpr1_tail },
|
||||
{ "_savevr_", 20, 31, savevr, savevr_tail },
|
||||
{ "_restvr_", 20, 31, restvr, restvr_tail }
|
||||
};
|
||||
|
||||
/* Called near the start of bfd_elf_size_dynamic_sections. We use
|
||||
this hook to a) provide some gcc support functions, and b) transfer
|
||||
dynamic linking information gathered so far on function code symbol
|
||||
|
@ -6919,21 +6984,6 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
|
|||
{
|
||||
struct ppc_link_hash_table *htab;
|
||||
unsigned int i;
|
||||
static const struct sfpr_def_parms funcs[] =
|
||||
{
|
||||
{ "_savegpr0_", 14, 31, savegpr0, savegpr0_tail },
|
||||
{ "_restgpr0_", 14, 29, restgpr0, restgpr0_tail },
|
||||
{ "_restgpr0_", 30, 31, restgpr0, restgpr0_tail },
|
||||
{ "_savegpr1_", 14, 31, savegpr1, savegpr1_tail },
|
||||
{ "_restgpr1_", 14, 31, restgpr1, restgpr1_tail },
|
||||
{ "_savefpr_", 14, 31, savefpr, savefpr0_tail },
|
||||
{ "_restfpr_", 14, 29, restfpr, restfpr0_tail },
|
||||
{ "_restfpr_", 30, 31, restfpr, restfpr0_tail },
|
||||
{ "._savef", 14, 31, savefpr, savefpr1_tail },
|
||||
{ "._restf", 14, 31, restfpr, restfpr1_tail },
|
||||
{ "_savevr_", 20, 31, savevr, savevr_tail },
|
||||
{ "_restvr_", 20, 31, restvr, restvr_tail }
|
||||
};
|
||||
|
||||
htab = ppc_hash_table (info);
|
||||
if (htab == NULL)
|
||||
|
@ -6966,8 +7016,8 @@ ppc64_elf_func_desc_adjust (bfd *obfd ATTRIBUTE_UNUSED,
|
|||
/* Provide any missing _save* and _rest* functions. */
|
||||
htab->sfpr->size = 0;
|
||||
if (htab->params->save_restore_funcs)
|
||||
for (i = 0; i < sizeof (funcs) / sizeof (funcs[0]); i++)
|
||||
if (!sfpr_define (info, &funcs[i]))
|
||||
for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
|
||||
if (!sfpr_define (info, &save_res_funcs[i], NULL))
|
||||
return FALSE;
|
||||
|
||||
elf_link_hash_traverse (&htab->elf, func_desc_adjust, info);
|
||||
|
@ -7029,7 +7079,8 @@ ppc64_elf_adjust_dynamic_symbol (struct bfd_link_info *info,
|
|||
|| (h->type != STT_GNU_IFUNC
|
||||
&& (SYMBOL_CALLS_LOCAL (info, h)
|
||||
|| (ELF_ST_VISIBILITY (h->other) != STV_DEFAULT
|
||||
&& h->root.type == bfd_link_hash_undefweak))))
|
||||
&& h->root.type == bfd_link_hash_undefweak)))
|
||||
|| ((struct ppc_link_hash_entry *) h)->save_res)
|
||||
{
|
||||
h->plt.plist = NULL;
|
||||
h->needs_plt = 0;
|
||||
|
@ -10945,6 +10996,9 @@ ppc_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
|||
size = p - loc;
|
||||
break;
|
||||
|
||||
case ppc_stub_save_res:
|
||||
return TRUE;
|
||||
|
||||
default:
|
||||
BFD_FAIL ();
|
||||
return FALSE;
|
||||
|
@ -11013,6 +11067,18 @@ ppc_size_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg)
|
|||
if (htab == NULL)
|
||||
return FALSE;
|
||||
|
||||
if (stub_entry->h != NULL
|
||||
&& stub_entry->h->save_res
|
||||
&& stub_entry->h->elf.root.type == bfd_link_hash_defined
|
||||
&& stub_entry->h->elf.root.u.def.section == htab->sfpr)
|
||||
{
|
||||
/* Don't make stubs to out-of-line register save/restore
|
||||
functions. Instead, emit copies of the functions. */
|
||||
stub_entry->group->needs_save_res = 1;
|
||||
stub_entry->stub_type = ppc_stub_save_res;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
if (stub_entry->stub_type == ppc_stub_plt_call
|
||||
|| stub_entry->stub_type == ppc_stub_plt_call_r2save)
|
||||
{
|
||||
|
@ -11900,6 +11966,9 @@ group_sections (struct bfd_link_info *info,
|
|||
return FALSE;
|
||||
group->link_sec = curr;
|
||||
group->stub_sec = NULL;
|
||||
group->needs_save_res = 0;
|
||||
group->next = htab->group;
|
||||
htab->group = group;
|
||||
do
|
||||
{
|
||||
prev = htab->sec_info[tail->id].u.list;
|
||||
|
@ -12020,7 +12089,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
|
|||
};
|
||||
unsigned i;
|
||||
|
||||
for (i = 0; i < sizeof (thread_starter)/ sizeof (thread_starter[0]); i++)
|
||||
for (i = 0; i < ARRAY_SIZE (thread_starter); i++)
|
||||
{
|
||||
struct elf_link_hash_entry *h;
|
||||
h = elf_link_hash_lookup (&htab->elf, thread_starter[i],
|
||||
|
@ -12043,6 +12112,7 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
|
|||
{
|
||||
bfd *input_bfd;
|
||||
unsigned int bfd_indx;
|
||||
struct map_stub *group;
|
||||
asection *stub_sec;
|
||||
|
||||
htab->stub_iteration += 1;
|
||||
|
@ -12370,6 +12440,10 @@ ppc64_elf_size_stubs (struct bfd_link_info *info)
|
|||
|
||||
bfd_hash_traverse (&htab->stub_hash_table, ppc_size_one_stub, info);
|
||||
|
||||
for (group = htab->group; group != NULL; group = group->next)
|
||||
if (group->needs_save_res)
|
||||
group->stub_sec->size += htab->sfpr->size;
|
||||
|
||||
if (info->emitrelocations
|
||||
&& htab->glink != NULL && htab->glink->size != 0)
|
||||
{
|
||||
|
@ -12730,6 +12804,7 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
|
|||
char **stats)
|
||||
{
|
||||
struct ppc_link_hash_table *htab = ppc_hash_table (info);
|
||||
struct map_stub *group;
|
||||
asection *stub_sec;
|
||||
bfd_byte *p;
|
||||
int stub_sec_count = 0;
|
||||
|
@ -12903,6 +12978,23 @@ ppc64_elf_build_stubs (struct bfd_link_info *info,
|
|||
/* Build the stubs as directed by the stub hash table. */
|
||||
bfd_hash_traverse (&htab->stub_hash_table, ppc_build_one_stub, info);
|
||||
|
||||
for (group = htab->group; group != NULL; group = group->next)
|
||||
if (group->needs_save_res)
|
||||
{
|
||||
stub_sec = group->stub_sec;
|
||||
memcpy (stub_sec->contents + stub_sec->size, htab->sfpr->contents,
|
||||
htab->sfpr->size);
|
||||
if (htab->params->emit_stub_syms)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE (save_res_funcs); i++)
|
||||
if (!sfpr_define (info, &save_res_funcs[i], stub_sec))
|
||||
return FALSE;
|
||||
}
|
||||
stub_sec->size += htab->sfpr->size;
|
||||
}
|
||||
|
||||
if (htab->relbrlt != NULL)
|
||||
htab->relbrlt->reloc_count = 0;
|
||||
|
||||
|
@ -13923,9 +14015,18 @@ ppc64_elf_relocate_section (bfd *output_bfd,
|
|||
{
|
||||
/* Munge up the value and addend so that we call the stub
|
||||
rather than the procedure directly. */
|
||||
relocation = (stub_entry->stub_offset
|
||||
+ stub_entry->group->stub_sec->output_offset
|
||||
+ stub_entry->group->stub_sec->output_section->vma);
|
||||
asection *stub_sec = stub_entry->group->stub_sec;
|
||||
|
||||
if (stub_entry->stub_type == ppc_stub_save_res)
|
||||
relocation += (stub_sec->output_offset
|
||||
+ stub_sec->output_section->vma
|
||||
+ stub_sec->size - htab->sfpr->size
|
||||
- htab->sfpr->output_offset
|
||||
- htab->sfpr->output_section->vma);
|
||||
else
|
||||
relocation = (stub_entry->stub_offset
|
||||
+ stub_sec->output_offset
|
||||
+ stub_sec->output_section->vma);
|
||||
addend = 0;
|
||||
reloc_dest = DEST_STUB;
|
||||
|
||||
|
|
Loading…
Reference in New Issue