1/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
2 *
3 * Copyright (c) 2008-2010 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#include "MachOLayout.hpp"
26#include <iterator>
27#include <deque>
28
29// iterate an entsize-based list
30// typedef entsize_iterator< A, type_t<A>, type_list_t<A> > type_iterator;
31template <typename A, typename T, typename Tlist>
32struct entsize_iterator {
33    uint32_t entsize;
34    uint32_t index;  // keeping track of this saves a divide in operator-
35    T* current;
36
37    typedef std::random_access_iterator_tag iterator_category;
38    typedef T value_type;
39    typedef ptrdiff_t difference_type;
40    typedef T* pointer;
41    typedef T& reference;
42
43    entsize_iterator() { }
44
45    entsize_iterator(const Tlist& list, uint32_t start = 0)
46        : entsize(list.getEntsize()), index(start), current(&list.get(start))
47    { }
48
49    const entsize_iterator<A,T,Tlist>& operator += (ptrdiff_t count) {
50        current = (T*)((uint8_t *)current + count*entsize);
51        index += count;
52        return *this;
53    }
54    const entsize_iterator<A,T,Tlist>& operator -= (ptrdiff_t count) {
55        current = (T*)((uint8_t *)current - count*entsize);
56        index -= count;
57        return *this;
58    }
59    const entsize_iterator<A,T,Tlist> operator + (ptrdiff_t count) const {
60        return entsize_iterator(*this) += count;
61    }
62    const entsize_iterator<A,T,Tlist> operator - (ptrdiff_t count) const {
63        return entsize_iterator(*this) -= count;
64    }
65
66    entsize_iterator<A,T,Tlist>& operator ++ () { *this += 1; return *this; }
67    entsize_iterator<A,T,Tlist>& operator -- () { *this -= 1; return *this; }
68    entsize_iterator<A,T,Tlist> operator ++ (int) {
69        entsize_iterator<A,T,Tlist> result(*this); *this += 1; return result;
70    }
71    entsize_iterator<A,T,Tlist> operator -- (int) {
72        entsize_iterator<A,T,Tlist> result(*this); *this -= 1; return result;
73    }
74
75    ptrdiff_t operator - (const entsize_iterator<A,T,Tlist>& rhs) const {
76        return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
77    }
78
79    T& operator * () { return *current; }
80    T& operator * () const { return *current; }
81    T& operator -> () { return *current; }
82    const T& operator -> () const { return *current; }
83
84    operator T& () const { return *current; }
85
86    bool operator == (const entsize_iterator<A,T,Tlist>& rhs) {
87        return this->current == rhs.current;
88    }
89    bool operator != (const entsize_iterator<A,T,Tlist>& rhs) {
90        return this->current != rhs.current;
91    }
92
93    bool operator < (const entsize_iterator<A,T,Tlist>& rhs) {
94        return this->current < rhs.current;
95    }
96    bool operator > (const entsize_iterator<A,T,Tlist>& rhs) {
97        return this->current > rhs.current;
98    }
99
100
101    static void overwrite(entsize_iterator<A,T,Tlist>& dst, const Tlist* srcList)
102    {
103        entsize_iterator<A,T,Tlist> src;
104        uint32_t ee = srcList->getEntsize();
105        for (src = srcList->begin(); src != srcList->end(); ++src) {
106            memcpy(&*dst, &*src, ee);
107            ++dst;
108        }
109    }
110};
111
112template <typename A>
113class objc_header_info_t {
114
115    typedef typename A::P P;
116    typedef typename A::P::uint_t pint_t;
117
118    pint_t next;   // objc_header_info *
119    pint_t mhdr;   // mach_header or mach_header_64
120    pint_t info;   // objc_image_info *
121    pint_t fname;  // const char *
122    bool loaded;
123    bool inSharedCache;
124    bool allClassesRealized;
125
126public:
127    objc_header_info_t(SharedCache<A>* cache, const macho_header<P>* mh)
128        : next(0),
129          mhdr(0),
130          info(0),
131          fname(0),
132          loaded(0),
133          allClassesRealized(0)
134    {
135        A::P::setP(mhdr, cache->VMAddressForMappedAddress(mh));
136        const macho_section<P>* sect = mh->getSection("__DATA", "__objc_imageinfo");
137        if (sect) A::P::setP(info, sect->addr());
138
139        // can't set fname because dyld sometimes edits it
140    }
141
142	void addPointers(std::vector<void*>& pointersToAdd) {
143        pointersToAdd.push_back(&mhdr);
144        if (info) pointersToAdd.push_back(&info);
145    }
146
147    uint64_t header_vmaddr() const { return mhdr; }
148};
149
150template <typename A> class objc_method_list_t;  // forward reference
151
152template <typename A>
153class objc_method_t {
154    typename A::P::uint_t name;   // SEL
155    typename A::P::uint_t types;  // const char *
156    typename A::P::uint_t imp;    // IMP
157	friend class objc_method_list_t<A>;
158public:
159    typename A::P::uint_t getName() const { return A::P::getP(name); }
160    void setName(typename A::P::uint_t newName) { A::P::setP(name, newName); }
161
162    struct SortBySELAddress :
163        public std::binary_function<const objc_method_t<A>&,
164                                    const objc_method_t<A>&, bool>
165    {
166        bool operator() (const objc_method_t<A>& lhs,
167                         const objc_method_t<A>& rhs)
168        {
169            return lhs.getName() < rhs.getName();
170        }
171    };
172};
173
174template <typename A>
175class objc_method_list_t {
176    uint32_t entsize;
177    uint32_t count;
178    objc_method_t<A> first;
179
180    // use newMethodList instead
181    void* operator new (size_t) { return NULL; }
182    void* operator new (size_t, void* buf) { return buf; }
183
184public:
185
186    typedef entsize_iterator< A, objc_method_t<A>, objc_method_list_t<A> > method_iterator;
187
188    uint32_t getCount() const { return A::P::E::get32(count); }
189
190	uint32_t getEntsize() const {return A::P::E::get32(entsize)&~(uint32_t)3;}
191
192    objc_method_t<A>& get(uint32_t i) const { return *(objc_method_t<A> *)((uint8_t *)&first + i * getEntsize()); }
193
194    uint32_t byteSize() const {
195        return byteSizeForCount(getCount(), getEntsize());
196    }
197
198    static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<A>)) {
199        return sizeof(objc_method_list_t<A>) - sizeof(objc_method_t<A>) + c*e;
200    }
201
202    method_iterator begin() { return method_iterator(*this, 0); }
203    method_iterator end() { return method_iterator(*this, getCount()); }
204    const method_iterator begin() const { return method_iterator(*this, 0); }
205    const method_iterator end() const { return method_iterator(*this, getCount()); }
206
207    void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); }
208
209	void getPointers(std::set<void*>& pointersToRemove) {
210		for(method_iterator it = begin(); it != end(); ++it) {
211			objc_method_t<A>& entry = *it;
212			pointersToRemove.insert(&(entry.name));
213			pointersToRemove.insert(&(entry.types));
214			pointersToRemove.insert(&(entry.imp));
215		}
216	}
217
218	static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
219		objc_method_list_t<A>* mlist = (objc_method_list_t<A>*)methodList;
220		for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
221			objc_method_t<A>& entry = *it;
222			pointersToAdd.push_back(&(entry.name));
223			pointersToAdd.push_back(&(entry.types));
224			pointersToAdd.push_back(&(entry.imp));
225		}
226	}
227
228    static objc_method_list_t<A>* newMethodList(size_t newCount, uint32_t newEntsize) {
229        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
230        return new (buf) objc_method_list_t<A>(newCount, newEntsize);
231    }
232
233    void operator delete(void * p) {
234        ::free(p);
235    }
236
237    objc_method_list_t(uint32_t newCount,
238                       uint32_t newEntsize = sizeof(objc_method_t<A>))
239        : entsize(newEntsize), count(newCount)
240    { }
241};
242
243template <typename A>
244class objc_ivar_t {
245    typedef typename A::P::uint_t pint_t;
246    typename A::P::uint_t offset;  // A::P *
247    typename A::P::uint_t name;    // const char *
248    typename A::P::uint_t type;    // const char *
249    uint32_t alignment;
250    uint32_t size;
251
252public:
253    const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
254
255    bool hasOffset() const { return A::P::getP(offset) != 0; }
256    pint_t getOffset(SharedCache<A> *cache) const { return A::P::getP(*(pint_t * const)cache->mappedAddressForVMAddress(A::P::getP(offset))); }
257    void setOffset(SharedCache<A> *cache, pint_t newOffset) { A::P::setP(*(pint_t *)cache->mappedAddressForVMAddress(A::P::getP(offset)), newOffset); }
258
259    uint32_t getAlignment()
260    {
261        uint32_t a = A::P::E::get32(alignment);
262        return a == (uint32_t)-1 ? sizeof(typename A::P::uint_t) : 1<<a;
263    }
264};
265
266template <typename A>
267class objc_ivar_list_t {
268    typedef typename A::P::uint_t pint_t;
269    uint32_t entsize;
270    uint32_t count;
271    objc_ivar_t<A> first;
272
273    // use newIvarList instead
274    void* operator new (size_t) { return NULL; }
275    void* operator new (size_t, void* buf) { return buf; }
276
277public:
278
279    typedef entsize_iterator< A, objc_ivar_t<A>, objc_ivar_list_t<A> > ivar_iterator;
280
281    uint32_t getCount() const { return A::P::E::get32(count); }
282
283	uint32_t getEntsize() const { return A::P::E::get32(entsize); }
284
285    objc_ivar_t<A>& get(pint_t i) const { return *(objc_ivar_t<A> *)((uint8_t *)&first + i * A::P::E::get32(entsize)); }
286
287    uint32_t byteSize() const {
288        return byteSizeForCount(getCount(), getEntsize());
289    }
290
291    static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t<A>)) {
292        return sizeof(objc_ivar_list_t<A>) - sizeof(objc_ivar_t<A>) + c*e;
293    }
294
295    ivar_iterator begin() { return ivar_iterator(*this, 0); }
296    ivar_iterator end() { return ivar_iterator(*this, getCount()); }
297    const ivar_iterator begin() const { return ivar_iterator(*this, 0); }
298    const ivar_iterator end() const { return ivar_iterator(*this, getCount()); }
299
300    static objc_ivar_list_t<A>* newIvarList(size_t newCount, uint32_t newEntsize) {
301        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
302        return new (buf) objc_ivar_list_t<A>(newCount, newEntsize);
303    }
304
305    void operator delete(void * p) {
306        ::free(p);
307    }
308
309    objc_ivar_list_t(uint32_t newCount,
310                         uint32_t newEntsize = sizeof(objc_ivar_t<A>))
311        : entsize(newEntsize), count(newCount)
312    { }
313
314};
315
316
317template <typename A> class objc_property_list_t; // forward
318
319template <typename A>
320class objc_property_t {
321    typename A::P::uint_t name;
322    typename A::P::uint_t attributes;
323	friend class objc_property_list_t<A>;
324public:
325
326    const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
327
328    const char * getAttributes(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(attributes)); }
329};
330
331template <typename A>
332class objc_property_list_t {
333    uint32_t entsize;
334    uint32_t count;
335    objc_property_t<A> first;
336
337    // use newPropertyList instead
338    void* operator new (size_t) { return NULL; }
339    void* operator new (size_t, void* buf) { return buf; }
340
341public:
342
343    typedef entsize_iterator< A, objc_property_t<A>, objc_property_list_t<A> > property_iterator;
344
345    uint32_t getCount() const { return A::P::E::get32(count); }
346
347	uint32_t getEntsize() const { return A::P::E::get32(entsize); }
348
349    objc_property_t<A>& get(uint32_t i) const { return *(objc_property_t<A> *)((uint8_t *)&first + i * getEntsize()); }
350
351    uint32_t byteSize() const {
352        return byteSizeForCount(getCount(), getEntsize());
353    }
354
355    static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t<A>)) {
356        return sizeof(objc_property_list_t<A>) - sizeof(objc_property_t<A>) + c*e;
357    }
358
359    property_iterator begin() { return property_iterator(*this, 0); }
360    property_iterator end() { return property_iterator(*this, getCount()); }
361    const property_iterator begin() const { return property_iterator(*this, 0); }
362    const property_iterator end() const { return property_iterator(*this, getCount()); }
363
364	void getPointers(std::set<void*>& pointersToRemove) {
365		for(property_iterator it = begin(); it != end(); ++it) {
366			objc_property_t<A>& entry = *it;
367			pointersToRemove.insert(&(entry.name));
368			pointersToRemove.insert(&(entry.attributes));
369		}
370	}
371
372	static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
373		objc_property_list_t<A>* plist = (objc_property_list_t<A>*)propertyList;
374		for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
375			objc_property_t<A>& entry = *it;
376			pointersToAdd.push_back(&(entry.name));
377			pointersToAdd.push_back(&(entry.attributes));
378		}
379	}
380
381     static objc_property_list_t<A>* newPropertyList(size_t newCount, uint32_t newEntsize) {
382        void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
383        return new (buf) objc_property_list_t<A>(newCount, newEntsize);
384    }
385
386    void operator delete(void * p) {
387        ::free(p);
388    }
389
390    objc_property_list_t(uint32_t newCount,
391                         uint32_t newEntsize = sizeof(objc_property_t<A>))
392        : entsize(newEntsize), count(newCount)
393    { }
394
395};
396
397template <typename A>
398class objc_protocol_t {
399    typename A::P::uint_t isa;
400    typename A::P::uint_t name;
401    typename A::P::uint_t protocols;
402    typename A::P::uint_t instanceMethods;
403    typename A::P::uint_t classMethods;
404    typename A::P::uint_t optionalInstanceMethods;
405    typename A::P::uint_t optionalClassMethods;
406    typename A::P::uint_t instanceProperties;
407
408public:
409    objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
410
411    objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
412
413    objc_method_list_t<A> *getOptionalInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalInstanceMethods)); }
414
415    objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); }
416
417};
418
419template <typename A>
420class objc_protocol_list_t {
421    typedef typename A::P::uint_t pint_t;
422    pint_t count;
423    pint_t list[0];
424
425    // use newProtocolList instead
426    void* operator new (size_t) { return NULL; }
427    void* operator new (size_t, void* buf) { return buf; }
428
429public:
430
431    pint_t getCount() const { return A::P::getP(count); }
432
433    objc_protocol_t<A>* get(SharedCache<A>* cache, pint_t i) {
434        return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(A::P::getP(list[i]));
435    }
436
437    void overwrite(pint_t& index, const objc_protocol_list_t<A>* src) {
438        pint_t srcCount = src->getCount();
439        memcpy(list+index, src->list, srcCount * sizeof(pint_t));
440        index += srcCount;
441    }
442
443    uint32_t byteSize() const {
444        return byteSizeForCount(getCount());
445    }
446    static uint32_t byteSizeForCount(pint_t c) {
447        return sizeof(objc_protocol_list_t<A>) + c*sizeof(pint_t);
448    }
449
450	void getPointers(std::set<void*>& pointersToRemove) {
451		for(int i=0 ; i < count; ++i) {
452			pointersToRemove.insert(&list[i]);
453		}
454	}
455
456 	static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
457		objc_protocol_list_t<A>* plist = (objc_protocol_list_t<A>*)protocolList;
458		for(int i=0 ; i < plist->count; ++i) {
459			pointersToAdd.push_back(&plist->list[i]);
460		}
461	}
462
463    static objc_protocol_list_t<A>* newProtocolList(pint_t newCount) {
464        void *buf = ::calloc(byteSizeForCount(newCount), 1);
465        return new (buf) objc_protocol_list_t<A>(newCount);
466    }
467
468    void operator delete(void * p) {
469        ::free(p);
470    }
471
472    objc_protocol_list_t(uint32_t newCount) : count(newCount) { }
473
474};
475
476
477template <typename A>
478class objc_class_data_t {
479    uint32_t flags;
480    uint32_t instanceStart;
481    // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
482    // on 64-bit archs, but no padding on 32-bit archs.
483    // This union is a way to model that.
484    union {
485        uint32_t                instanceSize;
486        typename A::P::uint_t   pad;
487    } instanceSize;
488    typename A::P::uint_t ivarLayout;
489    typename A::P::uint_t name;
490    typename A::P::uint_t baseMethods;
491    typename A::P::uint_t baseProtocols;
492    typename A::P::uint_t ivars;
493    typename A::P::uint_t weakIvarLayout;
494    typename A::P::uint_t baseProperties;
495
496public:
497    bool isMetaClass() { return A::P::E::get32(flags) & 1; }
498
499    uint32_t getInstanceStart() { return A::P::E::get32(instanceStart); }
500    void setInstanceStart(uint32_t newStart) { A::P::E::set32(instanceStart, newStart); }
501
502    uint32_t getInstanceSize() { return A::P::E::get32(instanceSize.instanceSize); }
503    void setInstanceSize(uint32_t newSiz) { A::P::E::set32(instanceSize.instanceSize, newSiz); }
504
505    objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseMethods)); }
506
507    objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProtocols)); }
508
509    objc_ivar_list_t<A> *getIvarList(SharedCache<A>* cache) const { return (objc_ivar_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(ivars)); }
510
511    objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProperties)); }
512
513    const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
514
515    void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
516        A::P::setP(baseMethods, cache->VMAddressForMappedAddress(mlist));
517    }
518
519    void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
520        A::P::setP(baseProtocols, cache->VMAddressForMappedAddress(protolist));
521    }
522
523    void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
524        A::P::setP(baseProperties, cache->VMAddressForMappedAddress(proplist));
525    }
526
527	void addMethodListPointer(std::vector<void*>& pointersToAdd) {
528		pointersToAdd.push_back(&this->baseMethods);
529	}
530
531	void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
532		pointersToAdd.push_back(&this->baseProperties);
533	}
534
535	void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
536		pointersToAdd.push_back(&this->baseProtocols);
537	}
538};
539
540template <typename A>
541class objc_class_t {
542    typename A::P::uint_t isa;
543    typename A::P::uint_t superclass;
544    typename A::P::uint_t method_cache;
545    typename A::P::uint_t vtable;
546    typename A::P::uint_t data;
547
548public:
549    bool isMetaClass(SharedCache<A>* cache) const { return getData(cache)->isMetaClass(); }
550
551    objc_class_t<A> *getIsa(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(isa)); }
552
553    objc_class_t<A> *getSuperclass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(superclass)); }
554
555    objc_class_data_t<A> *getData(SharedCache<A>* cache) const { return (objc_class_data_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(data)); }
556
557    objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return getData(cache)->getMethodList(cache); }
558
559    objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return getData(cache)->getProtocolList(cache); }
560
561    objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return getData(cache)->getPropertyList(cache); }
562
563    const char * getName(SharedCache<A>* cache) const {
564        return getData(cache)->getName(cache);
565    }
566
567    void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
568        getData(cache)->setMethodList(cache, mlist);
569    }
570
571    void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
572        getData(cache)->setProtocolList(cache, protolist);
573    }
574
575    void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
576        getData(cache)->setPropertyList(cache, proplist);
577    }
578
579	void addMethodListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
580        getData(cache)->addMethodListPointer(pointersToAdd);
581	}
582
583	void addPropertyListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
584        getData(cache)->addPropertyListPointer(pointersToAdd);
585	}
586
587	void addProtocolListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
588        getData(cache)->addProtocolListPointer(pointersToAdd);
589	}
590
591};
592
593
594
595template <typename A>
596class objc_category_t {
597    typename A::P::uint_t name;
598    typename A::P::uint_t cls;
599    typename A::P::uint_t instanceMethods;
600    typename A::P::uint_t classMethods;
601    typename A::P::uint_t protocols;
602    typename A::P::uint_t instanceProperties;
603
604public:
605
606    const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
607
608    objc_class_t<A> *getClass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(cls)); }
609
610    objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
611
612    objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
613
614    objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
615
616    objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
617
618	void getPointers(std::set<void*>& pointersToRemove) {
619		pointersToRemove.insert(&name);
620		pointersToRemove.insert(&cls);
621		pointersToRemove.insert(&instanceMethods);
622		pointersToRemove.insert(&classMethods);
623		pointersToRemove.insert(&protocols);
624		pointersToRemove.insert(&instanceProperties);
625	}
626
627
628};
629
630template <typename A>
631class objc_message_ref_t {
632    typename A::P::uint_t imp;
633    typename A::P::uint_t sel;
634
635public:
636    typename A::P::uint_t getName() const { return A::P::getP(sel); }
637
638    void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); }
639};
640
641// Call visitor.visitIvar() on every ivar in a given class.
642template <typename A, typename V>
643class IvarWalker {
644    typedef typename A::P P;
645    typedef typename A::P::uint_t pint_t;
646    V& ivarVisitor;
647public:
648
649    IvarWalker(V& visitor) : ivarVisitor(visitor) { }
650
651    void walk(SharedCache<A>* cache, const macho_header<P>* header, objc_class_t<A> *cls)
652    {
653        objc_class_data_t<A> *data = cls->getData(cache);
654        objc_ivar_list_t<A> *ivars = data->getIvarList(cache);
655        if (ivars) {
656            for (pint_t i = 0; i < ivars->getCount(); i++) {
657                objc_ivar_t<A>& ivar = ivars->get(i);
658                //fprintf(stderr, "visiting ivar: %s\n", ivar.getName(cache));
659                ivarVisitor.visitIvar(cache, header, cls, &ivar);
660            }
661        } else {
662            //fprintf(stderr, "no ivars\n");
663        }
664    }
665
666    void visitClass(SharedCache<A>* cache, const macho_header<P>* header, objc_class_t<A> *cls)
667    {
668        walk(cache, header, cls);
669    }
670};
671
672// Call visitor.visitClass() on every class.
673template <typename A, typename V>
674class ClassWalker {
675    typedef typename A::P P;
676    typedef typename A::P::uint_t pint_t;
677    V& classVisitor;
678public:
679
680    ClassWalker(V& visitor) : classVisitor(visitor) { }
681
682    void walk(SharedCache<A>* cache, const macho_header<P>* header)
683    {
684        PointerSection<A, objc_class_t<A> *>
685        classes(cache, header, "__DATA", "__objc_classlist");
686
687        for (pint_t i = 0; i < classes.count(); i++) {
688            objc_class_t<A> *cls = classes.get(i);
689            //fprintf(stderr, "visiting class: %s\n", cls->getName(cache));
690            classVisitor.visitClass(cache, header, cls);
691        }
692    }
693};
694
695
696// Call visitor.visitMethodList(mlist) on every method list in a header.
697template <typename A, typename V>
698class MethodListWalker {
699
700    typedef typename A::P P;
701    typedef typename A::P::uint_t pint_t;
702
703    V& mVisitor;
704
705public:
706
707    MethodListWalker(V& visitor) : mVisitor(visitor) { }
708
709    void walk(SharedCache<A>* cache, const macho_header<P>* header, bool walkProtocols)
710    {
711        // Method lists in classes
712        PointerSection<A, objc_class_t<A> *>
713            classes(cache, header, "__DATA", "__objc_classlist");
714
715        for (pint_t i = 0; i < classes.count(); i++) {
716            objc_class_t<A> *cls = classes.get(i);
717            objc_method_list_t<A> *mlist;
718            if ((mlist = cls->getMethodList(cache))) {
719                mVisitor.visitMethodList(mlist);
720            }
721            if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
722                mVisitor.visitMethodList(mlist);
723            }
724        }
725
726        // Method lists from categories
727        PointerSection<A, objc_category_t<A> *>
728            cats(cache, header, "__DATA", "__objc_catlist");
729        for (pint_t i = 0; i < cats.count(); i++) {
730            objc_category_t<A> *cat = cats.get(i);
731            objc_method_list_t<A> *mlist;
732            if ((mlist = cat->getInstanceMethods(cache))) {
733                mVisitor.visitMethodList(mlist);
734            }
735            if ((mlist = cat->getClassMethods(cache))) {
736                mVisitor.visitMethodList(mlist);
737            }
738        }
739
740		// Method description lists from protocols
741		if ( walkProtocols ) {
742			PointerSection<A, objc_protocol_t<A> *>
743				protocols(cache, header, "__DATA", "__objc_protolist");
744			for (pint_t i = 0; i < protocols.count(); i++) {
745				objc_protocol_t<A> *proto = protocols.get(i);
746				objc_method_list_t<A> *mlist;
747				if ((mlist = proto->getInstanceMethods(cache))) {
748					mVisitor.visitMethodList(mlist);
749				}
750				if ((mlist = proto->getClassMethods(cache))) {
751					mVisitor.visitMethodList(mlist);
752				}
753				if ((mlist = proto->getOptionalInstanceMethods(cache))) {
754					mVisitor.visitMethodList(mlist);
755				}
756				if ((mlist = proto->getOptionalClassMethods(cache))) {
757					mVisitor.visitMethodList(mlist);
758				}
759			}
760		}
761    }
762};
763
764
765// Update selector references. The visitor performs recording and uniquing.
766template <typename A, typename V>
767class SelectorOptimizer {
768
769    typedef typename A::P P;
770    typedef typename A::P::uint_t pint_t;
771
772    V& mVisitor;
773
774    friend class MethodListWalker< A, SelectorOptimizer<A,V> >;
775    void visitMethodList(objc_method_list_t<A> *mlist)
776    {
777        // Gather selectors. Update method names.
778        for (pint_t m = 0; m < mlist->getCount(); m++) {
779            pint_t oldValue = mlist->get(m).getName();
780            pint_t newValue = mVisitor.visit(oldValue);
781            mlist->get(m).setName(newValue);
782        }
783        // Do not setFixedUp: the methods are not yet sorted.
784    }
785
786public:
787
788    SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
789
790    void optimize(SharedCache<A>* cache, const macho_header<P>* header)
791    {
792        // method lists of all kinds
793        MethodListWalker< A, SelectorOptimizer<A,V> > mw(*this);
794        mw.walk(cache, header, true);
795
796        // @selector references
797        PointerSection<A, const char *>
798            selrefs(cache, header, "__DATA", "__objc_selrefs");
799        for (pint_t i = 0; i < selrefs.count(); i++) {
800            pint_t oldValue = selrefs.getUnmapped(i);
801            pint_t newValue = mVisitor.visit(oldValue);
802            selrefs.set(i, newValue);
803        }
804
805        // message references
806        ArraySection<A, objc_message_ref_t<A> >
807            msgrefs(cache, header, "__DATA", "__objc_msgrefs");
808        for (pint_t i = 0; i < msgrefs.count(); i++) {
809            objc_message_ref_t<A>& msg = msgrefs.get(i);
810            pint_t oldValue = msg.getName();
811            pint_t newValue = mVisitor.visit(oldValue);
812            msg.setName(newValue);
813        }
814    }
815};
816
817
818// Update selector references. The visitor performs recording and uniquing.
819template <typename A>
820class IvarOffsetOptimizer {
821    typedef typename A::P P;
822
823    uint32_t slide;
824    uint32_t maxAlignment;
825
826    uint32_t fOptimized;
827
828public:
829
830    IvarOffsetOptimizer() : fOptimized(0) { }
831
832    size_t optimized() const { return fOptimized; }
833
834    // dual purpose ivar visitor function
835    // if slide!=0 then slides the ivar by that amount, otherwise computes maxAlignment
836    void visitIvar(SharedCache<A>* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<A> *cls, objc_ivar_t<A> *ivar)
837    {
838        if (slide == 0) {
839            uint32_t alignment = ivar->getAlignment();
840            if (alignment > maxAlignment) maxAlignment = alignment;
841        } else {
842            // skip anonymous bitfields
843            if (ivar->hasOffset()) {
844                uint32_t oldOffset = (uint32_t)ivar->getOffset(cache);
845                ivar->setOffset(cache, oldOffset + slide);
846                fOptimized++;
847                //fprintf(stderr, "%d -> %d for %s.%s\n", oldOffset, oldOffset + slide, cls->getName(cache), ivar->getName(cache));
848            } else {
849                //fprintf(stderr, "NULL offset\n");
850            }
851        }
852    }
853
854    // Class visitor function. Evaluates whether to slide ivars and performs slide if needed.
855    // The slide algorithm is also implemented in objc. Any changes here should be reflected there also.
856    void visitClass(SharedCache<A>* cache, const macho_header<P>* /*unused, may be NULL*/, objc_class_t<A> *cls)
857    {
858        objc_class_t<A> *super = cls->getSuperclass(cache);
859        if (super) {
860            // Recursively visit superclasses to ensure we have the correct superclass start
861            // Note that we don't need the macho_header, so just pass NULL.
862            visitClass(cache, NULL, super);
863
864            objc_class_data_t<A> *data = cls->getData(cache);
865            objc_class_data_t<A> *super_data = super->getData(cache);
866            int32_t diff = super_data->getInstanceSize() - data->getInstanceStart();
867            if (diff > 0) {
868                IvarWalker<A, IvarOffsetOptimizer<A> > ivarVisitor(*this);
869                maxAlignment = 0;
870                slide = 0;
871
872                // This walk computes maxAlignment
873                ivarVisitor.walk(cache, NULL, cls);
874
875                // Compute a slide value that preserves that alignment
876                uint32_t alignMask = maxAlignment - 1;
877                if (diff & alignMask) diff = (diff + alignMask) & ~alignMask;
878
879                // Slide all of this class's ivars en masse
880                slide = diff;
881                if (slide != 0) {
882                    //fprintf(stderr, "Sliding ivars in %s by %u (superclass was %d, now %d)\n", cls->getName(cache), slide, data->getInstanceStart(), super_data->getInstanceSize());
883                    ivarVisitor.walk(cache, NULL, cls);
884                    data->setInstanceStart(data->getInstanceStart() + slide);
885                    data->setInstanceSize(data->getInstanceSize() + slide);
886                }
887            }
888        }
889    }
890
891    // Enumerates objc classes in the module and performs any ivar slides
892    void optimize(SharedCache<A>* cache, const macho_header<P>* header)
893    {
894        // The slide code cannot fix up GC layout strings so skip modules that support ore require GC
895        const macho_section<P> *imageInfoSection = header->getSection("__DATA", "__objc_imageinfo");
896        if (imageInfoSection) {
897            objc_image_info<A> *info = (objc_image_info<A> *)cache->mappedAddressForVMAddress(imageInfoSection->addr());
898            if (!info->supportsGCFlagSet() && !info->requiresGCFlagSet()) {
899                ClassWalker<A, IvarOffsetOptimizer<A> > classVisitor(*this);
900                classVisitor.walk(cache, header);
901            } else {
902                //fprintf(stderr, "GC support present - skipped module\n");
903            }
904        }
905    }
906};
907
908
909// Sort methods in place by selector.
910template <typename A>
911class MethodListSorter {
912
913    typedef typename A::P P;
914    typedef typename A::P::uint_t pint_t;
915
916    uint32_t fOptimized;
917
918    friend class MethodListWalker<A, MethodListSorter<A> >;
919    void visitMethodList(objc_method_list_t<A> *mlist)
920    {
921        typename objc_method_t<A>::SortBySELAddress sorter;
922        std::stable_sort(mlist->begin(), mlist->end(), sorter);
923        mlist->setFixedUp();
924        fOptimized++;
925    }
926
927public:
928    MethodListSorter() : fOptimized(0) { }
929
930    size_t optimized() const { return fOptimized; }
931
932    void optimize(SharedCache<A>* cache, macho_header<P>* header)
933    {
934        MethodListWalker<A, MethodListSorter<A> > mw(*this);
935        mw.walk(cache, header, false /* don't sort protocol method lists*/);
936    }
937};
938
939
940template <typename A>
941class HeaderInfoOptimizer {
942
943    typedef typename A::P P;
944    typedef typename A::P::uint_t pint_t;
945
946    objc_header_info_t<A>* fHinfos;
947    size_t fCount;
948
949public:
950    HeaderInfoOptimizer() : fHinfos(0), fCount(0) { }
951
952    const char *init(size_t count, uint8_t*& buf, size_t& bufSize)
953    {
954        if (count == 0) return NULL;
955
956        if (bufSize < 2*sizeof(uint32_t) + count*sizeof(objc_header_info_t<A>)) {
957            return "libobjc's read/write section is too small (metadata not optimized)";
958        }
959
960        uint32_t *buf32 = (uint32_t *)buf;
961        A::P::E::set32(buf32[0], count);
962        A::P::E::set32(buf32[1], sizeof(objc_header_info_t<A>));
963        fHinfos = (objc_header_info_t<A>*)(buf32+2);
964
965        size_t total = sizeof(uint32_t) + count*sizeof(objc_header_info_t<A>);
966        buf += total;
967        bufSize -= total;
968
969        return NULL;
970    }
971
972    void update(SharedCache<A>* cache, const macho_header<P>* mh, std::vector<void*>& pointersInData)
973    {
974        objc_header_info_t<A>* hi = new(&fHinfos[fCount++]) objc_header_info_t<A>(cache, mh);
975        hi->addPointers(pointersInData);
976    }
977
978    objc_header_info_t<A>* hinfoForHeader(SharedCache<A>* cache, const macho_header<P>* mh)
979    {
980        // fixme could be binary search
981        pint_t mh_vmaddr = cache->VMAddressForMappedAddress(mh);
982        for (size_t i = 0; i < fCount; i++) {
983            objc_header_info_t<A>* hi = &fHinfos[i];
984            if (hi->header_vmaddr() == mh_vmaddr) return hi;
985        }
986        return NULL;
987    }
988};
989