[v3,1/1] aarch64: mingw: Auto-import implementation

Message ID 20260929090647.46596-2-evgeny.karpov@arm.com
State Committed
Headers
Series aarch64: mingw: Auto-import implementation |

Checks

Context Check Description
linaro-tcwg-bot/tcwg_binutils_build--master-arm success Build passed
linaro-tcwg-bot/tcwg_binutils_build--master-aarch64 success Build passed
linaro-tcwg-bot/tcwg_binutils_check--master-aarch64 success Test passed
linaro-tcwg-bot/tcwg_binutils_check--master-arm success Test passed

Commit Message

Evgeny Karpov Sept. 29, 2026, 9:06 a.m. UTC
  Auto-import is needed to resolve symbols during dynamic linking.

For example,

  adrp	x0, dyn_v + 2
  add	x0, x0, :lo12:dyn_v + 2

or

  adrp  x0, dyn_v + 8
  ldr   x0, [x0, :lo12:dyn_v + 8]

or

  b init_dyn_v
  bl init_dyn_v

init_dyn_v and dyn_v are undefined at compile time and are getting resolved
during linking with a dynamic library to the IAT (import address table).

In case of b or bl, it can be done by using a thunk

init_dyn_v:
  adrp	x16, __imp_init_dyn_v
  add	x16, x16, :lo12:__imp_init_dyn_v
  ldr	x16, [x16]
  br	x16

"adrp", "add" and "ldr" are handled differently. The codegen usually
creates a reference to a symbol without an offset:
adrp x0, <symbol>
add x0, x0, :lo12:<symbol> / ldr x0, [x0, :lo12:<symbol>]

however it might be necessary to support offsets in some cases:
  adrp  x0, <symbol> +/- offset
  add   x0, x0, :lo12:<symbol> +/- offset / ldr   x0, [x0, :lo12:<symbol> +/- offset]

It is handled by detecting relocations that have symbols that are resolved
during dynamic linking.
"adrp" is replaced by an unconditional branching to a place where a symbol address
will be loaded from the IAT and then returning back.
Relocation for the "add" and "ldr" instructions is not needed in this case.

  adrp	x0, dyn_v + 8
  add	x0, x0, :lo12:dyn_v + 8  /  ldr   x0, [x0, :lo12:dyn_v + 8]

turns into

__fu1_dyn_v:
  b     __imp_dyn_v_10_8
  add	x0, x0, 8  /  ldr   x0, [x0, 8]
  ...

__imp_dyn_v_10_8:
   adrp	x0, __imp_dyn_v
   ldr	x0, [x0, :lo12:__imp_dyn_v]
   add	x0, x0, offset_4k, lsl #12
   b	__fu1_dyn_v + 0x4

Another option that was considered is using bl / ret.
However, in this case, an edge case will not be handled properly:
  adrp x30, dyn_v

Signed-off-by: Evgeny Karpov <evgeny@kmaps.co>

bfd/ChangeLog:

	* coff-aarch64.c (coff_pe_aarch64_relocate_section): Add relocation
	handling for dynamic linking.

ld/ChangeLog:

	* emultempl/pep.em: Update read_addend for aarch64 relocations.
	* pe-dll.c (aarch64_make_jump_stub): Add jump stub.
	(aarch64_make_imp_offset): Add adrp handler for dynamic linking.
	(pe_create_import_fixup): Add fixups for aarch64.
	* testsuite/ld-pe/pe.exp: Add auto-import-aarch64 test.
	* testsuite/ld-pe/auto-import-aarch64.d: New test.
	* testsuite/ld-pe/auto-import-aarch64.s: New test.
	* testsuite/ld-pe/auto-import-dll-aarch64.s: New test.
---
 bfd/coff-aarch64.c                           |  33 +++
 ld/emultempl/pep.em                          |   6 +
 ld/pe-dll.c                                  | 175 +++++++++++
 ld/testsuite/ld-pe/auto-import-aarch64.d     | 293 +++++++++++++++++++
 ld/testsuite/ld-pe/auto-import-aarch64.s     |  89 ++++++
 ld/testsuite/ld-pe/auto-import-dll-aarch64.s |  13 +
 ld/testsuite/ld-pe/pe.exp                    |  15 +-
 7 files changed, 622 insertions(+), 2 deletions(-)
 create mode 100644 ld/testsuite/ld-pe/auto-import-aarch64.d
 create mode 100644 ld/testsuite/ld-pe/auto-import-aarch64.s
 create mode 100644 ld/testsuite/ld-pe/auto-import-dll-aarch64.s
  

Comments

Alice Carlotti Oct. 2, 2026, 2:51 a.m. UTC | #1
On Tue, Sep 29, 2026 at 11:06:47AM +0200, Evgeny Karpov wrote:
> Auto-import is needed to resolve symbols during dynamic linking.
>

This looks mostly good, but I have one minor nit:

> +static void
> +aarch64_make_imp_offset(const char* imp_symbol_name,
> +			const char* imp_offset_name, const int offset,
> +			unsigned rd, const char* caller_label, bfd* parent)
> +{
> +  /* offset should be an adrp immediate size.  */
> +  if (offset >= (1 << 20) || offset < -(1 << 20))
> +    einfo (_("%X%P: offset out of range: %d\n"), offset);

I wouldn't call offset an adrp immediate, because those are multiples of 4096.
We should also clarify that this checked range is not the range that this
function itself can handle, but rather a known bound on the values the single
caller can pass.

(We can correctly handle (- 1 << 24 + 1 << 12) <= offset <= (1 << 24 - 1]).

To make this clearer, we could change the comment to say:

/* offset is extracted from an adrp immediate size, so we never fail this
   bounds check.  The code below can actually handle a wider range.  */


> +
> +  if (pe_dll_extra_pe_debug)
> +    printf("symbol: %s imp_offset_name: %s offset: %u rs: %u caller: %s\n",
> +	   imp_symbol_name, imp_offset_name, offset, rd, caller_label);
...

> +  rd &= (1 << 5) - 1;
> +  td[0] |= rd;
> +  td[1] |= (rd << 5) | rd;
> +
> +  /* The bottom 12 bits are handled outside the thunk.  */

Alternatively, we could delete the redundant bounds check, and replace this comment with:

  /* offset is extracted from a 21-bit adrp immediate.  The bottom 12 bits are
     handled outside the thunk.  The remaining bits are in range for an ADD/SUB
     with shifted immediate.  */


> +  unsigned imm = offset & -4096;
> +  if (offset < 0)
> +  {
> +    /* Change ADD (immediate) to SUB (immediate).  */
> +    td[2] |= 1 << 30;
> +    imm = -imm;
> +  }
> +  imm >>= 12;
> +  td[2] |= (rd << 5) | rd;
> +  td[2] |= imm << 10;


If you're ok with one of these changes (I prefer the version without the bounds
check, but either is fine), then I'll make that change and commit the patch for
you.

