+
+ free (irel);
+ free (irela);
+}
+
+struct elf_link_sort_rela {
+ bfd_vma offset;
+ enum elf_reloc_type_class type;
+ union {
+ Elf_Internal_Rel rel;
+ Elf_Internal_Rela rela;
+ } u;
+};
+
+static int
+elf_link_sort_cmp1 (A, B)
+ const PTR A;
+ const PTR B;
+{
+ struct elf_link_sort_rela *a = (struct elf_link_sort_rela *) A;
+ struct elf_link_sort_rela *b = (struct elf_link_sort_rela *) B;
+ int relativea, relativeb;
+
+ relativea = a->type == reloc_class_relative;
+ relativeb = b->type == reloc_class_relative;
+
+ if (relativea < relativeb)
+ return 1;
+ if (relativea > relativeb)
+ return -1;
+ if (ELF_R_SYM (a->u.rel.r_info) < ELF_R_SYM (b->u.rel.r_info))
+ return -1;
+ if (ELF_R_SYM (a->u.rel.r_info) > ELF_R_SYM (b->u.rel.r_info))
+ return 1;
+ if (a->u.rel.r_offset < b->u.rel.r_offset)
+ return -1;
+ if (a->u.rel.r_offset > b->u.rel.r_offset)
+ return 1;
+ return 0;
+}
+
+static int
+elf_link_sort_cmp2 (A, B)
+ const PTR A;
+ const PTR B;
+{
+ struct elf_link_sort_rela *a = (struct elf_link_sort_rela *) A;
+ struct elf_link_sort_rela *b = (struct elf_link_sort_rela *) B;
+ int copya, copyb;
+
+ if (a->offset < b->offset)
+ return -1;
+ if (a->offset > b->offset)
+ return 1;
+ copya = (a->type == reloc_class_copy) * 2 + (a->type == reloc_class_plt);
+ copyb = (b->type == reloc_class_copy) * 2 + (b->type == reloc_class_plt);
+ if (copya < copyb)
+ return -1;
+ if (copya > copyb)
+ return 1;
+ if (a->u.rel.r_offset < b->u.rel.r_offset)
+ return -1;
+ if (a->u.rel.r_offset > b->u.rel.r_offset)
+ return 1;
+ return 0;
+}
+
+static size_t
+elf_link_sort_relocs (abfd, info, psec)
+ bfd *abfd;
+ struct bfd_link_info *info;
+ asection **psec;
+{
+ bfd *dynobj = elf_hash_table (info)->dynobj;
+ asection *reldyn, *o;
+ boolean rel = false;
+ bfd_size_type count, size;
+ size_t i, j, ret;
+ struct elf_link_sort_rela *rela;
+ struct elf_backend_data *bed = get_elf_backend_data (abfd);
+
+ reldyn = bfd_get_section_by_name (abfd, ".rela.dyn");
+ if (reldyn == NULL || reldyn->_raw_size == 0)
+ {
+ reldyn = bfd_get_section_by_name (abfd, ".rel.dyn");
+ if (reldyn == NULL || reldyn->_raw_size == 0)
+ return 0;
+ rel = true;
+ count = reldyn->_raw_size / sizeof (Elf_External_Rel);
+ }
+ else
+ count = reldyn->_raw_size / sizeof (Elf_External_Rela);
+
+ size = 0;
+ for (o = dynobj->sections; o != NULL; o = o->next)
+ if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
+ == (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
+ && o->output_section == reldyn)
+ size += o->_raw_size;
+
+ if (size != reldyn->_raw_size)
+ return 0;
+
+ rela = (struct elf_link_sort_rela *) bfd_zmalloc (sizeof (*rela) * count);
+ if (rela == NULL)
+ {
+ (*info->callbacks->warning)
+ (info, _("Not enough memory to sort relocations"), 0, abfd, 0,
+ (bfd_vma) 0);
+ return 0;
+ }
+
+ for (o = dynobj->sections; o != NULL; o = o->next)
+ if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
+ == (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
+ && o->output_section == reldyn)
+ {
+ if (rel)
+ {
+ Elf_External_Rel *erel, *erelend;
+ struct elf_link_sort_rela *s;
+
+ erel = (Elf_External_Rel *) o->contents;
+ erelend = (Elf_External_Rel *) (o->contents + o->_raw_size);
+ s = rela + o->output_offset / sizeof (Elf_External_Rel);
+ for (; erel < erelend; erel++, s++)
+ {
+ if (bed->s->swap_reloc_in)
+ (*bed->s->swap_reloc_in) (abfd, (bfd_byte *) erel, &s->u.rel);
+ else
+ elf_swap_reloc_in (abfd, erel, &s->u.rel);
+
+ s->type = (*bed->elf_backend_reloc_type_class) (&s->u.rela);
+ }
+ }
+ else
+ {
+ Elf_External_Rela *erela, *erelaend;
+ struct elf_link_sort_rela *s;
+
+ erela = (Elf_External_Rela *) o->contents;
+ erelaend = (Elf_External_Rela *) (o->contents + o->_raw_size);
+ s = rela + o->output_offset / sizeof (Elf_External_Rela);
+ for (; erela < erelaend; erela++, s++)
+ {
+ if (bed->s->swap_reloca_in)
+ (*bed->s->swap_reloca_in) (dynobj, (bfd_byte *) erela,
+ &s->u.rela);
+ else
+ elf_swap_reloca_in (dynobj, erela, &s->u.rela);
+
+ s->type = (*bed->elf_backend_reloc_type_class) (&s->u.rela);
+ }
+ }
+ }
+
+ qsort (rela, (size_t) count, sizeof (*rela), elf_link_sort_cmp1);
+ for (ret = 0; ret < count && rela[ret].type == reloc_class_relative; ret++)
+ ;
+ for (i = ret, j = ret; i < count; i++)
+ {
+ if (ELF_R_SYM (rela[i].u.rel.r_info) != ELF_R_SYM (rela[j].u.rel.r_info))
+ j = i;
+ rela[i].offset = rela[j].u.rel.r_offset;
+ }
+ qsort (rela + ret, (size_t) count - ret, sizeof (*rela), elf_link_sort_cmp2);
+
+ for (o = dynobj->sections; o != NULL; o = o->next)
+ if ((o->flags & (SEC_HAS_CONTENTS|SEC_LINKER_CREATED))
+ == (SEC_HAS_CONTENTS|SEC_LINKER_CREATED)
+ && o->output_section == reldyn)
+ {
+ if (rel)
+ {
+ Elf_External_Rel *erel, *erelend;
+ struct elf_link_sort_rela *s;
+
+ erel = (Elf_External_Rel *) o->contents;
+ erelend = (Elf_External_Rel *) (o->contents + o->_raw_size);
+ s = rela + o->output_offset / sizeof (Elf_External_Rel);
+ for (; erel < erelend; erel++, s++)
+ {
+ if (bed->s->swap_reloc_out)
+ (*bed->s->swap_reloc_out) (abfd, &s->u.rel,
+ (bfd_byte *) erel);
+ else
+ elf_swap_reloc_out (abfd, &s->u.rel, erel);
+ }
+ }
+ else
+ {
+ Elf_External_Rela *erela, *erelaend;
+ struct elf_link_sort_rela *s;
+
+ erela = (Elf_External_Rela *) o->contents;
+ erelaend = (Elf_External_Rela *) (o->contents + o->_raw_size);
+ s = rela + o->output_offset / sizeof (Elf_External_Rela);
+ for (; erela < erelaend; erela++, s++)
+ {
+ if (bed->s->swap_reloca_out)
+ (*bed->s->swap_reloca_out) (dynobj, &s->u.rela,
+ (bfd_byte *) erela);
+ else
+ elf_swap_reloca_out (dynobj, &s->u.rela, erela);
+ }
+ }
+ }
+
+ free (rela);
+ *psec = reldyn;
+ return ret;