This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[PATCH]: Fix reloc size assumption in elf_link_sort_relocs()
- From: Nick Clifton <nickc at redhat dot com>
- To: binutils at sourceware dot org
- Date: Fri, 04 May 2007 15:16:13 +0100
- Subject: [PATCH]: Fix reloc size assumption in elf_link_sort_relocs()
Hi Guys,
I am applying the attached patch which fixes a problem in
elf_link_sort_relocs(). It was assuming that if a .rela.dyn section
is present that only rela relocs were present in the indirect
sections. This might not be true however, if there is also a
.rel.dyn section, and the assumption could lead to a segmentation
fault walking over the end of the sort array. (This was the cause
of the seg-fault reported when linking the zdoom executables).
Cheers
Nick
bfd/ChangeLog
2007-05-04 Nick Clifton <nickc@redhat.com>
* elflink.c (elf_link_sort_relocs): If both .rela.dyn and .rel.dyn
sections are present examine the indirect sections in an attempt
to compute the correct relocation size. If there is any
ambiguity, produce an error and refuse to sort.
Index: bfd/elflink.c
===================================================================
RCS file: /cvs/src/src/bfd/elflink.c,v
retrieving revision 1.258
diff -c -3 -p -r1.258 elflink.c
*** bfd/elflink.c 26 Apr 2007 14:46:57 -0000 1.258
--- bfd/elflink.c 4 May 2007 10:22:38 -0000
*************** elf_link_sort_cmp2 (const void *A, const
*** 7220,7226 ****
static size_t
elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
{
! asection *reldyn;
bfd_size_type count, size;
size_t i, ret, sort_elt, ext_size;
bfd_byte *sort, *s_non_relative, *p;
--- 7220,7228 ----
static size_t
elf_link_sort_relocs (bfd *abfd, struct bfd_link_info *info, asection **psec)
{
! asection *dynamic_relocs;
! asection * rela_dyn;
! asection * rel_dyn;
bfd_size_type count, size;
size_t i, ret, sort_elt, ext_size;
bfd_byte *sort, *s_non_relative, *p;
*************** elf_link_sort_relocs (bfd *abfd, struct
*** 7231,7269 ****
void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *);
struct bfd_link_order *lo;
bfd_vma r_sym_mask;
! reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
! if (reldyn == NULL || reldyn->size == 0)
! {
! reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
! if (reldyn == NULL || reldyn->size == 0)
! return 0;
! ext_size = bed->s->sizeof_rel;
! swap_in = bed->s->swap_reloc_in;
! swap_out = bed->s->swap_reloc_out;
}
else
{
ext_size = bed->s->sizeof_rela;
swap_in = bed->s->swap_reloca_in;
swap_out = bed->s->swap_reloca_out;
}
! count = reldyn->size / ext_size;
size = 0;
! for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
! {
! asection *o = lo->u.indirect.section;
! size += o->size;
! }
! if (size != reldyn->size)
return 0;
sort_elt = (sizeof (struct elf_link_sort_rela)
+ (i2e - 1) * sizeof (Elf_Internal_Rela));
sort = bfd_zmalloc (sort_elt * count);
if (sort == NULL)
{
(*info->callbacks->warning)
--- 7233,7400 ----
void (*swap_out) (bfd *, const Elf_Internal_Rela *, bfd_byte *);
struct bfd_link_order *lo;
bfd_vma r_sym_mask;
+ bfd_boolean use_rela;
! /* Find a dynamic reloc section. */
! rela_dyn = bfd_get_section_by_name (abfd, ".rela.dyn");
! rel_dyn = bfd_get_section_by_name (abfd, ".rel.dyn");
! if (rela_dyn != NULL && rela_dyn->size > 0
! && rel_dyn != NULL && rel_dyn->size > 0)
! {
! bfd_boolean use_rela_initialised = FALSE;
!
! /* This is just here to stop gcc from complaining.
! It's initialization checking code is not perfect. */
! use_rela = TRUE;
!
! /* Both sections are present. Examine the sizes
! of the indirect sections to help us choose. */
! for (lo = rela_dyn->map_head.link_order; lo != NULL; lo = lo->next)
! if (lo->type == bfd_indirect_link_order)
! {
! asection *o = lo->u.indirect.section;
!
! if ((o->size % bed->s->sizeof_rela) == 0)
! {
! if ((o->size % bed->s->sizeof_rel) == 0)
! /* Section size is divisible by both rel and rela sizes.
! It is of no help to us. */
! ;
! else
! {
! /* Section size is only divisible by rela. */
! if (use_rela_initialised && (use_rela == FALSE))
! {
! _bfd_error_handler
! (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
! bfd_set_error (bfd_error_invalid_operation);
! return 0;
! }
! else
! {
! use_rela = TRUE;
! use_rela_initialised = TRUE;
! }
! }
! }
! else if ((o->size % bed->s->sizeof_rel) == 0)
! {
! /* Section size is only divisible by rel. */
! if (use_rela_initialised && (use_rela == TRUE))
! {
! _bfd_error_handler
! (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
! bfd_set_error (bfd_error_invalid_operation);
! return 0;
! }
! else
! {
! use_rela = FALSE;
! use_rela_initialised = TRUE;
! }
! }
! else
! {
! /* The section size is not divisible by either - something is wrong. */
! _bfd_error_handler
! (_("%B: Unable to sort relocs - they are of an unknown size"), abfd);
! bfd_set_error (bfd_error_invalid_operation);
! return 0;
! }
! }
!
! for (lo = rel_dyn->map_head.link_order; lo != NULL; lo = lo->next)
! if (lo->type == bfd_indirect_link_order)
! {
! asection *o = lo->u.indirect.section;
!
! if ((o->size % bed->s->sizeof_rela) == 0)
! {
! if ((o->size % bed->s->sizeof_rel) == 0)
! /* Section size is divisible by both rel and rela sizes.
! It is of no help to us. */
! ;
! else
! {
! /* Section size is only divisible by rela. */
! if (use_rela_initialised && (use_rela == FALSE))
! {
! _bfd_error_handler
! (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
! bfd_set_error (bfd_error_invalid_operation);
! return 0;
! }
! else
! {
! use_rela = TRUE;
! use_rela_initialised = TRUE;
! }
! }
! }
! else if ((o->size % bed->s->sizeof_rel) == 0)
! {
! /* Section size is only divisible by rel. */
! if (use_rela_initialised && (use_rela == TRUE))
! {
! _bfd_error_handler
! (_("%B: Unable to sort relocs - they are in more than one size"), abfd);
! bfd_set_error (bfd_error_invalid_operation);
! return 0;
! }
! else
! {
! use_rela = FALSE;
! use_rela_initialised = TRUE;
! }
! }
! else
! {
! /* The section size is not divisible by either - something is wrong. */
! _bfd_error_handler
! (_("%B: Unable to sort relocs - they are of an unknown size"), abfd);
! bfd_set_error (bfd_error_invalid_operation);
! return 0;
! }
! }
!
! if (! use_rela_initialised)
! /* Make a guess. */
! use_rela = TRUE;
}
+ else if (rela_dyn == NULL || rela_dyn->size == 0)
+ use_rela = FALSE;
else
+ use_rela = TRUE;
+
+ if (use_rela)
{
+ dynamic_relocs = rela_dyn;
ext_size = bed->s->sizeof_rela;
swap_in = bed->s->swap_reloca_in;
swap_out = bed->s->swap_reloca_out;
}
! else
! {
! dynamic_relocs = rel_dyn;
! ext_size = bed->s->sizeof_rel;
! swap_in = bed->s->swap_reloc_in;
! swap_out = bed->s->swap_reloc_out;
! }
size = 0;
! for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
! size += lo->u.indirect.section->size;
! if (size != dynamic_relocs->size)
return 0;
sort_elt = (sizeof (struct elf_link_sort_rela)
+ (i2e - 1) * sizeof (Elf_Internal_Rela));
+
+ count = dynamic_relocs->size / ext_size;
sort = bfd_zmalloc (sort_elt * count);
+
if (sort == NULL)
{
(*info->callbacks->warning)
*************** elf_link_sort_relocs (bfd *abfd, struct
*** 7276,7282 ****
else
r_sym_mask = ~(bfd_vma) 0xffffffff;
! for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
{
bfd_byte *erel, *erelend;
--- 7407,7413 ----
else
r_sym_mask = ~(bfd_vma) 0xffffffff;
! for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
{
bfd_byte *erel, *erelend;
*************** elf_link_sort_relocs (bfd *abfd, struct
*** 7293,7301 ****
--- 7424,7434 ----
erel = o->contents;
erelend = o->contents + o->size;
p = sort + o->output_offset / ext_size * sort_elt;
+
while (erel < erelend)
{
struct elf_link_sort_rela *s = (struct elf_link_sort_rela *) p;
+
(*swap_in) (abfd, erel, s->rela);
s->type = (*bed->elf_backend_reloc_type_class) (s->rela);
s->u.sym_mask = r_sym_mask;
*************** elf_link_sort_relocs (bfd *abfd, struct
*** 7326,7332 ****
qsort (s_non_relative, count - ret, sort_elt, elf_link_sort_cmp2);
! for (lo = reldyn->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
{
bfd_byte *erel, *erelend;
--- 7459,7465 ----
qsort (s_non_relative, count - ret, sort_elt, elf_link_sort_cmp2);
! for (lo = dynamic_relocs->map_head.link_order; lo != NULL; lo = lo->next)
if (lo->type == bfd_indirect_link_order)
{
bfd_byte *erel, *erelend;
*************** elf_link_sort_relocs (bfd *abfd, struct
*** 7345,7351 ****
}
free (sort);
! *psec = reldyn;
return ret;
}
--- 7478,7484 ----
}
free (sort);
! *psec = dynamic_relocs;
return ret;
}