From 2d23f9656c3fd92335683188d26db198fe2ed3ec Mon Sep 17 00:00:00 2001 From: Cl?ment Chigot Date: Wed, 20 Apr 2022 15:11:47 +0100 Subject: [PATCH] xcoff: implement linker relaxation bfd/ChangeLog: * coff-rs6000.c (xcoff_reloc_type_noop): Add info argument. (xcoff_reloc_type_fail): Likewise. (xcoff_reloc_type_pos): Likewise. (xcoff_reloc_type_neg): Likewise. (xcoff_reloc_type_rel): Likewise. (xcoff_reloc_type_toc): Likewise. (xcoff_reloc_type_ba): Likewise. (xcoff_reloc_type_crel): Likewise. (xcoff_reloc_type_tls): Likewise. (xcoff_reloc_type_br): Add stub handler. (xcoff_ppc_relocate_section): Add info to xcoff_calculate_relocation. (xcoff_stub_indirect_call_code): New constant. (xcoff_stub_shared_call_code): Likewise. (bfd_xcoff_backend_data): Add stub code fields. (bfd_pmac_xcoff_backend_data): Likewise. * coff64-rs6000.c (xcoff64_reloc_type_br): Add stub handler. (xcoff64_ppc_relocate_section): Add info to xcoff64_calculate_relocation. (xcoff64_stub_indirect_call_code): New constant. (xcoff64_stub_shared_call_code): Likewise. (bfd_xcoff_backend_data): Add stub code fields. (bfd_xcoff_aix5_backend_data): Likewise. * libxcoff.h (struct xcoff_backend_data_rec): Add stub fields. (bfd_xcoff_stub_indirect_call_code): New define. (bfd_xcoff_stub_indirect_call_size): New define. (bfd_xcoff_stub_shared_call_code): New define. (bfd_xcoff_stub_shared_call_size): New define. (xcoff_reloc_function): Add info argument. (enum xcoff_stub_type): New enum. (struct xcoff_stub_hash_entry): New structure. * xcofflink.c (struct xcoff_link_hash_table): Add stub hash table and params fields. (xcoff_stub_hash_entry): New define. (xcoff_stub_hash_lookup): New define. (stub_hash_newfunc): New function. (_bfd_xcoff_bfd_link_hash_table_free): Free the new stub hash table. (_bfd_xcoff_bfd_link_hash_table_create): Create the new stub hash table. (xcoff_link_add_symbols): Save rawsize for XTY_SD. (bfd_xcoff_link_init): New function. (xcoff_stub_csect_name): New function. (xcoff_stub_get_csect_in_range): New function. (xcoff_stub_name): New function. (bfd_xcoff_get_stub_entry): New function. (bfd_xcoff_type_of_stub): New function. (xcoff_add_stub): New function. (xcoff_build_one_stub): New function. (bfd_xcoff_size_stubs): New function. (bfd_xcoff_build_stubs): New function. (xcoff_stub_create_relocations): New function. (xcoff_link_input_bfd): Adapt relocations to stub. (xcoff_write_global_symbol): Adapt to new TOC entries generated for stubs. (_bfd_xcoff_bfd_final_link): Handle stub file. * xcofflink.h (struct bfd_xcoff_link_params): New structure. ld/ChangeLog: * emultempl/aix.em (params): New variable. (stub_file): New variable. (xcoff_add_stub_section): New function. (xcoff_layout_sections_again): New function (hook_in_stub): New function. (_after_allocation): Add stub creation. (_create_output_section_statements): Allocate stub file and pass params to backend. --- bfd/ChangeLog | 60 +++ bfd/coff-rs6000.c | 99 ++++- bfd/coff64-rs6000.c | 66 +++- bfd/libxcoff.h | 63 ++- bfd/xcofflink.c | 940 +++++++++++++++++++++++++++++++++++++++++++- bfd/xcofflink.h | 17 + ld/ChangeLog | 11 + ld/emultempl/aix.em | 190 ++++++++- 8 files changed, 1413 insertions(+), 33 deletions(-) diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 06c44a3d150..957d8fd556c 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,63 @@ +2022-04-20 Clément Chigot + + * coff-rs6000.c (xcoff_reloc_type_noop): Add info argument. + (xcoff_reloc_type_fail): Likewise. + (xcoff_reloc_type_pos): Likewise. + (xcoff_reloc_type_neg): Likewise. + (xcoff_reloc_type_rel): Likewise. + (xcoff_reloc_type_toc): Likewise. + (xcoff_reloc_type_ba): Likewise. + (xcoff_reloc_type_crel): Likewise. + (xcoff_reloc_type_tls): Likewise. + (xcoff_reloc_type_br): Add stub handler. + (xcoff_ppc_relocate_section): Add info to + xcoff_calculate_relocation. + (xcoff_stub_indirect_call_code): New constant. + (xcoff_stub_shared_call_code): Likewise. + (bfd_xcoff_backend_data): Add stub code fields. + (bfd_pmac_xcoff_backend_data): Likewise. + * coff64-rs6000.c (xcoff64_reloc_type_br): Add stub handler. + (xcoff64_ppc_relocate_section): Add info to + xcoff64_calculate_relocation. + (xcoff64_stub_indirect_call_code): New constant. + (xcoff64_stub_shared_call_code): Likewise. + (bfd_xcoff_backend_data): Add stub code fields. + (bfd_xcoff_aix5_backend_data): Likewise. + * libxcoff.h (struct xcoff_backend_data_rec): Add stub fields. + (bfd_xcoff_stub_indirect_call_code): New define. + (bfd_xcoff_stub_indirect_call_size): New define. + (bfd_xcoff_stub_shared_call_code): New define. + (bfd_xcoff_stub_shared_call_size): New define. + (xcoff_reloc_function): Add info argument. + (enum xcoff_stub_type): New enum. + (struct xcoff_stub_hash_entry): New structure. + * xcofflink.c (struct xcoff_link_hash_table): Add stub hash + table and params fields. + (xcoff_stub_hash_entry): New define. + (xcoff_stub_hash_lookup): New define. + (stub_hash_newfunc): New function. + (_bfd_xcoff_bfd_link_hash_table_free): Free the new stub hash + table. + (_bfd_xcoff_bfd_link_hash_table_create): Create the new stub + hash table. + (xcoff_link_add_symbols): Save rawsize for XTY_SD. + (bfd_xcoff_link_init): New function. + (xcoff_stub_csect_name): New function. + (xcoff_stub_get_csect_in_range): New function. + (xcoff_stub_name): New function. + (bfd_xcoff_get_stub_entry): New function. + (bfd_xcoff_type_of_stub): New function. + (xcoff_add_stub): New function. + (xcoff_build_one_stub): New function. + (bfd_xcoff_size_stubs): New function. + (bfd_xcoff_build_stubs): New function. + (xcoff_stub_create_relocations): New function. + (xcoff_link_input_bfd): Adapt relocations to stub. + (xcoff_write_global_symbol): Adapt to new TOC entries generated + for stubs. + (_bfd_xcoff_bfd_final_link): Handle stub file. + * xcofflink.h (struct bfd_xcoff_link_params): New structure. + 2022-04-20 Clément Chigot * coff-rs6000.c (_bfd_xcoff_put_ldsymbol_name): Write len in diff --git a/bfd/coff-rs6000.c b/bfd/coff-rs6000.c index 8656dfdb4c4..8819187ab42 100644 --- a/bfd/coff-rs6000.c +++ b/bfd/coff-rs6000.c @@ -2937,7 +2937,8 @@ xcoff_reloc_type_noop (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val ATTRIBUTE_UNUSED, bfd_vma addend ATTRIBUTE_UNUSED, bfd_vma *relocation ATTRIBUTE_UNUSED, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { return true; } @@ -2952,7 +2953,8 @@ xcoff_reloc_type_fail (bfd *input_bfd, bfd_vma val ATTRIBUTE_UNUSED, bfd_vma addend ATTRIBUTE_UNUSED, bfd_vma *relocation ATTRIBUTE_UNUSED, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { _bfd_error_handler /* xgettext: c-format */ @@ -2972,7 +2974,8 @@ xcoff_reloc_type_pos (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { *relocation = val + addend; return true; @@ -2988,7 +2991,8 @@ xcoff_reloc_type_neg (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { *relocation = - val - addend; return true; @@ -3004,7 +3008,8 @@ xcoff_reloc_type_rel (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { howto->pc_relative = true; @@ -3027,7 +3032,8 @@ xcoff_reloc_type_toc (bfd *input_bfd, bfd_vma val, bfd_vma addend ATTRIBUTE_UNUSED, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { struct xcoff_link_hash_entry *h; @@ -3076,7 +3082,8 @@ xcoff_reloc_type_ba (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { howto->src_mask &= ~3; howto->dst_mask = howto->src_mask; @@ -3096,10 +3103,13 @@ xcoff_reloc_type_br (bfd *input_bfd, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents) + bfd_byte *contents, + struct bfd_link_info *info) { struct xcoff_link_hash_entry *h; bfd_vma section_offset; + struct xcoff_stub_hash_entry *stub_entry = NULL; + enum xcoff_stub_type stub_type; if (0 > rel->r_symndx) return false; @@ -3153,6 +3163,27 @@ xcoff_reloc_type_br (bfd *input_bfd, howto->complain_on_overflow = complain_overflow_dont; } + /* Check if a stub is needed. */ + stub_type = bfd_xcoff_type_of_stub (input_section, rel, val, h); + if (stub_type != xcoff_stub_none) + { + asection *stub_csect; + + stub_entry = bfd_xcoff_get_stub_entry (input_section, h, info); + if (stub_entry == NULL) + { + _bfd_error_handler (_("Unable to find the stub entry targeting %s"), + h->root.root.string); + bfd_set_error (bfd_error_bad_value); + return false; + } + + stub_csect = stub_entry->hcsect->root.u.def.section; + val = (stub_entry->stub_offset + + stub_csect->output_section->vma + + stub_csect->output_offset); + } + /* The original PC-relative relocation is biased by -r_vaddr, so adding the value below will give the absolute target address. */ *relocation = val + addend + rel->r_vaddr; @@ -3202,7 +3233,8 @@ xcoff_reloc_type_crel (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val ATTRIBUTE_UNUSED, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { howto->pc_relative = true; howto->src_mask &= ~3; @@ -3227,7 +3259,8 @@ xcoff_reloc_type_tls (bfd *input_bfd ATTRIBUTE_UNUSED, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents ATTRIBUTE_UNUSED) + bfd_byte *contents ATTRIBUTE_UNUSED, + struct bfd_link_info *info ATTRIBUTE_UNUSED) { struct xcoff_link_hash_entry *h; @@ -3763,7 +3796,7 @@ xcoff_ppc_relocate_section (bfd *output_bfd, if (rel->r_type >= XCOFF_MAX_CALCULATE_RELOCATION || !((*xcoff_calculate_relocation[rel->r_type]) (input_bfd, input_section, output_bfd, rel, sym, &howto, val, - addend, &relocation, contents))) + addend, &relocation, contents, info))) return false; /* address */ @@ -4281,6 +4314,34 @@ HOWTO (0, /* type */ 0xffffffff, /* dst_mask */ false); /* pcrel_offset */ +/* Indirect call stub + The first word of the code must be modified by filling in + the correct TOC offset. */ + +static const unsigned long xcoff_stub_indirect_call_code[4] = + { + 0x81820000, /* lwz r12,0(r2) */ + 0x800c0000, /* lwz r0,0(r12) */ + 0x7c0903a6, /* mtctr r0 */ + 0x4e800420, /* bctr */ + }; + +/* Shared call stub + The first word of the code must be modified by filling in + the correct TOC offset. + This is exactly as the glink code but without the traceback, + as it won't be an independent function. */ + +static const unsigned long xcoff_stub_shared_call_code[6] = + { + 0x81820000, /* lwz r12,0(r2) */ + 0x90410014, /* stw r2,20(r1) */ + 0x800c0000, /* lwz r0,0(r12) */ + 0x804c0004, /* lwz r2,4(r12) */ + 0x7c0903a6, /* mtctr r0 */ + 0x4e800420, /* bctr */ + }; + /* glink The first word of global linkage code must be modified by filling in @@ -4497,6 +4558,14 @@ static const struct xcoff_backend_data_rec bfd_xcoff_backend_data = /* rtinit */ 64, /* _xcoff_rtinit_size */ xcoff_generate_rtinit, + + /* Stub indirect call. */ + &xcoff_stub_indirect_call_code[0], + 16, /* _xcoff_stub_indirect_call_size */ + + /* Stub shared call. */ + &xcoff_stub_shared_call_code[0], + 24, /* _xcoff_stub_shared_call_size */ }; /* The transfer vector that leads the outside world to all of the above. */ @@ -4679,6 +4748,14 @@ static const struct xcoff_backend_data_rec bfd_pmac_xcoff_backend_data = /* rtinit */ 0, /* _xcoff_rtinit_size */ xcoff_generate_rtinit, + + /* Stub indirect call. */ + &xcoff_stub_indirect_call_code[0], + 16, /* _xcoff_stub_indirect_call_size */ + + /* Stub shared call. */ + &xcoff_stub_shared_call_code[0], + 24, /* _xcoff_stub_shared_call_size */ }; /* The transfer vector that leads the outside world to all of the above. */ diff --git a/bfd/coff64-rs6000.c b/bfd/coff64-rs6000.c index 95abbb9e957..2f8077ae6a2 100644 --- a/bfd/coff64-rs6000.c +++ b/bfd/coff64-rs6000.c @@ -778,10 +778,13 @@ xcoff64_reloc_type_br (bfd *input_bfd, bfd_vma val, bfd_vma addend, bfd_vma *relocation, - bfd_byte *contents) + bfd_byte *contents, + struct bfd_link_info *info) { struct xcoff_link_hash_entry *h; bfd_vma section_offset; + struct xcoff_stub_hash_entry *stub_entry = NULL; + enum xcoff_stub_type stub_type; if (0 > rel->r_symndx) return false; @@ -833,6 +836,27 @@ xcoff64_reloc_type_br (bfd *input_bfd, howto->complain_on_overflow = complain_overflow_dont; } + /* Check if a stub is needed. */ + stub_type = bfd_xcoff_type_of_stub (input_section, rel, val, h); + if (stub_type != xcoff_stub_none) + { + asection *stub_csect; + + stub_entry = bfd_xcoff_get_stub_entry (input_section, h, info); + if (stub_entry == NULL) + { + _bfd_error_handler (_("Unable to find the stub entry targeting %s"), + h->root.root.string); + bfd_set_error (bfd_error_bad_value); + return false; + } + + stub_csect = stub_entry->hcsect->root.u.def.section; + val = (stub_entry->stub_offset + + stub_csect->output_section->vma + + stub_csect->output_offset); + } + /* The original PC-relative relocation is biased by -r_vaddr, so adding the value below will give the absolute target address. */ *relocation = val + addend + rel->r_vaddr; @@ -1645,7 +1669,7 @@ xcoff64_ppc_relocate_section (bfd *output_bfd, if (rel->r_type >= XCOFF_MAX_CALCULATE_RELOCATION || !((*xcoff64_calculate_relocation[rel->r_type]) (input_bfd, input_section, output_bfd, rel, sym, &howto, val, - addend, &relocation, contents))) + addend, &relocation, contents, info))) return false; /* address */ @@ -2386,12 +2410,32 @@ HOWTO (0, /* type */ MINUS_ONE, /* dst_mask */ false); /* pcrel_offset */ +/* Indirect call stub */ +static const unsigned long xcoff64_stub_indirect_call_code[4] = + { + 0xe9820000, /* ld r12,0(r2) */ + 0xe80c0000, /* ld r0,0(r12) */ + 0x7c0903a6, /* mtctr r0 */ + 0x4e800420, /* bctr */ + }; + +/* Shared call stub */ +static const unsigned long xcoff64_stub_shared_call_code[6] = + { + 0xe9820000, /* ld r12,0(r2) */ + 0xf8410028, /* std r2,40(r1) */ + 0xe80c0000, /* ld r0,0(r12) */ + 0xe84c0008, /* ld r2,8(r12) */ + 0x7c0903a6, /* mtctr r0 */ + 0x4e800420, /* bctr */ + }; + static const unsigned long xcoff64_glink_code[10] = { 0xe9820000, /* ld r12,0(r2) */ 0xf8410028, /* std r2,40(r1) */ 0xe80c0000, /* ld r0,0(r12) */ - 0xe84c0008, /* ld r0,8(r12) */ + 0xe84c0008, /* ld r2,8(r12) */ 0x7c0903a6, /* mtctr r0 */ 0x4e800420, /* bctr */ 0x00000000, /* start of traceback table */ @@ -2495,6 +2539,14 @@ static const struct xcoff_backend_data_rec bfd_xcoff_backend_data = /* rtinit. */ 88, /* _xcoff_rtinit_size */ xcoff64_generate_rtinit, + + /* Stub indirect call. */ + &xcoff64_stub_indirect_call_code[0], + 16, /* _xcoff_stub_indirect_call_size */ + + /* Stub shared call. */ + &xcoff64_stub_shared_call_code[0], + 24, /* _xcoff_stub_shared_call_size */ }; /* The transfer vector that leads the outside world to all of the above. */ @@ -2759,6 +2811,14 @@ static const struct xcoff_backend_data_rec bfd_xcoff_aix5_backend_data = /* rtinit. */ 88, /* _xcoff_rtinit_size */ xcoff64_generate_rtinit, + + /* Stub indirect call. */ + &xcoff64_stub_indirect_call_code[0], + 16, /* _xcoff_stub_indirect_call_size */ + + /* Stub shared call. */ + &xcoff64_stub_shared_call_code[0], + 24, /* _xcoff_stub_shared_call_size */ }; /* The transfer vector that leads the outside world to all of the above. */ diff --git a/bfd/libxcoff.h b/bfd/libxcoff.h index 2a71ee5851a..c6ad6dc2c4a 100644 --- a/bfd/libxcoff.h +++ b/bfd/libxcoff.h @@ -99,6 +99,15 @@ struct xcoff_backend_data_rec unsigned int _xcoff_rtinit_size; bool (*_xcoff_generate_rtinit) (bfd *, const char *, const char *, bool); + + /* Stubs code generation. + The code part is an array which might need to be modified by + some relocations. + The size is in bytes. */ + const unsigned long *_xcoff_stub_indirect_call_code; + unsigned long _xcoff_stub_indirect_call_size; + const unsigned long *_xcoff_stub_shared_call_code; + unsigned long _xcoff_stub_shared_call_size; }; /* Look up an entry in an XCOFF link hash table. */ @@ -185,6 +194,11 @@ struct xcoff_backend_data_rec #define bfd_xcoff_glink_code(a, b) ((xcoff_backend (a)->_xcoff_glink_code[(b)])) #define bfd_xcoff_glink_code_size(a) ((xcoff_backend (a)->_xcoff_glink_size)) +#define bfd_xcoff_stub_indirect_call_code(a, b) ((xcoff_backend (a)->_xcoff_stub_indirect_call_code[(b)])) +#define bfd_xcoff_stub_indirect_call_size(a) ((xcoff_backend (a)->_xcoff_stub_indirect_call_size)) +#define bfd_xcoff_stub_shared_call_code(a, b) ((xcoff_backend (a)->_xcoff_stub_shared_call_code[(b)])) +#define bfd_xcoff_stub_shared_call_size(a) ((xcoff_backend (a)->_xcoff_stub_shared_call_size)) + /* Check for the magic number U803XTOCMAGIC or U64_TOCMAGIC for 64 bit targets. */ #define bfd_xcoff_is_xcoff64(a) \ @@ -211,11 +225,12 @@ struct xcoff_backend_data_rec #define N_ONES(n) (((((bfd_vma) 1 << ((n) - 1)) - 1) << 1) | 1) typedef bool xcoff_reloc_function (bfd *, asection *, bfd *, - struct internal_reloc *, - struct internal_syment *, - struct reloc_howto_struct *, - bfd_vma, bfd_vma, - bfd_vma *, bfd_byte *); + struct internal_reloc *, + struct internal_syment *, + struct reloc_howto_struct *, + bfd_vma, bfd_vma, + bfd_vma *, bfd_byte *, + struct bfd_link_info *); typedef bool xcoff_complain_function (bfd *, bfd_vma, bfd_vma, struct reloc_howto_struct *); @@ -261,4 +276,42 @@ struct xcoff_dwsect_name { extern const struct xcoff_dwsect_name xcoff_dwsect_names[XCOFF_DWSECT_NBR_NAMES]; +/* Structure and functions needed by backend in order to handle + stubs created in xcofflink.c. */ + +enum xcoff_stub_type + { + xcoff_stub_none, + xcoff_stub_indirect_call, + xcoff_stub_shared_call + }; + +struct xcoff_stub_hash_entry +{ + /* Base hash table entry structure. */ + struct bfd_hash_entry root; + + enum xcoff_stub_type stub_type; + + /* The hash table entry of the stub's csect. */ + struct xcoff_link_hash_entry *hcsect; + + /* Offset in the stub's csect. */ + bfd_vma stub_offset; + + /* The target's section. */ + asection *target_section; + + /* The target's hash table entry. */ + struct xcoff_link_hash_entry *htarget; +}; + + +extern enum xcoff_stub_type bfd_xcoff_type_of_stub + (asection *, const struct internal_reloc *, bfd_vma, + struct xcoff_link_hash_entry *); + +extern struct xcoff_stub_hash_entry *bfd_xcoff_get_stub_entry + (asection *, struct xcoff_link_hash_entry *, struct bfd_link_info *); + #endif /* LIBXCOFF_H */ diff --git a/bfd/xcofflink.c b/bfd/xcofflink.c index 6d4abdd1eda..6dbcd299b88 100644 --- a/bfd/xcofflink.c +++ b/bfd/xcofflink.c @@ -93,6 +93,12 @@ struct xcoff_link_hash_table { struct bfd_link_hash_table root; + /* The stub hash table. */ + struct bfd_hash_table stub_hash_table; + + /* Info passed by the linker. */ + struct bfd_xcoff_link_params *params; + /* The .debug string hash table. We need to compute this while reading the input files, so that we know how large the .debug section will be before we assign section positions. */ @@ -193,6 +199,13 @@ struct xcoff_final_link_info bfd_byte *external_relocs; }; +#define xcoff_stub_hash_entry(ent) \ + ((struct xcoff_stub_hash_entry *)(ent)) + +#define xcoff_stub_hash_lookup(table, string, create, copy) \ + ((struct xcoff_stub_hash_entry *) \ + bfd_hash_lookup ((table), (string), (create), (copy))) + static bool xcoff_mark (struct bfd_link_info *, asection *); @@ -529,6 +542,41 @@ xcoff_get_archive_info (struct bfd_link_info *info, bfd *archive) return entryp; } + +/* Initialize an entry in the stub hash table. */ +static struct bfd_hash_entry * +stub_hash_newfunc (struct bfd_hash_entry *entry, + struct bfd_hash_table *table, + const char *string) +{ + /* Allocate the structure if it has not already been allocated by a + subclass. */ + if (entry == NULL) + { + entry = bfd_hash_allocate (table, + sizeof (struct xcoff_stub_hash_entry)); + if (entry == NULL) + return entry; + } + + /* Call the allocation method of the superclass. */ + entry = bfd_hash_newfunc (entry, table, string); + if (entry != NULL) + { + struct xcoff_stub_hash_entry *hsh; + + /* Initialize the local fields. */ + hsh = (struct xcoff_stub_hash_entry *) entry; + hsh->stub_type = xcoff_stub_none; + hsh->hcsect = NULL; + hsh->stub_offset = 0; + hsh->target_section = NULL; + hsh->htarget = NULL; + } + + return entry; +} + /* Routine to create an entry in an XCOFF link hash table. */ static struct bfd_hash_entry * @@ -577,6 +625,8 @@ _bfd_xcoff_bfd_link_hash_table_free (bfd *obfd) htab_delete (ret->archive_info); if (ret->debug_strtab) _bfd_stringtab_free (ret->debug_strtab); + + bfd_hash_table_free (&ret->stub_hash_table); _bfd_generic_link_hash_table_free (obfd); } @@ -599,6 +649,14 @@ _bfd_xcoff_bfd_link_hash_table_create (bfd *abfd) return NULL; } + /* Init the stub hash table too. */ + if (!bfd_hash_table_init (&ret->stub_hash_table, stub_hash_newfunc, + sizeof (struct xcoff_stub_hash_entry))) + { + _bfd_xcoff_bfd_link_hash_table_free (abfd); + return NULL; + } + isxcoff64 = bfd_coff_debug_string_prefix_length (abfd) == 4; ret->debug_strtab = _bfd_xcoff_stringtab_init (isxcoff64); @@ -1738,6 +1796,7 @@ xcoff_link_add_symbols (bfd *abfd, struct bfd_link_info *info) + sym.n_value - enclosing->vma); csect->size = aux.x_csect.x_scnlen.l; + csect->rawsize = aux.x_csect.x_scnlen.l; csect->flags |= SEC_ALLOC | SEC_LOAD | SEC_HAS_CONTENTS; csect->alignment_power = SMTYP_ALIGN (aux.x_csect.x_smtyp); @@ -3159,6 +3218,17 @@ xcoff_sweep (struct bfd_link_info *info) } } +/* Initialize the back-end with linker infos. */ + +bool +bfd_xcoff_link_init (struct bfd_link_info *info, + struct bfd_xcoff_link_params *params) +{ + xcoff_hash_table (info)->params = params; + + return true; +} + /* Record the number of elements in a set. This is used to output the correct csect length. */ @@ -4218,6 +4288,750 @@ bfd_xcoff_link_generate_rtinit (bfd *abfd, return true; } + +/* Linker stubs. + The stubs will be gathered in stub csects named "@FIX'number'". + A new csect will be created by xcoff_stub_get_csect_in_range, + everytime a relocation cannot reach its target and its section + is too far from the others stub csects. + The stubs will simply be code generated inside these stub + csects. In order to simplify the symbol table, only the symbols + for the stub csects are written. + + As the code is dependent of the architecture, it's defined + in the backend. + + xcoff_stub_indirect_call: + Used when a 24 bit branch cannot reach its destination and that + this destination isn't a global linkage symbol. + + xcoff_stub_shared_call: + As above but when it's a global linkage symbol. + The main difference being that it doesn't branch to the global + linkage symbol which will then call the shared library. It + directly call it saving the TOC. + + TODO: -bbigtoc option should be able to be implemented using + this stubs. */ + +/* Get the name of a csect which will contain stubs. + It has the same pattern as AIX linker: @FIX"number". */ +static char * xcoff_stub_csect_name (unsigned int n) +{ + char buf[6]; + size_t len; + char *csect_name; + + /* For now, allow "only" 1000000 stub csects. */ + if (n >= 1000000) + { + BFD_FAIL(); + return NULL; + } + + sprintf (buf, "%d", n); + len = 4 + strlen (buf) + 1; + + csect_name = bfd_malloc (len); + if (csect_name == NULL) + return NULL; + sprintf (csect_name, "@FIX%d", n); + + return csect_name; +} + +/* Return a stub section which can be reach with a single branch + from SECTION. CREATE means that creating a csect is allowed. */ +static struct xcoff_link_hash_entry * +xcoff_stub_get_csect_in_range (asection *section, + struct bfd_link_info *info, + bool create) +{ + struct xcoff_link_hash_table *htab = xcoff_hash_table (info); + struct xcoff_link_hash_entry *csect_entry; + struct bfd_link_hash_entry *bh = NULL; + asection *csect; + unsigned int it; + char *csect_name; + + /* Search for a csect in range. */ + for (csect = htab->params->stub_bfd->sections, it = 0; + csect != NULL; + csect = csect->next, it++) + { + /* A csect is in range if everything instructions in SECTION + can branch to every stubs in the stub csect. This can + be simplify by saying that the first entry of each sections + (ie the vma of this section) can reach the last entry of the + stub csect (ie the vma of the csect + its size). + However, as the stub csect might be growing its size isn't + fixed. Thus, the last entry of SECTION might not be able + to reach the first entry of the stub csect anymore. + If this case happens, the following condition will be + false during the next pass of bfd_xcoff_size_stubs and + another csect will be used. + This means we might create more stubs than needed. */ + bfd_vma csect_vma, section_vma; + bfd_vma csect_last_vma, section_last_vma; + + csect_vma = (csect->output_section->vma + + csect->output_offset); + csect_last_vma = (csect->output_section->vma + + csect->output_offset + + csect->size); + section_vma = (section->output_section->vma + + section->output_offset); + section_last_vma = (section->output_section->vma + + section->output_offset + + section->size); + + if (csect_last_vma - section_vma + (1 << 25) < 2 * (1 << 25) + && section_last_vma - csect_vma + (1 << 25) < 2 * (1 << 25)) + break; + } + + if (!create && csect == NULL) + return NULL; + + csect_name = xcoff_stub_csect_name (it); + if (!csect_name) + return NULL; + + /* A stub csect already exists, get its entry. */ + if (csect != NULL) + { + csect_entry = xcoff_link_hash_lookup (htab, csect_name, false, false, true); + free(csect_name); + return csect_entry; + } + + /* Create the csect and its symbol. */ + csect = (*htab->params->add_stub_section) (".pr", section); + if (!csect) + { + free(csect_name); + return NULL; + } + + csect->alignment_power = 2; + csect->gc_mark = 1; + csect->reloc_count = 0; + + /* We need to associate a VMA to this new csect. Otherwise, + our "in range" algorithm won't find it for the next stub. + And as we will be adding this stub section just after the + SECTION, we know its address. */ + csect->output_offset = BFD_ALIGN (section->output_offset + section->size, + 4); + + if (!_bfd_generic_link_add_one_symbol (info, htab->params->stub_bfd, + csect_name, BSF_GLOBAL, csect, 0, + NULL, true, true, &bh)) + { + free(csect_name); + return NULL; + } + + csect_entry = (struct xcoff_link_hash_entry *)bh; + csect_entry->smclas = XMC_PR; + csect_entry->flags = XCOFF_MARK | XCOFF_DEF_REGULAR; + + free(csect_name); + return csect_entry; +} + + +/* Build a name for an entry in the stub hash table. */ +static char * +xcoff_stub_name (const struct xcoff_link_hash_entry *h, + const struct xcoff_link_hash_entry *hcsect) +{ + char *stub_name; + size_t len; + + if (h) + { + /* The name of a stub is based on its stub csect and the + symbol it wants to reach. It looks like: ".@FIX0.tramp.f". + When the stub targets a function, the last dot of ".tramp." + is removed to avoid having two dot. */ + len = (1 + 6 + + strlen (hcsect->root.root.string) + + strlen (h->root.root.string) + + 1); + if (h->root.root.string[0] != '.') + len++; + + stub_name = bfd_malloc (len); + if (stub_name == NULL) + return stub_name; + + if (h->root.root.string[0] == '.') + sprintf (stub_name, ".%s.tramp%s", + hcsect->root.root.string, + h->root.root.string); + else + sprintf (stub_name, ".%s.tramp.%s", + hcsect->root.root.string, + h->root.root.string); + } + else + { + BFD_FAIL(); + return NULL; + } + + return stub_name; +} + +/* Look up an entry in the stub hash. */ +struct xcoff_stub_hash_entry * +bfd_xcoff_get_stub_entry (asection *section, + struct xcoff_link_hash_entry *h, + struct bfd_link_info *info) +{ + struct xcoff_link_hash_table *htab = xcoff_hash_table (info); + struct xcoff_link_hash_entry *hcsect; + struct xcoff_stub_hash_entry *hstub; + char *stub_name; + + hcsect = xcoff_stub_get_csect_in_range (section, info, false); + if (!hcsect) + return NULL; + + stub_name = xcoff_stub_name (h, hcsect); + if (stub_name == NULL) + return NULL; + + hstub = xcoff_stub_hash_lookup (&htab->stub_hash_table, + stub_name, false, false); + + free (stub_name); + return hstub; +} + +/* Check if the symbol targeted by IREL is reachable. + Return the type of stub needed otherwise. */ +enum xcoff_stub_type +bfd_xcoff_type_of_stub (asection *sec, + const struct internal_reloc *irel, + bfd_vma destination, + struct xcoff_link_hash_entry *h) +{ + bfd_vma location, offset, max_offset; + + switch (irel->r_type) + { + default: + return xcoff_stub_none; + + case R_BR: + case R_RBR: + location = (sec->output_section->vma + + sec->output_offset + + irel->r_vaddr + - sec->vma); + + max_offset = 1 << 25 ; + + offset = destination - location; + + if (offset + max_offset < 2 * max_offset) + return xcoff_stub_none; + + /* A stub is needed. Now, check that we can make one. */ + if (h != NULL + && h->descriptor != NULL) + { + /* Not sure how to handle this case. For now, skip it. */ + if (bfd_is_abs_section (h->root.u.def.section)) + return xcoff_stub_none; + + if (h->smclas == XMC_GL) + return xcoff_stub_shared_call; + else + return xcoff_stub_indirect_call; + } + break; + } + + return xcoff_stub_none; +} + +/* Add a new stub entry to the stub hash. Not all fields of the new + stub entry are initialised. */ +static struct xcoff_stub_hash_entry * +xcoff_add_stub (const char *stub_name, + struct xcoff_link_hash_entry *hstub_csect, + struct xcoff_link_hash_entry *htarget, + struct bfd_link_info *info, + enum xcoff_stub_type stub_type) +{ + struct xcoff_link_hash_table *htab = xcoff_hash_table (info); + struct xcoff_stub_hash_entry *hstub; + bfd_vma stub_offset; + asection *stub_csect; + + stub_csect = hstub_csect->root.u.def.section; + stub_offset = stub_csect->size; + + /* Update the relocation counter and the size of + the containing csect. The size is needed for + the algorithm in xcoff_stub_get_csect_in_range. */ + switch (stub_type) + { + default: + BFD_FAIL (); + return NULL; + + case xcoff_stub_indirect_call: + stub_csect->reloc_count++; + stub_csect->size += bfd_xcoff_stub_indirect_call_size (info->output_bfd); + break; + + case xcoff_stub_shared_call: + stub_csect->reloc_count++; + stub_csect->size += bfd_xcoff_stub_shared_call_size (info->output_bfd); + break; + } + + /* Create the stub entry. */ + hstub = xcoff_stub_hash_lookup (&htab->stub_hash_table, stub_name, + true, true); + if (hstub == NULL) + return NULL; + + hstub->htarget = htarget; + hstub->stub_offset = stub_offset; + + /* For indirect call or shared call, the relocations are against + the target descriptor. Its toc entry will be used. */ + if (stub_type == xcoff_stub_indirect_call + || stub_type == xcoff_stub_shared_call) + { + struct xcoff_link_hash_entry *hds = htarget->descriptor; + asection *hds_section = hds->root.u.def.section; + + hstub->htarget = hds; + + /* If the symbol haven't been marked, its section might have + its size and its relocation count been deleted by xcoff_sweep. + Restore it. */ + if ((hds->flags & XCOFF_MARK) == 0) + { + if (hds_section->size == 0 + && hds_section->reloc_count == 0 + && hds_section->rawsize != 0) + { + hds_section->size = hds_section->rawsize; + /* Always two relocations for a XMC_DS symbol. */ + hds_section->reloc_count = 2; + } + + /* Mark the section and the symbol. */ + if (!xcoff_mark (info, hds_section)) + return NULL; + } + + /* Add a TOC entry for the descriptor if non exists. */ + if (hds->toc_section == NULL) + { + int byte_size; + + if (bfd_xcoff_is_xcoff64 (info->output_bfd)) + byte_size = 8; + else if (bfd_xcoff_is_xcoff32 (info->output_bfd)) + byte_size = 4; + else + return NULL; + + /* Allocate room in the fallback TOC section. */ + hds->toc_section = xcoff_hash_table (info)->toc_section; + hds->u.toc_offset = hds->toc_section->size; + hds->toc_section->size += byte_size; + if (!xcoff_mark (info, hds->toc_section)) + return NULL; + + /* Update relocation counters for a static and dynamic + R_TOC relocation. */ + ++hds->toc_section->reloc_count; + ++htab->ldinfo.ldrel_count; + + /* Set the index to -2 to force this symbol to + get written out. */ + hds->indx = -2; + hds->flags |= XCOFF_SET_TOC; + } + } + + return hstub; +} + +static bool +xcoff_build_one_stub (struct bfd_hash_entry *gen_entry, void *in_arg) +{ + struct xcoff_stub_hash_entry *hstub + = (struct xcoff_stub_hash_entry *) gen_entry; + + bfd *stub_bfd; + bfd *output_bfd; + struct bfd_link_info *info; + bfd_byte *loc; + bfd_byte *p; + unsigned int i; + + info = (struct bfd_link_info *) in_arg; + stub_bfd = xcoff_hash_table (info)->params->stub_bfd; + output_bfd = info->output_bfd; + + /* Fail if the target section could not be assigned to an output + section. The user should fix his linker script. */ + if (hstub->target_section != NULL + && hstub->target_section->output_section == NULL + && info->non_contiguous_regions) + info->callbacks->einfo (_("%F%P: Could not assign '%pA' to an output section. " + "Retry without --enable-non-contiguous-regions.\n"), + hstub->target_section); + + loc = (hstub->hcsect->root.u.def.section->contents + + hstub->stub_offset); + p = loc; + + switch (hstub->stub_type) + { + case xcoff_stub_indirect_call: + BFD_ASSERT (hstub->htarget->toc_section != NULL); + /* The first instruction in the stub code needs to be + cooked to hold the correct offset in the toc. It will + be filled by xcoff_stub_create_relocations. */ + for (i = 0; i < bfd_xcoff_stub_indirect_call_size(output_bfd) / 4; i++) + bfd_put_32 (stub_bfd, + (bfd_vma) bfd_xcoff_stub_indirect_call_code(output_bfd, i), + &p[4 * i]); + break; + + case xcoff_stub_shared_call: + BFD_ASSERT (hstub->htarget->toc_section != NULL); + /* The first instruction in the glink code needs to be + cooked to hold the correct offset in the toc. It will + be filled by xcoff_stub_create_relocations. */ + for (i = 0; i < bfd_xcoff_stub_shared_call_size(output_bfd) / 4; i++) + bfd_put_32 (stub_bfd, + (bfd_vma) bfd_xcoff_stub_shared_call_code(output_bfd, i), + &p[4 * i]); + + break; + + default: + BFD_FAIL (); + return false; + } + return true; +} + +/* Check relocations and adds stubs if needed. */ + +bool +bfd_xcoff_size_stubs (struct bfd_link_info *info) +{ + struct xcoff_link_hash_table *htab = xcoff_hash_table (info); + struct xcoff_loader_info *ldinfo = &(htab->ldinfo); + + while (1) + { + bfd *input_bfd; + bool stub_changed = false; + + for (input_bfd = info->input_bfds; + input_bfd != NULL; + input_bfd = input_bfd->link.next) + { + asection *section; + bfd_size_type symcount; + bfd_size_type symesz; + bfd_byte *esyms; + + if (bfd_get_flavour (input_bfd) != bfd_target_xcoff_flavour) + continue; + + symcount = obj_raw_syment_count (input_bfd); + if (!symcount) + continue; + symesz = bfd_coff_symesz (input_bfd); + esyms = (bfd_byte *) obj_coff_external_syms (input_bfd); + + /* Walk over each section attached to the input bfd. */ + for (section = input_bfd->sections; + section != NULL; + section = section->next) + { + struct internal_reloc *internal_relocs; + struct internal_reloc *irel, *irelend; + + /* If there aren't any relocs, then there's nothing more + to do. */ + if ((section->flags & SEC_RELOC) == 0 + || section->reloc_count == 0) + continue; + + /* If this section is a link-once section that will be + discarded, then don't create any stubs. */ + if (section->output_section == NULL + || section->output_section->owner != info->output_bfd) + continue; + + /* This section have been garbage-collected. */ + if (section->gc_mark == 0) + continue; + + /* Read in the relocs. */ + internal_relocs = (xcoff_read_internal_relocs + (input_bfd, section, true, NULL, + false, NULL)); + if (internal_relocs == NULL) + goto error_ret; + + irel = internal_relocs; + irelend = irel + section->reloc_count; + for (; irel < irelend; irel++) + { + enum xcoff_stub_type stub_type; + struct xcoff_link_hash_entry *hsym = NULL; + struct xcoff_link_hash_entry *hstub_csect = NULL; + struct xcoff_stub_hash_entry *hstub = NULL; + asection *sym_sec; + bfd_vma sym_value; + bfd_vma destination; + char *stub_name; + + if (irel->r_symndx == -1) + continue; + + switch (irel->r_type) + { + default: + continue; + + case R_BR: + case R_RBR: + break; + } + + /* Retrieve targeted symbol address */ + hsym = obj_xcoff_sym_hashes (input_bfd)[irel->r_symndx]; + if (hsym == NULL) + { + struct internal_syment sym; + if ((long unsigned int)irel->r_symndx > symcount) + { + BFD_FAIL(); + goto error_ret; + } + + bfd_coff_swap_sym_in (input_bfd, + (void *) esyms + irel->r_symndx * symesz, + (void *) &sym); + + sym_sec = xcoff_data (input_bfd)->csects[irel->r_symndx]; + sym_value = sym.n_value - sym_sec->vma; + + destination = (sym_value + + sym_sec->output_section->vma + + sym_sec->output_offset); + } + else if (hsym->root.type == bfd_link_hash_defined + || hsym->root.type == bfd_link_hash_defweak) + { + sym_sec = hsym->root.u.def.section; + sym_value = hsym->root.u.def.value; + destination = (sym_value + + sym_sec->output_section->vma + + sym_sec->output_offset); + } + else + { + bfd_set_error (bfd_error_bad_value); + goto error_ret; + } + + /* I'm not sure how to handle this case. Skip it for now. */ + if (bfd_is_abs_section (sym_sec)) + continue; + + stub_type = bfd_xcoff_type_of_stub (section, irel, destination, hsym); + + if (stub_type == xcoff_stub_none) + continue; + + /* Get a stub csect in ranch. */ + hstub_csect = xcoff_stub_get_csect_in_range (section, info, true); + if (!hstub_csect) + { + /* xgettext:c-format */ + _bfd_error_handler (_("%pB: Unable to find a stub csect in range" + "of relocation at %#" PRIx64 " targeting" + "'%s'"), + section->owner, irel->r_vaddr, + hsym->root.root.string); + goto error_ret; + } + + /* Get the name of this stub. */ + stub_name = xcoff_stub_name (hsym, hstub_csect); + if (!stub_name) + goto error_ret; + + hstub = xcoff_stub_hash_lookup (&(xcoff_hash_table (info)->stub_hash_table), + stub_name, false, false); + + /* A stub entry inside the in range csect already exists. */ + if (hstub != NULL) + { + free (stub_name); + continue; + } + + stub_changed = true; + + hstub = xcoff_add_stub (stub_name, hstub_csect, hsym, info, stub_type); + if (hstub == NULL) + { + free (stub_name); + /* xgettext:c-format */ + _bfd_error_handler (_("%pB: Cannot create stub entry '%s'"), + section->owner, stub_name); + goto error_ret; + } + + hstub->stub_type = stub_type; + hstub->hcsect = hstub_csect; + hstub->target_section = sym_sec; + free (stub_name); + } + } + } + + if (!stub_changed) + break; + + /* Update the size of the loader. */ + if (xcoff_hash_table (info)->loader_section + && !xcoff_size_loader_section (ldinfo)) + goto error_ret; + + /* Ask the linker to do its stuff. */ + (*htab->params->layout_sections_again) (); + + } + return true; + + error_ret: + bfd_set_error (bfd_error_bad_value); + return false; +} + +bool +bfd_xcoff_build_stubs (struct bfd_link_info *info) +{ + struct xcoff_link_hash_table *htab = xcoff_hash_table (info); + asection *stub_sec; + + for (stub_sec = htab->params->stub_bfd->sections; + stub_sec != NULL; + stub_sec = stub_sec->next) + { + bfd_size_type size; + + /* Allocate memory to hold the linker stubs. */ + size = stub_sec->size; + stub_sec->contents = bfd_zalloc (htab->params->stub_bfd, size); + if (stub_sec->contents == NULL && size != 0) + return false; + + } + + /* Build the stubs as directed by the stub hash table. */ + bfd_hash_traverse (&htab->stub_hash_table, xcoff_build_one_stub, info); + return true; +} + +/* Create and apply relocations made by a stub entry. */ +static bool +xcoff_stub_create_relocations (struct bfd_hash_entry *bh, void * inf) +{ + struct xcoff_stub_hash_entry *hstub + = (struct xcoff_stub_hash_entry *) bh; + struct xcoff_final_link_info *flinfo + = (struct xcoff_final_link_info *) inf; + + bfd *output_bfd; + struct internal_reloc *irel; + struct xcoff_link_hash_entry **rel_hash; + struct xcoff_link_hash_entry *htarget; + asection *sec, *osec; + bfd_vma off; + bfd_byte *p; + + htarget = hstub->htarget; + sec = hstub->hcsect->root.u.def.section; + osec = sec->output_section; + + irel = (flinfo->section_info[osec->target_index].relocs + + osec->reloc_count); + rel_hash = (flinfo->section_info[osec->target_index].rel_hashes + + osec->output_section->reloc_count); + *rel_hash = NULL; + output_bfd = flinfo->output_bfd; + + irel->r_symndx = htarget->indx; + irel->r_vaddr = (osec->vma + + sec->output_offset + + hstub->hcsect->root.u.def.value + + hstub->stub_offset); + + p = (sec->contents + + hstub->stub_offset); + + switch (hstub->stub_type) + { + default: + BFD_FAIL (); + return false; + + /* The first instruction of this stub code need + a R_TOC relocation. */ + case xcoff_stub_indirect_call: + case xcoff_stub_shared_call: + irel->r_size = 0xf; + irel->r_type = R_TOC; + + /* Retrieve the toc offset of the target which is + a function descriptor. */ + BFD_ASSERT (htarget->toc_section != NULL); + if ((htarget->flags & XCOFF_SET_TOC) != 0) + off = hstub->htarget->u.toc_offset; + else + off = (htarget->toc_section->output_section->vma + + htarget->toc_section->output_offset + - xcoff_data (flinfo->output_bfd)->toc); + if ((off & 0xffff) != off) + { + _bfd_error_handler + (_("TOC overflow during stub generation; try -mminimal-toc " + "when compiling")); + bfd_set_error (bfd_error_file_too_big); + return false; + } + + bfd_put_16 (output_bfd, off & 0xffff, p+2); + break; + } + + ++osec->reloc_count; + return true; +} + + /* Return the section that defines H. Return null if no section does. */ static asection * @@ -5049,8 +5863,6 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *flinfo, /* Adjust the reloc address and symbol index. */ - irel->r_vaddr += offset; - r_symndx = irel->r_symndx; if (r_symndx == -1) @@ -5058,8 +5870,48 @@ xcoff_link_input_bfd (struct xcoff_final_link_info *flinfo, else h = obj_xcoff_sym_hashes (input_bfd)[r_symndx]; + /* In case of a R_BR or R_RBR, change the target if + a stub is being called. */ + if (h != NULL + && (irel->r_type == R_BR + || irel->r_type == R_RBR)) + { + asection *sym_sec; + bfd_vma dest; + struct xcoff_stub_hash_entry *hstub = NULL; + enum xcoff_stub_type stub_type; + + if (h->root.type == bfd_link_hash_defined + || h->root.type == bfd_link_hash_defweak) + { + sym_sec = h->root.u.def.section; + dest = (h->root.u.def.value + + sym_sec->output_section->vma + + sym_sec->output_offset); + } + else + { + BFD_FAIL (); + goto err_out; + } + + stub_type = bfd_xcoff_type_of_stub (o, irel, dest, h); + if (stub_type != xcoff_stub_none) + { + hstub = bfd_xcoff_get_stub_entry (o, h, flinfo->info); + if (hstub == NULL) + goto err_out; + + h = hstub->hcsect; + } + + } + + irel->r_vaddr += offset; + if (r_symndx != -1 && flinfo->info->strip != strip_all) { + if (h != NULL && h->smclas != XMC_TD && (irel->r_type == R_TOC @@ -5574,8 +6426,6 @@ xcoff_write_global_symbol (struct bfd_hash_entry *bh, void * inf) irel->r_symndx = obj_raw_syment_count (output_bfd); } - BFD_ASSERT (h->ldindx >= 0); - /* Initialize the aux union here instead of closer to when it is written out below because the length of the csect depends on whether the output is 32 or 64 bit. */ @@ -5607,9 +6457,43 @@ xcoff_write_global_symbol (struct bfd_hash_entry *bh, void * inf) flinfo->section_info[oindx].rel_hashes[osec->reloc_count] = NULL; ++osec->reloc_count; - if (!xcoff_create_ldrel (output_bfd, flinfo, osec, - output_bfd, irel, NULL, h)) - return false; + /* There are two kind of linker-created TOC entry. + The ones importing their symbols from outside, made for the + global linkage. These symbols have XCOFF_LDREL set and only + requires a loader relocation on their imported symbol. + On the other hand, symbols without XCOFF_LDREL are TOC entries + of internal symbols (like function descriptors made for stubs). + These symbols needs a loader relocation over .data and this + relocation must be applied. */ + + if ((h->flags & XCOFF_LDREL) != 0 + && h->ldindx >= 0) + { + if (!xcoff_create_ldrel (output_bfd, flinfo, osec, + output_bfd, irel, NULL, h)) + return false; + } + else + { + bfd_byte *p; + bfd_vma val; + + p = tocsec->contents + h->u.toc_offset; + val = (h->root.u.def.value + + h->root.u.def.section->output_section->vma + + h->root.u.def.section->output_offset); + + if (bfd_xcoff_is_xcoff64 (output_bfd)) + bfd_put_64 (output_bfd, val, p); + else if (bfd_xcoff_is_xcoff32 (output_bfd)) + bfd_put_32 (output_bfd, val, p); + else + return false; + + if (!xcoff_create_ldrel (output_bfd, flinfo, osec, + output_bfd, irel, h->root.u.def.section, h)) + return false; + } /* We need to emit a symbol to define a csect which holds the reloc. */ @@ -5836,7 +6720,12 @@ xcoff_write_global_symbol (struct bfd_hash_entry *bh, void * inf) isym.n_sclass = C_HIDEXT; aux.x_csect.x_smtyp = XTY_SD; - if ((h->flags & XCOFF_HAS_SIZE) != 0) + /* For stub symbols, the section already has its correct size. */ + if (h->root.u.def.section->owner == xcoff_hash_table (flinfo->info)->params->stub_bfd) + { + aux.x_csect.x_scnlen.l = h->root.u.def.section->size; + } + else if ((h->flags & XCOFF_HAS_SIZE) != 0) { for (l = xcoff_hash_table (flinfo->info)->size_list; l != NULL; @@ -6403,8 +7292,20 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info) sub = p->u.indirect.section->owner; if (! sub->output_has_begun) { - if (! xcoff_link_input_bfd (&flinfo, sub)) - goto error_return; + if (sub == xcoff_hash_table (info)->params->stub_bfd) + { + continue; + } + else + { + if (! xcoff_link_input_bfd (&flinfo, sub)) + { + _bfd_error_handler + (_("Unable to link input file: %s"), sub->filename); + bfd_set_error (bfd_error_sorry); + goto error_return; + } + } sub->output_has_begun = true; } } @@ -6451,6 +7352,12 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info) input files. */ bfd_hash_traverse (&info->hash->table, xcoff_write_global_symbol, &flinfo); + /* Write out the relocations created by stub entries. The symbols + will have been already written by xcoff_write_global_symbol. */ + bfd_hash_traverse (&xcoff_hash_table(info)->stub_hash_table, + xcoff_stub_create_relocations, + &flinfo); + free (flinfo.outsyms); flinfo.outsyms = NULL; @@ -6551,6 +7458,19 @@ _bfd_xcoff_bfd_final_link (bfd *abfd, struct bfd_link_info *info) flinfo.section_info = NULL; } + /* Write out the stub sections. */ + for (o = xcoff_hash_table (info)->params->stub_bfd->sections; + o != NULL; o = o->next) + { + if ((o->flags & SEC_HAS_CONTENTS) == 0 + || o->size == 0) + continue; + + if (!bfd_set_section_contents (abfd, o->output_section, o->contents, + (file_ptr) o->output_offset, o->size)) + goto error_return; + } + /* Write out the loader section contents. */ o = xcoff_hash_table (info)->loader_section; if (o != NULL diff --git a/bfd/xcofflink.h b/bfd/xcofflink.h index 92feeb58958..d647ba0a5df 100644 --- a/bfd/xcofflink.h +++ b/bfd/xcofflink.h @@ -18,6 +18,17 @@ Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA 02110-1301, USA. */ +/* Used to pass info between ld and bfd. */ +struct bfd_xcoff_link_params +{ + /* Linker stub bfd. */ + bfd *stub_bfd; + + /* Linker call-backs. */ + asection * (*add_stub_section) (const char *, asection *); + void (*layout_sections_again) (void); +}; + extern bool bfd_xcoff_split_import_path (bfd *, const char *, const char **, const char **); extern bool bfd_xcoff_set_archive_import_path @@ -41,3 +52,9 @@ extern bool bfd_xcoff_build_dynamic_sections (bfd *, struct bfd_link_info *); extern bool bfd_xcoff_link_generate_rtinit (bfd *, const char *, const char *, bool); +extern bool bfd_xcoff_link_init + (struct bfd_link_info *, struct bfd_xcoff_link_params *); +extern bool bfd_xcoff_size_stubs + (struct bfd_link_info *info); +extern bool bfd_xcoff_build_stubs + (struct bfd_link_info *info); diff --git a/ld/ChangeLog b/ld/ChangeLog index 285aae94202..86ac875c4d1 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,14 @@ +2022-04-20 Clément Chigot + + * emultempl/aix.em (params): New variable. + (stub_file): New variable. + (xcoff_add_stub_section): New function. + (xcoff_layout_sections_again): New function + (hook_in_stub): New function. + (_after_allocation): Add stub creation. + (_create_output_section_statements): Allocate stub file and + pass params to backend. + 2022-04-20 Clément Chigot * emultempl/aix.em (_after_allocation): New function. diff --git a/ld/emultempl/aix.em b/ld/emultempl/aix.em index 3878d58bb32..355e2a4ffff 100644 --- a/ld/emultempl/aix.em +++ b/ld/emultempl/aix.em @@ -63,6 +63,15 @@ static void gld${EMULATION_NAME}_free (void *); static void gld${EMULATION_NAME}_find_relocs (lang_statement_union_type *); static void gld${EMULATION_NAME}_find_exp_assignment (etree_type *); +static asection *xcoff_add_stub_section (const char *, asection *); +static void xcoff_layout_sections_again (void); + +static struct bfd_xcoff_link_params params = { + NULL, + &xcoff_add_stub_section, + &xcoff_layout_sections_again +}; + /* The file alignment required for each section. */ static unsigned long file_align; @@ -138,6 +147,9 @@ static int rtld; /* Explicit command line library path, -blibpath */ static char *command_line_blibpath = NULL; +/* Fake input file for stubs. */ +static lang_input_statement_type *stub_file; + /* This routine is called before anything else is done. */ static void @@ -154,6 +166,7 @@ gld${EMULATION_NAME}_before_parse (void) link_info.init_function = NULL; link_info.fini_function = NULL; + } /* Handle AIX specific options. */ @@ -1009,12 +1022,157 @@ gld${EMULATION_NAME}_before_allocation (void) before_allocation_default (); } +struct hook_stub_info +{ + lang_statement_list_type add; + asection *input_section; +}; + +/* Traverse the linker tree to find the spot where the stub goes. */ + +static bool +hook_in_stub (struct hook_stub_info *info, lang_statement_union_type **lp) +{ + lang_statement_union_type *l; + bool ret; + + for (; (l = *lp) != NULL; lp = &l->header.next) + { + switch (l->header.type) + { + case lang_constructors_statement_enum: + ret = hook_in_stub (info, &constructor_list.head); + if (ret) + return ret; + break; + + case lang_output_section_statement_enum: + ret = hook_in_stub (info, + &l->output_section_statement.children.head); + if (ret) + return ret; + break; + + case lang_wild_statement_enum: + ret = hook_in_stub (info, &l->wild_statement.children.head); + if (ret) + return ret; + break; + + case lang_group_statement_enum: + ret = hook_in_stub (info, &l->group_statement.children.head); + if (ret) + return ret; + break; + + case lang_input_section_enum: + if (l->input_section.section == info->input_section) + { + /* We've found our section. Insert the stub immediately + after its associated input section. */ + *(info->add.tail) = l->header.next; + l->header.next = info->add.head; + return true; + } + break; + + case lang_data_statement_enum: + case lang_reloc_statement_enum: + case lang_object_symbols_statement_enum: + case lang_output_statement_enum: + case lang_target_statement_enum: + case lang_input_statement_enum: + case lang_assignment_statement_enum: + case lang_padding_statement_enum: + case lang_address_statement_enum: + case lang_fill_statement_enum: + break; + + default: + FAIL (); + break; + } + } + return false; +} + +/* Call-back for bfd_xcoff_link_relocations. + Create a new stub section, and arrange for it to be linked + immediately before INPUT_SECTION. */ + +static asection * +xcoff_add_stub_section (const char *stub_sec_name, asection *input_section) +{ + asection *stub_sec; + flagword flags; + asection *output_section; + lang_output_section_statement_type *os; + struct hook_stub_info info; + + flags = (SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE + | SEC_HAS_CONTENTS | SEC_IN_MEMORY | SEC_KEEP); + stub_sec = bfd_make_section_anyway_with_flags (stub_file->the_bfd, + stub_sec_name, flags); + if (stub_sec == NULL) + goto err_ret; + + output_section = input_section->output_section; + os = lang_output_section_get (output_section); + + info.input_section = input_section; + lang_list_init (&info.add); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); + + if (info.add.head == NULL) + goto err_ret; + + if (hook_in_stub (&info, &os->children.head)) + return stub_sec; + + err_ret: + einfo (_("%X%P: can not make stub section: %E\n")); + return NULL; +} + +/* Another call-back for bfd_xcoff_link_relocations. */ + +static void +xcoff_layout_sections_again (void) +{ + /* If we have changed sizes of the stub sections, then we need + to recalculate all the section offsets. This may mean we need to + add even more stubs. */ + lang_relax_sections (true); +} + +/* Call the back-end to verify relocations. */ + static void gld${EMULATION_NAME}_after_allocation (void) { + + /* If generating a relocatable output file, then we don't have any + stubs. */ + if (stub_file != NULL && !bfd_link_relocatable (&link_info)) + { + /* Call into the BFD backend to do the real work. */ + if (!bfd_xcoff_size_stubs (&link_info)) + einfo (_("%X%P: can not size stub sections: %E\n")); + } + /* Now that everything is in place, finalize the dynamic sections. */ if (!bfd_xcoff_build_dynamic_sections (link_info.output_bfd, &link_info)) einfo (_("%F%P: failed to layout dynamic sections: %E\n")); + + if (!bfd_link_relocatable (&link_info)) + { + /* Now build the linker stubs. */ + if (stub_file != NULL && stub_file->the_bfd->sections != NULL) + { + if (! bfd_xcoff_build_stubs (&link_info)) + einfo (_("%X%P: can not build stubs: %E\n")); + } + } } static char * @@ -1503,11 +1661,35 @@ fragment <the_bfd = bfd_create ("linker stubs", link_info.output_bfd); + if (stub_file->the_bfd == NULL + || !bfd_set_arch_mach (stub_file->the_bfd, + bfd_get_arch (link_info.output_bfd), + bfd_get_mach (link_info.output_bfd))) + { + einfo (_("%F%P: can not create stub BFD: %E\n")); + return; + } + + stub_file->the_bfd->flags |= BFD_LINKER_CREATED; + ldlang_add_file (stub_file); + params.stub_bfd = stub_file->the_bfd; + + /* Pass linker params to the back-end. */ + if (!bfd_xcoff_link_init (&link_info, ¶ms)) + einfo (_("%F%P: can not init BFD: %E\n")); + /* __rtinit */ - if ((bfd_get_flavour (link_info.output_bfd) == bfd_target_xcoff_flavour) - && (link_info.init_function != NULL - || link_info.fini_function != NULL - || rtld)) + if (link_info.init_function != NULL + || link_info.fini_function != NULL + || rtld) { initfini_file = lang_add_input_file ("initfini", lang_input_file_is_file_enum, -- 2.30.2