[binutils-gdb] PR ld/24600: LD: Add support for file collections as artificial archives

Maciej W. Rozycki macro@sourceware.org
Fri Feb 20 17:43:29 GMT 2026


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=b4f81bbdbc2e6c5731fee52f21e55d1563abad02

commit b4f81bbdbc2e6c5731fee52f21e55d1563abad02
Author: Maciej W. Rozycki <macro@redhat.com>
Date:   Fri Feb 20 17:29:51 2026 +0000

    PR ld/24600: LD: Add support for file collections as artificial archives
    
    Implement linker support for `--start-lib' and `--end-lib' command-line
    options and an analogous LIB linker script statement for treating a list
    of object files as members of an artificial archive.  This is built upon
    BFD's fake archive feature, to support the same command-line options of
    the GOLD linker being phased out.
    
    Usual archive semantics is preserved for such artificial archives, such
    as symbol resolution rules, archive grouping, and the `--whole-archive'
    feature, except for the `--no-link-mapless' setting which is ignored as
    meaningless for such loose file collections which cannot have a symbol
    map.
    
    There is no support for nested archives or other members that are not
    object files, e.g. if a file supplied as a member archive is a linker
    script, it will be silently ignored, just as with actual archives.  It
    is unlike with input files passed that are not members of an artificial
    archive.
    
    Update documentation and expand test coverage accordingly.

Diff:
---
 bfd/archive.c                       |   2 +-
 bfd/elf64-ia64-vms.c                |  15 ++-
 bfd/vms-alpha.c                     |  15 ++-
 bfd/vms-lib.c                       |   2 +
 ld/NEWS                             |   4 +
 ld/emultempl/xtensaelf.em           |   1 +
 ld/ld.texi                          |  39 +++++++-
 ld/ldgram.y                         |  27 ++++--
 ld/ldlang.c                         | 131 ++++++++++++++++++++++++++
 ld/ldlang.h                         |  21 +++++
 ld/ldlex.h                          |   2 +
 ld/ldlex.l                          |   1 +
 ld/ldmisc.c                         |  11 ++-
 ld/lexsup.c                         |  39 +++++++-
 ld/testsuite/ld-archive/ab.t        |   1 +
 ld/testsuite/ld-archive/abfc.vd     |  28 ++++++
 ld/testsuite/ld-archive/abfcfg.vd   |  36 +++++++
 ld/testsuite/ld-archive/absc.vd     |  35 +++++++
 ld/testsuite/ld-archive/abscfg.vd   |  43 +++++++++
 ld/testsuite/ld-archive/abwfc.vd    |  30 ++++++
 ld/testsuite/ld-archive/abwsc.vd    |  37 ++++++++
 ld/testsuite/ld-archive/archive.exp | 183 ++++++++++++++++++++++++++++++++++++
 ld/testsuite/ld-archive/cd.t        |   1 +
 ld/testsuite/ld-cris/archive.exp    |  18 ++++
 24 files changed, 709 insertions(+), 13 deletions(-)

diff --git a/bfd/archive.c b/bfd/archive.c
index 6841e0a5aae..db978dfea2c 100644
--- a/bfd/archive.c
+++ b/bfd/archive.c
@@ -673,7 +673,7 @@ char *
 _bfd_append_relative_path (bfd *arch, char *elt_name)
 {
   const char *arch_name = bfd_get_filename (arch);
-  const char *base_name = lbasename (arch_name);
+  const char *base_name = arch_name != NULL ? lbasename (arch_name) : NULL;
   size_t prefix_len;
   char *filename;
 
diff --git a/bfd/elf64-ia64-vms.c b/bfd/elf64-ia64-vms.c
index 4403688ec96..54735559ef8 100644
--- a/bfd/elf64-ia64-vms.c
+++ b/bfd/elf64-ia64-vms.c
@@ -5310,6 +5310,19 @@ elf64_vms_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
       return false;
     }
 
+  /* This can only ever happen for fake archives.  */
+  if (!bfd_has_map (abfd))
+    {
+      bfd *first_one = bfd_openr_next_archived_file (abfd, NULL);
+
+      /* An empty archive is a special case.  */
+      if (first_one == NULL)
+	return true;
+
+      if (!_bfd_make_armap (abfd, first_one))
+	return false;
+    }
+
   /* The archive_pass field in the archive itself is used to
      initialize PASS, since we may search the same archive multiple
      times.  */
@@ -5363,7 +5376,7 @@ elf64_vms_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
 	}
 
       orig_element = element;
-      if (bfd_is_thin_archive (abfd))
+      if (bfd_is_thin_archive (abfd) && !bfd_is_fake_archive (abfd))
 	{
 	  element = _bfd_vms_lib_get_imagelib_file (element);
 	  if (element == NULL || !bfd_check_format (element, bfd_object))
diff --git a/bfd/vms-alpha.c b/bfd/vms-alpha.c
index eea3c95dab8..8454ab6ebdc 100644
--- a/bfd/vms-alpha.c
+++ b/bfd/vms-alpha.c
@@ -9116,6 +9116,19 @@ alpha_vms_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
       return false;
     }
 
+  /* This can only ever happen for fake archives.  */
+  if (!bfd_has_map (abfd))
+    {
+      bfd *first_one = bfd_openr_next_archived_file (abfd, NULL);
+
+      /* An empty archive is a special case.  */
+      if (first_one == NULL)
+	return true;
+
+      if (!_bfd_make_armap (abfd, first_one))
+	return false;
+    }
+
   /* The archive_pass field in the archive itself is used to
      initialize PASS, since we may search the same archive multiple
      times.  */
@@ -9175,7 +9188,7 @@ alpha_vms_link_add_archive_symbols (bfd *abfd, struct bfd_link_info *info)
 	}
 
       orig_element = element;
