[1/2] Speedup part #1

Message ID 58501622.uTH28uq5Ug@polaris
State New
Headers show
Series
  • Speed up direct linking with DLLs on Windows
Related show

Commit Message

Eric Botcazou March 13, 2018, 5:57 p.m.
The first patch deals with the auto-import feature.  There are 2 versions of 
this feature: the original one, which was piggybacked on the OS loader with an 
optional help from the runtime (--enable-auto-import --enable-runtime-pseudo-
reloc-v1) and is still the one mostly documented in the sources and manual; 
the enhanced one by Kai Tietz, which is entirely piggybacked on the runtime 
(--enable-auto-import --enable-runtime-pseudo-reloc-v2) and is the default for 
Mingw and Cygwin nowadays.

The implementation is quite inefficient because of pe[p]_find_data_imports: 
for every undefined symbol, the function walks the entire set of relocations 
for all the input files and does a direct name comparison for each of them.

This is easily fixable by using a hash-based map for v1 and a simple hash 
table for v2.  The attached patch leaves v1 alone and thus only changes v2.
It also factors out pe[p]_find_data_imports into a common function, removes 
old cruft left and right, and attempts to better separate the implementations 
of v1 and v2 in the code.


2018-03-13  Eric Botcazou  <ebotcazou@adacore.com>

ld/
        * emultempl/pe.em (U_SIZE): Delete.
        (pe_data_import_dll): Likewise.
        (make_import_fixup): Return void, take 4th parameter and pass it down
        in call to pe_create_import_fixup.
        (pe_find_data_imports): Move to...
        (gld_${EMULATION_NAME}_after_open): Run the stdcall fixup pass after
        the auto-import pass and add a guard before running the latter.
        * emultempl/pep.em (U_SIZE): Delete.
        (pep_data_import_dll): Likewise.
        (make_import_fixup): Return void, take 4th parameter and pass it down
        in call to pe_create_import_fixup.
        (pep_find_data_imports): Move to...
        (gld_${EMULATION_NAME}_after_open): Run the stdcall fixup pass after
        the auto-import pass and add a guard before running the latter.
        * pe-dll.c (runtime_pseudp_reloc_v2_init): Change type to bfd_boolean.
        (pe_walk_relocs_of_symbol): Rename into...
        (pe_walk_relocs): ...this.  Add 2 more parameters,4th parameter to the
        callback prototype and pass 4th parameter in calls to the callback.
        If the import hash table is present, invoke the callback on the reloc
        if the symbol name is in the table.
        (pe_find_data_imports): ...here.  Take 2 parameters.  Build an import
        hash table for the pseudo-relocation support version 2.  When it is
        built, walk the relocations only once at the end; when it is not, do
        not build a fixup when the symbol isn't part of an import table.
        Issue the associated warning only after a first fixup is built.
        (tmp_seq2): Delete.
        (make_singleton_name_imp): Likewise.
        (make_import_fixup_mark): Return const char * and a stable string.
        (make_import_fixup_entry): Do not deal with the pseudo-relocation
        support version 2.
        (make_runtime_pseudo_reloc): Factor out code and fix formatting.
        (pe_create_import_fixup): Add 5th parameter.  Clearly separate the
        pseudo-relocation support version 2 from the rest.  Fix formatting.
        * pe-dll.h (pe_walk_relocs_of_symbol): Delete.
        (pe_find_data_imports): Declare.
        (pe_create_import_fixup): Add 5th parameter.
        * pep-dll.c (pe_data_import_dll): Delete.
        (pe_find_data_imports): Define.
        (pe_walk_relocs_of_symbol): Delete.
        * pep-dll.h (pep_walk_relocs_of_symbol): Delete.
        (pep_find_data_imports): Declare.
        (pep_create_import_fixup): Add 5th parameter.
        * ld.texinfo (--enable-auto-import): Adjust to new implementation.


---
 ld/emultempl/pe.em  | 143 ++--------------------
 ld/emultempl/pep.em | 121 ++-----------------
 ld/ld.texinfo       |  19 +--
 ld/pe-dll.c         | 339 ++++++++++++++++++++++++++++++++-------------------
 ld/pe-dll.h         |   6 +-
 ld/pep-dll.c        |   5 +-
 ld/pep-dll.h        |   7 +-
 7 files changed, 252 insertions(+), 388 deletions(-)

-- 
Eric Botcazou

Comments

Nick Clifton March 19, 2018, 5:34 p.m. | #1
Hi Eric

  Thanks for producing these improvments.

> 2018-03-13  Eric Botcazou  <ebotcazou@adacore.com>

> 

> ld/

>         * emultempl/pe.em (U_SIZE): Delete.

>         (pe_data_import_dll): Likewise.

>         (make_import_fixup): Return void, take 4th parameter and pass it down

>         in call to pe_create_import_fixup.

>         (pe_find_data_imports): Move to...

>         (gld_${EMULATION_NAME}_after_open): Run the stdcall fixup pass after

>         the auto-import pass and add a guard before running the latter.

>         * emultempl/pep.em (U_SIZE): Delete.

>         (pep_data_import_dll): Likewise.

>         (make_import_fixup): Return void, take 4th parameter and pass it down

>         in call to pe_create_import_fixup.

>         (pep_find_data_imports): Move to...

>         (gld_${EMULATION_NAME}_after_open): Run the stdcall fixup pass after

>         the auto-import pass and add a guard before running the latter.

>         * pe-dll.c (runtime_pseudp_reloc_v2_init): Change type to bfd_boolean.

>         (pe_walk_relocs_of_symbol): Rename into...

>         (pe_walk_relocs): ...this.  Add 2 more parameters,4th parameter to the

>         callback prototype and pass 4th parameter in calls to the callback.

>         If the import hash table is present, invoke the callback on the reloc

>         if the symbol name is in the table.

>         (pe_find_data_imports): ...here.  Take 2 parameters.  Build an import

