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.
This commit is contained in:
Alan Modra
2025-12-25 22:17:10 +10:30
parent 2254b6eb07
commit 4dc7130975
7 changed files with 112 additions and 93 deletions

View File

@@ -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

View File

@@ -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);
}
/*

View File

@@ -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

View File

@@ -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

View File

@@ -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;

View File

@@ -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

View File

@@ -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,