[PATCH] AArch64: Optimize zero-valued ADRP relocations for undefined weak symbols

Sivan Shani sivan.shani@arm.com
Wed Sep 9 16:05:55 GMT 2026


AAELF64 permits R_<CLS>_ADR_PREL_PG_HI21 relocations against
undefined weak symbols to emit MOV with a zero immediate.

When the calculated page value is zero, rewrite the ADRP as MOVZ while
preserving its destination register.  Honor --no-relax and restrict the
optimization to genuine undefined weak symbols.

An ADRP may already be recorded as a Cortex-A53 erratum 843419 candidate
before being rewritten.  Clear the pending erratum entry for both adr
and full fix modes, since the instruction is no longer an ADRP.

Add LP64 and ILP32 tests, including nonzero-addend, --no-relax, and
Cortex-A53 erratum 843419 coverage.

bfd/ChangeLog:

        * elfnn-aarch64.c (AARCH64_MOVZ_ZERO_OPCODE):
        (rewrite_adrp_as_mov_zero):
        (clear_erratum_843419_entry):
        (elfNN_aarch64_final_link_relocate):
        (_bfd_aarch64_erratum_843419_clear_stub):

ld/ChangeLog:

        * testsuite/ld-aarch64/aarch64-elf.exp:
        * testsuite/ld-aarch64/weak-undefined.d:
        * testsuite/ld-aarch64/weak-undefined-adrp-erratum-adr.d: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp-erratum-full.d: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp-erratum.s: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp-ilp32.d: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp-norelax-ilp32.d: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp-norelax.d: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp.d: New test.
        * testsuite/ld-aarch64/weak-undefined-adrp.s: New test.
---
 bfd/elfnn-aarch64.c                           | 42 +++++++++++++++++--
 ld/testsuite/ld-aarch64/aarch64-elf.exp       |  6 +++
 .../weak-undefined-adrp-erratum-adr.d         | 15 +++++++
 .../weak-undefined-adrp-erratum-full.d        | 15 +++++++
 .../ld-aarch64/weak-undefined-adrp-erratum.s  | 10 +++++
 .../ld-aarch64/weak-undefined-adrp-ilp32.d    | 17 ++++++++
 .../weak-undefined-adrp-norelax-ilp32.d       | 17 ++++++++
 .../ld-aarch64/weak-undefined-adrp-norelax.d  | 16 +++++++
 ld/testsuite/ld-aarch64/weak-undefined-adrp.d | 16 +++++++
 ld/testsuite/ld-aarch64/weak-undefined-adrp.s | 10 +++++
 ld/testsuite/ld-aarch64/weak-undefined.d      |  2 +-
 11 files changed, 161 insertions(+), 5 deletions(-)
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-adr.d
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-full.d
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum.s
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp-ilp32.d
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax-ilp32.d
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax.d
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp.d
 create mode 100644 ld/testsuite/ld-aarch64/weak-undefined-adrp.s

diff --git a/bfd/elfnn-aarch64.c b/bfd/elfnn-aarch64.c
index e7693da0d95..5ff41b4bd04 100644
--- a/bfd/elfnn-aarch64.c
+++ b/bfd/elfnn-aarch64.c
@@ -5739,6 +5739,23 @@ aarch64_relocation_aginst_gp_p (bfd_reloc_code_real_type reloc)
 	  || reloc == BFD_RELOC_AARCH64_MOVW_GOTOFF_G1);
 }
 
+/* Transform an ADRP instruction to a move instruction.  */
+
+#define AARCH64_MOVZ_ZERO_OPCODE 0xd2800000U
+
+static void
+rewrite_adrp_as_mov_zero (bfd_byte *location)
+{
+  uint32_t insn = bfd_getl32 (location);
+  unsigned int rd = insn & 0x1f;
+
+  bfd_putl32 (AARCH64_MOVZ_ZERO_OPCODE | rd, location);
+}
+
+static void
+clear_erratum_843419_entry (struct elf_aarch64_link_hash_table *,
+			    bfd_vma, asection *);
+
 /* Perform a relocation as part of a final link.  The input relocation type
    should be TLS relaxed.  */
 
