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