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 
objectHasObjCSection(MemoryBufferRef mb)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 
objectHasObjCSection(MemoryBufferRef mb)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 
hasObjCSection(MemoryBufferRef mb)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 
ObjcCategoryChecker()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.
getReferentString(const Reloc & r)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 
parseMethods(const ConcatInputSection * methodsIsec,const Symbol * methodContainerSym,const ConcatInputSection * containerIsec,MethodContainerKind mcKind,MethodKind mKind)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 
parseCategory(const ConcatInputSection * catIsec)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 
parseClass(const Defined * classSym)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 
checkCategories()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