From: Alan Modra Date: Mon, 31 Aug 2015 23:26:22 +0000 (+0930) Subject: ppc64 out-of-line register save/restore functions X-Git-Tag: users/ARM/embedded-binutils-2_26-branch-2016q1~779 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=a4b6fadd50968ec6c1b687fe52b88bd11ff734b3;p=external%2Fbinutils.git 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. --- 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 + + 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 * 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;