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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 #include "ObjC.h" 10 #include "InputFiles.h" 11 #include "InputSection.h" 12 #include "Layout.h" 13 #include "OutputSegment.h" 14 #include "Target.h" 15 16 #include "lld/Common/ErrorHandler.h" 17 #include "llvm/ADT/DenseMap.h" 18 #include "llvm/BinaryFormat/MachO.h" 19 #include "llvm/Bitcode/BitcodeReader.h" 20 21 using namespace llvm; 22 using namespace llvm::MachO; 23 using namespace lld; 24 using namespace lld::macho; 25 26 template <class LP> static bool objectHasObjCSection(MemoryBufferRef mb) { 27 using SectionHeader = typename LP::section; 28 29 auto *hdr = 30 reinterpret_cast<const typename LP::mach_header *>(mb.getBufferStart()); 31 if (hdr->magic != LP::magic) 32 return false; 33 34 if (const auto *c = 35 findCommand<typename LP::segment_command>(hdr, LP::segmentLCType)) { 36 auto sectionHeaders = ArrayRef<SectionHeader>{ 37 reinterpret_cast<const SectionHeader *>(c + 1), c->nsects}; 38 for (const SectionHeader &secHead : sectionHeaders) { 39 StringRef sectname(secHead.sectname, 40 strnlen(secHead.sectname, sizeof(secHead.sectname))); 41 StringRef segname(secHead.segname, 42 strnlen(secHead.segname, sizeof(secHead.segname))); 43 if ((segname == segment_names::data && 44 sectname == section_names::objcCatList) || 45 (segname == segment_names::text && 46 sectname.starts_with(section_names::swift))) { 47 return true; 48 } 49 } 50 } 51 return false; 52 } 53 54 static bool objectHasObjCSection(MemoryBufferRef mb) { 55 if (target->wordSize == 8) 56 return ::objectHasObjCSection<LP64>(mb); 57 else 58 return ::objectHasObjCSection<ILP32>(mb); 59 } 60 61 bool macho::hasObjCSection(MemoryBufferRef mb) { 62 switch (identify_magic(mb.getBuffer())) { 63 case file_magic::macho_object: 64 return objectHasObjCSection(mb); 65 case file_magic::bitcode: 66 return check(isBitcodeContainingObjCCategory(mb)); 67 default: 68 return false; 69 } 70 } 71 72 namespace { 73 74 #define FOR_EACH_CATEGORY_FIELD(DO) \ 75 DO(Ptr, name) \ 76 DO(Ptr, klass) \ 77 DO(Ptr, instanceMethods) \ 78 DO(Ptr, classMethods) \ 79 DO(Ptr, protocols) \ 80 DO(Ptr, instanceProps) \ 81 DO(Ptr, classProps) 82 83 CREATE_LAYOUT_CLASS(Category, FOR_EACH_CATEGORY_FIELD); 84 85 #undef FOR_EACH_CATEGORY_FIELD 86 87 #define FOR_EACH_CLASS_FIELD(DO) \ 88 DO(Ptr, metaClass) \ 89 DO(Ptr, superClass) \ 90 DO(Ptr, methodCache) \ 91 DO(Ptr, vtable) \ 92 DO(Ptr, roData) 93 94 CREATE_LAYOUT_CLASS(Class, FOR_EACH_CLASS_FIELD); 95 96 #undef FOR_EACH_CLASS_FIELD 97 98 #define FOR_EACH_RO_CLASS_FIELD(DO) \ 99 DO(uint32_t, flags) \ 100 DO(uint32_t, instanceStart) \ 101 DO(Ptr, instanceSize) \ 102 DO(Ptr, ivarLayout) \ 103 DO(Ptr, name) \ 104 DO(Ptr, baseMethods) \ 105 DO(Ptr, baseProtocols) \ 106 DO(Ptr, ivars) \ 107 DO(Ptr, weakIvarLayout) \ 108 DO(Ptr, baseProperties) 109 110 CREATE_LAYOUT_CLASS(ROClass, FOR_EACH_RO_CLASS_FIELD); 111 112 #undef FOR_EACH_RO_CLASS_FIELD 113 114 #define FOR_EACH_LIST_HEADER(DO) \ 115 DO(uint32_t, size) \ 116 DO(uint32_t, count) 117 118 CREATE_LAYOUT_CLASS(ListHeader, FOR_EACH_LIST_HEADER); 119 120 #undef FOR_EACH_LIST_HEADER 121 122 #define FOR_EACH_METHOD(DO) \ 123 DO(Ptr, name) \ 124 DO(Ptr, type) \ 125 DO(Ptr, impl) 126 127 CREATE_LAYOUT_CLASS(Method, FOR_EACH_METHOD); 128 129 #undef FOR_EACH_METHOD 130 131 enum MethodContainerKind { 132 MCK_Class, 133 MCK_Category, 134 }; 135 136 struct MethodContainer { 137 MethodContainerKind kind; 138 const ConcatInputSection *isec; 139 }; 140 141 enum MethodKind { 142 MK_Instance, 143 MK_Static, 144 }; 145 146 struct ObjcClass { 147 DenseMap<CachedHashStringRef, MethodContainer> instanceMethods; 148 DenseMap<CachedHashStringRef, MethodContainer> classMethods; 149 }; 150 151 } // namespace 152 153 class ObjcCategoryChecker { 154 public: 155 ObjcCategoryChecker(); 156 void parseCategory(const ConcatInputSection *catListIsec); 157 158 private: 159 void parseClass(const Defined *classSym); 160 void parseMethods(const ConcatInputSection *methodsIsec, 161 const Symbol *methodContainer, 162 const ConcatInputSection *containerIsec, 163 MethodContainerKind, MethodKind); 164 165 CategoryLayout catLayout; 166 ClassLayout classLayout; 167 ROClassLayout roClassLayout; 168 ListHeaderLayout listHeaderLayout; 169 MethodLayout methodLayout; 170 171 DenseMap<const Symbol *, ObjcClass> classMap; 172 }; 173 174 ObjcCategoryChecker::ObjcCategoryChecker() 175 : catLayout(target->wordSize), classLayout(target->wordSize), 176 roClassLayout(target->wordSize), listHeaderLayout(target->wordSize), 177 methodLayout(target->wordSize) {} 178 179 // \p r must point to an offset within a cstring section. 180 static StringRef getReferentString(const Reloc &r) { 181 if (auto *isec = r.referent.dyn_cast<InputSection *>()) 182 return cast<CStringInputSection>(isec)->getStringRefAtOffset(r.addend); 183 auto *sym = cast<Defined>(r.referent.get<Symbol *>()); 184 return cast<CStringInputSection>(sym->isec)->getStringRefAtOffset(sym->value + 185 r.addend); 186 } 187 188 void 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(getReferentString(r)); 200 // +load methods are special: all implementations are called by the runtime 201 // even if they are part of the same class. Thus there is no need to check 202 // for duplicates. 203 // NOTE: Instead of specifically checking for this method name, ld64 simply 204 // 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 there 207 // are multiple +load methods. Since it does dupe checking as part of the 208 // 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 (methodMap 216 .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 = getReferentString(*nameReloc); 232 StringRef methPrefix = mKind == MK_Instance ? "-" : "+"; 233 234 // We should only ever encounter collisions when parsing category methods 235 // (since the Class struct is parsed before any of its categories). 236 assert(mcKind == MCK_Category); 237 StringRef newCatName = 238 getReferentString(*containerIsec->getRelocAt(catLayout.nameOffset)); 239 240 StringRef containerType = mc.kind == MCK_Category ? "category" : "class"; 241 warn("method '" + methPrefix + methodName.val() + 242 "' has conflicting definitions:\n>>> defined in category " + 243 newCatName + " from " + toString(containerIsec->getFile()) + 244 "\n>>> defined in " + containerType + " " + containerName + " from " + 245 toString(mc.isec->getFile())); 246 } 247 } 248 249 void ObjcCategoryChecker::parseCategory(const ConcatInputSection *catIsec) { 250 auto *classReloc = catIsec->getRelocAt(catLayout.klassOffset); 251 if (!classReloc) 252 return; 253 254 auto *classSym = classReloc->referent.get<Symbol *>(); 255 if (auto *d = dyn_cast<Defined>(classSym)) 256 if (!classMap.count(d)) 257 parseClass(d); 258 259 if (const auto *r = catIsec->getRelocAt(catLayout.classMethodsOffset)) { 260 parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()), 261 classSym, catIsec, MCK_Category, MK_Static); 262 } 263 264 if (const auto *r = catIsec->getRelocAt(catLayout.instanceMethodsOffset)) { 265 parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()), 266 classSym, catIsec, MCK_Category, MK_Instance); 267 } 268 } 269 270 void ObjcCategoryChecker::parseClass(const Defined *classSym) { 271 // Given a Class struct, get its corresponding Methods struct 272 auto getMethodsIsec = 273 [&](const InputSection *classIsec) -> ConcatInputSection * { 274 if (const auto *r = classIsec->getRelocAt(classLayout.roDataOffset)) { 275 if (const auto *roIsec = 276 cast_or_null<ConcatInputSection>(r->getReferentInputSection())) { 277 if (const auto *r = 278 roIsec->getRelocAt(roClassLayout.baseMethodsOffset)) { 279 if (auto *methodsIsec = cast_or_null<ConcatInputSection>( 280 r->getReferentInputSection())) 281 return methodsIsec; 282 } 283 } 284 } 285 return nullptr; 286 }; 287 288 const auto *classIsec = cast<ConcatInputSection>(classSym->isec); 289 290 // Parse instance methods. 291 if (const auto *instanceMethodsIsec = getMethodsIsec(classIsec)) 292 parseMethods(instanceMethodsIsec, classSym, classIsec, MCK_Class, 293 MK_Instance); 294 295 // Class methods are contained in the metaclass. 296 if (const auto *r = classSym->isec->getRelocAt(classLayout.metaClassOffset)) 297 if (const auto *classMethodsIsec = getMethodsIsec( 298 cast<ConcatInputSection>(r->getReferentInputSection()))) 299 parseMethods(classMethodsIsec, classSym, classIsec, MCK_Class, MK_Static); 300 } 301 302 void objc::checkCategories() { 303 ObjcCategoryChecker checker; 304 for (const InputSection *isec : inputSections) { 305 if (isec->getName() == section_names::objcCatList) 306 for (const Reloc &r : isec->relocs) { 307 auto *catIsec = cast<ConcatInputSection>(r.getReferentInputSection()); 308 checker.parseCategory(catIsec); 309 } 310 } 311 } 312