>         hash table for the pseudo-relocation support version 2.  When it is

>         built, walk the relocations only once at the end; when it is not, do

>         not build a fixup when the symbol isn't part of an import table.

>         Issue the associated warning only after a first fixup is built.

>         (tmp_seq2): Delete.

>         (make_singleton_name_imp): Likewise.

>         (make_import_fixup_mark): Return const char * and a stable string.

>         (make_import_fixup_entry): Do not deal with the pseudo-relocation

>         support version 2.

>         (make_runtime_pseudo_reloc): Factor out code and fix formatting.

>         (pe_create_import_fixup): Add 5th parameter.  Clearly separate the

>         pseudo-relocation support version 2 from the rest.  Fix formatting.

>         * pe-dll.h (pe_walk_relocs_of_symbol): Delete.

>         (pe_find_data_imports): Declare.

>         (pe_create_import_fixup): Add 5th parameter.

>         * pep-dll.c (pe_data_import_dll): Delete.

>         (pe_find_data_imports): Define.

>         (pe_walk_relocs_of_symbol): Delete.

>         * pep-dll.h (pep_walk_relocs_of_symbol): Delete.

>         (pep_find_data_imports): Declare.

>         (pep_create_import_fixup): Add 5th parameter.>         * ld.texinfo (--enable-auto-import): Adjust to new implementation.


Approved - please apply - but please could you also add a line to the ld/NEWS file mentioning this new feature.

Cheers
  Nick

Patch

commit fe0b8cbe90d4202aaffeea6bcac688f179b9a47b
Author: Eric Botcazou <ebotcazou@gcc.gnu.org>
Date:   Mon Mar 5 12:14:19 2018 +0100

    Speedup part #1.

diff --git a/ld/emultempl/pe.em b/ld/emultempl/pe.em
index ffa8b74b20..7e96911296 100644
--- a/ld/emultempl/pe.em
+++ b/ld/emultempl/pe.em
@@ -385,11 +385,6 @@  typedef struct
 #define U(CSTR) \
   ((is_underscoring () == 0) ? CSTR : "_" CSTR)
 
-/* Get size of constant string for a possible underscore prefixed
-   C visible symbol.  */
-#define U_SIZE(CSTR) \
-  (sizeof (CSTR) + (is_underscoring () == 0 ? 0 : 1))
-
 #define D(field,symbol,def,usc)  {&pe.field, sizeof (pe.field), def, symbol, 0, usc}
 
 static definfo init[] =
@@ -1022,13 +1017,6 @@  gld_${EMULATION_NAME}_after_parse (void)
   after_parse_default ();
 }
 
-/* pe-dll.c directly accesses pe_data_import_dll,
-   so it must be defined outside of #ifdef DLL_SUPPORT.
-   Note - this variable is deliberately not initialised.
-   This allows it to be treated as a common varaible, and only
-   exist in one incarnation in a multiple target enabled linker.  */
-char * pe_data_import_dll;
-
 #ifdef DLL_SUPPORT
 static struct bfd_link_hash_entry *pe_undef_found_sym;
 
@@ -1129,11 +1117,12 @@  pe_fixup_stdcalls (void)
       }
 }
 
