aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ld/ChangeLog8
-rw-r--r--ld/emultempl/ppc64elf.em149
2 files changed, 156 insertions, 1 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog
index 61f5081..db1a305 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,11 @@
+2010-03-15 Alan Modra <amodra@gmail.com>
+
+ * emultempl/ppc64elf.em (move_input_section, sort_toc_sections): New.
+ (ppc_before_allocation): Call sort_toc_sections.
+ (no_toc_sort, OPTION_NO_TOC_SORT): New.
+ (PARSE_AND_LIST_PROLOGUE, PARSE_AND_LIST_LONGOPTS,
+ PARSE_AND_LIST_OPTIONS): Handle --no-toc-sort.
+
2010-03-14 Alan Modra <amodra@gmail.com>
PR ld/11378
diff --git a/ld/emultempl/ppc64elf.em b/ld/emultempl/ppc64elf.em
index 0d9dba9..f8604a6 100644
--- a/ld/emultempl/ppc64elf.em
+++ b/ld/emultempl/ppc64elf.em
@@ -58,6 +58,9 @@ static int no_toc_opt = 0;
/* Whether to allow multiple toc sections. */
static int no_multi_toc = 0;
+/* Whether to sort input toc and got sections. */
+static int no_toc_sort = 0;
+
/* Whether to emit symbols for stubs. */
static int emit_stub_syms = -1;
@@ -97,6 +100,132 @@ ppc_create_output_section_statements (void)
ppc64_elf_init_stub_bfd (stub_file->the_bfd, &link_info);
}
+/* Move the input section statement at *U which happens to be on LIST
+ to be just before *TO. */
+
+static void
+move_input_section (lang_statement_list_type *list,
+ lang_statement_union_type **u,
+ lang_statement_union_type **to)
+{
+ lang_statement_union_type *s = *u;
+ asection *i = s->input_section.section;
+ asection *p, *n;
+
+ /* Snip the input section from the statement list. If it was the
+ last statement, fix the list tail pointer. */
+ *u = s->header.next;
+ if (*u == NULL)
+ list->tail = u;
+ /* Add it back in the new position. */
+ s->header.next = *to;
+ *to = s;
+ if (list->tail == to)
+ list->tail = &s->header.next;
+
+ /* Trim I off the bfd map_head/map_tail doubly linked lists. */
+ n = i->map_head.s;
+ p = i->map_tail.s;
+ (p != NULL ? p : i->output_section)->map_head.s = n;
+ (n != NULL ? n : i->output_section)->map_tail.s = p;
+
+ /* Add I back on in its new position. */
+ if (s->header.next->header.type == lang_input_section_enum)
+ {
+ n = s->header.next->input_section.section;
+ p = n->map_tail.s;
+ }
+ else
+ {
+ /* If the next statement is not an input section statement then
+ TO must point at the previous input section statement
+ header.next field. */
+ lang_input_section_type *prev = (lang_input_section_type *)
+ ((char *) to - offsetof (lang_statement_union_type, header.next));
+
+ ASSERT (prev->header.type == lang_input_section_enum);
+ p = prev->section;
+ n = p->map_head.s;
+ }
+ i->map_head.s = n;
+ i->map_tail.s = p;
+ (p != NULL ? p : i->output_section)->map_head.s = i;
+ (n != NULL ? n : i->output_section)->map_tail.s = i;
+}
+
+/* Sort input section statements in the linker script tree rooted at
+ LIST so that those whose owning bfd happens to have a section
+ called .init or .fini are placed first. Place any TOC sections
+ referenced by small TOC relocs next, with TOC sections referenced
+ only by bigtoc relocs last. */
+
+static void
+sort_toc_sections (lang_statement_list_type *list,
+ lang_statement_union_type **ini,
+ lang_statement_union_type **small)
+{
+ lang_statement_union_type *s, **u;
+ asection *i;
+
+ u = &list->head;
+ while ((s = *u) != NULL)
+ {
+ switch (s->header.type)
+ {
+ case lang_wild_statement_enum:
+ sort_toc_sections (&s->wild_statement.children, ini, small);
+ break;
+
+ case lang_group_statement_enum:
+ sort_toc_sections (&s->group_statement.children, ini, small);
+ break;
+
+ case lang_input_section_enum:
+ i = s->input_section.section;
+ /* Leave the stub_file .got where it is. We put the .got
+ header there. */
+ if (i->owner == stub_file->the_bfd)
+ break;
+ if (bfd_get_section_by_name (i->owner, ".init") != NULL
+ || bfd_get_section_by_name (i->owner, ".fini") != NULL)
+ {
+ if (ini != NULL && *ini != s)
+ {
+ move_input_section (list, u, ini);
+ if (small == ini)
+ small = &s->header.next;
+ ini = &s->header.next;
+ continue;
+ }
+ if (small == ini)
+ small = &s->header.next;
+ ini = &s->header.next;
+ break;
+ }
+ else if (ini == NULL)
+ ini = u;
+
+ if (ppc64_elf_has_small_toc_reloc (i))
+ {
+ if (small != NULL && *small != s)
+ {
+ move_input_section (list, u, small);
+ small = &s->header.next;
+ continue;
+ }
+ small = &s->header.next;
+ }
+ else if (small == NULL)
+ small = u;
+ break;
+
+ default:
+ break;
+ }
+ u = &s->header.next;
+ }
+}
+
static void
ppc_before_allocation (void)
{
@@ -126,6 +255,15 @@ ppc_before_allocation (void)
&& !link_info.relocatable
&& !ppc64_elf_edit_toc (&link_info))
einfo ("%X%P: can not edit %s %E\n", "toc");
+
+ if (!no_toc_sort)
+ {
+ lang_output_section_statement_type *toc_os;
+
+ toc_os = lang_output_section_find (".got");
+ if (toc_os != NULL)
+ sort_toc_sections (&toc_os->children, NULL, NULL);
+ }
}
gld${EMULATION_NAME}_before_allocation ();
@@ -507,7 +645,8 @@ PARSE_AND_LIST_PROLOGUE='
#define OPTION_NO_OPD_OPT (OPTION_NO_TLS_GET_ADDR_OPT + 1)
#define OPTION_NO_TOC_OPT (OPTION_NO_OPD_OPT + 1)
#define OPTION_NO_MULTI_TOC (OPTION_NO_TOC_OPT + 1)
-#define OPTION_NON_OVERLAPPING_OPD (OPTION_NO_MULTI_TOC + 1)
+#define OPTION_NO_TOC_SORT (OPTION_NO_MULTI_TOC + 1)
+#define OPTION_NON_OVERLAPPING_OPD (OPTION_NO_TOC_SORT + 1)
'
PARSE_AND_LIST_LONGOPTS='
@@ -521,6 +660,7 @@ PARSE_AND_LIST_LONGOPTS='
{ "no-opd-optimize", no_argument, NULL, OPTION_NO_OPD_OPT },
{ "no-toc-optimize", no_argument, NULL, OPTION_NO_TOC_OPT },
{ "no-multi-toc", no_argument, NULL, OPTION_NO_MULTI_TOC },
+ { "no-toc-sort", no_argument, NULL, OPTION_NO_TOC_SORT },
{ "non-overlapping-opd", no_argument, NULL, OPTION_NON_OVERLAPPING_OPD },
'
@@ -566,6 +706,9 @@ PARSE_AND_LIST_OPTIONS='
--no-multi-toc Disallow automatic multiple toc sections.\n"
));
fprintf (file, _("\
+ --no-toc-sort Don'\''t sort TOC and GOT sections.\n"
+ ));
+ fprintf (file, _("\
--non-overlapping-opd Canonicalize .opd, so that there are no\n\
overlapping .opd entries.\n"
));
@@ -617,6 +760,10 @@ PARSE_AND_LIST_ARGS_CASES='
no_multi_toc = 1;
break;
+ case OPTION_NO_TOC_SORT:
+ no_toc_sort = 1;
+ break;
+
case OPTION_NON_OVERLAPPING_OPD:
non_overlapping_opd = 1;
break;