[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