@@ -6554,8 +6571,25 @@ elfNN_aarch64_final_link_relocate (reloc_howto_type *howto,
   if (save_addend)
     return bfd_reloc_continue;
 
-  return _bfd_aarch64_elf_put_addend (input_bfd, hit_data, bfd_r_type,
-				      howto, value, optimize_relocations);
+  bfd_reloc_status_type status
+    = _bfd_aarch64_elf_put_addend (input_bfd, hit_data, bfd_r_type,
+				    howto, value,
+				    optimize_relocations);
+
+  if (status == bfd_reloc_ok
+      && optimize_relocations
+      && h != NULL
+      && h->root.type == bfd_link_hash_undefweak
+      && bfd_r_type == BFD_RELOC_AARCH64_ADR_HI21_PCREL
+      && value == 0)
+    {
+      rewrite_adrp_as_mov_zero (hit_data);
+      /* We have relaxed the adrp into a mov, we may have to clear any
+	 pending erratum fixes.  */
+      clear_erratum_843419_entry (globals, rel->r_offset, input_section);
+    }
+
+  return status;
 }
 
 /* LP64 and ILP32 operates on x- and w-registers respectively.
@@ -6625,7 +6659,7 @@ _bfd_aarch64_erratum_843419_clear_stub (struct bfd_hash_entry *gen_entry,
   return false;
 }
 
-/* TLS Relaxations may relax an adrp sequence that matches the erratum 843419
+/* Relaxations may replace an adrp sequence that matches the erratum 843419
    sequence.  In this case the erratum no longer applies and we need to remove
    the entry from the pending stub generation.  This clears matching adrp insn
    at ADRP_OFFSET in INPUT_SECTION in the stub table defined in GLOBALS.  */
