Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / lld / MachO / ObjC.cpp
blob7e8cec026d2f7c257c383d61596475cd9f6eed1b
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 //===----------------------------------------------------------------------===//
9 #include "ObjC.h"
10 #include "InputFiles.h"
11 #include "InputSection.h"
12 #include "Layout.h"
13 #include "OutputSegment.h"
14 #include "Target.h"
16 #include "lld/Common/ErrorHandler.h"
17 #include "llvm/ADT/DenseMap.h"
18 #include "llvm/BinaryFormat/MachO.h"
19 #include "llvm/Bitcode/BitcodeReader.h"
21 using namespace llvm;
22 using namespace llvm::MachO;
23 using namespace lld;
24 using namespace lld::macho;
26 template <class LP> static bool objectHasObjCSection(MemoryBufferRef mb) {
27 using SectionHeader = typename LP::section;
29 auto *hdr =
30 reinterpret_cast<const typename LP::mach_header *>(mb.getBufferStart());
31 if (hdr->magic != LP::magic)
32 return false;
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;
51 return false;
54 static bool objectHasObjCSection(MemoryBufferRef mb) {
55 if (target->wordSize == 8)
56 return ::objectHasObjCSection<LP64>(mb);
57 else
58 return ::objectHasObjCSection<ILP32>(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;
72 namespace {
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)
83 CREATE_LAYOUT_CLASS(Category, FOR_EACH_CATEGORY_FIELD);
85 #undef FOR_EACH_CATEGORY_FIELD
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)
94 CREATE_LAYOUT_CLASS(Class, FOR_EACH_CLASS_FIELD);
96 #undef FOR_EACH_CLASS_FIELD
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)
110 CREATE_LAYOUT_CLASS(ROClass, FOR_EACH_RO_CLASS_FIELD);
112 #undef FOR_EACH_RO_CLASS_FIELD
114 #define FOR_EACH_LIST_HEADER(DO) \
115 DO(uint32_t, size) \
116 DO(uint32_t, count)
118 CREATE_LAYOUT_CLASS(ListHeader, FOR_EACH_LIST_HEADER);
120 #undef FOR_EACH_LIST_HEADER
122 #define FOR_EACH_METHOD(DO) \
123 DO(Ptr, name) \
124 DO(Ptr, type) \
125 DO(Ptr, impl)
127 CREATE_LAYOUT_CLASS(Method, FOR_EACH_METHOD);
129 #undef FOR_EACH_METHOD
131 enum MethodContainerKind {
132 MCK_Class,
133 MCK_Category,
136 struct MethodContainer {
137 MethodContainerKind kind;
138 const ConcatInputSection *isec;
141 enum MethodKind {
142 MK_Instance,
143 MK_Static,
146 struct ObjcClass {
147 DenseMap<CachedHashStringRef, MethodContainer> instanceMethods;
148 DenseMap<CachedHashStringRef, MethodContainer> classMethods;
151 } // namespace
153 class ObjcCategoryChecker {
154 public:
155 ObjcCategoryChecker();
156 void parseCategory(const ConcatInputSection *catListIsec);
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);
165 CategoryLayout catLayout;
166 ClassLayout classLayout;
167 ROClassLayout roClassLayout;
168 ListHeaderLayout listHeaderLayout;
169 MethodLayout methodLayout;
171 DenseMap<const Symbol *, ObjcClass> classMap;
174 ObjcCategoryChecker::ObjcCategoryChecker()
175 : catLayout(target->wordSize), classLayout(target->wordSize),
176 roClassLayout(target->wordSize), listHeaderLayout(target->wordSize),
177 methodLayout(target->wordSize) {}
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);
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;
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;
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;
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);
231 StringRef containerName = getReferentString(*nameReloc);
232 StringRef methPrefix = mKind == MK_Instance ? "-" : "+";
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));
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()));
249 void ObjcCategoryChecker::parseCategory(const ConcatInputSection *catIsec) {
250 auto *classReloc = catIsec->getRelocAt(catLayout.klassOffset);
251 if (!classReloc)
252 return;
254 auto *classSym = classReloc->referent.get<Symbol *>();
255 if (auto *d = dyn_cast<Defined>(classSym))
256 if (!classMap.count(d))
257 parseClass(d);
259 if (const auto *r = catIsec->getRelocAt(catLayout.classMethodsOffset)) {
260 parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()),
261 classSym, catIsec, MCK_Category, MK_Static);
264 if (const auto *r = catIsec->getRelocAt(catLayout.instanceMethodsOffset)) {
265 parseMethods(cast<ConcatInputSection>(r->getReferentInputSection()),
266 classSym, catIsec, MCK_Category, MK_Instance);
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;
285 return nullptr;
288 const auto *classIsec = cast<ConcatInputSection>(classSym->isec);
290 // Parse instance methods.
291 if (const auto *instanceMethodsIsec = getMethodsIsec(classIsec))
292 parseMethods(instanceMethodsIsec, classSym, classIsec, MCK_Class,
293 MK_Instance);
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);
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);