This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH 1/7] Fix --gc-sections for C++ MIPS ELF
- From: Richard Sandiford <rsandifo at nildram dot co dot uk>
- To: binutils at sourceware dot org
- Date: Sun, 02 Dec 2007 20:17:58 +0000
- Subject: [PATCH 1/7] Fix --gc-sections for C++ MIPS ELF
- References: <87r6i4svjg.fsf@firetop.home>
The discard routines store some relocation information in a structure
called elf_reloc_cookie, which the .eh_frame parser uses to check that
the relocs are as expected. If we're going to parse .eh_frame earlier,
it would be good to have cookies at that stage too.
_bfd_elf_gc_mark uses the same information that you get from an
elf_reloc_cookie, but stores it in separate variables. This first
patch therefore splits out the cookie initialisation and finalisation
routines from bfd_elf_discard_info and uses them in _bfd_elf_gc_mark as
well. The patch also splits out parts of _bfd_elf_gc_mark so that they
can be reused in later patches.
The patch fixes a potential memory leak, although it probably never
occured in practice. _bfd_elf_gc_mark behaves as though it could be
the first routine to set symtab_hdr->contents:
if (isym != NULL && symtab_hdr->contents != (unsigned char *) isym)
{
if (! info->keep_memory)
free (isym);
else
symtab_hdr->contents = (unsigned char *) isym;
}
but it does this after a loop that involves recursive calls to
_bfd_elf_gc_mark. If one of those recursive calls was for the
same input bfd, that call would still see a null contents field,
and would therefore create a second copy of the memory. I avoided
this by putting the symtab_hdr->contents assignment in the
initialisation code instead.
Richard
bfd/
* elf-bfd.h (_bfd_elf_gc_mark_rsec, _bfd_elf_gc_mark_reloc): Declare.
(_bfd_elf_gc_mark): Use elf_gc_mark_hook_fn.
* elflink.c (init_reloc_cookie, fini_reloc_cookie)
(init_reloc_cookie_rels, fini_reloc_cookie_rels): New functions,
split out from...
(bfd_elf_discard_info): ...here.
(init_reloc_cookie_for_section): New function.
(fini_reloc_cookie_for_section): Likewise.
(_bfd_elf_gc_mark_rsec, _bfd_elf_gc_mark_reloc): New functions,
split out from...
(_bfd_elf_gc_mark): ...here. Use init_reloc_cookie_for_section
and fini_reloc_cookie_for_section.
Index: bfd/elf-bfd.h
===================================================================
*** bfd/elf-bfd.h 2007-12-02 16:59:44.000000000 +0000
--- bfd/elf-bfd.h 2007-12-02 16:59:46.000000000 +0000
*************** extern void _bfd_elf_set_osabi (bfd * ,
*** 1971,1980 ****
(asection *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *);
extern bfd_boolean _bfd_elf_gc_mark
! (struct bfd_link_info *, asection *,
! asection * (*) (asection *, struct bfd_link_info *, Elf_Internal_Rela *,
! struct elf_link_hash_entry *, Elf_Internal_Sym *));
extern bfd_boolean bfd_elf_gc_common_finalize_got_offsets
(bfd *, struct bfd_link_info *);
--- 1971,1986 ----
(asection *, struct bfd_link_info *, Elf_Internal_Rela *,
struct elf_link_hash_entry *, Elf_Internal_Sym *);
+ extern asection *_bfd_elf_gc_mark_rsec
+ (struct bfd_link_info *, asection *, elf_gc_mark_hook_fn,
+ struct elf_reloc_cookie *);
+
+ extern bfd_boolean _bfd_elf_gc_mark_reloc
+ (struct bfd_link_info *, asection *, elf_gc_mark_hook_fn,
+ struct elf_reloc_cookie *, bfd_boolean);
+
extern bfd_boolean _bfd_elf_gc_mark
! (struct bfd_link_info *, asection *, elf_gc_mark_hook_fn);
extern bfd_boolean bfd_elf_gc_common_finalize_got_offsets
(bfd *, struct bfd_link_info *);
Index: bfd/elflink.c
===================================================================
*** bfd/elflink.c 2007-12-02 16:59:44.000000000 +0000
--- bfd/elflink.c 2007-12-02 17:01:03.000000000 +0000
*************** bfd_elf_final_link (bfd *abfd, struct bf
*** 10940,10945 ****
--- 10940,11078 ----
return FALSE;
}
+ /* Initialize COOKIE for input bfd ABFD. */
+
+ static bfd_boolean
+ init_reloc_cookie (struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info, bfd *abfd)
+ {
+ Elf_Internal_Shdr *symtab_hdr;
+ const struct elf_backend_data *bed;
+
+ bed = get_elf_backend_data (abfd);
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+
+ cookie->abfd = abfd;
+ cookie->sym_hashes = elf_sym_hashes (abfd);
+ cookie->bad_symtab = elf_bad_symtab (abfd);
+ if (cookie->bad_symtab)
+ {
+ cookie->locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
+ cookie->extsymoff = 0;
+ }
+ else
+ {
+ cookie->locsymcount = symtab_hdr->sh_info;
+ cookie->extsymoff = symtab_hdr->sh_info;
+ }
+
+ if (bed->s->arch_size == 32)
+ cookie->r_sym_shift = 8;
+ else
+ cookie->r_sym_shift = 32;
+
+ cookie->locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
+ if (cookie->locsyms == NULL && cookie->locsymcount != 0)
+ {
+ cookie->locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
+ cookie->locsymcount, 0,
+ NULL, NULL, NULL);
+ if (cookie->locsyms == NULL)
+ {
+ info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
+ return FALSE;
+ }
+ if (info->keep_memory)
+ symtab_hdr->contents = (bfd_byte *) cookie->locsyms;
+ }
+ return TRUE;
+ }
+
+ /* Free the memory allocated by init_reloc_cookie, if appropriate. */
+
+ static void
+ fini_reloc_cookie (struct elf_reloc_cookie *cookie, bfd *abfd)
+ {
+ Elf_Internal_Shdr *symtab_hdr;
+
+ symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
+ if (cookie->locsyms != NULL
+ && symtab_hdr->contents != (unsigned char *) cookie->locsyms)
+ free (cookie->locsyms);
+ }
+
+ /* Initialize the relocation information in COOKIE for input section SEC
+ of input bfd ABFD. */
+
+ static bfd_boolean
+ init_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info, bfd *abfd,
+ asection *sec)
+ {
+ const struct elf_backend_data *bed;
+
+ if (sec->reloc_count == 0)
+ {
+ cookie->rels = NULL;
+ cookie->relend = NULL;
+ }
+ else
+ {
+ bed = get_elf_backend_data (abfd);
+
+ cookie->rels = _bfd_elf_link_read_relocs (abfd, sec, NULL, NULL,
+ info->keep_memory);
+ if (cookie->rels == NULL)
+ return FALSE;
+ cookie->rel = cookie->rels;
+ cookie->relend = (cookie->rels
+ + sec->reloc_count * bed->s->int_rels_per_ext_rel);
+ }
+ cookie->rel = cookie->rels;
+ return TRUE;
+ }
+
+ /* Free the memory allocated by init_reloc_cookie_rels,
+ if appropriate. */
+
+ static void
+ fini_reloc_cookie_rels (struct elf_reloc_cookie *cookie,
+ asection *sec)
+ {
+ if (cookie->rels && elf_section_data (sec)->relocs != cookie->rels)
+ free (cookie->rels);
+ }
+
+ /* Initialize the whole of COOKIE for input section SEC. */
+
+ static bfd_boolean
+ init_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+ struct bfd_link_info *info,
+ asection *sec)
+ {
+ if (!init_reloc_cookie (cookie, info, sec->owner))
+ goto error1;
+ if (!init_reloc_cookie_rels (cookie, info, sec->owner, sec))
+ goto error2;
+ return TRUE;
+
+ error2:
+ fini_reloc_cookie (cookie, sec->owner);
+ error1:
+ return FALSE;
+ }
+
+ /* Free the memory allocated by init_reloc_cookie_for_section,
+ if appropriate. */
+
+ static void
+ fini_reloc_cookie_for_section (struct elf_reloc_cookie *cookie,
+ asection *sec)
+ {
+ fini_reloc_cookie_rels (cookie, sec);
+ fini_reloc_cookie (cookie, sec->owner);
+ }
+
/* Garbage collect unused sections. */
/* Default gc_mark_hook. */
*************** _bfd_elf_gc_mark_hook (asection *sec,
*** 10972,10977 ****
--- 11105,11167 ----
return NULL;
}
+ /* COOKIE->rel describes a relocation against section SEC, which is
+ a section we've decided to keep. Return the section that contains
+ the relocation symbol, or NULL if no section contains it. */
+
+ asection *
+ _bfd_elf_gc_mark_rsec (struct bfd_link_info *info, asection *sec,
+ elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie)
+ {
+ unsigned long r_symndx;
+ struct elf_link_hash_entry *h;
+
+ r_symndx = cookie->rel->r_info >> cookie->r_sym_shift;
+ if (r_symndx == 0)
+ return NULL;
+
+ if (r_symndx >= cookie->locsymcount
+ || ELF_ST_BIND (cookie->locsyms[r_symndx].st_info) != STB_LOCAL)
+ {
+ h = cookie->sym_hashes[r_symndx - cookie->extsymoff];
+ while (h->root.type == bfd_link_hash_indirect
+ || h->root.type == bfd_link_hash_warning)
+ h = (struct elf_link_hash_entry *) h->root.u.i.link;
+ return (*gc_mark_hook) (sec, info, cookie->rel, h, NULL);
+ }
+
+ return (*gc_mark_hook) (sec, info, cookie->rel, NULL,
+ &cookie->locsyms[r_symndx]);
+ }
+
+ /* COOKIE->rel describes a relocation against section SEC, which is
+ a section we've decided to keep. Mark the section that contains
+ the relocation symbol. IS_EH is true if the mark comes from
+ .eh_frame. */
+
+ bfd_boolean
+ _bfd_elf_gc_mark_reloc (struct bfd_link_info *info,
+ asection *sec,
+ elf_gc_mark_hook_fn gc_mark_hook,
+ struct elf_reloc_cookie *cookie,
+ bfd_boolean is_eh)
+ {
+ asection *rsec;
+
+ rsec = _bfd_elf_gc_mark_rsec (info, sec, gc_mark_hook, cookie);
+ if (rsec && !rsec->gc_mark)
+ {
+ if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
+ rsec->gc_mark = 1;
+ else if (is_eh)
+ rsec->gc_mark_from_eh = 1;
+ else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
+ return FALSE;
+ }
+ return TRUE;
+ }
+
/* The mark phase of garbage collection. For a given section, mark
it and any sections in this section's group, and all the sections
which define symbols to which it refers. */
*************** _bfd_elf_gc_mark (struct bfd_link_info *
*** 10998,11100 ****
is_eh = strcmp (sec->name, ".eh_frame") == 0;
if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
{
! Elf_Internal_Rela *relstart, *rel, *relend;
! Elf_Internal_Shdr *symtab_hdr;
! struct elf_link_hash_entry **sym_hashes;
! size_t nlocsyms;
! size_t extsymoff;
! bfd *input_bfd = sec->owner;
! const struct elf_backend_data *bed = get_elf_backend_data (input_bfd);
! Elf_Internal_Sym *isym = NULL;
! int r_sym_shift;
! symtab_hdr = &elf_tdata (input_bfd)->symtab_hdr;
! sym_hashes = elf_sym_hashes (input_bfd);
!
! /* Read the local symbols. */
! if (elf_bad_symtab (input_bfd))
! {
! nlocsyms = symtab_hdr->sh_size / bed->s->sizeof_sym;
! extsymoff = 0;
! }
else
- extsymoff = nlocsyms = symtab_hdr->sh_info;
-
- isym = (Elf_Internal_Sym *) symtab_hdr->contents;
- if (isym == NULL && nlocsyms != 0)
- {
- isym = bfd_elf_get_elf_syms (input_bfd, symtab_hdr, nlocsyms, 0,
- NULL, NULL, NULL);
- if (isym == NULL)
- return FALSE;
- }
-
- /* Read the relocations. */
- relstart = _bfd_elf_link_read_relocs (input_bfd, sec, NULL, NULL,
- info->keep_memory);
- if (relstart == NULL)
- {
- ret = FALSE;
- goto out1;
- }
- relend = relstart + sec->reloc_count * bed->s->int_rels_per_ext_rel;
-
- if (bed->s->arch_size == 32)
- r_sym_shift = 8;
- else
- r_sym_shift = 32;
-
- for (rel = relstart; rel < relend; rel++)
- {
- unsigned long r_symndx;
- asection *rsec;
- struct elf_link_hash_entry *h;
-
- r_symndx = rel->r_info >> r_sym_shift;
- if (r_symndx == 0)
- continue;
-
- if (r_symndx >= nlocsyms
- || ELF_ST_BIND (isym[r_symndx].st_info) != STB_LOCAL)
- {
- h = sym_hashes[r_symndx - extsymoff];
- while (h->root.type == bfd_link_hash_indirect
- || h->root.type == bfd_link_hash_warning)
- h = (struct elf_link_hash_entry *) h->root.u.i.link;
- rsec = (*gc_mark_hook) (sec, info, rel, h, NULL);
- }
- else
- {
- rsec = (*gc_mark_hook) (sec, info, rel, NULL, &isym[r_symndx]);
- }
-
- if (rsec && !rsec->gc_mark)
- {
- if (bfd_get_flavour (rsec->owner) != bfd_target_elf_flavour)
- rsec->gc_mark = 1;
- else if (is_eh)
- rsec->gc_mark_from_eh = 1;
- else if (!_bfd_elf_gc_mark (info, rsec, gc_mark_hook))
- {
- ret = FALSE;
- goto out2;
- }
- }
- }
-
- out2:
- if (elf_section_data (sec)->relocs != relstart)
- free (relstart);
- out1:
- if (isym != NULL && symtab_hdr->contents != (unsigned char *) isym)
{
! if (! info->keep_memory)
! free (isym);
! else
! symtab_hdr->contents = (unsigned char *) isym;
}
}
-
return ret;
}
--- 11188,11209 ----
is_eh = strcmp (sec->name, ".eh_frame") == 0;
if ((sec->flags & SEC_RELOC) != 0 && sec->reloc_count > 0)
{
! struct elf_reloc_cookie cookie;
! if (!init_reloc_cookie_for_section (&cookie, info, sec))
! ret = FALSE;
else
{
! for (; cookie.rel < cookie.relend; cookie.rel++)
! if (!_bfd_elf_gc_mark_reloc (info, sec, gc_mark_hook,
! &cookie, is_eh))
! {
! ret = FALSE;
! break;
! }
! fini_reloc_cookie_for_section (&cookie, sec);
}
}
return ret;
}
*************** bfd_elf_discard_info (bfd *output_bfd, s
*** 11777,11786 ****
{
struct elf_reloc_cookie cookie;
asection *stab, *eh;
- Elf_Internal_Shdr *symtab_hdr;
const struct elf_backend_data *bed;
bfd *abfd;
- unsigned int count;
bfd_boolean ret = FALSE;
if (info->traditional_format
--- 11886,11893 ----
*************** bfd_elf_discard_info (bfd *output_bfd, s
*** 11819,11913 ****
&& bed->elf_backend_discard_info == NULL)
continue;
! symtab_hdr = &elf_tdata (abfd)->symtab_hdr;
! cookie.abfd = abfd;
! cookie.sym_hashes = elf_sym_hashes (abfd);
! cookie.bad_symtab = elf_bad_symtab (abfd);
! if (cookie.bad_symtab)
! {
! cookie.locsymcount = symtab_hdr->sh_size / bed->s->sizeof_sym;
! cookie.extsymoff = 0;
! }
! else
! {
! cookie.locsymcount = symtab_hdr->sh_info;
! cookie.extsymoff = symtab_hdr->sh_info;
! }
!
! if (bed->s->arch_size == 32)
! cookie.r_sym_shift = 8;
! else
! cookie.r_sym_shift = 32;
!
! cookie.locsyms = (Elf_Internal_Sym *) symtab_hdr->contents;
! if (cookie.locsyms == NULL && cookie.locsymcount != 0)
! {
! cookie.locsyms = bfd_elf_get_elf_syms (abfd, symtab_hdr,
! cookie.locsymcount, 0,
! NULL, NULL, NULL);
! if (cookie.locsyms == NULL)
! {
! info->callbacks->einfo (_("%P%X: can not read symbols: %E\n"));
! return FALSE;
! }
! }
! if (stab != NULL)
{
! cookie.rels = NULL;
! count = stab->reloc_count;
! if (count != 0)
! cookie.rels = _bfd_elf_link_read_relocs (abfd, stab, NULL, NULL,
! info->keep_memory);
! if (cookie.rels != NULL)
! {
! cookie.rel = cookie.rels;
! cookie.relend = cookie.rels;
! cookie.relend += count * bed->s->int_rels_per_ext_rel;
! if (_bfd_discard_section_stabs (abfd, stab,
! elf_section_data (stab)->sec_info,
! bfd_elf_reloc_symbol_deleted_p,
! &cookie))
! ret = TRUE;
! if (elf_section_data (stab)->relocs != cookie.rels)
! free (cookie.rels);
! }
}
! if (eh != NULL)
{
- cookie.rels = NULL;
- count = eh->reloc_count;
- if (count != 0)
- cookie.rels = _bfd_elf_link_read_relocs (abfd, eh, NULL, NULL,
- info->keep_memory);
- cookie.rel = cookie.rels;
- cookie.relend = cookie.rels;
- if (cookie.rels != NULL)
- cookie.relend += count * bed->s->int_rels_per_ext_rel;
-
if (_bfd_elf_discard_section_eh_frame (abfd, info, eh,
bfd_elf_reloc_symbol_deleted_p,
&cookie))
ret = TRUE;
!
! if (cookie.rels != NULL
! && elf_section_data (eh)->relocs != cookie.rels)
! free (cookie.rels);
}
if (bed->elf_backend_discard_info != NULL
&& (*bed->elf_backend_discard_info) (abfd, &cookie, info))
ret = TRUE;
! if (cookie.locsyms != NULL
! && symtab_hdr->contents != (unsigned char *) cookie.locsyms)
! {
! if (! info->keep_memory)
! free (cookie.locsyms);
! else
! symtab_hdr->contents = (unsigned char *) cookie.locsyms;
! }
}
if (info->eh_frame_hdr
--- 11926,11961 ----
&& bed->elf_backend_discard_info == NULL)
continue;
! if (!init_reloc_cookie (&cookie, info, abfd))
! return FALSE;
! if (stab != NULL
! && stab->reloc_count > 0
! && init_reloc_cookie_rels (&cookie, info, abfd, stab))
{
! if (_bfd_discard_section_stabs (abfd, stab,
! elf_section_data (stab)->sec_info,
! bfd_elf_reloc_symbol_deleted_p,
! &cookie))
! ret = TRUE;
! fini_reloc_cookie_rels (&cookie, stab);
}
! if (eh != NULL
! && init_reloc_cookie_rels (&cookie, info, abfd, eh))
{
if (_bfd_elf_discard_section_eh_frame (abfd, info, eh,
bfd_elf_reloc_symbol_deleted_p,
&cookie))
ret = TRUE;
! fini_reloc_cookie_rels (&cookie, eh);
}
if (bed->elf_backend_discard_info != NULL
&& (*bed->elf_backend_discard_info) (abfd, &cookie, info))
ret = TRUE;
! fini_reloc_cookie (&cookie, abfd);
}
if (info->eh_frame_hdr