@@ -6634,7 +6668,7 @@ static void
 clear_erratum_843419_entry (struct elf_aarch64_link_hash_table *globals,
 			    bfd_vma adrp_offset, asection *input_section)
 {
-  if (globals->fix_erratum_843419 & ERRAT_ADRP)
+  if (globals->fix_erratum_843419 & (ERRAT_ADR | ERRAT_ADRP))
     {
       struct erratum_843419_branch_to_stub_clear_data data;
       data.adrp_offset = adrp_offset;
diff --git a/ld/testsuite/ld-aarch64/aarch64-elf.exp b/ld/testsuite/ld-aarch64/aarch64-elf.exp
index f4941b01518..33ad7c8ec2e 100644
--- a/ld/testsuite/ld-aarch64/aarch64-elf.exp
+++ b/ld/testsuite/ld-aarch64/aarch64-elf.exp
@@ -66,10 +66,16 @@ run_dump_test "erratum843419-far-adr"
 run_dump_test "erratum843419-far-full"
 run_dump_test "erratum843419-full"
 run_dump_test "erratum843419-no-args"
+run_dump_test_lp64 "weak-undefined-adrp-erratum-full"
+run_dump_test_lp64 "weak-undefined-adrp-erratum-adr"
 run_dump_test "erratum835769-843419"
 
 # Relocation Tests
 run_dump_test_lp64 "weak-undefined"
+run_dump_test_lp64 "weak-undefined-adrp"
+run_dump_test_lp64 "weak-undefined-adrp-norelax"
+run_dump_test "weak-undefined-adrp-ilp32"
+run_dump_test "weak-undefined-adrp-norelax-ilp32"
 run_dump_test "emit-relocs-22"
 run_dump_test "emit-relocs-23"
 run_dump_test "emit-relocs-28"
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-adr.d b/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-adr.d
new file mode 100644
index 00000000000..04a8051713d
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-adr.d
@@ -0,0 +1,15 @@
+#source: weak-undefined-adrp-erratum.s
+#ld: -Ttext=0x400000 --fix-cortex-a53-843419=adr
+#objdump: -dr
+#...
+
+Disassembly of section \.text:
+
+0*400000 <_start>:
+	...
+  400ffc:	d2800000 	mov	x0, #0x0.*
+  401000:	f9000042 	str	x2, \[x2\]
+  401004:	d2800002 	mov	x2, #0x0.*
+  401008:	f9402001 	ldr	x1, \[x0, #64\]
+  40100c:	d503201f 	nop
+#...
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-full.d b/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-full.d
new file mode 100644
index 00000000000..f7a6f247ca3
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum-full.d
@@ -0,0 +1,15 @@
+#source: weak-undefined-adrp-erratum.s
+#ld: -Ttext=0x400000 --fix-cortex-a53-843419=full
+#objdump: -dr
+#...
+
+Disassembly of section \.text:
+
+0*400000 <_start>:
+	...
+  400ffc:	d2800000 	mov	x0, #0x0.*
+  401000:	f9000042 	str	x2, \[x2\]
+  401004:	d2800002 	mov	x2, #0x0.*
+  401008:	f9402001 	ldr	x1, \[x0, #64\]
+  40100c:	d503201f 	nop
+#...
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum.s b/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum.s
new file mode 100644
index 00000000000..995866a9bbb
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp-erratum.s
@@ -0,0 +1,10 @@
+	.balign	0x1000
+	.weak	weak_sym
+	.globl	_start
+_start:
+	.skip	0xffc
+	adrp	x0, weak_sym
+	str	x2, [x2]
+	mov	x2, #0
+	ldr	x1, [x0, #0x40]
+	nop
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp-ilp32.d b/ld/testsuite/ld-aarch64/weak-undefined-adrp-ilp32.d
new file mode 100644
index 00000000000..ae0e8570724
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp-ilp32.d
@@ -0,0 +1,17 @@
+#source: weak-undefined-adrp.s
+#as: -mabi=ilp32
+#ld: -m [aarch64_choose_ilp32_emul] -Ttext 0xF0000000 -e _start
+#objdump: -d
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+[0-9a-f]+ <_start>:
+ *[0-9a-f]+:	d2800000 	mov	x0, #0x0.*
+ *[0-9a-f]+:	d2800007 	mov	x7, #0x0.*
+ *[0-9a-f]+:	b0000004 	adrp	x4, .*
+ *[0-9a-f]+:	90000003 	adrp	x3, .*
+
+[0-9a-f]+ <defined_sym>:
+ *[0-9a-f]+:	d503201f 	nop
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax-ilp32.d b/ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax-ilp32.d
new file mode 100644
index 00000000000..c8c48621a28
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax-ilp32.d
@@ -0,0 +1,17 @@
+#source: weak-undefined-adrp.s
+#as: -mabi=ilp32
+#ld: -m [aarch64_choose_ilp32_emul] -Ttext 0xF0000000 -e _start --no-relax
+#objdump: -d
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+[0-9a-f]+ <_start>:
+ *[0-9a-f]+:	90000000 	adrp	x0, .*
+ *[0-9a-f]+:	90000007 	adrp	x7, .*
+ *[0-9a-f]+:	b0000004 	adrp	x4, .*
+ *[0-9a-f]+:	90000003 	adrp	x3, .*
+
+[0-9a-f]+ <defined_sym>:
+ *[0-9a-f]+:	d503201f 	nop
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax.d b/ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax.d
new file mode 100644
index 00000000000..2c44ca0413e
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp-norelax.d
@@ -0,0 +1,16 @@
+#source: weak-undefined-adrp.s
+#ld: -Ttext 0xF0000000 -e _start --no-relax
+#objdump: -d
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+[0-9a-f]+ <_start>:
+ *[0-9a-f]+:	90000000 	adrp	x0, .*
+ *[0-9a-f]+:	90000007 	adrp	x7, .*
+ *[0-9a-f]+:	b0000004 	adrp	x4, .*
+ *[0-9a-f]+:	90000003 	adrp	x3, .*
+
+[0-9a-f]+ <defined_sym>:
+ *[0-9a-f]+:	d503201f 	nop
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp.d b/ld/testsuite/ld-aarch64/weak-undefined-adrp.d
new file mode 100644
index 00000000000..9187c728fb5
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp.d
@@ -0,0 +1,16 @@
+#source: weak-undefined-adrp.s
+#ld: -Ttext 0xF0000000 -e _start
+#objdump: -d
+
+.*: +file format .*
+
+Disassembly of section .text:
+
+[0-9a-f]+ <_start>:
+ *[0-9a-f]+:	d2800000 	mov	x0, #0x0.*
+ *[0-9a-f]+:	d2800007 	mov	x7, #0x0.*
+ *[0-9a-f]+:	b0000004 	adrp	x4, .*
+ *[0-9a-f]+:	90000003 	adrp	x3, .*
+
+[0-9a-f]+ <defined_sym>:
+ *[0-9a-f]+:	d503201f 	nop
diff --git a/ld/testsuite/ld-aarch64/weak-undefined-adrp.s b/ld/testsuite/ld-aarch64/weak-undefined-adrp.s
new file mode 100644
index 00000000000..aca12ecb16e
--- /dev/null
+++ b/ld/testsuite/ld-aarch64/weak-undefined-adrp.s
@@ -0,0 +1,10 @@
+	.text
+	.weak	weak_sym
+	.global	_start
+_start:
+	adrp	x0, weak_sym
+	adrp	x7, weak_sym
+	adrp	x4, weak_sym + 0x1000
+	adrp	x3, defined_sym
+defined_sym:
+	nop
diff --git a/ld/testsuite/ld-aarch64/weak-undefined.d b/ld/testsuite/ld-aarch64/weak-undefined.d
index 3627d46c82b..fa4ba64e3b9 100644
--- a/ld/testsuite/ld-aarch64/weak-undefined.d
+++ b/ld/testsuite/ld-aarch64/weak-undefined.d
@@ -14,5 +14,5 @@
  +f0000024:	d503201f 	nop
  +f0000028:	58000000 	ldr	x0, f0000028 <main\+0x28>
  +f000002c:	10000000 	adr	x0, f000002c <main\+0x2c>
- +f0000030:	90000000 	adrp	x0, f0000000 <main>
+ +f0000030:	d2800000 	mov	x0, #0x0.*
  +f0000034:	d503201f 	nop
-- 
2.43.0



More information about the Binutils mailing list