[lld] [lld-macho] Implement support for ObjC relative method lists (PR #86231)

via llvm-commits llvm-commits at lists.llvm.org
Mon Mar 25 16:43:39 PDT 2024


https://github.com/alx32 updated https://github.com/llvm/llvm-project/pull/86231

>From d120f34aeecf8907791abf042199d40d98ea18f9 Mon Sep 17 00:00:00 2001
From: Alex B <alexborcan at meta.com>
Date: Tue, 12 Mar 2024 01:18:46 -0700
Subject: [PATCH 1/3] [lld-macho] Implement support for ObjC relative method
 lists

---
 lld/MachO/Config.h                            |   1 +
 lld/MachO/Driver.cpp                          |  17 ++
 lld/MachO/InputSection.cpp                    |   8 +
 lld/MachO/InputSection.h                      |   1 +
 lld/MachO/MapFile.cpp                         |  22 +-
 lld/MachO/ObjC.h                              |   2 +
 lld/MachO/Options.td                          |   6 +
 lld/MachO/SyntheticSections.cpp               | 229 ++++++++++++++++++
 lld/MachO/SyntheticSections.h                 |  48 ++++
 lld/MachO/Writer.cpp                          |   5 +
 .../MachO/objc-relative-method-lists-simple.s | 211 ++++++++++++++++
 11 files changed, 543 insertions(+), 7 deletions(-)
 create mode 100644 lld/test/MachO/objc-relative-method-lists-simple.s

diff --git a/lld/MachO/Config.h b/lld/MachO/Config.h
index f820513a111ea3..7b45f7f4c39a1b 100644
--- a/lld/MachO/Config.h
+++ b/lld/MachO/Config.h
@@ -135,6 +135,7 @@ struct Configuration {
   bool emitEncryptionInfo = false;
   bool emitInitOffsets = false;
   bool emitChainedFixups = false;
+  bool emitRelativeMethodLists = false;
   bool thinLTOEmitImportsFiles;
   bool thinLTOEmitIndexFiles;
   bool thinLTOIndexOnly;
diff --git a/lld/MachO/Driver.cpp b/lld/MachO/Driver.cpp
index 14c111ce9685c9..65de531db04b75 100644
--- a/lld/MachO/Driver.cpp
+++ b/lld/MachO/Driver.cpp
@@ -1086,6 +1086,22 @@ static bool shouldEmitChainedFixups(const InputArgList &args) {
   return isRequested;
 }
 
+static bool shouldEmitRelativeMethodLists(const InputArgList &args) {
+  const Arg *arg = args.getLastArg(OPT_objc_relative_method_lists,
+                                   OPT_no_objc_relative_method_lists);
+  if (arg && arg->getOption().getID() == OPT_objc_relative_method_lists)
+    return true;
+  if (arg && arg->getOption().getID() == OPT_no_objc_relative_method_lists)
+    return false;
+
+  // TODO: If no flag is specified, don't default to false, but instead:
+  //   - default false on   <   ios14
+  //   - default true  on   >=  ios14
+  // For now, until this feature is confirmed stable, default to false if no
+  // flag is explicitly specified
+  return false;
+}
+
 void SymbolPatterns::clear() {
   literals.clear();
   globs.clear();
@@ -1630,6 +1646,7 @@ bool link(ArrayRef<const char *> argsArr, llvm::raw_ostream &stdoutOS,
   config->emitChainedFixups = shouldEmitChainedFixups(args);
   config->emitInitOffsets =
       config->emitChainedFixups || args.hasArg(OPT_init_offsets);
+  config->emitRelativeMethodLists = shouldEmitRelativeMethodLists(args);
   config->icfLevel = getICFLevel(args);
   config->dedupStrings =
       args.hasFlag(OPT_deduplicate_strings, OPT_no_deduplicate_strings, true);
diff --git a/lld/MachO/InputSection.cpp b/lld/MachO/InputSection.cpp
index 22930d52dd1db2..e3d7a400e4ce92 100644
--- a/lld/MachO/InputSection.cpp
+++ b/lld/MachO/InputSection.cpp
@@ -46,6 +46,14 @@ void lld::macho::addInputSection(InputSection *inputSection) {
   if (auto *isec = dyn_cast<ConcatInputSection>(inputSection)) {
     if (isec->isCoalescedWeak())
       return;
+    if (config->emitRelativeMethodLists &&
+        ObjCMethListSection::isMethodList(isec)) {
+      if (in.objcMethList->inputOrder == UnspecifiedInputOrder)
+        in.objcMethList->inputOrder = inputSectionsOrder++;
+      in.objcMethList->addInput(isec);
+      isec->parent = in.objcMethList;
+      return;
+    }
     if (config->emitInitOffsets &&
         sectionType(isec->getFlags()) == S_MOD_INIT_FUNC_POINTERS) {
       in.initOffsets->addInput(isec);
diff --git a/lld/MachO/InputSection.h b/lld/MachO/InputSection.h
index 694bdf734907ba..a0e6afef92cb82 100644
--- a/lld/MachO/InputSection.h
+++ b/lld/MachO/InputSection.h
@@ -342,6 +342,7 @@ constexpr const char moduleTermFunc[] = "__mod_term_func";
 constexpr const char nonLazySymbolPtr[] = "__nl_symbol_ptr";
 constexpr const char objcCatList[] = "__objc_catlist";
 constexpr const char objcClassList[] = "__objc_classlist";
+constexpr const char objcMethList[] = "__objc_methlist";
 constexpr const char objcClassRefs[] = "__objc_classrefs";
 constexpr const char objcConst[] = "__objc_const";
 constexpr const char objCImageInfo[] = "__objc_imageinfo";
diff --git a/lld/MachO/MapFile.cpp b/lld/MachO/MapFile.cpp
index f736360624ebd1..2a31a5c09cdd22 100644
--- a/lld/MachO/MapFile.cpp
+++ b/lld/MachO/MapFile.cpp
@@ -197,18 +197,24 @@ void macho::writeMapFile() {
                    seg->name.str().c_str(), osec->name.str().c_str());
     }
 
+  // Shared function to print an array of symbols.
+  auto printIsecArrSyms = [&](const std::vector<ConcatInputSection *> &arr) {
+    for (const ConcatInputSection *isec : arr) {
+      for (Defined *sym : isec->symbols) {
+        if (!(isPrivateLabel(sym->getName()) && sym->size == 0))
+          os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(),
+                       sym->size, readerToFileOrdinal[sym->getFile()],
+                       sym->getName().str().data());
+      }
+    }
+  };
+
   os << "# Symbols:\n";
   os << "# Address\tSize    \tFile  Name\n";
   for (const OutputSegment *seg : outputSegments) {
     for (const OutputSection *osec : seg->getSections()) {
       if (auto *concatOsec = dyn_cast<ConcatOutputSection>(osec)) {
-        for (const InputSection *isec : concatOsec->inputs) {
-          for (Defined *sym : isec->symbols)
-            if (!(isPrivateLabel(sym->getName()) && sym->size == 0))
-              os << format("0x%08llX\t0x%08llX\t[%3u] %s\n", sym->getVA(),
-                           sym->size, readerToFileOrdinal[sym->getFile()],
-                           sym->getName().str().data());
-        }
+        printIsecArrSyms(concatOsec->inputs);
       } else if (osec == in.cStringSection || osec == in.objcMethnameSection) {
         const auto &liveCStrings = info.liveCStringsForSection.lookup(osec);
         uint64_t lastAddr = 0; // strings will never start at address 0, so this
@@ -237,6 +243,8 @@ void macho::writeMapFile() {
         printNonLazyPointerSection(os, in.got);
       } else if (osec == in.tlvPointers) {
         printNonLazyPointerSection(os, in.tlvPointers);
+      } else if (osec == in.objcMethList) {
+        printIsecArrSyms(in.objcMethList->getInputs());
       }
       // TODO print other synthetic sections
     }
diff --git a/lld/MachO/ObjC.h b/lld/MachO/ObjC.h
index 9fbe984e6223ec..8081605670c519 100644
--- a/lld/MachO/ObjC.h
+++ b/lld/MachO/ObjC.h
@@ -22,6 +22,8 @@ constexpr const char klassPropList[] = "__OBJC_$_CLASS_PROP_LIST_";
 constexpr const char metaclass[] = "_OBJC_METACLASS_$_";
 constexpr const char ehtype[] = "_OBJC_EHTYPE_$_";
 constexpr const char ivar[] = "_OBJC_IVAR_$_";
+constexpr const char instanceMethods[] = "__OBJC_$_INSTANCE_METHODS_";
+constexpr const char classMethods[] = "__OBJC_$_CLASS_METHODS_";
 constexpr const char listProprieties[] = "__OBJC_$_PROP_LIST_";
 
 constexpr const char category[] = "__OBJC_$_CATEGORY_";
diff --git a/lld/MachO/Options.td b/lld/MachO/Options.td
index 0d8ee2a0926be2..19f8509ba714bd 100644
--- a/lld/MachO/Options.td
+++ b/lld/MachO/Options.td
@@ -1284,6 +1284,12 @@ def fixup_chains_section : Flag<["-"], "fixup_chains_section">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
     Group<grp_undocumented>;
+def objc_relative_method_lists : Flag<["-"], "objc_relative_method_lists">,
+    HelpText<"Emit relative method lists (more compact representation)">,
+    Group<grp_undocumented>;
+def no_objc_relative_method_lists : Flag<["-"], "no_objc_relative_method_lists">,
+    HelpText<"Don't emit relative method lists (use traditional representation)">,
+    Group<grp_undocumented>;
 def flto_codegen_only : Flag<["-"], "flto-codegen-only">,
     HelpText<"This option is undocumented in ld64">,
     Flags<[HelpHidden]>,
diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index 0afbbd478bb9fd..df4401df765715 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -12,6 +12,7 @@
 #include "ExportTrie.h"
 #include "InputFiles.h"
 #include "MachOStructs.h"
+#include "ObjC.h"
 #include "OutputSegment.h"
 #include "SymbolTable.h"
 #include "Symbols.h"
@@ -1975,6 +1976,234 @@ void InitOffsetsSection::setUp() {
   }
 }
 
+ObjCMethListSection::ObjCMethListSection()
+    : SyntheticSection(segment_names::text, section_names::objcMethList) {
+  flags = S_ATTR_NO_DEAD_STRIP;
+  align = m_align;
+}
+
+// Go through all input method lists and ensure that we have selrefs for all
+// their method names. The selrefs will be needed later by ::writeTo. We need to
+// create them early on here to ensure they are processed correctly by the lld
+// pipeline.
+void ObjCMethListSection::setUp() {
+  for (const ConcatInputSection *isec : inputs) {
+    uint32_t structSizeAndFlags = 0, structCount = 0;
+    readMethodListHeader(isec->data.data(), structSizeAndFlags, structCount);
+    uint32_t structSize = structSizeAndFlags & m_structSizeMask;
+
+    // Method name is immediately after header
+    uint32_t methodNameOff = m_methodListHeaderSize;
+
+    // Loop through all methods, and ensure a selref for each of them exists.
+    while (methodNameOff < isec->data.size()) {
+      const Reloc *reloc = isec->getRelocAt(methodNameOff);
+      assert(reloc && "Relocation expected at method list name slot");
+      auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+      assert(def && "Expected valid Defined at method list name slot");
+      auto *cisec = cast<CStringInputSection>(def->isec);
+      assert(cisec && "Expected method name to be in a CStringInputSection");
+      auto methname = cisec->getStringRefAtOffset(def->value);
+      if (!ObjCSelRefsHelper::getSelRef(methname))
+        ObjCSelRefsHelper::makeSelRef(methname);
+
+      // Jump to method name offset in next struct
+      methodNameOff += structSize;
+    }
+  }
+}
+
+// Calculate section size and final offsets for where InputSection's need to be
+// written.
+void ObjCMethListSection::finalize() {
+  // m_size will be the total size of the __objc_methlist section
+  m_size = 0;
+  for (ConcatInputSection *isec : inputs) {
+    // We can also use m_size as write offset for isec
+    assert(m_size == alignToPowerOf2(m_size, m_align) &&
+           "expected __objc_methlist to be aligned by default with the "
+           "required section alignment");
+    isec->outSecOff = m_size;
+
+    isec->isFinal = true;
+    uint32_t relativeListSize =
+        methodListSizeToRelativeMethodListSize(isec->data.size());
+    m_size += relativeListSize;
+
+    // If encoding the method list in relative offset format shrinks the size,
+    // then we also need to adjust symbol sizes to match the new size. Note that
+    // on 32bit platforms the size of the method list will remain the same when
+    // encoded in relative offset format.
+    if (relativeListSize != isec->data.size()) {
+      for (Symbol *sym : isec->symbols) {
+        assert(isa<Defined>(sym) &&
+               "Unexpected undefined symbol in ObjC method list");
+        auto *def = cast<Defined>(sym);
+        // There can be 0-size symbols, check if this is the case and ignore
+        // them.
+        if (def->size) {
+          assert(
+              def->size == isec->data.size() &&
+              "Invalid ObjC method list symbol size: expected symbol size to "
+              "match isec size");
+          def->size = relativeListSize;
+        }
+      }
+    }
+  }
+}
+
+void ObjCMethListSection::writeTo(uint8_t *bufStart) const {
+  uint8_t *buf = bufStart;
+  for (const ConcatInputSection *isec : inputs) {
+    assert(buf - bufStart == long(isec->outSecOff) &&
+           "Writing at unexpected offset");
+    uint32_t writtenSize = writeRelativeMethodList(isec, buf);
+    buf += writtenSize;
+  }
+  assert(buf - bufStart == m_size &&
+         "Written size does not match expected section size");
+}
+
+// Check if an InputSection is a method list. To do this we scan the
+// InputSection for any symbols who's names match the patterns we expect clang
+// to generate for method lists.
+bool ObjCMethListSection::isMethodList(const ConcatInputSection *isec) {
+  const char *symPrefixes[] = {objc::symbol_names::classMethods,
+                               objc::symbol_names::instanceMethods,
+                               objc::symbol_names::categoryInstanceMethods,
+                               objc::symbol_names::categoryClassMethods};
+  if (!isec)
+    return false;
+  for (const Symbol *sym : isec->symbols) {
+    auto *def = dyn_cast_or_null<Defined>(sym);
+    if (!def)
+      continue;
+    for (const char *prefix : symPrefixes) {
+      if (def->getName().starts_with(prefix)) {
+        assert(def->size == isec->data.size() &&
+               "Invalid ObjC method list symbol size: expected symbol size to "
+               "match isec size");
+        assert(def->value == 0 &&
+               "Offset of ObjC method list symbol must be 0");
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
+// Encode a single relative offset value. The input is the data/symbol at
+// (&isec->data[inSecOff]). The output is written to (&buf[outSecOff]).
+// 'createSelRef' indicates that we should not directly use the specified
+// symbol, but instead get the selRef for the symbol and use that instead.
+void ObjCMethListSection::writeRelativeOffsetForIsec(
+    const ConcatInputSection *isec, uint8_t *buf, uint32_t &inSecOff,
+    uint32_t &outSecOff, bool useSelRef) const {
+  const Reloc *reloc = isec->getRelocAt(inSecOff);
+  assert(reloc && "Relocation expected at __objc_methlist Offset");
+  auto *def = dyn_cast_or_null<Defined>(reloc->referent.get<Symbol *>());
+  assert(def && "Expected all syms in __objc_methlist to be defined");
+  uint32_t symVA = def->getVA();
+
+  if (useSelRef) {
+    auto *cisec = cast<CStringInputSection>(def->isec);
+    auto methname = cisec->getStringRefAtOffset(def->value);
+    ConcatInputSection *selRef = ObjCSelRefsHelper::getSelRef(methname);
+    assert(selRef && "Expected all selector names to already be already be "
+                     "present in __objc_selrefs");
+    symVA = selRef->getVA();
+    assert(selRef->data.size() == sizeof(target->wordSize) &&
+           "Expected one selref per ConcatInputSection");
+  }
+
+  uint32_t currentVA = isec->getVA() + outSecOff;
+  uint32_t delta = symVA - currentVA;
+  write32le(buf + outSecOff, delta);
+
+  inSecOff += target->wordSize;
+  outSecOff += sizeof(uint32_t);
+}
+
+// Write a relative method list to buf, return the size of the written
+// information
+uint32_t
+ObjCMethListSection::writeRelativeMethodList(const ConcatInputSection *isec,
+                                             uint8_t *buf) const {
+  // Copy over the header, and add the "this is a relative method list" magic
+  // value flag
+  uint32_t structSizeAndFlags = 0, structCount = 0;
+  readMethodListHeader(isec->data.data(), structSizeAndFlags, structCount);
+  structSizeAndFlags |= m_relMethodHeaderFlag;
+  writeMethodListHeader(buf, structSizeAndFlags, structCount);
+
+  assert(m_methodListHeaderSize +
+                 (structCount * m_pointersPerStruct * target->wordSize) ==
+             isec->data.size() &&
+         "Invalid computed ObjC method list size");
+
+  uint32_t inSecOff = m_methodListHeaderSize;
+  uint32_t outSecOff = m_methodListHeaderSize;
+
+  // Go through the method list and encode input absolute pointers as relative
+  // offsets. writeRelativeOffsetForIsec will be incrementing inSecOff and
+  // outSecOff
+  for (uint32_t i = 0; i < structCount; i++) {
+    // Write the name of the method
+    writeRelativeOffsetForIsec(isec, buf, inSecOff, outSecOff, true);
+    // Write the type of the method
+    writeRelativeOffsetForIsec(isec, buf, inSecOff, outSecOff, false);
+    // Write reference to the selector of the method
+    writeRelativeOffsetForIsec(isec, buf, inSecOff, outSecOff, false);
+  }
+
+  // Expecting to have read all the data in the isec
+  assert(inSecOff == isec->data.size() &&
+         "Invalid actual ObjC method list size");
+  assert(
+      outSecOff == methodListSizeToRelativeMethodListSize(inSecOff) &&
+      "Mismatch between input & output size when writing relative method list");
+  return outSecOff;
+}
+
+// Given the size of an ObjC method list InputSection, return the size of the
+// method list when encoded in relative offsets format. We can do this without
+// decoding the actual data, as it can be directly infered from the size of the
+// isec.
+uint32_t
+ObjCMethListSection::methodListSizeToRelativeMethodListSize(uint32_t iSecSize) {
+  uint32_t oldPointersSize = iSecSize - m_methodListHeaderSize;
+  uint32_t pointerCount = oldPointersSize / target->wordSize;
+  assert(((pointerCount % m_pointersPerStruct) == 0) &&
+         "__objc_methlist expects method lists to have multiple-of-3 pointers");
+
+  constexpr uint32_t sizeOfRelativeOffset = sizeof(uint32_t);
+  uint32_t newPointersSize = pointerCount * sizeOfRelativeOffset;
+  uint32_t newTotalSize = m_methodListHeaderSize + newPointersSize;
+
+  assert((newTotalSize <= iSecSize) &&
+         "Expected relative method list size to be smaller or equal than "
+         "original size");
+  return newTotalSize;
+}
+
+// Read a method list header from buf
+void ObjCMethListSection::readMethodListHeader(const uint8_t *buf,
+                                               uint32_t &structSizeAndFlags,
+                                               uint32_t &structCount) {
+  structSizeAndFlags = read32le(buf);
+  structCount = read32le(buf + sizeof(uint32_t));
+}
+
+// Write a method list header to buf
+void ObjCMethListSection::writeMethodListHeader(uint8_t *buf,
+                                                uint32_t structSizeAndFlags,
+                                                uint32_t structCount) {
+  write32le(buf, structSizeAndFlags);
+  write32le(buf + sizeof(structSizeAndFlags), structCount);
+}
+
 void macho::createSyntheticSymbols() {
   auto addHeaderSymbol = [](const char *name) {
     symtab->addSynthetic(name, in.header->isec, /*value=*/0,
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index 4586a4a0bf4361..bfbf9af9ef34fb 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -684,6 +684,53 @@ class InitOffsetsSection final : public SyntheticSection {
   std::vector<ConcatInputSection *> sections;
 };
 
+// This SyntheticSection is for the __objc_methlist section, which contains
+// relative method lists if the -objc_relative_method_lists option is enabled.
+class ObjCMethListSection final : public SyntheticSection {
+public:
+  ObjCMethListSection();
+
+  static bool isMethodList(const ConcatInputSection *isec);
+  void addInput(ConcatInputSection *isec) { inputs.push_back(isec); }
+  std::vector<ConcatInputSection *> getInputs() { return inputs; }
+
+  void setUp();
+  void finalize() override;
+  bool isNeeded() const override { return !inputs.empty(); }
+  uint64_t getSize() const override { return m_size; }
+  void writeTo(uint8_t *bufStart) const override;
+
+private:
+  static void readMethodListHeader(const uint8_t *buf,
+                                   uint32_t &structSizeAndFlags,
+                                   uint32_t &structCount);
+  static void writeMethodListHeader(uint8_t *buf, uint32_t structSizeAndFlags,
+                                    uint32_t structCount);
+  static uint32_t methodListSizeToRelativeMethodListSize(uint32_t iSecSize);
+  void writeRelativeOffsetForIsec(const ConcatInputSection *isec, uint8_t *buf,
+                                  uint32_t &inSecOff, uint32_t &outSecOff,
+                                  bool useSelRef) const;
+  uint32_t writeRelativeMethodList(const ConcatInputSection *isec,
+                                   uint8_t *buf) const;
+
+  static constexpr uint32_t m_methodListHeaderSize =
+      /*structSizeAndFlags*/ sizeof(uint32_t) +
+      /*structCount*/ sizeof(uint32_t);
+  // Relative method lists are supported only for 3-pointer method lists
+  static constexpr uint32_t m_pointersPerStruct = 3;
+  // The runtime identifies relative method lists via this magic value
+  static constexpr uint32_t m_relMethodHeaderFlag = 0x80000000;
+  // In the method list header, the first 2 bytes are the size of struct
+  static constexpr uint32_t m_structSizeMask = 0xFFFF;
+  // Relative method lists have 4 byte alignment as all data in the InputSection
+  // is 4 byte
+  static constexpr uint32_t m_align = 4;
+
+  // The output size of the __objc_methlist section, computed during finalize()
+  uint32_t m_size = 0;
+  std::vector<ConcatInputSection *> inputs;
+};
+
 // Chained fixups are a replacement for classic dyld opcodes. In this format,
 // most of the metadata necessary for binding symbols and rebasing addresses is
 // stored directly in the memory location that will have the fixup applied.
@@ -810,6 +857,7 @@ struct InStruct {
   ObjCImageInfoSection *objCImageInfo = nullptr;
   ConcatInputSection *imageLoaderCache = nullptr;
   InitOffsetsSection *initOffsets = nullptr;
+  ObjCMethListSection *objcMethList = nullptr;
   ChainedFixupsSection *chainedFixups = nullptr;
 };
 
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index a18b5268fd42aa..9c0f8949bd0200 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -1292,6 +1292,8 @@ template <class LP> void Writer::run() {
   scanSymbols();
   if (in.objcStubs->isNeeded())
     in.objcStubs->setUp();
+  if (in.objcMethList)
+    in.objcMethList->setUp();
   scanRelocations();
   if (in.initOffsets->isNeeded())
     in.initOffsets->setUp();
@@ -1363,6 +1365,9 @@ void macho::createSyntheticSections() {
   in.unwindInfo = makeUnwindInfoSection();
   in.objCImageInfo = make<ObjCImageInfoSection>();
   in.initOffsets = make<InitOffsetsSection>();
+  // Only create ObjCMethListSection if we need it
+  if (config->emitRelativeMethodLists)
+    in.objcMethList = make<ObjCMethListSection>();
 
   // This section contains space for just a single word, and will be used by
   // dyld to cache an address to the image loader it uses.
diff --git a/lld/test/MachO/objc-relative-method-lists-simple.s b/lld/test/MachO/objc-relative-method-lists-simple.s
new file mode 100644
index 00000000000000..860236b737b832
--- /dev/null
+++ b/lld/test/MachO/objc-relative-method-lists-simple.s
@@ -0,0 +1,211 @@
+# REQUIRES: aarch64
+# RUN: rm -rf %t; split-file %s %t && cd %t
+
+## Compile a64_rel_dylib.o
+# RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_rel_dylib.o a64_simple_class.s
+
+## Test arm64 + relative method lists
+# RUN: ld64.lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -platform_version macos 11.0.0 11.0.0 -objc_relative_method_lists
+# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib  | FileCheck %s --check-prefix=CHK_REL
+
+## Test arm64 + traditional method lists (no relative offsets)
+# RUN: ld64.lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -platform_version macos 11.0.0 11.0.0 -no_objc_relative_method_lists
+# RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib  | FileCheck %s --check-prefix=CHK_NO_REL
+
+CHK_NO_REL-NOT: (relative)
+
+CHK_REL:       Contents of (__DATA_CONST,__objc_classlist) section
+CHK_REL-NEXT:  _OBJC_CLASS_$_MyClass
+CHK_REL:       baseMethods
+CHK_REL-NEXT:  entsize 24 (relative)
+CHK_REL-NEXT:  count 3
+CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) instance_method_00
+CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) v16 at 0:8
+CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) -[MyClass instance_method_00]
+CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) instance_method_01
+CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) v16 at 0:8
+CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) -[MyClass instance_method_01]
+CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) instance_method_02
+CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) v16 at 0:8
+CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) -[MyClass instance_method_02]
+
+CHK_REL:       Meta Class
+CHK_REL-NEXT:  isa 0x{{[0-9a-f]*}} _OBJC_METACLASS_$_MyClass
+CHK_REL:       baseMethods 0x694 (struct method_list_t *)
+CHK_REL-NEXT:  entsize 24 (relative)
+CHK_REL-NEXT:  count 3
+CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  class_method_00
+CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  v16 at 0:8
+CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  +[MyClass class_method_00]
+CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  class_method_01
+CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  v16 at 0:8
+CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  +[MyClass class_method_01]
+CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  class_method_02
+CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  v16 at 0:8
+CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  +[MyClass class_method_02]
+
+
+
+######################## Generate a64_simple_class.s #########################
+# clang -c simple_class.mm -s -o a64_simple_class.s -target arm64-apple-macos -arch arm64 -Oz
+
+########################       simple_class.mm       ########################
+#  __attribute__((objc_root_class))
+#  @interface MyClass
+#  - (void)instance_method_00;
+#  - (void)instance_method_01;
+#  - (void)instance_method_02;
+#  + (void)class_method_00;
+#  + (void)class_method_01;
+#  + (void)class_method_02;
+#  @end
+#
+#  @implementation MyClass
+#  - (void)instance_method_00 {}
+#  - (void)instance_method_01 {}
+#  - (void)instance_method_02 {}
+#  + (void)class_method_00 {}
+#  + (void)class_method_01 {}
+#  + (void)class_method_02 {}
+#  @end
+#
+#  void *_objc_empty_cache;
+#  void *_objc_empty_vtable;
+#
+
+#--- objc-macros.s
+.macro .objc_selector_def name
+	.p2align	2
+"\name":
+	.cfi_startproc
+	ret
+	.cfi_endproc
+.endm
+
+#--- a64_simple_class.s
+.include "objc-macros.s"
+
+.section	__TEXT,__text,regular,pure_instructions
+.build_version macos, 11, 0
+
+.objc_selector_def "-[MyClass instance_method_00]"
+.objc_selector_def "-[MyClass instance_method_01]"
+.objc_selector_def "-[MyClass instance_method_02]"
+
+.objc_selector_def "+[MyClass class_method_00]"
+.objc_selector_def "+[MyClass class_method_01]"
+.objc_selector_def "+[MyClass class_method_02]"
+
+.globl	__objc_empty_vtable
+.zerofill __DATA,__common,__objc_empty_vtable,8,3
+.section	__DATA,__objc_data
+.globl	_OBJC_CLASS_$_MyClass
+.p2align	3, 0x0
+
+_OBJC_CLASS_$_MyClass:
+	.quad	_OBJC_METACLASS_$_MyClass
+	.quad	0
+	.quad	__objc_empty_cache
+	.quad	__objc_empty_vtable
+	.quad	__OBJC_CLASS_RO_$_MyClass
+	.globl	_OBJC_METACLASS_$_MyClass
+	.p2align	3, 0x0
+
+_OBJC_METACLASS_$_MyClass:
+	.quad	_OBJC_METACLASS_$_MyClass
+	.quad	_OBJC_CLASS_$_MyClass
+	.quad	__objc_empty_cache
+	.quad	__objc_empty_vtable
+	.quad	__OBJC_METACLASS_RO_$_MyClass
+
+	.section	__TEXT,__objc_classname,cstring_literals
+l_OBJC_CLASS_NAME_:
+	.asciz	"MyClass"
+	.section	__TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_:
+	.asciz	"class_method_00"
+	.section	__TEXT,__objc_methtype,cstring_literals
+l_OBJC_METH_VAR_TYPE_:
+	.asciz	"v16 at 0:8"
+	.section	__TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.1:
+	.asciz	"class_method_01"
+l_OBJC_METH_VAR_NAME_.2:
+	.asciz	"class_method_02"
+	.section	__DATA,__objc_const
+	.p2align	3, 0x0
+__OBJC_$_CLASS_METHODS_MyClass:
+	.long	24
+	.long	3
+	.quad	l_OBJC_METH_VAR_NAME_
+	.quad	l_OBJC_METH_VAR_TYPE_
+	.quad	"+[MyClass class_method_00]"
+	.quad	l_OBJC_METH_VAR_NAME_.1
+	.quad	l_OBJC_METH_VAR_TYPE_
+	.quad	"+[MyClass class_method_01]"
+	.quad	l_OBJC_METH_VAR_NAME_.2
+	.quad	l_OBJC_METH_VAR_TYPE_
+	.quad	"+[MyClass class_method_02]"
+	.p2align	3, 0x0
+
+__OBJC_METACLASS_RO_$_MyClass:
+	.long	3
+	.long	40
+	.long	40
+	.space	4
+	.quad	0
+	.quad	l_OBJC_CLASS_NAME_
+	.quad	__OBJC_$_CLASS_METHODS_MyClass
+	.quad	0
+	.quad	0
+	.quad	0
+	.quad	0
+
+	.section	__TEXT,__objc_methname,cstring_literals
+l_OBJC_METH_VAR_NAME_.3:
+	.asciz	"instance_method_00"
+l_OBJC_METH_VAR_NAME_.4:
+	.asciz	"instance_method_01"
+l_OBJC_METH_VAR_NAME_.5:
+	.asciz	"instance_method_02"
+
+	.section	__DATA,__objc_const
+	.p2align	3, 0x0
+__OBJC_$_INSTANCE_METHODS_MyClass:
+	.long	24
+	.long	3
+	.quad	l_OBJC_METH_VAR_NAME_.3
+	.quad	l_OBJC_METH_VAR_TYPE_
+	.quad	"-[MyClass instance_method_00]"
+	.quad	l_OBJC_METH_VAR_NAME_.4
+	.quad	l_OBJC_METH_VAR_TYPE_
+	.quad	"-[MyClass instance_method_01]"
+	.quad	l_OBJC_METH_VAR_NAME_.5
+	.quad	l_OBJC_METH_VAR_TYPE_
+	.quad	"-[MyClass instance_method_02]"
+	.p2align	3, 0x0
+
+__OBJC_CLASS_RO_$_MyClass:
+	.long	2
+	.long	0
+	.long	0
+	.space	4
+	.quad	0
+	.quad	l_OBJC_CLASS_NAME_
+	.quad	__OBJC_$_INSTANCE_METHODS_MyClass
+	.quad	0
+	.quad	0
+	.quad	0
+	.quad	0
+	.globl	__objc_empty_cache
+
+.zerofill __DATA,__common,__objc_empty_cache,8,3
+	.section	__DATA,__objc_classlist,regular,no_dead_strip
+	.p2align	3, 0x0
+l_OBJC_LABEL_CLASS_$:
+	.quad	_OBJC_CLASS_$_MyClass
+	.section	__DATA,__objc_imageinfo,regular,no_dead_strip
+L_OBJC_IMAGE_INFO:
+	.long	0
+	.long	64
+.subsections_via_symbols

>From 5c4a8dae0a3a421eb15a25931a5fab6ea073a4e6 Mon Sep 17 00:00:00 2001
From: Alex B <alexborcan at meta.com>
Date: Mon, 25 Mar 2024 16:20:31 -0700
Subject: [PATCH 2/3] Address Feedback #1

---
 lld/MachO/SyntheticSections.cpp               | 57 ++++++++++---------
 lld/MachO/SyntheticSections.h                 | 23 ++++----
 lld/MachO/Writer.cpp                          |  6 +-
 .../MachO/objc-relative-method-lists-simple.s | 40 ++++++++++++-
 4 files changed, 79 insertions(+), 47 deletions(-)

diff --git a/lld/MachO/SyntheticSections.cpp b/lld/MachO/SyntheticSections.cpp
index df4401df765715..a5aeff06f78509 100644
--- a/lld/MachO/SyntheticSections.cpp
+++ b/lld/MachO/SyntheticSections.cpp
@@ -1979,7 +1979,7 @@ void InitOffsetsSection::setUp() {
 ObjCMethListSection::ObjCMethListSection()
     : SyntheticSection(segment_names::text, section_names::objcMethList) {
   flags = S_ATTR_NO_DEAD_STRIP;
-  align = m_align;
+  align = relativeOffsetSize;
 }
 
 // Go through all input method lists and ensure that we have selrefs for all
@@ -1993,7 +1993,7 @@ void ObjCMethListSection::setUp() {
     uint32_t structSize = structSizeAndFlags & m_structSizeMask;
 
     // Method name is immediately after header
-    uint32_t methodNameOff = m_methodListHeaderSize;
+    uint32_t methodNameOff = methodListHeaderSize;
 
     // Loop through all methods, and ensure a selref for each of them exists.
     while (methodNameOff < isec->data.size()) {
@@ -2016,19 +2016,19 @@ void ObjCMethListSection::setUp() {
 // Calculate section size and final offsets for where InputSection's need to be
 // written.
 void ObjCMethListSection::finalize() {
-  // m_size will be the total size of the __objc_methlist section
-  m_size = 0;
+  // sectionSize will be the total size of the __objc_methlist section
+  sectionSize = 0;
   for (ConcatInputSection *isec : inputs) {
-    // We can also use m_size as write offset for isec
-    assert(m_size == alignToPowerOf2(m_size, m_align) &&
+    // We can also use sectionSize as write offset for isec
+    assert(sectionSize == alignToPowerOf2(sectionSize, relativeOffsetSize) &&
            "expected __objc_methlist to be aligned by default with the "
            "required section alignment");
-    isec->outSecOff = m_size;
+    isec->outSecOff = sectionSize;
 
     isec->isFinal = true;
     uint32_t relativeListSize =
-        methodListSizeToRelativeMethodListSize(isec->data.size());
-    m_size += relativeListSize;
+        computeRelativeMethodListSize(isec->data.size());
+    sectionSize += relativeListSize;
 
     // If encoding the method list in relative offset format shrinks the size,
     // then we also need to adjust symbol sizes to match the new size. Note that
@@ -2061,7 +2061,7 @@ void ObjCMethListSection::writeTo(uint8_t *bufStart) const {
     uint32_t writtenSize = writeRelativeMethodList(isec, buf);
     buf += writtenSize;
   }
-  assert(buf - bufStart == m_size &&
+  assert(buf - bufStart == sectionSize &&
          "Written size does not match expected section size");
 }
 
@@ -2122,8 +2122,10 @@ void ObjCMethListSection::writeRelativeOffsetForIsec(
   uint32_t delta = symVA - currentVA;
   write32le(buf + outSecOff, delta);
 
+  // Move one pointer forward in the absolute method list
   inSecOff += target->wordSize;
-  outSecOff += sizeof(uint32_t);
+  // Move one relative offset forward in the relative method list (32 bits)
+  outSecOff += relativeOffsetSize;
 }
 
 // Write a relative method list to buf, return the size of the written
@@ -2135,16 +2137,16 @@ ObjCMethListSection::writeRelativeMethodList(const ConcatInputSection *isec,
   // value flag
   uint32_t structSizeAndFlags = 0, structCount = 0;
   readMethodListHeader(isec->data.data(), structSizeAndFlags, structCount);
-  structSizeAndFlags |= m_relMethodHeaderFlag;
+  structSizeAndFlags |= relMethodHeaderFlag;
   writeMethodListHeader(buf, structSizeAndFlags, structCount);
 
-  assert(m_methodListHeaderSize +
-                 (structCount * m_pointersPerStruct * target->wordSize) ==
+  assert(methodListHeaderSize +
+                 (structCount * pointersPerStruct * target->wordSize) ==
              isec->data.size() &&
          "Invalid computed ObjC method list size");
 
-  uint32_t inSecOff = m_methodListHeaderSize;
-  uint32_t outSecOff = m_methodListHeaderSize;
+  uint32_t inSecOff = methodListHeaderSize;
+  uint32_t outSecOff = methodListHeaderSize;
 
   // Go through the method list and encode input absolute pointers as relative
   // offsets. writeRelativeOffsetForIsec will be incrementing inSecOff and
@@ -2162,27 +2164,26 @@ ObjCMethListSection::writeRelativeMethodList(const ConcatInputSection *isec,
   assert(inSecOff == isec->data.size() &&
          "Invalid actual ObjC method list size");
   assert(
-      outSecOff == methodListSizeToRelativeMethodListSize(inSecOff) &&
+      outSecOff == computeRelativeMethodListSize(inSecOff) &&
       "Mismatch between input & output size when writing relative method list");
   return outSecOff;
 }
 
 // Given the size of an ObjC method list InputSection, return the size of the
 // method list when encoded in relative offsets format. We can do this without
-// decoding the actual data, as it can be directly infered from the size of the
+// decoding the actual data, as it can be directly inferred from the size of the
 // isec.
-uint32_t
-ObjCMethListSection::methodListSizeToRelativeMethodListSize(uint32_t iSecSize) {
-  uint32_t oldPointersSize = iSecSize - m_methodListHeaderSize;
+uint32_t ObjCMethListSection::computeRelativeMethodListSize(
+    uint32_t absoluteMethodListSize) const {
+  uint32_t oldPointersSize = absoluteMethodListSize - methodListHeaderSize;
   uint32_t pointerCount = oldPointersSize / target->wordSize;
-  assert(((pointerCount % m_pointersPerStruct) == 0) &&
+  assert(((pointerCount % pointersPerStruct) == 0) &&
          "__objc_methlist expects method lists to have multiple-of-3 pointers");
 
-  constexpr uint32_t sizeOfRelativeOffset = sizeof(uint32_t);
-  uint32_t newPointersSize = pointerCount * sizeOfRelativeOffset;
-  uint32_t newTotalSize = m_methodListHeaderSize + newPointersSize;
+  uint32_t newPointersSize = pointerCount * relativeOffsetSize;
+  uint32_t newTotalSize = methodListHeaderSize + newPointersSize;
 
-  assert((newTotalSize <= iSecSize) &&
+  assert((newTotalSize <= absoluteMethodListSize) &&
          "Expected relative method list size to be smaller or equal than "
          "original size");
   return newTotalSize;
@@ -2191,7 +2192,7 @@ ObjCMethListSection::methodListSizeToRelativeMethodListSize(uint32_t iSecSize) {
 // Read a method list header from buf
 void ObjCMethListSection::readMethodListHeader(const uint8_t *buf,
                                                uint32_t &structSizeAndFlags,
-                                               uint32_t &structCount) {
+                                               uint32_t &structCount) const {
   structSizeAndFlags = read32le(buf);
   structCount = read32le(buf + sizeof(uint32_t));
 }
@@ -2199,7 +2200,7 @@ void ObjCMethListSection::readMethodListHeader(const uint8_t *buf,
 // Write a method list header to buf
 void ObjCMethListSection::writeMethodListHeader(uint8_t *buf,
                                                 uint32_t structSizeAndFlags,
-                                                uint32_t structCount) {
+                                                uint32_t structCount) const {
   write32le(buf, structSizeAndFlags);
   write32le(buf + sizeof(structSizeAndFlags), structCount);
 }
diff --git a/lld/MachO/SyntheticSections.h b/lld/MachO/SyntheticSections.h
index bfbf9af9ef34fb..7b7899e1dbcdb7 100644
--- a/lld/MachO/SyntheticSections.h
+++ b/lld/MachO/SyntheticSections.h
@@ -697,37 +697,36 @@ class ObjCMethListSection final : public SyntheticSection {
   void setUp();
   void finalize() override;
   bool isNeeded() const override { return !inputs.empty(); }
-  uint64_t getSize() const override { return m_size; }
+  uint64_t getSize() const override { return sectionSize; }
   void writeTo(uint8_t *bufStart) const override;
 
 private:
-  static void readMethodListHeader(const uint8_t *buf,
-                                   uint32_t &structSizeAndFlags,
-                                   uint32_t &structCount);
-  static void writeMethodListHeader(uint8_t *buf, uint32_t structSizeAndFlags,
-                                    uint32_t structCount);
-  static uint32_t methodListSizeToRelativeMethodListSize(uint32_t iSecSize);
+  void readMethodListHeader(const uint8_t *buf, uint32_t &structSizeAndFlags,
+                            uint32_t &structCount) const;
+  void writeMethodListHeader(uint8_t *buf, uint32_t structSizeAndFlags,
+                             uint32_t structCount) const;
+  uint32_t computeRelativeMethodListSize(uint32_t absoluteMethodListSize) const;
   void writeRelativeOffsetForIsec(const ConcatInputSection *isec, uint8_t *buf,
                                   uint32_t &inSecOff, uint32_t &outSecOff,
                                   bool useSelRef) const;
   uint32_t writeRelativeMethodList(const ConcatInputSection *isec,
                                    uint8_t *buf) const;
 
-  static constexpr uint32_t m_methodListHeaderSize =
+  static constexpr uint32_t methodListHeaderSize =
       /*structSizeAndFlags*/ sizeof(uint32_t) +
       /*structCount*/ sizeof(uint32_t);
   // Relative method lists are supported only for 3-pointer method lists
-  static constexpr uint32_t m_pointersPerStruct = 3;
+  static constexpr uint32_t pointersPerStruct = 3;
   // The runtime identifies relative method lists via this magic value
-  static constexpr uint32_t m_relMethodHeaderFlag = 0x80000000;
+  static constexpr uint32_t relMethodHeaderFlag = 0x80000000;
   // In the method list header, the first 2 bytes are the size of struct
   static constexpr uint32_t m_structSizeMask = 0xFFFF;
   // Relative method lists have 4 byte alignment as all data in the InputSection
   // is 4 byte
-  static constexpr uint32_t m_align = 4;
+  static constexpr uint32_t relativeOffsetSize = sizeof(uint32_t);
 
   // The output size of the __objc_methlist section, computed during finalize()
-  uint32_t m_size = 0;
+  uint32_t sectionSize = 0;
   std::vector<ConcatInputSection *> inputs;
 };
 
diff --git a/lld/MachO/Writer.cpp b/lld/MachO/Writer.cpp
index 9c0f8949bd0200..fe989de648d789 100644
--- a/lld/MachO/Writer.cpp
+++ b/lld/MachO/Writer.cpp
@@ -1292,7 +1292,7 @@ template <class LP> void Writer::run() {
   scanSymbols();
   if (in.objcStubs->isNeeded())
     in.objcStubs->setUp();
-  if (in.objcMethList)
+  if (in.objcMethList->isNeeded())
     in.objcMethList->setUp();
   scanRelocations();
   if (in.initOffsets->isNeeded())
@@ -1365,9 +1365,7 @@ void macho::createSyntheticSections() {
   in.unwindInfo = makeUnwindInfoSection();
   in.objCImageInfo = make<ObjCImageInfoSection>();
   in.initOffsets = make<InitOffsetsSection>();
-  // Only create ObjCMethListSection if we need it
-  if (config->emitRelativeMethodLists)
-    in.objcMethList = make<ObjCMethListSection>();
+  in.objcMethList = make<ObjCMethListSection>();
 
   // This section contains space for just a single word, and will be used by
   // dyld to cache an address to the image loader it uses.
diff --git a/lld/test/MachO/objc-relative-method-lists-simple.s b/lld/test/MachO/objc-relative-method-lists-simple.s
index 860236b737b832..271b681eea320e 100644
--- a/lld/test/MachO/objc-relative-method-lists-simple.s
+++ b/lld/test/MachO/objc-relative-method-lists-simple.s
@@ -5,14 +5,13 @@
 # RUN: llvm-mc -filetype=obj -triple=arm64-apple-macos -o a64_rel_dylib.o a64_simple_class.s
 
 ## Test arm64 + relative method lists
-# RUN: ld64.lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -platform_version macos 11.0.0 11.0.0 -objc_relative_method_lists
+# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -objc_relative_method_lists
 # RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib  | FileCheck %s --check-prefix=CHK_REL
 
 ## Test arm64 + traditional method lists (no relative offsets)
-# RUN: ld64.lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -platform_version macos 11.0.0 11.0.0 -no_objc_relative_method_lists
+# RUN: %no-lsystem-lld a64_rel_dylib.o -o a64_rel_dylib.dylib -map a64_rel_dylib.map -dylib -arch arm64 -no_objc_relative_method_lists
 # RUN: llvm-objdump --macho --objc-meta-data a64_rel_dylib.dylib  | FileCheck %s --check-prefix=CHK_NO_REL
 
-CHK_NO_REL-NOT: (relative)
 
 CHK_REL:       Contents of (__DATA_CONST,__objc_classlist) section
 CHK_REL-NEXT:  _OBJC_CLASS_$_MyClass
@@ -45,6 +44,41 @@ CHK_REL-NEXT:  types 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  v16 at 0:8
 CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  +[MyClass class_method_02]
 
 
+CHK_NO_REL-NOT: (relative)
+
+CHK_NO_REL:           Contents of (__DATA_CONST,__objc_classlist) section
+CHK_NO_REL-NEXT:      _OBJC_CLASS_$_MyClass
+
+CHK_NO_REL:            baseMethods 0x80e8 (struct method_list_t *)
+CHK_NO_REL-NEXT:		   entsize 24
+CHK_NO_REL-NEXT:		     count 3
+CHK_NO_REL-NEXT:		      name 0x5b8 instance_method_00
+CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		       imp -[MyClass instance_method_00]
+CHK_NO_REL-NEXT:		      name 0x5cb instance_method_01
+CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		       imp -[MyClass instance_method_01]
+CHK_NO_REL-NEXT:		      name 0x5de instance_method_02
+CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		       imp -[MyClass instance_method_02]
+
+
+CHK_NO_REL:             Meta Class
+CHK_NO_REL-NEXT:        _OBJC_METACLASS_$_MyClass
+
+CHK_NO_REL:             baseMethods 0x8050 (struct method_list_t *)
+CHK_NO_REL-NEXT:		   entsize 24
+CHK_NO_REL-NEXT:		     count 3
+CHK_NO_REL-NEXT:		      name 0x588 class_method_00
+CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		       imp +[MyClass class_method_00]
+CHK_NO_REL-NEXT:		      name 0x598 class_method_01
+CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		       imp +[MyClass class_method_01]
+CHK_NO_REL-NEXT:		      name 0x5a8 class_method_02
+CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		       imp +[MyClass class_method_02]
+
 
 ######################## Generate a64_simple_class.s #########################
 # clang -c simple_class.mm -s -o a64_simple_class.s -target arm64-apple-macos -arch arm64 -Oz

>From 75245988698c6fe6939f2cbb4a83ff8010ed0e66 Mon Sep 17 00:00:00 2001
From: Alex B <alexborcan at meta.com>
Date: Mon, 25 Mar 2024 16:43:22 -0700
Subject: [PATCH 3/3] Feedback #2

---
 .../MachO/objc-relative-method-lists-simple.s | 30 +++++++++----------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/lld/test/MachO/objc-relative-method-lists-simple.s b/lld/test/MachO/objc-relative-method-lists-simple.s
index 271b681eea320e..f6e452930b8a01 100644
--- a/lld/test/MachO/objc-relative-method-lists-simple.s
+++ b/lld/test/MachO/objc-relative-method-lists-simple.s
@@ -30,7 +30,7 @@ CHK_REL-NEXT:    imp 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}}) -[MyClass instance_method
 
 CHK_REL:       Meta Class
 CHK_REL-NEXT:  isa 0x{{[0-9a-f]*}} _OBJC_METACLASS_$_MyClass
-CHK_REL:       baseMethods 0x694 (struct method_list_t *)
+CHK_REL:       baseMethods 0x{{[0-9a-f]*}} (struct method_list_t *)
 CHK_REL-NEXT:  entsize 24 (relative)
 CHK_REL-NEXT:  count 3
 CHK_REL-NEXT:   name 0x{{[0-9a-f]*}} (0x{{[0-9a-f]*}})  class_method_00
@@ -49,34 +49,34 @@ CHK_NO_REL-NOT: (relative)
 CHK_NO_REL:           Contents of (__DATA_CONST,__objc_classlist) section
 CHK_NO_REL-NEXT:      _OBJC_CLASS_$_MyClass
 
-CHK_NO_REL:            baseMethods 0x80e8 (struct method_list_t *)
+CHK_NO_REL:            baseMethods 0x{{[0-9a-f]*}} (struct method_list_t *)
 CHK_NO_REL-NEXT:		   entsize 24
 CHK_NO_REL-NEXT:		     count 3
-CHK_NO_REL-NEXT:		      name 0x5b8 instance_method_00
-CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		      name 0x{{[0-9a-f]*}} instance_method_00
+CHK_NO_REL-NEXT:		     types 0x{{[0-9a-f]*}} v16 at 0:8
 CHK_NO_REL-NEXT:		       imp -[MyClass instance_method_00]
-CHK_NO_REL-NEXT:		      name 0x5cb instance_method_01
-CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		      name 0x{{[0-9a-f]*}} instance_method_01
+CHK_NO_REL-NEXT:		     types 0x{{[0-9a-f]*}} v16 at 0:8
 CHK_NO_REL-NEXT:		       imp -[MyClass instance_method_01]
-CHK_NO_REL-NEXT:		      name 0x5de instance_method_02
-CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		      name 0x{{[0-9a-f]*}} instance_method_02
+CHK_NO_REL-NEXT:		     types 0x{{[0-9a-f]*}} v16 at 0:8
 CHK_NO_REL-NEXT:		       imp -[MyClass instance_method_02]
 
 
 CHK_NO_REL:             Meta Class
 CHK_NO_REL-NEXT:        _OBJC_METACLASS_$_MyClass
 
-CHK_NO_REL:             baseMethods 0x8050 (struct method_list_t *)
+CHK_NO_REL:             baseMethods 0x{{[0-9a-f]*}} (struct method_list_t *)
 CHK_NO_REL-NEXT:		   entsize 24
 CHK_NO_REL-NEXT:		     count 3
-CHK_NO_REL-NEXT:		      name 0x588 class_method_00
-CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		      name 0x{{[0-9a-f]*}} class_method_00
+CHK_NO_REL-NEXT:		     types 0x{{[0-9a-f]*}} v16 at 0:8
 CHK_NO_REL-NEXT:		       imp +[MyClass class_method_00]
-CHK_NO_REL-NEXT:		      name 0x598 class_method_01
-CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		      name 0x{{[0-9a-f]*}} class_method_01
+CHK_NO_REL-NEXT:		     types 0x{{[0-9a-f]*}} v16 at 0:8
 CHK_NO_REL-NEXT:		       imp +[MyClass class_method_01]
-CHK_NO_REL-NEXT:		      name 0x5a8 class_method_02
-CHK_NO_REL-NEXT:		     types 0x580 v16 at 0:8
+CHK_NO_REL-NEXT:		      name 0x{{[0-9a-f]*}} class_method_02
+CHK_NO_REL-NEXT:		     types 0x{{[0-9a-f]*}} v16 at 0:8
 CHK_NO_REL-NEXT:		       imp +[MyClass class_method_02]
 
 



More information about the llvm-commits mailing list