[lld-macho] Diagnose unaligned arm64 PAGEOFF12 relocations
authorDaniel Bertalan <dani@danielbertalan.dev>
Sat, 3 Sep 2022 17:21:12 +0000 (19:21 +0200)
committerDaniel Bertalan <dani@danielbertalan.dev>
Mon, 5 Sep 2022 14:58:51 +0000 (16:58 +0200)
The LDR and STR instructions store their immediate offsets as a multiple
of the load/store's size. Therefore, if the target address is not
aligned, the relocation is not representable. We now emit an error if
that happens, similarly to ld64.

This commit removes a test case from loh-adrp-ldr.s that contained an
unaligned LDR.

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

lld/MachO/Arch/ARM64Common.cpp
lld/MachO/Arch/ARM64Common.h
lld/MachO/Relocations.cpp
lld/MachO/Relocations.h
lld/test/MachO/invalid/arm64-unaligned-load.s [new file with mode: 0644]
lld/test/MachO/loh-adrp-ldr.s

index 27fdf4b..45cf0ea 100644 (file)
@@ -84,7 +84,7 @@ void ARM64Common::relocateOne(uint8_t *loc, const Reloc &r, uint64_t value,
   case ARM64_RELOC_GOT_LOAD_PAGEOFF12:
   case ARM64_RELOC_TLVP_LOAD_PAGEOFF12:
     assert(!r.pcrel);
-    encodePageOff12(loc32, base, value);
+    encodePageOff12(loc32, r, base, value);
     break;
   default:
     llvm_unreachable("unexpected relocation type");
@@ -127,3 +127,26 @@ void ARM64Common::handleDtraceReloc(const Symbol *sym, const Reloc &r,
     error("Unrecognized dtrace symbol prefix: " + toString(*sym));
   }
 }
+
+static void reportUnalignedLdrStr(Twine loc, uint64_t va, int align,
+                                  const Symbol *sym) {
+  std::string symbolHint;
+  if (sym)
+    symbolHint = " (" + toString(*sym) + ")";
+  error(loc + ": " + Twine(8 * align) + "-bit LDR/STR to 0x" +
+        llvm::utohexstr(va) + symbolHint + " is not " + Twine(align) +
+        "-byte aligned");
+}
+
+void macho::reportUnalignedLdrStr(void *loc, const lld::macho::Reloc &r,
+                                  uint64_t va, int align) {
+  uint64_t off = reinterpret_cast<const uint8_t *>(loc) - in.bufferStart;
+  const InputSection *isec = offsetToInputSection(&off);
+  std::string locStr = isec ? isec->getLocation(off) : "(invalid location)";
+  ::reportUnalignedLdrStr(locStr, va, align, r.referent.dyn_cast<Symbol *>());
+}
+
+void macho::reportUnalignedLdrStr(void *loc, lld::macho::SymbolDiagnostic d,
+                                  uint64_t va, int align) {
+  ::reportUnalignedLdrStr(d.reason, va, align, d.symbol);
+}
index 6bd0039..31b4b51 100644 (file)
@@ -75,18 +75,26 @@ inline void encodePage21(uint32_t *loc, SymbolDiagnostic d, uint32_t base,
                                             bitField(va, 14, 19, 5));
 }
 
+void reportUnalignedLdrStr(void *loc, const Reloc &, uint64_t va, int align);
+void reportUnalignedLdrStr(void *loc, SymbolDiagnostic, uint64_t va, int align);
+
 //                      21                   10
 // +-------------------+-----------------------+-------------------+
 // |                   |         imm12         |                   |
 // +-------------------+-----------------------+-------------------+
 
