From 4dc7130975cfcbc263f5bad2aa9e35fc3eb10d5c Mon Sep 17 00:00:00 2001 From: Alan Modra Date: Thu, 25 Dec 2025 22:17:10 +1030 Subject: [PATCH] PR 33726, symbols in excluded sections This improves "nearby" section choice when memory regions are active, preferring a section in the same region as the excluded section over other sections. PR 33726 include/ * bfdlink.h (struct bfd_link_callbacks): Add nearby_section. (_bfd_nearby_section): Delete. (bfd_fix_excluded_sec_syms): Rename and remove bfd param from _bfd_fix_excluded_sec_syms. bfd/ * linker.c (_bfd_nearby_section): Delete. (fix_syms): Use linker callback. * elflink.c (elf_link_input_bfd): Likewise. (_bfd_elf_final_link): Update. ld/ * ldemul.c (finish_default): Update. * ldlang.c (lang_output_section_get): Delete. (ldlang_nearby_section): New function. * ldlang.h (ldlang_nearby_section): Declare. (lang_output_section_get): New static inline. * ldmain.c (link_callbacks): Add ldlang_nearby_section. --- bfd/elflink.c | 6 +-- bfd/linker.c | 80 +++------------------------------------- include/bfdlink.h | 11 +++--- ld/ldemul.c | 2 +- ld/ldlang.c | 93 +++++++++++++++++++++++++++++++++++++++++++---- ld/ldlang.h | 12 +++++- ld/ldmain.c | 1 + 7 files changed, 112 insertions(+), 93 deletions(-) diff --git a/bfd/elflink.c b/bfd/elflink.c index 3f60dba1fef..d3084720382 100644 --- a/bfd/elflink.c +++ b/bfd/elflink.c @@ -12113,8 +12113,8 @@ elf_link_input_bfd (struct elf_final_link_info *flinfo, bfd *input_bfd) if (r_symndx == STN_UNDEF) { irela->r_addend += osec->vma; - osec = _bfd_nearby_section (output_bfd, osec, - osec->vma); + osec = flinfo->info->callbacks->nearby_section + (output_bfd, osec, osec->vma); irela->r_addend -= osec->vma; r_symndx = osec->target_index; } @@ -12755,7 +12755,7 @@ _bfd_elf_final_link (bfd *abfd, struct bfd_link_info *info) } } if (sections_removed) - _bfd_fix_excluded_sec_syms (abfd, info); + bfd_fix_excluded_sec_syms (info); /* Count up the number of relocations we will output for each output section, so that we know the sizes of the reloc sections. We diff --git a/bfd/linker.c b/bfd/linker.c index 247c259a95e..137184a6c12 100644 --- a/bfd/linker.c +++ b/bfd/linker.c @@ -3093,82 +3093,13 @@ _bfd_generic_section_already_linked (bfd *abfd ATTRIBUTE_UNUSED, return false; } -/* Choose a neighbouring section to S in OBFD that will be output, or - the absolute section if ADDR is out of bounds of the neighbours. */ - -asection * -_bfd_nearby_section (bfd *obfd, asection *s, bfd_vma addr) -{ - asection *next, *prev, *best; - - /* Find preceding kept section. */ - for (prev = s->prev; prev != NULL; prev = prev->prev) - if ((prev->flags & SEC_EXCLUDE) == 0 - && !bfd_section_removed_from_list (obfd, prev)) - break; - - /* Find following kept section. Start at prev->next because - other sections may have been added after S was removed. */ - if (s->prev != NULL) - next = s->prev->next; - else - next = s->owner->sections; - for (; next != NULL; next = next->next) - if ((next->flags & SEC_EXCLUDE) == 0 - && !bfd_section_removed_from_list (obfd, next)) - break; - - /* Choose better of two sections, based on flags. The idea - is to choose a section that will be in the same segment - as S would have been if it was kept. */ - best = next; - if (prev == NULL) - { - if (next == NULL) - best = bfd_abs_section_ptr; - } - else if (next == NULL) - best = prev; - else if (((prev->flags ^ next->flags) - & (SEC_ALLOC | SEC_THREAD_LOCAL | SEC_LOAD)) != 0) - { - if (((next->flags ^ s->flags) - & (SEC_ALLOC | SEC_THREAD_LOCAL)) != 0 - /* We prefer to choose a loaded section. Section S - doesn't have SEC_LOAD set (it being excluded, that - part of the flag processing didn't happen) so we - can't compare that flag to those of NEXT and PREV. */ - || ((prev->flags & SEC_LOAD) != 0 - && (next->flags & SEC_LOAD) == 0)) - best = prev; - } - else if (((prev->flags ^ next->flags) & SEC_READONLY) != 0) - { - if (((next->flags ^ s->flags) & SEC_READONLY) != 0) - best = prev; - } - else if (((prev->flags ^ next->flags) & SEC_CODE) != 0) - { - if (((next->flags ^ s->flags) & SEC_CODE) != 0) - best = prev; - } - else - { - /* Flags we care about are the same. Prefer the following - section if that will result in a positive valued sym. */ - if (addr < next->vma) - best = prev; - } - - return best; -} - /* Convert symbols in excluded output sections to use a kept section. */ static bool fix_syms (struct bfd_link_hash_entry *h, void *data) { - bfd *obfd = (bfd *) data; + struct bfd_link_info *info = data; + bfd *obfd = info->output_bfd; if (h->type == bfd_link_hash_defined || h->type == bfd_link_hash_defweak) @@ -3182,7 +3113,8 @@ fix_syms (struct bfd_link_hash_entry *h, void *data) asection *op; h->u.def.value += s->output_offset + s->output_section->vma; - op = _bfd_nearby_section (obfd, s->output_section, h->u.def.value); + op = info->callbacks->nearby_section (obfd, s->output_section, + h->u.def.value); h->u.def.value -= op->vma; h->u.def.section = op; } @@ -3192,9 +3124,9 @@ fix_syms (struct bfd_link_hash_entry *h, void *data) } void -_bfd_fix_excluded_sec_syms (bfd *obfd, struct bfd_link_info *info) +bfd_fix_excluded_sec_syms (struct bfd_link_info *info) { - bfd_link_hash_traverse (info->hash, fix_syms, obfd); + bfd_link_hash_traverse (info->hash, fix_syms, info); } /* diff --git a/include/bfdlink.h b/include/bfdlink.h index 00fe0f8c7c8..0bd2132df7c 100644 --- a/include/bfdlink.h +++ b/include/bfdlink.h @@ -900,6 +900,11 @@ struct bfd_link_callbacks (struct bfd_link_info *, bfd * abfd, asection * current_section, asection * previous_section, bool new_segment); + /* Choose a neighbouring section to the given excluded section, or + the absolute section if no suitable neighbours are found that + will be output. */ + asection *(*nearby_section) + (bfd *, asection *, bfd_vma); /* This callback provides a chance for callers of the BFD to examine the ELF (dynamic) string table once it is complete. */ void (*examine_strtab) @@ -1028,11 +1033,7 @@ extern bool _bfd_handle_already_linked (struct bfd_section *, struct bfd_section_already_linked *, struct bfd_link_info *); -extern struct bfd_section *_bfd_nearby_section - (bfd *, struct bfd_section *, bfd_vma); - -extern void _bfd_fix_excluded_sec_syms - (bfd *, struct bfd_link_info *); +extern void bfd_fix_excluded_sec_syms (struct bfd_link_info *); /* These structures are used to describe version information for the ELF linker. These structures could be manipulated entirely inside diff --git a/ld/ldemul.c b/ld/ldemul.c index 35f91a287ca..b8517a917e1 100644 --- a/ld/ldemul.c +++ b/ld/ldemul.c @@ -314,7 +314,7 @@ finish_default (void) os->data = NULL; } if (!bfd_link_relocatable (&link_info)) - _bfd_fix_excluded_sec_syms (link_info.output_bfd, &link_info); + bfd_fix_excluded_sec_syms (&link_info); } void diff --git a/ld/ldlang.c b/ld/ldlang.c index 9963d5eb98f..17fb249ef1a 100644 --- a/ld/ldlang.c +++ b/ld/ldlang.c @@ -1578,14 +1578,6 @@ lang_memory_default (asection *section) return lang_memory_region_lookup (DEFAULT_MEMORY_REGION, false); } -/* Get the output section statement directly from the userdata. */ - -lang_output_section_statement_type * -lang_output_section_get (const asection *output_section) -{ - return bfd_section_userdata (output_section); -} - /* Find or create an output_section_statement with the given NAME. If CONSTRAINT is non-zero match one with that constraint, otherwise match any non-negative constraint. If CREATE is 0 return NULL when @@ -6983,6 +6975,91 @@ section_for_dot (void) return bfd_abs_section_ptr; } +/* Choose a neighbouring section to S in OBFD that will be output, or + the absolute section if no suitable neighbours are found. This is + used to give symbols in excluded sections another section. */ + +asection * +ldlang_nearby_section (bfd *obfd, asection *s, bfd_vma addr) +{ + asection *next, *prev, *best; + lang_memory_region_type *region = lang_output_section_get (s)->region; + int match; + + /* Try for a neighbour in the same region first. If there are none, + then accept sections in other regions. */ + for (match = 1; match >= 0; --match) + { + /* Find preceding kept section. */ + for (prev = s->prev; prev != NULL; prev = prev->prev) + if ((prev->flags & SEC_EXCLUDE) == 0 + && !bfd_section_removed_from_list (obfd, prev) + && (lang_output_section_get (prev)->region == region || !match)) + break; + + /* Find following kept section. Start at prev->next because + other sections may have been added after S was removed. */ + if (s->prev != NULL) + next = s->prev->next; + else + next = s->owner->sections; + for (; next != NULL; next = next->next) + if ((next->flags & SEC_EXCLUDE) == 0 + && !bfd_section_removed_from_list (obfd, next) + && (lang_output_section_get (next)->region == region || !match)) + break; + + /* Choose better of two sections, based on flags. The idea + is to choose a section that will be in the same segment + as S would have been if it was kept. */ + best = next; + if (prev == NULL) + ; + else if (next == NULL) + best = prev; + else if (((prev->flags ^ next->flags) + & (SEC_ALLOC | SEC_THREAD_LOCAL | SEC_LOAD)) != 0) + { + if (((next->flags ^ s->flags) + & (SEC_ALLOC | SEC_THREAD_LOCAL)) != 0 + /* We prefer to choose a loaded section. Section S + doesn't have SEC_LOAD set (it being excluded, that + part of the flag processing didn't happen) so we + can't compare that flag to those of NEXT and PREV. */ + || ((prev->flags & SEC_LOAD) != 0 + && (next->flags & SEC_LOAD) == 0)) + best = prev; + } + else if (((prev->flags ^ next->flags) & SEC_READONLY) != 0) + { + if (((next->flags ^ s->flags) & SEC_READONLY) != 0) + best = prev; + } + else if (((prev->flags ^ next->flags) & SEC_CODE) != 0) + { + if (((next->flags ^ s->flags) & SEC_CODE) != 0) + best = prev; + } + else + { + /* Flags we care about are the same. Prefer the following + section if that will result in a positive valued sym. */ + if (addr < next->vma) + best = prev; + } + if (best != NULL) + return best; + } + /* For those targets that implement absolute symbols "properly" in + ld and ld.so, ie. their value is not relocated, it is very likely + wrong to transform a symbol in a removed section to an absolute + symbol. In a PIE or shared library a symbol value in an + allocated section ought to be relocated by the base address. + However, we will only get here if there are no sections at all, + so this should not be a concern except in odd testcases. */ + return bfd_abs_section_ptr; +} + /* Array of __start/__stop/.startof./.sizeof/ symbols. */ static struct bfd_link_hash_entry **start_stop_syms; diff --git a/ld/ldlang.h b/ld/ldlang.h index a9607bef765..ea7654fad2a 100644 --- a/ld/ldlang.h +++ b/ld/ldlang.h @@ -607,6 +607,8 @@ extern void lang_do_assignments (lang_phase_type); extern asection *section_for_dot (void); +extern asection *ldlang_nearby_section + (bfd *, asection *, bfd_vma); #define LANG_FOR_EACH_INPUT_STATEMENT(statement) \ lang_input_statement_type *statement; \ @@ -631,8 +633,6 @@ extern lang_input_statement_type *lang_add_input_file (const char *, lang_input_file_enum_type, const char *); extern void lang_add_keepsyms_file (const char *); -extern lang_output_section_statement_type *lang_output_section_get - (const asection *); extern lang_output_section_statement_type *lang_output_section_statement_lookup (const char *, int, int); extern lang_output_section_statement_type *next_matching_output_section_statement @@ -794,4 +794,12 @@ extern void cmdline_emit_object_only_section (void); extern void cmdline_check_object_only_section (bfd *, bool); extern void cmdline_remove_object_only_files (void); +/* Get the output section statement from section userdata. */ + +static inline lang_output_section_statement_type * +lang_output_section_get (const asection *output_section) +{ + return bfd_section_userdata (output_section); +} + #endif diff --git a/ld/ldmain.c b/ld/ldmain.c index afffdd2e43d..bfd3923024b 100644 --- a/ld/ldmain.c +++ b/ld/ldmain.c @@ -155,6 +155,7 @@ static struct bfd_link_callbacks link_callbacks = info_msg, minfo, ldlang_override_segment_assignment, + ldlang_nearby_section, ldlang_ctf_acquire_strings, NULL, ldlang_ctf_new_dynsym,