[ELF] ARM and AArch64 undefined weak reference values
authorPeter Smith <peter.smith@linaro.org>
Wed, 9 Nov 2016 10:22:29 +0000 (10:22 +0000)
committerPeter Smith <peter.smith@linaro.org>
Wed, 9 Nov 2016 10:22:29 +0000 (10:22 +0000)
The ARM 32 and 64-bit ABI does not use 0 for undefined weak references
that are used in PC relative relocations. In particular:
- A branch relocation to an undefined weak resolves to the next
  instruction. Effectively making the branch a no-op
- In all other cases the symbol resolves to the place so that S + A - P
  resolves to A.

Differential Revision: https://reviews.llvm.org/D26240

llvm-svn: 286353

lld/ELF/InputSection.cpp
lld/test/ELF/aarch64-undefined-weak.s [new file with mode: 0644]
lld/test/ELF/arm-thumb-no-undefined-thunk.s
lld/test/ELF/arm-thumb-undefined-weak.s [new file with mode: 0644]
lld/test/ELF/arm-undefined-weak.s [new file with mode: 0644]

index 63f7bda..225ff07 100644 (file)
@@ -258,6 +258,42 @@ static uint64_t getAArch64Page(uint64_t Expr) {
   return Expr & (~static_cast<uint64_t>(0xFFF));
 }
 
+static uint32_t getARMUndefinedRelativeWeakVA(uint32_t Type,
+                                              uint32_t A,
+                                              uint32_t P) {
+  switch (Type) {
+  case R_ARM_THM_JUMP11:
+    return P + 2;
+  case R_ARM_CALL:
+  case R_ARM_JUMP24:
+  case R_ARM_PC24:
+  case R_ARM_PLT32:
+  case R_ARM_PREL31:
+  case R_ARM_THM_JUMP19:
+  case R_ARM_THM_JUMP24:
+    return P + 4;
+  case R_ARM_THM_CALL:
+    // We don't want an interworking BLX to ARM
+    return P + 5;
+  default:
+    return A;
+  }
+}
+
+static uint64_t getAArch64UndefinedRelativeWeakVA(uint64_t Type,
+                                                  uint64_t A,
+                                                  uint64_t P) {
+  switch (Type) {
+  case R_AARCH64_CALL26:
+  case R_AARCH64_CONDBR19:
+  case R_AARCH64_JUMP26:
+  case R_AARCH64_TSTBR14:
+    return P + 4;
+  default:
+    return A;
+  }
+}
+
 template <class ELFT>
 static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
                                     typename ELFT::uint P,
@@ -373,10 +409,20 @@ static typename ELFT::uint getSymVA(uint32_t Type, typename ELFT::uint A,
     return SymVA - P;
   }
   case R_PC:
+    if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak()) {
+      // On ARM and AArch64 a branch to an undefined weak resolves to the
+      // next instruction, otherwise the place.
+      if (Config->EMachine == EM_ARM)
+        return getARMUndefinedRelativeWeakVA(Type, A, P);
+      if (Config->EMachine == EM_AARCH64)
+        return getAArch64UndefinedRelativeWeakVA(Type, A, P);
+    }
   case R_RELAX_GOT_PC:
     return Body.getVA<ELFT>(A) - P;
   case R_PLT_PAGE_PC:
   case R_PAGE_PC:
+    if (Body.isUndefined() && !Body.isLocal() && Body.symbol()->isWeak())
+      return getAArch64Page(A);
     return getAArch64Page(Body.getVA<ELFT>(A)) - getAArch64Page(P);
   }
   llvm_unreachable("Invalid expression");
diff --git a/lld/test/ELF/aarch64-undefined-weak.s b/lld/test/ELF/aarch64-undefined-weak.s
new file mode 100644 (file)
index 0000000..74fef66
--- /dev/null
@@ -0,0 +1,45 @@
+// RUN: llvm-mc -filetype=obj -triple=aarch64-none-linux %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=aarch64-none-linux -d %t2 | FileCheck %s
+// REQUIRES: aarch64
+
+// Check that the ARM 64-bit ABI rules for undefined weak symbols are applied.
+// Branch instructions are resolved to the next instruction. Undefined
+// Symbols in relative are resolved to the place so S - P + A = A.
+
+ .weak target
+
+ .text
+ .global _start
+_start:
+// R_AARCH64_JUMP26
+ b target
+// R_AARCH64_CALL26
+ bl target
+// R_AARCH64_CONDBR19
+ b.eq target
+// R_AARCH64_TSTBR14
+ cbz x1, target
+// R_AARCH64_ADR_PREL_LO21
+ adr x0, target
+// R_AARCH64_ADR_PREL_PG_HI21
+ adrp x0, target
+// R_AARCH64_PREL32
+ .word target - .
+// R_AARCH64_PREL64
+ .xword target - .
+// R_AARCH64_PREL16
+ .hword target - .
+
+// CHECK: Disassembly of section .text:
+// 131076 = 0x20004
+// CHECK:         20000:       01 80 00 14     b       #131076
+// CHECK-NEXT:    20004:       02 80 00 94     bl      #131080
+// CHECK-NEXT:    20008:       60 00 10 54     b.eq    #131084
+// CHECK-NEXT:    2000c:       81 00 10 b4     cbz     x1, #131088
+// CHECK-NEXT:    20010:       00 00 00 10     adr     x0, #0
+// CHECK-NEXT:    20014:       00 00 00 90     adrp    x0, #0
+// CHECK:         20018:       00 00 00 00     .word   0x00000000
+// CHECK-NEXT:    2001c:       00 00 00 00     .word   0x00000000
+// CHECK-NEXT:    20020:       00 00 00 00     .word   0x00000000
+// CHECK-NEXT:    20024:       00 00           .short  0x0000
index 80cf50d..f668c8c 100644 (file)
@@ -18,6 +18,7 @@ _start:
 
 // CHECK: Disassembly of section .text:
 // CHECK-NEXT: _start:
