1 /* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
3 * Copyright (c) 2008-2010 Apple Inc. All rights reserved.
5 * @APPLE_LICENSE_HEADER_START@
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
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.
22 * @APPLE_LICENSE_HEADER_END@
28 // iterate an entsize-based list
29 // typedef entsize_iterator< A, type_t<A>, type_list_t<A> > type_iterator;
30 template <typename A, typename T, typename Tlist>
31 struct entsize_iterator {
33 uint32_t index; // keeping track of this saves a divide in operator-
36 typedef std::random_access_iterator_tag iterator_category;
38 typedef ptrdiff_t difference_type;
42 entsize_iterator() { }
44 entsize_iterator(const Tlist& list, uint32_t start = 0)
45 : entsize(list.getEntsize()), index(start), current(&list.get(start))
48 const entsize_iterator<A,T,Tlist>& operator += (ptrdiff_t count) {
49 current = (T*)((uint8_t *)current + count*entsize);
53 const entsize_iterator<A,T,Tlist>& operator -= (ptrdiff_t count) {
54 current = (T*)((uint8_t *)current - count*entsize);
58 const entsize_iterator<A,T,Tlist> operator + (ptrdiff_t count) const {
59 return entsize_iterator(*this) += count;
61 const entsize_iterator<A,T,Tlist> operator - (ptrdiff_t count) const {
62 return entsize_iterator(*this) -= count;
65 entsize_iterator<A,T,Tlist>& operator ++ () { *this += 1; return *this; }
66 entsize_iterator<A,T,Tlist>& operator -- () { *this -= 1; return *this; }
67 entsize_iterator<A,T,Tlist> operator ++ (int) {
68 entsize_iterator<A,T,Tlist> result(*this); *this += 1; return result;
70 entsize_iterator<A,T,Tlist> operator -- (int) {
71 entsize_iterator<A,T,Tlist> result(*this); *this -= 1; return result;
74 ptrdiff_t operator - (const entsize_iterator<A,T,Tlist>& rhs) const {
75 return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
78 T& operator * () { return *current; }
79 const T& operator * () const { return *current; }
80 T& operator -> () { return *current; }
81 const T& operator -> () const { return *current; }
83 operator T& () const { return *current; }
85 bool operator == (const entsize_iterator<A,T,Tlist>& rhs) {
86 return this->current == rhs.current;
88 bool operator != (const entsize_iterator<A,T,Tlist>& rhs) {
89 return this->current != rhs.current;
92 bool operator < (const entsize_iterator<A,T,Tlist>& rhs) {
93 return this->current < rhs.current;
95 bool operator > (const entsize_iterator<A,T,Tlist>& rhs) {
96 return this->current > rhs.current;
100 static void overwrite(entsize_iterator<A,T,Tlist>& dst, const Tlist* srcList)
102 entsize_iterator<A,T,Tlist> src;
103 uint32_t ee = srcList->getEntsize();
104 for (src = srcList->begin(); src != srcList->end(); ++src) {
105 memcpy(&*dst, &*src, ee);
111 template <typename A> class objc_method_list_t; // forward reference
113 template <typename A>
114 class objc_method_t {
115 typename A::P::uint_t name; // SEL
116 typename A::P::uint_t types; // const char *
117 typename A::P::uint_t imp; // IMP
118 friend class objc_method_list_t<A>;
120 typename A::P::uint_t getName() const { return A::P::getP(name); }
121 void setName(typename A::P::uint_t newName) { A::P::setP(name, newName); }
123 struct SortBySELAddress :
124 public std::binary_function<const objc_method_t<A>&,
125 const objc_method_t<A>&, bool>
127 bool operator() (const objc_method_t<A>& lhs,
128 const objc_method_t<A>& rhs)
130 return lhs.getName() < rhs.getName();
135 template <typename A>
136 class objc_method_list_t {
139 objc_method_t<A> first;
141 // use newMethodList instead
142 void* operator new (size_t) { return NULL; }
143 void* operator new (size_t, void* buf) { return buf; }
147 typedef entsize_iterator< A, objc_method_t<A>, objc_method_list_t<A> > method_iterator;
149 uint32_t getCount() const { return A::P::E::get32(count); }
151 uint32_t getEntsize() const {return A::P::E::get32(entsize)&~(uint32_t)3;}
153 objc_method_t<A>& get(uint32_t i) const { return *(objc_method_t<A> *)((uint8_t *)&first + i * getEntsize()); }
155 uint32_t byteSize() const {
156 return byteSizeForCount(getCount(), getEntsize());
159 static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<A>)) {
160 return sizeof(objc_method_list_t<A>) - sizeof(objc_method_t<A>) + c*e;
163 method_iterator begin() { return method_iterator(*this, 0); }
164 method_iterator end() { return method_iterator(*this, getCount()); }
165 const method_iterator begin() const { return method_iterator(*this, 0); }
166 const method_iterator end() const { return method_iterator(*this, getCount()); }
168 void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); }
170 void getPointers(std::set<void*>& pointersToRemove) {
171 for(method_iterator it = begin(); it != end(); ++it) {
172 objc_method_t<A>& entry = *it;
173 pointersToRemove.insert(&(entry.name));
174 pointersToRemove.insert(&(entry.types));
175 pointersToRemove.insert(&(entry.imp));
179 static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
180 objc_method_list_t<A>* mlist = (objc_method_list_t<A>*)methodList;
181 for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
182 objc_method_t<A>& entry = *it;
183 pointersToAdd.push_back(&(entry.name));
184 pointersToAdd.push_back(&(entry.types));
185 pointersToAdd.push_back(&(entry.imp));
189 static objc_method_list_t<A>* newMethodList(size_t newCount, uint32_t newEntsize) {
190 void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
191 return new (buf) objc_method_list_t<A>(newCount, newEntsize);
194 void operator delete(void * p) {
198 objc_method_list_t(uint32_t newCount,
199 uint32_t newEntsize = sizeof(objc_method_t<A>))
200 : entsize(newEntsize), count(newCount)
204 template <typename A>
206 typename A::P::uint_t offset; // A::P *
207 typename A::P::uint_t name; // const char *
208 typename A::P::uint_t type; // const char *
213 template <typename A>
214 class objc_ivar_list_t {
217 objc_ivar_t<A> first;
219 // use newIvarList instead
220 void* operator new (size_t) { return NULL; }
221 void* operator new (size_t, void* buf) { return buf; }
225 typedef entsize_iterator< A, objc_ivar_t<A>, objc_ivar_list_t<A> > ivar_iterator;
227 uint32_t getCount() const { return A::P::E::get32(count); }
229 uint32_t getEntsize() const { return A::P::E::get32(entsize); }
231 objc_ivar_t<A>& get(typename A::P::pint_t i) const { return *(objc_ivar_t<A> *)((uint8_t *)&first + i * A::P::E::get32(entsize)); }
233 uint32_t byteSize() const {
234 return byteSizeForCount(getCount(), getEntsize());
237 static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t<A>)) {
238 return sizeof(objc_ivar_list_t<A>) - sizeof(objc_ivar_t<A>) + c*e;
241 ivar_iterator begin() { return ivar_iterator(*this, 0); }
242 ivar_iterator end() { return ivar_iterator(*this, getCount()); }
243 const ivar_iterator begin() const { return ivar_iterator(*this, 0); }
244 const ivar_iterator end() const { return ivar_iterator(*this, getCount()); }
246 static objc_ivar_list_t<A>* newIvarList(size_t newCount, uint32_t newEntsize) {
247 void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
248 return new (buf) objc_ivar_list_t<A>(newCount, newEntsize);
251 void operator delete(void * p) {
255 objc_ivar_list_t(uint32_t newCount,
256 uint32_t newEntsize = sizeof(objc_ivar_t<A>))
257 : entsize(newEntsize), count(newCount)
263 template <typename A> class objc_property_list_t; // forward
265 template <typename A>
266 class objc_property_t {
267 typename A::P::uint_t name;
268 typename A::P::uint_t attributes;
269 friend class objc_property_list_t<A>;
272 const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
274 const char * getAttributes(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(attributes)); }
277 template <typename A>
278 class objc_property_list_t {
281 objc_property_t<A> first;
283 // use newPropertyList instead
284 void* operator new (size_t) { return NULL; }
285 void* operator new (size_t, void* buf) { return buf; }
289 typedef entsize_iterator< A, objc_property_t<A>, objc_property_list_t<A> > property_iterator;
291 uint32_t getCount() const { return A::P::E::get32(count); }
293 uint32_t getEntsize() const { return A::P::E::get32(entsize); }
295 objc_property_t<A>& get(uint32_t i) const { return *(objc_property_t<A> *)((uint8_t *)&first + i * getEntsize()); }
297 uint32_t byteSize() const {
298 return byteSizeForCount(getCount(), getEntsize());
301 static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t<A>)) {
302 return sizeof(objc_property_list_t<A>) - sizeof(objc_property_t<A>) + c*e;
305 property_iterator begin() { return property_iterator(*this, 0); }
306 property_iterator end() { return property_iterator(*this, getCount()); }
307 const property_iterator begin() const { return property_iterator(*this, 0); }
308 const property_iterator end() const { return property_iterator(*this, getCount()); }
310 void getPointers(std::set<void*>& pointersToRemove) {
311 for(property_iterator it = begin(); it != end(); ++it) {
312 objc_property_t<A>& entry = *it;
313 pointersToRemove.insert(&(entry.name));
314 pointersToRemove.insert(&(entry.attributes));
318 static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
319 objc_property_list_t<A>* plist = (objc_property_list_t<A>*)propertyList;
320 for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
321 objc_property_t<A>& entry = *it;
322 pointersToAdd.push_back(&(entry.name));
323 pointersToAdd.push_back(&(entry.attributes));
327 static objc_property_list_t<A>* newPropertyList(size_t newCount, uint32_t newEntsize) {
328 void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
329 return new (buf) objc_property_list_t<A>(newCount, newEntsize);
332 void operator delete(void * p) {
336 objc_property_list_t(uint32_t newCount,
337 uint32_t newEntsize = sizeof(objc_property_t<A>))
338 : entsize(newEntsize), count(newCount)
343 template <typename A>
344 class objc_protocol_t {
345 typename A::P::uint_t isa;
346 typename A::P::uint_t name;
347 typename A::P::uint_t protocols;
348 typename A::P::uint_t instanceMethods;
349 typename A::P::uint_t classMethods;
350 typename A::P::uint_t optionalInstanceMethods;
351 typename A::P::uint_t optionalClassMethods;
352 typename A::P::uint_t instanceProperties;
355 objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
357 objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
359 objc_method_list_t<A> *getOptionalInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalInstanceMethods)); }
361 objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); }
365 template <typename A>
366 class objc_protocol_list_t {
367 typedef typename A::P::uint_t pint_t;
371 // use newProtocolList instead
372 void* operator new (size_t) { return NULL; }
373 void* operator new (size_t, void* buf) { return buf; }
377 pint_t getCount() const { return A::P::getP(count); }
379 objc_protocol_t<A>* get(SharedCache<A>* cache, pint_t i) {
380 return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(A::P::getP(list[i]));
383 void overwrite(pint_t& index, const objc_protocol_list_t<A>* src) {
384 pint_t srcCount = src->getCount();
385 memcpy(list+index, src->list, srcCount * sizeof(pint_t));
389 uint32_t byteSize() const {
390 return byteSizeForCount(getCount());
392 static uint32_t byteSizeForCount(pint_t c) {
393 return sizeof(objc_protocol_list_t<A>) + c*sizeof(pint_t);
396 void getPointers(std::set<void*>& pointersToRemove) {
397 for(int i=0 ; i < count; ++i) {
398 pointersToRemove.insert(&list[i]);
402 static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
403 objc_protocol_list_t<A>* plist = (objc_protocol_list_t<A>*)protocolList;
404 for(int i=0 ; i < plist->count; ++i) {
405 pointersToAdd.push_back(&plist->list[i]);
409 static objc_protocol_list_t<A>* newProtocolList(pint_t newCount) {
410 void *buf = ::calloc(byteSizeForCount(newCount), 1);
411 return new (buf) objc_protocol_list_t<A>(newCount);
414 void operator delete(void * p) {
418 objc_protocol_list_t(uint32_t newCount) : count(newCount) { }
423 template <typename A>
424 class objc_class_data_t {
426 uint32_t instanceStart;
427 // Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
428 // on 64-bit archs, but no padding on 32-bit archs.
429 // This union is a way to model that.
431 uint32_t instanceSize;
432 typename A::P::uint_t pad;
434 typename A::P::uint_t ivarLayout;
435 typename A::P::uint_t name;
436 typename A::P::uint_t baseMethods;
437 typename A::P::uint_t baseProtocols;
438 typename A::P::uint_t ivars;
439 typename A::P::uint_t weakIvarLayout;
440 typename A::P::uint_t baseProperties;
443 objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseMethods)); }
445 objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProtocols)); }
447 objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProperties)); }
449 const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
451 void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
452 A::P::setP(baseMethods, cache->VMAddressForMappedAddress(mlist));
455 void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
456 A::P::setP(baseProtocols, cache->VMAddressForMappedAddress(protolist));
459 void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
460 A::P::setP(baseProperties, cache->VMAddressForMappedAddress(proplist));
463 void addMethodListPointer(std::vector<void*>& pointersToAdd) {
464 pointersToAdd.push_back(&this->baseMethods);
467 void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
468 pointersToAdd.push_back(&this->baseProperties);
471 void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
472 pointersToAdd.push_back(&this->baseProtocols);
476 template <typename A>
478 typename A::P::uint_t isa;
479 typename A::P::uint_t superclass;
480 typename A::P::uint_t method_cache;
481 typename A::P::uint_t vtable;
482 typename A::P::uint_t data;
485 objc_class_t<A> *getIsa(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(isa)); }
487 objc_class_data_t<A> *getData(SharedCache<A>* cache) const { return (objc_class_data_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(data)); }
489 objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return getData(cache)->getMethodList(cache); }
491 objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return getData(cache)->getProtocolList(cache); }
493 objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return getData(cache)->getPropertyList(cache); }
495 const char * getName(SharedCache<A>* cache) const {
496 return getData(cache)->getName(cache);
499 void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
500 getData(cache)->setMethodList(cache, mlist);
503 void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
504 getData(cache)->setProtocolList(cache, protolist);
507 void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
508 getData(cache)->setPropertyList(cache, proplist);
511 void addMethodListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
512 getData(cache)->addMethodListPointer(pointersToAdd);
515 void addPropertyListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
516 getData(cache)->addPropertyListPointer(pointersToAdd);
519 void addProtocolListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
520 getData(cache)->addProtocolListPointer(pointersToAdd);
527 template <typename A>
528 class objc_category_t {
529 typename A::P::uint_t name;
530 typename A::P::uint_t cls;
531 typename A::P::uint_t instanceMethods;
532 typename A::P::uint_t classMethods;
533 typename A::P::uint_t protocols;
534 typename A::P::uint_t instanceProperties;
538 const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
540 objc_class_t<A> *getClass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(cls)); }
542 objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
544 objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
546 objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
548 objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
550 void getPointers(std::set<void*>& pointersToRemove) {
551 pointersToRemove.insert(&name);
552 pointersToRemove.insert(&cls);
553 pointersToRemove.insert(&instanceMethods);
554 pointersToRemove.insert(&classMethods);
555 pointersToRemove.insert(&protocols);
556 pointersToRemove.insert(&instanceProperties);
562 template <typename A>
563 class objc_message_ref_t {
564 typename A::P::uint_t imp;
565 typename A::P::uint_t sel;
568 typename A::P::uint_t getName() const { return A::P::getP(sel); }
570 void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); }
574 // Call visitor.visitMethodList(mlist) on every method list in a header.
575 template <typename A, typename V>
576 class MethodListWalker {
578 typedef typename A::P P;
579 typedef typename A::P::uint_t pint_t;
585 MethodListWalker(V& visitor) : mVisitor(visitor) { }
587 void walk(SharedCache<A>* cache, const macho_header<P>* header)
589 // Method lists in classes
590 PointerSection<A, objc_class_t<A> *>
591 classes(cache, header, "__DATA", "__objc_classlist");
593 for (pint_t i = 0; i < classes.count(); i++) {
594 objc_class_t<A> *cls = classes.get(i);
595 objc_method_list_t<A> *mlist;
596 if ((mlist = cls->getMethodList(cache))) {
597 mVisitor.visitMethodList(mlist);
599 if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
600 mVisitor.visitMethodList(mlist);
604 // Method lists from categories
605 PointerSection<A, objc_category_t<A> *>
606 cats(cache, header, "__DATA", "__objc_catlist");
607 for (pint_t i = 0; i < cats.count(); i++) {
608 objc_category_t<A> *cat = cats.get(i);
609 objc_method_list_t<A> *mlist;
610 if ((mlist = cat->getInstanceMethods(cache))) {
611 mVisitor.visitMethodList(mlist);
613 if ((mlist = cat->getClassMethods(cache))) {
614 mVisitor.visitMethodList(mlist);
618 // Method description lists from protocols
619 PointerSection<A, objc_protocol_t<A> *>
620 protocols(cache, header, "__DATA", "__objc_protolist");
621 for (pint_t i = 0; i < protocols.count(); i++) {
622 objc_protocol_t<A> *proto = protocols.get(i);
623 objc_method_list_t<A> *mlist;
624 if ((mlist = proto->getInstanceMethods(cache))) {
625 mVisitor.visitMethodList(mlist);
627 if ((mlist = proto->getClassMethods(cache))) {
628 mVisitor.visitMethodList(mlist);
630 if ((mlist = proto->getOptionalInstanceMethods(cache))) {
631 mVisitor.visitMethodList(mlist);
633 if ((mlist = proto->getOptionalClassMethods(cache))) {
634 mVisitor.visitMethodList(mlist);
641 // Update selector references. The visitor performs recording and uniquing.
642 template <typename A, typename V>
643 class SelectorOptimizer {
645 typedef typename A::P P;
646 typedef typename A::P::uint_t pint_t;
650 friend class MethodListWalker< A, SelectorOptimizer<A,V> >;
651 void visitMethodList(objc_method_list_t<A> *mlist)
653 // Gather selectors. Update method names.
654 for (pint_t m = 0; m < mlist->getCount(); m++) {
655 pint_t oldValue = mlist->get(m).getName();
656 pint_t newValue = mVisitor.visit(oldValue);
657 mlist->get(m).setName(newValue);
659 // Do not setFixedUp: the methods are not yet sorted.
664 SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
666 void optimize(SharedCache<A>* cache, const macho_header<P>* header)
668 // method lists of all kinds
669 MethodListWalker< A, SelectorOptimizer<A,V> > mw(*this);
670 mw.walk(cache, header);
672 // @selector references
673 PointerSection<A, const char *>
674 selrefs(cache, header, "__DATA", "__objc_selrefs");
675 for (pint_t i = 0; i < selrefs.count(); i++) {
676 pint_t oldValue = selrefs.getUnmapped(i);
677 pint_t newValue = mVisitor.visit(oldValue);
678 selrefs.set(i, newValue);
681 // message references
682 ArraySection<A, objc_message_ref_t<A> >
683 msgrefs(cache, header, "__DATA", "__objc_msgrefs");
684 for (pint_t i = 0; i < msgrefs.count(); i++) {
685 objc_message_ref_t<A>& msg = msgrefs.get(i);
686 pint_t oldValue = msg.getName();
687 pint_t newValue = mVisitor.visit(oldValue);
688 msg.setName(newValue);
692 const macho_section<P> *imageInfoSection =
693 header->getSection("__DATA", "__objc_imageinfo");
694 if (imageInfoSection) {
695 objc_image_info<A> *info = (objc_image_info<A> *)
696 cache->mappedAddressForVMAddress(imageInfoSection->addr());
697 info->setSelectorsPrebound();
703 // Sort methods in place by selector.
704 template <typename A>
705 class MethodListSorter {
707 typedef typename A::P P;
708 typedef typename A::P::uint_t pint_t;
710 friend class MethodListWalker<A, MethodListSorter<A> >;
711 void visitMethodList(objc_method_list_t<A> *mlist)
713 typename objc_method_t<A>::SortBySELAddress sorter;
714 std::stable_sort(mlist->begin(), mlist->end(), sorter);
720 void optimize(SharedCache<A>* cache, macho_header<P>* header)
722 MethodListWalker<A, MethodListSorter<A> > mw(*this);
723 mw.walk(cache, header);
728 // Attach categories to classes in the same framework.
729 // Merge method and protocol and property lists.
730 template <typename A>
731 class CategoryAttacher {
733 typedef typename A::P P;
734 typedef typename A::P::uint_t pint_t;
740 size_t mCategoriesAttached;
742 bool segmentContainsPointer(SharedCache<A>* cache,
743 const macho_segment_command<P>* seg, void *ptr)
745 if (!seg) return false;
746 void *start = (void*)
747 cache->mappedAddressForVMAddress(seg->vmaddr());
748 void *end = (uint8_t *)start + seg->filesize();
749 return (ptr >= start && ptr < end);
752 bool headerContainsPointer(SharedCache<A>* cache,
753 macho_header<P>* header, void *ptr)
756 segmentContainsPointer(cache, header->getSegment("__DATA"), ptr) ||
757 segmentContainsPointer(cache, header->getSegment("__TEXT"), ptr) ||
758 segmentContainsPointer(cache, header->getSegment("__OBJC"), ptr);
761 struct pointer_hash {
762 size_t operator () (void* ptr) const {
763 return __gnu_cxx::hash<long>()((long)ptr);
767 typedef std::deque<objc_category_t<A>*> CategoryList;
768 typedef std::vector<uint64_t> CategoryRefs;
770 struct ClassChanges {
771 CategoryList categories;
772 CategoryRefs catrefs;
774 objc_method_list_t<A>* instanceMethods;
775 objc_method_list_t<A>* classMethods;
776 objc_protocol_list_t<A>* protocols;
777 objc_property_list_t<A>* instanceProperties;
780 : instanceMethods(NULL), classMethods(NULL),
781 protocols(NULL), instanceProperties(NULL)
785 if (instanceMethods) delete instanceMethods;
786 if (classMethods) delete classMethods;
787 if (protocols) delete protocols;
788 if (instanceProperties) delete instanceProperties;
792 typedef __gnu_cxx::hash_map<objc_class_t<A>*, ClassChanges, pointer_hash> ClassMap;
795 typedef std::pair<uint8_t*,uint32_t> Range;
796 std::deque<Range> ranges;
802 SizeFits(uint32_t size) : mSize(size) { }
803 bool operator() (const Range& r) {
804 return r.second >= mSize;
809 bool operator() (const Range& lhs, const Range& rhs) {
810 return (lhs.first < rhs.first);
815 void add(void* p, uint32_t size) {
816 add(Range((uint8_t *)p, size));
818 void add(const Range& r) {
819 // find insertion point
820 std::deque<Range>::iterator iter;
821 iter = upper_bound(ranges.begin(), ranges.end(), r, AddressComp());
823 // fixme doesn't fully coalesce if new range exactly fills a gap
824 if (iter != ranges.begin()) {
825 std::deque<Range>::iterator prev = iter - 1;
826 if ((*prev).first + (*prev).second == r.first) {
827 (*prev).second += r.second;
831 if (iter != ranges.end() && iter+1 != ranges.end()) {
832 std::deque<Range>::iterator next = iter + 1;
833 if (r.first + r.second == (*next).first) {
834 (*next).second += r.second;
835 (*next).first = r.first;
839 ranges.insert(iter, r);
842 uint8_t* remove(uint32_t size) {
844 // this saves 50-75% of space overhead;
845 // a better algorithm might do better
847 std::deque<Range>::iterator iter;
848 iter = find_if(ranges.begin(), ranges.end(), SizeFits(size));
849 if (iter == ranges.end()) {
853 Range& found = *iter;
854 uint8_t *result = found.first;
855 if (found.second > size) {
858 found.second -= size;
867 void copyMethods(typename objc_method_list_t<A>::method_iterator& dst,
868 const objc_method_list_t<A>* srcList)
870 objc_method_list_t<A>::method_iterator::
871 overwrite(dst, srcList);
874 void copyProperties(typename objc_property_list_t<A>::property_iterator& dst,
875 const objc_property_list_t<A>* srcList)
877 objc_property_list_t<A>::property_iterator::
878 overwrite(dst, srcList);
881 void copyProtocols(objc_protocol_list_t<A>* dst, pint_t& dstIndex,
882 const objc_protocol_list_t<A>* src)
884 dst->overwrite(dstIndex, src);
890 InSet(std::set<void*>& deadPointers) : _deadPointers(deadPointers) {}
892 bool operator()(void* ptr) const {
893 return ( _deadPointers.count(ptr) != 0 );
897 std::set<void*>& _deadPointers;
902 CategoryAttacher(uint8_t *bytes, ssize_t bytesFree)
903 : mBytes(bytes), mBytesFree(bytesFree)
904 , mBytesUsed(0), mCategoriesAttached(0)
907 size_t count() const { return mCategoriesAttached; }
909 const char *optimize(SharedCache<A>* cache, macho_header<P>* header, std::vector<void*>& pointersInData)
911 // Build class=>cateories mapping.
912 // Disregard target classes that aren't in this binary.
916 PointerSection<A, objc_category_t<A> *>
917 nlcatsect(cache, header, "__DATA", "__objc_nlcatlist");
918 PointerSection<A, objc_category_t<A> *>
919 catsect(cache, header, "__DATA", "__objc_catlist");
920 for (pint_t i = 0; i < catsect.count(); i++) {
921 objc_category_t<A> *cat = catsect.get(i);
922 objc_class_t<A> *cls = cat->getClass(cache);
924 if (!headerContainsPointer(cache, header, cls)) continue;
925 if ( nlcatsect.count() !=0 ) {
926 // don't optimize categories also in __objc_nlcatlist
927 bool alsoInNlcatlist = false;
928 for (pint_t nli = 0; nli < nlcatsect.count(); nli++) {
929 if ( nlcatsect.get(nli) == cat ) {
930 //fprintf(stderr, "skipping cat in __objc_nlcatlist for mh=%p\n", header);
931 alsoInNlcatlist = true;
935 if ( alsoInNlcatlist )
939 // The LAST category found is the FIRST to be processed later.
940 map[cls].categories.push_front(cat);
942 // We don't care about the category reference order.
943 map[cls].catrefs.push_back(i);
946 if (map.size() == 0) {
947 // No attachable categories.
951 // Process each class.
952 // Each class is all-or-nothing: either all of its categories
953 // are attached successfully, or none of them are. This preserves
954 // cache validity if we run out of space for more reallocations.
956 // unusedMemory stores memory ranges evacuated by now-unused metadata.
957 // It is available for re-use by other newly-added metadata.
958 // fixme could packing algorithm be improved?
959 RangeArray unusedMemory;
963 // First: build new aggregated lists on the heap.
964 // Require enough space in mBytes for all of it.
966 std::set<void*> pointersToRemove;
967 for (typename ClassMap::iterator i = map.begin();
971 objc_class_t<A>* cls = i->first;
972 objc_class_t<A>* meta = cls->getIsa(cache);
973 ClassChanges& changes = i->second;
974 CategoryList& cats = changes.categories;
976 // Count memory needed for all categories on this class.
978 uint32_t methodEntsize = 0;
979 uint32_t propertyEntsize = 0;
980 objc_method_list_t<A>* mlist;
981 objc_property_list_t<A>* proplist;
982 objc_protocol_list_t<A>* protolist;
983 uint32_t instanceMethodsCount = 0;
984 uint32_t classMethodsCount = 0;
985 uint32_t instancePropertyCount = 0;
986 pint_t protocolCount = 0;
987 bool addedInstanceMethods = false;
988 bool addedClassMethods = false;
989 bool addedInstanceProperties = false;
990 bool addedProtocols = false;
992 mlist = cls->getMethodList(cache);
994 instanceMethodsCount = mlist->getCount();
996 std::max(methodEntsize, mlist->getEntsize());
999 mlist = meta->getMethodList(cache);
1001 classMethodsCount = mlist->getCount();
1003 std::max(methodEntsize, mlist->getEntsize());
1006 proplist = cls->getPropertyList(cache);
1008 instancePropertyCount = proplist->getCount();
1010 std::max(propertyEntsize, proplist->getEntsize());
1013 protolist = cls->getProtocolList(cache);
1015 protocolCount = protolist->getCount();
1018 typename CategoryList::iterator j;
1019 for (j = cats.begin(); j != cats.end(); ++j) {
1020 objc_category_t<A>* cat = *j;
1022 mlist = cat->getInstanceMethods(cache);
1023 if (mlist && mlist->getCount() > 0) {
1024 addedInstanceMethods = true;
1025 instanceMethodsCount += mlist->getCount();
1027 std::max(methodEntsize, mlist->getEntsize());
1030 mlist = cat->getClassMethods(cache);
1031 if (mlist && mlist->getCount() > 0) {
1032 addedClassMethods = true;
1033 classMethodsCount += mlist->getCount();
1035 std::max(methodEntsize, mlist->getEntsize());
1038 proplist = cat->getInstanceProperties(cache);
1039 if (proplist && proplist->getCount() > 0) {
1040 addedInstanceProperties = true;
1041 instancePropertyCount += proplist->getCount();
1043 std::max(propertyEntsize, proplist->getEntsize());
1046 protolist = cat->getProtocols(cache);
1047 if (protolist && protolist->getCount() > 0) {
1048 addedProtocols = true;
1049 protocolCount += protolist->getCount();
1053 // Allocate memory for aggregated lists.
1054 // Reserve the same amount of space from mBytes.
1056 if (addedInstanceMethods) {
1057 changes.instanceMethods = objc_method_list_t<A>::newMethodList(instanceMethodsCount, methodEntsize);
1058 reserve = P::round_up(reserve + changes.instanceMethods->byteSize());
1060 if (addedClassMethods) {
1061 changes.classMethods = objc_method_list_t<A>::newMethodList(classMethodsCount, methodEntsize);
1062 reserve = P::round_up(reserve + changes.classMethods->byteSize());
1064 if (addedInstanceProperties) {
1065 changes.instanceProperties = objc_property_list_t<A>::newPropertyList(instancePropertyCount, propertyEntsize);
1066 reserve = P::round_up(reserve + changes.instanceProperties->byteSize());
1068 if (addedProtocols) {
1069 changes.protocols = objc_protocol_list_t<A>::newProtocolList(protocolCount);
1070 reserve = P::round_up(reserve + changes.protocols->byteSize());
1073 // Merge. The LAST category's contents ends up FIRST in each list.
1074 // The aggregated lists are not sorted; a future pass does that.
1076 typename objc_method_list_t<A>::method_iterator newInstanceMethods;
1077 typename objc_method_list_t<A>::method_iterator newClassMethods;
1078 typename objc_property_list_t<A>::property_iterator newInstanceProperties;
1079 pint_t newProtocolIndex;
1081 if (addedInstanceMethods) {
1082 newInstanceMethods = changes.instanceMethods->begin();
1084 if (addedClassMethods) {
1085 newClassMethods = changes.classMethods->begin();
1087 if (addedInstanceProperties) {
1088 newInstanceProperties = changes.instanceProperties->begin();
1090 if (addedProtocols) {
1091 newProtocolIndex = 0;
1094 for (j = cats.begin(); j != cats.end(); ++j) {
1095 objc_category_t<A>* cat = *j;
1097 mlist = cat->getInstanceMethods(cache);
1099 copyMethods(newInstanceMethods, mlist);
1100 mlist->getPointers(pointersToRemove);
1101 unusedMemory.add(mlist, mlist->byteSize());
1104 mlist = cat->getClassMethods(cache);
1106 copyMethods(newClassMethods, mlist);
1107 mlist->getPointers(pointersToRemove);
1108 unusedMemory.add(mlist, mlist->byteSize());
1111 proplist = cat->getInstanceProperties(cache);
1113 copyProperties(newInstanceProperties, proplist);
1114 proplist->getPointers(pointersToRemove);
1115 unusedMemory.add(proplist, proplist->byteSize());
1118 protolist = cat->getProtocols(cache);
1120 copyProtocols(changes.protocols, newProtocolIndex, protolist);
1121 protolist->getPointers(pointersToRemove);
1122 unusedMemory.add(protolist, protolist->byteSize());
1125 cat->getPointers(pointersToRemove);
1126 unusedMemory.add(cat, sizeof(*cat));
1129 if (addedInstanceMethods && (mlist = cls->getMethodList(cache))) {
1130 copyMethods(newInstanceMethods, mlist);
1131 mlist->getPointers(pointersToRemove);
1132 unusedMemory.add(mlist, mlist->byteSize());
1134 if (addedClassMethods && (mlist = meta->getMethodList(cache))) {
1135 copyMethods(newClassMethods, mlist);
1136 mlist->getPointers(pointersToRemove);
1137 unusedMemory.add(mlist, mlist->byteSize());
1139 if (addedInstanceProperties && (proplist = cls->getPropertyList(cache))) {
1140 copyProperties(newInstanceProperties, proplist);
1141 proplist->getPointers(pointersToRemove);
1142 unusedMemory.add(proplist, proplist->byteSize());
1144 if (addedProtocols && (protolist = cls->getProtocolList(cache))) {
1145 copyProtocols(changes.protocols, newProtocolIndex, protolist);
1146 protolist->getPointers(pointersToRemove);
1147 unusedMemory.add(protolist, protolist->byteSize());
1151 if (reserve > mBytesFree) {
1152 return "insufficient space for category data (metadata not optimized)";
1155 // update cache slide info and remove areas now longer containing pointers
1156 //fprintf(stderr, "found %lu pointers in objc structures being moved\n", pointersToRemove.size());
1157 pointersInData.erase(std::remove_if(pointersInData.begin(), pointersInData.end(), InSet(pointersToRemove)), pointersInData.end());
1160 // All lists are now built.
1161 // mBytes is big enough to hold everything if necessary.
1162 // Everything in unusedMemory is now available for re-use.
1163 // The original metadata is still untouched.
1165 // Second: write lists into mBytes and unusedMemory,
1166 // then disconnect categories.
1168 for (typename ClassMap::iterator i = map.begin();
1172 objc_class_t<A>* cls = i->first;
1173 objc_class_t<A>* meta = cls->getIsa(cache);
1174 ClassChanges& changes = i->second;
1178 if (changes.instanceMethods) {
1180 uint32_t size = changes.instanceMethods->byteSize();
1181 if (! (bytes = unusedMemory.remove(size))) {
1182 bytes = mBytes + mBytesUsed;
1186 memcpy(bytes, changes.instanceMethods, size);
1187 objc_method_list_t<A>::addPointers(bytes, pointersInData);
1188 cls->setMethodList(cache, (objc_method_list_t<A> *)bytes);
1189 cls->addMethodListPointer(cache, pointersInData);
1192 if (changes.classMethods) {
1194 uint32_t size = changes.classMethods->byteSize();
1195 if (! (bytes = unusedMemory.remove(size))) {
1196 bytes = mBytes + mBytesUsed;
1200 memcpy(bytes, changes.classMethods, size);
1201 objc_method_list_t<A>::addPointers(bytes, pointersInData);
1202 meta->setMethodList(cache, (objc_method_list_t<A> *)bytes);
1203 meta->addMethodListPointer(cache, pointersInData);
1206 if (changes.instanceProperties) {
1208 uint32_t size = changes.instanceProperties->byteSize();
1209 if (! (bytes = unusedMemory.remove(size))) {
1210 bytes = mBytes + mBytesUsed;
1214 memcpy(bytes, changes.instanceProperties, size);
1215 objc_property_list_t<A>::addPointers(bytes, pointersInData);
1216 cls->setPropertyList(cache, (objc_property_list_t<A> *)bytes);
1217 cls->addPropertyListPointer(cache, pointersInData);
1220 if (changes.protocols) {
1222 uint32_t size = changes.protocols->byteSize();
1223 if (! (bytes = unusedMemory.remove(size))) {
1224 bytes = mBytes + mBytesUsed;
1228 memcpy(bytes, changes.protocols, size);
1229 cls->setProtocolList(cache, (objc_protocol_list_t<A> *)bytes);
1230 objc_protocol_list_t<A>::addPointers(bytes, pointersInData);
1231 cls->addProtocolListPointer(cache, pointersInData);
1232 meta->setProtocolList(cache, (objc_protocol_list_t<A> *)bytes);
1233 meta->addProtocolListPointer(cache, pointersInData);
1236 // Disavow all knowledge of the categories.
1238 for (typename CategoryRefs::iterator j = changes.catrefs.begin();
1239 j != changes.catrefs.end();
1245 mCategoriesAttached += changes.categories.size();
1248 catsect.removeNulls();
1253 ssize_t bytesUsed() { return mBytesUsed; }