aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ld/ChangeLog26
-rw-r--r--ld/ld.texinfo38
-rw-r--r--ld/ldgram.y12
-rw-r--r--ld/ldlang.c307
-rw-r--r--ld/ldlang.h11
-rw-r--r--ld/ldlex.l3
-rw-r--r--ld/ldmain.c4
-rw-r--r--ld/ldmain.h3
-rw-r--r--ld/lexsup.c2
-rw-r--r--ld/testsuite/ChangeLog8
-rw-r--r--ld/testsuite/ld-spu/ovl.d2
-rw-r--r--ld/testsuite/ld-spu/ovl.lnk7
-rw-r--r--ld/testsuite/ld-spu/ovl1.lnk9
-rw-r--r--ld/testsuite/ld-spu/ovl2.d2
-rw-r--r--ld/testsuite/ld-spu/ovl2.lnk10
15 files changed, 363 insertions, 81 deletions
diff --git a/ld/ChangeLog b/ld/ChangeLog
index c8e6b5b..ec540ad 100644
--- a/ld/ChangeLog
+++ b/ld/ChangeLog
@@ -1,3 +1,29 @@
+2008-01-25 Alan Modra <amodra@bigpond.net.au>
+
+ * ld.texinfo (INSERT): Describe.
+ * ldgram.y (ldgram_in_script, ldgram_had_equals): Delete.
+ (INSERT_K, AFTER, BEFORE): Add as tokens.
+ (ifile_p1): Handle INSERT statements.
+ (saved_script_handle, force_make_executable): Move to..
+ * ldmain.c: ..here.
+ (previous_script_handle): New global var.
+ * ldmain.h (saved_script_handle, force_make_executable): Declare.
+ (previous_script_handle): Likewise.
+ * ldlex.l (INSERT_K, AFTER, BEFORE): Add tokens.
+ * lexsup.c (parge_args <-T>): Set previous_script_handle.
+ * ldlang.c (lang_for_each_statement_worker): Handle insert statement.
+ (map_input_to_output_sections, print_statement): Likewise.
+ (lang_size_sections_1, lang_do_assignments_1): Likewise.
+ (insert_os_after): New function, extracted from..
+ (lang_insert_orphan): ..here.
+ (process_insert_statements): New function.
+ (lang_process): Call it.
+ (lang_add_insert): New function.
+ * ldlang.h (lang_insert_statement_enum): New.
+ (lang_insert_statement_type): New.
+ (lang_statement_union_type): Add insert_statement.
+ (lang_add_insert): Declare.
+
2008-01-18 Bob Wilson <bob.wilson@acm.org>
* scripttempl/elfxtensa.sc: Merge ENTRY and .note.gnu.build-id
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index 7d77d1c..00081da 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -297,10 +297,11 @@ augments the main linker script used for the link (either the default
linker script or the one specified by using @samp{-T}). This feature
permits the linker to link against a file which appears to be an object
or an archive, but actually merely defines some symbol values, or uses
-@code{INPUT} or @code{GROUP} to load other objects. Note that
-specifying a script in this way merely augments the main linker script;
-use the @samp{-T} option to replace the default linker script entirely.
-@xref{Scripts}.
+@code{INPUT} or @code{GROUP} to load other objects. Specifying a
+script in this way merely augments the main linker script, with the
+extra commands placed after the main script; use the @samp{-T} option
+to replace the default linker script entirely, but note the effect of
+the @code{INSERT} command. @xref{Scripts}.
For options whose names are a single letter,
option arguments must either follow the option letter without intervening
@@ -2903,6 +2904,35 @@ This command has the same effect as the @samp{--no-define-common}
command-line option: to make @code{ld} omit the assignment of addresses
to common symbols even for a non-relocatable output file.
+@item INSERT [ AFTER | BEFORE ] @var{output_section}
+@kindex INSERT
+@cindex insert user script into default script
+This command is typically used in a script specified by @samp{-T} to
+augment the default @code{SECTIONS} with, for example, overlays. It
+inserts all prior linker script statements after (or before)
+@var{output_section}, and also causes @samp{-T} to not override the
+default linker script. The exact insertion point is as for orphan
+sections. @xref{Location Counter}. The insertion happens after the
+linker has mapped input sections to output sections. Prior to the
+insertion, since @samp{-T} scripts are parsed before the default
+linker script, statements in the @samp{-T} script occur before the
+default linker script statements in the internal linker representation
+of the script. In particular, input section assignments will be made
+to @samp{-T} output sections before those in the default script. Here
+is an example of how a @samp{-T} script using @code{INSERT} might look:
+
+@smallexample
+SECTIONS
+@{
+ OVERLAY :
+ @{
+ .ov1 @{ ov1*(.text) @}
+ .ov2 @{ ov2*(.text) @}
+ @}
+@}
+INSERT AFTER .text;
+@end smallexample
+
@item NOCROSSREFS(@var{section} @var{section} @dots{})
@kindex NOCROSSREFS(@var{sections})
@cindex cross references
diff --git a/ld/ldgram.y b/ld/ldgram.y
index 5b68b6b..f481f54 100644
--- a/ld/ldgram.y
+++ b/ld/ldgram.y
@@ -49,11 +49,6 @@
static enum section_type sectype;
static lang_memory_region_type *region;
-FILE *saved_script_handle = NULL;
-bfd_boolean force_make_executable = FALSE;
-
-bfd_boolean ldgram_in_script = FALSE;
-bfd_boolean ldgram_had_equals = FALSE;
bfd_boolean ldgram_had_keep = FALSE;
char *ldgram_vers_current_lang = NULL;
@@ -127,7 +122,8 @@ static int error_index;
%token END
%left <token> '('
%token <token> ALIGN_K BLOCK BIND QUAD SQUAD LONG SHORT BYTE
-%token SECTIONS PHDRS DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END DATA_SEGMENT_END
+%token SECTIONS PHDRS INSERT_K AFTER BEFORE
+%token DATA_SEGMENT_ALIGN DATA_SEGMENT_RELRO_END DATA_SEGMENT_END
%token SORT_BY_NAME SORT_BY_ALIGNMENT
%token '{' '}'
%token SIZEOF_HEADERS OUTPUT_FORMAT FORCE_COMMON_ALLOCATION OUTPUT_ARCH
@@ -352,6 +348,10 @@ ifile_p1:
lang_add_nocrossref ($3);
}
| EXTERN '(' extern_name_list ')'
+ | INSERT_K AFTER NAME
+ { lang_add_insert ($3, 0); }
+ | INSERT_K BEFORE NAME
+ { lang_add_insert ($3, 1); }
;
input_list:
diff --git a/ld/ldlang.c b/ld/ldlang.c
index c5ad76d..de3f64b7 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -845,6 +845,7 @@ lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
case lang_padding_statement_enum:
case lang_address_statement_enum:
case lang_fill_statement_enum:
+ case lang_insert_statement_enum:
break;
default:
FAIL ();
@@ -1451,6 +1452,73 @@ output_prev_sec_find (lang_output_section_statement_type *os)
return NULL;
}
+/* Look for a suitable place for a new output section statement. The
+ idea is to skip over anything that might be inside a SECTIONS {}
+ statement in a script, before we find another output section
+ statement. Assignments to "dot" before an output section statement
+ are assumed to belong to it. An exception to this rule is made for
+ the first assignment to dot, otherwise we might put an orphan
+ before . = . + SIZEOF_HEADERS or similar assignments that set the
+ initial address. */
+
+static lang_statement_union_type **
+insert_os_after (lang_output_section_statement_type *after)
+{
+ lang_statement_union_type **where;
+ lang_statement_union_type **assign = NULL;
+ bfd_boolean ignore_first;
+
+ ignore_first
+ = after == &lang_output_section_statement.head->output_section_statement;
+
+ for (where = &after->header.next;
+ *where != NULL;
+ where = &(*where)->header.next)
+ {
+ switch ((*where)->header.type)
+ {
+ case lang_assignment_statement_enum:
+ if (assign == NULL)
+ {
+ lang_assignment_statement_type *ass;
+
+ ass = &(*where)->assignment_statement;
+ if (ass->exp->type.node_class != etree_assert
+ && ass->exp->assign.dst[0] == '.'
+ && ass->exp->assign.dst[1] == 0
+ && !ignore_first)
+ assign = where;
+ }
+ ignore_first = FALSE;
+ continue;
+ case lang_wild_statement_enum:
+ case lang_input_section_enum:
+ case lang_object_symbols_statement_enum:
+ case lang_fill_statement_enum:
+ case lang_data_statement_enum:
+ case lang_reloc_statement_enum:
+ case lang_padding_statement_enum:
+ case lang_constructors_statement_enum:
+ assign = NULL;
+ continue;
+ case lang_output_section_statement_enum:
+ if (assign != NULL)
+ where = assign;
+ break;
+ case lang_input_statement_enum:
+ case lang_address_statement_enum:
+ case lang_target_statement_enum:
+ case lang_output_statement_enum:
+ case lang_group_statement_enum:
+ case lang_insert_statement_enum:
+ continue;
+ }
+ break;
+ }
+
+ return where;
+}
+
lang_output_section_statement_type *
lang_insert_orphan (asection *s,
const char *secname,
@@ -1606,64 +1674,7 @@ lang_insert_orphan (asection *s,
if (place->stmt == NULL)
{
- lang_statement_union_type **where;
- lang_statement_union_type **assign = NULL;
- bfd_boolean ignore_first;
-
- /* Look for a suitable place for the new statement list.
- The idea is to skip over anything that might be inside
- a SECTIONS {} statement in a script, before we find
- another output_section_statement. Assignments to "dot"
- before an output section statement are assumed to
- belong to it. An exception to this rule is made for
- the first assignment to dot, otherwise we might put an
- orphan before . = . + SIZEOF_HEADERS or similar
- assignments that set the initial address. */
-
- ignore_first = after == (&lang_output_section_statement.head
- ->output_section_statement);
- for (where = &after->header.next;
- *where != NULL;
- where = &(*where)->header.next)
- {
- switch ((*where)->header.type)
- {
- case lang_assignment_statement_enum:
- if (assign == NULL)
- {
- lang_assignment_statement_type *ass;
- ass = &(*where)->assignment_statement;
- if (ass->exp->type.node_class != etree_assert
- && ass->exp->assign.dst[0] == '.'
- && ass->exp->assign.dst[1] == 0
- && !ignore_first)
- assign = where;
- }
- ignore_first = FALSE;
- continue;
- case lang_wild_statement_enum:
- case lang_input_section_enum:
- case lang_object_symbols_statement_enum:
- case lang_fill_statement_enum:
- case lang_data_statement_enum:
- case lang_reloc_statement_enum:
- case lang_padding_statement_enum:
- case lang_constructors_statement_enum:
- assign = NULL;
- continue;
- case lang_output_section_statement_enum:
- if (assign != NULL)
- where = assign;
- break;
- case lang_input_statement_enum:
- case lang_address_statement_enum:
- case lang_target_statement_enum:
- case lang_output_statement_enum:
- case lang_group_statement_enum:
- continue;
- }
- break;
- }
+ lang_statement_union_type **where = insert_os_after (after);
*add.tail = *where;
*where = add.head;
@@ -3312,6 +3323,154 @@ map_input_to_output_sections
aos->addr_tree = s->address_statement.address;
}
break;
+ case lang_insert_statement_enum:
+ break;
+ }
+ }
+}
+
+/* An insert statement snips out all the linker statements from the
+ start of the list and places them after the output section
+ statement specified by the insert. This operation is complicated
+ by the fact that we keep a doubly linked list of output section
+ statements as well as the singly linked list of all statements. */
+
+static void
+process_insert_statements (void)
+{
+ lang_statement_union_type **s;
+ lang_output_section_statement_type *first_os = NULL;
+ lang_output_section_statement_type *last_os = NULL;
+
+ /* "start of list" is actually the statement immediately after
+ the special abs_section output statement, so that it isn't
+ reordered. */
+ s = &lang_output_section_statement.head;
+ while (*(s = &(*s)->header.next) != NULL)
+ {
+ if ((*s)->header.type == lang_output_section_statement_enum)
+ {
+ /* Keep pointers to the first and last output section
+ statement in the sequence we may be about to move. */
+ last_os = &(*s)->output_section_statement;
+ if (first_os == NULL)
+ first_os = last_os;
+ }
+ else if ((*s)->header.type == lang_insert_statement_enum)
+ {
+ lang_insert_statement_type *i = &(*s)->insert_statement;
+ lang_output_section_statement_type *where;
+ lang_output_section_statement_type *os;
+ lang_statement_union_type **ptr;
+ lang_statement_union_type *first;
+
+ where = lang_output_section_find (i->where);
+ if (where != NULL && i->is_before)
+ {
+ do
+ where = where->prev;
+ while (where != NULL && where->constraint == -1);
+ }
+ if (where == NULL)
+ {
+ einfo (_("%X%P: %s not found for insert\n"), i->where);
+ continue;
+ }
+ /* You can't insert into the list you are moving. */
+ for (os = first_os; os != NULL; os = os->next)
+ if (os == where || os == last_os)
+ break;
+ if (os == where)
+ {
+ einfo (_("%X%P: %s not found for insert\n"), i->where);
+ continue;
+ }
+
+ /* Deal with reordering the output section statement list. */
+ if (last_os != NULL)
+ {
+ asection *first_sec, *last_sec;
+
+ /* Snip out the output sections we are moving. */
+ first_os->prev->next = last_os->next;
+ if (last_os->next == NULL)
+ lang_output_section_statement.tail
+ = (union lang_statement_union **) &first_os->prev->next;
+ else
+ last_os->next->prev = first_os->prev;
+ /* Add them in at the new position. */
+ last_os->next = where->next;
+ if (where->next == NULL)
+ lang_output_section_statement.tail
+ = (union lang_statement_union **) &last_os->next;
+ else
+ where->next->prev = last_os;
+ first_os->prev = where;
+ where->next = first_os;
+
+ /* Move the bfd sections in the same way. */
+ first_sec = NULL;
+ last_sec = NULL;
+ for (os = first_os; os != NULL; os = os->next)
+ {
+ if (os->bfd_section != NULL
+ && os->bfd_section->owner != NULL)
+ {
+ last_sec = os->bfd_section;
+ if (first_sec == NULL)
+ first_sec = last_sec;
+ }
+ if (os == last_os)
+ break;
+ }
+ if (last_sec != NULL)
+ {
+ asection *sec = where->bfd_section;
+ if (sec == NULL)
+ sec = output_prev_sec_find (where);
+
+ /* The place we want to insert must come after the
+ sections we are moving. So if we find no
+ section or if the section is the same as our
+ last section, then no move is needed. */
+ if (sec != NULL && sec != last_sec)
+ {
+ /* Trim them off. */
+ if (first_sec->prev != NULL)
+ first_sec->prev->next = last_sec->next;
+ else
+ output_bfd->sections = last_sec->next;
+ if (last_sec->next != NULL)
+ last_sec->next->prev = first_sec->prev;
+ else
+ output_bfd->section_last = first_sec->prev;
+ /* Add back. */
+ last_sec->next = sec->next;
+ if (sec->next != NULL)
+ sec->next->prev = last_sec;
+ else
+ output_bfd->section_last = last_sec;
+ first_sec->prev = sec;
+ sec->next = first_sec;
+ }
+ }
+
+ first_os = NULL;
+ last_os = NULL;
+ }
+
+ ptr = insert_os_after (where);
+ /* Snip everything after the abs_section output statement we
+ know is at the start of the list, up to and including
+ the insert statement we are currently processing. */
+ first = lang_output_section_statement.head->header.next;
+ lang_output_section_statement.head->header.next = (*s)->header.next;
+ /* Add them back where they belong. */
+ *s = *ptr;
+ if (*s == NULL)
+ statement_list.tail = s;
+ *ptr = first;
+ s = &lang_output_section_statement.head;
}
}
}
@@ -3954,6 +4113,11 @@ print_statement (lang_statement_union_type *s,
case lang_group_statement_enum:
print_group (&s->group_statement, os);
break;
+ case lang_insert_statement_enum:
+ minfo ("INSERT %s %s\n",
+ s->insert_statement.is_before ? "BEFORE" : "AFTER",
+ s->insert_statement.where);
+ break;
}
}
@@ -4734,13 +4898,16 @@ lang_size_sections_1
fill, dot, relax, check_regions);
break;
- default:
- FAIL ();
+ case lang_insert_statement_enum:
break;
/* We can only get here when relaxing is turned on. */
case lang_address_statement_enum:
break;
+
+ default:
+ FAIL ();
+ break;
}
prev = &s->header.next;
}
@@ -4999,12 +5166,15 @@ lang_do_assignments_1 (lang_statement_union_type *s,
current_os, fill, dot);
break;
- default:
- FAIL ();
+ case lang_insert_statement_enum:
break;
case lang_address_statement_enum:
break;
+
+ default:
+ FAIL ();
+ break;
}
}
return dot;
@@ -5853,6 +6023,8 @@ lang_process (void)
to the correct output sections. */
map_input_to_output_sections (statement_list.head, NULL, NULL);
+ process_insert_statements ();
+
/* Find any sections not attached explicitly and handle them. */
lang_place_orphans ();
@@ -6269,6 +6441,17 @@ lang_add_output_format (const char *format,
}
}
+void
+lang_add_insert (const char *where, int is_before)
+{
+ lang_insert_statement_type *new;
+
+ new = new_stat (lang_insert_statement, stat_ptr);
+ new->where = where;
+ new->is_before = is_before;
+ saved_script_handle = previous_script_handle;
+}
+
/* Enter a group. This creates a new lang_group_statement, and sets
stat_ptr to build new statements within the group. */
diff --git a/ld/ldlang.h b/ld/ldlang.h
index d0d2cf0..6de4267 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -79,6 +79,7 @@ typedef struct lang_statement_header_struct
lang_output_statement_enum,
lang_padding_statement_enum,
lang_group_statement_enum,
+ lang_insert_statement_enum,
lang_constructors_statement_enum
} type;
} lang_statement_header_type;
@@ -352,6 +353,13 @@ typedef struct
lang_statement_list_type children;
} lang_group_statement_type;
+typedef struct
+{
+ lang_statement_header_type header;
+ const char *where;
+ bfd_boolean is_before;
+} lang_insert_statement_type;
+
typedef union lang_statement_union
{
lang_statement_header_type header;
@@ -370,6 +378,7 @@ typedef union lang_statement_union
lang_fill_statement_type fill_statement;
lang_padding_statement_type padding_statement;
lang_group_statement_type group_statement;
+ lang_insert_statement_type insert_statement;
} lang_statement_union_type;
/* This structure holds information about a program header, from the
@@ -565,6 +574,8 @@ extern void lang_size_sections
(bfd_boolean *, bfd_boolean);
extern void one_lang_size_sections_pass
(bfd_boolean *, bfd_boolean);
+extern void lang_add_insert
+ (const char *, int);
extern void lang_enter_group
(void);
extern void lang_leave_group
diff --git a/ld/ldlex.l b/ld/ldlex.l
index c7b1cf9..62610cf 100644
--- a/ld/ldlex.l
+++ b/ld/ldlex.l
@@ -274,6 +274,9 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
<BOTH,SCRIPT>"FORCE_COMMON_ALLOCATION" { RTOKEN(FORCE_COMMON_ALLOCATION);}
<BOTH,SCRIPT>"INHIBIT_COMMON_ALLOCATION" { RTOKEN(INHIBIT_COMMON_ALLOCATION);}
<BOTH,SCRIPT>"SECTIONS" { RTOKEN(SECTIONS);}
+<BOTH,SCRIPT>"INSERT" { RTOKEN(INSERT_K);}
+<BOTH,SCRIPT>"AFTER" { RTOKEN(AFTER);}
+<BOTH,SCRIPT>"BEFORE" { RTOKEN(BEFORE);}
<BOTH,SCRIPT>"FILL" { RTOKEN(FILL);}
<BOTH,SCRIPT>"STARTUP" { RTOKEN(STARTUP);}
<BOTH,SCRIPT>"OUTPUT_FORMAT" { RTOKEN(OUTPUT_FORMAT);}
diff --git a/ld/ldmain.c b/ld/ldmain.c
index 29f00d6..a9af255 100644
--- a/ld/ldmain.c
+++ b/ld/ldmain.c
@@ -60,6 +60,10 @@ extern void *sbrk ();
/* EXPORTS */
+FILE *saved_script_handle = NULL;
+FILE *previous_script_handle = NULL;
+bfd_boolean force_make_executable = FALSE;
+
char *default_target;
const char *output_filename = "a.out";
diff --git a/ld/ldmain.h b/ld/ldmain.h
index 5491939..d297e8a 100644
--- a/ld/ldmain.h
+++ b/ld/ldmain.h
@@ -26,6 +26,9 @@ extern char *program_name;
extern const char *ld_sysroot;
extern char *ld_canon_sysroot;
extern int ld_canon_sysroot_len;
+extern FILE *saved_script_handle;
+extern FILE *previous_script_handle;
+extern bfd_boolean force_make_executable;
extern bfd *output_bfd;
extern char *default_target;
extern bfd_boolean trace_files;
diff --git a/ld/lexsup.c b/ld/lexsup.c
index cdbfbdf..1e133d9 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -1166,9 +1166,11 @@ parse_args (unsigned argc, char **argv)
trace_files = TRUE;
break;
case 'T':
+ previous_script_handle = saved_script_handle;
ldfile_open_command_file (optarg);
parser_input = input_script;
yyparse ();
+ previous_script_handle = NULL;
break;
case OPTION_DEFAULT_SCRIPT:
command_line.default_script = optarg;
diff --git a/ld/testsuite/ChangeLog b/ld/testsuite/ChangeLog
index f55e310..bda99a2 100644
--- a/ld/testsuite/ChangeLog
+++ b/ld/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2008-01-25 Alan Modra <amodra@bigpond.net.au>
+
+ * ld-spu/ovl.lnk: Delete overlay.
+ * ld-spu/ovl1.lnk: New file.
+ * ld-spu/ovl2.lnk: New file.
+ * ld-spu/ovl.d: Update.
+ * ld-spu/ovl2.d: Update.
+
2008-01-23 Andreas Schwab <schwab@suse.de>
* ld-gc/gc.c: Make sure used_func is not inlined.
diff --git a/ld/testsuite/ld-spu/ovl.d b/ld/testsuite/ld-spu/ovl.d
index 0f3deb2..c624659 100644
--- a/ld/testsuite/ld-spu/ovl.d
+++ b/ld/testsuite/ld-spu/ovl.d
@@ -1,5 +1,5 @@
#source: ovl.s
-#ld: -N -T ovl.lnk --emit-relocs
+#ld: -N -T ovl1.lnk -T ovl.lnk --emit-relocs
#objdump: -D -r
.*elf32-spu
diff --git a/ld/testsuite/ld-spu/ovl.lnk b/ld/testsuite/ld-spu/ovl.lnk
index 408ed1e..0015652 100644
--- a/ld/testsuite/ld-spu/ovl.lnk
+++ b/ld/testsuite/ld-spu/ovl.lnk
@@ -2,13 +2,6 @@ SECTIONS
{
. = SIZEOF_HEADERS;
.text : { *(.text) *(.stub) }
-
- OVERLAY 0x400 :
- {
- .ov_a1 { *(.ov_a1) }
- .ov_a2 { *(.ov_a2) }
- }
-
.data : { *(.data) *(.ovtab) }
.bss : { *(.bss) }
}
diff --git a/ld/testsuite/ld-spu/ovl1.lnk b/ld/testsuite/ld-spu/ovl1.lnk
new file mode 100644
index 0000000..5fd4844
--- /dev/null
+++ b/ld/testsuite/ld-spu/ovl1.lnk
@@ -0,0 +1,9 @@
+SECTIONS
+{
+ OVERLAY 0x400 :
+ {
+ .ov_a1 { *(.ov_a1) }
+ .ov_a2 { *(.ov_a2) }
+ }
+}
+INSERT AFTER .text;
diff --git a/ld/testsuite/ld-spu/ovl2.d b/ld/testsuite/ld-spu/ovl2.d
index e4a47a6..bf62e03 100644
--- a/ld/testsuite/ld-spu/ovl2.d
+++ b/ld/testsuite/ld-spu/ovl2.d
@@ -1,5 +1,5 @@
#source: ovl2.s
-#ld: -N -T ovl.lnk --emit-relocs
+#ld: -N -T ovl2.lnk -T ovl.lnk --emit-relocs
#objdump: -D -r
.*elf32-spu
diff --git a/ld/testsuite/ld-spu/ovl2.lnk b/ld/testsuite/ld-spu/ovl2.lnk
new file mode 100644
index 0000000..9916ab2
--- /dev/null
+++ b/ld/testsuite/ld-spu/ovl2.lnk
@@ -0,0 +1,10 @@
+SECTIONS
+{
+ OVERLAY 0x400 :
+ {
+ .ov_a1 { *(.ov_a1) }
+ .ov_a2 { *(.ov_a2) }
+ .empty { empty.o?(.text) }
+ }
+}
+INSERT BEFORE .data;