1e8d8bef9SDimitry Andric //===- ObjC.cpp -----------------------------------------------------------===// 2e8d8bef9SDimitry Andric // 3e8d8bef9SDimitry Andric // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4e8d8bef9SDimitry Andric // See https://llvm.org/LICENSE.txt for license information. 5e8d8bef9SDimitry Andric // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6e8d8bef9SDimitry Andric // 7e8d8bef9SDimitry Andric //===----------------------------------------------------------------------===// 8e8d8bef9SDimitry Andric 9e8d8bef9SDimitry Andric #include "ObjC.h" 10e8d8bef9SDimitry Andric #include "InputFiles.h" 11fe6060f1SDimitry Andric #include "InputSection.h" 12*06c3fb27SDimitry Andric #include "Layout.h" 13e8d8bef9SDimitry Andric #include "OutputSegment.h" 14fe6060f1SDimitry Andric #include "Target.h" 15e8d8bef9SDimitry Andric 16bdd1243dSDimitry Andric #include "lld/Common/ErrorHandler.h" 17*06c3fb27SDimitry Andric #include "llvm/ADT/DenseMap.h" 18e8d8bef9SDimitry Andric #include "llvm/BinaryFormat/MachO.h" 19349cc55cSDimitry Andric #include "llvm/Bitcode/BitcodeReader.h" 20e8d8bef9SDimitry Andric 21e8d8bef9SDimitry Andric using namespace llvm; 22e8d8bef9SDimitry Andric using namespace llvm::MachO; 23e8d8bef9SDimitry Andric using namespace lld; 24fe6060f1SDimitry Andric using namespace lld::macho; 25e8d8bef9SDimitry Andric 26349cc55cSDimitry Andric template <class LP> static bool objectHasObjCSection(MemoryBufferRef mb) { 27349cc55cSDimitry Andric using SectionHeader = typename LP::section; 28fe6060f1SDimitry Andric 29fe6060f1SDimitry Andric auto *hdr = 30fe6060f1SDimitry Andric reinterpret_cast<const typename LP::mach_header *>(mb.getBufferStart()); 31fe6060f1SDimitry Andric if (hdr->magic != LP::magic) 32fe6060f1SDimitry Andric return false; 33fe6060f1SDimitry Andric 34fe6060f1SDimitry Andric if (const auto *c = 35fe6060f1SDimitry Andric findCommand<typename LP::segment_command>(hdr, LP::segmentLCType)) { 36349cc55cSDimitry Andric auto sectionHeaders = ArrayRef<SectionHeader>{ 37349cc55cSDimitry Andric reinterpret_cast<const SectionHeader *>(c + 1), c->nsects}; 38349cc55cSDimitry Andric for (const SectionHeader &secHead : sectionHeaders) { 39349cc55cSDimitry Andric StringRef sectname(secHead.sectname, 40349cc55cSDimitry Andric strnlen(secHead.sectname, sizeof(secHead.sectname))); 41349cc55cSDimitry Andric StringRef segname(secHead.segname, 42349cc55cSDimitry Andric strnlen(secHead.segname, sizeof(secHead.segname))); 43fe6060f1SDimitry Andric if ((segname == segment_names::data && 44fe6060f1SDimitry Andric sectname == section_names::objcCatList) || 45fe6060f1SDimitry Andric (segname == segment_names::text && 46*06c3fb27SDimitry Andric sectname.starts_with(section_names::swift))) { 47e8d8bef9SDimitry Andric return true; 48e8d8bef9SDimitry Andric } 49e8d8bef9SDimitry Andric } 50e8d8bef9SDimitry Andric } 51e8d8bef9SDimitry Andric return false; 52e8d8bef9SDimitry Andric } 53fe6060f1SDimitry Andric 54349cc55cSDimitry Andric static bool objectHasObjCSection(MemoryBufferRef mb) { 55fe6060f1SDimitry Andric if (target->wordSize == 8) 56349cc55cSDimitry Andric return ::objectHasObjCSection<LP64>(mb); 57fe6060f1SDimitry Andric else 58349cc55cSDimitry Andric return ::objectHasObjCSection<ILP32>(mb); 59349cc55cSDimitry Andric } 60349cc55cSDimitry Andric 61349cc55cSDimitry Andric bool macho::hasObjCSection(MemoryBufferRef mb) { 62349cc55cSDimitry Andric switch (identify_magic(mb.getBuffer())) { 63349cc55cSDimitry Andric case file_magic::macho_object: 64349cc55cSDimitry Andric return objectHasObjCSection(mb); 65349cc55cSDimitry Andric case file_magic::bitcode: 66349cc55cSDimitry Andric return check(isBitcodeContainingObjCCategory(mb)); 67349cc55cSDimitry Andric default: 68349cc55cSDimitry Andric return false; 69349cc55cSDimitry Andric } 70fe6060f1SDimitry Andric } 71*06c3fb27SDimitry Andric 72*06c3fb27SDimitry Andric namespace { 73*06c3fb27SDimitry Andric 74*06c3fb27SDimitry Andric #define FOR_EACH_CATEGORY_FIELD(DO) \ 75*06c3fb27SDimitry Andric DO(Ptr, name) \ 76*06c3fb27SDimitry Andric DO(Ptr, klass) \ 77*06c3fb27SDimitry Andric DO(Ptr, instanceMethods) \ 78*06c3fb27SDimitry Andric DO(Ptr, classMethods) \ 79*06c3fb27SDimitry Andric DO(Ptr, protocols) \ 80*06c3fb27SDimitry Andric DO(Ptr, instanceProps) \ 81*06c3fb27SDimitry Andric DO(Ptr, classProps) 82*06c3fb27SDimitry Andric 83*06c3fb27SDimitry Andric CREATE_LAYOUT_CLASS(Category, FOR_EACH_CATEGORY_FIELD); 84*06c3fb27SDimitry Andric 85*06c3fb27SDimitry Andric #undef FOR_EACH_CATEGORY_FIELD 86*06c3fb27SDimitry Andric 87*06c3fb27SDimitry Andric #define FOR_EACH_CLASS_FIELD(DO) \ 88*06c3fb27SDimitry Andric DO(Ptr, metaClass) \ 89*06c3fb27SDimitry Andric DO(Ptr, superClass) \ 90*06c3fb27SDimitry Andric DO(Ptr, methodCache) \ 91*06c3fb27SDimitry Andric DO(Ptr, vtable) \ 92*06c3fb27SDimitry Andric DO(Ptr, roData) 93*06c3fb27SDimitry Andric 94*06c3fb27SDimitry Andric CREATE_LAYOUT_CLASS(Class, FOR_EACH_CLASS_FIELD); 95*06c3fb27SDimitry Andric 96*06c3fb27SDimitry Andric #undef FOR_EACH_CLASS_FIELD 97*06c3fb27SDimitry Andric 98*06c3fb27SDimitry Andric #define FOR_EACH_RO_CLASS_FIELD(DO) \ 99*06c3fb27SDimitry Andric DO(uint32_t, flags) \ 100*06c3fb27SDimitry Andric DO(uint32_t, instanceStart) \ 101*06c3fb27SDimitry Andric DO(Ptr, instanceSize) \ 102*06c3fb27SDimitry Andric DO(Ptr, ivarLayout) \ 103*06c3fb27SDimitry Andric DO(Ptr, name) \ 104*06c3fb27SDimitry Andric DO(Ptr, baseMethods) \ 105*06c3fb27SDimitry Andric DO(Ptr, baseProtocols) \ 106*06c3fb27SDimitry Andric DO(Ptr, ivars) \ 107*06c3fb27SDimitry Andric DO(Ptr, weakIvarLayout) \ 108*06c3fb27SDimitry Andric DO(Ptr, baseProperties) 109*06c3fb27SDimitry Andric 110*06c3fb27SDimitry Andric CREATE_LAYOUT_CLASS(ROClass, FOR_EACH_RO_CLASS_FIELD); 111*06c3fb27SDimitry Andric 112*06c3fb27SDimitry Andric #undef FOR_EACH_RO_CLASS_FIELD 113*06c3fb27SDimitry Andric 114*06c3fb27SDimitry Andric #define FOR_EACH_LIST_HEADER(DO) \ 115*06c3fb27SDimitry Andric DO(uint32_t, size) \ 116*06c3fb27SDimitry Andric DO(uint32_t, count) 117*06c3fb27SDimitry Andric 118*06c3fb27SDimitry Andric CREATE_LAYOUT_CLASS(ListHeader, FOR_EACH_LIST_HEADER); 119*06c3fb27SDimitry Andric 120*06c3fb27SDimitry Andric #undef FOR_EACH_LIST_HEADER 121*06c3fb27SDimitry Andric 122*06c3fb27SDimitry Andric #define FOR_EACH_METHOD(DO) \ 123*06c3fb27SDimitry Andric DO(Ptr, name) \ 124*06c3fb27SDimitry Andric DO(Ptr, type) \ 125*06c3fb27SDimitry Andric DO(Ptr, impl) 126*06c3fb27SDimitry Andric 127*06c3fb27SDimitry Andric CREATE_LAYOUT_CLASS(Method, FOR_EACH_METHOD); 128*06c3fb27SDimitry Andric 129*06c3fb27SDimitry Andric #undef FOR_EACH_METHOD 130*06c3fb27SDimitry Andric 131*06c3fb27SDimitry Andric enum MethodContainerKind { 132*06c3fb27SDimitry Andric MCK_Class, 133*06c3fb27SDimitry Andric MCK_Category, 134*06c3fb27SDimitry Andric }; 135*06c3fb27SDimitry Andric 136*06c3fb27SDimitry Andric struct MethodContainer { 137*06c3fb27SDimitry Andric MethodContainerKind kind; 138*06c3fb27SDimitry Andric const ConcatInputSection *isec; 139*06c3fb27SDimitry Andric }; 140*06c3fb27SDimitry Andric 141*06c3fb27SDimitry Andric enum MethodKind { 142*06c3fb27SDimitry Andric MK_Instance, 143*06c3fb27SDimitry Andric MK_Static, 144*06c3fb27SDimitry Andric }; 145*06c3fb27SDimitry Andric 146*06c3fb27SDimitry Andric struct ObjcClass { 147*06c3fb27SDimitry Andric DenseMap<CachedHashStringRef, MethodContainer> instanceMethods; 148*06c3fb27SDimitry Andric DenseMap<CachedHashStringRef, MethodContainer> classMethods; 149*06c3fb27SDimitry Andric }; 150*06c3fb27SDimitry Andric 151*06c3fb27SDimitry Andric } // namespace 152*06c3fb27SDimitry Andric 153*06c3fb27SDimitry Andric class ObjcCategoryChecker { 154*06c3fb27SDimitry Andric public: 155*06c3fb27SDimitry Andric ObjcCategoryChecker(); 156*06c3fb27SDimitry Andric void parseCategory(const ConcatInputSection *catListIsec); 157*06c3fb27SDimitry Andric 158*06c3fb27SDimitry Andric private: 159*06c3fb27SDimitry Andric void parseClass(const Defined *classSym); 160*06c3fb27SDimitry Andric void parseMethods(const ConcatInputSection *methodsIsec, 161*06c3fb27SDimitry Andric const Symbol *methodContainer, 162*06c3fb27SDimitry Andric const ConcatInputSection *containerIsec, 163*06c3fb27SDimitry Andric MethodContainerKind, MethodKind); 164*06c3fb27SDimitry Andric 165*06c3fb27SDimitry Andric CategoryLayout catLayout; 166*06c3fb27SDimitry Andric ClassLayout classLayout; 167*06c3fb27SDimitry Andric ROClassLayout roClassLayout; 168*06c3fb27SDimitry Andric ListHeaderLayout listHeaderLayout; 169*06c3fb27SDimitry Andric MethodLayout methodLayout; 170*06c3fb27SDimitry Andric 171*06c3fb27SDimitry Andric DenseMap<const Symbol *, ObjcClass> classMap; 172*06c3fb27SDimitry Andric }; 173*06c3fb27SDimitry Andric 174*06c3fb27SDimitry Andric ObjcCategoryChecker::ObjcCategoryChecker() 175*06c3fb27SDimitry Andric : catLayout(target->wordSize), classLayout(target->wordSize), 176*06c3fb27SDimitry Andric roClassLayout(target->wordSize), listHeaderLayout(target->wordSize), 177*06c3fb27SDimitry Andric methodLayout(target->wordSize) {} 178*06c3fb27SDimitry Andric 179*06c3fb27SDimitry Andric // \p r must point to an offset within a cstring section. 180*06c3fb27SDimitry Andric static StringRef getReferentString(const Reloc &r) { 181*06c3fb27SDimitry Andric if (auto *isec = r.referent.dyn_cast<InputSection *>()) 182*06c3fb27SDimitry Andric return cast<CStringInputSection>(isec)->getStringRefAtOffset(r.addend); 183*06c3fb27SDimitry Andric auto *sym = cast<Defined>(r.referent.get<Symbol *>()); 184*06c3fb27SDimitry Andric return cast<CStringInputSection>(sym->isec)->getStringRefAtOffset(sym->value + 185*06c3fb27SDimitry Andric r.addend); 186*06c3fb27SDimitry Andric } 187*06c3fb27SDimitry Andric 188*06c3fb27SDimitry Andric void ObjcCategoryChecker::parseMethods(const ConcatInputSection *methodsIsec, 189*06c3fb27SDimitry Andric const Symbol *methodContainerSym, 190*06c3fb27SDimitry Andric const ConcatInputSection *containerIsec, 191*06c3fb27SDimitry Andric MethodContainerKind mcKind, 192*06c3fb27SDimitry Andric MethodKind mKind) { 193*06c3fb27SDimitry Andric ObjcClass &klass = classMap[methodContainerSym]; 194*06c3fb27SDimitry Andric for (const Reloc &r : methodsIsec->relocs) { 195*06c3fb27SDimitry Andric if ((r.offset - listHeaderLayout.totalSize) % methodLayout.totalSize != 196*06c3fb27SDimitry Andric methodLayout.nameOffset) 197*06c3fb27SDimitry Andric continue; 198*06c3fb27SDimitry Andric 199*06c3fb27SDimitry Andric CachedHashStringRef methodName(getReferentString(r)); 200*06c3fb27SDimitry Andric // +load methods are special: all implementations are called by the runtime 201*06c3fb27SDimitry Andric // even if they are part of the same class. Thus there is no need to check 202*06c3fb27SDimitry Andric // for duplicates. 203*06c3fb27SDimitry Andric // NOTE: Instead of specifically checking for this method name, ld64 simply 204*06c3fb27SDimitry Andric // checks whether a class / category is present in __objc_nlclslist / 205*06c3fb27SDimitry Andric // __objc_nlcatlist respectively. This will be the case if the class / 206*06c3fb27SDimitry Andric // category has a +load method. It skips optimizing the categories if there 207*06c3fb27SDimitry Andric // are multiple +load methods. Since it does dupe checking as part of the 208*06c3fb27SDimitry Andric // optimization process, this avoids spurious dupe messages around +load, 209*06c3fb27SDimitry Andric // but it also means that legit dupe issues for other methods are ignored. 210*06c3fb27SDimitry Andric if (mKind == MK_Static && methodName.val() == "load") 211*06c3fb27SDimitry Andric continue; 212*06c3fb27SDimitry Andric 213*06c3fb27SDimitry Andric auto &methodMap = 214*06c3fb27SDimitry Andric mKind == MK_Instance ? klass.instanceMethods : klass.classMethods; 215*06c3fb27SDimitry Andric if (methodMap 216*06c3fb27SDimitry Andric .try_emplace(methodName, MethodContainer{mcKind, containerIsec}) 217*06c3fb27SDimitry Andric .second) 218*06c3fb27SDimitry Andric continue; 219*06c3fb27SDimitry Andric 220*06c3fb27SDimitry Andric // We have a duplicate; generate a warning message. 221*06c3fb27SDimitry Andric const auto &mc = methodMap.lookup(methodName); 222*06c3fb27SDimitry Andric const Reloc *nameReloc = nullptr; 223*06c3fb27SDimitry Andric if (mc.kind == MCK_Category) { 224*06c3fb27SDimitry Andric nameReloc = mc.isec->getRelocAt(catLayout.nameOffset); 225*06c3fb27SDimitry Andric } else { 226*06c3fb27SDimitry Andric assert(mc.kind == MCK_Class); 227*06c3fb27SDimitry Andric const auto *roIsec = mc.isec->getRelocAt(classLayout.roDataOffset) 228*06c3fb27SDimitry Andric ->getReferentInputSection(); 229*06c3fb27SDimitry Andric nameReloc = roIsec->getRelocAt(roClassLayout.nameOffset); 230*06c3fb27SDimitry Andric } 231*06c3fb27SDimitry Andric StringRef containerName = getReferentString(*nameReloc); 232*06c3fb27SDimitry Andric StringRef methPrefix = mKind == MK_Instance ? "-" : "+"; 233*06c3fb27SDimitry Andric 234*06c3fb27SDimitry Andric // We should only ever encounter collisions when parsing category methods 235*06c3fb27SDimitry Andric // (since the Class struct is parsed before any of its categories). 236*06c3fb27SDimitry Andric assert(mcKind == MCK_Category); 237*06c3fb27SDimitry Andric StringRef newCatName = 238*06c3fb27SDimitry Andric getReferentString(*containerIsec->getRelocAt(catLayout.nameOffset)); 239*06c3fb27SDimitry Andric 240*06c3fb27SDimitry Andric StringRef containerType = mc.kind == MCK_Category ? "category" : "class"; 241*06c3fb27SDimitry Andric warn("method '" + methPrefix + methodName.val() + 242*06c3fb27SDimitry Andric "' has conflicting definitions:\n>>> defined in category " + 243*06c3fb27SDimitry Andric newCatName + " from " + toString(containerIsec->getFile()) + 244*06c3fb27SDimitry Andric "\n>>> defined in " + containerType + " " + containerName + " from " + 245*06c3fb27SDimitry Andric toString(mc.isec->getFile())); 246*06c3fb27SDimitry Andric } 247*06c3fb27SDimitry Andric } 248*06c3fb27SDimitry Andric 249*06c3fb27SDimitry Andric void ObjcCategoryChecker::parseCategory(const ConcatInputSection *catIsec) { 250*06c3fb27SDimitry Andric auto *classReloc = catIsec->getRelocAt(catLayout.klassOffset); 251*06c3fb27SDimitry Andric if (!classReloc) 252*06c3fb27SDimitry Andric return; 253*06c3fb27SDimitry Andric 254*06c3fb27SDimitry Andric auto *classSym = classReloc->referent.get<Symbol *>(); 255*06c3fb27SDimitry Andric if (auto *d = dyn_cast<Defined>(classSym)) 256*06c3fb27SDimitry Andric if (!classMap.count(d)) 257*06c3fb27SDimitry Andric parseClass(d); 258*06c3fb27SDimitry Andric 259*06c3fb27SDimitry Andric if (const auto *r = catIsec->getRelocAt(catLayout.classMethodsOffset)) { 260*06c3fb27SDimitry Andric parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()), 261*06c3fb27SDimitry Andric classSym, catIsec, MCK_Category, MK_Static); 262*06c3fb27SDimitry Andric } 263*06c3fb27SDimitry Andric 264*06c3fb27SDimitry Andric if (const auto *r = catIsec->getRelocAt(catLayout.instanceMethodsOffset)) { 265*06c3fb27SDimitry Andric parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()), 266*06c3fb27SDimitry Andric classSym, catIsec, MCK_Category, MK_Instance); 267*06c3fb27SDimitry Andric } 268*06c3fb27SDimitry Andric } 269*06c3fb27SDimitry Andric 270*06c3fb27SDimitry Andric void ObjcCategoryChecker::parseClass(const Defined *classSym) { 271*06c3fb27SDimitry Andric // Given a Class struct, get its corresponding Methods struct 272*06c3fb27SDimitry Andric auto getMethodsIsec = 273*06c3fb27SDimitry Andric [&](const InputSection *classIsec) -> ConcatInputSection * { 274*06c3fb27SDimitry Andric if (const auto *r = classIsec->getRelocAt(classLayout.roDataOffset)) { 275*06c3fb27SDimitry Andric if (const auto *roIsec = 276*06c3fb27SDimitry Andric cast_or_null<ConcatInputSection>(r->getReferentInputSection())) { 277*06c3fb27SDimitry Andric if (const auto *r = 278*06c3fb27SDimitry Andric roIsec->getRelocAt(roClassLayout.baseMethodsOffset)) { 279*06c3fb27SDimitry Andric if (auto *methodsIsec = cast_or_null<ConcatInputSection>( 280*06c3fb27SDimitry Andric r->getReferentInputSection())) 281*06c3fb27SDimitry Andric return methodsIsec; 282*06c3fb27SDimitry Andric } 283*06c3fb27SDimitry Andric } 284*06c3fb27SDimitry Andric } 285*06c3fb27SDimitry Andric return nullptr; 286*06c3fb27SDimitry Andric }; 287*06c3fb27SDimitry Andric 288*06c3fb27SDimitry Andric const auto *classIsec = cast<ConcatInputSection>(classSym->isec); 289*06c3fb27SDimitry Andric 290*06c3fb27SDimitry Andric // Parse instance methods. 291*06c3fb27SDimitry Andric if (const auto *instanceMethodsIsec = getMethodsIsec(classIsec)) 292*06c3fb27SDimitry Andric parseMethods(instanceMethodsIsec, classSym, classIsec, MCK_Class, 293*06c3fb27SDimitry Andric MK_Instance); 294*06c3fb27SDimitry Andric 295*06c3fb27SDimitry Andric // Class methods are contained in the metaclass. 296*06c3fb27SDimitry Andric if (const auto *r = classSym->isec->getRelocAt(classLayout.metaClassOffset)) 297*06c3fb27SDimitry Andric if (const auto *classMethodsIsec = getMethodsIsec( 298*06c3fb27SDimitry Andric cast<ConcatInputSection>(r->getReferentInputSection()))) 299*06c3fb27SDimitry Andric parseMethods(classMethodsIsec, classSym, classIsec, MCK_Class, MK_Static); 300*06c3fb27SDimitry Andric } 301*06c3fb27SDimitry Andric 302*06c3fb27SDimitry Andric void objc::checkCategories() { 303*06c3fb27SDimitry Andric ObjcCategoryChecker checker; 304*06c3fb27SDimitry Andric for (const InputSection *isec : inputSections) { 305*06c3fb27SDimitry Andric if (isec->getName() == section_names::objcCatList) 306*06c3fb27SDimitry Andric for (const Reloc &r : isec->relocs) { 307*06c3fb27SDimitry Andric auto *catIsec = cast<ConcatInputSection>(r.getReferentInputSection()); 308*06c3fb27SDimitry Andric checker.parseCategory(catIsec); 309*06c3fb27SDimitry Andric } 310*06c3fb27SDimitry Andric } 311*06c3fb27SDimitry Andric } 312