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