1 //===- ObjC.cpp -----------------------------------------------------------===//
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
7 //===----------------------------------------------------------------------===//
10 #include "InputFiles.h"
11 #include "InputSection.h"
13 #include "OutputSegment.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"
22 using namespace llvm::MachO
;
24 using namespace lld::macho
;
26 template <class LP
> static bool objectHasObjCSection(MemoryBufferRef mb
) {
27 using SectionHeader
= typename
LP::section
;
30 reinterpret_cast<const typename
LP::mach_header
*>(mb
.getBufferStart());
31 if (hdr
->magic
!= LP::magic
)
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
))) {
54 static bool objectHasObjCSection(MemoryBufferRef mb
) {
55 if (target
->wordSize
== 8)
56 return ::objectHasObjCSection
<LP64
>(mb
);
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
));
74 #define FOR_EACH_CATEGORY_FIELD(DO) \
77 DO(Ptr, instanceMethods) \
78 DO(Ptr, classMethods) \
80 DO(Ptr, instanceProps) \
83 CREATE_LAYOUT_CLASS(Category
, FOR_EACH_CATEGORY_FIELD
);
85 #undef FOR_EACH_CATEGORY_FIELD
87 #define FOR_EACH_CLASS_FIELD(DO) \
90 DO(Ptr, methodCache) \
94 CREATE_LAYOUT_CLASS(Class
, FOR_EACH_CLASS_FIELD
);
96 #undef FOR_EACH_CLASS_FIELD
98 #define FOR_EACH_RO_CLASS_FIELD(DO) \
100 DO(uint32_t, instanceStart) \
101 DO(Ptr, instanceSize) \
102 DO(Ptr, ivarLayout) \
104 DO(Ptr, baseMethods) \
105 DO(Ptr, baseProtocols) \
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) \
118 CREATE_LAYOUT_CLASS(ListHeader
, FOR_EACH_LIST_HEADER
);
120 #undef FOR_EACH_LIST_HEADER
122 #define FOR_EACH_METHOD(DO) \
127 CREATE_LAYOUT_CLASS(Method
, FOR_EACH_METHOD
);
129 #undef FOR_EACH_METHOD
131 enum MethodContainerKind
{
136 struct MethodContainer
{
137 MethodContainerKind kind
;
138 const ConcatInputSection
*isec
;
147 DenseMap
<CachedHashStringRef
, MethodContainer
> instanceMethods
;
148 DenseMap
<CachedHashStringRef
, MethodContainer
> classMethods
;
153 class ObjcCategoryChecker
{
155 ObjcCategoryChecker();
156 void parseCategory(const ConcatInputSection
*catListIsec
);
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
+
188 void ObjcCategoryChecker::parseMethods(const ConcatInputSection
*methodsIsec
,
189 const Symbol
*methodContainerSym
,
190 const ConcatInputSection
*containerIsec
,
191 MethodContainerKind mcKind
,
193 ObjcClass
&klass
= classMap
[methodContainerSym
];
194 for (const Reloc
&r
: methodsIsec
->relocs
) {
195 if ((r
.offset
- listHeaderLayout
.totalSize
) % methodLayout
.totalSize
!=
196 methodLayout
.nameOffset
)
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
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")
214 mKind
== MK_Instance
? klass
.instanceMethods
: klass
.classMethods
;
216 .try_emplace(methodName
, MethodContainer
{mcKind
, containerIsec
})
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
);
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
);
254 auto *classSym
= classReloc
->referent
.get
<Symbol
*>();
255 if (auto *d
= dyn_cast
<Defined
>(classSym
))
256 if (!classMap
.count(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())) {
278 roIsec
->getRelocAt(roClassLayout
.baseMethodsOffset
)) {
279 if (auto *methodsIsec
= cast_or_null
<ConcatInputSection
>(
280 r
->getReferentInputSection()))
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
,
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
);