Thanks,
Alice
  
Evgeny Karpov Oct. 2, 2026, 10:09 a.m. UTC | #2
On Fri, 02 Oct 2026, Alice Carlotti wrote:
> > +aarch64_make_imp_offset(const char* imp_symbol_name,
> > +			const char* imp_offset_name, const int offset,
> > +			unsigned rd, const char* caller_label, bfd* parent)
> > +{
> > +  /* offset should be an adrp immediate size.  */
> > +  if (offset >= (1 << 20) || offset < -(1 << 20))
> > +    einfo (_("%X%P: offset out of range: %d\n"), offset);
> 
> I wouldn't call offset an adrp immediate, because those are multiples of 4096.
> We should also clarify that this checked range is not the range that this
> function itself can handle, but rather a known bound on the values the single
> caller can pass.
> 
> (We can correctly handle (- 1 << 24 + 1 << 12) <= offset <= (1 << 24 - 1]).
> 
> To make this clearer, we could change the comment to say:
> 
> /* offset is extracted from an adrp immediate size, so we never fail this
>    bounds check.  The code below can actually handle a wider range.  */
> 
> 
> > +
> > +  if (pe_dll_extra_pe_debug)
> > +    printf("symbol: %s imp_offset_name: %s offset: %u rs: %u caller: %s\n",
> > +	   imp_symbol_name, imp_offset_name, offset, rd, caller_label);
> ...
> 
> > +  rd &= (1 << 5) - 1;
> > +  td[0] |= rd;
> > +  td[1] |= (rd << 5) | rd;
> > +
> > +  /* The bottom 12 bits are handled outside the thunk.  */
> 
> Alternatively, we could delete the redundant bounds check, and replace this comment with:
> 
>   /* offset is extracted from a 21-bit adrp immediate.  The bottom 12 bits are
>      handled outside the thunk.  The remaining bits are in range for an ADD/SUB
>      with shifted immediate.  */
> 
> 
> > +  unsigned imm = offset & -4096;
> > +  if (offset < 0)
> > +  {
> > +    /* Change ADD (immediate) to SUB (immediate).  */
> > +    td[2] |= 1 << 30;
> > +    imm = -imm;
> > +  }
> > +  imm >>= 12;
> > +  td[2] |= (rd << 5) | rd;
> > +  td[2] |= imm << 10;
> 
> 
> If you're ok with one of these changes (I prefer the version without the bounds
> check, but either is fine), then I'll make that change and commit the patch for
> you.

The second version looks more preferable. Thanks.

Regards,
Evgeny
  
Alan Modra Oct. 6, 2026, 3:50 a.m. UTC | #3
On Tue, Sep 29, 2026 at 11:06:47AM +0200, Evgeny Karpov wrote:
> --- a/ld/testsuite/ld-pe/pe.exp
> +++ b/ld/testsuite/ld-pe/pe.exp
> @@ -185,10 +196,10 @@ if [check_shared_lib_support] {
>      }
>      run_dump_test "symbols-ordinals-hints-exports-ld"
>      # no dlltool for these two
> -    setup_xfail aarch64-*-* sh-*-*
> +    setup_xfail sh-*-*
>      run_dump_test "symbols-ordinals-hints-exports-dlltool"
>      run_dump_test "symbols-ordinals-hints-imports-ld"
> -    setup_xfail aarch64-*-* sh-*-*
> +    setup_xfail sh-*-*
>      run_dump_test "symbols-ordinals-hints-imports-dlltool"
>      set ASFLAGS $old_ASFLAGS
>  }

This resulted in
aarch64-pe  +FAIL: ld-pe/symbols-ordinals-hints-exports-dlltool
aarch64-pe  +FAIL: ld-pe/symbols-ordinals-hints-imports-dlltool
due to dlltool not being built for that target.
  
Evgeny Karpov Oct. 6, 2026, 9:20 a.m. UTC | #4
On Tue, 06 Oct 2026, Alan Modra wrote:
> >      run_dump_test "symbols-ordinals-hints-exports-dlltool"
> >      run_dump_test "symbols-ordinals-hints-imports-ld"
> > -    setup_xfail aarch64-*-* sh-*-*
> > +    setup_xfail sh-*-*
> >      run_dump_test "symbols-ordinals-hints-imports-dlltool"
> >      set ASFLAGS $old_ASFLAGS
> >  }
> 
> This resulted in
> aarch64-pe  +FAIL: ld-pe/symbols-ordinals-hints-exports-dlltool
> aarch64-pe  +FAIL: ld-pe/symbols-ordinals-hints-imports-dlltool
> due to dlltool not being built for that target.

Thanks for reporting this issue and sorry for this regression.
It has been tested for aarch64-*-mingw*.

Should it be changed to "setup_xfail aarch64-*-pe* sh-*-*"?

Regards,
Evgeny
  
