This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Re: combreloc
Hi Jakub,
This patch fixes the "pessimisation" you mentioned re. DT_TEXTREL for
x86. Setting DF_TEXTREL in check_relocs is pessimistic because
a) If dynamic relocs are garbage collected, or removed due to symbol
visibility or -Bsymbolic actions, we may not end up with any relocs
against read-only sections.
b) Testing whether the input section is read-only isn't reliable. (*)
A read-only input section can easily be mapped to a read-write output
section; When check_relocs is called, the linker has not yet
mapped input sections to output sections. Fortunately, I don't
think a read-write input section can be mapped to a read-only output
section. If this is possible, then trying to set DF_TEXTREL in
check relocs could result in omitting DT_TEXTREL when it's needed.
I've also implemented garbage collection of dyn_relocs, something I've
been meaning to do for quite some time.
(*) Yeah, I use this test for copy relocs, which is wrong...
bfd/ChangeLog
* elf32-i386.c (struct elf_i386_dyn_relocs): Add "sec", and
"pc_count" fields. Rename "section" field to "sreloc".
(check_relocs): Don't set DF_TEXTREL here. Instead, record all
dynamic relocations via dyn_relocs.
(elf_i386_gc_sweep_hook): Sweep dyn_relocs too.
(allocate_plt_and_got_and_discard_relocs): For the shared case we
now track all dynamic relocs, not just pc-relative ones, so use
pc_count when removing unneeded relocs. Discard eh->dyn_relocs
entries if the count drops to zero.
(readonly_dynrelocs): New function.
(elf_i386_size_dynamic_sections): Call it. Rename "i" to "ibfd".
--
Alan Modra
Index: bfd/elf32-i386.c
===================================================================
RCS file: /cvs/src/src/bfd/elf32-i386.c,v
retrieving revision 1.46
diff -u -p -r1.46 elf32-i386.c
--- elf32-i386.c 2001/09/24 01:38:31 1.46
+++ elf32-i386.c 2001/09/24 13:42:53
@@ -51,6 +51,8 @@ static boolean elf_i386_adjust_dynamic_s
PARAMS ((struct bfd_link_info *, struct elf_link_hash_entry *));
static boolean allocate_plt_and_got_and_discard_relocs
PARAMS ((struct elf_link_hash_entry *, PTR));
+static boolean readonly_dynrelocs
+ PARAMS ((struct elf_link_hash_entry *, PTR));
static boolean elf_i386_size_dynamic_sections
PARAMS ((bfd *, struct bfd_link_info *));
static boolean elf_i386_relocate_section
@@ -378,12 +380,19 @@ static const bfd_byte elf_i386_pic_plt_e
struct elf_i386_dyn_relocs
{
- /* Next section. */
struct elf_i386_dyn_relocs *next;
- /* A section in dynobj. */
- asection *section;
- /* Number of relocs copied in this section. */
+
+ /* The input section of the reloc. */
+ asection *sec;
+
+ /* A section in dynobj for the reloc. */
+ asection *sreloc;
+
+ /* Total number of relocs copied in the input section. */
bfd_size_type count;
+
+ /* Number of pc-relative relocs copied in the input section. */
+ bfd_size_type pc_count;
};
/* i386 ELF linker hash entry. */
@@ -392,7 +401,7 @@ struct elf_i386_link_hash_entry
{
struct elf_link_hash_entry root;
- /* Number of PC relative relocs copied for this symbol. */
+ /* Track dynamic relocs copied for this symbol. */
struct elf_i386_dyn_relocs *dyn_relocs;
};
@@ -743,9 +752,9 @@ elf_i386_check_relocs (abfd, info, sec,
|| strcmp (bfd_get_section_name (abfd, sec),
name + 4) != 0)
{
- (*_bfd_error_handler) (_("%s: bad relocation section name `%s\'"),
- bfd_archive_filename (abfd),
- name);
+ (*_bfd_error_handler)
+ (_("%s: bad relocation section name `%s\'"),
+ bfd_archive_filename (abfd), name);
}
sreloc = bfd_get_section_by_name (dynobj, name);
@@ -763,32 +772,22 @@ elf_i386_check_relocs (abfd, info, sec,
|| ! bfd_set_section_alignment (dynobj, sreloc, 2))
return false;
}
- if (sec->flags & SEC_READONLY)
- info->flags |= DF_TEXTREL;
}
sreloc->_raw_size += sizeof (Elf32_External_Rel);
- /* If this is a global symbol, we count the number of PC
- relative relocations we have entered for this symbol,
- so that we can discard them later as necessary. Note
- that this function is only called if we are using an
- elf_i386 linker hash table, which means that h is
- really a pointer to an elf_i386_link_hash_entry. */
- if (!info->shared
- || (h != NULL
- && ELF32_R_TYPE (rel->r_info) == R_386_PC32))
+ /* If this is a global symbol, we count the number of
+ relocations we have entered for this symbol, so that
+ we can discard them later as necessary. */
+ if (h != NULL)
{
struct elf_i386_link_hash_entry *eh;
struct elf_i386_dyn_relocs *p;
eh = (struct elf_i386_link_hash_entry *) h;
-
- for (p = eh->dyn_relocs; p != NULL; p = p->next)
- if (p->section == sreloc)
- break;
+ p = eh->dyn_relocs;
- if (p == NULL)
+ if (p == NULL || p->sec != sec)
{
p = ((struct elf_i386_dyn_relocs *)
bfd_alloc (dynobj, (bfd_size_type) sizeof *p));
@@ -796,11 +795,15 @@ elf_i386_check_relocs (abfd, info, sec,
return false;
p->next = eh->dyn_relocs;
eh->dyn_relocs = p;
- p->section = sreloc;
+ p->sec = sec;
+ p->sreloc = sreloc;
p->count = 0;
+ p->pc_count = 0;
}
- ++p->count;
+ p->count += 1;
+ if (ELF32_R_TYPE (rel->r_info) == R_386_PC32)
+ p->pc_count += 1;
}
}
@@ -924,9 +927,33 @@ elf_i386_gc_sweep_hook (abfd, info, sec,
case R_386_32:
case R_386_PC32:
- if (info->shared)
- break;
- /* Fall through. */
+ r_symndx = ELF32_R_SYM (rel->r_info);
+ if (r_symndx >= symtab_hdr->sh_info)
+ {
+ struct elf_i386_link_hash_entry *eh;
+ struct elf_i386_dyn_relocs **pp;
+ struct elf_i386_dyn_relocs *p;
+
+ h = sym_hashes[r_symndx - symtab_hdr->sh_info];
+
+ if (!info->shared && h->plt.refcount > 0)
+ h->plt.refcount -= 1;
+
+ eh = (struct elf_i386_link_hash_entry *) h;
+
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; pp = &p->next)
+ if (p->sec == sec)
+ {
+ p->sreloc->_raw_size -= sizeof (Elf32_External_Rel);
+ if (ELF32_R_TYPE (rel->r_info) == R_386_PC32)
+ p->pc_count -= 1;
+ p->count -= 1;
+ if (p->count == 0)
+ *pp = p->next;
+ break;
+ }
+ }
+ break;
case R_386_PLT32:
r_symndx = ELF32_R_SYM (rel->r_info);
@@ -1100,6 +1127,7 @@ allocate_plt_and_got_and_discard_relocs
struct elf_i386_link_hash_table *htab;
asection *s;
struct elf_i386_link_hash_entry *eh;
+ struct elf_i386_dyn_relocs *p;
if (h->root.type == bfd_link_hash_indirect
|| h->root.type == bfd_link_hash_warning)
@@ -1191,19 +1219,40 @@ allocate_plt_and_got_and_discard_relocs
else
h->got.offset = (bfd_vma) -1;
- /* In the shared -Bsymbolic case, discard space allocated for
- dynamic relocs against symbols which turn out to be defined
- in regular objects. For the normal shared case, discard space
- for relocs that have become local due to symbol visibility
- changes. For the non-shared case, discard space for symbols
- which turn out to need copy relocs or are not dynamic. */
-
eh = (struct elf_i386_link_hash_entry *) h;
if (eh->dyn_relocs == NULL)
return true;
+
+ /* In the shared -Bsymbolic case, discard space allocated for
+ dynamic pc-relative relocs against symbols which turn out to be
+ defined in regular objects. For the normal shared case, discard
+ space for relocs that have become local due to symbol visibility
+ changes. */
+
+ if (info->shared)
+ {
+ struct elf_i386_dyn_relocs **pp;
- if (!info->shared
- && (h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
+ if ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0
+ && ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0
+ || info->symbolic))
+ for (pp = &eh->dyn_relocs; (p = *pp) != NULL; )
+ {
+ p->sreloc->_raw_size -= p->pc_count * sizeof (Elf32_External_Rel);
+ p->count -= p->pc_count;
+ p->pc_count = 0;
+ if (p->count == 0)
+ *pp = p->next;
+ else
+ pp = &p->next;
+ }
+ return true;
+ }
+
+ /* For the non-shared case, discard space for relocs against symbols
+ which turn out to need copy relocs or are not dynamic. */
+
+ if ((h->elf_link_hash_flags & ELF_LINK_NON_GOT_REF) == 0
&& (((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_DYNAMIC) != 0
&& (h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) == 0)
|| (htab->root.dynamic_sections_created
@@ -1224,17 +1273,39 @@ allocate_plt_and_got_and_discard_relocs
return true;
}
- if (!info->shared
- || ((h->elf_link_hash_flags & ELF_LINK_HASH_DEF_REGULAR) != 0
- && ((h->elf_link_hash_flags & ELF_LINK_FORCED_LOCAL) != 0
- || info->symbolic)))
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
+ p->sreloc->_raw_size -= p->count * sizeof (Elf32_External_Rel);
+
+ eh->dyn_relocs = NULL;
+
+ return true;
+}
+
+/* Find any dynamic relocs that apply to read-only sections. */
+
+static boolean
+readonly_dynrelocs (h, inf)
+ struct elf_link_hash_entry *h;
+ PTR inf;
+{
+ struct elf_i386_link_hash_entry *eh;
+ struct elf_i386_dyn_relocs *p;
+
+ eh = (struct elf_i386_link_hash_entry *) h;
+ for (p = eh->dyn_relocs; p != NULL; p = p->next)
{
- struct elf_i386_dyn_relocs *c;
+ asection *s = p->sec->output_section;
- for (c = eh->dyn_relocs; c != NULL; c = c->next)
- c->section->_raw_size -= c->count * sizeof (Elf32_External_Rel);
- }
+ if (s != NULL && (s->flags & SEC_READONLY) != 0)
+ {
+ struct bfd_link_info *info = (struct bfd_link_info *) inf;
+ info->flags |= DF_TEXTREL;
+
+ /* Not an error, just cut short the traversal. */
+ return false;
+ }
+ }
return true;
}
@@ -1249,7 +1320,7 @@ elf_i386_size_dynamic_sections (output_b
bfd *dynobj;
asection *s;
boolean relocs;
- bfd *i;
+ bfd *ibfd;
htab = elf_i386_hash_table (info);
dynobj = htab->root.dynobj;
@@ -1270,7 +1341,7 @@ elf_i386_size_dynamic_sections (output_b
}
/* Set up .got offsets for local syms. */
- for (i = info->input_bfds; i; i = i->link_next)
+ for (ibfd = info->input_bfds; ibfd != NULL; ibfd = ibfd->link_next)
{
bfd_signed_vma *local_got;
bfd_signed_vma *end_local_got;
@@ -1278,14 +1349,14 @@ elf_i386_size_dynamic_sections (output_b
Elf_Internal_Shdr *symtab_hdr;
asection *srel;
- if (bfd_get_flavour (i) != bfd_target_elf_flavour)
+ if (bfd_get_flavour (ibfd) != bfd_target_elf_flavour)
continue;
- local_got = elf_local_got_refcounts (i);
+ local_got = elf_local_got_refcounts (ibfd);
if (!local_got)
continue;
- symtab_hdr = &elf_tdata (i)->symtab_hdr;
+ symtab_hdr = &elf_tdata (ibfd)->symtab_hdr;
locsymcount = symtab_hdr->sh_info;
end_local_got = local_got + locsymcount;
s = htab->sgot;
@@ -1402,12 +1473,16 @@ elf_i386_size_dynamic_sections (output_b
|| !add_dynamic_entry (DT_RELSZ, 0)
|| !add_dynamic_entry (DT_RELENT, sizeof (Elf32_External_Rel)))
return false;
- }
- if ((info->flags & DF_TEXTREL) != 0)
- {
- if (!add_dynamic_entry (DT_TEXTREL, 0))
- return false;
+ /* If any dynamic relocs apply to a read-only section,
+ then we need a DT_TEXTREL entry. */
+ elf_link_hash_traverse (&htab->root, readonly_dynrelocs, (PTR) info);
+
+ if ((info->flags & DF_TEXTREL) != 0)
+ {
+ if (!add_dynamic_entry (DT_TEXTREL, 0))
+ return false;
+ }
}
}
#undef add_dynamic_entry