-inline void encodePageOff12(uint32_t *loc, uint32_t base, uint64_t va) {
+template <typename Target>
+inline void encodePageOff12(uint32_t *loc, Target t, uint32_t base,
+                            uint64_t va) {
   int scale = 0;
   if ((base & 0x3b00'0000) == 0x3900'0000) { // load/store
     scale = base >> 30;
     if (scale == 0 && (base & 0x0480'0000) == 0x0480'0000) // 128-bit variant
       scale = 4;
   }
+  const int size = 1 << scale;
+  if ((va & (size - 1)) != 0)
+    reportUnalignedLdrStr(loc, t, va, size);
 
   // TODO(gkm): extract embedded addend and warn if != 0
   // uint64_t addend = ((base & 0x003FFC00) >> 10);
@@ -104,13 +112,13 @@ inline void writeStub(uint8_t *buf8, const uint32_t stubCode[3],
                       const macho::Symbol &sym) {
   auto *buf32 = reinterpret_cast<uint32_t *>(buf8);
   constexpr size_t stubCodeSize = 3 * sizeof(uint32_t);
+  SymbolDiagnostic d = {&sym, "stub"};
   uint64_t pcPageBits =
       pageBits(in.stubs->addr + sym.stubsIndex * stubCodeSize);
   uint64_t lazyPointerVA =
       in.lazyPointers->addr + sym.stubsIndex * LP::wordSize;
-  encodePage21(&buf32[0], {&sym, "stub"}, stubCode[0],
-               pageBits(lazyPointerVA) - pcPageBits);
-  encodePageOff12(&buf32[1], stubCode[1], lazyPointerVA);
+  encodePage21(&buf32[0], d, stubCode[0], pageBits(lazyPointerVA) - pcPageBits);
+  encodePageOff12(&buf32[1], d, stubCode[1], lazyPointerVA);
   buf32[2] = stubCode[2];
 }
 
@@ -125,13 +133,13 @@ inline void writeStubHelperHeader(uint8_t *buf8,
   SymbolDiagnostic d = {nullptr, "stub header helper"};
   encodePage21(&buf32[0], d, stubHelperHeaderCode[0],
                pageBits(loaderVA) - pcPageBits(0));
-  encodePageOff12(&buf32[1], stubHelperHeaderCode[1], loaderVA);
+  encodePageOff12(&buf32[1], d, stubHelperHeaderCode[1], loaderVA);
   buf32[2] = stubHelperHeaderCode[2];
   uint64_t binderVA =
       in.got->addr + in.stubHelper->stubBinder->gotIndex * LP::wordSize;
   encodePage21(&buf32[3], d, stubHelperHeaderCode[3],
                pageBits(binderVA) - pcPageBits(3));
-  encodePageOff12(&buf32[4], stubHelperHeaderCode[4], binderVA);
+  encodePageOff12(&buf32[4], d, stubHelperHeaderCode[4], binderVA);
   buf32[5] = stubHelperHeaderCode[5];
 }
 
@@ -163,7 +171,8 @@ writeObjCMsgSendStub(uint8_t *buf, const uint32_t objcStubsFastCode[8],
   uint64_t selectorOffset = selectorIndex * LP::wordSize;
   encodePage21(&buf32[0], d, objcStubsFastCode[0],
                pageBits(selrefsVA + selectorOffset) - pcPageBits(0));
-  encodePageOff12(&buf32[1], objcStubsFastCode[1], selrefsVA + selectorOffset);
+  encodePageOff12(&buf32[1], d, objcStubsFastCode[1],
+                  selrefsVA + selectorOffset);
   encodePage21(&buf32[2], d, objcStubsFastCode[2],
                pageBits(gotAddr) - pcPageBits(2));
   encodePage21(&buf32[3], d, objcStubsFastCode[3], msgSendIndex * LP::wordSize);
index 6f4b00c..9e5ac69 100644 (file)
@@ -52,7 +52,7 @@ bool macho::validateSymbolRelocation(const Symbol *sym,
 // This is implemented as a slow linear search through OutputSegments,
 // OutputSections, and finally the InputSections themselves. However, this
 // function should be called only on error paths, so some overhead is fine.
-static InputSection *offsetToInputSection(uint64_t *off) {
+InputSection *macho::offsetToInputSection(uint64_t *off) {
   for (OutputSegment *seg : outputSegments) {
     if (*off < seg->fileOff || *off >= seg->fileOff + seg->fileSize)
       continue;
index 04ef420..d0eba46 100644 (file)
@@ -121,6 +121,8 @@ inline void writeAddress(uint8_t *loc, uint64_t addr, uint8_t length) {
   }
 }
 
+InputSection *offsetToInputSection(uint64_t *);
+
 extern const RelocAttrs invalidRelocAttrs;
 
 } // namespace lld::Macho
diff --git a/lld/test/MachO/invalid/arm64-unaligned-load.s b/lld/test/MachO/invalid/arm64-unaligned-load.s
new file mode 100644 (file)
index 0000000..60e89f3
--- /dev/null
@@ -0,0 +1,46 @@
+# REQUIRES: aarch64
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-darwin %s -o %t.o
+# RUN: not %lld -arch arm64 %t.o -o %t 2>&1 | FileCheck %s --implicit-check-not=_byte \
+# RUN:   --implicit-check-not=_correct
+
+# CHECK-DAG: error: {{.*}}:(symbol _main+0x4): 16-bit LDR/STR to 0x[[#%X,]] (_half) is not 2-byte aligned
+# CHECK-DAG: error: {{.*}}:(symbol _main+0xc): 32-bit LDR/STR to 0x[[#%X,]] (_word) is not 4-byte aligned
+# CHECK-DAG: error: {{.*}}:(symbol _main+0x14): 64-bit LDR/STR to 0x[[#%X,]] (_double) is not 8-byte aligned
+# CHECK-DAG: error: {{.*}}:(symbol _main+0x1c): 128-bit LDR/STR to 0x[[#%X,]] (_quad) is not 16-byte aligned
+
+.globl _main
+_main:
+  adrp x0, _half@PAGE
+  ldrh w0, [x0, _half@PAGEOFF]
+
+  adrp x1, _word@PAGE
+  ldr  w1, [x1, _word@PAGEOFF]
+
+  adrp x2, _double@PAGE
+  ldr  x2, [x2, _double@PAGEOFF]
+
+  adrp x3, _quad@PAGE
+  ldr  q0, [x3, _quad@PAGEOFF]
+
+  adrp x4, _byte@PAGE
+  ldrb w4, [x4, _byte@PAGEOFF]
+
+  adrp x5, _correct@PAGE
+  ldr  x5, [x5, _correct@PAGEOFF]
+
+.data
+.p2align 4
+_correct:
+.8byte 0
+.byte 0
+_half:
+.2byte 0
+_word:
+.4byte 0
+_double:
+.8byte 0
+_quad:
+.8byte 0
+.8byte 0
+_byte:
+.byte 0
index 46e8e3a..6f32c1f 100644 (file)
@@ -52,12 +52,6 @@ L12: ldr   x6, 0
 # CHECK-NEXT: adrp x6
 # CHECK-NEXT: ldr x6, #0
 
-## Target is not aligned to 4 bytes
-L13: adrp  x7, _after_unaligned@PAGE
-L14: ldr   x7, [x7, _after_unaligned@PAGEOFF]
-# CHECK-NEXT: adrp x7
-# CHECK-NEXT: ldr x7
-
 ## Byte load, unsupported
 L15: adrp  x8, _after_near@PAGE
 L16: ldr   b8, [x8, _after_near@PAGEOFF]
@@ -123,9 +117,7 @@ L34: ldr   x17, [x17, _after_far@PAGEOFF]
 _after_near:
   .quad 0
   .quad 0
-  .byte 0
-_after_unaligned:
-.space 1048575
+.space 1048576
 
 _after_far:
   .quad 0
@@ -136,7 +128,6 @@ _after_far:
 .loh AdrpLdr L7, L8
 .loh AdrpLdr L9, L10
 .loh AdrpLdr L11, L12
-.loh AdrpLdr L13, L14
 .loh AdrpLdr L15, L16
 .loh AdrpLdr L17, L18
 .loh AdrpLdr L19, L20