[llvm] [Object][COFF][llvm-readobj] Add support for ARM64X dynamic relocations. (PR #97229)

Jacek Caban via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 31 04:32:47 PDT 2024


================
@@ -800,6 +800,219 @@ Error COFFObjectFile::initLoadConfigPtr() {
     }
   }
 
+  // Interpret and validate dynamic relocations.
+  uint32_t DynamicRelocTableOffset = 0, DynamicRelocTableSection = 0;
+  if (is64()) {
+    auto Config = getLoadConfig64();
+    if (Config->Size >=
+        offsetof(coff_load_configuration64, DynamicValueRelocTableSection) +
+            sizeof(Config->DynamicValueRelocTableSection)) {
+      DynamicRelocTableSection = Config->DynamicValueRelocTableSection;
+      DynamicRelocTableOffset = Config->DynamicValueRelocTableOffset;
+    }
+  } else {
+    auto Config = getLoadConfig32();
+    if (Config->Size >=
+        offsetof(coff_load_configuration32, DynamicValueRelocTableSection) +
+            sizeof(Config->DynamicValueRelocTableSection)) {
+      DynamicRelocTableSection = Config->DynamicValueRelocTableSection;
+      DynamicRelocTableOffset = Config->DynamicValueRelocTableOffset;
+    }
+  }
+
+  Expected<const coff_section *> Section = getSection(DynamicRelocTableSection);
+  if (!Section)
+    return Section.takeError();
+  if (*Section) {
+    ArrayRef<uint8_t> Contents;
+    if (Error E = getSectionContents(*Section, Contents))
+      return E;
+
+    Contents = Contents.drop_front(DynamicRelocTableOffset);
+    if (Contents.size() < sizeof(coff_dynamic_reloc_table))
+      return createStringError(object_error::parse_failed,
+                               "Too large DynamicValueRelocTableOffset (" +
+                                   Twine(DynamicRelocTableOffset) + ")");
+
+    DynamicRelocTable =
+        reinterpret_cast<const coff_dynamic_reloc_table *>(Contents.data());
+
+    if (DynamicRelocTable->Version != 1 && DynamicRelocTable->Version != 2)
+      return createStringError(
+          object_error::parse_failed,
+          "Unsupported dynamic relocations table version (" +
+              Twine(DynamicRelocTable->Version) + ")");
+
+    Contents = Contents.drop_front(sizeof(*DynamicRelocTable));
+    if (DynamicRelocTable->Size > Contents.size())
+      return createStringError(object_error::parse_failed,
+                               "Indvalid dynamic relocations directory size (" +
+                                   Twine(DynamicRelocTable->Size) + ")");
+    Contents = Contents.take_front(DynamicRelocTable->Size);
+
+    while (!Contents.empty()) {
+      uint32_t DynRelocSize;
+      uint64_t Symbol;
+
+      if (DynamicRelocTable->Version == 1) {
+        if (is64()) {
+          if (Contents.size() < sizeof(coff_dynamic_relocation64))
+            return createStringError(
+                object_error::parse_failed,
+                "Unexpected end of dynamic relocations data");
+
+          auto DynReloc = reinterpret_cast<const coff_dynamic_relocation64 *>(
+              Contents.data());
+          Symbol = DynReloc->Symbol;
+          DynRelocSize = DynReloc->BaseRelocSize;
+          Contents = Contents.drop_front(sizeof(*DynReloc));
+        } else {
+          if (Contents.size() < sizeof(coff_dynamic_relocation32))
+            return createStringError(
+                object_error::parse_failed,
+                "Unexpected end of dynamic relocations data");
+
+          auto DynReloc = reinterpret_cast<const coff_dynamic_relocation32 *>(
+              Contents.data());
+          Symbol = DynReloc->Symbol;
+          DynRelocSize = DynReloc->BaseRelocSize;
+          Contents = Contents.drop_front(sizeof(*DynReloc));
+        }
+      } else {
+        if (is64()) {
+          if (Contents.size() < sizeof(coff_dynamic_relocation64_v2))
+            return createStringError(
+                object_error::parse_failed,
+                "Unexpected end of dynamic relocations data");
+
+          auto DynReloc =
+              reinterpret_cast<const coff_dynamic_relocation64_v2 *>(
+                  Contents.data());
+          if (DynReloc->HeaderSize < sizeof(*DynReloc) ||
+              Contents.size() < DynReloc->HeaderSize)
+            return createStringError(
+                object_error::parse_failed,
+                "Invalid dynamic relocation header size (" +
+                    Twine(DynReloc->HeaderSize) + ")");
+
+          Symbol = DynReloc->Symbol;
+          DynRelocSize = DynReloc->FixupInfoSize;
+          Contents = Contents.drop_front(DynReloc->HeaderSize);
+        } else {
+          if (Contents.size() < sizeof(coff_dynamic_relocation32_v2))
+            return createStringError(
+                object_error::parse_failed,
+                "Unexpected end of dynamic relocations data");
+
+          auto DynReloc =
+              reinterpret_cast<const coff_dynamic_relocation32_v2 *>(
+                  Contents.data());
+          if (DynReloc->HeaderSize < sizeof(*DynReloc) ||
+              Contents.size() < DynReloc->HeaderSize)
+            return createStringError(
+                object_error::parse_failed,
+                "Invalid dynamic relocation header size (" +
+                    Twine(DynReloc->HeaderSize) + ")");
+
+          Symbol = DynReloc->Symbol;
+          DynRelocSize = DynReloc->FixupInfoSize;
+          Contents = Contents.drop_front(DynReloc->HeaderSize);
+        }
+      }
+      if (DynRelocSize > Contents.size())
+        return createStringError(object_error::parse_failed,
+                                 "Too large dynamic relocation size (" +
+                                     Twine(DynRelocSize) + ")");
+
+      ArrayRef<uint8_t> RelocContents = Contents.take_front(DynRelocSize);
+      Contents = Contents.drop_front(DynRelocSize);
+
+      switch (Symbol) {
+      case COFF::IMAGE_DYNAMIC_RELOCATION_ARM64X:
+        while (!RelocContents.empty()) {
+          if (RelocContents.size() < sizeof(coff_base_reloc_block_header))
+            return createStringError(
+                object_error::parse_failed,
+                "Unexpected end of ARM64X relocations data");
+
+          auto Header = reinterpret_cast<const coff_base_reloc_block_header *>(
+              RelocContents.data());
+          if (Header->BlockSize <= sizeof(*Header))
+            return createStringError(object_error::parse_failed,
+                                     "ARM64X relocations block size (" +
+                                         Twine(Header->BlockSize) +
+                                         ") is too small");
+          if (Header->BlockSize % sizeof(uint32_t))
+            return createStringError(
+                object_error::parse_failed,
+                "Unaligned ARM64X relocations block size (" +
+                    Twine(Header->BlockSize) + ")");
+          if (Header->BlockSize > RelocContents.size())
+            return createStringError(object_error::parse_failed,
+                                     "ARM64X relocations block size (" +
+                                         Twine(Header->BlockSize) +
+                                         ") is too large");
+          if (Header->PageRVA & 0xfff)
+            return createStringError(object_error::parse_failed,
+                                     "Unaligned ARM64X relocations page RVA (" +
+                                         Twine(Header->PageRVA) + ")");
+
+          ArrayRef<uint16_t> Relocs(
+              reinterpret_cast<const uint16_t *>(RelocContents.data() +
+                                                 sizeof(*Header)),
+              (Header->BlockSize - sizeof(*Header)) / sizeof(uint16_t));
+          RelocContents = RelocContents.drop_front(Header->BlockSize);
+
+          while (!Relocs.empty()) {
+            if (!Relocs[0]) {
+              if (Relocs.size() != 1)
+                return createStringError(
+                    object_error::parse_failed,
+                    "Unexpected ARM64X relocations terminator");
+              break;
+            }
+
+            uint16_t Arg = Relocs[0] >> 14, RelocSize = 1, Size;
+            switch ((Relocs[0] >> 12) & 3) {
+            case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_ZEROFILL:
+              Size = 1 << Arg;
+              break;
+            case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_VALUE:
+              if (!Arg)
+                return createStringError(
+                    object_error::parse_failed,
+                    "Invalid ARM64X relocation value size (0)");
+              Size = 1 << Arg;
+              RelocSize += Size / sizeof(Relocs[0]);
+              break;
+            case COFF::IMAGE_DVRT_ARM64X_FIXUP_TYPE_DELTA:
+              ++RelocSize;
+              Size = sizeof(uint32_t);
+              break;
+            default:
+              return createStringError(object_error::parse_failed,
+                                       "Invalid relocation type");
+            }
+            if (Header->PageRVA) {
+              uint64_t IntPtr;
+              uint16_t Offset = Relocs[0] & 0xfff;
+              if (Offset % Size)
+                return createStringError(object_error::parse_failed,
+                                         "Unaligned ARM64X relocation RVA (" +
+                                             Twine(Header->PageRVA + Offset) +
+                                             ")");
+              if (Error E = getRvaPtr(Header->PageRVA + Offset + Size, IntPtr,
----------------
cjacek wrote:

> Alternatively, would it be possible to collapse it even further, into basically what the caller would do, e.g. something like this:

No, it wouldn't be enough. We need to validate that the content is properly terminated to make `moveNext` safe.

https://github.com/llvm/llvm-project/pull/97229


More information about the llvm-commits mailing list