-static int
-make_import_fixup (arelent *rel, asection *s, char *name)
+static void
+make_import_fixup (arelent *rel, asection *s, char *name, const char *symname)
 {
   struct bfd_symbol *sym = *rel->sym_ptr_ptr;
   char addend[4];
+  bfd_vma _addend;
 
   if (pe_dll_extra_pe_debug)
     printf ("arelent: %s@%#lx: add=%li\n", sym->name,
@@ -1143,117 +1132,8 @@  make_import_fixup (arelent *rel, asection *s, char *name)
     einfo (_("%P: %C: cannot get section contents - auto-import exception\n"),
 	   s->owner, s, rel->address);
 
-  pe_create_import_fixup (rel, s, bfd_get_32 (s->owner, addend), name);
-
-  return 1;
-}
-
-static void
-pe_find_data_imports (void)
-{
-  struct bfd_link_hash_entry *undef, *sym;
-  size_t namelen;
-  char *buf, *name;
-
-  if (link_info.pei386_auto_import == 0)
-    return;
-
-  namelen = 0;
-  for (undef = link_info.hash->undefs; undef; undef = undef->u.undef.next)
-    {
-      if (undef->type == bfd_link_hash_undefined)
-	{
-	  size_t len = strlen (undef->root.string);
-	  if (namelen < len)
-	    namelen = len;
-	}
-    }
-  if (namelen == 0)
-    return;
-
-  /* We are being a bit cunning here.  The buffer will have space for
-     prefixes at the beginning.  The prefix is modified here and in a
-     number of functions called from this function.  */
-#define PREFIX_LEN 32
-  buf = xmalloc (PREFIX_LEN + namelen + 1);
-  name = buf + PREFIX_LEN;
-
-  for (undef = link_info.hash->undefs; undef; undef = undef->u.undef.next)
-    {
-      if (undef->type == bfd_link_hash_undefined)
-	{
-	  char *impname;
-
-	  if (pe_dll_extra_pe_debug)
-	    printf ("%s:%s\n", __FUNCTION__, undef->root.string);
-
-	  strcpy (name, undef->root.string);
-	  impname = name - (sizeof "__imp_" - 1);
-	  memcpy (impname, "__imp_", sizeof "__imp_" - 1);
-
-	  sym = bfd_link_hash_lookup (link_info.hash, impname, 0, 0, 1);
-
-	  if (sym && sym->type == bfd_link_hash_defined)
-	    {
-	      bfd *b = sym->u.def.section->owner;
-	      asymbol **symbols;
-	      int nsyms, i;
-
-	      if (link_info.pei386_auto_import == -1)
-		{
-		  static bfd_boolean warned = FALSE;
-
-		  info_msg (_("Info: resolving %s by linking to %s "
-			      "(auto-import)\n"), name, impname);
-
-		  /* PR linker/4844.  */
-		  if (! warned)
-		    {
-		      warned = TRUE;
-		      einfo (_("%P: warning: auto-importing has been activated "
-			       "without --enable-auto-import specified on the "
-			       "command line; this should work unless it "
-			       "involves constant data structures referencing "
-			       "symbols from auto-imported DLLs\n"));
-		    }
-		}
-
-	      if (!bfd_generic_link_read_symbols (b))
-		{
-		  einfo (_("%F%P: %pB: could not read symbols: %E\n"), b);
-		  return;
-		}
-
-	      symbols = bfd_get_outsymbols (b);
-	      nsyms = bfd_get_symcount (b);
-
-	      for (i = 0; i < nsyms; i++)
-		{
-		  if (! CONST_STRNEQ (symbols[i]->name, U ("_head_")))
-		    continue;
-
-		  if (pe_dll_extra_pe_debug)
-		    printf ("->%s\n", symbols[i]->name);
-
-		  pe_data_import_dll = (char *) (symbols[i]->name
-						 + U_SIZE ("_head_") - 1);
-		  break;
-		}
-
-	      pe_walk_relocs_of_symbol (&link_info, name, make_import_fixup);
-
-	      /* Let's differentiate it somehow from defined.  */
-	      undef->type = bfd_link_hash_defweak;
-	      /* We replace original name with __imp_ prefixed, this
-		 1) may trash memory 2) leads to duplicate symbol generation.
-		 Still, IMHO it's better than having name polluted.  */
-	      undef->root.string = sym->root.string;
-	      undef->u.def.value = sym->u.def.value;
-	      undef->u.def.section = sym->u.def.section;
-	    }
-	}
-    }
-  free (buf);
+  _addend = bfd_get_32 (s->owner, addend);
+  pe_create_import_fixup (rel, s, _addend, name, symname);
 }
 
 static bfd_boolean
@@ -1523,16 +1403,15 @@  gld_${EMULATION_NAME}_after_open (void)
   pe_output_file_set_long_section_names (link_info.output_bfd);
 
 #ifdef DLL_SUPPORT
-  if (pe_enable_stdcall_fixup) /* -1=warn or 1=disable */
-    pe_fixup_stdcalls ();
-
   pe_process_import_defs (link_info.output_bfd, &link_info);
 
-  pe_find_data_imports ();
+  if (link_info.pei386_auto_import) /* -1=warn or 1=enable */
+    pe_find_data_imports (U ("_head_"), make_import_fixup);
 
-  /* As possibly new symbols are added by imports, we rerun
-     stdcall/fastcall fixup here.  */
-  if (pe_enable_stdcall_fixup) /* -1=warn or 1=disable */
+  /* The implementation of the feature is rather dumb and would cause the
+     compilation time to go through the roof if there are many undefined
+     symbols in the link, so it needs to be run after auto-import.  */
+  if (pe_enable_stdcall_fixup) /* -1=warn or 1=enable */
     pe_fixup_stdcalls ();
 
 #if defined (TARGET_IS_i386pe) \
diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index cea210c96c..e7b3bc4806 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -356,11 +356,6 @@  typedef struct
 #define U(CSTR) \
   ((is_underscoring () == 0) ? CSTR : "_" CSTR)
 
-/* Get size of constant string for a possible underscore prefixed
-   C visible symbol.  */
-#define U_SIZE(CSTR) \
-  (sizeof (CSTR) + (is_underscoring () == 0 ? 0 : 1))
-
 #define D(field,symbol,def,usc)  {&pep.field, sizeof (pep.field), def, symbol, 0, usc}
 
 static definfo init[] =
@@ -966,13 +961,6 @@  gld_${EMULATION_NAME}_after_parse (void)
   after_parse_default ();
 }
 
-/* pep-dll.c directly accesses pep_data_import_dll,
-   so it must be defined outside of #ifdef DLL_SUPPORT.
-   Note - this variable is deliberately not initialised.
-   This allows it to be treated as a common varaible, and only
-   exist in one incarnation in a multiple target enabled linker.  */
-char * pep_data_import_dll;
-
 #ifdef DLL_SUPPORT
 static struct bfd_link_hash_entry *pep_undef_found_sym;
 
@@ -1074,8 +1062,8 @@  pep_fixup_stdcalls (void)
       }
 }
 
-static int
-make_import_fixup (arelent *rel, asection *s, char *name)
+static void
+make_import_fixup (arelent *rel, asection *s, char *name, const char *symname)
 {
   struct bfd_symbol *sym = *rel->sym_ptr_ptr;
   char addend[8];
@@ -1128,98 +1116,8 @@  make_import_fixup (arelent *rel, asection *s, char *name)
 	printf (" pcrel");
       printf (" %d bit rel.\n", (int) rel->howto->bitsize);
     }
-  pep_create_import_fixup (rel, s, _addend, name);
 