-      if (bfd_is_thin_archive (abfd))
+      if (bfd_is_thin_archive (abfd) && !bfd_is_fake_archive (abfd))
 	{
 	  element = _bfd_vms_lib_get_imagelib_file (element);
 	  if (element == NULL || !bfd_check_format (element, bfd_object))
diff --git a/bfd/vms-lib.c b/bfd/vms-lib.c
index a718705038f..87dc57aea36 100644
--- a/bfd/vms-lib.c
+++ b/bfd/vms-lib.c
@@ -1485,6 +1485,8 @@ _bfd_vms_lib_get_elt_at_index (bfd *abfd, symindex symidx)
   /* Check symidx.  */
   if (symidx > tdata->artdata.symdef_count)
     return NULL;
+  if (tdata->artdata.symdef_use_bfd)
+    return tdata->artdata.symdefs[symidx].u.abfd;
   file_off = tdata->artdata.symdefs[symidx].u.file_offset;
 
   /* Linear-scan.  */
diff --git a/ld/NEWS b/ld/NEWS
index 9c9896d950a..c4f89fd389f 100644
--- a/ld/NEWS
+++ b/ld/NEWS
@@ -2,6 +2,10 @@
 
 Changes in 2.47:
 
+* Add linker support for --start-lib/--end-lib options and LIB linker script
+  statement for treating a list of object files as members of an artificial
+  archive.
+
 * Add linker support for archives with no symbol index in their all formats,
   not only XCOFF.  On by default and controlled with --no-link-mapless and
   --link-mapless options, also for XCOFF.
diff --git a/ld/emultempl/xtensaelf.em b/ld/emultempl/xtensaelf.em
index 99d159658ce..3a7e0842979 100644
--- a/ld/emultempl/xtensaelf.em
+++ b/ld/emultempl/xtensaelf.em
@@ -1395,6 +1395,7 @@ xtensa_strip_inconsistent_linkonce_sections (lang_statement_list_type *slist)
 	case lang_padding_statement_enum:
 	case lang_address_statement_enum:
 	case lang_fill_statement_enum:
+	case lang_lib_statement_enum:
 	  break;
 
 	default:
diff --git a/ld/ld.texi b/ld/ld.texi
index 13aef27612f..90d19c27d57 100644
--- a/ld/ld.texi
+++ b/ld/ld.texi
@@ -1790,10 +1790,35 @@ would not be able to resolve that reference.  By grouping the archives,
 they will all be searched repeatedly until all possible references are
 resolved.
 
+This option is not allowed within a @option{--start-lib}/@option{--end-lib}
+option pair.
+
 Using this option has a significant performance cost.  It is best to use
 it only when there are unavoidable circular references between two or
 more archives.
 
+@kindex --start-lib
+@cindex artificial archives
+@item --start-lib @var{objects} --end-lib
+The @var{objects} should be a list of object files.
+
+The specified objects are treated as if they were members of an archive
+the name of which has been placed at the location of @option{--start-lib}
+on the command line.  All the usual linker archive handling properties
+and command line options apply to such an artificial archive.
+
+This option may be used multiple times, in which case each set of the
+objects specified will be treated as an individual archive.  Nesting this
+option is not allowed though.
+
+Options that affect archive processing are not allowed between
+@option{--start-lib} and @option{--end-lib} as it would make no sense
+to apply them midway through handling an archive.  These options include:
+@option{-(}/@option{--start-group}, @option{-)}/@option{--end-group},
+@option{--whole-archive}, and @option{--no-whole-archive}, as well as
+@option{--pop-state} where it would change the state of
+@option{--whole-archive} setting.
+
 @kindex --accept-unknown-input-arch
 @kindex --no-accept-unknown-input-arch
 @item --accept-unknown-input-arch
@@ -2372,7 +2397,8 @@ library during a library search.  This option silences the warning.
 @kindex --no-whole-archive
 @item --no-whole-archive
 Turn off the effect of the @option{--whole-archive} option for subsequent
-archive files.
+archive files.  Not allowed within a @option{--start-lib}/@option{--end-lib}
+option pair.
 
 @kindex --no-link-mapless
 @item --no-link-mapless
@@ -3187,6 +3213,9 @@ files.  This is normally used to turn an archive file into a shared
 library, forcing every object to be included in the resulting shared
 library.  This option may be used more than once.
 
+This option is not allowed within a @option{--start-lib}/@option{--end-lib}
+option pair.
+
 Two notes when using this option from gcc: First, gcc doesn't know
 about this option, so you have to use @option{-Wl,-whole-archive}.
 Second, don't forget to use @option{-Wl,-no-whole-archive} after your
@@ -4515,6 +4544,14 @@ files should all be archives, and they are searched repeatedly until no
 new undefined references are created.  See the description of @samp{-(}
 in @ref{Options,,Command-line Options}.
 
+@item LIB(@var{file}, @var{file}, @dots{})
+@itemx LIB(@var{file} @var{file} @dots{})
+@kindex LIB(@var{files})
+@cindex artificial archives in linker scripts
+The @code{LIB} command is like @code{INPUT}, except that the named files
+are treated as if they were members of an archive.  See the description
+of @samp{--start-lib} in @ref{Options,,Command-line Options}.
+
 @item AS_NEEDED(@var{file}, @var{file}, @dots{})
 @itemx AS_NEEDED(@var{file} @var{file} @dots{})
 @kindex AS_NEEDED(@var{files})
diff --git a/ld/ldgram.y b/ld/ldgram.y
index 47cc1bb6f05..0b5f1d4aac1 100644
--- a/ld/ldgram.y
+++ b/ld/ldgram.y
@@ -149,7 +149,7 @@ static void yyerror (const char *);
 %token SIZEOF ALIGNOF ADDR LOADADDR MAX_K MIN_K
 %token STARTUP HLL SYSLIB FLOAT NOFLOAT NOCROSSREFS NOCROSSREFS_TO
 %token ORIGIN FILL
-%token LENGTH CREATE_OBJECT_SYMBOLS INPUT GROUP OUTPUT CONSTRUCTORS
+%token LENGTH CREATE_OBJECT_SYMBOLS INPUT GROUP LIB OUTPUT CONSTRUCTORS
 %token ALIGNMOD AT SUBALIGN HIDDEN PROVIDE PROVIDE_HIDDEN AS_NEEDED
 %type <token> assign_op atype attributes_opt sect_constraint opt_align_with_input
 %type <name>  filename
@@ -342,6 +342,10 @@ ifile_p1:
 		  { lang_enter_group (); }
 		    '(' input_list ')'
 		  { lang_leave_group (); }
+	|	LIB
+		  { lang_enter_lib (); }
+		    '(' input_list ')'
+		  { lang_leave_lib (); }
 	|	MAP '(' filename ')'
 		{ lang_add_map($3); }
 	|	INCLUDE filename
@@ -374,14 +378,23 @@ input_list:
 
 input_list1:
 		NAME
-		{ lang_add_input_file($1,lang_input_file_is_search_file_enum,
-				 (char *)NULL); }
+		{ lang_add_input_file ($1,
+				       (input_flags.fake_archive
+					? lang_input_file_is_search_member_enum
+					: lang_input_file_is_search_file_enum),
+				       (char *) NULL); }
 	|	input_list1 ',' NAME
-		{ lang_add_input_file($3,lang_input_file_is_search_file_enum,
-				 (char *)NULL); }
+		{ lang_add_input_file ($3,
+				       (input_flags.fake_archive
+					? lang_input_file_is_search_member_enum
+					: lang_input_file_is_search_file_enum),
+				       (char *) NULL); }
 	|	input_list1 NAME
-		{ lang_add_input_file($2,lang_input_file_is_search_file_enum,
-				 (char *)NULL); }
+		{ lang_add_input_file ($2,
+				       (input_flags.fake_archive
+					? lang_input_file_is_search_member_enum
+					: lang_input_file_is_search_file_enum),
+				       (char *) NULL); }
 	|	LNAME
 		{ lang_add_input_file($1,lang_input_file_is_l_enum,
 				 (char *)NULL); }
diff --git a/ld/ldlang.c b/ld/ldlang.c
index 21247a23f3d..df19dca6c17 100644
--- a/ld/ldlang.c
+++ b/ld/ldlang.c
@@ -21,6 +21,7 @@
 #include "sysdep.h"
 #include <limits.h>
 #include "bfd.h"
+#include "libbfd.h"
 #include "libiberty.h"
 #include "filenames.h"
 #include "safe-ctype.h"
@@ -1118,6 +1119,11 @@ lang_for_each_statement_worker (void (*func) (lang_statement_union_type *),
 					  s->group_statement.children.head,
 					  constrained);
 	  break;
+	case lang_lib_statement_enum:
+	  lang_for_each_statement_worker (func,
+					  s->lib_statement.children.head,
+					  constrained);
+	  break;
 	case lang_data_statement_enum:
 	case lang_reloc_statement_enum:
 	case lang_object_symbols_statement_enum:
@@ -1225,6 +1231,25 @@ new_afile (const char *name,
       p->filename = name;
       p->local_sym_name = name;
       break;
+    case lang_input_file_is_fake_archive_enum:
+      p->filename = name;
+      p->flags.link_mapless = true;
+      break;
+    case lang_input_file_is_search_member_enum:
+      p->filename = name;
+      p->local_sym_name = name;
+      /* If name is a relative path, search the directory of the current linker
+	 script first. */
+      if (from_filename && !IS_ABSOLUTE_PATH (name))
+	p->extra_search_path = stat_ldirname (from_filename);
+      p->flags.member = true;
+      p->flags.search_dirs = true;
+      break;
+    case lang_input_file_is_member_enum:
+      p->filename = name;
+      p->local_sym_name = name;
+      p->flags.member = true;
+      break;
     case lang_input_file_is_l_enum:
       if (name[0] == ':' && name[1] != '\0')
 	{
@@ -1998,6 +2023,7 @@ insert_os_after (lang_statement_union_type *after)
 	case lang_output_statement_enum:
 	case lang_group_statement_enum:
 	case lang_insert_statement_enum:
+	case lang_lib_statement_enum:
 	  continue;
 	case lang_input_matcher_enum:
 	  FAIL ();
@@ -3796,6 +3822,54 @@ open_input_bfds (lang_statement_union_type *s,
 	  if (s->assignment_statement.exp->type.node_class != etree_assert)
 	    exp_fold_tree_no_dot (s->assignment_statement.exp, os);
 	  break;
+	case lang_lib_statement_enum:
+	  {
+	    lang_statement_union_type *c;
+	    bfd *first_bfd = NULL, **next_bfd = &first_bfd;
+
+	    for (c = s->lib_statement.children.head;
+		 c != NULL;
+		 c = c->header.next)
+	      if (c->header.type == lang_input_statement_enum
+		  && c->input_statement.flags.member)
+		{
+		  bfd *abfd;
+
+		  c->input_statement.target = current_target;
+		  ldfile_open_file (&c->input_statement);
+		  abfd = c->input_statement.the_bfd;
+		  if (abfd != NULL)
+		    {
+		      *next_bfd = abfd;
+		      next_bfd = &abfd->proxy_handle.abfd;
+		    }
+		}
+
+	    if (first_bfd != NULL)
+	      {
+		lang_input_statement_type *h, *p;
+		bfd *fake_bfd;
+
+		h = &s->lib_statement.children.head->input_statement;
+		p = insert_input_file (NULL,
+				       lang_input_file_is_fake_archive_enum,
+				       h->target);
+		ASSERT (p != NULL);
+
+		fake_bfd = bfd_openr_fake_archive (first_bfd);
+		if (fake_bfd == NULL)
+		  fatal
+		    (_("cannot create --start-lib/--end-lib wrapper BFD:"
+		       " %E\n"));
+		p->the_bfd = fake_bfd;
+
+		if (!load_symbols (p, NULL))
+		  config.make_executable = false;
+	      }
+
+	    open_input_bfds (s->lib_statement.children.head, os, mode,
+			     nested_group_count_p);
+	  }
 	default:
 	  break;
 	}
@@ -4256,6 +4330,12 @@ check_input_sections
 	  if (!output_section_statement->all_input_readonly)
 	    return;
 	  break;
+	case lang_lib_statement_enum:
+	  check_input_sections (s->lib_statement.children.head,
+				output_section_statement);
+	  if (!output_section_statement->all_input_readonly)
+	    return;
+	  break;
 	default:
 	  break;
 	}
@@ -4379,6 +4459,8 @@ map_input_to_output_sections
 					target,
 					os);
 	  break;
+	case lang_lib_statement_enum:
+	  break;
 	case lang_data_statement_enum:
 	  if (os == NULL)
 	    /* This should never happen.  */
@@ -5421,6 +5503,17 @@ print_group (lang_group_statement_type *s,
   fprintf (config.map_file, "END GROUP\n");
 }
 
+/* Print a lib statement.  */
+
+static void
+print_lib (lang_lib_statement_type *s,
+	   lang_output_section_statement_type *os)
+{
+  fprintf (config.map_file, "START LIB\n");
+  print_statement_list (s->children.head, os);
+  fprintf (config.map_file, "END LIB\n");
+}
+
 /* Print the list of statements in S.
    This can be called for any statement type.  */
 
@@ -5503,6 +5596,9 @@ print_statement (lang_statement_union_type *s,
     case lang_group_statement_enum:
       print_group (&s->group_statement, os);
       break;
+    case lang_lib_statement_enum:
+      print_lib (&s->lib_statement, os);
+      break;
     case lang_insert_statement_enum:
       minfo ("INSERT %s %s\n",
 	     s->insert_statement.is_before ? "BEFORE" : "AFTER",
@@ -6515,6 +6611,9 @@ lang_size_sections_1
 	case lang_address_statement_enum:
 	  break;
 
+	case lang_lib_statement_enum:
+	  break;
+
 	default:
 	  FAIL ();
 	  break;
@@ -6910,6 +7009,9 @@ lang_do_assignments_1 (lang_statement_union_type *s,
 	case lang_address_statement_enum:
 	  break;
 
+	case lang_lib_statement_enum:
+	  break;
+
 	default:
 	  FAIL ();
 	  break;
@@ -9200,6 +9302,35 @@ lang_leave_group (void)
   pop_stat_ptr ();
 }
 
+/* Enter an artificial library.  This creates a new lang_lib_statement,
+   and sets stat_ptr to build new statements within the library.  */
+
+void
+lang_enter_lib (void)
+{
+  lang_lib_statement_type *l;
+
+  ASSERT (!input_flags.fake_archive);
+  l = new_stat (lang_lib_statement, stat_ptr);
+  lang_list_init (&l->children);
+  push_stat_ptr (&l->children);
+  input_flags.fake_archive = true;
+}
+
+/* Leave an artificial library.  This just resets stat_ptr to start
+   writing to the regular list of statements again.  We only support
+   a single level of artificial libraries (i.e. they can't be nested
+   in one another) and they need to be wholly contained in any group,
+   so there's no issue with getting stat_ptr messed up.  */
+
+void
+lang_leave_lib (void)
+{
+  ASSERT (input_flags.fake_archive);
+  input_flags.fake_archive = false;
+  pop_stat_ptr ();
+}
+
 /* Add a new program header.  This is called for each entry in a PHDRS
    command in a linker script.  */
 
diff --git a/ld/ldlang.h b/ld/ldlang.h
index 70b91941817..2f0bae2beab 100644
--- a/ld/ldlang.h
+++ b/ld/ldlang.h
@@ -31,6 +31,9 @@ typedef enum
   lang_input_file_is_symbols_only_enum,
   lang_input_file_is_marker_enum,
   lang_input_file_is_fake_enum,
+  lang_input_file_is_fake_archive_enum,
+  lang_input_file_is_search_member_enum,
+  lang_input_file_is_member_enum,
   lang_input_file_is_search_file_enum,
   lang_input_file_is_file_enum
 } lang_input_file_enum_type;
@@ -79,6 +82,7 @@ enum statement_enum
   lang_input_matcher_enum,
   lang_input_statement_enum,
   lang_insert_statement_enum,
+  lang_lib_statement_enum,
   lang_output_section_statement_enum,
   lang_output_statement_enum,
   lang_padding_statement_enum,
@@ -279,9 +283,15 @@ struct lang_input_statement_flags
   /* Whether to accept archives without a symbol map.  */
   unsigned int link_mapless : 1;
 
+  /* Whether we're building an artificial archive.  */
+  unsigned int fake_archive : 1;
+
   /* Set when bfd opening is successful.  */
   unsigned int loaded : 1;
 
+  /* Set for member entries of an artificial archive.  */
+  unsigned int member : 1;
+
   unsigned int real : 1;
 
   /* Set if the file does not exist.  */
@@ -439,6 +449,12 @@ typedef struct
   bool is_before;
 } lang_insert_statement_type;
 
+typedef struct
+{
+  lang_statement_header_type header;
+  lang_statement_list_type children;
+} lang_lib_statement_type;
+
 typedef union lang_statement_union
 {
   lang_statement_header_type header;
@@ -451,6 +467,7 @@ typedef union lang_statement_union
   lang_input_matcher_type input_matcher;
   lang_input_statement_type input_statement;
   lang_insert_statement_type insert_statement;
+  lang_lib_statement_type lib_statement;
   lang_output_section_statement_type output_section_statement;
   lang_output_statement_type output_statement;
   lang_padding_statement_type padding_statement;
@@ -680,6 +697,10 @@ extern void lang_enter_group
   (void);
 extern void lang_leave_group
   (void);
+extern void lang_enter_lib
+  (void);
+extern void lang_leave_lib
+  (void);
 extern void lang_add_section
   (lang_statement_list_type *, asection *, struct wildcard_list *,
    struct flag_info *, lang_output_section_statement_type *);
diff --git a/ld/ldlex.h b/ld/ldlex.h
index 86718087ed5..958ec3fbf81 100644
--- a/ld/ldlex.h
+++ b/ld/ldlex.h
@@ -53,6 +53,7 @@ enum option_values
   OPTION_NON_SHARED,
   OPTION_NO_WHOLE_ARCHIVE,
   OPTION_NO_LINK_MAPLESS,
+  OPTION_END_LIB,
   OPTION_OFORMAT,
   OPTION_RELAX,
   OPTION_NO_RELAX,
@@ -100,6 +101,7 @@ enum option_values
   OPTION_SPLIT_BY_FILE ,
   OPTION_WHOLE_ARCHIVE,
   OPTION_LINK_MAPLESS,
+  OPTION_START_LIB,
   OPTION_ADD_DT_NEEDED_FOR_DYNAMIC,
   OPTION_NO_ADD_DT_NEEDED_FOR_DYNAMIC,
   OPTION_ADD_DT_NEEDED_FOR_REGULAR,
diff --git a/ld/ldlex.l b/ld/ldlex.l
index 83e0b8e58f0..ad4b9e3e420 100644
--- a/ld/ldlex.l
+++ b/ld/ldlex.l
@@ -289,6 +289,7 @@ V_IDENTIFIER [*?.$_a-zA-Z\[\]\-\!\^\\]([*?.$_a-zA-Z0-9\[\]\-\!\^\\]|::)*
 <SCRIPT>"OUTPUT"			{ RTOKEN(OUTPUT); }
 <SCRIPT>"INPUT"				{ RTOKEN(INPUT); }
 <SCRIPT>"GROUP"				{ RTOKEN(GROUP); }
+<SCRIPT>"LIB"				{ RTOKEN(LIB); }
 <INPUTLIST>"AS_NEEDED"			{ RTOKEN(AS_NEEDED); }
 <SCRIPT,EXPRESSION>"DEFINED"		{ RTOKEN(DEFINED); }
 <WILD>"CREATE_OBJECT_SYMBOLS"		{ RTOKEN(CREATE_OBJECT_SYMBOLS); }
diff --git a/ld/ldmisc.c b/ld/ldmisc.c
index f39e0a3dbe7..31ca64b4ba0 100644
--- a/ld/ldmisc.c
+++ b/ld/ldmisc.c
@@ -482,7 +482,14 @@ vfinfo (FILE *fp, const char *fmt, va_list ap, bool is_warning)
 			     bfd_get_filename (abfd->my_archive),
 			     bfd_get_filename (abfd));
 		  else
-		    fprintf (fp, "%s", bfd_get_filename (abfd));
+		    {
+		      const char *filename = bfd_get_filename (abfd);
+
+		      if (filename == NULL)
+			fprintf (fp, "<no file> (%s generated)", program_name);
+		      else
+			fprintf (fp, "%s", filename);
+		    }
 		}
 	      else if (*fmt == 'I')
 		{
@@ -498,6 +505,8 @@ vfinfo (FILE *fp, const char *fmt, va_list ap, bool is_warning)
 		    fprintf (fp, "(%s)%s",
 			     bfd_get_filename (i->the_bfd->my_archive),
 			     i->local_sym_name);
+		  else if (i->filename == NULL)
+		    fprintf (fp, "<no file> (%s generated)", program_name);
 		  else
 		    fprintf (fp, "%s", i->filename);
 		}
diff --git a/ld/lexsup.c b/ld/lexsup.c
index d93e8b4ca77..4e89feffb09 100644
--- a/ld/lexsup.c
+++ b/ld/lexsup.c
@@ -423,6 +423,8 @@ static const struct ld_option ld_options[] =
     '\0', NULL, N_("Turn off --whole-archive"), TWO_DASHES },
   { {"no-link-mapless", no_argument, NULL, OPTION_NO_LINK_MAPLESS},
     '\0', NULL, N_("Reject archives without a symbol map"), TWO_DASHES },