Alan Modra Oct. 6, 2026, 10:59 a.m. UTC | #5
On Tue, Oct 06, 2026 at 11:20:17AM +0200, Evgeny Karpov wrote:
> On Tue, 06 Oct 2026, Alan Modra wrote:
> > >      run_dump_test "symbols-ordinals-hints-exports-dlltool"
> > >      run_dump_test "symbols-ordinals-hints-imports-ld"
> > > -    setup_xfail aarch64-*-* sh-*-*
> > > +    setup_xfail sh-*-*
> > >      run_dump_test "symbols-ordinals-hints-imports-dlltool"
> > >      set ASFLAGS $old_ASFLAGS
> > >  }
> > 
> > This resulted in
> > aarch64-pe  +FAIL: ld-pe/symbols-ordinals-hints-exports-dlltool
> > aarch64-pe  +FAIL: ld-pe/symbols-ordinals-hints-imports-dlltool
> > due to dlltool not being built for that target.
> 
> Thanks for reporting this issue and sorry for this regression.
> It has been tested for aarch64-*-mingw*.
> 
> Should it be changed to "setup_xfail aarch64-*-pe* sh-*-*"?

Or should aarch64-pe build dlltool?  I'm not sure and intend to leave
that decision to the aarch64 maintainers.
  

Patch

diff --git a/bfd/coff-aarch64.c b/bfd/coff-aarch64.c
index d889359a6fc..a8e2b37b14a 100644
--- a/bfd/coff-aarch64.c
+++ b/bfd/coff-aarch64.c
@@ -521,6 +521,39 @@  coff_pe_aarch64_relocate_section (bfd *output_bfd,
 
       h = obj_coff_sym_hashes (input_bfd)[symndx];
 
+      if (h && h->root.type == bfd_link_hash_defweak)
+	switch (rel->r_type)
+	  {
+	  case IMAGE_REL_ARM64_PAGEOFFSET_12A:
+	  case IMAGE_REL_ARM64_PAGEOFFSET_12L:
+	    {
+	      rel->r_ignore = 1;
+	      continue;
+	    }
+	  case IMAGE_REL_ARM64_BRANCH26:
+	    {
+	      const unsigned char prefix_length = sizeof("__imp_") - 1;
+	      h = (struct coff_link_hash_entry*)
+		  bfd_link_hash_lookup (info->hash, h->root.root.string
+					+ prefix_length, 0, 0, 1);
+	      break;
+	    }
+	  case IMAGE_REL_ARM64_PAGEBASE_REL21:
+	    {
+	      uint32_t opcode = 0x14000000; /* b <label>.  */
+	      bfd_putl32 (opcode, contents + rel->r_vaddr);
+	      rel->r_type = IMAGE_REL_ARM64_BRANCH26;
+
+	      char* imp_label = xasprintf("%s_%x_%lx", h->root.root.string,
+					  input_section->id,
+					  (long unsigned) rel->r_vaddr);
+	      h = (struct coff_link_hash_entry*)
+		  bfd_link_hash_lookup (info->hash, imp_label, 0, 0, 1);
+	      free(imp_label);
+	      break;
+	    }
+	  }
+
       if (h && h->root.type == bfd_link_hash_defined)
 	{
 	  sec = h->root.u.def.section;
diff --git a/ld/emultempl/pep.em b/ld/emultempl/pep.em
index a20b5c7dc0d..de462cf3f02 100644
--- a/ld/emultempl/pep.em
+++ b/ld/emultempl/pep.em
@@ -1021,6 +1021,7 @@  gld${EMULATION_NAME}_after_parse (void)
 }
 
 #ifdef DLL_SUPPORT
+#if !defined (COFF_WITH_peAArch64)
 static bfd_vma
 read_addend (arelent *rel, asection *s)
 {
@@ -1072,6 +1073,7 @@  read_addend (arelent *rel, asection *s)
 	   s->owner, s, rel->address);
   return addend;
 }
+#endif
 
 static void
 make_import_fixup (arelent *rel, asection *s, char *name, const char *symname)
@@ -1083,7 +1085,11 @@  make_import_fixup (arelent *rel, asection *s, char *name, const char *symname)
     printf ("arelent: %s@%#lx: add=%li\n", sym->name,
 	    (unsigned long) rel->address, (long) rel->addend);
 
+#if defined (COFF_WITH_peAArch64)
+  addend = 0;
+#else
   addend = read_addend (rel, s);
+#endif
 
   if (pep_dll_extra_pe_debug)
     {
diff --git a/ld/pe-dll.c b/ld/pe-dll.c
index f43bd14896e..3963002c581 100644
--- a/ld/pe-dll.c
+++ b/ld/pe-dll.c
@@ -2842,10 +2842,185 @@  pe_create_runtime_relocator_reference (bfd *parent)
   return abfd;
 }
 