-  return 1;
-}
-
-static void
-pep_find_data_imports (void)
-{
-  struct bfd_link_hash_entry *undef, *sym;
-  size_t namelen;
-  char *buf, *name;
-
-  if (link_info.pei386_auto_import == 0)
-    return;
-
-  namelen = 0;
-  for (undef = link_info.hash->undefs; undef; undef = undef->u.undef.next)
-    {
-      if (undef->type == bfd_link_hash_undefined)
-	{
-	  size_t len = strlen (undef->root.string);
-	  if (namelen < len)
-	    namelen = len;
-	}
-    }
-  if (namelen == 0)
-    return;
-
-  /* We are being a bit cunning here.  The buffer will have space for
-     prefixes at the beginning.  The prefix is modified here and in a
-     number of functions called from this function.  */
-#define PREFIX_LEN 32
-  buf = xmalloc (PREFIX_LEN + namelen + 1);
-  name = buf + PREFIX_LEN;
-
-  for (undef = link_info.hash->undefs; undef; undef = undef->u.undef.next)
-    {
-      if (undef->type == bfd_link_hash_undefined)
-	{
-	  char *impname;
-
-	  if (pep_dll_extra_pe_debug)
-	    printf ("%s:%s\n", __FUNCTION__, undef->root.string);
-
-	  strcpy (name, undef->root.string);
-	  impname = name - (sizeof "__imp_" - 1);
-	  memcpy (impname, "__imp_", sizeof "__imp_" - 1);
-
-	  sym = bfd_link_hash_lookup (link_info.hash, impname, 0, 0, 1);
-
-	  if (sym && sym->type == bfd_link_hash_defined)
-	    {
-	      bfd *b = sym->u.def.section->owner;
-	      asymbol **symbols;
-	      int nsyms, i;
-
-	      if (!bfd_generic_link_read_symbols (b))
-		{
-		  einfo (_("%F%P: %pB: could not read symbols: %E\n"), b);
-		  return;
-		}
-
-	      symbols = bfd_get_outsymbols (b);
-	      nsyms = bfd_get_symcount (b);
-
-	      for (i = 0; i < nsyms; i++)
-		{
-		  if (! CONST_STRNEQ (symbols[i]->name, U ("_head_")))
-		    continue;
-
-		  if (pep_dll_extra_pe_debug)
-		    printf ("->%s\n", symbols[i]->name);
-
-		  pep_data_import_dll = (char *) (symbols[i]->name
-						  + U_SIZE ("_head_") - 1);
-		  break;
-		}
-
-	      pep_walk_relocs_of_symbol (&link_info, name, make_import_fixup);
-
-	      /* Let's differentiate it somehow from defined.  */
-	      undef->type = bfd_link_hash_defweak;
-	      /* We replace original name with __imp_ prefixed, this
-		 1) may trash memory 2) leads to duplicate symbol generation.
-		 Still, IMHO it's better than having name polluted.  */
-	      undef->root.string = sym->root.string;
-	      undef->u.def.value = sym->u.def.value;
-	      undef->u.def.section = sym->u.def.section;
-	    }
-	}
-    }
-  free (buf);
+  pep_create_import_fixup (rel, s, _addend, name, symname);
 }
 
 static bfd_boolean
@@ -1491,16 +1389,15 @@  gld_${EMULATION_NAME}_after_open (void)
   pep_output_file_set_long_section_names (link_info.output_bfd);
 
 #ifdef DLL_SUPPORT
-  if (pep_enable_stdcall_fixup) /* -1=warn or 1=disable */
-    pep_fixup_stdcalls ();
-
   pep_process_import_defs (link_info.output_bfd, &link_info);
 
-  pep_find_data_imports ();
+  if (link_info.pei386_auto_import) /* -1=warn or 1=enable */
+    pep_find_data_imports (U ("_head_"), make_import_fixup);
 
-  /* As possibly new symbols are added by imports, we rerun
-     stdcall/fastcall fixup here.  */
-  if (pep_enable_stdcall_fixup) /* -1=warn or 1=disable */
+  /* The implementation of the feature is rather dumb and would cause the
+     compilation time to go through the roof if there are many undefined
+     symbols in the link, so it needs to be run after auto-import.  */
+  if (pep_enable_stdcall_fixup) /* -1=warn or 1=enable */
     pep_fixup_stdcalls ();
 
 #ifndef TARGET_IS_i386pep
diff --git a/ld/ld.texinfo b/ld/ld.texinfo
index 226e2deb32..c9770e1f2f 100644
--- a/ld/ld.texinfo
+++ b/ld/ld.texinfo
@@ -2752,11 +2752,16 @@  uwin, pw, etc.  For instance, cygwin DLLs typically use
 @kindex --enable-auto-import
 @item --enable-auto-import
 Do sophisticated linking of @code{_symbol} to @code{__imp__symbol} for
-DATA imports from DLLs, and create the necessary thunking symbols when
-building the import libraries with those DATA exports. Note: Use of the
-'auto-import' extension will cause the text section of the image file
-to be made writable. This does not conform to the PE-COFF format
-specification published by Microsoft.
+DATA imports from DLLs, thus making it possible to bypass the dllimport
+mechanism on the user side and to reference unmangled symbol names.
+[This option is specific to the i386 PE targeted port of the linker]
+
+The following remarks pertain to the original implementation of the
+feature and are obsolete nowadays for Cygwin and MinGW targets.
+
+Note: Use of the 'auto-import' extension will cause the text section
+of the image file to be made writable. This does not conform to the
+PE-COFF format specification published by Microsoft.
 
 Note - use of the 'auto-import' extension will also cause read only
 data which would normally be placed into the .rdata section to be
@@ -2878,7 +2883,6 @@  A fourth way to avoid this problem is to re-code your
 library to use a functional interface rather than a data interface
 for the offending variables (e.g. set_foo() and get_foo() accessor
 functions).
-[This option is specific to the i386 PE targeted port of the linker]
 
 @kindex --disable-auto-import
 @item --disable-auto-import
@@ -2896,8 +2900,7 @@  environment to adjust references to such data in your client code.
 
 @kindex --disable-runtime-pseudo-reloc
 @item --disable-runtime-pseudo-reloc
-Do not create pseudo relocations for non-zero offset DATA imports from
-DLLs.
+Do not create pseudo relocations for non-zero offset DATA imports from DLLs.
 [This option is specific to the i386 PE targeted port of the linker]
 
 @kindex --enable-extra-pe-debug
diff --git a/ld/pe-dll.c b/ld/pe-dll.c
index c087bf23bc..ad0ffcffea 100644
--- a/ld/pe-dll.c
+++ b/ld/pe-dll.c
@@ -168,7 +168,7 @@  static struct bfd_section *edata_s, *reloc_s;
 static unsigned char *edata_d, *reloc_d;
 static size_t edata_sz, reloc_sz;
 static int runtime_pseudo_relocs_created = 0;
