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