brintos

brintos / llvm-project-archived public Read only

0
0
Text · 59.4 KiB · ab7f73c Raw
1512 lines · cpp
1//===- ObjC.cpp -----------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "ObjC.h"10#include "ConcatOutputSection.h"11#include "InputFiles.h"12#include "InputSection.h"13#include "Layout.h"14#include "OutputSegment.h"15#include "SyntheticSections.h"16#include "Target.h"17 18#include "lld/Common/ErrorHandler.h"19#include "llvm/ADT/DenseMap.h"20#include "llvm/Bitcode/BitcodeReader.h"21#include "llvm/Support/TimeProfiler.h"22 23using namespace llvm;24using namespace llvm::MachO;25using namespace lld;26using namespace lld::macho;27 28template <class LP> static bool objectHasObjCSection(MemoryBufferRef mb) {29  using SectionHeader = typename LP::section;30 31  auto *hdr =32      reinterpret_cast<const typename LP::mach_header *>(mb.getBufferStart());33  if (hdr->magic != LP::magic)34    return false;35 36  if (const auto *c =37          findCommand<typename LP::segment_command>(hdr, LP::segmentLCType)) {38    auto sectionHeaders = ArrayRef<SectionHeader>{39        reinterpret_cast<const SectionHeader *>(c + 1), c->nsects};40    for (const SectionHeader &secHead : sectionHeaders) {41      StringRef sectname(secHead.sectname,42                         strnlen(secHead.sectname, sizeof(secHead.sectname)));43      StringRef segname(secHead.segname,44                        strnlen(secHead.segname, sizeof(secHead.segname)));45      if ((segname == segment_names::data &&46           sectname == section_names::objcCatList) ||47          (segname == segment_names::text &&48           sectname.starts_with(section_names::swift))) {49        return true;50      }51    }52  }53  return false;54}55 56static bool objectHasObjCSection(MemoryBufferRef mb) {57  if (target->wordSize == 8)58    return ::objectHasObjCSection<LP64>(mb);59  else60    return ::objectHasObjCSection<ILP32>(mb);61}62 63bool macho::hasObjCSection(MemoryBufferRef mb) {64  switch (identify_magic(mb.getBuffer())) {65  case file_magic::macho_object:66    return objectHasObjCSection(mb);67  case file_magic::bitcode:68    return check(isBitcodeContainingObjCCategory(mb));69  default:70    return false;71  }72}73 74namespace {75 76#define FOR_EACH_CATEGORY_FIELD(DO)                                            \77  DO(Ptr, name)                                                                \78  DO(Ptr, klass)                                                               \79  DO(Ptr, instanceMethods)                                                     \80  DO(Ptr, classMethods)                                                        \81  DO(Ptr, protocols)                                                           \82  DO(Ptr, instanceProps)                                                       \83  DO(Ptr, classProps)                                                          \84  DO(uint32_t, size)85 86CREATE_LAYOUT_CLASS(Category, FOR_EACH_CATEGORY_FIELD);87 88#undef FOR_EACH_CATEGORY_FIELD89 90#define FOR_EACH_CLASS_FIELD(DO)                                               \91  DO(Ptr, metaClass)                                                           \92  DO(Ptr, superClass)                                                          \93  DO(Ptr, methodCache)                                                         \94  DO(Ptr, vtable)                                                              \95  DO(Ptr, roData)96 97CREATE_LAYOUT_CLASS(Class, FOR_EACH_CLASS_FIELD);98 99#undef FOR_EACH_CLASS_FIELD100 101#define FOR_EACH_RO_CLASS_FIELD(DO)                                            \102  DO(uint32_t, flags)                                                          \103  DO(uint32_t, instanceStart)                                                  \104  DO(Ptr, instanceSize)                                                        \105  DO(Ptr, ivarLayout)                                                          \106  DO(Ptr, name)                                                                \107  DO(Ptr, baseMethods)                                                         \108  DO(Ptr, baseProtocols)                                                       \109  DO(Ptr, ivars)                                                               \110  DO(Ptr, weakIvarLayout)                                                      \111  DO(Ptr, baseProperties)112 113CREATE_LAYOUT_CLASS(ROClass, FOR_EACH_RO_CLASS_FIELD);114 115#undef FOR_EACH_RO_CLASS_FIELD116 117#define FOR_EACH_LIST_HEADER(DO)                                               \118  DO(uint32_t, structSize)                                                     \119  DO(uint32_t, structCount)120 121CREATE_LAYOUT_CLASS(ListHeader, FOR_EACH_LIST_HEADER);122 123#undef FOR_EACH_LIST_HEADER124 125#define FOR_EACH_PROTOCOL_LIST_HEADER(DO) DO(Ptr, protocolCount)126 127CREATE_LAYOUT_CLASS(ProtocolListHeader, FOR_EACH_PROTOCOL_LIST_HEADER);128 129#undef FOR_EACH_PROTOCOL_LIST_HEADER130 131#define FOR_EACH_METHOD(DO)                                                    \132  DO(Ptr, name)                                                                \133  DO(Ptr, type)                                                                \134  DO(Ptr, impl)135 136CREATE_LAYOUT_CLASS(Method, FOR_EACH_METHOD);137 138#undef FOR_EACH_METHOD139 140enum MethodContainerKind {141  MCK_Class,142  MCK_Category,143};144 145struct MethodContainer {146  MethodContainerKind kind;147  const ConcatInputSection *isec;148};149 150enum MethodKind {151  MK_Instance,152  MK_Static,153};154 155struct ObjcClass {156  DenseMap<CachedHashStringRef, MethodContainer> instanceMethods;157  DenseMap<CachedHashStringRef, MethodContainer> classMethods;158};159 160} // namespace161 162class ObjcCategoryChecker {163public:164  ObjcCategoryChecker();165  void parseCategory(const ConcatInputSection *catListIsec);166 167private:168  void parseClass(const Defined *classSym);169  void parseMethods(const ConcatInputSection *methodsIsec,170                    const Symbol *methodContainer,171                    const ConcatInputSection *containerIsec,172                    MethodContainerKind, MethodKind);173 174  CategoryLayout catLayout;175  ClassLayout classLayout;176  ROClassLayout roClassLayout;177  ListHeaderLayout listHeaderLayout;178  MethodLayout methodLayout;179 180  DenseMap<const Symbol *, ObjcClass> classMap;181};182 183ObjcCategoryChecker::ObjcCategoryChecker()184    : catLayout(target->wordSize), classLayout(target->wordSize),185      roClassLayout(target->wordSize), listHeaderLayout(target->wordSize),186      methodLayout(target->wordSize) {}187 188void ObjcCategoryChecker::parseMethods(const ConcatInputSection *methodsIsec,189                                       const Symbol *methodContainerSym,190                                       const ConcatInputSection *containerIsec,191                                       MethodContainerKind mcKind,192                                       MethodKind mKind) {193  ObjcClass &klass = classMap[methodContainerSym];194  for (const Reloc &r : methodsIsec->relocs) {195    if ((r.offset - listHeaderLayout.totalSize) % methodLayout.totalSize !=196        methodLayout.nameOffset)197      continue;198 199    CachedHashStringRef methodName(r.getReferentString());200    // +load methods are special: all implementations are called by the runtime201    // even if they are part of the same class. Thus there is no need to check202    // for duplicates.203    // NOTE: Instead of specifically checking for this method name, ld64 simply204    // checks whether a class / category is present in __objc_nlclslist /205    // __objc_nlcatlist respectively. This will be the case if the class /206    // category has a +load method. It skips optimizing the categories if there207    // are multiple +load methods. Since it does dupe checking as part of the208    // optimization process, this avoids spurious dupe messages around +load,209    // but it also means that legit dupe issues for other methods are ignored.210    if (mKind == MK_Static && methodName.val() == "load")211      continue;212 213    auto &methodMap =214        mKind == MK_Instance ? klass.instanceMethods : klass.classMethods;215    if (methodMap216            .try_emplace(methodName, MethodContainer{mcKind, containerIsec})217            .second)218      continue;219 220    // We have a duplicate; generate a warning message.221    const auto &mc = methodMap.lookup(methodName);222    const Reloc *nameReloc = nullptr;223    if (mc.kind == MCK_Category) {224      nameReloc = mc.isec->getRelocAt(catLayout.nameOffset);225    } else {226      assert(mc.kind == MCK_Class);227      const auto *roIsec = mc.isec->getRelocAt(classLayout.roDataOffset)228                         ->getReferentInputSection();229      nameReloc = roIsec->getRelocAt(roClassLayout.nameOffset);230    }231    StringRef containerName = nameReloc->getReferentString();232    StringRef methPrefix = mKind == MK_Instance ? "-" : "+";233 234    // We should only ever encounter collisions when parsing category methods235    // (since the Class struct is parsed before any of its categories).236    assert(mcKind == MCK_Category);237    StringRef newCatName =238        containerIsec->getRelocAt(catLayout.nameOffset)->getReferentString();239 240    auto formatObjAndSrcFileName = [](const InputSection *section) {241      lld::macho::InputFile *inputFile = section->getFile();242      std::string result = toString(inputFile);243 244      auto objFile = dyn_cast_or_null<ObjFile>(inputFile);245      if (objFile && objFile->compileUnit)246        result += " (" + objFile->sourceFile() + ")";247 248      return result;249    };250 251    StringRef containerType = mc.kind == MCK_Category ? "category" : "class";252    warn("method '" + methPrefix + methodName.val() +253         "' has conflicting definitions:\n>>> defined in category " +254         newCatName + " from " + formatObjAndSrcFileName(containerIsec) +255         "\n>>> defined in " + containerType + " " + containerName + " from " +256         formatObjAndSrcFileName(mc.isec));257  }258}259 260void ObjcCategoryChecker::parseCategory(const ConcatInputSection *catIsec) {261  auto *classReloc = catIsec->getRelocAt(catLayout.klassOffset);262  if (!classReloc)263    return;264 265  auto *classSym = cast<Symbol *>(classReloc->referent);266  if (auto *d = dyn_cast<Defined>(classSym))267    if (!classMap.count(d))268      parseClass(d);269 270  if (const auto *r = catIsec->getRelocAt(catLayout.classMethodsOffset)) {271    parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()),272                 classSym, catIsec, MCK_Category, MK_Static);273  }274 275  if (const auto *r = catIsec->getRelocAt(catLayout.instanceMethodsOffset)) {276    parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()),277                 classSym, catIsec, MCK_Category, MK_Instance);278  }279}280 281void ObjcCategoryChecker::parseClass(const Defined *classSym) {282  // Given a Class struct, get its corresponding Methods struct283  auto getMethodsIsec =284      [&](const InputSection *classIsec) -> ConcatInputSection * {285    if (const auto *r = classIsec->getRelocAt(classLayout.roDataOffset)) {286      if (const auto *roIsec =287              cast_or_null<ConcatInputSection>(r->getReferentInputSection())) {288        if (const auto *r =289                roIsec->getRelocAt(roClassLayout.baseMethodsOffset)) {290          if (auto *methodsIsec = cast_or_null<ConcatInputSection>(291                  r->getReferentInputSection()))292            return methodsIsec;293        }294      }295    }296    return nullptr;297  };298 299  const auto *classIsec = cast<ConcatInputSection>(classSym->isec());300 301  // Parse instance methods.302  if (const auto *instanceMethodsIsec = getMethodsIsec(classIsec))303    parseMethods(instanceMethodsIsec, classSym, classIsec, MCK_Class,304                 MK_Instance);305 306  // Class methods are contained in the metaclass.307  if (const auto *r = classSym->isec()->getRelocAt(classLayout.metaClassOffset))308    if (const auto *classMethodsIsec = getMethodsIsec(309            cast<ConcatInputSection>(r->getReferentInputSection())))310      parseMethods(classMethodsIsec, classSym, classIsec, MCK_Class, MK_Static);311}312 313void objc::checkCategories() {314  TimeTraceScope timeScope("ObjcCategoryChecker");315 316  ObjcCategoryChecker checker;317  for (const InputSection *isec : inputSections) {318    if (isec->getName() == section_names::objcCatList)319      for (const Reloc &r : isec->relocs) {320        auto *catIsec = cast<ConcatInputSection>(r.getReferentInputSection());321        checker.parseCategory(catIsec);322      }323  }324}325 326namespace {327 328class ObjcCategoryMerger {329  // In which language was a particular construct originally defined330  enum SourceLanguage { Unknown, ObjC, Swift };331 332  // Information about an input category333  struct InfoInputCategory {334    ConcatInputSection *catListIsec;335    ConcatInputSection *catBodyIsec;336    uint32_t offCatListIsec = 0;337    SourceLanguage sourceLanguage = SourceLanguage::Unknown;338 339    bool wasMerged = false;340  };341 342  // To write new (merged) categories or classes, we will try make limited343  // assumptions about the alignment and the sections the various class/category344  // info are stored in and . So we'll just reuse the same sections and345  // alignment as already used in existing (input) categories. To do this we346  // have InfoCategoryWriter which contains the various sections that the347  // generated categories will be written to.348  struct InfoWriteSection {349    bool valid = false; // Data has been successfully collected from input350    uint32_t align = 0;351    Section *inputSection;352    Reloc relocTemplate;353    OutputSection *outputSection;354  };355 356  struct InfoCategoryWriter {357    InfoWriteSection catListInfo;358    InfoWriteSection catBodyInfo;359    InfoWriteSection catNameInfo;360    InfoWriteSection catPtrListInfo;361  };362 363  // Information about a pointer list in the original categories or class(method364  // lists, protocol lists, etc)365  struct PointerListInfo {366    PointerListInfo() = default;367    PointerListInfo(const PointerListInfo &) = default;368    PointerListInfo(const char *_categoryPrefix, uint32_t _pointersPerStruct)369        : categoryPrefix(_categoryPrefix),370          pointersPerStruct(_pointersPerStruct) {}371 372    inline bool operator==(const PointerListInfo &cmp) const {373      return pointersPerStruct == cmp.pointersPerStruct &&374             structSize == cmp.structSize && structCount == cmp.structCount &&375             allPtrs == cmp.allPtrs;376    }377 378    const char *categoryPrefix;379 380    uint32_t pointersPerStruct = 0;381 382    uint32_t structSize = 0;383    uint32_t structCount = 0;384 385    std::vector<Symbol *> allPtrs;386  };387 388  // Full information describing an ObjC class . This will include all the389  // additional methods, protocols, and properties that are contained in the390  // class and all the categories that extend a particular class.391  struct ClassExtensionInfo {392    ClassExtensionInfo(CategoryLayout &_catLayout) : catLayout(_catLayout){};393 394    // Merged names of containers. Ex: base|firstCategory|secondCategory|...395    std::string mergedContainerName;396    std::string baseClassName;397    const Symbol *baseClass = nullptr;398    SourceLanguage baseClassSourceLanguage = SourceLanguage::Unknown;399 400    CategoryLayout &catLayout;401 402    // In case we generate new data, mark the new data as belonging to this file403    ObjFile *objFileForMergeData = nullptr;404 405    PointerListInfo instanceMethods = {objc::symbol_names::instanceMethods,406                                       /*pointersPerStruct=*/3};407    PointerListInfo classMethods = {objc::symbol_names::categoryClassMethods,408                                    /*pointersPerStruct=*/3};409    PointerListInfo protocols = {objc::symbol_names::categoryProtocols,410                                 /*pointersPerStruct=*/0};411    PointerListInfo instanceProps = {objc::symbol_names::listProprieties,412                                     /*pointersPerStruct=*/2};413    PointerListInfo classProps = {objc::symbol_names::klassPropList,414                                  /*pointersPerStruct=*/2};415  };416 417public:418  ObjcCategoryMerger(std::vector<ConcatInputSection *> &_allInputSections);419  void doMerge();420  static void doCleanup();421 422private:423  DenseSet<const Symbol *> collectNlCategories();424  void collectAndValidateCategoriesData();425  bool426  mergeCategoriesIntoSingleCategory(std::vector<InfoInputCategory> &categories);427 428  void eraseISec(ConcatInputSection *isec);429  void eraseMergedCategories();430 431  void generateCatListForNonErasedCategories(432      MapVector<ConcatInputSection *, std::set<uint64_t>>433          catListToErasedOffsets);434  void collectSectionWriteInfoFromIsec(const InputSection *isec,435                                       InfoWriteSection &catWriteInfo);436  bool collectCategoryWriterInfoFromCategory(const InfoInputCategory &catInfo);437  bool parseCatInfoToExtInfo(const InfoInputCategory &catInfo,438                             ClassExtensionInfo &extInfo);439 440  void parseProtocolListInfo(const ConcatInputSection *isec, uint32_t secOffset,441                             PointerListInfo &ptrList,442                             SourceLanguage sourceLang);443 444  PointerListInfo parseProtocolListInfo(const ConcatInputSection *isec,445                                        uint32_t secOffset,446                                        SourceLanguage sourceLang);447 448  bool parsePointerListInfo(const ConcatInputSection *isec, uint32_t secOffset,449                            PointerListInfo &ptrList);450 451  void emitAndLinkPointerList(Defined *parentSym, uint32_t linkAtOffset,452                              const ClassExtensionInfo &extInfo,453                              const PointerListInfo &ptrList);454 455  Defined *emitAndLinkProtocolList(Defined *parentSym, uint32_t linkAtOffset,456                                   const ClassExtensionInfo &extInfo,457                                   const PointerListInfo &ptrList);458 459  Defined *emitCategory(const ClassExtensionInfo &extInfo);460  Defined *emitCatListEntrySec(const std::string &forCategoryName,461                               const std::string &forBaseClassName,462                               ObjFile *objFile);463  Defined *emitCategoryBody(const std::string &name, const Defined *nameSym,464                            const Symbol *baseClassSym,465                            const std::string &baseClassName, ObjFile *objFile);466  Defined *emitCategoryName(const std::string &name, ObjFile *objFile);467  void createSymbolReference(Defined *refFrom, const Symbol *refTo,468                             uint32_t offset, const Reloc &relocTemplate);469  Defined *tryFindDefinedOnIsec(const InputSection *isec, uint32_t offset);470  Symbol *tryGetSymbolAtIsecOffset(const ConcatInputSection *isec,471                                   uint32_t offset);472  Defined *tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,473                                     uint32_t offset);474  Defined *getClassRo(const Defined *classSym, bool getMetaRo);475  SourceLanguage getClassSymSourceLang(const Defined *classSym);476  bool mergeCategoriesIntoBaseClass(const Defined *baseClass,477                                    std::vector<InfoInputCategory> &categories);478  void eraseSymbolAtIsecOffset(ConcatInputSection *isec, uint32_t offset);479  void tryEraseDefinedAtIsecOffset(const ConcatInputSection *isec,480                                   uint32_t offset);481 482  // Allocate a null-terminated StringRef backed by generatedSectionData483  StringRef newStringData(const char *str);484  // Allocate section data, backed by generatedSectionData485  SmallVector<uint8_t> &newSectionData(uint32_t size);486 487  CategoryLayout catLayout;488  ClassLayout classLayout;489  ROClassLayout roClassLayout;490  ListHeaderLayout listHeaderLayout;491  MethodLayout methodLayout;492  ProtocolListHeaderLayout protocolListHeaderLayout;493 494  InfoCategoryWriter infoCategoryWriter;495  std::vector<ConcatInputSection *> &allInputSections;496  // Map of base class Symbol to list of InfoInputCategory's for it497  MapVector<const Symbol *, std::vector<InfoInputCategory>> categoryMap;498 499  // Normally, the binary data comes from the input files, but since we're500  // generating binary data ourselves, we use the below array to store it in.501  // Need this to be 'static' so the data survives past the ObjcCategoryMerger502  // object, as the data will be read by the Writer when the final binary is503  // generated.504  static SmallVector<std::unique_ptr<SmallVector<uint8_t>>>505      generatedSectionData;506};507 508SmallVector<std::unique_ptr<SmallVector<uint8_t>>>509    ObjcCategoryMerger::generatedSectionData;510 511ObjcCategoryMerger::ObjcCategoryMerger(512    std::vector<ConcatInputSection *> &_allInputSections)513    : catLayout(target->wordSize), classLayout(target->wordSize),514      roClassLayout(target->wordSize), listHeaderLayout(target->wordSize),515      methodLayout(target->wordSize),516      protocolListHeaderLayout(target->wordSize),517      allInputSections(_allInputSections) {}518 519void ObjcCategoryMerger::collectSectionWriteInfoFromIsec(520    const InputSection *isec, InfoWriteSection &catWriteInfo) {521 522  catWriteInfo.inputSection = const_cast<Section *>(&isec->section);523  catWriteInfo.align = isec->align;524  catWriteInfo.outputSection = isec->parent;525 526  assert(catWriteInfo.outputSection &&527         "outputSection may not be null in collectSectionWriteInfoFromIsec.");528 529  if (isec->relocs.size())530    catWriteInfo.relocTemplate = isec->relocs[0];531 532  catWriteInfo.valid = true;533}534 535Symbol *536ObjcCategoryMerger::tryGetSymbolAtIsecOffset(const ConcatInputSection *isec,537                                             uint32_t offset) {538  if (!isec)539    return nullptr;540  const Reloc *reloc = isec->getRelocAt(offset);541 542  if (!reloc)543    return nullptr;544 545  Symbol *sym = dyn_cast_if_present<Symbol *>(reloc->referent);546 547  if (reloc->addend && sym) {548    assert(isa<Defined>(sym) && "Expected defined for non-zero addend");549    Defined *definedSym = cast<Defined>(sym);550    sym = tryFindDefinedOnIsec(definedSym->isec(),551                               definedSym->value + reloc->addend);552  }553 554  return sym;555}556 557Defined *ObjcCategoryMerger::tryFindDefinedOnIsec(const InputSection *isec,558                                                  uint32_t offset) {559  for (Defined *sym : isec->symbols)560    if ((sym->value <= offset) && (sym->value + sym->size > offset))561      return sym;562 563  return nullptr;564}565 566Defined *567ObjcCategoryMerger::tryGetDefinedAtIsecOffset(const ConcatInputSection *isec,568                                              uint32_t offset) {569  Symbol *sym = tryGetSymbolAtIsecOffset(isec, offset);570  return dyn_cast_or_null<Defined>(sym);571}572 573// Get the class's ro_data symbol. If getMetaRo is true, then we will return574// the meta-class's ro_data symbol. Otherwise, we will return the class575// (instance) ro_data symbol.576Defined *ObjcCategoryMerger::getClassRo(const Defined *classSym,577                                        bool getMetaRo) {578  ConcatInputSection *isec = dyn_cast<ConcatInputSection>(classSym->isec());579  if (!isec)580    return nullptr;581 582  if (!getMetaRo)583    return tryGetDefinedAtIsecOffset(isec, classLayout.roDataOffset +584                                               classSym->value);585 586  Defined *metaClass = tryGetDefinedAtIsecOffset(587      isec, classLayout.metaClassOffset + classSym->value);588  if (!metaClass)589    return nullptr;590 591  return tryGetDefinedAtIsecOffset(592      dyn_cast<ConcatInputSection>(metaClass->isec()),593      classLayout.roDataOffset);594}595 596// Given an ConcatInputSection or CStringInputSection and an offset, if there is597// a symbol(Defined) at that offset, then erase the symbol (mark it not live)598void ObjcCategoryMerger::tryEraseDefinedAtIsecOffset(599    const ConcatInputSection *isec, uint32_t offset) {600  const Reloc *reloc = isec->getRelocAt(offset);601 602  if (!reloc)603    return;604 605  Defined *sym = dyn_cast_or_null<Defined>(cast<Symbol *>(reloc->referent));606  if (!sym)607    return;608 609  if (auto *cisec = dyn_cast_or_null<ConcatInputSection>(sym->isec()))610    eraseISec(cisec);611  else if (auto *csisec = dyn_cast_or_null<CStringInputSection>(sym->isec())) {612    uint32_t totalOffset = sym->value + reloc->addend;613    StringPiece &piece = csisec->getStringPiece(totalOffset);614    piece.live = false;615  } else {616    llvm_unreachable("erased symbol has to be Defined or CStringInputSection");617  }618}619 620bool ObjcCategoryMerger::collectCategoryWriterInfoFromCategory(621    const InfoInputCategory &catInfo) {622 623  if (!infoCategoryWriter.catListInfo.valid)624    collectSectionWriteInfoFromIsec(catInfo.catListIsec,625                                    infoCategoryWriter.catListInfo);626  if (!infoCategoryWriter.catBodyInfo.valid)627    collectSectionWriteInfoFromIsec(catInfo.catBodyIsec,628                                    infoCategoryWriter.catBodyInfo);629 630  if (!infoCategoryWriter.catNameInfo.valid) {631    lld::macho::Defined *catNameSym =632        tryGetDefinedAtIsecOffset(catInfo.catBodyIsec, catLayout.nameOffset);633 634    if (!catNameSym) {635      // This is an unhandled case where the category name is not a symbol but636      // instead points to an CStringInputSection (that doesn't have any symbol)637      // TODO: Find a small repro and either fix or add a test case for this638      // scenario639      return false;640    }641 642    collectSectionWriteInfoFromIsec(catNameSym->isec(),643                                    infoCategoryWriter.catNameInfo);644  }645 646  // Collect writer info from all the category lists (we're assuming they all647  // would provide the same info)648  if (!infoCategoryWriter.catPtrListInfo.valid) {649    for (uint32_t off = catLayout.instanceMethodsOffset;650         off <= catLayout.classPropsOffset; off += target->wordSize) {651      if (Defined *ptrList =652              tryGetDefinedAtIsecOffset(catInfo.catBodyIsec, off)) {653        collectSectionWriteInfoFromIsec(ptrList->isec(),654                                        infoCategoryWriter.catPtrListInfo);655        // we've successfully collected data, so we can break656        break;657      }658    }659  }660 661  return true;662}663 664// Parse a protocol list that might be linked to ConcatInputSection at a given665// offset. The format of the protocol list is different than other lists (prop666// lists, method lists) so we need to parse it differently667void ObjcCategoryMerger::parseProtocolListInfo(668    const ConcatInputSection *isec, uint32_t secOffset,669    PointerListInfo &ptrList, [[maybe_unused]] SourceLanguage sourceLang) {670  assert((isec && (secOffset + target->wordSize <= isec->data.size())) &&671         "Tried to read pointer list beyond protocol section end");672 673  const Reloc *reloc = isec->getRelocAt(secOffset);674  if (!reloc)675    return;676 677  auto *ptrListSym = dyn_cast_or_null<Defined>(cast<Symbol *>(reloc->referent));678  assert(ptrListSym && "Protocol list reloc does not have a valid Defined");679 680  // Theoretically protocol count can be either 32b or 64b, depending on681  // platform pointer size, but to simplify implementation we always just read682  // the lower 32b which should be good enough.683  uint32_t protocolCount = *reinterpret_cast<const uint32_t *>(684      ptrListSym->isec()->data.data() + listHeaderLayout.structSizeOffset);685 686  ptrList.structCount += protocolCount;687  ptrList.structSize = target->wordSize;688 689  [[maybe_unused]] uint32_t expectedListSize =690      (protocolCount * target->wordSize) +691      /*header(count)*/ protocolListHeaderLayout.totalSize +692      /*extra null value*/ target->wordSize;693 694  // On Swift, the protocol list does not have the extra (unnecessary) null695  [[maybe_unused]] uint32_t expectedListSizeSwift =696      expectedListSize - target->wordSize;697 698  assert(((expectedListSize == ptrListSym->isec()->data.size() &&699           sourceLang == SourceLanguage::ObjC) ||700          (expectedListSizeSwift == ptrListSym->isec()->data.size() &&701           sourceLang == SourceLanguage::Swift)) &&702         "Protocol list does not match expected size");703 704  uint32_t off = protocolListHeaderLayout.totalSize;705  for (uint32_t inx = 0; inx < protocolCount; ++inx) {706    const Reloc *reloc = ptrListSym->isec()->getRelocAt(off);707    assert(reloc && "No reloc found at protocol list offset");708 709    auto *listSym = dyn_cast_or_null<Defined>(cast<Symbol *>(reloc->referent));710    assert(listSym && "Protocol list reloc does not have a valid Defined");711 712    ptrList.allPtrs.push_back(listSym);713    off += target->wordSize;714  }715  assert((ptrListSym->isec()->getRelocAt(off) == nullptr) &&716         "expected null terminating protocol");717  assert(off + /*extra null value*/ target->wordSize == expectedListSize &&718         "Protocol list end offset does not match expected size");719}720 721// Parse a protocol list and return the PointerListInfo for it722ObjcCategoryMerger::PointerListInfo723ObjcCategoryMerger::parseProtocolListInfo(const ConcatInputSection *isec,724                                          uint32_t secOffset,725                                          SourceLanguage sourceLang) {726  PointerListInfo ptrList;727  parseProtocolListInfo(isec, secOffset, ptrList, sourceLang);728  return ptrList;729}730 731// Parse a pointer list that might be linked to ConcatInputSection at a given732// offset. This can be used for instance methods, class methods, instance props733// and class props since they have the same format.734bool ObjcCategoryMerger::parsePointerListInfo(const ConcatInputSection *isec,735                                              uint32_t secOffset,736                                              PointerListInfo &ptrList) {737  assert(ptrList.pointersPerStruct == 2 || ptrList.pointersPerStruct == 3);738  assert(isec && "Trying to parse pointer list from null isec");739  assert(secOffset + target->wordSize <= isec->data.size() &&740         "Trying to read pointer list beyond section end");741 742  const Reloc *reloc = isec->getRelocAt(secOffset);743  // Empty list is a valid case, return true.744  if (!reloc)745    return true;746 747  auto *ptrListSym = dyn_cast_or_null<Defined>(cast<Symbol *>(reloc->referent));748  assert(ptrListSym && "Reloc does not have a valid Defined");749 750  uint32_t thisStructSize = *reinterpret_cast<const uint32_t *>(751      ptrListSym->isec()->data.data() + listHeaderLayout.structSizeOffset);752  uint32_t thisStructCount = *reinterpret_cast<const uint32_t *>(753      ptrListSym->isec()->data.data() + listHeaderLayout.structCountOffset);754  assert(thisStructSize == ptrList.pointersPerStruct * target->wordSize);755 756  assert(!ptrList.structSize || (thisStructSize == ptrList.structSize));757 758  ptrList.structCount += thisStructCount;759  ptrList.structSize = thisStructSize;760 761  uint32_t expectedListSize =762      listHeaderLayout.totalSize + (thisStructSize * thisStructCount);763  assert(expectedListSize == ptrListSym->isec()->data.size() &&764         "Pointer list does not match expected size");765 766  for (uint32_t off = listHeaderLayout.totalSize; off < expectedListSize;767       off += target->wordSize) {768    const Reloc *reloc = ptrListSym->isec()->getRelocAt(off);769    assert(reloc && "No reloc found at pointer list offset");770 771    auto *listSym =772        dyn_cast_or_null<Defined>(reloc->referent.dyn_cast<Symbol *>());773    // Sometimes, the reloc points to a StringPiece (InputSection + addend)774    // instead of a symbol.775    // TODO: Skip these cases for now, but we should fix this.776    if (!listSym)777      return false;778 779    ptrList.allPtrs.push_back(listSym);780  }781 782  return true;783}784 785// Here we parse all the information of an input category (catInfo) and786// append the parsed info into the structure which will contain all the787// information about how a class is extended (extInfo)788bool ObjcCategoryMerger::parseCatInfoToExtInfo(const InfoInputCategory &catInfo,789                                               ClassExtensionInfo &extInfo) {790  const Reloc *catNameReloc =791      catInfo.catBodyIsec->getRelocAt(catLayout.nameOffset);792 793  // Parse name794  assert(catNameReloc && "Category does not have a reloc at 'nameOffset'");795 796  // is this the first category we are parsing?797  if (extInfo.mergedContainerName.empty())798    extInfo.objFileForMergeData =799        dyn_cast_or_null<ObjFile>(catInfo.catBodyIsec->getFile());800  else801    extInfo.mergedContainerName += "|";802 803  assert(extInfo.objFileForMergeData &&804         "Expected to already have valid objextInfo.objFileForMergeData");805 806  StringRef catName = catNameReloc->getReferentString();807  extInfo.mergedContainerName += catName.str();808 809  // Parse base class810  if (!extInfo.baseClass) {811    Symbol *classSym =812        tryGetSymbolAtIsecOffset(catInfo.catBodyIsec, catLayout.klassOffset);813    assert(extInfo.baseClassName.empty());814    extInfo.baseClass = classSym;815    llvm::StringRef classPrefix(objc::symbol_names::klass);816    assert(classSym->getName().starts_with(classPrefix) &&817           "Base class symbol does not start with expected prefix");818    extInfo.baseClassName = classSym->getName().substr(classPrefix.size());819  } else {820    assert((extInfo.baseClass ==821            tryGetSymbolAtIsecOffset(catInfo.catBodyIsec,822                                     catLayout.klassOffset)) &&823           "Trying to parse category info into container with different base "824           "class");825  }826 827  if (!parsePointerListInfo(catInfo.catBodyIsec,828                            catLayout.instanceMethodsOffset,829                            extInfo.instanceMethods))830    return false;831 832  if (!parsePointerListInfo(catInfo.catBodyIsec, catLayout.classMethodsOffset,833                            extInfo.classMethods))834    return false;835 836  parseProtocolListInfo(catInfo.catBodyIsec, catLayout.protocolsOffset,837                        extInfo.protocols, catInfo.sourceLanguage);838 839  if (!parsePointerListInfo(catInfo.catBodyIsec, catLayout.instancePropsOffset,840                            extInfo.instanceProps))841    return false;842 843  if (!parsePointerListInfo(catInfo.catBodyIsec, catLayout.classPropsOffset,844                            extInfo.classProps))845    return false;846 847  return true;848}849 850// Generate a protocol list (including header) and link it into the parent at851// the specified offset.852Defined *ObjcCategoryMerger::emitAndLinkProtocolList(853    Defined *parentSym, uint32_t linkAtOffset,854    const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {855  if (ptrList.allPtrs.empty())856    return nullptr;857 858  assert(ptrList.allPtrs.size() == ptrList.structCount);859 860  uint32_t bodySize = (ptrList.structCount * target->wordSize) +861                      /*header(count)*/ protocolListHeaderLayout.totalSize +862                      /*extra null value*/ target->wordSize;863  llvm::ArrayRef<uint8_t> bodyData = newSectionData(bodySize);864 865  // This theoretically can be either 32b or 64b, but writing just the first 32b866  // is good enough867  const uint32_t *ptrProtoCount = reinterpret_cast<const uint32_t *>(868      bodyData.data() + protocolListHeaderLayout.protocolCountOffset);869 870  *const_cast<uint32_t *>(ptrProtoCount) = ptrList.allPtrs.size();871 872  ConcatInputSection *listSec = make<ConcatInputSection>(873      *infoCategoryWriter.catPtrListInfo.inputSection, bodyData,874      infoCategoryWriter.catPtrListInfo.align);875  listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;876  listSec->live = true;877 878  listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;879 880  std::string symName = ptrList.categoryPrefix;881  symName += extInfo.baseClassName + "(" + extInfo.mergedContainerName + ")";882 883  Defined *ptrListSym = make<Defined>(884      newStringData(symName.c_str()), /*file=*/parentSym->getObjectFile(),885      listSec, /*value=*/0, bodyData.size(), /*isWeakDef=*/false,886      /*isExternal=*/false, /*isPrivateExtern=*/false, /*includeInSymtab=*/true,887      /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,888      /*isWeakDefCanBeHidden=*/false);889 890  ptrListSym->used = true;891  parentSym->getObjectFile()->symbols.push_back(ptrListSym);892  addInputSection(listSec);893 894  createSymbolReference(parentSym, ptrListSym, linkAtOffset,895                        infoCategoryWriter.catBodyInfo.relocTemplate);896 897  uint32_t offset = protocolListHeaderLayout.totalSize;898  for (Symbol *symbol : ptrList.allPtrs) {899    createSymbolReference(ptrListSym, symbol, offset,900                          infoCategoryWriter.catPtrListInfo.relocTemplate);901    offset += target->wordSize;902  }903 904  return ptrListSym;905}906 907// Generate a pointer list (including header) and link it into the parent at the908// specified offset. This is used for instance and class methods and909// proprieties.910void ObjcCategoryMerger::emitAndLinkPointerList(911    Defined *parentSym, uint32_t linkAtOffset,912    const ClassExtensionInfo &extInfo, const PointerListInfo &ptrList) {913  if (ptrList.allPtrs.empty())914    return;915 916  assert(ptrList.allPtrs.size() * target->wordSize ==917         ptrList.structCount * ptrList.structSize);918 919  // Generate body920  uint32_t bodySize =921      listHeaderLayout.totalSize + (ptrList.structSize * ptrList.structCount);922  llvm::ArrayRef<uint8_t> bodyData = newSectionData(bodySize);923 924  const uint32_t *ptrStructSize = reinterpret_cast<const uint32_t *>(925      bodyData.data() + listHeaderLayout.structSizeOffset);926  const uint32_t *ptrStructCount = reinterpret_cast<const uint32_t *>(927      bodyData.data() + listHeaderLayout.structCountOffset);928 929  *const_cast<uint32_t *>(ptrStructSize) = ptrList.structSize;930  *const_cast<uint32_t *>(ptrStructCount) = ptrList.structCount;931 932  ConcatInputSection *listSec = make<ConcatInputSection>(933      *infoCategoryWriter.catPtrListInfo.inputSection, bodyData,934      infoCategoryWriter.catPtrListInfo.align);935  listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;936  listSec->live = true;937 938  listSec->parent = infoCategoryWriter.catPtrListInfo.outputSection;939 940  std::string symName = ptrList.categoryPrefix;941  symName += extInfo.baseClassName + "(" + extInfo.mergedContainerName + ")";942 943  Defined *ptrListSym = make<Defined>(944      newStringData(symName.c_str()), /*file=*/parentSym->getObjectFile(),945      listSec, /*value=*/0, bodyData.size(), /*isWeakDef=*/false,946      /*isExternal=*/false, /*isPrivateExtern=*/false, /*includeInSymtab=*/true,947      /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,948      /*isWeakDefCanBeHidden=*/false);949 950  ptrListSym->used = true;951  parentSym->getObjectFile()->symbols.push_back(ptrListSym);952  addInputSection(listSec);953 954  createSymbolReference(parentSym, ptrListSym, linkAtOffset,955                        infoCategoryWriter.catBodyInfo.relocTemplate);956 957  uint32_t offset = listHeaderLayout.totalSize;958  for (Symbol *symbol : ptrList.allPtrs) {959    createSymbolReference(ptrListSym, symbol, offset,960                          infoCategoryWriter.catPtrListInfo.relocTemplate);961    offset += target->wordSize;962  }963}964 965// This method creates an __objc_catlist ConcatInputSection with a single slot966Defined *967ObjcCategoryMerger::emitCatListEntrySec(const std::string &forCategoryName,968                                        const std::string &forBaseClassName,969                                        ObjFile *objFile) {970  uint32_t sectionSize = target->wordSize;971  llvm::ArrayRef<uint8_t> bodyData = newSectionData(sectionSize);972 973  ConcatInputSection *newCatList =974      make<ConcatInputSection>(*infoCategoryWriter.catListInfo.inputSection,975                               bodyData, infoCategoryWriter.catListInfo.align);976  newCatList->parent = infoCategoryWriter.catListInfo.outputSection;977  newCatList->live = true;978 979  newCatList->parent = infoCategoryWriter.catListInfo.outputSection;980 981  std::string catSymName = "<__objc_catlist slot for merged category ";982  catSymName += forBaseClassName + "(" + forCategoryName + ")>";983 984  Defined *catListSym = make<Defined>(985      newStringData(catSymName.c_str()), /*file=*/objFile, newCatList,986      /*value=*/0, bodyData.size(), /*isWeakDef=*/false, /*isExternal=*/false,987      /*isPrivateExtern=*/false, /*includeInSymtab=*/false,988      /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,989      /*isWeakDefCanBeHidden=*/false);990 991  catListSym->used = true;992  objFile->symbols.push_back(catListSym);993  addInputSection(newCatList);994  return catListSym;995}996 997// Here we generate the main category body and link the name and base class into998// it. We don't link any other info yet like the protocol and class/instance999// methods/props.1000Defined *ObjcCategoryMerger::emitCategoryBody(const std::string &name,1001                                              const Defined *nameSym,1002                                              const Symbol *baseClassSym,1003                                              const std::string &baseClassName,1004                                              ObjFile *objFile) {1005  llvm::ArrayRef<uint8_t> bodyData = newSectionData(catLayout.totalSize);1006 1007  uint32_t *ptrSize = (uint32_t *)(const_cast<uint8_t *>(bodyData.data()) +1008                                   catLayout.sizeOffset);1009  *ptrSize = catLayout.totalSize;1010 1011  ConcatInputSection *newBodySec =1012      make<ConcatInputSection>(*infoCategoryWriter.catBodyInfo.inputSection,1013                               bodyData, infoCategoryWriter.catBodyInfo.align);1014  newBodySec->parent = infoCategoryWriter.catBodyInfo.outputSection;1015  newBodySec->live = true;1016 1017  std::string symName =1018      objc::symbol_names::category + baseClassName + "(" + name + ")";1019  Defined *catBodySym = make<Defined>(1020      newStringData(symName.c_str()), /*file=*/objFile, newBodySec,1021      /*value=*/0, bodyData.size(), /*isWeakDef=*/false, /*isExternal=*/false,1022      /*isPrivateExtern=*/false, /*includeInSymtab=*/true,1023      /*isReferencedDynamically=*/false, /*noDeadStrip=*/false,1024      /*isWeakDefCanBeHidden=*/false);1025 1026  catBodySym->used = true;1027  objFile->symbols.push_back(catBodySym);1028  addInputSection(newBodySec);1029 1030  createSymbolReference(catBodySym, nameSym, catLayout.nameOffset,1031                        infoCategoryWriter.catBodyInfo.relocTemplate);1032 1033  // Create a reloc to the base class (either external or internal)1034  createSymbolReference(catBodySym, baseClassSym, catLayout.klassOffset,1035                        infoCategoryWriter.catBodyInfo.relocTemplate);1036 1037  return catBodySym;1038}1039 1040// This writes the new category name (for the merged category) into the binary1041// and returns the sybmol for it.1042Defined *ObjcCategoryMerger::emitCategoryName(const std::string &name,1043                                              ObjFile *objFile) {1044  StringRef nameStrData = newStringData(name.c_str());1045  // We use +1 below to include the null terminator1046  llvm::ArrayRef<uint8_t> nameData(1047      reinterpret_cast<const uint8_t *>(nameStrData.data()),1048      nameStrData.size() + 1);1049 1050  auto *parentSection = infoCategoryWriter.catNameInfo.inputSection;1051  CStringInputSection *newStringSec = make<CStringInputSection>(1052      *infoCategoryWriter.catNameInfo.inputSection, nameData,1053      infoCategoryWriter.catNameInfo.align, /*dedupLiterals=*/true);1054 1055  parentSection->subsections.push_back({0, newStringSec});1056 1057  newStringSec->splitIntoPieces();1058  newStringSec->pieces[0].live = true;1059  newStringSec->parent = infoCategoryWriter.catNameInfo.outputSection;1060  in.cStringSection->addInput(newStringSec);1061  assert(newStringSec->pieces.size() == 1);1062 1063  Defined *catNameSym = make<Defined>(1064      "<merged category name>", /*file=*/objFile, newStringSec,1065      /*value=*/0, nameData.size(),1066      /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false,1067      /*includeInSymtab=*/false, /*isReferencedDynamically=*/false,1068      /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);1069 1070  catNameSym->used = true;1071  objFile->symbols.push_back(catNameSym);1072  return catNameSym;1073}1074 1075// This method fully creates a new category from the given ClassExtensionInfo.1076// It creates the category name, body and method/protocol/prop lists and links1077// them all together. Then it creates a new __objc_catlist entry and adds the1078// category to it. Calling this method will fully generate a category which will1079// be available in the final binary.1080Defined *ObjcCategoryMerger::emitCategory(const ClassExtensionInfo &extInfo) {1081  Defined *catNameSym = emitCategoryName(extInfo.mergedContainerName,1082                                         extInfo.objFileForMergeData);1083 1084  Defined *catBodySym = emitCategoryBody(1085      extInfo.mergedContainerName, catNameSym, extInfo.baseClass,1086      extInfo.baseClassName, extInfo.objFileForMergeData);1087 1088  Defined *catListSym =1089      emitCatListEntrySec(extInfo.mergedContainerName, extInfo.baseClassName,1090                          extInfo.objFileForMergeData);1091 1092  // Add the single category body to the category list at the offset 0.1093  createSymbolReference(catListSym, catBodySym, /*offset=*/0,1094                        infoCategoryWriter.catListInfo.relocTemplate);1095 1096  emitAndLinkPointerList(catBodySym, catLayout.instanceMethodsOffset, extInfo,1097                         extInfo.instanceMethods);1098 1099  emitAndLinkPointerList(catBodySym, catLayout.classMethodsOffset, extInfo,1100                         extInfo.classMethods);1101 1102  emitAndLinkProtocolList(catBodySym, catLayout.protocolsOffset, extInfo,1103                          extInfo.protocols);1104 1105  emitAndLinkPointerList(catBodySym, catLayout.instancePropsOffset, extInfo,1106                         extInfo.instanceProps);1107 1108  emitAndLinkPointerList(catBodySym, catLayout.classPropsOffset, extInfo,1109                         extInfo.classProps);1110 1111  return catBodySym;1112}1113 1114// This method merges all the categories (sharing a base class) into a single1115// category.1116bool ObjcCategoryMerger::mergeCategoriesIntoSingleCategory(1117    std::vector<InfoInputCategory> &categories) {1118  assert(categories.size() > 1 && "Expected at least 2 categories");1119 1120  ClassExtensionInfo extInfo(catLayout);1121 1122  for (auto &catInfo : categories)1123    if (!parseCatInfoToExtInfo(catInfo, extInfo))1124      return false;1125 1126  Defined *newCatDef = emitCategory(extInfo);1127  assert(newCatDef && "Failed to create a new category");1128 1129  // Suppress unsuded var warning1130  (void)newCatDef;1131 1132  for (auto &catInfo : categories)1133    catInfo.wasMerged = true;1134 1135  return true;1136}1137 1138void ObjcCategoryMerger::createSymbolReference(Defined *refFrom,1139                                               const Symbol *refTo,1140                                               uint32_t offset,1141                                               const Reloc &relocTemplate) {1142  Reloc r = relocTemplate;1143  r.offset = offset;1144  r.addend = 0;1145  r.referent = const_cast<Symbol *>(refTo);1146  refFrom->isec()->relocs.push_back(r);1147}1148 1149// Get the list of categories in the '__objc_nlcatlist' section. We can't1150// optimize these as they have a '+load' method that has to be called at1151// runtime.1152DenseSet<const Symbol *> ObjcCategoryMerger::collectNlCategories() {1153  DenseSet<const Symbol *> nlCategories;1154 1155  for (InputSection *sec : allInputSections) {1156    if (sec->getName() != section_names::objcNonLazyCatList)1157      continue;1158 1159    for (auto &r : sec->relocs) {1160      const Symbol *sym = r.referent.dyn_cast<Symbol *>();1161      nlCategories.insert(sym);1162    }1163  }1164  return nlCategories;1165}1166 1167void ObjcCategoryMerger::collectAndValidateCategoriesData() {1168  auto nlCategories = collectNlCategories();1169 1170  for (InputSection *sec : allInputSections) {1171    if (sec->getName() != section_names::objcCatList)1172      continue;1173    ConcatInputSection *catListCisec = dyn_cast<ConcatInputSection>(sec);1174    assert(catListCisec &&1175           "__objc_catList InputSection is not a ConcatInputSection");1176 1177    for (uint32_t off = 0; off < catListCisec->getSize();1178         off += target->wordSize) {1179      Defined *categorySym = tryGetDefinedAtIsecOffset(catListCisec, off);1180      assert(categorySym &&1181             "Failed to get a valid category at __objc_catlit offset");1182 1183      if (nlCategories.count(categorySym))1184        continue;1185 1186      auto *catBodyIsec = dyn_cast<ConcatInputSection>(categorySym->isec());1187      assert(catBodyIsec &&1188             "Category data section is not an ConcatInputSection");1189 1190      SourceLanguage eLang = SourceLanguage::Unknown;1191      if (categorySym->getName().starts_with(objc::symbol_names::category))1192        eLang = SourceLanguage::ObjC;1193      else if (categorySym->getName().starts_with(1194                   objc::symbol_names::swift_objc_category))1195        eLang = SourceLanguage::Swift;1196      else1197        llvm_unreachable("Unexpected category symbol name");1198 1199      InfoInputCategory catInputInfo{catListCisec, catBodyIsec, off, eLang};1200 1201      // Check that the category has a reloc at 'klassOffset' (which is1202      // a pointer to the class symbol)1203 1204      Symbol *classSym =1205          tryGetSymbolAtIsecOffset(catBodyIsec, catLayout.klassOffset);1206      assert(classSym && "Category does not have a valid base class");1207 1208      if (!collectCategoryWriterInfoFromCategory(catInputInfo))1209        continue;1210 1211      categoryMap[classSym].push_back(catInputInfo);1212    }1213  }1214}1215 1216// In the input we have multiple __objc_catlist InputSection, each of which may1217// contain links to multiple categories. Of these categories, we will merge (and1218// erase) only some. There will be some categories that will remain untouched1219// (not erased). For these not erased categories, we generate new __objc_catlist1220// entries since the parent __objc_catlist entry will be erased1221void ObjcCategoryMerger::generateCatListForNonErasedCategories(1222    const MapVector<ConcatInputSection *, std::set<uint64_t>>1223        catListToErasedOffsets) {1224 1225  // Go through all offsets of all __objc_catlist's that we process and if there1226  // are categories that we didn't process - generate a new __objc_catlist for1227  // each.1228  for (auto &mapEntry : catListToErasedOffsets) {1229    ConcatInputSection *catListIsec = mapEntry.first;1230    for (uint32_t catListIsecOffset = 0;1231         catListIsecOffset < catListIsec->data.size();1232         catListIsecOffset += target->wordSize) {1233      // This slot was erased, we can just skip it1234      if (mapEntry.second.count(catListIsecOffset))1235        continue;1236 1237      Defined *nonErasedCatBody =1238          tryGetDefinedAtIsecOffset(catListIsec, catListIsecOffset);1239      assert(nonErasedCatBody && "Failed to relocate non-deleted category");1240 1241      // Allocate data for the new __objc_catlist slot1242      llvm::ArrayRef<uint8_t> bodyData = newSectionData(target->wordSize);1243 1244      // We mark the __objc_catlist slot as belonging to the same file as the1245      // category1246      ObjFile *objFile = dyn_cast<ObjFile>(nonErasedCatBody->getFile());1247 1248      ConcatInputSection *listSec = make<ConcatInputSection>(1249          *infoCategoryWriter.catListInfo.inputSection, bodyData,1250          infoCategoryWriter.catListInfo.align);1251      listSec->parent = infoCategoryWriter.catListInfo.outputSection;1252      listSec->live = true;1253 1254      std::string slotSymName = "<__objc_catlist slot for category ";1255      slotSymName += nonErasedCatBody->getName();1256      slotSymName += ">";1257 1258      Defined *catListSlotSym = make<Defined>(1259          newStringData(slotSymName.c_str()), /*file=*/objFile, listSec,1260          /*value=*/0, bodyData.size(),1261          /*isWeakDef=*/false, /*isExternal=*/false, /*isPrivateExtern=*/false,1262          /*includeInSymtab=*/false, /*isReferencedDynamically=*/false,1263          /*noDeadStrip=*/false, /*isWeakDefCanBeHidden=*/false);1264 1265      catListSlotSym->used = true;1266      objFile->symbols.push_back(catListSlotSym);1267      addInputSection(listSec);1268 1269      // Now link the category body into the newly created slot1270      createSymbolReference(catListSlotSym, nonErasedCatBody, 0,1271                            infoCategoryWriter.catListInfo.relocTemplate);1272    }1273  }1274}1275 1276void ObjcCategoryMerger::eraseISec(ConcatInputSection *isec) {1277  isec->live = false;1278  for (auto &sym : isec->symbols)1279    sym->used = false;1280}1281 1282// This fully erases the merged categories, including their body, their names,1283// their method/protocol/prop lists and the __objc_catlist entries that link to1284// them.1285void ObjcCategoryMerger::eraseMergedCategories() {1286  // Map of InputSection to a set of offsets of the categories that were merged1287  MapVector<ConcatInputSection *, std::set<uint64_t>> catListToErasedOffsets;1288 1289  for (auto &mapEntry : categoryMap) {1290    for (InfoInputCategory &catInfo : mapEntry.second) {1291      if (catInfo.wasMerged) {1292        eraseISec(catInfo.catListIsec);1293        catListToErasedOffsets[catInfo.catListIsec].insert(1294            catInfo.offCatListIsec);1295      }1296    }1297  }1298 1299  // If there were categories that we did not erase, we need to generate a new1300  // __objc_catList that contains only the un-merged categories, and get rid of1301  // the references to the ones we merged.1302  generateCatListForNonErasedCategories(catListToErasedOffsets);1303 1304  // Erase the old method lists & names of the categories that were merged1305  for (auto &mapEntry : categoryMap) {1306    for (InfoInputCategory &catInfo : mapEntry.second) {1307      if (!catInfo.wasMerged)1308        continue;1309 1310      eraseISec(catInfo.catBodyIsec);1311 1312      // We can't erase 'catLayout.nameOffset' for either Swift or ObjC1313      //   categories because the name will sometimes also be used for other1314      //   purposes.1315      // For Swift, see usages of 'l_.str.11.SimpleClass' in1316      //   objc-category-merging-swift.s1317      // For ObjC, see usages of 'l_OBJC_CLASS_NAME_.1' in1318      //   objc-category-merging-erase-objc-name-test.s1319      // TODO: handle the above in a smarter way1320 1321      tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,1322                                  catLayout.instanceMethodsOffset);1323      tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,1324                                  catLayout.classMethodsOffset);1325      tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,1326                                  catLayout.protocolsOffset);1327      tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,1328                                  catLayout.classPropsOffset);1329      tryEraseDefinedAtIsecOffset(catInfo.catBodyIsec,1330                                  catLayout.instancePropsOffset);1331    }1332  }1333}1334 1335void ObjcCategoryMerger::doMerge() {1336  collectAndValidateCategoriesData();1337 1338  for (auto &[baseClass, catInfos] : categoryMap) {1339    bool merged = false;1340    if (auto *baseClassDef = dyn_cast<Defined>(baseClass)) {1341      // Merge all categories into the base class1342      merged = mergeCategoriesIntoBaseClass(baseClassDef, catInfos);1343    } else if (catInfos.size() > 1) {1344      // Merge all categories into a new, single category1345      merged = mergeCategoriesIntoSingleCategory(catInfos);1346    }1347    if (!merged)1348      warn("ObjC category merging skipped for class symbol' " +1349           baseClass->getName().str() + "'\n");1350  }1351 1352  // Erase all categories that were merged1353  eraseMergedCategories();1354}1355 1356void ObjcCategoryMerger::doCleanup() { generatedSectionData.clear(); }1357 1358StringRef ObjcCategoryMerger::newStringData(const char *str) {1359  uint32_t len = strlen(str);1360  uint32_t bufSize = len + 1;1361  SmallVector<uint8_t> &data = newSectionData(bufSize);1362  char *strData = reinterpret_cast<char *>(data.data());1363  // Copy the string chars and null-terminator1364  memcpy(strData, str, bufSize);1365  return StringRef(strData, len);1366}1367 1368SmallVector<uint8_t> &ObjcCategoryMerger::newSectionData(uint32_t size) {1369  generatedSectionData.push_back(1370      std::make_unique<SmallVector<uint8_t>>(size, 0));1371  return *generatedSectionData.back();1372}1373 1374} // namespace1375 1376void objc::mergeCategories() {1377  TimeTraceScope timeScope("ObjcCategoryMerger");1378 1379  ObjcCategoryMerger merger(inputSections);1380  merger.doMerge();1381}1382 1383void objc::doCleanup() { ObjcCategoryMerger::doCleanup(); }1384 1385ObjcCategoryMerger::SourceLanguage1386ObjcCategoryMerger::getClassSymSourceLang(const Defined *classSym) {1387  if (classSym->getName().starts_with(objc::symbol_names::swift_objc_klass))1388    return SourceLanguage::Swift;1389 1390  // If the symbol name matches the ObjC prefix, we don't necessarely know this1391  // comes from ObjC, since Swift creates ObjC-like alias symbols for some Swift1392  // classes. Ex:1393  //  .globl	_OBJC_CLASS_$__TtC11MyTestClass11MyTestClass1394  //  .private_extern _OBJC_CLASS_$__TtC11MyTestClass11MyTestClass1395  //  .set _OBJC_CLASS_$__TtC11MyTestClass11MyTestClass, _$s11MyTestClassAACN1396  //1397  // So we scan for symbols with the same address and check for the Swift class1398  if (classSym->getName().starts_with(objc::symbol_names::klass)) {1399    for (auto &sym : classSym->originalIsec->symbols)1400      if (sym->value == classSym->value)1401        if (sym->getName().starts_with(objc::symbol_names::swift_objc_klass))1402          return SourceLanguage::Swift;1403    return SourceLanguage::ObjC;1404  }1405 1406  llvm_unreachable("Unexpected class symbol name during category merging");1407}1408 1409bool ObjcCategoryMerger::mergeCategoriesIntoBaseClass(1410    const Defined *baseClass, std::vector<InfoInputCategory> &categories) {1411  assert(categories.size() >= 1 && "Expected at least one category to merge");1412 1413  // Collect all the info from the categories1414  ClassExtensionInfo extInfo(catLayout);1415  extInfo.baseClass = baseClass;1416  extInfo.baseClassSourceLanguage = getClassSymSourceLang(baseClass);1417 1418  for (auto &catInfo : categories)1419    if (!parseCatInfoToExtInfo(catInfo, extInfo))1420      return false;1421 1422  // Get metadata for the base class1423  Defined *metaRo = getClassRo(baseClass, /*getMetaRo=*/true);1424  ConcatInputSection *metaIsec = dyn_cast<ConcatInputSection>(metaRo->isec());1425  Defined *classRo = getClassRo(baseClass, /*getMetaRo=*/false);1426  ConcatInputSection *classIsec = dyn_cast<ConcatInputSection>(classRo->isec());1427 1428  // Now collect the info from the base class from the various lists in the1429  // class metadata1430 1431  // Protocol lists are a special case - the same protocol list is in classRo1432  // and metaRo, so we only need to parse it once1433  parseProtocolListInfo(classIsec, roClassLayout.baseProtocolsOffset,1434                        extInfo.protocols, extInfo.baseClassSourceLanguage);1435 1436  // Check that the classRo and metaRo protocol lists are identical1437  assert(parseProtocolListInfo(classIsec, roClassLayout.baseProtocolsOffset,1438                               extInfo.baseClassSourceLanguage) ==1439             parseProtocolListInfo(metaIsec, roClassLayout.baseProtocolsOffset,1440                                   extInfo.baseClassSourceLanguage) &&1441         "Category merger expects classRo and metaRo to have the same protocol "1442         "list");1443 1444  parsePointerListInfo(metaIsec, roClassLayout.baseMethodsOffset,1445                       extInfo.classMethods);1446  parsePointerListInfo(classIsec, roClassLayout.baseMethodsOffset,1447                       extInfo.instanceMethods);1448 1449  parsePointerListInfo(metaIsec, roClassLayout.basePropertiesOffset,1450                       extInfo.classProps);1451  parsePointerListInfo(classIsec, roClassLayout.basePropertiesOffset,1452                       extInfo.instanceProps);1453 1454  // Erase the old lists - these will be generated and replaced1455  eraseSymbolAtIsecOffset(metaIsec, roClassLayout.baseMethodsOffset);1456  eraseSymbolAtIsecOffset(metaIsec, roClassLayout.baseProtocolsOffset);1457  eraseSymbolAtIsecOffset(metaIsec, roClassLayout.basePropertiesOffset);1458  eraseSymbolAtIsecOffset(classIsec, roClassLayout.baseMethodsOffset);1459  eraseSymbolAtIsecOffset(classIsec, roClassLayout.baseProtocolsOffset);1460  eraseSymbolAtIsecOffset(classIsec, roClassLayout.basePropertiesOffset);1461 1462  // Emit the newly merged lists - first into the meta RO then into the class RO1463  // First we emit and link the protocol list into the meta RO. Then we link it1464  // in the classRo as well (they're supposed to be identical)1465  if (Defined *protoListSym =1466          emitAndLinkProtocolList(metaRo, roClassLayout.baseProtocolsOffset,1467                                  extInfo, extInfo.protocols)) {1468    createSymbolReference(classRo, protoListSym,1469                          roClassLayout.baseProtocolsOffset,1470                          infoCategoryWriter.catBodyInfo.relocTemplate);1471  }1472 1473  emitAndLinkPointerList(metaRo, roClassLayout.baseMethodsOffset, extInfo,1474                         extInfo.classMethods);1475  emitAndLinkPointerList(classRo, roClassLayout.baseMethodsOffset, extInfo,1476                         extInfo.instanceMethods);1477 1478  emitAndLinkPointerList(metaRo, roClassLayout.basePropertiesOffset, extInfo,1479                         extInfo.classProps);1480 1481  emitAndLinkPointerList(classRo, roClassLayout.basePropertiesOffset, extInfo,1482                         extInfo.instanceProps);1483 1484  // Mark all the categories as merged - this will be used to erase them later1485  for (auto &catInfo : categories)1486    catInfo.wasMerged = true;1487 1488  return true;1489}1490 1491// Erase the symbol at a given offset in an InputSection1492void ObjcCategoryMerger::eraseSymbolAtIsecOffset(ConcatInputSection *isec,1493                                                 uint32_t offset) {1494  Defined *sym = tryGetDefinedAtIsecOffset(isec, offset);1495  if (!sym)1496    return;1497 1498  // Remove the symbol from isec->symbols1499  assert(isa<Defined>(sym) && "Can only erase a Defined");1500  llvm::erase(isec->symbols, sym);1501 1502  // Remove the relocs that refer to this symbol1503  auto removeAtOff = [offset](Reloc const &r) { return r.offset == offset; };1504  llvm::erase_if(isec->relocs, removeAtOff);1505 1506  // Now, if the symbol fully occupies a ConcatInputSection, we can also erase1507  // the whole ConcatInputSection1508  if (ConcatInputSection *cisec = dyn_cast<ConcatInputSection>(sym->isec()))1509    if (cisec->data.size() == sym->size)1510      eraseISec(cisec);1511}1512