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