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