]> granicus.if.org Git - llvm/commitdiff
[COFF, ARM64] Fix symbol offsets in ADRP/ADD/LDR/STR relocations
authorMartin Storsjo <martin@martin.st>
Wed, 26 Jul 2017 11:19:17 +0000 (11:19 +0000)
committerMartin Storsjo <martin@martin.st>
Wed, 26 Jul 2017 11:19:17 +0000 (11:19 +0000)
In COFF, a symbol offset can't be stored in the relocation (as is
done in ELF or MachO), but is stored as the immediate in the
instruction itself. The immediate in the ADRP thus is the symbol
offset in bytes, not in pages. For the PAGEOFFSET_12A/L relocations,
ignore any offset outside of the lowest 12 bits; they won't have any
effect on the ADD/LDR/STR instruction itself but only on the associated
ADRP.

This is similar to how the same issue is handled for MOVW/MOVT
instructions in ELF (see e.g. SVN r307713, and r307728 in lld).

This fixes "fixup out of range" errors while building larger object
files, where temporary symbols end up as a plain section symbol and
an offset, and fixes any cases where the symbol offset mean that
the actual target ended up on a different page than the symbol
itself.

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

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@309105 91177308-0d34-0410-b5e6-96231b3b80d8

lib/Target/AArch64/MCTargetDesc/AArch64AsmBackend.cpp
test/MC/AArch64/coff-relocations.s
test/MC/AArch64/fixup-out-of-range.s