-static int runtime_pseudp_reloc_v2_init = 0;
+static bfd_boolean runtime_pseudp_reloc_v2_init = FALSE;
 
 typedef struct
 {
@@ -1287,10 +1287,12 @@  fill_edata (bfd *abfd, struct bfd_link_info *info ATTRIBUTE_UNUSED)
 
 static struct bfd_section *current_sec;
 
-void
-pe_walk_relocs_of_symbol (struct bfd_link_info *info,
-			  char *name,
-			  int (*cb) (arelent *, asection *, char *))
+static void
+pe_walk_relocs (struct bfd_link_info *info,
+		char *name,
+		const char *symname,
+		struct bfd_hash_table *import_hash,
+		void (*cb) (arelent *, asection *, char *, const char *))
 {
   bfd *b;
   asection *s;
@@ -1328,8 +1330,20 @@  pe_walk_relocs_of_symbol (struct bfd_link_info *info,
 	    {
 	      struct bfd_symbol *sym = *relocs[i]->sym_ptr_ptr;
 
-	      if (!strcmp (name, sym->name))
-		cb (relocs[i], s, name);
+	      /* Warning: the callback needs to be passed NAME directly.  */
+	      if (import_hash)
+		{
+		  if (bfd_hash_lookup (import_hash, sym->name, FALSE, FALSE))
+		    {
+		      strcpy (name, sym->name);
+		      cb (relocs[i], s, name, symname);
+		    }
+		}
+	      else
+		{
+		  if (strcmp (name, sym->name) == 0)
+		    cb (relocs[i], s, name, symname);
+		}
 	    }
 
 	  free (relocs);
@@ -1341,6 +1355,138 @@  pe_walk_relocs_of_symbol (struct bfd_link_info *info,
     }
 }
 
+void
+pe_find_data_imports (const char *symhead,
+		      void (*cb) (arelent *, asection *, char *, const char *))
+{
+  struct bfd_link_hash_entry *undef;
+  const size_t headlen = strlen (symhead);
+  size_t namelen = 0;
+  char *buf, *name;
+  struct bfd_hash_table *import_hash;
+
+  for (undef = link_info.hash->undefs; undef; undef = undef->u.undef.next)
+    if (undef->type == bfd_link_hash_undefined)
+      {
+	size_t len = strlen (undef->root.string);
+	if (namelen < len)
+	  namelen = len;
+      }
+  if (namelen == 0)
+    return;
+
+  /* For the pseudo-relocation support version 2, we can collect the symbols
+     that are subject to auto-import and adjust the relocations en masse.  */
+  if (link_info.pei386_runtime_pseudo_reloc == 2)
+    {
+      import_hash
+	= (struct bfd_hash_table *) xmalloc (sizeof (struct bfd_hash_table));
+      if (!bfd_hash_table_init (import_hash,
+				bfd_hash_newfunc,
+				sizeof (struct bfd_hash_entry)))
+	einfo (_("%F%P: bfd_hash_table_init failed: %E\n"));
+    }
+  else
+    import_hash = NULL;
+
+  /* We are being a bit cunning here.  The buffer will have space for
+     prefixes at the beginning.  The prefix is modified here and in a
+     number of functions called from this function.  */
+#define PREFIX_LEN 32
+  buf = xmalloc (PREFIX_LEN + namelen + 1);
+  name = buf + PREFIX_LEN;
+
+  for (undef = link_info.hash->undefs; undef; undef = undef->u.undef.next)
+    if (undef->type == bfd_link_hash_undefined)
+      {
+	struct bfd_link_hash_entry *sym;
+	char *impname;
+
+	if (pe_dll_extra_pe_debug)
+	  printf ("%s:%s\n", __FUNCTION__, undef->root.string);
+
+	strcpy (name, undef->root.string);
+	impname = name - (sizeof "__imp_" - 1);
+	memcpy (impname, "__imp_", sizeof "__imp_" - 1);
+
+	sym = bfd_link_hash_lookup (link_info.hash, impname, 0, 0, 1);
+
+	if (sym && sym->type == bfd_link_hash_defined)
+	  {
+	    if (import_hash)
+	      bfd_hash_lookup (import_hash, undef->root.string, TRUE, FALSE);
+	    else
+	      {
+		bfd *b = sym->u.def.section->owner;
+		const char *symname = NULL;
+		asymbol **symbols;
+		int nsyms, i;
+
+		if (!bfd_generic_link_read_symbols (b))
+		  {
+		    einfo (_("%F%P: %pB: could not read symbols: %E\n"), b);
+		    return;
+		  }
+
+		symbols = bfd_get_outsymbols (b);
+		nsyms = bfd_get_symcount (b);
+
+		for (i = 0; i < nsyms; i++)
+		  if (strncmp (symbols[i]->name, symhead, headlen) == 0)
+		    {
+		      if (pe_dll_extra_pe_debug)
+			printf ("->%s\n", symbols[i]->name);
+
+		      symname = symbols[i]->name + headlen;
+		      break;
+		    }
+
+		/* If the symobl isn't part of an import table, there is no
+		   point in building a fixup, this would give rise to link
+		   errors for mangled symbols instead of the original one.  */
+		if (symname)
+		  pe_walk_relocs (&link_info, name, symname, NULL, cb);
+		else
+		  continue;
+	      }
+
+	    /* Let's differentiate it somehow from defined.  */
+	    undef->type = bfd_link_hash_defweak;
+	    undef->u.def.value = sym->u.def.value;
+	    undef->u.def.section = sym->u.def.section;
+
+	    if (link_info.pei386_auto_import == -1)
+	      {
+		static bfd_boolean warned = FALSE;
+
+		info_msg (_("Info: resolving %s by linking to %s "
+			    "(auto-import)\n"), name, impname);
+
+		/* PR linker/4844.  */
+		if (!warned)
+		  {
+		    einfo (_("%P: warning: auto-importing has been activated "
+			     "without --enable-auto-import specified on the "
+			     "command line; this should work unless it "
+			     "involves constant data structures referencing "
+			     "symbols from auto-imported DLLs\n"));
+		    warned = TRUE;
+		  }
+	      }
+	  }
+      }
+
+  /* If we have the import hash table, walk the relocations only once.  */
+  if (import_hash)
+    {
+      pe_walk_relocs (&link_info, name, NULL, import_hash, cb);
+      bfd_hash_table_free (import_hash);
+      free (import_hash);
+    }
+
+  free (buf);
+}
+
 /* Gather all the relocations and build the .reloc section.  */
 
 static void
@@ -1794,7 +1940,6 @@  pe_dll_generate_def_file (const char *pe_out_def_filename)
 static asymbol **symtab;
 static int symptr;
 static int tmp_seq;
-static int tmp_seq2;
 static const char *dll_filename;
 static char *dll_symname;
 
@@ -2327,47 +2472,6 @@  make_one (def_file_export *exp, bfd *parent, bfd_boolean include_jmp_stub)
 }
 
 static bfd *
-make_singleton_name_imp (const char *import, bfd *parent)
-{
-  /* Name thunks go to idata$4.  */
-  asection *id5;
-  unsigned char *d5;
-  char *oname;
-  bfd *abfd;
-
-  oname = xmalloc (20);
-  sprintf (oname, "nmimp%06d.o", tmp_seq2);
-  tmp_seq2++;
-
-  abfd = bfd_create (oname, parent);
-  bfd_find_target (pe_details->object_target, abfd);
-  bfd_make_writable (abfd);
-
-  bfd_set_format (abfd, bfd_object);
-  bfd_set_arch_mach (abfd, pe_details->bfd_arch, 0);
-
-  symptr = 0;
-  symtab = xmalloc (3 * sizeof (asymbol *));
-  id5 = quick_section (abfd, ".idata$5", SEC_HAS_CONTENTS, 2);
-  quick_symbol (abfd, "__imp_", import, "", id5, BSF_GLOBAL, 0);
-
-  /* We need space for the real thunk and for the null terminator.  */
-  bfd_set_section_size (abfd, id5, PE_IDATA5_SIZE * 2);
-  d5 = xmalloc (PE_IDATA5_SIZE * 2);
-  id5->contents = d5;
-  memset (d5, 0, PE_IDATA5_SIZE * 2);
-  quick_reloc (abfd, 0, BFD_RELOC_RVA, 2);
-  save_relocs (id5);
-
-  bfd_set_symtab (abfd, symtab, symptr);
-
-  bfd_set_section_contents (abfd, id5, d5, 0, PE_IDATA4_SIZE * 2);
-
-  bfd_make_readable (abfd);
-  return abfd;
-}
-
-static bfd *
 make_singleton_name_thunk (const char *import, bfd *parent)
 {
   /* Name thunks go to idata$4.  */
@@ -2409,7 +2513,7 @@  make_singleton_name_thunk (const char *import, bfd *parent)
   return abfd;
 }
 
-static char *
+static const char *
 make_import_fixup_mark (arelent *rel, char *name)
 {
   /* We convert reloc to symbol, for later reference.  */
@@ -2431,7 +2535,7 @@  make_import_fixup_mark (arelent *rel, char *name)
 				current_sec, /* sym->section, */
 				rel->address, NULL, TRUE, FALSE, &bh);
 
-  return fixup_name;
+  return bh->root.string;
 }
 
 /*	.section	.idata$2
@@ -2469,12 +2573,7 @@  make_import_fixup_entry (const char *name,
 
   quick_symbol (abfd, "__nm_thnk_", name, "", UNDSEC, BSF_GLOBAL, 0);
   quick_symbol (abfd, U (""), symname, "_iname", UNDSEC, BSF_GLOBAL, 0);
-  /* For relocator v2 we have to use the .idata$5 element and not
-     fixup_name.  */
-  if (link_info.pei386_runtime_pseudo_reloc == 2)
-    quick_symbol (abfd, "__imp_", name, "", UNDSEC, BSF_GLOBAL, 0);
-  else
-    quick_symbol (abfd, "", fixup_name, "", UNDSEC, BSF_GLOBAL, 0);
+  quick_symbol (abfd, "", fixup_name, "", UNDSEC, BSF_GLOBAL, 0);
 
   bfd_set_section_size (abfd, id2, 20);
   d2 = xmalloc (20);