+static void
+aarch64_make_jump_stub(const char* symbol_name, bfd* parent)
+{
+  static struct bfd_hash_table *stub_hash = NULL;
+  if (!stub_hash)
+    {
+      stub_hash = (struct bfd_hash_table *) xmalloc
+		  (sizeof (struct bfd_hash_table));
+      bfd_hash_table_init (stub_hash, bfd_hash_newfunc,
+			  sizeof (struct bfd_hash_entry));
+    }
+
+  if (pe_dll_extra_pe_debug)
+    printf ("searching for existing jump stub %s\n", symbol_name);
+
+  if (bfd_hash_lookup(stub_hash, symbol_name, false, false))
+    return;
+
+  if (pe_dll_extra_pe_debug)
+    printf ("creating jump stub for %s\n", symbol_name);
+  bfd_hash_lookup(stub_hash, symbol_name, true, true);
+
+  static unsigned tmp_stub_seq = 0;
+  char *oname = xasprintf ("jump_stub_d%06d.o", tmp_stub_seq);
+  ++tmp_stub_seq;
+
+  bfd *abfd = bfd_create (oname, parent);
+  free (oname);
+  bfd_make_writable (abfd);
+
+  bfd_set_format (abfd, bfd_object);
+  bfd_set_arch_mach (abfd, pe_details->bfd_arch, 0);
+
+  symptr = 0;
+  symtab = xmalloc (4 * sizeof (asymbol *));
+
+  asection *tx  = quick_section (abfd, ".text", SEC_CODE | SEC_HAS_CONTENTS
+				 | SEC_READONLY, 2);
+  quick_symbol (abfd, "", symbol_name, "", tx, BSF_GLOBAL, 0);
+  quick_symbol (abfd, "__imp_", symbol_name, "", bfd_und_section_ptr,
+		BSF_GLOBAL, 0);
+
+  const unsigned jmp_byte_count = sizeof (jmp_aarch64_bytes);
+  bfd_set_section_size (tx, jmp_byte_count);
+  unsigned char *td = xmalloc (jmp_byte_count);
+  tx->contents = td;
+  memcpy (td, jmp_aarch64_bytes, jmp_byte_count);
+
+  quick_reloc (abfd, 0, BFD_RELOC_AARCH64_ADR_HI21_PCREL, 2);
+  quick_reloc (abfd, 4, BFD_RELOC_AARCH64_ADD_LO12, 2);
+  save_relocs (tx);
+
+  bfd_set_symtab (abfd, symtab, symptr);
+
+  bfd_set_section_contents (abfd, tx, td, 0, jmp_byte_count);
+  bfd_make_readable (abfd);
+  add_bfd_to_link (abfd, bfd_get_filename (abfd), &link_info);
+}
+
+static void
+aarch64_make_imp_offset(const char* imp_symbol_name,
+			const char* imp_offset_name, const int offset,
+			unsigned rd, const char* caller_label, bfd* parent)
+{
+  /* offset should be an adrp immediate size.  */
+  if (offset >= (1 << 20) || offset < -(1 << 20))
+    einfo (_("%X%P: offset out of range: %d\n"), offset);
+
+  if (pe_dll_extra_pe_debug)
+    printf("symbol: %s imp_offset_name: %s offset: %u rs: %u caller: %s\n",
+	   imp_symbol_name, imp_offset_name, offset, rd, caller_label);
+
+  static const unsigned char imp_offset_bytes[] =
+    {
+      0x00, 0x00, 0x00, 0x90, /* adrp x0, <imp_symbol_name>		*/
+      0x00, 0x00, 0x40, 0xf9, /* ldr x0, [x0, :lo12:<imp_symbol_name>]	*/
+      0x00, 0x00, 0x40, 0x91, /* add x0, x0, 0, lsl 12			*/
+      0x01, 0x00, 0x00, 0x14  /* b <caller_label> + 4			*/
+    };
+
+  static unsigned tmp_stub_seq = 0;
+  char *oname = xasprintf ("imp_offset_stub_d%06d.o", tmp_stub_seq);
+  ++tmp_stub_seq;
+
+  bfd *abfd = bfd_create (oname, parent);
+  free (oname);
+  bfd_make_writable (abfd);
+
+  bfd_set_format (abfd, bfd_object);
+  bfd_set_arch_mach (abfd, pe_details->bfd_arch, 0);
+
+  symptr = 0;
+  symtab = xmalloc (5 * sizeof (asymbol *));
+
+  asection *tx = quick_section (abfd, ".text", SEC_CODE | SEC_HAS_CONTENTS
+				| SEC_READONLY, 2);
+  quick_symbol (abfd, "", imp_offset_name, "", tx, BSF_GLOBAL, 0);
+  quick_symbol (abfd, "", imp_symbol_name, "", UNDSEC, BSF_GLOBAL, 0);
+  quick_symbol (abfd, "", caller_label, "", UNDSEC, BSF_GLOBAL, 0);
+
+  const unsigned imp_offset_byte_count = sizeof (imp_offset_bytes);
+  bfd_set_section_size (tx, imp_offset_byte_count);
+  uint32_t *td = xmalloc (imp_offset_byte_count);
+  tx->contents = (bfd_byte*) td;
+  memcpy (td, imp_offset_bytes, imp_offset_byte_count);
+
+  rd &= (1 << 5) - 1;
+  td[0] |= rd;
+  td[1] |= (rd << 5) | rd;
+
+  /* The bottom 12 bits are handled outside the thunk.  */
+  unsigned imm = offset & -4096;
+  if (offset < 0)
+  {
+    /* Change ADD (immediate) to SUB (immediate).  */
+    td[2] |= 1 << 30;
+    imm = -imm;
+  }
+  imm >>= 12;
+  td[2] |= (rd << 5) | rd;
+  td[2] |= imm << 10;
+
+  quick_reloc (abfd, 0, BFD_RELOC_AARCH64_ADR_HI21_PCREL, 2);
+  quick_reloc (abfd, 4, BFD_RELOC_AARCH64_LDST64_LO12, 2);
+  quick_reloc (abfd, 12, BFD_RELOC_AARCH64_CALL26, 3);
+  save_relocs (tx);
+
+  bfd_set_symtab (abfd, symtab, symptr);
+
+  bfd_set_section_contents (abfd, tx, td, 0, imp_offset_byte_count);
+  bfd_make_readable (abfd);
+  add_bfd_to_link (abfd, bfd_get_filename (abfd), &link_info);
+}
+
 void
 pe_create_import_fixup (arelent *rel, asection *s, bfd_vma addend, char *name,
 			const char *symname)
 {
+ if (pe_details->pe_arch == PE_ARCH_aarch64)
+   {
+      if (rel->howto->bitsize == 12)
+	/* :lo12: bits are used within the replacement for the adrp
+	   instruction.  */
+	return;
+      else if (rel->howto->bitsize == 26)
+	{
+	  /* On AArch64, a single opcode is not sufficient for relocation
+	     in dynamic linking. The linker generates a jump stub instead.  */
+	  aarch64_make_jump_stub(name, s->owner);
+	  return;
+	}
+      else if (rel->howto->bitsize == 21)
+	{
+	  uint32_t opcode;
+	  if (!bfd_get_section_contents (s->owner, s, &opcode,
+					 rel->address, 4))
+	    return;
+
+	  int32_t imm = (opcode >> 5) & ((1 << 19) - 1);
+	  imm <<= 2;
+	  imm |= (opcode >> 29) & ((1 << 2) - 1);
+	  if (imm & (1 << 20))
+	    imm -= (1 << 21);
+
+	  char *imp_offset_label = xasprintf("__imp_%s", name);
+	  char *imp_label = xasprintf("%s_%x_%lx", imp_offset_label,
+				      current_sec->id,
+				      (long unsigned) rel->address);
+	  const char *fixup_name = make_import_fixup_mark (rel, name);
+	  unsigned rd = opcode & ((1 << 5) - 1);
+	  aarch64_make_imp_offset(imp_offset_label, imp_label, imm, rd,
+				  fixup_name, s->owner);
+
+	  free(imp_label);
+	  free(imp_offset_label);
+	  return;
+	}
+   }
+
   const char *fixup_name = make_import_fixup_mark (rel, name);
   bfd *b;
 
diff --git a/ld/testsuite/ld-pe/auto-import-aarch64.d b/ld/testsuite/ld-pe/auto-import-aarch64.d
new file mode 100644
index 00000000000..71f26128f73
--- /dev/null
+++ b/ld/testsuite/ld-pe/auto-import-aarch64.d
@@ -0,0 +1,293 @@ 
+
+.*:     file format pei-aarch64-little
+
+
+Disassembly of section .text:
+
+0000000140001000 <.*>:
+   140001000:	1400003e 	b	1400010f8 <__imp_dyn_v_10_0>
+   140001004:	91000000 	add	x0, x0, #0x0
+
+0000000140001008 <__fu1_dyn_v>:
+   140001008:	14000040 	b	140001108 <__imp_dyn_v_10_8>
+   14000100c:	f9400000 	ldr	x0, \[x0\]
+
+0000000140001010 <__fu2_dyn_v>:
+   140001010:	14000042 	b	140001118 <__imp_dyn_v_10_10>
+   140001014:	91000800 	add	x0, x0, #0x2
+
+0000000140001018 <__fu3_dyn_v>:
+   140001018:	14000044 	b	140001128 <__imp_dyn_v_10_18>
+   14000101c:	f9400400 	ldr	x0, \[x0, #8\]
+
+0000000140001020 <__fu4_dyn_v>:
+   140001020:	14000046 	b	140001138 <__imp_dyn_v_10_20>
+   140001024:	913ffc00 	add	x0, x0, #0xfff
+
+0000000140001028 <__fu5_dyn_v>:
+   140001028:	14000048 	b	140001148 <__imp_dyn_v_10_28>
+   14000102c:	f947fc00 	ldr	x0, \[x0, #4088\]
+
+0000000140001030 <__fu6_dyn_v>:
+   140001030:	1400004a 	b	140001158 <__imp_dyn_v_10_30>
+   140001034:	91000000 	add	x0, x0, #0x0
+
+0000000140001038 <__fu7_dyn_v>:
+   140001038:	1400004c 	b	140001168 <__imp_dyn_v_10_38>
+   14000103c:	f9400000 	ldr	x0, \[x0\]
+
+0000000140001040 <__fu8_dyn_v>:
+   140001040:	1400004e 	b	140001178 <__imp_dyn_v_10_40>
+   140001044:	912eb000 	add	x0, x0, #0xbac
+
+0000000140001048 <__fu9_dyn_v>:
+   140001048:	14000050 	b	140001188 <__imp_dyn_v_10_48>
+   14000104c:	f945d800 	ldr	x0, \[x0, #2992\]
+
+0000000140001050 <__fu10_dyn_v>:
+   140001050:	14000052 	b	140001198 <__imp_dyn_v_10_50>
+   140001054:	913ffc00 	add	x0, x0, #0xfff
+
+0000000140001058 <__fu11_dyn_v>:
+   140001058:	14000054 	b	1400011a8 <__imp_dyn_v_10_58>
+   14000105c:	f947fc00 	ldr	x0, \[x0, #4088\]
+
+0000000140001060 <__fu12_dyn_v>:
+   140001060:	14000056 	b	1400011b8 <__imp_dyn_v_10_60>
+   140001064:	913ff800 	add	x0, x0, #0xffe
+
+0000000140001068 <__fu13_dyn_v>:
+   140001068:	14000058 	b	1400011c8 <__imp_dyn_v_10_68>
+   14000106c:	f947fc00 	ldr	x0, \[x0, #4088\]
+
+0000000140001070 <__fu14_dyn_v>:
+   140001070:	1400005a 	b	1400011d8 <__imp_dyn_v_10_70>
+   140001074:	91000400 	add	x0, x0, #0x1
+
+0000000140001078 <__fu15_dyn_v>:
+   140001078:	1400005c 	b	1400011e8 <__imp_dyn_v_10_78>
+   14000107c:	f9400400 	ldr	x0, \[x0, #8\]
+
+0000000140001080 <__fu16_dyn_v>:
+   140001080:	1400005e 	b	1400011f8 <__imp_dyn_v_10_80>
+   140001084:	91000000 	add	x0, x0, #0x0
+
+0000000140001088 <__fu17_dyn_v>:
+   140001088:	14000060 	b	140001208 <__imp_dyn_v_10_88>
+   14000108c:	f9400000 	ldr	x0, \[x0\]
+
+0000000140001090 <__fu18_dyn_v>:
+   140001090:	14000062 	b	140001218 <__imp_dyn_v_10_90>
+   140001094:	91115000 	add	x0, x0, #0x454
+
+0000000140001098 <__fu19_dyn_v>:
+   140001098:	14000064 	b	140001228 <__imp_dyn_v_10_98>
+   14000109c:	f9422800 	ldr	x0, \[x0, #1104\]
+
+00000001400010a0 <__fu20_dyn_v>:
+   1400010a0:	14000066 	b	140001238 <__imp_dyn_v_10_a0>
+   1400010a4:	91000000 	add	x0, x0, #0x0
+
+00000001400010a8 <__fu21_dyn_v>:
+   1400010a8:	14000068 	b	140001248 <__imp_dyn_v_10_a8>
+   1400010ac:	f9400000 	ldr	x0, \[x0\]
+
+00000001400010b0 <__fu22_dyn_v>:
+   1400010b0:	1400006a 	b	140001258 <__imp_dyn_v_10_b0>
+   1400010b4:	91000821 	add	x1, x1, #0x2
+
+00000001400010b8 <__fu23_dyn_v>:
+   1400010b8:	1400006c 	b	140001268 <__imp_dyn_v_10_b8>
+   1400010bc:	f9400421 	ldr	x1, \[x1, #8\]
+
+00000001400010c0 <__fu24_dyn_v>:
+   1400010c0:	1400006e 	b	140001278 <__imp_dyn_v_10_c0>
+   1400010c4:	91000820 	add	x0, x1, #0x2
+
+00000001400010c8 <__fu25_dyn_v>:
+   1400010c8:	14000070 	b	140001288 <__imp_dyn_v_10_c8>
+   1400010cc:	f9400420 	ldr	x0, \[x1, #8\]
+
+00000001400010d0 <__fu26_dyn_v>:
+   1400010d0:	14000072 	b	140001298 <__imp_dyn_v_10_d0>
+   1400010d4:	913ff821 	add	x1, x1, #0xffe
+
+00000001400010d8 <__fu27_dyn_v>:
+   1400010d8:	14000074 	b	1400012a8 <__imp_dyn_v_10_d8>
+   1400010dc:	f947fc21 	ldr	x1, \[x1, #4088\]
+   1400010e0:	14000002 	b	1400010e8 <init_dyn_v>
+   1400010e4:	94000001 	bl	1400010e8 <init_dyn_v>
+
+00000001400010e8 <init_dyn_v>:
+   1400010e8:	d0000010 	adrp	x16, 140003000 <.*>
+   1400010ec:	91012210 	add	x16, x16, #0x48
+   1400010f0:	f9400210 	ldr	x16, \[x16\]
+   1400010f4:	d61f0200 	br	x16
+
+00000001400010f8 <__imp_dyn_v_10_0>:
+   1400010f8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400010fc:	f9402000 	ldr	x0, \[x0, #64\]
+   140001100:	91400000 	add	x0, x0, #0x0, lsl #12
+   140001104:	17ffffc0 	b	140001004 <.*\+0x4>
+
+0000000140001108 <__imp_dyn_v_10_8>:
+   140001108:	d0000000 	adrp	x0, 140003000 <.*>
+   14000110c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001110:	91400000 	add	x0, x0, #0x0, lsl #12
+   140001114:	17ffffbe 	b	14000100c <__fu1_dyn_v\+0x4>
+
+0000000140001118 <__imp_dyn_v_10_10>:
+   140001118:	d0000000 	adrp	x0, 140003000 <.*>
+   14000111c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001120:	91400000 	add	x0, x0, #0x0, lsl #12
+   140001124:	17ffffbc 	b	140001014 <__fu2_dyn_v\+0x4>
+
+0000000140001128 <__imp_dyn_v_10_18>:
+   140001128:	d0000000 	adrp	x0, 140003000 <.*>
+   14000112c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001130:	91400000 	add	x0, x0, #0x0, lsl #12
+   140001134:	17ffffba 	b	14000101c <__fu3_dyn_v\+0x4>
+
+0000000140001138 <__imp_dyn_v_10_20>:
+   140001138:	d0000000 	adrp	x0, 140003000 <.*>
+   14000113c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001140:	91400000 	add	x0, x0, #0x0, lsl #12
+   140001144:	17ffffb8 	b	140001024 <__fu4_dyn_v\+0x4>
+
+0000000140001148 <__imp_dyn_v_10_28>:
+   140001148:	d0000000 	adrp	x0, 140003000 <.*>
+   14000114c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001150:	91400000 	add	x0, x0, #0x0, lsl #12
+   140001154:	17ffffb6 	b	14000102c <__fu5_dyn_v\+0x4>
+
+0000000140001158 <__imp_dyn_v_10_30>:
+   140001158:	d0000000 	adrp	x0, 140003000 <.*>
+   14000115c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001160:	91400400 	add	x0, x0, #0x1, lsl #12
+   140001164:	17ffffb4 	b	140001034 <__fu6_dyn_v\+0x4>
+
+0000000140001168 <__imp_dyn_v_10_38>:
+   140001168:	d0000000 	adrp	x0, 140003000 <.*>
+   14000116c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001170:	91400400 	add	x0, x0, #0x1, lsl #12
+   140001174:	17ffffb2 	b	14000103c <__fu7_dyn_v\+0x4>
+
+0000000140001178 <__imp_dyn_v_10_40>:
+   140001178:	d0000000 	adrp	x0, 140003000 <.*>
+   14000117c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001180:	91436c00 	add	x0, x0, #0xdb, lsl #12
+   140001184:	17ffffb0 	b	140001044 <__fu8_dyn_v\+0x4>
+
+0000000140001188 <__imp_dyn_v_10_48>:
+   140001188:	d0000000 	adrp	x0, 140003000 <.*>
+   14000118c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001190:	91436c00 	add	x0, x0, #0xdb, lsl #12
+   140001194:	17ffffae 	b	14000104c <__fu9_dyn_v\+0x4>
+
+0000000140001198 <__imp_dyn_v_10_50>:
+   140001198:	d0000000 	adrp	x0, 140003000 <.*>
+   14000119c:	f9402000 	ldr	x0, \[x0, #64\]
+   1400011a0:	9143fc00 	add	x0, x0, #0xff, lsl #12
+   1400011a4:	17ffffac 	b	140001054 <__fu10_dyn_v\+0x4>
+
+00000001400011a8 <__imp_dyn_v_10_58>:
+   1400011a8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400011ac:	f9402000 	ldr	x0, \[x0, #64\]
+   1400011b0:	9143fc00 	add	x0, x0, #0xff, lsl #12
+   1400011b4:	17ffffaa 	b	14000105c <__fu11_dyn_v\+0x4>
+
+00000001400011b8 <__imp_dyn_v_10_60>:
+   1400011b8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400011bc:	f9402000 	ldr	x0, \[x0, #64\]
+   1400011c0:	d1400400 	sub	x0, x0, #0x1, lsl #12
+   1400011c4:	17ffffa8 	b	140001064 <__fu12_dyn_v\+0x4>
+
+00000001400011c8 <__imp_dyn_v_10_68>:
+   1400011c8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400011cc:	f9402000 	ldr	x0, \[x0, #64\]
+   1400011d0:	d1400400 	sub	x0, x0, #0x1, lsl #12
+   1400011d4:	17ffffa6 	b	14000106c <__fu13_dyn_v\+0x4>
+
+00000001400011d8 <__imp_dyn_v_10_70>:
+   1400011d8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400011dc:	f9402000 	ldr	x0, \[x0, #64\]
+   1400011e0:	d1400400 	sub	x0, x0, #0x1, lsl #12
+   1400011e4:	17ffffa4 	b	140001074 <__fu14_dyn_v\+0x4>
+
+00000001400011e8 <__imp_dyn_v_10_78>:
+   1400011e8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400011ec:	f9402000 	ldr	x0, \[x0, #64\]
+   1400011f0:	d1400400 	sub	x0, x0, #0x1, lsl #12
+   1400011f4:	17ffffa2 	b	14000107c <__fu15_dyn_v\+0x4>
+
+00000001400011f8 <__imp_dyn_v_10_80>:
+   1400011f8:	d0000000 	adrp	x0, 140003000 <.*>
+   1400011fc:	f9402000 	ldr	x0, \[x0, #64\]
+   140001200:	d1400400 	sub	x0, x0, #0x1, lsl #12
+   140001204:	17ffffa0 	b	140001084 <__fu16_dyn_v\+0x4>
+
+0000000140001208 <__imp_dyn_v_10_88>:
+   140001208:	d0000000 	adrp	x0, 140003000 <.*>
+   14000120c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001210:	d1400400 	sub	x0, x0, #0x1, lsl #12
+   140001214:	17ffff9e 	b	14000108c <__fu17_dyn_v\+0x4>
+
+0000000140001218 <__imp_dyn_v_10_90>:
+   140001218:	d0000000 	adrp	x0, 140003000 <.*>
+   14000121c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001220:	d1437000 	sub	x0, x0, #0xdc, lsl #12
+   140001224:	17ffff9c 	b	140001094 <__fu18_dyn_v\+0x4>
+
+0000000140001228 <__imp_dyn_v_10_98>:
+   140001228:	d0000000 	adrp	x0, 140003000 <.*>
+   14000122c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001230:	d1437000 	sub	x0, x0, #0xdc, lsl #12
+   140001234:	17ffff9a 	b	14000109c <__fu19_dyn_v\+0x4>
+
+0000000140001238 <__imp_dyn_v_10_a0>:
+   140001238:	d0000000 	adrp	x0, 140003000 <.*>
+   14000123c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001240:	d1440000 	sub	x0, x0, #0x100, lsl #12
+   140001244:	17ffff98 	b	1400010a4 <__fu20_dyn_v\+0x4>
+
+0000000140001248 <__imp_dyn_v_10_a8>:
+   140001248:	d0000000 	adrp	x0, 140003000 <.*>
+   14000124c:	f9402000 	ldr	x0, \[x0, #64\]
+   140001250:	d1440000 	sub	x0, x0, #0x100, lsl #12
+   140001254:	17ffff96 	b	1400010ac <__fu21_dyn_v\+0x4>
+
+0000000140001258 <__imp_dyn_v_10_b0>:
+   140001258:	d0000001 	adrp	x1, 140003000 <.*>
+   14000125c:	f9402021 	ldr	x1, \[x1, #64\]
+   140001260:	91400021 	add	x1, x1, #0x0, lsl #12
+   140001264:	17ffff94 	b	1400010b4 <__fu22_dyn_v\+0x4>
+
+0000000140001268 <__imp_dyn_v_10_b8>:
+   140001268:	d0000001 	adrp	x1, 140003000 <.*>
+   14000126c:	f9402021 	ldr	x1, \[x1, #64\]
+   140001270:	91400021 	add	x1, x1, #0x0, lsl #12
+   140001274:	17ffff92 	b	1400010bc <__fu23_dyn_v\+0x4>
+
+0000000140001278 <__imp_dyn_v_10_c0>:
+   140001278:	d0000001 	adrp	x1, 140003000 <.*>
+   14000127c:	f9402021 	ldr	x1, \[x1, #64\]
+   140001280:	91400021 	add	x1, x1, #0x0, lsl #12
+   140001284:	17ffff90 	b	1400010c4 <__fu24_dyn_v\+0x4>
+
+0000000140001288 <__imp_dyn_v_10_c8>:
+   140001288:	d0000001 	adrp	x1, 140003000 <.*>
+   14000128c:	f9402021 	ldr	x1, \[x1, #64\]
+   140001290:	91400021 	add	x1, x1, #0x0, lsl #12
+   140001294:	17ffff8e 	b	1400010cc <__fu25_dyn_v\+0x4>
+
+0000000140001298 <__imp_dyn_v_10_d0>:
+   140001298:	d0000001 	adrp	x1, 140003000 <.*>
+   14000129c:	f9402021 	ldr	x1, \[x1, #64\]
+   1400012a0:	d1400421 	sub	x1, x1, #0x1, lsl #12
+   1400012a4:	17ffff8c 	b	1400010d4 <__fu26_dyn_v\+0x4>
+
+00000001400012a8 <__imp_dyn_v_10_d8>:
+   1400012a8:	d0000001 	adrp	x1, 140003000 <.*>
+   1400012ac:	f9402021 	ldr	x1, \[x1, #64\]
+   1400012b0:	d1400421 	sub	x1, x1, #0x1, lsl #12
+   1400012b4:	17ffff8a 	b	1400010dc <__fu27_dyn_v\+0x4>
diff --git a/ld/testsuite/ld-pe/auto-import-aarch64.s b/ld/testsuite/ld-pe/auto-import-aarch64.s
new file mode 100644
index 00000000000..3217ac4bf3a
--- /dev/null
+++ b/ld/testsuite/ld-pe/auto-import-aarch64.s
@@ -0,0 +1,89 @@ 
+	.text
+	.global main
+main:
+	adrp	x0, dyn_v
+	add	x0, x0, :lo12:dyn_v
+
+	adrp	x0, dyn_v
+	ldr	x0, [x0, :lo12:dyn_v]
+
+	adrp	x0, dyn_v + 2
+	add	x0, x0, :lo12:dyn_v + 2
+
+	adrp	x0, dyn_v + 8
+	ldr	x0, [x0, :lo12:dyn_v + 8]
+
+	adrp	x0, dyn_v + 4095
+	add	x0, x0, :lo12:dyn_v + 4095
+
+	adrp	x0, dyn_v + 4088
+	ldr	x0, [x0, :lo12:dyn_v + 4088]
+
+	adrp	x0, dyn_v + 4096
+	add	x0, x0, :lo12:dyn_v + 4096
+
+	adrp	x0, dyn_v + 4096
+	ldr	x0, [x0, :lo12:dyn_v + 4096]
+
+	adrp	x0, dyn_v + 900012
+	add	x0, x0, :lo12:dyn_v + 900012
+
+	adrp	x0, dyn_v + 900016
+	ldr	x0, [x0, :lo12:dyn_v + 900016]
+
+	adrp	x0, dyn_v + 1048575
+	add	x0, x0, :lo12:dyn_v + 1048575
+
+	adrp	x0, dyn_v + 1048568
+	ldr	x0, [x0, :lo12:dyn_v + 1048568]
+
+	adrp	x0, dyn_v - 2
+	add	x0, x0, :lo12:dyn_v - 2
+
+	adrp	x0, dyn_v - 8
+	ldr	x0, [x0, :lo12:dyn_v - 8]
+
+	adrp	x0, dyn_v - 4095
+	add	x0, x0, :lo12:dyn_v - 4095
+
+	adrp	x0, dyn_v - 4088
+	ldr	x0, [x0, :lo12:dyn_v - 4088]
+
+	adrp	x0, dyn_v - 4096
+	add	x0, x0, :lo12:dyn_v - 4096
+
+	adrp	x0, dyn_v - 4096
+	ldr	x0, [x0, :lo12:dyn_v - 4096]
+
+	adrp	x0, dyn_v - 900012
+	add	x0, x0, :lo12:dyn_v - 900012
+
+	adrp	x0, dyn_v - 900016
+	ldr	x0, [x0, :lo12:dyn_v - 900016]
+
+	adrp	x0, dyn_v - 1048576
+	add	x0, x0, :lo12:dyn_v - 1048576
+
+	adrp	x0, dyn_v - 1048576
+	ldr	x0, [x0, :lo12:dyn_v - 1048576]
+
+	adrp	x1, dyn_v + 2
+	add	x1, x1, :lo12:dyn_v + 2
+
+	adrp	x1, dyn_v + 8
+	ldr	x1, [x1, :lo12:dyn_v + 8]
+
+	adrp	x1, dyn_v + 2
+	add	x0, x1, :lo12:dyn_v + 2
+
+	adrp	x1, dyn_v + 8
+	ldr	x0, [x1, :lo12:dyn_v + 8]
+
+	adrp	x1, dyn_v - 2
+	add	x1, x1, :lo12:dyn_v - 2
+
+	adrp	x1, dyn_v - 8
+	ldr	x1, [x1, :lo12:dyn_v - 8]
+
+	b init_dyn_v
+	bl init_dyn_v
diff --git a/ld/testsuite/ld-pe/auto-import-dll-aarch64.s b/ld/testsuite/ld-pe/auto-import-dll-aarch64.s
new file mode 100644
index 00000000000..dc9314273fa
--- /dev/null
+++ b/ld/testsuite/ld-pe/auto-import-dll-aarch64.s
@@ -0,0 +1,13 @@ 
+	.data
+	.global	dyn_v
+	.align	3
+	.def	dyn_v;	.scl	2;	.type	0;	.endef
+dyn_v:
+	.space	8
+
+	.text
+	.align	2
+	.global	init_dyn_v
+	.def	init_dyn_v;	.scl	2;	.type	32;	.endef
+init_dyn_v:
+	ret
diff --git a/ld/testsuite/ld-pe/pe.exp b/ld/testsuite/ld-pe/pe.exp
index b95f61b8578..53b04bb6757 100644
--- a/ld/testsuite/ld-pe/pe.exp
+++ b/ld/testsuite/ld-pe/pe.exp
@@ -121,6 +121,17 @@  if {[istarget "aarch64-*-pe*"] || [istarget "aarch64-*-mingw*"]} {
     }
 
     run_ld_link_tests $pe_tests
+
+    set auto_import_aarch64_test "auto-import-aarch64"
+    if { [ld_assemble $as $srcdir/$subdir/auto-import-dll-aarch64.s tmpdir/auto-import-dll-aarch64.o]
+	 && [ld_assemble $as $srcdir/$subdir/auto-import-aarch64.s tmpdir/auto-import-aarch64.o]
+	 && [ld_link $ld "tmpdir/auto-import-dll.dll" "-shared tmpdir/auto-import-dll-aarch64.o --out-implib tmpdir/libauto-import-dll-aarch64.a"]
+	 && [ld_link $ld "tmpdir/auto-import-aarch64.exe" "tmpdir/auto-import-aarch64.o tmpdir/libauto-import-dll-aarch64.a"]
+	 && [regexp [file_contents "$srcdir/$subdir/auto-import-aarch64.d"] [run_host_cmd "$objdump" "-d tmpdir/auto-import-aarch64.exe"]] } {
+	pass $auto_import_aarch64_test
+    } else {
+	fail $auto_import_aarch64_test
+    }
 }
 
 run_dump_test "image_size"
@@ -185,10 +196,10 @@  if [check_shared_lib_support] {
     }
     run_dump_test "symbols-ordinals-hints-exports-ld"
     # no dlltool for these two
-    setup_xfail aarch64-*-* sh-*-*
+    setup_xfail sh-*-*
     run_dump_test "symbols-ordinals-hints-exports-dlltool"
     run_dump_test "symbols-ordinals-hints-imports-ld"
-    setup_xfail aarch64-*-* sh-*-*
+    setup_xfail sh-*-*
     run_dump_test "symbols-ordinals-hints-imports-dlltool"
     set ASFLAGS $old_ASFLAGS
 }