summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlan Modra <amodra@bigpond.net.au>2012-03-23 09:27:40 +0000
committerAlan Modra <amodra@bigpond.net.au>2012-03-23 09:27:40 +0000
commit8efd1781af338ecd687515ff6a34d3ab1fe044ed (patch)
tree42f28d1c81a6ba78aba1bfc9ec258aecbe926cd9
parent20a90f314c776b2f708361bbacee0ad0ab7dcbb1 (diff)
downloadbinutils-redhat-8efd1781af338ecd687515ff6a34d3ab1fe044ed.tar.gz
* linker.c (_bfd_nearby_section): New function, split out from..
(fix_syms): ..here. * bfd-in.h (_bfd_nearby_section): Declare. * bfd-in2.h: Regenerate. * elflink.c (elf_link_input_bfd): Don't use text_index_section or data_index_section with ld -q or ld -r output relocs against stripped output sections. Instead use _bfd_nearby_section.
-rw-r--r--bfd/ChangeLog10
-rw-r--r--bfd/bfd-in.h3
-rw-r--r--bfd/bfd-in2.h3
-rw-r--r--bfd/elflink.c21
-rw-r--r--bfd/linker.c143
5 files changed, 98 insertions, 82 deletions
diff --git a/bfd/ChangeLog b/bfd/ChangeLog
index 854beee0a5..c59bb4c6a9 100644
--- a/bfd/ChangeLog
+++ b/bfd/ChangeLog
@@ -1,5 +1,15 @@
2012-03-23 Alan Modra <amodra@gmail.com>
+ * linker.c (_bfd_nearby_section): New function, split out from..
+ (fix_syms): ..here.
+ * bfd-in.h (_bfd_nearby_section): Declare.
+ * bfd-in2.h: Regenerate.
+ * elflink.c (elf_link_input_bfd): Don't use text_index_section or
+ data_index_section with ld -q or ld -r output relocs against
+ stripped output sections. Instead use _bfd_nearby_section.
+
+2012-03-23 Alan Modra <amodra@gmail.com>
+
PR binutils/13894
* elf64-ppc.c (opd_entry_value): Read full symbol table when
sym hashes unavailable.
diff --git a/bfd/bfd-in.h b/bfd/bfd-in.h
index 40ed786628..c331f3bbf8 100644
--- a/bfd/bfd-in.h
+++ b/bfd/bfd-in.h
@@ -705,6 +705,9 @@ extern int bfd_get_sign_extend_vma
extern struct bfd_section *_bfd_elf_tls_setup
(bfd *, 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 *);
diff --git a/bfd/bfd-in2.h b/bfd/bfd-in2.h
index bea3a05d4d..61513f20ed 100644
--- a/bfd/bfd-in2.h
+++ b/bfd/bfd-in2.h
@@ -712,6 +712,9 @@ extern int bfd_get_sign_extend_vma
extern struct bfd_section *_bfd_elf_tls_setup
(bfd *, 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 *);
diff --git a/bfd/elflink.c b/bfd/elflink.c
index 7f9ec609b5..3236911744 100644
--- a/bfd/elflink.c
+++ b/bfd/elflink.c
@@ -9747,23 +9747,12 @@ elf_link_input_bfd (struct elf_final_link_info *finfo, bfd *input_bfd)
r_symndx = osec->target_index;
if (r_symndx == STN_UNDEF)
{
- struct elf_link_hash_table *htab;
- asection *oi;
-
- htab = elf_hash_table (finfo->info);
- oi = htab->text_index_section;
- if ((osec->flags & SEC_READONLY) == 0
- && htab->data_index_section != NULL)
- oi = htab->data_index_section;
-
- if (oi != NULL)
- {
- irela->r_addend += osec->vma - oi->vma;
- r_symndx = oi->target_index;
- }
+ irela->r_addend += osec->vma;
+ osec = _bfd_nearby_section (output_bfd, osec,
+ osec->vma);
+ irela->r_addend -= osec->vma;
+ r_symndx = osec->target_index;
}
-
- BFD_ASSERT (r_symndx != STN_UNDEF);
}
}
diff --git a/bfd/linker.c b/bfd/linker.c
index 04044743f2..2f8ecbb3da 100644
--- a/bfd/linker.c
+++ b/bfd/linker.c
@@ -3130,6 +3130,81 @@ _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;
+ }
+
+ /* Refuse to choose a section for which we are out of bounds. */
+ /* ??? This may make most of the above moot. */
+ if (addr < best->vma || addr > best->vma + best->size)
+ best = bfd_abs_section_ptr;
+
+ return best;
+}
+
/* Convert symbols in excluded output sections to use a kept section. */
static bfd_boolean
@@ -3146,74 +3221,10 @@ fix_syms (struct bfd_link_hash_entry *h, void *data)
&& (s->output_section->flags & SEC_EXCLUDE) != 0
&& bfd_section_removed_from_list (obfd, s->output_section))
{
- asection *op, *op1;
+ asection *op;
h->u.def.value += s->output_offset + s->output_section->vma;
-
- /* Find preceding kept section. */
- for (op1 = s->output_section->prev; op1 != NULL; op1 = op1->prev)
- if ((op1->flags & SEC_EXCLUDE) == 0
- && !bfd_section_removed_from_list (obfd, op1))
- break;
-
- /* Find following kept section. Start at prev->next because
- other sections may have been added after S was removed. */
- if (s->output_section->prev != NULL)
- op = s->output_section->prev->next;
- else
- op = s->output_section->owner->sections;
- for (; op != NULL; op = op->next)
- if ((op->flags & SEC_EXCLUDE) == 0
- && !bfd_section_removed_from_list (obfd, op))
- 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. */
- if (op1 == NULL)
- {
- if (op == NULL)
- op = bfd_abs_section_ptr;
- }
- else if (op == NULL)
- op = op1;
- else if (((op1->flags ^ op->flags)
- & (SEC_ALLOC | SEC_THREAD_LOCAL | SEC_LOAD)) != 0)
- {
- if (((op->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 OP and OP1. */
- || ((op1->flags & SEC_LOAD) != 0
- && (op->flags & SEC_LOAD) == 0))
- op = op1;
- }
- else if (((op1->flags ^ op->flags) & SEC_READONLY) != 0)
- {
- if (((op->flags ^ s->flags) & SEC_READONLY) != 0)
- op = op1;
- }
- else if (((op1->flags ^ op->flags) & SEC_CODE) != 0)
- {
- if (((op->flags ^ s->flags) & SEC_CODE) != 0)
- op = op1;
- }
- else
- {
- /* Flags we care about are the same. Prefer the following
- section if that will result in a positive valued sym. */
- if (h->u.def.value < op->vma)
- op = op1;
- }
-
- /* Refuse to choose a section for which we are out of bounds. */
- /* ??? This may make most of the above moot. */
- if (h->u.def.value < op->vma
- || h->u.def.value > op->vma + op->size)
- op = bfd_abs_section_ptr;
-
+ op = _bfd_nearby_section (obfd, s->output_section, h->u.def.value);
h->u.def.value -= op->vma;
h->u.def.section = op;
}