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