@@ -2509,6 +2608,8 @@  make_runtime_pseudo_reloc (const char *name ATTRIBUTE_UNUSED,
   unsigned char *rt_rel_d;
   char *oname;
   bfd *abfd;
+  bfd_size_type size;
+
   oname = xmalloc (20);
   sprintf (oname, "rtr%06d.o", tmp_seq);
   tmp_seq++;
@@ -2520,47 +2621,52 @@  make_runtime_pseudo_reloc (const char *name ATTRIBUTE_UNUSED,
   bfd_set_format (abfd, bfd_object);
   bfd_set_arch_mach (abfd, pe_details->bfd_arch, 0);
 
-  symptr = 0;
   if (link_info.pei386_runtime_pseudo_reloc == 2)
     {
-      symtab = xmalloc ((runtime_pseudp_reloc_v2_init ? 3 : 6) * sizeof (asymbol *));
+      if (runtime_pseudp_reloc_v2_init)
+	size = 3 * sizeof (asymbol *);
+      else
+	size = 6 * sizeof (asymbol *);
     }
   else
-    {
-      symtab = xmalloc (2 * sizeof (asymbol *));
-    }
-  rt_rel = quick_section (abfd, ".rdata_runtime_pseudo_reloc",
-			  SEC_HAS_CONTENTS, 2);
+    size = 2 * sizeof (asymbol *);
+
+  symptr = 0;
+  symtab = xmalloc (size);
+
+  rt_rel
+    = quick_section (abfd, ".rdata_runtime_pseudo_reloc", SEC_HAS_CONTENTS, 2);
 
   quick_symbol (abfd, "", fixup_name, "", UNDSEC, BSF_GLOBAL, 0);
 
   if (link_info.pei386_runtime_pseudo_reloc == 2)
     {
-	  size_t size = 12;
-	  if (! runtime_pseudp_reloc_v2_init)
-	    {
-		  size += 12;
-		  runtime_pseudp_reloc_v2_init = 1;
-	    }
+      size = 12;
+      if (!runtime_pseudp_reloc_v2_init)
+	{
+	  size += 12;
+	  runtime_pseudp_reloc_v2_init = TRUE;
+	}
+
       quick_symbol (abfd, "__imp_", name, "", UNDSEC, BSF_GLOBAL, 0);
 
       bfd_set_section_size (abfd, rt_rel, size);
       rt_rel_d = xmalloc (size);
       rt_rel->contents = rt_rel_d;
       memset (rt_rel_d, 0, size);
-	  quick_reloc (abfd, size - 8, BFD_RELOC_RVA, 1);
-	  quick_reloc (abfd, size - 12, BFD_RELOC_RVA, 2);
-	  bfd_put_32 (abfd, bitsize, rt_rel_d + (size - 4));
-	  if (size != 12)
-	    bfd_put_32 (abfd, 1, rt_rel_d + 8);
+      quick_reloc (abfd, size - 8, BFD_RELOC_RVA, 1);
+      quick_reloc (abfd, size - 12, BFD_RELOC_RVA, 2);
+      bfd_put_32 (abfd, bitsize, rt_rel_d + (size - 4));
+      if (size != 12)
+	bfd_put_32 (abfd, 1, rt_rel_d + 8);
       save_relocs (rt_rel);
 
       bfd_set_symtab (abfd, symtab, symptr);
 
       bfd_set_section_contents (abfd, rt_rel, rt_rel_d, 0, size);
-   }
+    }
   else
-   {
+    {
       bfd_set_section_size (abfd, rt_rel, 8);
       rt_rel_d = xmalloc (8);
       rt_rel->contents = rt_rel_d;
@@ -2575,6 +2681,7 @@  make_runtime_pseudo_reloc (const char *name ATTRIBUTE_UNUSED,
 
       bfd_set_section_contents (abfd, rt_rel, rt_rel_d, 0, 8);
    }
+
   bfd_make_readable (abfd);
   return abfd;
 }
@@ -2624,65 +2731,46 @@  pe_create_runtime_relocator_reference (bfd *parent)
 }
 
 void
-pe_create_import_fixup (arelent *rel, asection *s, bfd_vma addend, char *name)
+pe_create_import_fixup (arelent *rel, asection *s, bfd_vma addend, char *name,
+			const char *symname)
 {
-  struct bfd_symbol *sym = *rel->sym_ptr_ptr;
-  struct bfd_link_hash_entry *name_thunk_sym;
-  struct bfd_link_hash_entry *name_imp_sym;
-  char *fixup_name, *impname;
+  const char *fixup_name = make_import_fixup_mark (rel, name);
   bfd *b;
-  int need_import_table = 1;
-
-  /* name buffer is allocated with space at beginning for prefixes.  */
-  impname = name - (sizeof "__imp_" - 1);
-  memcpy (impname, "__imp_", sizeof "__imp_" - 1);
-  name_imp_sym = bfd_link_hash_lookup (link_info.hash, impname, 0, 0, 1);
 
-  impname = name - (sizeof "__nm_thnk_" - 1);
-  memcpy (impname, "__nm_thnk_", sizeof "__nm_thnk_" - 1);
-  name_thunk_sym = bfd_link_hash_lookup (link_info.hash, impname, 0, 0, 1);
-
-  fixup_name = make_import_fixup_mark (rel, name);
-
-  /* For version 2 pseudo relocation we don't need to add an import
-     if the import symbol is already present.  */
-  if (link_info.pei386_runtime_pseudo_reloc == 2
-      && name_imp_sym
-      && name_imp_sym->type == bfd_link_hash_defined)
-    need_import_table = 0;
-
-  if (need_import_table == 1
-      && (!name_thunk_sym || name_thunk_sym->type != bfd_link_hash_defined))
+  /* This is the original implementation of the auto-import feature, which
+     primarily relied on the OS loader to patch things up with some help
+     from the pseudo-relocator to overcome the main limitation.  See the
+     comment at the beginning of the file for an overview of the feature.  */
+  if (link_info.pei386_runtime_pseudo_reloc != 2)
     {
-      b = make_singleton_name_thunk (name, link_info.output_bfd);
-      add_bfd_to_link (b, b->filename, &link_info);
+      struct bfd_link_hash_entry *name_thunk_sym;
+      /* name buffer is allocated with space at beginning for prefixes.  */
+      char *thname = name - (sizeof "__nm_thnk_" - 1);
+      memcpy (thname, "__nm_thnk_", sizeof "__nm_thnk_" - 1);
+      name_thunk_sym = bfd_link_hash_lookup (link_info.hash, thname, 0, 0, 1);
 
-      /* If we ever use autoimport, we have to cast text section writable.
-	 But not for version 2.  */
-      if (link_info.pei386_runtime_pseudo_reloc != 2)
+      if (!(name_thunk_sym && name_thunk_sym->type == bfd_link_hash_defined))
 	{
+	  b = make_singleton_name_thunk (name, link_info.output_bfd);
+	  add_bfd_to_link (b, b->filename, &link_info);
+
+	  /* If we ever use autoimport, we have to cast text section writable.  */
 	  config.text_read_only = FALSE;
 	  link_info.output_bfd->flags &= ~WP_TEXT;
 	}
-      if (link_info.pei386_runtime_pseudo_reloc == 2)
+
+      if (addend == 0 || link_info.pei386_runtime_pseudo_reloc == 1)
 	{
-	  b = make_singleton_name_imp (name, link_info.output_bfd);
+	  b = make_import_fixup_entry (name, fixup_name, symname,
+				       link_info.output_bfd);
 	  add_bfd_to_link (b, b->filename, &link_info);
 	}
     }
 
-  if ((addend == 0 || link_info.pei386_runtime_pseudo_reloc)
-      && need_import_table == 1)
-    {
-      extern char * pe_data_import_dll;
-      char * symname = pe_data_import_dll ? pe_data_import_dll : "unknown";
-
-      b = make_import_fixup_entry (name, fixup_name, symname,
-				   link_info.output_bfd);
-      add_bfd_to_link (b, b->filename, &link_info);
-    }
-
-  if ((link_info.pei386_runtime_pseudo_reloc != 0 && addend != 0)
+  /* In the original implementation, the pseudo-relocator was only used when
+     the addend was not null.  In the new implementation, the OS loader is
+     completely bypassed and the pseudo-relocator does the entire work.  */
+  if ((addend != 0 && link_info.pei386_runtime_pseudo_reloc == 1)
       || link_info.pei386_runtime_pseudo_reloc == 2)
     {
       if (pe_dll_extra_pe_debug)
@@ -2693,19 +2781,18 @@  pe_create_import_fixup (arelent *rel, asection *s, bfd_vma addend, char *name)
 				     link_info.output_bfd);
       add_bfd_to_link (b, b->filename, &link_info);
 
-      if (runtime_pseudo_relocs_created == 0)
+      if (runtime_pseudo_relocs_created++ == 0)
 	{
 	  b = pe_create_runtime_relocator_reference (link_info.output_bfd);
 	  add_bfd_to_link (b, b->filename, &link_info);
 	}
-      runtime_pseudo_relocs_created++;
     }
+
   else if (addend != 0)
     einfo (_("%X%P: %C: variable '%pT' can't be auto-imported; please read the documentation for ld's --enable-auto-import for details\n"),
-	   s->owner, s, rel->address, sym->name);
+	   s->owner, s, rel->address, (*rel->sym_ptr_ptr)->name);
 }
 
