]> git.saurik.com Git - apple/ld64.git/blob - src/ld/passes/objc.cpp
ld64-351.8.tar.gz
[apple/ld64.git] / src / ld / passes / objc.cpp
1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2010-2011 Apple Inc. All rights reserved.
4 *
5 * @APPLE_LICENSE_HEADER_START@
6 *
7 * This file contains Original Code and/or Modifications of Original Code
8 * as defined in and that are subject to the Apple Public Source License
9 * Version 2.0 (the 'License'). You may not use this file except in
10 * compliance with the License. Please obtain a copy of the License at
11 * http://www.opensource.apple.com/apsl/ and read it before using this
12 * file.
13 *
14 * The Original Code and all software distributed under the License are
15 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
16 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
17 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
19 * Please see the License for the specific language governing rights and
20 * limitations under the License.
21 *
22 * @APPLE_LICENSE_HEADER_END@
23 */
24
25
26 #include <stdint.h>
27 #include <math.h>
28 #include <unistd.h>
29 #include <dlfcn.h>
30 #include <mach/machine.h>
31
32 #include <vector>
33 #include <map>
34 #include <set>
35
36 #include "Architectures.hpp"
37 #include "MachOFileAbstraction.hpp"
38
39 #include "ld.hpp"
40 #include "objc.h"
41
42 namespace ld {
43 namespace passes {
44 namespace objc {
45
46
47
48 struct objc_image_info {
49 uint32_t version; // initially 0
50 uint32_t flags;
51 };
52
53 #define OBJC_IMAGE_SUPPORTS_GC (1<<1)
54 #define OBJC_IMAGE_REQUIRES_GC (1<<2)
55 #define OBJC_IMAGE_OPTIMIZED_BY_DYLD (1<<3)
56 #define OBJC_IMAGE_SUPPORTS_COMPACTION (1<<4)
57 #define OBJC_IMAGE_IS_SIMULATED (1<<5)
58 #define OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES (1<<6)
59
60
61
62 //
63 // This class is the 8 byte section containing ObjC flags
64 //
65 template <typename A>
66 class ObjCImageInfoAtom : public ld::Atom {
67 public:
68 ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint,
69 bool compaction, bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion);
70
71 virtual const ld::File* file() const { return NULL; }
72 virtual const char* name() const { return "objc image info"; }
73 virtual uint64_t size() const { return sizeof(objc_image_info); }
74 virtual uint64_t objectAddress() const { return 0; }
75 virtual void setScope(Scope) { }
76 virtual void copyRawContent(uint8_t buffer[]) const {
77 memcpy(buffer, &_content, sizeof(objc_image_info));
78 }
79
80 private:
81 objc_image_info _content;
82
83 static ld::Section _s_sectionABI1;
84 static ld::Section _s_sectionABI2;
85 };
86
87 template <typename A> ld::Section ObjCImageInfoAtom<A>::_s_sectionABI1("__OBJC", "__image_info", ld::Section::typeUnclassified);
88 template <typename A> ld::Section ObjCImageInfoAtom<A>::_s_sectionABI2("__DATA", "__objc_imageinfo", ld::Section::typeUnclassified);
89
90
91 template <typename A>
92 ObjCImageInfoAtom<A>::ObjCImageInfoAtom(ld::File::ObjcConstraint objcConstraint, bool compaction,
93 bool abi2, bool hasCategoryClassProperties, uint8_t swiftVersion)
94 : ld::Atom(abi2 ? _s_sectionABI2 : _s_sectionABI1, ld::Atom::definitionRegular, ld::Atom::combineNever,
95 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
96 symbolTableNotIn, false, false, false, ld::Atom::Alignment(2))
97 {
98
99 uint32_t value = 0;
100 switch ( objcConstraint ) {
101 case ld::File::objcConstraintNone:
102 case ld::File::objcConstraintRetainRelease:
103 if ( compaction )
104 warning("ignoring -objc_gc_compaction because code not compiled for ObjC garbage collection");
105 break;
106 case ld::File::objcConstraintRetainReleaseOrGC:
107 value |= OBJC_IMAGE_SUPPORTS_GC;
108 if ( compaction )
109 value |= OBJC_IMAGE_SUPPORTS_COMPACTION;
110 break;
111 case ld::File::objcConstraintGC:
112 value |= OBJC_IMAGE_SUPPORTS_GC | OBJC_IMAGE_REQUIRES_GC;
113 if ( compaction )
114 value |= OBJC_IMAGE_SUPPORTS_COMPACTION;
115 break;
116 case ld::File::objcConstraintRetainReleaseForSimulator:
117 value |= OBJC_IMAGE_IS_SIMULATED;
118 break;
119 }
120
121 if ( hasCategoryClassProperties ) {
122 value |= OBJC_IMAGE_HAS_CATEGORY_CLASS_PROPERTIES;
123 }
124
125 // provide swift language version in final binary for runtime to inspect
126 value |= (swiftVersion << 8);
127
128 _content.version = 0;
129 A::P::E::set32(_content.flags, value);
130 }
131
132
133
134 //
135 // This class is for a new Atom which is an ObjC method list created by merging method lists from categories
136 //
137 template <typename A>
138 class MethodListAtom : public ld::Atom {
139 public:
140 MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta,
141 const std::vector<const ld::Atom*>* categories,
142 std::set<const ld::Atom*>& deadAtoms);
143
144 virtual const ld::File* file() const { return _file; }
145 virtual const char* name() const { return "objc merged method list"; }
146 virtual uint64_t size() const { return _methodCount*3*sizeof(pint_t) + 8; }
147 virtual uint64_t objectAddress() const { return 0; }
148 virtual void setScope(Scope) { }
149 virtual void copyRawContent(uint8_t buffer[]) const {
150 bzero(buffer, size());
151 A::P::E::set32(*((uint32_t*)(&buffer[0])), 3*sizeof(pint_t)); // entry size
152 A::P::E::set32(*((uint32_t*)(&buffer[4])), _methodCount);
153 }
154 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; }
155 virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; }
156
157 private:
158 typedef typename A::P::uint_t pint_t;
159
160 const ld::File* _file;
161 unsigned int _methodCount;
162 std::vector<ld::Fixup> _fixups;
163
164 static ld::Section _s_section;
165 };
166
167 template <typename A>
168 ld::Section MethodListAtom<A>::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified);
169
170
171 //
172 // This class is for a new Atom which is an ObjC protocol list created by merging protocol lists from categories
173 //
174 template <typename A>
175 class ProtocolListAtom : public ld::Atom {
176 public:
177 ProtocolListAtom(ld::Internal& state, const ld::Atom* baseProtocolList,
178 const std::vector<const ld::Atom*>* categories,
179 std::set<const ld::Atom*>& deadAtoms);
180
181 virtual const ld::File* file() const { return _file; }
182 virtual const char* name() const { return "objc merged protocol list"; }
183 virtual uint64_t size() const { return (_protocolCount+1)*sizeof(pint_t); }
184 virtual uint64_t objectAddress() const { return 0; }
185 virtual void setScope(Scope) { }
186 virtual void copyRawContent(uint8_t buffer[]) const {
187 bzero(buffer, size());
188 A::P::setP(*((pint_t*)(buffer)), _protocolCount);
189 }
190 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; }
191 virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; }
192
193 private:
194 typedef typename A::P::uint_t pint_t;
195
196 const ld::File* _file;
197 unsigned int _protocolCount;
198 std::vector<ld::Fixup> _fixups;
199
200 static ld::Section _s_section;
201 };
202
203 template <typename A>
204 ld::Section ProtocolListAtom<A>::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified);
205
206
207
208 //
209 // This class is for a new Atom which is an ObjC property list created by merging property lists from categories
210 //
211 template <typename A>
212 class PropertyListAtom : public ld::Atom {
213 public:
214 enum class PropertyKind { ClassProperties, InstanceProperties };
215
216 PropertyListAtom(ld::Internal& state, const ld::Atom* baseProtocolList,
217 const std::vector<const ld::Atom*>* categories,
218 std::set<const ld::Atom*>& deadAtoms,
219 PropertyKind kind);
220
221 virtual const ld::File* file() const { return _file; }
222 virtual const char* name() const { return "objc merged property list"; }
223 virtual uint64_t size() const { return _propertyCount*2*sizeof(pint_t) + 8; }
224 virtual uint64_t objectAddress() const { return 0; }
225 virtual void setScope(Scope) { }
226 virtual void copyRawContent(uint8_t buffer[]) const {
227 bzero(buffer, size());
228 A::P::E::set32(((uint32_t*)(buffer))[0], 2*sizeof(pint_t)); // sizeof(objc_property)
229 A::P::E::set32(((uint32_t*)(buffer))[1], _propertyCount);
230 }
231 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; }
232 virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; }
233
234 private:
235 typedef typename A::P::uint_t pint_t;
236
237 const ld::File* _file;
238 unsigned int _propertyCount;
239 std::vector<ld::Fixup> _fixups;
240
241 static ld::Section _s_section;
242 };
243
244 template <typename A>
245 ld::Section PropertyListAtom<A>::_s_section("__DATA", "__objc_const", ld::Section::typeUnclassified);
246
247
248
249
250
251 //
252 // This class is used to create an Atom that replaces an atom from a .o file that holds a class_ro_t.
253 // It is needed because there is no way to add Fixups to an existing atom.
254 //
255 template <typename A>
256 class ClassROOverlayAtom : public ld::Atom {
257 public:
258 ClassROOverlayAtom(const ld::Atom* classROAtom);
259
260 // overrides of ld::Atom
261 virtual const ld::File* file() const { return _atom->file(); }
262 virtual const char* name() const { return _atom->name(); }
263 virtual uint64_t size() const { return _atom->size(); }
264 virtual uint64_t objectAddress() const { return _atom->objectAddress(); }
265 virtual void copyRawContent(uint8_t buffer[]) const
266 { _atom->copyRawContent(buffer); }
267 virtual const uint8_t* rawContentPointer() const
268 { return _atom->rawContentPointer(); }
269 virtual unsigned long contentHash(const class ld::IndirectBindingTable& ibt) const
270 { return _atom->contentHash(ibt); }
271 virtual bool canCoalesceWith(const ld::Atom& rhs, const class ld::IndirectBindingTable& ibt) const
272 { return _atom->canCoalesceWith(rhs,ibt); }
273
274 virtual ld::Fixup::iterator fixupsBegin() const { return (ld::Fixup*)&_fixups[0]; }
275 virtual ld::Fixup::iterator fixupsEnd() const { return (ld::Fixup*)&_fixups[_fixups.size()]; }
276
277 void addProtocolListFixup();
278 void addPropertyListFixup();
279 void addMethodListFixup();
280
281 private:
282 typedef typename A::P::uint_t pint_t;
283
284 void addFixupAtOffset(uint32_t offset);
285
286 const ld::Atom* _atom;
287 std::vector<ld::Fixup> _fixups;
288 };
289
290 template <typename A>
291 ClassROOverlayAtom<A>::ClassROOverlayAtom(const ld::Atom* classROAtom)
292 : ld::Atom(classROAtom->section(), ld::Atom::definitionRegular, ld::Atom::combineNever,
293 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
294 classROAtom->symbolTableInclusion(), false, false, false, classROAtom->alignment()),
295 _atom(classROAtom)
296 {
297 // ensure all attributes are same as original
298 this->setAttributesFromAtom(*classROAtom);
299
300 // copy fixups from orginal atom
301 for (ld::Fixup::iterator fit=classROAtom->fixupsBegin(); fit != classROAtom->fixupsEnd(); ++fit) {
302 ld::Fixup fixup = *fit;
303 _fixups.push_back(fixup);
304 }
305 }
306
307
308 //
309 // Base class for reading and updating existing ObjC atoms from .o files
310 //
311 template <typename A>
312 class ObjCData {
313 public:
314 static const ld::Atom* getPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, unsigned int offset, bool* hasAddend=NULL);
315 static void setPointerInContent(ld::Internal& state, const ld::Atom* contentAtom,
316 unsigned int offset, const ld::Atom* newAtom);
317 typedef typename A::P::uint_t pint_t;
318 };
319
320 template <typename A>
321 const ld::Atom* ObjCData<A>::getPointerInContent(ld::Internal& state, const ld::Atom* contentAtom, unsigned int offset, bool* hasAddend)
322 {
323 const ld::Atom* target = NULL;
324 if ( hasAddend != NULL )
325 *hasAddend = false;
326 for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) {
327 if ( (fit->offsetInAtom == offset) && (fit->kind != ld::Fixup::kindNoneFollowOn) ) {
328 switch ( fit->binding ) {
329 case ld::Fixup::bindingsIndirectlyBound:
330 target = state.indirectBindingTable[fit->u.bindingIndex];
331 break;
332 case ld::Fixup::bindingDirectlyBound:
333 target = fit->u.target;
334 break;
335 case ld::Fixup::bindingNone:
336 if ( fit->kind == ld::Fixup::kindAddAddend ) {
337 if ( hasAddend != NULL )
338 *hasAddend = true;
339 }
340 break;
341 default:
342 break;
343 }
344 }
345 }
346 return target;
347 }
348
349 template <typename A>
350 void ObjCData<A>::setPointerInContent(ld::Internal& state, const ld::Atom* contentAtom,
351 unsigned int offset, const ld::Atom* newAtom)
352 {
353 for (ld::Fixup::iterator fit=contentAtom->fixupsBegin(); fit != contentAtom->fixupsEnd(); ++fit) {
354 if ( fit->offsetInAtom == offset ) {
355 switch ( fit->binding ) {
356 case ld::Fixup::bindingsIndirectlyBound:
357 state.indirectBindingTable[fit->u.bindingIndex] = newAtom;
358 return;
359 case ld::Fixup::bindingDirectlyBound:
360 fit->u.target = newAtom;
361 return;
362 default:
363 break;
364 }
365 }
366 }
367 assert(0 && "could not update method list");
368 }
369
370
371
372 //
373 // Helper class for reading and updating existing ObjC category atoms from .o files
374 //
375 template <typename A>
376 class Category : public ObjCData<A> {
377 public:
378 static const ld::Atom* getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend);
379 static const ld::Atom* getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom);
380 static const ld::Atom* getClassMethods(ld::Internal& state, const ld::Atom* contentAtom);
381 static const ld::Atom* getProtocols(ld::Internal& state, const ld::Atom* contentAtom);
382 static const ld::Atom* getInstanceProperties(ld::Internal& state, const ld::Atom* contentAtom);
383 static const ld::Atom* getClassProperties(ld::Internal& state, const ld::Atom* contentAtom);
384 static uint32_t size() { return 6*sizeof(pint_t); }
385 private:
386 typedef typename A::P::uint_t pint_t;
387 };
388
389
390 template <typename A>
391 const ld::Atom* Category<A>::getClass(ld::Internal& state, const ld::Atom* contentAtom, bool& hasAddend)
392 {
393 return ObjCData<A>::getPointerInContent(state, contentAtom, sizeof(pint_t), &hasAddend); // category_t.cls
394 }
395
396 template <typename A>
397 const ld::Atom* Category<A>::getInstanceMethods(ld::Internal& state, const ld::Atom* contentAtom)
398 {
399 return ObjCData<A>::getPointerInContent(state, contentAtom, 2*sizeof(pint_t)); // category_t.instanceMethods
400 }
401
402 template <typename A>
403 const ld::Atom* Category<A>::getClassMethods(ld::Internal& state, const ld::Atom* contentAtom)
404 {
405 return ObjCData<A>::getPointerInContent(state, contentAtom, 3*sizeof(pint_t)); // category_t.classMethods
406 }
407
408 template <typename A>
409 const ld::Atom* Category<A>::getProtocols(ld::Internal& state, const ld::Atom* contentAtom)
410 {
411 return ObjCData<A>::getPointerInContent(state, contentAtom, 4*sizeof(pint_t)); // category_t.protocols
412 }
413
414 template <typename A>
415 const ld::Atom* Category<A>::getInstanceProperties(ld::Internal& state, const ld::Atom* contentAtom)
416 {
417 return ObjCData<A>::getPointerInContent(state, contentAtom, 5*sizeof(pint_t)); // category_t.instanceProperties
418 }
419
420 template <typename A>
421 const ld::Atom* Category<A>::getClassProperties(ld::Internal& state, const ld::Atom* contentAtom)
422 {
423 // Only specially-marked files have this field.
424 if (!contentAtom->file()->objcHasCategoryClassPropertiesField()) return NULL;
425
426 return ObjCData<A>::getPointerInContent(state, contentAtom, 6*sizeof(pint_t)); // category_t.classProperties
427 }
428
429
430 template <typename A>
431 class MethodList : public ObjCData<A> {
432 public:
433 static uint32_t count(ld::Internal& state, const ld::Atom* methodListAtom) {
434 const uint32_t* methodListData = (uint32_t*)(methodListAtom->rawContentPointer());
435 return A::P::E::get32(methodListData[1]); // method_list_t.count
436 }
437 };
438
439 template <typename A>
440 class ProtocolList : public ObjCData<A> {
441 public:
442 static uint32_t count(ld::Internal& state, const ld::Atom* protocolListAtom) {
443 pint_t* protocolListData = (pint_t*)(protocolListAtom->rawContentPointer());
444 return A::P::getP(*protocolListData); // protocol_list_t.count
445 }
446 private:
447 typedef typename A::P::uint_t pint_t;
448 };
449
450 template <typename A>
451 class PropertyList : public ObjCData<A> {
452 public:
453 static uint32_t count(ld::Internal& state, const ld::Atom* protocolListAtom) {
454 uint32_t* protocolListData = (uint32_t*)(protocolListAtom->rawContentPointer());
455 return A::P::E::get32(protocolListData[1]); // property_list_t.count
456 }
457 private:
458 typedef typename A::P::uint_t pint_t;
459 };
460
461
462
463 //
464 // Helper class for reading and updating existing ObjC class atoms from .o files
465 //
466 template <typename A>
467 class Class : public ObjCData<A> {
468 public:
469 static const ld::Atom* getMetaClass(ld::Internal& state, const ld::Atom* classAtom);
470 static const ld::Atom* getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom);
471 static const ld::Atom* getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom);
472 static const ld::Atom* getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom);
473 static const ld::Atom* getClassMethodList(ld::Internal& state, const ld::Atom* classAtom);
474 static const ld::Atom* getClassPropertyList(ld::Internal& state, const ld::Atom* classAtom);
475 static const ld::Atom* setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom,
476 const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms);
477 static const ld::Atom* setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom,
478 const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms);
479 static const ld::Atom* setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom,
480 const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms);
481 static const ld::Atom* setClassMethodList(ld::Internal& state, const ld::Atom* classAtom,
482 const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms);
483 static const ld::Atom* setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom,
484 const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms);
485 static const ld::Atom* setClassPropertyList(ld::Internal& state, const ld::Atom* classAtom,
486 const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms);
487 static uint32_t size() { return sizeof(Content); }
488
489 private:
490 friend class ClassROOverlayAtom<A>;
491
492 typedef typename A::P::uint_t pint_t;
493
494 static const ld::Atom* getROData(ld::Internal& state, const ld::Atom* classAtom);
495
496 struct Content {
497 pint_t isa;
498 pint_t superclass;
499 pint_t method_cache;
500 pint_t vtable;
501 pint_t data;
502 };
503
504 struct ROContent {
505 uint32_t flags;
506 uint32_t instanceStart;
507 // Note there is 4-bytes of alignment padding between instanceSize
508 // and ivarLayout on 64-bit archs, but no padding on 32-bit archs.
509 // This union is a way to model that.
510 union {
511 uint32_t instanceSize;
512 pint_t pad;
513 } instanceSize;
514 pint_t ivarLayout;
515 pint_t name;
516 pint_t baseMethods;
517 pint_t baseProtocols;
518 pint_t ivars;
519 pint_t weakIvarLayout;
520 pint_t baseProperties;
521 };
522 };
523
524 #define GET_FIELD(state, classAtom, field) \
525 ObjCData<A>::getPointerInContent(state, classAtom, offsetof(Content, field))
526 #define SET_FIELD(state, classAtom, field, valueAtom) \
527 ObjCData<A>::setPointerInContent(state, classAtom, offsetof(Content, field), valueAtom)
528
529 #define GET_RO_FIELD(state, classAtom, field) \
530 ObjCData<A>::getPointerInContent(state, getROData(state, classAtom), offsetof(ROContent, field))
531 #define SET_RO_FIELD(state, classROAtom, field, valueAtom) \
532 ObjCData<A>::setPointerInContent(state, getROData(state, classAtom), offsetof(ROContent, field), valueAtom)
533
534 template <typename A>
535 const ld::Atom* Class<A>::getMetaClass(ld::Internal& state, const ld::Atom* classAtom)
536 {
537 const ld::Atom* metaClassAtom = GET_FIELD(state, classAtom, isa);
538 assert(metaClassAtom != NULL);
539 return metaClassAtom;
540 }
541
542 template <typename A>
543 const ld::Atom* Class<A>::getROData(ld::Internal& state, const ld::Atom* classAtom)
544 {
545 const ld::Atom* classROAtom = GET_FIELD(state, classAtom, data);
546 assert(classROAtom != NULL);
547 return classROAtom;
548 }
549
550 template <typename A>
551 const ld::Atom* Class<A>::getInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom)
552 {
553 return GET_RO_FIELD(state, classAtom, baseMethods);
554 }
555
556 template <typename A>
557 const ld::Atom* Class<A>::getInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom)
558 {
559 return GET_RO_FIELD(state, classAtom, baseProtocols);
560 }
561
562 template <typename A>
563 const ld::Atom* Class<A>::getInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom)
564 {
565 return GET_RO_FIELD(state, classAtom, baseProperties);
566 }
567
568 template <typename A>
569 const ld::Atom* Class<A>::getClassMethodList(ld::Internal& state, const ld::Atom* classAtom)
570 {
571 return Class<A>::getInstanceMethodList(state, getMetaClass(state, classAtom));
572 }
573
574 template <typename A>
575 const ld::Atom* Class<A>::getClassPropertyList(ld::Internal& state, const ld::Atom* classAtom)
576 {
577 return Class<A>::getInstancePropertyList(state, getMetaClass(state, classAtom));
578 }
579
580 template <typename A>
581 const ld::Atom* Class<A>::setInstanceMethodList(ld::Internal& state, const ld::Atom* classAtom,
582 const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms)
583 {
584 // if the base class does not already have a method list, we need to create an overlay
585 if ( getInstanceMethodList(state, classAtom) == NULL ) {
586 const ld::Atom* oldROAtom = getROData(state, classAtom);
587 deadAtoms.insert(oldROAtom);
588 ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(oldROAtom);
589 //fprintf(stderr, "replace class RO atom %p with %p for method list in class atom %s\n", classROAtom, overlay, classAtom->name());
590 overlay->addMethodListFixup();
591 SET_FIELD(state, classAtom, data, overlay);
592 SET_RO_FIELD(state, classAtom, baseMethods, methodListAtom);
593 return overlay;
594 }
595 SET_RO_FIELD(state, classAtom, baseMethods, methodListAtom);
596 return NULL; // means classRO atom was not replaced
597 }
598
599 template <typename A>
600 const ld::Atom* Class<A>::setInstanceProtocolList(ld::Internal& state, const ld::Atom* classAtom,
601 const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms)
602 {
603 // if the base class does not already have a protocol list, we need to create an overlay
604 if ( getInstanceProtocolList(state, classAtom) == NULL ) {
605 const ld::Atom* oldROAtom = getROData(state, classAtom);
606 deadAtoms.insert(oldROAtom);
607 ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(oldROAtom);
608 //fprintf(stderr, "replace class RO atom %p with %p for protocol list in class atom %s\n", classROAtom, overlay, classAtom->name());
609 overlay->addProtocolListFixup();
610 SET_FIELD(state, classAtom, data, overlay);
611 SET_RO_FIELD(state, classAtom, baseProtocols, protocolListAtom);
612 return overlay;
613 }
614 //fprintf(stderr, "set class RO atom %p protocol list in class atom %s\n", classROAtom, classAtom->name());
615 SET_RO_FIELD(state, classAtom, baseProtocols, protocolListAtom);
616 return NULL; // means classRO atom was not replaced
617 }
618
619 template <typename A>
620 const ld::Atom* Class<A>::setClassProtocolList(ld::Internal& state, const ld::Atom* classAtom,
621 const ld::Atom* protocolListAtom, std::set<const ld::Atom*>& deadAtoms)
622 {
623 // meta class also points to same protocol list as class
624 const ld::Atom* metaClassAtom = getMetaClass(state, classAtom);
625 //fprintf(stderr, "setClassProtocolList(), classAtom=%p %s, metaClass=%p %s\n", classAtom, classAtom->name(), metaClassAtom, metaClassAtom->name());
626 return setInstanceProtocolList(state, metaClassAtom, protocolListAtom, deadAtoms);
627 }
628
629
630
631 template <typename A>
632 const ld::Atom* Class<A>::setInstancePropertyList(ld::Internal& state, const ld::Atom* classAtom,
633 const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms)
634 {
635 // if the base class does not already have a property list, we need to create an overlay
636 if ( getInstancePropertyList(state, classAtom) == NULL ) {
637 const ld::Atom* oldROAtom = getROData(state, classAtom);
638 deadAtoms.insert(oldROAtom);
639 ClassROOverlayAtom<A>* overlay = new ClassROOverlayAtom<A>(oldROAtom);
640 //fprintf(stderr, "replace class RO atom %p with %p for property list in class atom %s\n", classROAtom, overlay, classAtom->name());
641 overlay->addPropertyListFixup();
642 SET_FIELD(state, classAtom, data, overlay);
643 SET_RO_FIELD(state, classAtom, baseProperties, propertyListAtom);
644 return overlay;
645 }
646 SET_RO_FIELD(state, classAtom, baseProperties, propertyListAtom);
647 return NULL; // means classRO atom was not replaced
648 }
649
650 template <typename A>
651 const ld::Atom* Class<A>::setClassMethodList(ld::Internal& state, const ld::Atom* classAtom,
652 const ld::Atom* methodListAtom, std::set<const ld::Atom*>& deadAtoms)
653 {
654 // class methods is just instance methods of metaClass
655 return setInstanceMethodList(state, getMetaClass(state, classAtom), methodListAtom, deadAtoms);
656 }
657
658 template <typename A>
659 const ld::Atom* Class<A>::setClassPropertyList(ld::Internal& state, const ld::Atom* classAtom,
660 const ld::Atom* propertyListAtom, std::set<const ld::Atom*>& deadAtoms)
661 {
662 // class properties is just instance properties of metaClass
663 return setInstancePropertyList(state, getMetaClass(state, classAtom), propertyListAtom, deadAtoms);
664 }
665
666 #undef GET_FIELD
667 #undef SET_FIELD
668 #undef GET_RO_FIELD
669 #undef SET_RO_FIELD
670
671
672 template <typename P>
673 ld::Fixup::Kind pointerFixupKind();
674
675 template <>
676 ld::Fixup::Kind pointerFixupKind<Pointer32<BigEndian>>() {
677 return ld::Fixup::kindStoreTargetAddressBigEndian32;
678 }
679 template <>
680 ld::Fixup::Kind pointerFixupKind<Pointer64<BigEndian>>() {
681 return ld::Fixup::kindStoreTargetAddressBigEndian64;
682 }
683 template <>
684 ld::Fixup::Kind pointerFixupKind<Pointer32<LittleEndian>>() {
685 return ld::Fixup::kindStoreTargetAddressLittleEndian32;
686 }
687 template <>
688 ld::Fixup::Kind pointerFixupKind<Pointer64<LittleEndian>>() {
689 return ld::Fixup::kindStoreTargetAddressLittleEndian64;
690 }
691
692 template <typename A>
693 void ClassROOverlayAtom<A>::addFixupAtOffset(uint32_t offset)
694 {
695 const ld::Atom* targetAtom = this; // temporary
696 _fixups.push_back(ld::Fixup(offset, ld::Fixup::k1of1, pointerFixupKind<typename A::P>(), targetAtom));
697 }
698
699
700 template <typename A>
701 void ClassROOverlayAtom<A>::addMethodListFixup()
702 {
703 addFixupAtOffset(offsetof(typename Class<A>::ROContent, baseMethods));
704 }
705
706 template <typename A>
707 void ClassROOverlayAtom<A>::addProtocolListFixup()
708 {
709 addFixupAtOffset(offsetof(typename Class<A>::ROContent, baseProtocols));
710 }
711
712 template <typename A>
713 void ClassROOverlayAtom<A>::addPropertyListFixup()
714 {
715 addFixupAtOffset(offsetof(typename Class<A>::ROContent, baseProperties));
716 }
717
718
719
720
721 //
722 // Encapsulates merging of ObjC categories
723 //
724 template <typename A>
725 class OptimizeCategories {
726 public:
727 static void doit(const Options& opts, ld::Internal& state);
728 static bool hasInstanceMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
729 static bool hasClassMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
730 static bool hasProtocols(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
731 static bool hasInstanceProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
732 static bool hasClassProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories);
733
734 static unsigned int class_ro_baseMethods_offset();
735 private:
736 typedef typename A::P::uint_t pint_t;
737
738 };
739
740
741 template <typename A>
742 bool OptimizeCategories<A>::hasInstanceMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
743 {
744 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
745 const ld::Atom* categoryAtom = *it;
746 const ld::Atom* methodList = Category<A>::getInstanceMethods(state, categoryAtom);
747 if ( methodList != NULL ) {
748 if ( MethodList<A>::count(state, methodList) > 0 )
749 return true;
750 }
751 }
752 return false;
753 }
754
755
756 template <typename A>
757 bool OptimizeCategories<A>::hasClassMethods(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
758 {
759 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
760 const ld::Atom* categoryAtom = *it;
761 const ld::Atom* methodList = Category<A>::getClassMethods(state, categoryAtom);
762 if ( methodList != NULL ) {
763 if ( MethodList<A>::count(state, methodList) > 0 )
764 return true;
765 }
766 }
767 return false;
768 }
769
770 template <typename A>
771 bool OptimizeCategories<A>::hasProtocols(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
772 {
773 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
774 const ld::Atom* categoryAtom = *it;
775 const ld::Atom* protocolListAtom = Category<A>::getProtocols(state, categoryAtom);
776 if ( protocolListAtom != NULL ) {
777 if ( ProtocolList<A>::count(state, protocolListAtom) > 0 ) {
778 return true;
779 }
780 }
781 }
782 return false;
783 }
784
785
786 template <typename A>
787 bool OptimizeCategories<A>::hasInstanceProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
788 {
789 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
790 const ld::Atom* categoryAtom = *it;
791 const ld::Atom* propertyListAtom = Category<A>::getInstanceProperties(state, categoryAtom);
792 if ( propertyListAtom != NULL ) {
793 if ( PropertyList<A>::count(state, propertyListAtom) > 0 )
794 return true;
795 }
796 }
797 return false;
798 }
799
800
801 template <typename A>
802 bool OptimizeCategories<A>::hasClassProperties(ld::Internal& state, const std::vector<const ld::Atom*>* categories)
803 {
804 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
805 const ld::Atom* categoryAtom = *it;
806 const ld::Atom* propertyListAtom = Category<A>::getClassProperties(state, categoryAtom);
807 if ( propertyListAtom != NULL ) {
808 if ( PropertyList<A>::count(state, propertyListAtom) > 0 )
809 return true;
810 }
811 }
812 return false;
813 }
814
815
816 static const ld::Atom* fixClassAliases(const ld::Atom* classAtom)
817 {
818 if ( (classAtom->size() != 0) || (classAtom->definition() == ld::Atom::definitionProxy) )
819 return classAtom;
820
821 for (ld::Fixup::iterator fit=classAtom->fixupsBegin(); fit != classAtom->fixupsEnd(); ++fit) {
822 if ( fit->kind == ld::Fixup::kindNoneFollowOn ) {
823 assert(fit->offsetInAtom == 0);
824 assert(fit->binding == ld::Fixup::bindingDirectlyBound);
825 return fit->u.target;
826 }
827 }
828
829 return classAtom;
830 }
831
832 //
833 // Helper for std::remove_if
834 //
835 class OptimizedAway {
836 public:
837 OptimizedAway(const std::set<const ld::Atom*>& oa) : _dead(oa) {}
838 bool operator()(const ld::Atom* atom) const {
839 return ( _dead.count(atom) != 0 );
840 }
841 private:
842 const std::set<const ld::Atom*>& _dead;
843 };
844
845 struct AtomSorter
846 {
847 bool operator()(const Atom* left, const Atom* right)
848 {
849 // sort by file ordinal, then object address, then zero size, then symbol name
850 // only file based atoms are supported (file() != NULL)
851 if (left==right) return false;
852 const File *leftf = left->file();
853 const File *rightf = right->file();
854
855 if (leftf == rightf) {
856 if (left->objectAddress() != right->objectAddress()) {
857 return left->objectAddress() < right->objectAddress();
858 } else {
859 // for atoms in the same file with the same address, zero sized
860 // atoms must sort before nonzero sized atoms
861 if ((left->size() == 0 && right->size() > 0) || (left->size() > 0 && right->size() == 0))
862 return left->size() < right->size();
863 return strcmp(left->name(), right->name());
864 }
865 }
866 return (leftf->ordinal() < rightf->ordinal());
867 }
868 };
869
870 static void sortAtomVector(std::vector<const Atom*> &atoms) {
871 std::sort(atoms.begin(), atoms.end(), AtomSorter());
872 }
873
874
875 template <typename A>
876 void OptimizeCategories<A>::doit(const Options& opts, ld::Internal& state)
877 {
878 // first find all categories referenced by __objc_nlcatlist section
879 std::set<const ld::Atom*> nlcatListAtoms;
880 for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
881 ld::Internal::FinalSection* sect = *sit;
882 if ( (strcmp(sect->sectionName(), "__objc_nlcatlist") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) ) {
883 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
884 const ld::Atom* categoryListElementAtom = *ait;
885 for (unsigned int offset=0; offset < categoryListElementAtom->size(); offset += sizeof(pint_t)) {
886 const ld::Atom* categoryAtom = ObjCData<A>::getPointerInContent(state, categoryListElementAtom, offset);
887 //fprintf(stderr, "offset=%d, cat=%p %s\n", offset, categoryAtom, categoryAtom->name());
888 assert(categoryAtom != NULL);
889 nlcatListAtoms.insert(categoryAtom);
890 }
891 }
892 }
893 }
894
895 // build map of all classes in this image that have categories on them
896 typedef std::map<const ld::Atom*, std::vector<const ld::Atom*>*> CatMap;
897 CatMap classToCategories;
898 std::vector<const ld::Atom*> classOrder;
899 std::set<const ld::Atom*> deadAtoms;
900 ld::Internal::FinalSection* methodListSection = NULL;
901 for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
902 ld::Internal::FinalSection* sect = *sit;
903 if ( sect->type() == ld::Section::typeObjC2CategoryList ) {
904 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
905 const ld::Atom* categoryListElementAtom = *ait;
906 bool hasAddend;
907 const ld::Atom* categoryAtom = ObjCData<A>::getPointerInContent(state, categoryListElementAtom, 0, &hasAddend);
908 if ( hasAddend || (categoryAtom->symbolTableInclusion() == ld::Atom::symbolTableNotIn)) {
909 //<rdar://problem/8309530> gcc-4.0 uses 'L' labels on categories which disables this optimization
910 //warning("__objc_catlist element does not point to start of category");
911 continue;
912 }
913 assert(categoryAtom != NULL);
914 assert(categoryAtom->size() >= Category<A>::size());
915 // ignore categories also in __objc_nlcatlist
916 if ( nlcatListAtoms.count(categoryAtom) != 0 )
917 continue;
918 const ld::Atom* categoryOnClassAtom = fixClassAliases(Category<A>::getClass(state, categoryAtom, hasAddend));
919 assert(categoryOnClassAtom != NULL);
920 // only look at classes defined in this image
921 if ( categoryOnClassAtom->definition() != ld::Atom::definitionProxy ) {
922 // <rdar://problem/16107696> for now, back off optimization on new style classes
923 if ( hasAddend != 0 )
924 continue;
925 // <rdar://problem/17249777> don't apply categories to swift classes
926 if ( categoryOnClassAtom->hasFixupsOfKind(ld::Fixup::kindNoneGroupSubordinate) )
927 continue;
928
929 CatMap::iterator pos = classToCategories.find(categoryOnClassAtom);
930 if ( pos == classToCategories.end() ) {
931 classToCategories[categoryOnClassAtom] = new std::vector<const ld::Atom*>();
932 classOrder.push_back(categoryOnClassAtom);
933 }
934 classToCategories[categoryOnClassAtom]->push_back(categoryAtom);
935 // mark category atom and catlist atom as dead
936 deadAtoms.insert(categoryAtom);
937 deadAtoms.insert(categoryListElementAtom);
938 }
939 }
940 }
941 // record method list section
942 if ( (strcmp(sect->sectionName(), "__objc_const") == 0) && (strncmp(sect->segmentName(), "__DATA", 6) == 0) )
943 methodListSection = sect;
944 }
945
946 // if found some categories
947 if ( classToCategories.size() != 0 ) {
948 assert(methodListSection != NULL);
949 sortAtomVector(classOrder);
950 // alter each class definition to have new method list which includes all category methods
951 for (std::vector<const ld::Atom*>::iterator it = classOrder.begin(); it != classOrder.end(); it++) {
952 const ld::Atom* classAtom = *it;
953 const std::vector<const ld::Atom*>* categories = classToCategories[classAtom];
954 assert(categories->size() != 0);
955 // if any category adds instance methods, generate new merged method list, and replace
956 if ( OptimizeCategories<A>::hasInstanceMethods(state, categories) ) {
957 const ld::Atom* baseInstanceMethodListAtom = Class<A>::getInstanceMethodList(state, classAtom);
958 const ld::Atom* newInstanceMethodListAtom = new MethodListAtom<A>(state, baseInstanceMethodListAtom, false, categories, deadAtoms);
959 const ld::Atom* newClassRO = Class<A>::setInstanceMethodList(state, classAtom, newInstanceMethodListAtom, deadAtoms);
960 // add new method list to final sections
961 methodListSection->atoms.push_back(newInstanceMethodListAtom);
962 state.atomToSection[newInstanceMethodListAtom] = methodListSection;
963 if ( newClassRO != NULL ) {
964 assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0);
965 methodListSection->atoms.push_back(newClassRO);
966 state.atomToSection[newClassRO] = methodListSection;
967 }
968 }
969 // if any category adds class methods, generate new merged method list, and replace
970 if ( OptimizeCategories<A>::hasClassMethods(state, categories) ) {
971 const ld::Atom* baseClassMethodListAtom = Class<A>::getClassMethodList(state, classAtom);
972 const ld::Atom* newClassMethodListAtom = new MethodListAtom<A>(state, baseClassMethodListAtom, true, categories, deadAtoms);
973 const ld::Atom* newClassRO = Class<A>::setClassMethodList(state, classAtom, newClassMethodListAtom, deadAtoms);
974 // add new method list to final sections
975 methodListSection->atoms.push_back(newClassMethodListAtom);
976 state.atomToSection[newClassMethodListAtom] = methodListSection;
977 if ( newClassRO != NULL ) {
978 assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0);
979 methodListSection->atoms.push_back(newClassRO);
980 state.atomToSection[newClassRO] = methodListSection;
981 }
982 }
983 // if any category adds protocols, generate new merged protocol list, and replace
984 if ( OptimizeCategories<A>::hasProtocols(state, categories) ) {
985 const ld::Atom* baseProtocolListAtom = Class<A>::getInstanceProtocolList(state, classAtom);
986 const ld::Atom* newProtocolListAtom = new ProtocolListAtom<A>(state, baseProtocolListAtom, categories, deadAtoms);
987 const ld::Atom* newClassRO = Class<A>::setInstanceProtocolList(state, classAtom, newProtocolListAtom, deadAtoms);
988 const ld::Atom* newMetaClassRO = Class<A>::setClassProtocolList(state, classAtom, newProtocolListAtom, deadAtoms);
989 // add new protocol list to final sections
990 methodListSection->atoms.push_back(newProtocolListAtom);
991 state.atomToSection[newProtocolListAtom] = methodListSection;
992 if ( newClassRO != NULL ) {
993 assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0);
994 methodListSection->atoms.push_back(newClassRO);
995 state.atomToSection[newClassRO] = methodListSection;
996 }
997 if ( newMetaClassRO != NULL ) {
998 assert(strcmp(newMetaClassRO->section().sectionName(), "__objc_const") == 0);
999 methodListSection->atoms.push_back(newMetaClassRO);
1000 state.atomToSection[newMetaClassRO] = methodListSection;
1001 }
1002 }
1003 // if any category adds instance properties, generate new merged property list, and replace
1004 if ( OptimizeCategories<A>::hasInstanceProperties(state, categories) ) {
1005 const ld::Atom* basePropertyListAtom = Class<A>::getInstancePropertyList(state, classAtom);
1006 const ld::Atom* newPropertyListAtom = new PropertyListAtom<A>(state, basePropertyListAtom, categories, deadAtoms, PropertyListAtom<A>::PropertyKind::InstanceProperties);
1007 const ld::Atom* newClassRO = Class<A>::setInstancePropertyList(state, classAtom, newPropertyListAtom, deadAtoms);
1008 // add new property list to final sections
1009 methodListSection->atoms.push_back(newPropertyListAtom);
1010 state.atomToSection[newPropertyListAtom] = methodListSection;
1011 if ( newClassRO != NULL ) {
1012 assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0);
1013 methodListSection->atoms.push_back(newClassRO);
1014 state.atomToSection[newClassRO] = methodListSection;
1015 }
1016 }
1017 // if any category adds class properties, generate new merged property list, and replace
1018 if ( OptimizeCategories<A>::hasClassProperties(state, categories) ) {
1019 const ld::Atom* basePropertyListAtom = Class<A>::getClassPropertyList(state, classAtom);
1020 const ld::Atom* newPropertyListAtom = new PropertyListAtom<A>(state, basePropertyListAtom, categories, deadAtoms, PropertyListAtom<A>::PropertyKind::ClassProperties);
1021 const ld::Atom* newClassRO = Class<A>::setClassPropertyList(state, classAtom, newPropertyListAtom, deadAtoms);
1022 // add new property list to final sections
1023 methodListSection->atoms.push_back(newPropertyListAtom);
1024 state.atomToSection[newPropertyListAtom] = methodListSection;
1025 if ( newClassRO != NULL ) {
1026 assert(strcmp(newClassRO->section().sectionName(), "__objc_const") == 0);
1027 methodListSection->atoms.push_back(newClassRO);
1028 state.atomToSection[newClassRO] = methodListSection;
1029 }
1030 }
1031 }
1032
1033 // remove dead atoms
1034 for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
1035 ld::Internal::FinalSection* sect = *sit;
1036 sect->atoms.erase(std::remove_if(sect->atoms.begin(), sect->atoms.end(), OptimizedAway(deadAtoms)), sect->atoms.end());
1037 }
1038 }
1039 }
1040
1041
1042
1043 template <typename A>
1044 MethodListAtom<A>::MethodListAtom(ld::Internal& state, const ld::Atom* baseMethodList, bool meta,
1045 const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms)
1046 : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
1047 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
1048 symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _methodCount(0)
1049 {
1050 unsigned int fixupCount = 0;
1051 std::set<const ld::Atom*> baseMethodListMethodNameAtoms;
1052 // if base class has method list, then associate new method list with file defining class
1053 if ( baseMethodList != NULL ) {
1054 _file = baseMethodList->file();
1055 // calculate total size of merge method lists
1056 _methodCount = MethodList<A>::count(state, baseMethodList);
1057 deadAtoms.insert(baseMethodList);
1058 fixupCount = baseMethodList->fixupsEnd() - baseMethodList->fixupsBegin();
1059 for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) {
1060 if ( (fit->offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) {
1061 assert(fit->binding == ld::Fixup::bindingsIndirectlyBound && "malformed method list");
1062 const ld::Atom* target = state.indirectBindingTable[fit->u.bindingIndex];
1063 assert(target->contentType() == ld::Atom::typeCString && "malformed method list");
1064 baseMethodListMethodNameAtoms.insert(target);
1065 }
1066 }
1067 }
1068 for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) {
1069 const ld::Atom* categoryMethodListAtom;
1070 if ( meta )
1071 categoryMethodListAtom = Category<A>::getClassMethods(state, *ait);
1072 else
1073 categoryMethodListAtom = Category<A>::getInstanceMethods(state, *ait);
1074 if ( categoryMethodListAtom != NULL ) {
1075 _methodCount += MethodList<A>::count(state, categoryMethodListAtom);
1076 fixupCount += (categoryMethodListAtom->fixupsEnd() - categoryMethodListAtom->fixupsBegin());
1077 deadAtoms.insert(categoryMethodListAtom);
1078 // if base class did not have method list, associate new method list with file the defined category
1079 if ( _file == NULL )
1080 _file = categoryMethodListAtom->file();
1081 }
1082 }
1083 //if ( baseMethodList != NULL )
1084 // fprintf(stderr, "total merged method count=%u for baseMethodList=%s\n", _methodCount, baseMethodList->name());
1085 //else
1086 // fprintf(stderr, "total merged method count=%u\n", _methodCount);
1087 //fprintf(stderr, "total merged fixup count=%u\n", fixupCount);
1088
1089 // copy fixups and adjust offsets (in reverse order to simulator objc runtime)
1090 _fixups.reserve(fixupCount);
1091 uint32_t slide = 0;
1092 std::set<const ld::Atom*> categoryMethodNameAtoms;
1093 for (std::vector<const ld::Atom*>::const_reverse_iterator rit=categories->rbegin(); rit != categories->rend(); ++rit) {
1094 const ld::Atom* categoryMethodListAtom;
1095 if ( meta )
1096 categoryMethodListAtom = Category<A>::getClassMethods(state, *rit);
1097 else
1098 categoryMethodListAtom = Category<A>::getInstanceMethods(state, *rit);
1099 if ( categoryMethodListAtom != NULL ) {
1100 for (ld::Fixup::iterator fit=categoryMethodListAtom->fixupsBegin(); fit != categoryMethodListAtom->fixupsEnd(); ++fit) {
1101 ld::Fixup fixup = *fit;
1102 fixup.offsetInAtom += slide;
1103 _fixups.push_back(fixup);
1104 if ( (fixup.offsetInAtom - 8) % (3*sizeof(pint_t)) == 0 ) {
1105 // <rdar://problem/8642343> warning when a method is overridden in a category in the same link unit
1106 assert(fixup.binding == ld::Fixup::bindingsIndirectlyBound && "malformed category method list");
1107 const ld::Atom* target = state.indirectBindingTable[fixup.u.bindingIndex];
1108 assert(target->contentType() == ld::Atom::typeCString && "malformed method list");
1109 // this objc pass happens after cstrings are coalesced, so we can just compare the atom addres instead of its content
1110 if ( baseMethodListMethodNameAtoms.count(target) != 0 ) {
1111 warning("%s method '%s' in category from %s overrides method from class in %s",
1112 (meta ? "meta" : "instance"), target->rawContentPointer(),
1113 categoryMethodListAtom->safeFilePath(), baseMethodList->safeFilePath() );
1114 }
1115 if ( categoryMethodNameAtoms.count(target) != 0 ) {
1116 warning("%s method '%s' in category from %s conflicts with same method from another category",
1117 (meta ? "meta" : "instance"), target->rawContentPointer(),
1118 categoryMethodListAtom->safeFilePath());
1119 }
1120 categoryMethodNameAtoms.insert(target);
1121 }
1122 }
1123 slide += 3*sizeof(pint_t) * MethodList<A>::count(state, categoryMethodListAtom);
1124 }
1125 }
1126 // add method list from base class last
1127 if ( baseMethodList != NULL ) {
1128 for (ld::Fixup::iterator fit=baseMethodList->fixupsBegin(); fit != baseMethodList->fixupsEnd(); ++fit) {
1129 ld::Fixup fixup = *fit;
1130 fixup.offsetInAtom += slide;
1131 _fixups.push_back(fixup);
1132 }
1133 }
1134 }
1135
1136
1137 template <typename A>
1138 ProtocolListAtom<A>::ProtocolListAtom(ld::Internal& state, const ld::Atom* baseProtocolList,
1139 const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms)
1140 : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
1141 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
1142 symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _protocolCount(0)
1143 {
1144 unsigned int fixupCount = 0;
1145 if ( baseProtocolList != NULL ) {
1146 // if base class has protocol list, then associate new protocol list with file defining class
1147 _file = baseProtocolList->file();
1148 // calculate total size of merged protocol list
1149 _protocolCount = ProtocolList<A>::count(state, baseProtocolList);
1150 deadAtoms.insert(baseProtocolList);
1151 fixupCount = baseProtocolList->fixupsEnd() - baseProtocolList->fixupsBegin();
1152 }
1153 for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) {
1154 const ld::Atom* categoryProtocolListAtom = Category<A>::getProtocols(state, *ait);
1155 if ( categoryProtocolListAtom != NULL ) {
1156 _protocolCount += ProtocolList<A>::count(state, categoryProtocolListAtom);
1157 fixupCount += (categoryProtocolListAtom->fixupsEnd() - categoryProtocolListAtom->fixupsBegin());
1158 deadAtoms.insert(categoryProtocolListAtom);
1159 // if base class did not have protocol list, associate new protocol list with file the defined category
1160 if ( _file == NULL )
1161 _file = categoryProtocolListAtom->file();
1162 }
1163 }
1164 //fprintf(stderr, "total merged protocol count=%u\n", _protocolCount);
1165 //fprintf(stderr, "total merged fixup count=%u\n", fixupCount);
1166
1167 // copy fixups and adjust offsets
1168 _fixups.reserve(fixupCount);
1169 uint32_t slide = 0;
1170 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
1171 const ld::Atom* categoryProtocolListAtom = Category<A>::getProtocols(state, *it);
1172 if ( categoryProtocolListAtom != NULL ) {
1173 for (ld::Fixup::iterator fit=categoryProtocolListAtom->fixupsBegin(); fit != categoryProtocolListAtom->fixupsEnd(); ++fit) {
1174 ld::Fixup fixup = *fit;
1175 fixup.offsetInAtom += slide;
1176 _fixups.push_back(fixup);
1177 //if ( fixup.binding == ld::Fixup::bindingDirectlyBound )
1178 // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name());
1179 }
1180 slide += sizeof(pint_t) * ProtocolList<A>::count(state, categoryProtocolListAtom);
1181 }
1182 }
1183 // add method list from base class last
1184 if ( baseProtocolList != NULL ) {
1185 for (ld::Fixup::iterator fit=baseProtocolList->fixupsBegin(); fit != baseProtocolList->fixupsEnd(); ++fit) {
1186 ld::Fixup fixup = *fit;
1187 fixup.offsetInAtom += slide;
1188 _fixups.push_back(fixup);
1189 }
1190 }
1191 }
1192
1193
1194 template <typename A>
1195 PropertyListAtom<A>::PropertyListAtom(ld::Internal& state, const ld::Atom* basePropertyList,
1196 const std::vector<const ld::Atom*>* categories, std::set<const ld::Atom*>& deadAtoms, PropertyKind kind)
1197 : ld::Atom(_s_section, ld::Atom::definitionRegular, ld::Atom::combineNever,
1198 ld::Atom::scopeLinkageUnit, ld::Atom::typeUnclassified,
1199 symbolTableNotIn, false, false, false, ld::Atom::Alignment(3)), _file(NULL), _propertyCount(0)
1200 {
1201 unsigned int fixupCount = 0;
1202 if ( basePropertyList != NULL ) {
1203 // if base class has property list, then associate new property list with file defining class
1204 _file = basePropertyList->file();
1205 // calculate total size of merged property list
1206 _propertyCount = PropertyList<A>::count(state, basePropertyList);
1207 deadAtoms.insert(basePropertyList);
1208 fixupCount = basePropertyList->fixupsEnd() - basePropertyList->fixupsBegin();
1209 }
1210 for (std::vector<const ld::Atom*>::const_iterator ait=categories->begin(); ait != categories->end(); ++ait) {
1211 const ld::Atom* categoryPropertyListAtom = kind == PropertyKind::ClassProperties ? Category<A>::getClassProperties(state, *ait) : Category<A>::getInstanceProperties(state, *ait);
1212 if ( categoryPropertyListAtom != NULL ) {
1213 _propertyCount += PropertyList<A>::count(state, categoryPropertyListAtom);
1214 fixupCount += (categoryPropertyListAtom->fixupsEnd() - categoryPropertyListAtom->fixupsBegin());
1215 deadAtoms.insert(categoryPropertyListAtom);
1216 // if base class did not have property list, associate new property list with file the defined category
1217 if ( _file == NULL )
1218 _file = categoryPropertyListAtom->file();
1219 }
1220 }
1221 //fprintf(stderr, "total merged property count=%u\n", _propertyCount);
1222 //fprintf(stderr, "total merged fixup count=%u\n", fixupCount);
1223
1224 // copy fixups and adjust offsets
1225 _fixups.reserve(fixupCount);
1226 uint32_t slide = 0;
1227 for (std::vector<const ld::Atom*>::const_iterator it=categories->begin(); it != categories->end(); ++it) {
1228 const ld::Atom* categoryPropertyListAtom = kind == PropertyKind::ClassProperties ? Category<A>::getClassProperties(state, *it) : Category<A>::getInstanceProperties(state, *it);
1229 if ( categoryPropertyListAtom != NULL ) {
1230 for (ld::Fixup::iterator fit=categoryPropertyListAtom->fixupsBegin(); fit != categoryPropertyListAtom->fixupsEnd(); ++fit) {
1231 ld::Fixup fixup = *fit;
1232 fixup.offsetInAtom += slide;
1233 _fixups.push_back(fixup);
1234 //fprintf(stderr, "offset=0x%08X, binding=%d\n", fixup.offsetInAtom, fixup.binding);
1235 //if ( fixup.binding == ld::Fixup::bindingDirectlyBound )
1236 // fprintf(stderr, "offset=0x%08X, name=%s\n", fixup.offsetInAtom, fixup.u.target->name());
1237 //else if ( fixup.binding == ld::Fixup::bindingsIndirectlyBound )
1238 // fprintf(stderr, "offset=0x%08X, indirect index=%u, name=%s\n", fixup.offsetInAtom, fixup.u.bindingIndex,
1239 // (char*)(state.indirectBindingTable[fixup.u.bindingIndex]->rawContentPointer()));
1240 }
1241 slide += 2*sizeof(pint_t) * PropertyList<A>::count(state, categoryPropertyListAtom);
1242 }
1243 }
1244 // add method list from base class last
1245 if ( basePropertyList != NULL ) {
1246 for (ld::Fixup::iterator fit=basePropertyList->fixupsBegin(); fit != basePropertyList->fixupsEnd(); ++fit) {
1247 ld::Fixup fixup = *fit;
1248 fixup.offsetInAtom += slide;
1249 _fixups.push_back(fixup);
1250 }
1251 }
1252 }
1253
1254
1255 template <typename A>
1256 void scanCategories(ld::Internal& state,
1257 bool& haveCategoriesWithNonNullClassProperties,
1258 bool& haveCategoriesWithoutClassPropertyStorage)
1259 {
1260 haveCategoriesWithNonNullClassProperties = false;
1261 haveCategoriesWithoutClassPropertyStorage = false;
1262
1263 for (std::vector<ld::Internal::FinalSection*>::iterator sit=state.sections.begin(); sit != state.sections.end(); ++sit) {
1264 ld::Internal::FinalSection* sect = *sit;
1265 if ( sect->type() == ld::Section::typeObjC2CategoryList ) {
1266 for (std::vector<const ld::Atom*>::iterator ait=sect->atoms.begin(); ait != sect->atoms.end(); ++ait) {
1267 const ld::Atom* categoryListElementAtom = *ait;
1268 bool hasAddend;
1269 const ld::Atom* categoryAtom = ObjCData<A>::getPointerInContent(state, categoryListElementAtom, 0, &hasAddend);
1270
1271 if (Category<A>::getClassProperties(state, categoryAtom)) {
1272 haveCategoriesWithNonNullClassProperties = true;
1273 // fprintf(stderr, "category in file %s has non-null class properties\n", categoryAtom->safeFilePath());
1274 }
1275
1276 if (!categoryAtom->file()->objcHasCategoryClassPropertiesField()) {
1277 haveCategoriesWithoutClassPropertyStorage = true;
1278 // fprintf(stderr, "category in file %s has no storage for class properties\n", categoryAtom->safeFilePath());
1279 }
1280 }
1281 }
1282 }
1283 }
1284
1285
1286 template <typename A, bool isObjC2>
1287 void doPass(const Options& opts, ld::Internal& state)
1288 {
1289 // Do nothing if the output has no ObjC content.
1290 if ( state.objcObjectConstraint == ld::File::objcConstraintNone ) {
1291 return;
1292 }
1293
1294 // verify dylibs are GC compatible with object files
1295 if ( state.objcObjectConstraint != state.objcDylibConstraint ) {
1296 if ( (state.objcDylibConstraint == ld::File::objcConstraintRetainRelease)
1297 && (state.objcObjectConstraint == ld::File::objcConstraintGC) ) {
1298 throw "Linked dylibs built for retain/release but object files built for GC-only";
1299 }
1300 else if ( (state.objcDylibConstraint == ld::File::objcConstraintGC)
1301 && (state.objcObjectConstraint == ld::File::objcConstraintRetainRelease) ) {
1302 throw "Linked dylibs built for GC-only but object files built for retain/release";
1303 }
1304 }
1305
1306 if ( opts.objcCategoryMerging() ) {
1307 // optimize classes defined in this linkage unit by merging in categories also in this linkage unit
1308 OptimizeCategories<A>::doit(opts, state);
1309 }
1310
1311 // Search for surviving categories that have a non-null class properties field.
1312 // Search for surviving categories that do not have storage for the class properties field.
1313 bool haveCategoriesWithNonNullClassProperties;
1314 bool haveCategoriesWithoutClassPropertyStorage;
1315 scanCategories<A>(state, haveCategoriesWithNonNullClassProperties, haveCategoriesWithoutClassPropertyStorage);
1316
1317 // Complain about mismatched category ABI.
1318 // These can't be combined into a single linkage unit because there is only one size indicator for all categories in the file.
1319 // If there is a mismatch then we don't set the HasCategoryClassProperties bit in the output file,
1320 // which has at runtime causes any class property metadata that was present to be ignored.
1321 if (haveCategoriesWithNonNullClassProperties && haveCategoriesWithoutClassPropertyStorage) {
1322 warning("Some object files have incompatible Objective-C category definitions. Some category metadata may be lost. All files containing Objective-C categories should be built using the same compiler.");
1323 }
1324
1325 // add image info atom
1326 // The HasCategoryClassProperties bit is set as often as possible.
1327 state.addAtom(*new ObjCImageInfoAtom<A>(state.objcObjectConstraint, opts.objcGcCompaction(), isObjC2,
1328 !haveCategoriesWithoutClassPropertyStorage, state.swiftVersion));
1329 }
1330
1331
1332 void doPass(const Options& opts, ld::Internal& state)
1333 {
1334 switch ( opts.architecture() ) {
1335 #if SUPPORT_ARCH_x86_64
1336 case CPU_TYPE_X86_64:
1337 doPass<x86_64, true>(opts, state);
1338 break;
1339 #endif
1340 #if SUPPORT_ARCH_i386
1341 case CPU_TYPE_I386:
1342 if (opts.objCABIVersion2POverride()) {
1343 doPass<x86, true>(opts, state);
1344 } else {
1345 doPass<x86, false>(opts, state);
1346 }
1347 break;
1348 #endif
1349 #if SUPPORT_ARCH_arm_any
1350 case CPU_TYPE_ARM:
1351 doPass<arm, true>(opts, state);
1352 break;
1353 #endif
1354 #if SUPPORT_ARCH_arm64
1355 case CPU_TYPE_ARM64:
1356 doPass<arm64, true>(opts, state);
1357 break;
1358 #endif
1359 default:
1360 assert(0 && "unknown objc arch");
1361 }
1362 }
1363
1364
1365 } // namespace objc
1366 } // namespace passes
1367 } // namespace ld