+  { {"end-lib", no_argument, NULL, OPTION_END_LIB},
+    '\0', NULL, N_("Treat files specified as link objects"), TWO_DASHES },
   { {"noinhibit-exec", no_argument, NULL, OPTION_NOINHIBIT_EXEC},
     '\0', NULL, N_("Create an output file even if errors occur"),
     TWO_DASHES },
@@ -607,6 +609,9 @@ static const struct ld_option ld_options[] =
   { {"link-mapless", no_argument, NULL, OPTION_LINK_MAPLESS},
     '\0', NULL, N_("Accept archives without a symbol map"),
     TWO_DASHES },
+  { {"start-lib", no_argument, NULL, OPTION_START_LIB},
+    '\0', NULL, N_("Treat files specified as artificial archive members"),
+    TWO_DASHES },
   { {"wrap", required_argument, NULL, OPTION_WRAP},
     '\0', N_("SYMBOL"), N_("Use wrapper functions for SYMBOL"), TWO_DASHES },
   { {"ignore-unresolved-symbol", required_argument, NULL,
@@ -853,7 +858,11 @@ parse_args (unsigned argc, char **argv)
 	  break;
 
 	case 1:			/* File name.  */
-	  lang_add_input_file (optarg, lang_input_file_is_file_enum, NULL);
+	  lang_add_input_file (optarg,
+			       (input_flags.fake_archive
+				? lang_input_file_is_member_enum
+				: lang_input_file_is_file_enum),
+			       NULL);
 	  break;
 
 	case OPTION_IGNORE:
@@ -1180,11 +1189,19 @@ parse_args (unsigned argc, char **argv)
 	  config.only_cmd_line_lib_dirs = true;
 	  break;
 	case OPTION_NO_WHOLE_ARCHIVE:
+	  if (input_flags.fake_archive)
+	    fatal (_("%P: --no-whole-archive"
+		     " not allowed between --start-lib and --end-lib\n"));
 	  input_flags.whole_archive = false;
 	  break;
 	case OPTION_NO_LINK_MAPLESS:
 	  input_flags.link_mapless = false;
 	  break;
+	case OPTION_END_LIB:
+	  if (!input_flags.fake_archive)
+	    fatal (_("%P: --end-lib without --start-lib\n"));
+	  lang_leave_lib ();
+	  break;
 	case 'O':
 	  /* FIXME "-O<non-digits> <value>" used to set the address of
 	     section <non-digits>.  Was this for compatibility with
@@ -1663,11 +1680,19 @@ parse_args (unsigned argc, char **argv)
 	  link_info.warn_alternate_em = true;
 	  break;
 	case OPTION_WHOLE_ARCHIVE:
+	  if (input_flags.fake_archive)
+	    fatal (_("%P: --whole-archive"
+		     " not allowed between --start-lib and --end-lib\n"));
 	  input_flags.whole_archive = true;
 	  break;
 	case OPTION_LINK_MAPLESS:
 	  input_flags.link_mapless = true;
 	  break;
+	case OPTION_START_LIB:
+	  if (input_flags.fake_archive)
+	    fatal (_("%P: nested --start-lib not allowed\n"));
+	  lang_enter_lib ();
+	  break;
 	case OPTION_ADD_DT_NEEDED_FOR_DYNAMIC:
 	  input_flags.add_DT_NEEDED_for_dynamic = true;
 	  break;
@@ -1732,10 +1757,16 @@ parse_args (unsigned argc, char **argv)
 	  command_line.accept_unknown_input_arch = false;
 	  break;
 	case '(':
+	  if (input_flags.fake_archive)
+	    fatal (_("%P: -( or --start-group"
+		     " not allowed between --start-lib and --end-lib\n"));
 	  lang_enter_group ();
 	  ingroup++;
 	  break;
 	case ')':
+	  if (input_flags.fake_archive)
+	    fatal (_("%P: -) or --end-group"
+		     " not allowed between --start-lib and --end-lib\n"));
 	  if (! ingroup)
 	    fatal (_("%P: group ended before it began (--help for usage)\n"));
 
@@ -1809,6 +1840,12 @@ parse_args (unsigned argc, char **argv)
 	case OPTION_POP_STATE:
 	  if (input_flags.pushed == NULL)
 	    fatal (_("%P: no state pushed before popping\n"));
+	  else if (input_flags.fake_archive
+		   && (input_flags.pushed->whole_archive
+		       != input_flags.whole_archive))
+	    fatal
+	      (_("%P: --pop-state not allowed to change --whole-archive"
+		 " setting between --start-lib and --end-lib\n"));
 	  else
 	    {
 	      struct lang_input_statement_flags *oldp = input_flags.pushed;
diff --git a/ld/testsuite/ld-archive/ab.t b/ld/testsuite/ld-archive/ab.t
new file mode 100644
index 00000000000..fda63975406
--- /dev/null
+++ b/ld/testsuite/ld-archive/ab.t
@@ -0,0 +1 @@
+LIB(tmpdir/a.o tmpdir/b.o tmpdir/x.o)
diff --git a/ld/testsuite/ld-archive/abfc.vd b/ld/testsuite/ld-archive/abfc.vd
new file mode 100644
index 00000000000..0036bbd1549
--- /dev/null
+++ b/ld/testsuite/ld-archive/abfc.vd
@@ -0,0 +1,28 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/abc\.o succeeded
+tmpdir/abc\.o
+attempt to open tmpdir/a\.o succeeded
+attempt to open tmpdir/b\.o succeeded
+attempt to open tmpdir/x\.o succeeded
+<no file> \(.* generated\)
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/a\.o +tmpdir/abc\.o \(aa\)
+tmpdir/a\.o
+tmpdir/b\.o +tmpdir/abc\.o \(bb\)
+tmpdir/b\.o
+!(:?\()?tmpdir/.*
+#...
+LOAD tmpdir/abc\.o
+START LIB
+LOAD tmpdir/a\.o
+LOAD tmpdir/b\.o
+LOAD tmpdir/x\.o
+END LIB
+!LOAD .*
+#pass
diff --git a/ld/testsuite/ld-archive/abfcfg.vd b/ld/testsuite/ld-archive/abfcfg.vd
new file mode 100644
index 00000000000..a5a91be3d9a
--- /dev/null
+++ b/ld/testsuite/ld-archive/abfcfg.vd
@@ -0,0 +1,36 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/a\.o succeeded
+attempt to open tmpdir/b\.o succeeded
+attempt to open tmpdir/x\.o succeeded
+<no file> \(.* generated\)
+attempt to open tmpdir/abc\.o succeeded
+<no file> \(.* generated\)
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/abc\.o +\(ff\)
+tmpdir/abc\.o
+<no file> \(.* generated\)
+tmpdir/a\.o +tmpdir/abc\.o \(aa\)
+tmpdir/a\.o
+tmpdir/b\.o +tmpdir/abc\.o \(bb\)
+tmpdir/b\.o
+<no file> \(.* generated\)
+!(:?\()?tmpdir/.*
+#...
+START GROUP
+START LIB
+LOAD tmpdir/a\.o
+LOAD tmpdir/b\.o
+LOAD tmpdir/x\.o
+END LIB
+START LIB
+LOAD tmpdir/abc\.o
+END LIB
+END GROUP
+!LOAD .*
+#pass
diff --git a/ld/testsuite/ld-archive/absc.vd b/ld/testsuite/ld-archive/absc.vd
new file mode 100644
index 00000000000..c48f8f3652c
--- /dev/null
+++ b/ld/testsuite/ld-archive/absc.vd
@@ -0,0 +1,35 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/abc\.o succeeded
+tmpdir/abc\.o
+attempt to open .*/ld/testsuite/ld-archive/ab\.t succeeded
+.*/ld/testsuite/ld-archive/ab\.t
+opened script file .*/ld/testsuite/ld-archive/ab\.t
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/a\.o failed
+attempt to open tmpdir/a\.o succeeded
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/b\.o failed
+attempt to open tmpdir/b\.o succeeded
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/x\.o failed
+attempt to open tmpdir/x\.o succeeded
+<no file> \(.* generated\)
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/a\.o +tmpdir/abc\.o \(aa\)
+tmpdir/a\.o
+tmpdir/b\.o +tmpdir/abc\.o \(bb\)
+tmpdir/b\.o
+!(:?\()?tmpdir/.*
+#...
+LOAD tmpdir/abc\.o
+LOAD .*/ld/testsuite/ld-archive/ab\.t
+START LIB
+LOAD tmpdir/a\.o
+LOAD tmpdir/b\.o
+LOAD tmpdir/x\.o
+END LIB
+!LOAD .*
+#pass
diff --git a/ld/testsuite/ld-archive/abscfg.vd b/ld/testsuite/ld-archive/abscfg.vd
new file mode 100644
index 00000000000..8751f390216
--- /dev/null
+++ b/ld/testsuite/ld-archive/abscfg.vd
@@ -0,0 +1,43 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open .*/ld/testsuite/ld-archive/ab\.t succeeded
+.*/ld/testsuite/ld-archive/ab\.t
+opened script file .*/ld/testsuite/ld-archive/ab\.t
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/a\.o failed
+attempt to open tmpdir/a\.o succeeded
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/b\.o failed
+attempt to open tmpdir/b\.o succeeded
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/x\.o failed
+attempt to open tmpdir/x\.o succeeded
+<no file> \(.* generated\)
+attempt to open tmpdir/abc\.o succeeded
+<no file> \(.* generated\)
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/abc\.o +\(ff\)
+tmpdir/abc\.o
+<no file> \(.* generated\)
+tmpdir/a\.o +tmpdir/abc\.o \(aa\)
+tmpdir/a\.o
+tmpdir/b\.o +tmpdir/abc\.o \(bb\)
+tmpdir/b\.o
+<no file> \(.* generated\)
+!(:?\()?tmpdir/.*
+#...
+START GROUP
+LOAD .*/ld/testsuite/ld-archive/ab\.t
+START LIB
+LOAD tmpdir/a\.o
+LOAD tmpdir/b\.o
+LOAD tmpdir/x\.o
+END LIB
+START LIB
+LOAD tmpdir/abc\.o
+END LIB
+END GROUP
+!LOAD .*
+#pass
diff --git a/ld/testsuite/ld-archive/abwfc.vd b/ld/testsuite/ld-archive/abwfc.vd
new file mode 100644
index 00000000000..16f12ab4af8
--- /dev/null
+++ b/ld/testsuite/ld-archive/abwfc.vd
@@ -0,0 +1,30 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/abc\.o succeeded
+tmpdir/abc\.o
+attempt to open tmpdir/a\.o succeeded
+attempt to open tmpdir/b\.o succeeded
+attempt to open tmpdir/x\.o succeeded
+<no file> \(.* generated\)
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/a\.o +\(--whole-archive\)
+tmpdir/a\.o
+tmpdir/b\.o +\(--whole-archive\)
+tmpdir/b\.o
+tmpdir/x\.o +\(--whole-archive\)
+tmpdir/x\.o
+!(:?\()?tmpdir/.*
+#...
+LOAD tmpdir/abc\.o
+START LIB
+LOAD tmpdir/a\.o
+LOAD tmpdir/b\.o
+LOAD tmpdir/x\.o
+END LIB
+!LOAD .*
+#pass
diff --git a/ld/testsuite/ld-archive/abwsc.vd b/ld/testsuite/ld-archive/abwsc.vd
new file mode 100644
index 00000000000..8c36e694cfa
--- /dev/null
+++ b/ld/testsuite/ld-archive/abwsc.vd
@@ -0,0 +1,37 @@
+#...
+using ..ternal linker script:.*
+==================================================
+#...
+==================================================
+.*: mode .*
+attempt to open tmpdir/abc\.o succeeded
+tmpdir/abc\.o
+attempt to open .*/ld/testsuite/ld-archive/ab\.t succeeded
+.*/ld/testsuite/ld-archive/ab\.t
+opened script file .*/ld/testsuite/ld-archive/ab\.t
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/a\.o failed
+attempt to open tmpdir/a\.o succeeded
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/b\.o failed
+attempt to open tmpdir/b\.o succeeded
+attempt to open .*/ld/testsuite/ld-archive/tmpdir/x\.o failed
+attempt to open tmpdir/x\.o succeeded
+<no file> \(.* generated\)
+Archive member included to satisfy reference by file \(symbol\)
+
+tmpdir/a\.o +\(--whole-archive\)
+tmpdir/a\.o
+tmpdir/b\.o +\(--whole-archive\)
+tmpdir/b\.o
+tmpdir/x\.o +\(--whole-archive\)
+tmpdir/x\.o
+!(:?\()?tmpdir/.*
+#...
+LOAD tmpdir/abc\.o
+LOAD .*/ld/testsuite/ld-archive/ab\.t
+START LIB
+LOAD tmpdir/a\.o
+LOAD tmpdir/b\.o
+LOAD tmpdir/x\.o
+END LIB
+!LOAD .*
+#pass
diff --git a/ld/testsuite/ld-archive/archive.exp b/ld/testsuite/ld-archive/archive.exp
index f77c449a4f1..0815064eccb 100644
--- a/ld/testsuite/ld-archive/archive.exp
+++ b/ld/testsuite/ld-archive/archive.exp
@@ -86,6 +86,23 @@ run_ld_link_tests [list \
 				  {nm "" nx.nd}}] \
 	"abtc" \
     ] \
+    [list "Fake archive link" \
+	"-e ff" "--start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+		 --verbose --print-map" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{ld abfc.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abfc" \
+    ] \
+    [list "Script fake archive link" \
+	"-e ff" "$srcdir/$subdir/ab.t --verbose --print-map" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{ld absc.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"absc" \
+    ] \
 ]
 
 # Single whole archive tests.
@@ -113,6 +130,24 @@ run_ld_link_tests [list \
 				  {nm "" x.nd}}] \
 	"abwtc" \
     ] \
+    [list "Fake whole archive link" \
+	"-e ff" "--whole-archive\
+		 --start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+		 --verbose --print-map" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{ld abwfc.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" x.nd}}] \
+	"abwfc" \
+    ] \
+    [list "Script fake whole archive link" \
+	"-e ff" "--whole-archive $srcdir/$subdir/ab.t --verbose --print-map" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{ld abwsc.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" x.nd}}] \
+	"abwsc" \
+    ] \
 ]
 
 # Group archive tests.
@@ -129,6 +164,19 @@ run_ld_link_tests [list \
 	{{ld abcn.ed}} \
 	"abtctn" \
     ] \
+    [list "Fake non-group archive link reject" \
+	"-e ff --start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+	 --start-lib" "--end-lib" \
+	"" {abc.s} \
+	{{ld abcn.ed}} \
+	"abfcfn" \
+    ] \
+    [list "Script fake non-group archive link reject" \
+	"-e ff $srcdir/$subdir/ab.t --start-lib" "--end-lib" \
+	"" {abc.s} \
+	{{ld abcn.ed}} \
+	"abscfn" \
+    ] \
     [list "Regular group archive link accept" \
 	"-e ff -\\( tmpdir/ab.a" "tmpdir/abc.a -\\) --verbose --print-map" \
 	"" {} \
@@ -154,6 +202,24 @@ run_ld_link_tests [list \
 				  {nm "" nx.nd}}] \
 	"abtctg" \
     ] \