-
 void
 pe_dll_generate_implib (def_file *def, const char *impfilename, struct bfd_link_info *info)
 {
diff --git a/ld/pe-dll.h b/ld/pe-dll.h
index 48d169b6cc..38655f70d2 100644
--- a/ld/pe-dll.h
+++ b/ld/pe-dll.h
@@ -61,10 +61,10 @@  extern void pe_dll_fill_sections
   (bfd *, struct bfd_link_info *);
 extern void pe_exe_fill_sections
   (bfd *, struct bfd_link_info *);
-extern void pe_walk_relocs_of_symbol
-  (struct bfd_link_info *, char *, int (*) (arelent *, asection *, char *));
+extern void pe_find_data_imports
+  (const char *, void (*cb) (arelent *, asection *, char *, const char *));
 extern void pe_create_import_fixup
-  (arelent * rel, asection *, bfd_vma, char *);
+  (arelent * rel, asection *, bfd_vma, char *, const char *);
 extern bfd_boolean pe_bfd_is_dll
   (bfd *);
 extern void pe_output_file_set_long_section_names
diff --git a/ld/pep-dll.c b/ld/pep-dll.c
index b8c017f86d..c53a676d98 100644
--- a/ld/pep-dll.c
+++ b/ld/pep-dll.c
@@ -40,10 +40,8 @@ 
 				    pep_use_coff_long_section_names
 #define pe_leading_underscore	    pep_leading_underscore
 
-/* External globals.  */
-#define pe_data_import_dll          pep_data_import_dll
-
 /* Unique global name for functions to avoid double defined symbols.  */
+#define pe_find_data_imports        pep_find_data_imports
 #define pe_create_import_fixup      pep_create_import_fixup
 #define pe_dll_generate_def_file    pep_dll_generate_def_file
 #define pe_process_import_defs      pep_process_import_defs
@@ -55,7 +53,6 @@ 
 #define pe_exe_fill_sections        pep_exe_fill_sections
 #define pe_dll_generate_implib      pep_dll_generate_implib
 #define pe_dll_add_excludes         pep_dll_add_excludes
-#define pe_walk_relocs_of_symbol    pep_walk_relocs_of_symbol
 #define pe_bfd_is_dll		    pep_bfd_is_dll
 #define pe_output_file_set_long_section_names \
 				    pep_output_file_set_long_section_names
diff --git a/ld/pep-dll.h b/ld/pep-dll.h
index 0a27c1fc02..611c8cdb27 100644
--- a/ld/pep-dll.h
+++ b/ld/pep-dll.h
@@ -52,10 +52,11 @@  extern void pep_dll_build_sections  (bfd *, struct bfd_link_info *);
 extern void pep_exe_build_sections  (bfd *, struct bfd_link_info *);
 extern void pep_dll_fill_sections  (bfd *, struct bfd_link_info *);
 extern void pep_exe_fill_sections  (bfd *, struct bfd_link_info *);
-extern void pep_walk_relocs_of_symbol
-  (struct bfd_link_info *, char *, int (*) (arelent *, asection *, char *));
+extern void pep_find_data_imports  (const char *,
+				    void (*cb) (arelent *, asection *, char *,
+						const char *));
 extern void pep_create_import_fixup  (arelent * rel, asection *, bfd_vma,
-				      char *);
+				      char *, const char *);
 extern bfd_boolean pep_bfd_is_dll  (bfd *);
 extern void pep_output_file_set_long_section_names (bfd *);