-// CHECK-NEXT:    11000:        ee f7 fe ef     blx     #-69636
-// CHECK-NEXT:    11004:        ee f7 fc bf     b.w     #-69640
-// CHECK-NEXT:    11008:        ee f7 fa bf     b.w     #-69644
+// 69636 = 0x11004 = next instruction
+// CHECK:         11000:        11 f0 02 f8     bl      #69636
+// CHECK-NEXT:    11004:        11 f0 04 b8     b.w     #69640
+// CHECK-NEXT:    11008:        11 f0 06 b8     b.w     #69644
diff --git a/lld/test/ELF/arm-thumb-undefined-weak.s b/lld/test/ELF/arm-thumb-undefined-weak.s
new file mode 100644 (file)
index 0000000..087f8e7
--- /dev/null
@@ -0,0 +1,38 @@
+// RUN: llvm-mc -filetype=obj -triple=thumbv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=thumbv7a-none-linux-gnueabi -d %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that the ARM ABI rules for undefined weak symbols are applied.
+// Branch instructions are resolved to the next instruction. Relative
+// relocations are resolved to the place.
+
+ .syntax unified
+
+ .weak target
+
+ .text
+ .global _start
+_start:
+// R_ARM_THM_JUMP19
+ beq.w target
+// R_ARM_THM_JUMP24
+ b.w target
+// R_ARM_THM_CALL
+ bl target
+// R_ARM_THM_CALL with exchange
+ blx target
+// R_ARM_THM_MOVT_PREL
+ movt r0, :upper16:target - .
+// R_ARM_THM_MOVW_PREL_NC
+ movw r0, :lower16:target - .
+
+// CHECK: Disassembly of section .text:
+// 69636 = 0x11004
+// CHECK:         11000:       11 f0 02 80     beq.w   #69636
+// CHECK-NEXT:    11004:       11 f0 04 b8     b.w     #69640
+// CHECK-NEXT:    11008:       11 f0 06 f8     bl      #69644
+// blx is transformed into bl so we don't change state
+// CHECK-NEXT:    1100c:       11 f0 08 f8     bl      #69648
+// CHECK-NEXT:    11010:       c0 f2 00 00     movt    r0, #0
+// CHECK-NEXT:    11014:       40 f2 00 00     movw    r0, #0
diff --git a/lld/test/ELF/arm-undefined-weak.s b/lld/test/ELF/arm-undefined-weak.s
new file mode 100644 (file)
index 0000000..df67b46
--- /dev/null
@@ -0,0 +1,39 @@
+// RUN: llvm-mc -filetype=obj -triple=armv7a-none-linux-gnueabi %s -o %t
+// RUN: ld.lld %t -o %t2 2>&1
+// RUN: llvm-objdump -triple=armv7a-none-linux-gnueabi -d %t2 | FileCheck %s
+// REQUIRES: arm
+
+// Check that the ARM ABI rules for undefined weak symbols are applied.
+// Branch instructions are resolved to the next instruction. Undefined
+// Symbols in relative are resolved to the place so S - P + A = A.
+
+ .syntax unified
+
+ .weak target
+
+ .text
+ .global _start
+_start:
+// R_ARM_JUMP24
+ b target
+// R_ARM_CALL
+ bl target
+// R_ARM_CALL with exchange
+ blx target
+// R_ARM_MOVT_PREL
+ movt r0, :upper16:target - .
+// R_ARM_MOVW_PREL_NC
+ movw r0, :lower16:target - .
+// R_ARM_REL32
+ .word target - .
+
+// CHECK: Disassembly of section .text:
+// 69636 = 0x11004
+// CHECK:         11000:       01 44 00 ea     b       #69636
+// CHECK-NEXT:    11004:       02 44 00 eb     bl      #69640
+// blx is transformed into bl so we don't change state
+// CHECK-NEXT:    11008:       03 44 00 eb     bl      #69644
+// CHECK-NEXT:    1100c:       00 00 40 e3     movt    r0, #0
+// CHECK-NEXT:    11010:       00 00 00 e3     movw    r0, #0
+// CHECK:         11014:       00 00 00 00     .word   0x00000000
+