index 0243e75e44c6ae9ae00f127ffbefecd6e2f476a9..0e42cf422bd5f7cab9848d5152fb3a58efa4c987 100644 (file)
@@ -30,12 +30,14 @@ namespace {
 class AArch64AsmBackend : public MCAsmBackend {
   static const unsigned PCRelFlagVal =
       MCFixupKindInfo::FKF_IsAlignedDownTo32Bits | MCFixupKindInfo::FKF_IsPCRel;
+  Triple TheTriple;
+
 public:
   bool IsLittleEndian;
 
 public:
-  AArch64AsmBackend(const Target &T, bool IsLittleEndian)
-     : MCAsmBackend(), IsLittleEndian(IsLittleEndian) {}
+  AArch64AsmBackend(const Target &T, const Triple &TT, bool IsLittleEndian)
+      : MCAsmBackend(), TheTriple(TT), IsLittleEndian(IsLittleEndian) {}
 
   unsigned getNumFixupKinds() const override {
     return AArch64::NumTargetFixupKinds;
@@ -143,7 +145,8 @@ static unsigned AdrImmBits(unsigned Value) {
 }
 
 static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
-                                 MCContext &Ctx) {
+                                 MCContext &Ctx, const Triple &TheTriple,
+                                 bool IsResolved) {
   unsigned Kind = Fixup.getKind();
   int64_t SignedValue = static_cast<int64_t>(Value);
   switch (Kind) {
@@ -154,6 +157,9 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
     return AdrImmBits(Value & 0x1fffffULL);
   case AArch64::fixup_aarch64_pcrel_adrp_imm21:
+    assert(!IsResolved);
+    if (TheTriple.isOSBinFormatCOFF())
+      return AdrImmBits(Value & 0x1fffffULL);
     return AdrImmBits((Value & 0x1fffff000ULL) >> 12);
   case AArch64::fixup_aarch64_ldr_pcrel_imm19:
   case AArch64::fixup_aarch64_pcrel_branch19:
@@ -166,11 +172,15 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
     return (Value >> 2) & 0x7ffff;
   case AArch64::fixup_aarch64_add_imm12:
   case AArch64::fixup_aarch64_ldst_imm12_scale1:
+    if (TheTriple.isOSBinFormatCOFF() && !IsResolved)
+      Value &= 0xfff;
     // Unsigned 12-bit immediate
     if (Value >= 0x1000)
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
     return Value;
   case AArch64::fixup_aarch64_ldst_imm12_scale2:
+    if (TheTriple.isOSBinFormatCOFF() && !IsResolved)
+      Value &= 0xfff;
     // Unsigned 12-bit immediate which gets multiplied by 2
     if (Value >= 0x2000)
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
@@ -178,6 +188,8 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
       Ctx.reportError(Fixup.getLoc(), "fixup must be 2-byte aligned");
     return Value >> 1;
   case AArch64::fixup_aarch64_ldst_imm12_scale4:
+    if (TheTriple.isOSBinFormatCOFF() && !IsResolved)
+      Value &= 0xfff;
     // Unsigned 12-bit immediate which gets multiplied by 4
     if (Value >= 0x4000)
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
@@ -185,6 +197,8 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
       Ctx.reportError(Fixup.getLoc(), "fixup must be 4-byte aligned");
     return Value >> 2;
   case AArch64::fixup_aarch64_ldst_imm12_scale8:
+    if (TheTriple.isOSBinFormatCOFF() && !IsResolved)
+      Value &= 0xfff;
     // Unsigned 12-bit immediate which gets multiplied by 8
     if (Value >= 0x8000)
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
@@ -192,6 +206,8 @@ static uint64_t adjustFixupValue(const MCFixup &Fixup, uint64_t Value,
       Ctx.reportError(Fixup.getLoc(), "fixup must be 8-byte aligned");
     return Value >> 3;
   case AArch64::fixup_aarch64_ldst_imm12_scale16:
+    if (TheTriple.isOSBinFormatCOFF() && !IsResolved)
+      Value &= 0xfff;
     // Unsigned 12-bit immediate which gets multiplied by 16
     if (Value >= 0x10000)
       Ctx.reportError(Fixup.getLoc(), "fixup value out of range");
@@ -278,7 +294,7 @@ void AArch64AsmBackend::applyFixup(const MCAssembler &Asm, const MCFixup &Fixup,
   MCFixupKindInfo Info = getFixupKindInfo(Fixup.getKind());
   MCContext &Ctx = Asm.getContext();
   // Apply any target-specific value adjustments.
-  Value = adjustFixupValue(Fixup, Value, Ctx);
+  Value = adjustFixupValue(Fixup, Value, Ctx, TheTriple, IsResolved);
 
   // Shift the value into position.
   Value <<= Info.TargetOffset;
@@ -412,8 +428,9 @@ class DarwinAArch64AsmBackend : public AArch64AsmBackend {
   }
 
 public:
-  DarwinAArch64AsmBackend(const Target &T, const MCRegisterInfo &MRI)
-      : AArch64AsmBackend(T, /*IsLittleEndian*/true), MRI(MRI) {}
+  DarwinAArch64AsmBackend(const Target &T, const Triple &TT,
+                          const MCRegisterInfo &MRI)
+      : AArch64AsmBackend(T, TT, /*IsLittleEndian*/ true), MRI(MRI) {}
 
   MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
     return createAArch64MachObjectWriter(OS, MachO::CPU_TYPE_ARM64,
@@ -560,9 +577,10 @@ public:
   uint8_t OSABI;
   bool IsILP32;
 
-  ELFAArch64AsmBackend(const Target &T, uint8_t OSABI, bool IsLittleEndian,
-                       bool IsILP32)
-    : AArch64AsmBackend(T, IsLittleEndian), OSABI(OSABI), IsILP32(IsILP32) {}
+  ELFAArch64AsmBackend(const Target &T, const Triple &TT, uint8_t OSABI,
+                       bool IsLittleEndian, bool IsILP32)
+      : AArch64AsmBackend(T, TT, IsLittleEndian), OSABI(OSABI),
+        IsILP32(IsILP32) {}
 
   MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
     return createAArch64ELFObjectWriter(OS, OSABI, IsLittleEndian, IsILP32);
@@ -575,7 +593,7 @@ namespace {
 class COFFAArch64AsmBackend : public AArch64AsmBackend {
 public:
   COFFAArch64AsmBackend(const Target &T, const Triple &TheTriple)
-      : AArch64AsmBackend(T, /*IsLittleEndian*/true) {}
+      : AArch64AsmBackend(T, TheTriple, /*IsLittleEndian*/ true) {}
 
   MCObjectWriter *createObjectWriter(raw_pwrite_stream &OS) const override {
     return createAArch64WinCOFFObjectWriter(OS);
@@ -589,7 +607,7 @@ MCAsmBackend *llvm::createAArch64leAsmBackend(const Target &T,
                                               StringRef CPU,
                                               const MCTargetOptions &Options) {
   if (TheTriple.isOSBinFormatMachO())
-    return new DarwinAArch64AsmBackend(T, MRI);
+    return new DarwinAArch64AsmBackend(T, TheTriple, MRI);
 
   if (TheTriple.isOSBinFormatCOFF())
     return new COFFAArch64AsmBackend(T, TheTriple);
@@ -598,7 +616,8 @@ MCAsmBackend *llvm::createAArch64leAsmBackend(const Target &T,
 
   uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS());
   bool IsILP32 = Options.getABIName() == "ilp32";
-  return new ELFAArch64AsmBackend(T, OSABI, /*IsLittleEndian=*/true, IsILP32);
+  return new ELFAArch64AsmBackend(T, TheTriple, OSABI, /*IsLittleEndian=*/true,
+                                  IsILP32);
 }
 
 MCAsmBackend *llvm::createAArch64beAsmBackend(const Target &T,
@@ -610,5 +629,6 @@ MCAsmBackend *llvm::createAArch64beAsmBackend(const Target &T,
          "Big endian is only supported for ELF targets!");
   uint8_t OSABI = MCELFObjectTargetWriter::getOSABI(TheTriple.getOS());
   bool IsILP32 = Options.getABIName() == "ilp32";
-  return new ELFAArch64AsmBackend(T, OSABI, /*IsLittleEndian=*/false, IsILP32);
+  return new ELFAArch64AsmBackend(T, TheTriple, OSABI, /*IsLittleEndian=*/false,
+                                  IsILP32);
 }
index 10c2dbfcb5354a9ae1c529b8be14529d523f72fa..5d811df3ec54adf0530de4a9ffb694fd06ee2ec9 100644 (file)
@@ -1,5 +1,6 @@
-; RUN: llvm-mc -triple aarch64-windows -filetype obj -o - %s | \
-; RUN: llvm-readobj -r - | FileCheck %s
+; RUN: llvm-mc -triple aarch64-windows -filetype obj -o %t.obj %s
+; RUN: llvm-readobj -r %t.obj | FileCheck %s
+; RUN: llvm-objdump -d %t.obj | FileCheck %s -check-prefix DISASM
 
 ; IMAGE_REL_ARM64_ADDR32
 .Linfo_foo:
@@ -37,6 +38,13 @@ bar:
 ; IMAGE_REL_ARM64_SECTION
 .secidx func
 
+.align 2
+adrp x0, baz + 0x12345
+baz:
+add x0, x0, :lo12:foo + 0x12345
+ldrb w0, [x0, :lo12:foo + 0x12345]
+ldr x0, [x0, :lo12:foo + 0x12348]
+
 ; CHECK: Format: COFF-ARM64
 ; CHECK: Arch: aarch64
 ; CHECK: AddressSize: 64bit
@@ -52,5 +60,14 @@ bar:
 ; CHECK: 0x24 IMAGE_REL_ARM64_PAGEBASE_REL21 bar
 ; CHECK: 0x28 IMAGE_REL_ARM64_SECREL .text
 ; CHECK: 0x2C IMAGE_REL_ARM64_SECTION func
+; CHECK: 0x30 IMAGE_REL_ARM64_PAGEBASE_REL21 baz
+; CHECK: 0x34 IMAGE_REL_ARM64_PAGEOFFSET_12A foo
+; CHECK: 0x38 IMAGE_REL_ARM64_PAGEOFFSET_12L foo
+; CHECK: 0x3C IMAGE_REL_ARM64_PAGEOFFSET_12L foo
 ; CHECK:   }
 ; CHECK: ]
+
+; DISASM: 30:       20 1a 09 b0     adrp    x0, #305418240
+; DISASM: 34:       00 14 0d 91     add     x0, x0, #837
+; DISASM: 38:       00 14 4d 39     ldrb    w0, [x0, #837]
+; DISASM: 3c:       00 a4 41 f9     ldr     x0, [x0, #840]
index 0d0d2ffb0bf3cd4b9ea9495de15acc2b6bb349d7..3acffe202f03c75c63f35ee1b25ad50a4d12ed9f 100644 (file)
@@ -1,4 +1,5 @@
 // RUN: not llvm-mc -triple aarch64--none-eabi -filetype obj < %s -o /dev/null 2>&1 | FileCheck %s
+// RUN: not llvm-mc -triple aarch64-windows -filetype obj < %s -o /dev/null 2>&1 | FileCheck %s
 
 // CHECK: :[[@LINE+1]]:{{[0-9]+}}: error: fixup value out of range
   adr x0, distant