aboutsummaryrefslogtreecommitdiff
path: root/bfd
diff options
context:
space:
mode:
authorAlan Modra <amodra@gmail.com>2015-09-01 08:56:22 +0930
committerAlan Modra <amodra@gmail.com>2015-09-01 17:42:32 +0930
commita4b6fadd50968ec6c1b687fe52b88bd11ff734b3 (patch)
tree980406b43224c4a4b2eebd3071c30f84042343e8 /bfd
parent76b20b99f79c7352db31c31a504e86843bc5c882 (diff)
downloadbinutils-a4b6fadd50968ec6c1b687fe52b88bd11ff734b3.zip
binutils-a4b6fadd50968ec6c1b687fe52b88bd11ff734b3.tar.gz
binutils-a4b6fadd50968ec6c1b687fe52b88bd11ff734b3.tar.bz2
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.
Diffstat (limited to 'bfd')
-rw-r--r--bfd/ChangeLog21
-rw-r--r--bfd/elf64-ppc.c199
2 files changed, 171 insertions, 49 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 5742aa3..e2819fe 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -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.
diff --git a/bfd/elf64-ppc.c b/bfd/elf64-ppc.c
index 810b227..15d5238 100644
--- a/bfd/elf64-ppc.c
+++ b/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;