+    [list "Fake group archive link accept" \
+	"-e ff -\\( --start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+	 --start-lib" "--end-lib -\\) --verbose --print-map" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{ld abfcfg.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abfcfg" \
+    ] \
+    [list "Script fake group archive link accept" \
+	"-e ff -\\( $srcdir/$subdir/ab.t\
+	 --start-lib" "--end-lib -\\) --verbose --print-map" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{ld abscfg.vd} \
+				  {nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abscfg" \
+    ] \
 ]
 
 # Dual archive tests.
@@ -179,6 +245,23 @@ run_ld_link_tests [list \
 				  {nm "" ny.nd}}] \
 	"abcdte" \
     ] \
+    [list "Regular archive plus fake link" \
+	"-e ff" "tmpdir/ab.a\
+		 --start-lib tmpdir/c.o tmpdir/d.o tmpdir/y.o --end-lib" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abcdfe" \
+    ] \
+    [list "Regular archive plus script fake link" \
+	"-e ff" "tmpdir/ab.a $srcdir/$subdir/cd.t" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abcdse" \
+    ] \
 ]
 setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
 setup_xfail "binutils/33485" "cris-*-*aout*" "i\[3-7\]86-*-bsd*" \
@@ -206,6 +289,81 @@ run_ld_link_tests [list \
 	"abtcdte" \
     ] \
 ]
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+setup_xfail "binutils/33485" "cris-*-*aout*" "i\[3-7\]86-*-bsd*" \
+    "i\[3-7\]86-*-msdos*" "ns32k-*-*" "pdp11-*-*"
+run_ld_link_tests [list \
+    [list "Thin archive plus fake link" \
+	"-e ff" "tmpdir/abt.a\
+		 --start-lib tmpdir/c.o tmpdir/d.o tmpdir/y.o --end-lib" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abtcdfe" \
+    ] \
+]
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+setup_xfail "binutils/33485" "cris-*-*aout*" "i\[3-7\]86-*-bsd*" \
+    "i\[3-7\]86-*-msdos*" "ns32k-*-*" "pdp11-*-*"
+run_ld_link_tests [list \
+    [list "Thin archive plus script fake link" \
+	"-e ff" "tmpdir/abt.a $srcdir/$subdir/cd.t" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abtcdse" \
+    ] \
+    [list "Fake archive plus regular link" \
+	"-e ff" "--start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+		 tmpdir/cd.a" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abfcde" \
+    ] \
+    [list "Script fake archive plus regular link" \
+	"-e ff" "$srcdir/$subdir/ab.t tmpdir/cd.a" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abscde" \
+    ] \
+]
+setup_xfail "binutils/33484" "alpha*-*-linux*ecoff*" "alpha*-*-osf*"
+setup_xfail "binutils/33485" "cris-*-*aout*" "i\[3-7\]86-*-bsd*" \
+    "i\[3-7\]86-*-msdos*" "ns32k-*-*" "pdp11-*-*"
+run_ld_link_tests [list \
+    [list "Fake archive plus thin link" \
+	"-e ff" "--start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+		 tmpdir/cdt.a" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abfcdte" \
+    ] \
+    [list "Fake archive plus fake link" \
+	"-e ff" "--start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+		 --start-lib tmpdir/c.o tmpdir/d.o tmpdir/y.o --end-lib" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abfcdfe" \
+    ] \
+    [list "Script fake archive plus script fake link" \
+	"-e ff" "$srcdir/$subdir/ab.t $srcdir/$subdir/cd.t" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" ny.nd}}] \
+	"abscdse" \
+    ] \
+]
 
 # Dual whole archive tests.
 run_ld_link_tests [list \
@@ -228,6 +386,16 @@ run_ld_link_tests [list \
 				  {nm "" y.nd}}] \
 	"abcdwte" \
     ] \
+    [list "Regular archive plus whole fake link" \
+	"-e ff" "tmpdir/ab.a\
+		 --whole-archive\
+		 --start-lib tmpdir/c.o tmpdir/d.o tmpdir/y.o --end-lib" \
+	"" {abcde.s} \
+	[ld_archive_filter_tests {{nm "" abcde.nd} \
+				  {nm "" nx.nd} \
+				  {nm "" y.nd}}] \
+	"abcdwfe" \
+    ] \
 ]
 
 # Single non-index archive accept tests.
@@ -249,6 +417,21 @@ run_ld_link_tests [list \
 				  {nm "" nx.nd}}] \
 	"abntc" \
     ] \
+    [list "Fake archive with --no-link-mapless link accept" \
+	"-e ff" "--no-link-mapless\
+		 --start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abnfc" \
+    ] \
+    [list "Script fake archive with --no-link-mapless link accept" \
+	"-e ff" "--no-link-mapless $srcdir/$subdir/ab.t" \
+	"" {abc.s} \
+	[ld_archive_filter_tests {{nm "" abc.nd} \
+				  {nm "" nx.nd}}] \
+	"abnsc" \
+    ] \
 ]
 
 # Dual non-index archive accept tests.
diff --git a/ld/testsuite/ld-archive/cd.t b/ld/testsuite/ld-archive/cd.t
new file mode 100644
index 00000000000..969d3a333ec
--- /dev/null
+++ b/ld/testsuite/ld-archive/cd.t
@@ -0,0 +1 @@
+LIB(tmpdir/c.o tmpdir/d.o tmpdir/y.o)
diff --git a/ld/testsuite/ld-cris/archive.exp b/ld/testsuite/ld-cris/archive.exp
index 4a9d49adf44..45df6a6d39a 100644
--- a/ld/testsuite/ld-cris/archive.exp
+++ b/ld/testsuite/ld-cris/archive.exp
@@ -90,6 +90,24 @@ run_ld_link_tests [list \
 	 {nm "" ../ld-archive/nx.nd}} \
 	"abci" \
     ] \
+    [list "CRIS fake archive link (explicit emulation)" \
+	"$ldemul -e ff" "--start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o\
+			 --end-lib --verbose --print-map" \
+	"$asemul" {../ld-archive/abc.s} \
+	{{ld ../ld-archive/abfc.vd} \
+	 {nm "" ../ld-archive/abc.nd} \
+	 {nm "" ../ld-archive/nx.nd}} \
+	"abfcx" \
+    ] \
+    [list "CRIS fake archive link (implicit emulation)" \
+	"-e ff" "--start-lib tmpdir/a.o tmpdir/b.o tmpdir/x.o --end-lib\
+		 --verbose --print-map" \
+	"$asemul" {../ld-archive/abc.s} \
+	{{ld ../ld-archive/abfc.vd} \
+	 {nm "" ../ld-archive/abc.nd} \
+	 {nm "" ../ld-archive/nx.nd}} \
+	"abfci" \
+    ] \
 ]
 # PR binutils/33485 hits with the aout archive format and with this
 # test that format is used with non-aout targets, hence the reverse


More information about the Binutils-cvs mailing list