From: Alan Modra Date: Wed, 13 Jan 2021 03:03:34 +0000 (+1030) Subject: SHF_LINK_ORDER fixup_link_order in ld X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b209b5a6b8a;p=binutils-gdb.git SHF_LINK_ORDER fixup_link_order in ld This moves the SHF_LINK_ORDER sorting from bfd_elf_final_link to the linker which means generic ELF targets now support SHF_LINK_ORDER and we cope with odd cases that require resizing of output sections. The patch also fixes two bugs in the current implementation, introduced by commit cd6d537c48fa. The pattern test used by that commit meant that sections matching something like "*(.IA_64.unwind* .gnu.linkonce.ia64unw.*)" would not properly sort a mix of sections matching the two wildcards. That commit also assumed a stable qsort. bfd/ PR 27160 * section.c (struct bfd_section): Remove pattern field. (BFD_FAKE_SECTION): Adjust to suit. * bfd-in2.h: Regenerate. * elflink.c (compare_link_order, elf_fixup_link_order): Delete. (bfd_elf_final_link): Don't call elf_fixup_link_order. ld/ PR 27160 * ldlang.h (lang_output_section_statement_type): Add data field. (lang_input_section_type, lang_section_bst_type): Add pattern field. (statement_list): Declare. (lang_add_section): Adjust prototype. * emultempl/aarch64elf.em: Adjust lang_add_section calls. * emultempl/armelf.em: Likewise. * emultempl/beos.em: Likewise. * emultempl/cskyelf.em: Likewise. * emultempl/hppaelf.em: Likewise. * emultempl/m68hc1xelf.em: Likewise. * emultempl/metagelf.em: Likewise. * emultempl/mipself.em: Likewise. * emultempl/mmo.em: Likewise. * emultempl/msp430.em: Likewise. * emultempl/nios2elf.em: Likewise. * emultempl/pe.em: Likewise. * emultempl/pep.em: Likewise. * emultempl/ppc64elf.em: Likewise. * emultempl/spuelf.em: Likewise. * emultempl/vms.em: Likewise. * ldelf.c: Likewise. * ldelfgen.c: Include ldctor.h. (struct os_sections): New. (add_link_order_input_section, link_order_scan): New functions. (compare_link_order, fixup_link_order): New functions. (ldelf_map_segments): Call link_order_scan and fixup_link_order. * ldlang.c (statement_list): Make global. (output_section_callback_fast): Save pattern in tree node. (lang_add_section): Add pattern parameter, save in lang_input_section. (output_section_callback_tree_to_list): Adjust lang_add_section calls. (lang_insert_orphan, output_section_callback): Likewise. (ldlang_place_orphan): Likewise. (gc_section_callback): Don't set section->pattern * testsuite/ld-elf/pr26256-2a.d: Don't xfail generic. * testsuite/ld-elf/pr26256-3b.d: Likewise. * testsuite/ld-elf/pr26256-2b.d: Likewise. notarget xgate. --- diff --git a/bfd/ChangeLog b/bfd/ChangeLog index 158b798c35c..0e12e74ea6e 100644 --- a/bfd/ChangeLog +++ b/bfd/ChangeLog @@ -1,3 +1,12 @@ +2021-01-13 Alan Modra + + PR 27160 + * section.c (struct bfd_section): Remove pattern field. + (BFD_FAKE_SECTION): Adjust to suit. + * bfd-in2.h: Regenerate. + * elflink.c (compare_link_order, elf_fixup_link_order): Delete. + (bfd_elf_final_link): Don't call elf_fixup_link_order. + 2021-01-12 H.J. Lu PR binutils/26792 diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h index 7eff85b7eaa..d142bb52213 100644 --- a/bfd/bfd-in2.h +++ b/bfd/bfd-in2.h @@ -1184,9 +1184,6 @@ typedef struct bfd_section struct bfd_symbol *symbol; struct bfd_symbol **symbol_ptr_ptr; - /* The matching section name pattern in linker script. */ - const char *pattern; - /* Early in the link process, map_head and map_tail are used to build a list of input sections attached to an output section. Later, output sections use these fields for a list of bfd_link_order @@ -1380,8 +1377,8 @@ discarded_section (const asection *sec) /* target_index, used_by_bfd, constructor_chain, owner, */ \ 0, NULL, NULL, NULL, \ \ - /* symbol, symbol_ptr_ptr, pattern, */ \ - (struct bfd_symbol *) SYM, &SEC.symbol, NULL, \ + /* symbol, symbol_ptr_ptr, */ \ + (struct bfd_symbol *) SYM, &SEC.symbol, \ \ /* map_head, map_tail, already_assigned */ \ { NULL }, { NULL }, NULL \ diff --git a/bfd/elflink.c b/bfd/elflink.c index acc959d526a..d20857eb6bd 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -11863,193 +11863,6 @@ elf_reloc_link_order (bfd *output_bfd, return TRUE; } - -/* Compare two sections based on the locations of the sections they are - linked to. Used by elf_fixup_link_order. */ - -static int -compare_link_order (const void *a, const void *b) -{ - const struct bfd_link_order *alo = *(const struct bfd_link_order **) a; - const struct bfd_link_order *blo = *(const struct bfd_link_order **) b; - asection *asec = elf_linked_to_section (alo->u.indirect.section); - asection *bsec = elf_linked_to_section (blo->u.indirect.section); - bfd_vma apos, bpos; - - /* Check if any sections are unordered. */ - if (asec == NULL || bsec == NULL) - { - /* Place unordered sections before ordered sections. */ - if (bsec != NULL) - return -1; - else if (asec != NULL) - return 1; - return 0; - } - - apos = asec->output_section->lma + asec->output_offset; - bpos = bsec->output_section->lma + bsec->output_offset; - - if (apos < bpos) - return -1; - if (apos > bpos) - return 1; - - /* The only way we should get matching LMAs is when the first of two - sections has zero size. */ - if (asec->size < bsec->size) - return -1; - if (asec->size > bsec->size) - return 1; - - /* If they are both zero size then they almost certainly have the same - VMA and thus are not ordered with respect to each other. Test VMA - anyway, and fall back to id to make the result reproducible across - qsort implementations. */ - apos = asec->output_section->vma + asec->output_offset; - bpos = bsec->output_section->vma + bsec->output_offset; - if (apos < bpos) - return -1; - if (apos > bpos) - return 1; - - return asec->id - bsec->id; -} - - -/* Looks for sections with SHF_LINK_ORDER set. Rearranges them into the same - order as their linked sections. Returns false if this could not be done - because an output section includes both ordered and unordered - sections. Ideally we'd do this in the linker proper. */ - -static bfd_boolean -elf_fixup_link_order (struct bfd_link_info *info, bfd *abfd, asection *o) -{ - size_t seen_linkorder; - size_t seen_other; - size_t n; - struct bfd_link_order *p; - bfd *sub; - struct bfd_link_order **sections, **indirect_sections; - asection *other_sec, *linkorder_sec; - bfd_vma offset; /* Octets. */ - - other_sec = NULL; - linkorder_sec = NULL; - seen_other = 0; - seen_linkorder = 0; - for (p = o->map_head.link_order; p != NULL; p = p->next) - { - if (p->type == bfd_indirect_link_order) - { - asection *s = p->u.indirect.section; - sub = s->owner; - if ((s->flags & SEC_LINKER_CREATED) == 0 - && bfd_get_flavour (sub) == bfd_target_elf_flavour - && elf_section_data (s) != NULL - && elf_linked_to_section (s) != NULL) - { - seen_linkorder++; - linkorder_sec = s; - } - else - { - seen_other++; - other_sec = s; - } - } - else - seen_other++; - - /* Allow mixed ordered and unordered input sections for - non-relocatable link. */ - if (bfd_link_relocatable (info) && seen_other && seen_linkorder) - { - if (other_sec && linkorder_sec) - _bfd_error_handler - /* xgettext:c-format */ - (_("%pA has both ordered [`%pA' in %pB] " - "and unordered [`%pA' in %pB] sections"), - o, linkorder_sec, linkorder_sec->owner, - other_sec, other_sec->owner); - else - _bfd_error_handler - (_("%pA has both ordered and unordered sections"), o); - bfd_set_error (bfd_error_bad_value); - return FALSE; - } - } - - if (!seen_linkorder) - return TRUE; - - /* Non-relocatable output can have both ordered and unordered input - sections. */ - seen_linkorder += seen_other; - - sections = bfd_malloc (seen_linkorder * sizeof (*sections)); - if (sections == NULL) - return FALSE; - - seen_linkorder = 0; - for (p = o->map_head.link_order; p != NULL; p = p->next) - sections[seen_linkorder++] = p; - - for (indirect_sections = sections, n = 0; - n < seen_linkorder; - indirect_sections++, n++) - { - /* Find the first bfd_indirect_link_order section. */ - if (indirect_sections[0]->type == bfd_indirect_link_order) - { - /* Count the consecutive bfd_indirect_link_order sections - with the same pattern. */ - size_t i, n_indirect; - const char *pattern - = indirect_sections[0]->u.indirect.section->pattern; - for (i = n + 1; i < seen_linkorder; i++) - if (sections[i]->type != bfd_indirect_link_order - || sections[i]->u.indirect.section->pattern != pattern) - break; - n_indirect = i - n; - /* Sort the bfd_indirect_link_order sections in the order of - their linked section. */ - qsort (indirect_sections, n_indirect, sizeof (*sections), - compare_link_order); - indirect_sections += n_indirect; - n += n_indirect; - } - } - - /* Change the offsets of the bfd_indirect_link_order sections. */ - offset = 0; - for (n = 0; n < seen_linkorder; n++) - if (sections[n]->type == bfd_indirect_link_order) - { - bfd_vma mask; - asection *s = sections[n]->u.indirect.section; - unsigned int opb = bfd_octets_per_byte (abfd, s); - - mask = ~(bfd_vma) 0 << s->alignment_power * opb; - offset = (offset + ~mask) & mask; - sections[n]->offset = s->output_offset = offset / opb; - offset += sections[n]->size; - } - else - offset = sections[n]->offset + sections[n]->size; - - free (sections); - - /* Verify that fixing up SHF_LINK_ORDER doesn't increase the section - size. */ - if (offset > o->size) - info->callbacks->einfo - (_("%F%P: %pA has ordered sections with incompatible alignments\n"), - o); - - return TRUE; -} - /* Generate an import library in INFO->implib_bfd from symbols in ABFD. Returns TRUE upon success, FALSE otherwise. */ @@ -12683,13 +12496,6 @@ bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) htab->tls_size = end - base; } - /* Reorder SHF_LINK_ORDER sections. */ - for (o = abfd->sections; o != NULL; o = o->next) - { - if (!elf_fixup_link_order (info, abfd, o)) - return FALSE; - } - if (!_bfd_elf_fixup_eh_frame_hdr (info)) return FALSE; diff --git a/bfd/section.c b/bfd/section.c index 10efc3c4aa7..3e6ba0c0938 100644 --- a/bfd/section.c +++ b/bfd/section.c @@ -541,9 +541,6 @@ CODE_FRAGMENT . struct bfd_symbol *symbol; . struct bfd_symbol **symbol_ptr_ptr; . -. {* The matching section name pattern in linker script. *} -. const char *pattern; -. . {* Early in the link process, map_head and map_tail are used to build . a list of input sections attached to an output section. Later, . output sections use these fields for a list of bfd_link_order @@ -737,8 +734,8 @@ CODE_FRAGMENT . {* target_index, used_by_bfd, constructor_chain, owner, *} \ . 0, NULL, NULL, NULL, \ . \ -. {* symbol, symbol_ptr_ptr, pattern, *} \ -. (struct bfd_symbol *) SYM, &SEC.symbol, NULL, \ +. {* symbol, symbol_ptr_ptr, *} \ +. (struct bfd_symbol *) SYM, &SEC.symbol, \ . \ . {* map_head, map_tail, already_assigned *} \ . { NULL }, { NULL }, NULL \ diff --git a/ld/ChangeLog b/ld/ChangeLog index 0224aef5a32..b1db01110d7 100644 --- a/ld/ChangeLog +++ b/ld/ChangeLog @@ -1,3 +1,43 @@ +2021-01-13 Alan Modra + + PR 27160 + * ldlang.h (lang_output_section_statement_type): Add data field. + (lang_input_section_type, lang_section_bst_type): Add pattern field. + (statement_list): Declare. + (lang_add_section): Adjust prototype. + * emultempl/aarch64elf.em: Adjust lang_add_section calls. + * emultempl/armelf.em: Likewise. + * emultempl/beos.em: Likewise. + * emultempl/cskyelf.em: Likewise. + * emultempl/hppaelf.em: Likewise. + * emultempl/m68hc1xelf.em: Likewise. + * emultempl/metagelf.em: Likewise. + * emultempl/mipself.em: Likewise. + * emultempl/mmo.em: Likewise. + * emultempl/msp430.em: Likewise. + * emultempl/nios2elf.em: Likewise. + * emultempl/pe.em: Likewise. + * emultempl/pep.em: Likewise. + * emultempl/ppc64elf.em: Likewise. + * emultempl/spuelf.em: Likewise. + * emultempl/vms.em: Likewise. + * ldelf.c: Likewise. + * ldelfgen.c: Include ldctor.h. + (struct os_sections): New. + (add_link_order_input_section, link_order_scan): New functions. + (compare_link_order, fixup_link_order): New functions. + (ldelf_map_segments): Call link_order_scan and fixup_link_order. + * ldlang.c (statement_list): Make global. + (output_section_callback_fast): Save pattern in tree node. + (lang_add_section): Add pattern parameter, save in lang_input_section. + (output_section_callback_tree_to_list): Adjust lang_add_section calls. + (lang_insert_orphan, output_section_callback): Likewise. + (ldlang_place_orphan): Likewise. + (gc_section_callback): Don't set section->pattern. + * testsuite/ld-elf/pr26256-2a.d: Don't xfail generic. + * testsuite/ld-elf/pr26256-3b.d: Likewise. + * testsuite/ld-elf/pr26256-2b.d: Likewise. notarget xgate. + 2021-01-13 Alan Modra * ldlang.h (callback_t): Remove flag_info function parameter. diff --git a/ld/emultempl/aarch64elf.em b/ld/emultempl/aarch64elf.em index bda5afcb1e9..a1855607327 100644 --- a/ld/emultempl/aarch64elf.em +++ b/ld/emultempl/aarch64elf.em @@ -192,7 +192,7 @@ elf${ELFSIZE}_aarch64_add_stub_section (const char *stub_sec_name, info.input_section = input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/armelf.em b/ld/emultempl/armelf.em index 0c031080bdb..a4cf93b493d 100644 --- a/ld/emultempl/armelf.em +++ b/ld/emultempl/armelf.em @@ -246,7 +246,7 @@ elf32_arm_add_stub_section (const char * stub_sec_name, info.input_section = after_input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/beos.em b/ld/emultempl/beos.em index 64c0e1102e3..bb4395f63ae 100644 --- a/ld/emultempl/beos.em +++ b/ld/emultempl/beos.em @@ -704,7 +704,7 @@ gld${EMULATION_NAME}_place_orphan (asection *s, The sections still have to be sorted, but that has to wait until all such sections have been processed by us. The sorting is done by sort_sections. */ - lang_add_section (&l->wild_statement.children, s, NULL, os); + lang_add_section (&l->wild_statement.children, s, NULL, NULL, os); return os; } diff --git a/ld/emultempl/cskyelf.em b/ld/emultempl/cskyelf.em index ce4047b49bb..ca38cf62b98 100644 --- a/ld/emultempl/cskyelf.em +++ b/ld/emultempl/cskyelf.em @@ -189,7 +189,7 @@ elf32_csky_add_stub_section (const char *stub_sec_name, info.input_section = input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/hppaelf.em b/ld/emultempl/hppaelf.em index bbb8b7f4d70..f195a177aba 100644 --- a/ld/emultempl/hppaelf.em +++ b/ld/emultempl/hppaelf.em @@ -193,7 +193,7 @@ hppaelf_add_stub_section (const char *stub_sec_name, asection *input_section) info.input_section = input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/m68hc1xelf.em b/ld/emultempl/m68hc1xelf.em index c4546c62777..212db7c4e10 100644 --- a/ld/emultempl/m68hc1xelf.em +++ b/ld/emultempl/m68hc1xelf.em @@ -275,7 +275,7 @@ m68hc11elf_add_stub_section (const char *stub_sec_name, at the correct place. */ info.input_section = tramp_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/metagelf.em b/ld/emultempl/metagelf.em index 51bec07f3c6..41ada3fbebb 100644 --- a/ld/emultempl/metagelf.em +++ b/ld/emultempl/metagelf.em @@ -169,7 +169,7 @@ metagelf_add_stub_section (const char *stub_sec_name, asection *input_section) info.input_section = input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/mipself.em b/ld/emultempl/mipself.em index d27aa769f83..e27e53cf556 100644 --- a/ld/emultempl/mipself.em +++ b/ld/emultempl/mipself.em @@ -175,7 +175,7 @@ mips_add_stub_section (const char *stub_sec_name, asection *input_section, /* Initialize a statement list that contains only the new statement. */ lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/mmo.em b/ld/emultempl/mmo.em index 4289e779bcb..fa0b19ae646 100644 --- a/ld/emultempl/mmo.em +++ b/ld/emultempl/mmo.em @@ -102,7 +102,7 @@ mmo_place_orphan (asection *s, (regardless of whether the linker script lists it as input). */ if (os != NULL) { - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); return os; } diff --git a/ld/emultempl/msp430.em b/ld/emultempl/msp430.em index e3ea3c69a90..7e364afda81 100644 --- a/ld/emultempl/msp430.em +++ b/ld/emultempl/msp430.em @@ -325,7 +325,7 @@ gld${EMULATION_NAME}_place_orphan (asection * s, /* Always place orphaned sections in lower. Optimal placement of either sections is performed later, once section sizes have been finalized. */ - lang_add_section (& lower->children, s, NULL, lower); + lang_add_section (& lower->children, s, NULL, NULL, lower); end: free (upper_name); free (lower_name); @@ -358,7 +358,8 @@ change_output_section (lang_statement_union_type **head, lang_statement_list_type *old_list = (lang_statement_list_type *) &old_os->children; s->output_section = NULL; - lang_add_section (&new_os->children, s, NULL, new_os); + lang_add_section (&new_os->children, s, + curr->input_section.pattern, NULL, new_os); /* Remove the section from the old output section. */ if (prev == NULL) diff --git a/ld/emultempl/nios2elf.em b/ld/emultempl/nios2elf.em index 29059809f11..fcc27561140 100644 --- a/ld/emultempl/nios2elf.em +++ b/ld/emultempl/nios2elf.em @@ -186,7 +186,7 @@ nios2elf_add_stub_section (const char *stub_sec_name, asection *input_section, info.input_section = input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em index ab7d4c485d2..f9060be8c6c 100644 --- a/ld/emultempl/pe.em +++ b/ld/emultempl/pe.em @@ -2085,7 +2085,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s, If the section already exists but does not have any flags set, then it has been created by the linker, probably as a result of a --section-start command line switch. */ - lang_add_section (&add_child, s, NULL, os); + lang_add_section (&add_child, s, NULL, NULL, os); break; } @@ -2099,7 +2099,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s, unused one and use that. */ if (os == NULL && match_by_name) { - lang_add_section (&match_by_name->children, s, NULL, match_by_name); + lang_add_section (&match_by_name->children, s, NULL, NULL, match_by_name); return match_by_name; } diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em index 3fdd605baf3..ca335b5aa6e 100644 --- a/ld/emultempl/pep.em +++ b/ld/emultempl/pep.em @@ -1905,7 +1905,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s, If the section already exists but does not have any flags set, then it has been created by the linker, probably as a result of a --section-start command line switch. */ - lang_add_section (&add_child, s, NULL, os); + lang_add_section (&add_child, s, NULL, NULL, os); break; } @@ -1919,7 +1919,7 @@ gld_${EMULATION_NAME}_place_orphan (asection *s, unused one and use that. */ if (os == NULL && match_by_name) { - lang_add_section (&match_by_name->children, s, NULL, match_by_name); + lang_add_section (&match_by_name->children, s, NULL, NULL, match_by_name); return match_by_name; } diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em index 403fd094c92..82536045395 100644 --- a/ld/emultempl/ppc64elf.em +++ b/ld/emultempl/ppc64elf.em @@ -445,7 +445,7 @@ ppc_add_stub_section (const char *stub_sec_name, asection *input_section) info.input_section = input_section; lang_list_init (&info.add); - lang_add_section (&info.add, stub_sec, NULL, os); + lang_add_section (&info.add, stub_sec, NULL, NULL, os); if (info.add.head == NULL) goto err_ret; diff --git a/ld/emultempl/spuelf.em b/ld/emultempl/spuelf.em index 2afad3e4dc5..0c51b8e98f8 100644 --- a/ld/emultempl/spuelf.em +++ b/ld/emultempl/spuelf.em @@ -151,7 +151,7 @@ spu_place_special_section (asection *s, asection *o, const char *output_name) lang_statement_list_type add; lang_list_init (&add); - lang_add_section (&add, s, NULL, os); + lang_add_section (&add, s, NULL, NULL, os); *add.tail = os->children.head; os->children.head = add.head; } @@ -168,7 +168,7 @@ spu_place_special_section (asection *s, asection *o, const char *output_name) lang_add_assignment (exp_assign (".", e_size, FALSE)); pop_stat_ptr (); } - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); } s->output_section->size += s->size; diff --git a/ld/emultempl/vms.em b/ld/emultempl/vms.em index 3aa00e3ab96..4c869626b9b 100644 --- a/ld/emultempl/vms.em +++ b/ld/emultempl/vms.em @@ -116,7 +116,7 @@ vms_place_orphan (asection *s, if (hold_data.os != NULL) { - lang_add_section (&hold_data.os->children, s, NULL, hold_data.os); + lang_add_section (&hold_data.os->children, s, NULL, NULL, hold_data.os); return hold_data.os; } else diff --git a/ld/ldelf.c b/ld/ldelf.c index f7407ab55a7..9887e53c17f 100644 --- a/ld/ldelf.c +++ b/ld/ldelf.c @@ -2006,7 +2006,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint) && (elf_section_data (os->bfd_section)->this_hdr.sh_info == elf_section_data (s)->this_hdr.sh_info)) { - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); return os; } @@ -2049,7 +2049,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint) || !elfoutput || elf_orphan_compatible (s, os->bfd_section))))) { - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); return os; } @@ -2063,7 +2063,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint) unused one and use that. */ if (match_by_name) { - lang_add_section (&match_by_name->children, s, NULL, match_by_name); + lang_add_section (&match_by_name->children, s, NULL, NULL, match_by_name); return match_by_name; } @@ -2088,7 +2088,7 @@ ldelf_place_orphan (asection *s, const char *secname, int constraint) && hold[orphan_text].os != NULL) { os = hold[orphan_text].os; - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); return os; } diff --git a/ld/ldelfgen.c b/ld/ldelfgen.c index f0502efaa27..8014e2229b9 100644 --- a/ld/ldelfgen.c +++ b/ld/ldelfgen.c @@ -27,20 +27,263 @@ #include "ldmisc.h" #include "ldexp.h" #include "ldlang.h" +#include "ldctor.h" #include "elf-bfd.h" #include "elf/internal.h" #include "ldelfgen.h" +/* Info attached to an output_section_statement about input sections, + used when sorting SHF_LINK_ORDER sections. */ + +struct os_sections +{ + /* Size allocated for isec. */ + unsigned int alloc; + /* Used entries in isec. */ + unsigned int count; + /* How many are SHF_LINK_ORDER. */ + unsigned int ordered; + /* Input sections attached to this output section. */ + struct os_sections_input { + lang_input_section_type *is; + unsigned int idx; + } isec[1]; +}; + +/* Add IS to data kept for OS. */ + +static bfd_boolean +add_link_order_input_section (lang_input_section_type *is, + lang_output_section_statement_type *os) +{ + struct os_sections *os_info = os->data; + asection *s; + + if (os_info == NULL) + { + os_info = xmalloc (sizeof (*os_info) + 63 * sizeof (*os_info->isec)); + os_info->alloc = 64; + os_info->count = 0; + os_info->ordered = 0; + os->data = os_info; + } + if (os_info->count == os_info->alloc) + { + size_t want; + os_info->alloc *= 2; + want = sizeof (*os_info) + (os_info->alloc - 1) * sizeof (*os_info->isec); + os_info = xrealloc (os_info, want); + os->data = os_info; + } + os_info->isec[os_info->count].is = is; + os_info->isec[os_info->count].idx = os_info->count; + os_info->count++; + s = is->section; + if ((s->flags & SEC_LINKER_CREATED) == 0 + && elf_section_data (s) != NULL + && elf_linked_to_section (s) != NULL) + os_info->ordered++; + return FALSE; +} + +/* Run over the linker's statement list, extracting info about input + sections attached to each output section. */ + +static bfd_boolean +link_order_scan (lang_statement_union_type *u, + lang_output_section_statement_type *os) +{ + asection *s; + bfd_boolean ret = FALSE; + + for (; u != NULL; u = u->header.next) + { + switch (u->header.type) + { + case lang_wild_statement_enum: + if (link_order_scan (u->wild_statement.children.head, os)) + ret = TRUE; + break; + case lang_constructors_statement_enum: + if (link_order_scan (constructor_list.head, os)) + ret = TRUE; + break; + case lang_output_section_statement_enum: + if (u->output_section_statement.constraint != -1 + && link_order_scan (u->output_section_statement.children.head, + &u->output_section_statement)) + ret = TRUE; + break; + case lang_group_statement_enum: + if (link_order_scan (u->group_statement.children.head, os)) + ret = TRUE; + break; + case lang_input_section_enum: + s = u->input_section.section; + if (s->output_section != NULL + && s->output_section->owner == link_info.output_bfd + && (s->output_section->flags & SEC_EXCLUDE) == 0 + && ((s->output_section->flags & SEC_HAS_CONTENTS) != 0 + || ((s->output_section->flags & (SEC_LOAD | SEC_THREAD_LOCAL)) + == (SEC_LOAD | SEC_THREAD_LOCAL)))) + if (add_link_order_input_section (&u->input_section, os)) + ret = TRUE; + break; + default: + break; + } + } + return ret; +} + +/* Compare two sections based on the locations of the sections they are + linked to. Used by fixup_link_order. */ + +static int +compare_link_order (const void *a, const void *b) +{ + const struct os_sections_input *ai = a; + const struct os_sections_input *bi = b; + asection *asec = elf_linked_to_section (ai->is->section); + asection *bsec = elf_linked_to_section (bi->is->section); + bfd_vma apos, bpos; + + /* Place unordered sections before ordered sections. */ + if (asec == NULL || bsec == NULL) + { + if (bsec != NULL) + return -1; + else if (asec != NULL) + return 1; + return ai->idx - bi->idx; + } + + apos = asec->output_section->lma + asec->output_offset; + bpos = bsec->output_section->lma + bsec->output_offset; + + if (apos < bpos) + return -1; + else if (apos > bpos) + return 1; + + /* The only way we should get matching LMAs is when the first of two + sections has zero size. */ + if (asec->size < bsec->size) + return -1; + else if (asec->size > bsec->size) + return 1; + + /* If they are both zero size then they almost certainly have the same + VMA and thus are not ordered with respect to each other. Test VMA + anyway, and fall back to id to make the result reproducible across + qsort implementations. */ + apos = asec->output_section->vma + asec->output_offset; + bpos = bsec->output_section->vma + bsec->output_offset; + if (apos < bpos) + return -1; + else if (apos > bpos) + return 1; + + return asec->id - bsec->id; +} + +/* Rearrange sections with SHF_LINK_ORDER into the same order as their + linked sections. */ + +static bfd_boolean +fixup_link_order (lang_output_section_statement_type *os) +{ + struct os_sections *os_info = os->data; + unsigned int i, j; + lang_input_section_type **orig_is; + asection **save_s; + + for (i = 0; i < os_info->count; i = j) + { + /* Normally a linker script will select SHF_LINK_ORDER sections + with an input section wildcard something like the following: + *(.IA_64.unwind* .gnu.linkonce.ia64unw.*) + However if some other random sections are smashed into an + output section, or if SHF_LINK_ORDER are split up by the + linker script, then we only want to sort sections matching a + given wildcard. That's the purpose of the pattern test. */ + for (j = i + 1; j < os_info->count; j++) + if (os_info->isec[j].is->pattern != os_info->isec[i].is->pattern) + break; + if (j - i > 1) + qsort (&os_info->isec[i], j - i, sizeof (*os_info->isec), + compare_link_order); + } + for (i = 0; i < os_info->count; i++) + if (os_info->isec[i].idx != i) + break; + if (i == os_info->count) + return FALSE; + + /* Now reorder the linker input section statements to reflect the + proper sorting. The is done by rewriting the existing statements + rather than fiddling with lists, since the only thing we need to + change is the bfd section pointer. */ + orig_is = xmalloc (os_info->count * sizeof (*orig_is)); + save_s = xmalloc (os_info->count * sizeof (*save_s)); + for (i = 0; i < os_info->count; i++) + { + orig_is[os_info->isec[i].idx] = os_info->isec[i].is; + save_s[i] = os_info->isec[i].is->section; + } + for (i = 0; i < os_info->count; i++) + if (os_info->isec[i].idx != i) + { + orig_is[i]->section = save_s[i]; + /* Restore os_info to pristine state before the qsort, for the + next pass over sections. */ + os_info->isec[i].is = orig_is[i]; + os_info->isec[i].idx = i; + } + free (save_s); + free (orig_is); + return TRUE; +} + void ldelf_map_segments (bfd_boolean need_layout) { int tries = 10; + static bfd_boolean done_link_order_scan = FALSE; do { lang_relax_sections (need_layout); need_layout = FALSE; + if (link_info.output_bfd->xvec->flavour == bfd_target_elf_flavour) + { + lang_output_section_statement_type *os; + if (!done_link_order_scan) + { + link_order_scan (statement_list.head, NULL); + done_link_order_scan = TRUE; + } + for (os = (void *) lang_os_list.head; os != NULL; os = os->next) + { + struct os_sections *os_info = os->data; + if (os_info != NULL && os_info->ordered != 0) + { + if (os_info->ordered != os_info->count + && bfd_link_relocatable (&link_info)) + { + einfo (_("%F%P: " + "%pA has both ordered and unordered sections"), + os->bfd_section); + return; + } + if (os_info->count > 1 + && fixup_link_order (os)) + need_layout = TRUE; + } + } + } + if (link_info.output_bfd->xvec->flavour == bfd_target_elf_flavour && !bfd_link_relocatable (&link_info)) { diff --git a/ld/ldlang.c b/ld/ldlang.c index 4d3560fb1d2..4ae9cec8853 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -69,13 +69,6 @@ static bfd_boolean map_option_f; static bfd_vma print_dot; static lang_input_statement_type *first_file; static const char *current_target; -/* Header for list of statements corresponding to any files involved in the - link, either specified from the command-line or added implicitely (eg. - archive member used to resolved undefined symbol, wildcard statement from - linker script, etc.). Next pointer is in next field of a - lang_statement_header_type (reached via header field in a - lang_statement_union). */ -static lang_statement_list_type statement_list; static lang_statement_list_type *stat_save[10]; static lang_statement_list_type **stat_save_ptr = &stat_save[0]; static struct unique_sections *unique_section_list; @@ -103,6 +96,13 @@ static void lang_do_memory_regions (bfd_boolean); /* Exported variables. */ const char *output_target; lang_output_section_statement_type *abs_output_section; +/* Header for list of statements corresponding to any files involved in the + link, either specified from the command-line or added implicitely (eg. + archive member used to resolved undefined symbol, wildcard statement from + linker script, etc.). Next pointer is in next field of a + lang_statement_header_type (reached via header field in a + lang_statement_union). */ +lang_statement_list_type statement_list; lang_statement_list_type lang_os_list; lang_statement_list_type *stat_ptr = &statement_list; /* Header for list of statements corresponding to files used in the final @@ -582,6 +582,7 @@ output_section_callback_fast (lang_wild_statement_type *ptr, node->left = 0; node->right = 0; node->section = section; + node->pattern = ptr->section_list; tree = wild_sort_fast (ptr, sec, file, section); if (tree != NULL) @@ -598,7 +599,7 @@ output_section_callback_tree_to_list (lang_wild_statement_type *ptr, if (tree->left) output_section_callback_tree_to_list (ptr, tree->left, output); - lang_add_section (&ptr->children, tree->section, NULL, + lang_add_section (&ptr->children, tree->section, tree->pattern, NULL, (lang_output_section_statement_type *) output); if (tree->right) @@ -1896,7 +1897,7 @@ lang_insert_orphan (asection *s, if (add_child == NULL) add_child = &os->children; - lang_add_section (add_child, s, NULL, os); + lang_add_section (add_child, s, NULL, NULL, os); if (after && (s->flags & (SEC_LOAD | SEC_ALLOC)) != 0) { @@ -2537,6 +2538,7 @@ lang_discard_section_p (asection *section) void lang_add_section (lang_statement_list_type *ptr, asection *section, + struct wildcard_list *pattern, struct flag_info *sflag_info, lang_output_section_statement_type *output) { @@ -2717,6 +2719,7 @@ lang_add_section (lang_statement_list_type *ptr, /* Add a section reference to the list. */ new_section = new_stat (lang_input_section, ptr); new_section->section = section; + new_section->pattern = pattern; } /* Handle wildcard sorting. This returns the lang_input_section which @@ -2842,14 +2845,16 @@ output_section_callback (lang_wild_statement_type *ptr, of the current list. */ if (before == NULL) - lang_add_section (&ptr->children, section, ptr->section_flag_list, os); + lang_add_section (&ptr->children, section, ptr->section_list, + ptr->section_flag_list, os); else { lang_statement_list_type list; lang_statement_union_type **pp; lang_list_init (&list); - lang_add_section (&list, section, ptr->section_flag_list, os); + lang_add_section (&list, section, ptr->section_list, + ptr->section_flag_list, os); /* If we are discarding the section, LIST.HEAD will be NULL. */ @@ -7204,7 +7209,7 @@ ldlang_place_orphan (asection *s) && (bfd_link_relocatable (&link_info) || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)) os->addr_tree = exp_intop (0); - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); } else { @@ -7227,7 +7232,7 @@ ldlang_place_orphan (asection *s) && (bfd_link_relocatable (&link_info) || (s->flags & (SEC_LOAD | SEC_ALLOC)) == 0)) os->addr_tree = exp_intop (0); - lang_add_section (&os->children, s, NULL, os); + lang_add_section (&os->children, s, NULL, NULL, os); } if (config.orphan_handling == orphan_handling_warn) @@ -7271,7 +7276,7 @@ lang_place_orphans (void) default_common_section = lang_output_section_statement_lookup (".bss", 0, 1); lang_add_section (&default_common_section->children, s, - NULL, default_common_section); + NULL, NULL, default_common_section); } } else @@ -7485,7 +7490,7 @@ lang_reset_memory_regions (void) static void gc_section_callback (lang_wild_statement_type *ptr, - struct wildcard_list *sec, + struct wildcard_list *sec ATTRIBUTE_UNUSED, asection *section, lang_input_statement_type *file ATTRIBUTE_UNUSED, void *data ATTRIBUTE_UNUSED) @@ -7494,8 +7499,6 @@ gc_section_callback (lang_wild_statement_type *ptr, should be as well. */ if (ptr->keep_sections) section->flags |= SEC_KEEP; - if (sec) - section->pattern = sec->spec.name; } /* Iterate over sections marking them against GC. */ diff --git a/ld/ldlang.h b/ld/ldlang.h index 205c305ccea..3463d4ca0cc 100644 --- a/ld/ldlang.h +++ b/ld/ldlang.h @@ -158,6 +158,9 @@ typedef struct lang_output_section_statement_struct lang_output_section_phdr_list *phdrs; + /* Used by ELF SHF_LINK_ORDER sorting. */ + void *data; + unsigned int block_value; int constraint; flagword flags; @@ -323,6 +326,7 @@ typedef struct { lang_statement_header_type header; asection *section; + void *pattern; } lang_input_section_type; struct map_symbol_def { @@ -364,6 +368,7 @@ typedef bfd_boolean (*lang_match_sec_type_func) (bfd *, const asection *, typedef struct lang_section_bst { asection *section; + void *pattern; struct lang_section_bst *left; struct lang_section_bst *right; } lang_section_bst_type; @@ -506,6 +511,7 @@ extern lang_output_section_statement_type *abs_output_section; extern lang_statement_list_type lang_os_list; extern struct lang_input_statement_flags input_flags; extern bfd_boolean lang_has_input_file; +extern lang_statement_list_type statement_list; extern lang_statement_list_type *stat_ptr; extern bfd_boolean delete_output_file_on_failure; @@ -650,7 +656,7 @@ extern void lang_enter_group extern void lang_leave_group (void); extern void lang_add_section - (lang_statement_list_type *, asection *, + (lang_statement_list_type *, asection *, struct wildcard_list *, struct flag_info *, lang_output_section_statement_type *); extern void lang_new_phdr (const char *, etree_type *, bfd_boolean, bfd_boolean, etree_type *, diff --git a/ld/testsuite/ld-elf/pr26256-2a.d b/ld/testsuite/ld-elf/pr26256-2a.d index 03804d844d7..24e8e67ec36 100644 --- a/ld/testsuite/ld-elf/pr26256-2a.d +++ b/ld/testsuite/ld-elf/pr26256-2a.d @@ -1,7 +1,6 @@ #source: pr26256-2.s #ld: -e _start -T pr26256-2.t #nm: -n -#xfail: [is_generic] #... [0-9a-f]+ R linkorder2 diff --git a/ld/testsuite/ld-elf/pr26256-2b.d b/ld/testsuite/ld-elf/pr26256-2b.d index 60c3bff7cd3..3f8c37e267b 100644 --- a/ld/testsuite/ld-elf/pr26256-2b.d +++ b/ld/testsuite/ld-elf/pr26256-2b.d @@ -1,8 +1,7 @@ #source: pr26256-2.s #ld: -e _start #nm: -n -#xfail: [is_generic] -#notarget: fr30-*-* iq2000-*-* ip2k-*-* xstormy16-*-* +#notarget: fr30-*-* iq2000-*-* ip2k-*-* xgate-*-* xstormy16-*-* # These targets place .linkorder sections before .text sections. #... diff --git a/ld/testsuite/ld-elf/pr26256-3b.d b/ld/testsuite/ld-elf/pr26256-3b.d index 7d6dff24fbd..8a5e6dd59eb 100644 --- a/ld/testsuite/ld-elf/pr26256-3b.d +++ b/ld/testsuite/ld-elf/pr26256-3b.d @@ -1,7 +1,6 @@ #source: pr26256-3.s #ld: -e _start -T pr26256-3b.t #readelf: -x .rodata -x .text -#xfail: [is_generic] Hex dump of section \'.rodata\': 0x[a-f0-9]+ +00020301 +040907 +.+