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