ObjCModernAbstraction.hpp [plain text]
/* -*- mode: C++; c-basic-offset: 4; tab-width: 4 -*-
*
* Copyright (c) 2008-2010 Apple Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* This file contains Original Code and/or Modifications of Original Code
* as defined in and that are subject to the Apple Public Source License
* Version 2.0 (the 'License'). You may not use this file except in
* compliance with the License. Please obtain a copy of the License at
* http://www.opensource.apple.com/apsl/ and read it before using this
* file.
*
* The Original Code and all software distributed under the License are
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
* Please see the License for the specific language governing rights and
* limitations under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
#include <iterator>
#include <deque>
// iterate an entsize-based list
// typedef entsize_iterator< A, type_t<A>, type_list_t<A> > type_iterator;
template <typename A, typename T, typename Tlist>
struct entsize_iterator {
uint32_t entsize;
uint32_t index; // keeping track of this saves a divide in operator-
T* current;
typedef std::random_access_iterator_tag iterator_category;
typedef T value_type;
typedef ptrdiff_t difference_type;
typedef T* pointer;
typedef T& reference;
entsize_iterator() { }
entsize_iterator(const Tlist& list, uint32_t start = 0)
: entsize(list.getEntsize()), index(start), current(&list.get(start))
{ }
const entsize_iterator<A,T,Tlist>& operator += (ptrdiff_t count) {
current = (T*)((uint8_t *)current + count*entsize);
index += count;
return *this;
}
const entsize_iterator<A,T,Tlist>& operator -= (ptrdiff_t count) {
current = (T*)((uint8_t *)current - count*entsize);
index -= count;
return *this;
}
const entsize_iterator<A,T,Tlist> operator + (ptrdiff_t count) const {
return entsize_iterator(*this) += count;
}
const entsize_iterator<A,T,Tlist> operator - (ptrdiff_t count) const {
return entsize_iterator(*this) -= count;
}
entsize_iterator<A,T,Tlist>& operator ++ () { *this += 1; return *this; }
entsize_iterator<A,T,Tlist>& operator -- () { *this -= 1; return *this; }
entsize_iterator<A,T,Tlist> operator ++ (int) {
entsize_iterator<A,T,Tlist> result(*this); *this += 1; return result;
}
entsize_iterator<A,T,Tlist> operator -- (int) {
entsize_iterator<A,T,Tlist> result(*this); *this -= 1; return result;
}
ptrdiff_t operator - (const entsize_iterator<A,T,Tlist>& rhs) const {
return (ptrdiff_t)this->index - (ptrdiff_t)rhs.index;
}
T& operator * () { return *current; }
const T& operator * () const { return *current; }
T& operator -> () { return *current; }
const T& operator -> () const { return *current; }
operator T& () const { return *current; }
bool operator == (const entsize_iterator<A,T,Tlist>& rhs) {
return this->current == rhs.current;
}
bool operator != (const entsize_iterator<A,T,Tlist>& rhs) {
return this->current != rhs.current;
}
bool operator < (const entsize_iterator<A,T,Tlist>& rhs) {
return this->current < rhs.current;
}
bool operator > (const entsize_iterator<A,T,Tlist>& rhs) {
return this->current > rhs.current;
}
static void overwrite(entsize_iterator<A,T,Tlist>& dst, const Tlist* srcList)
{
entsize_iterator<A,T,Tlist> src;
uint32_t ee = srcList->getEntsize();
for (src = srcList->begin(); src != srcList->end(); ++src) {
memcpy(&*dst, &*src, ee);
++dst;
}
}
};
template <typename A> class objc_method_list_t; // forward reference
template <typename A>
class objc_method_t {
typename A::P::uint_t name; // SEL
typename A::P::uint_t types; // const char *
typename A::P::uint_t imp; // IMP
friend class objc_method_list_t<A>;
public:
typename A::P::uint_t getName() const { return A::P::getP(name); }
void setName(typename A::P::uint_t newName) { A::P::setP(name, newName); }
struct SortBySELAddress :
public std::binary_function<const objc_method_t<A>&,
const objc_method_t<A>&, bool>
{
bool operator() (const objc_method_t<A>& lhs,
const objc_method_t<A>& rhs)
{
return lhs.getName() < rhs.getName();
}
};
};
template <typename A>
class objc_method_list_t {
uint32_t entsize;
uint32_t count;
objc_method_t<A> first;
// use newMethodList instead
void* operator new (size_t) { return NULL; }
void* operator new (size_t, void* buf) { return buf; }
public:
typedef entsize_iterator< A, objc_method_t<A>, objc_method_list_t<A> > method_iterator;
uint32_t getCount() const { return A::P::E::get32(count); }
uint32_t getEntsize() const {return A::P::E::get32(entsize)&~(uint32_t)3;}
objc_method_t<A>& get(uint32_t i) const { return *(objc_method_t<A> *)((uint8_t *)&first + i * getEntsize()); }
uint32_t byteSize() const {
return byteSizeForCount(getCount(), getEntsize());
}
static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_method_t<A>)) {
return sizeof(objc_method_list_t<A>) - sizeof(objc_method_t<A>) + c*e;
}
method_iterator begin() { return method_iterator(*this, 0); }
method_iterator end() { return method_iterator(*this, getCount()); }
const method_iterator begin() const { return method_iterator(*this, 0); }
const method_iterator end() const { return method_iterator(*this, getCount()); }
void setFixedUp() { A::P::E::set32(entsize, getEntsize() | 3); }
void getPointers(std::set<void*>& pointersToRemove) {
for(method_iterator it = begin(); it != end(); ++it) {
objc_method_t<A>& entry = *it;
pointersToRemove.insert(&(entry.name));
pointersToRemove.insert(&(entry.types));
pointersToRemove.insert(&(entry.imp));
}
}
static void addPointers(uint8_t* methodList, std::vector<void*>& pointersToAdd) {
objc_method_list_t<A>* mlist = (objc_method_list_t<A>*)methodList;
for(method_iterator it = mlist->begin(); it != mlist->end(); ++it) {
objc_method_t<A>& entry = *it;
pointersToAdd.push_back(&(entry.name));
pointersToAdd.push_back(&(entry.types));
pointersToAdd.push_back(&(entry.imp));
}
}
static objc_method_list_t<A>* newMethodList(size_t newCount, uint32_t newEntsize) {
void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
return new (buf) objc_method_list_t<A>(newCount, newEntsize);
}
void operator delete(void * p) {
::free(p);
}
objc_method_list_t(uint32_t newCount,
uint32_t newEntsize = sizeof(objc_method_t<A>))
: entsize(newEntsize), count(newCount)
{ }
};
template <typename A>
class objc_ivar_t {
typename A::P::uint_t offset; // A::P *
typename A::P::uint_t name; // const char *
typename A::P::uint_t type; // const char *
uint32_t alignment;
uint32_t size;
};
template <typename A>
class objc_ivar_list_t {
uint32_t entsize;
uint32_t count;
objc_ivar_t<A> first;
// use newIvarList instead
void* operator new (size_t) { return NULL; }
void* operator new (size_t, void* buf) { return buf; }
public:
typedef entsize_iterator< A, objc_ivar_t<A>, objc_ivar_list_t<A> > ivar_iterator;
uint32_t getCount() const { return A::P::E::get32(count); }
uint32_t getEntsize() const { return A::P::E::get32(entsize); }
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)); }
uint32_t byteSize() const {
return byteSizeForCount(getCount(), getEntsize());
}
static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_ivar_t<A>)) {
return sizeof(objc_ivar_list_t<A>) - sizeof(objc_ivar_t<A>) + c*e;
}
ivar_iterator begin() { return ivar_iterator(*this, 0); }
ivar_iterator end() { return ivar_iterator(*this, getCount()); }
const ivar_iterator begin() const { return ivar_iterator(*this, 0); }
const ivar_iterator end() const { return ivar_iterator(*this, getCount()); }
static objc_ivar_list_t<A>* newIvarList(size_t newCount, uint32_t newEntsize) {
void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
return new (buf) objc_ivar_list_t<A>(newCount, newEntsize);
}
void operator delete(void * p) {
::free(p);
}
objc_ivar_list_t(uint32_t newCount,
uint32_t newEntsize = sizeof(objc_ivar_t<A>))
: entsize(newEntsize), count(newCount)
{ }
};
template <typename A> class objc_property_list_t; // forward
template <typename A>
class objc_property_t {
typename A::P::uint_t name;
typename A::P::uint_t attributes;
friend class objc_property_list_t<A>;
public:
const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
const char * getAttributes(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(attributes)); }
};
template <typename A>
class objc_property_list_t {
uint32_t entsize;
uint32_t count;
objc_property_t<A> first;
// use newPropertyList instead
void* operator new (size_t) { return NULL; }
void* operator new (size_t, void* buf) { return buf; }
public:
typedef entsize_iterator< A, objc_property_t<A>, objc_property_list_t<A> > property_iterator;
uint32_t getCount() const { return A::P::E::get32(count); }
uint32_t getEntsize() const { return A::P::E::get32(entsize); }
objc_property_t<A>& get(uint32_t i) const { return *(objc_property_t<A> *)((uint8_t *)&first + i * getEntsize()); }
uint32_t byteSize() const {
return byteSizeForCount(getCount(), getEntsize());
}
static uint32_t byteSizeForCount(uint32_t c, uint32_t e = sizeof(objc_property_t<A>)) {
return sizeof(objc_property_list_t<A>) - sizeof(objc_property_t<A>) + c*e;
}
property_iterator begin() { return property_iterator(*this, 0); }
property_iterator end() { return property_iterator(*this, getCount()); }
const property_iterator begin() const { return property_iterator(*this, 0); }
const property_iterator end() const { return property_iterator(*this, getCount()); }
void getPointers(std::set<void*>& pointersToRemove) {
for(property_iterator it = begin(); it != end(); ++it) {
objc_property_t<A>& entry = *it;
pointersToRemove.insert(&(entry.name));
pointersToRemove.insert(&(entry.attributes));
}
}
static void addPointers(uint8_t* propertyList, std::vector<void*>& pointersToAdd) {
objc_property_list_t<A>* plist = (objc_property_list_t<A>*)propertyList;
for(property_iterator it = plist->begin(); it != plist->end(); ++it) {
objc_property_t<A>& entry = *it;
pointersToAdd.push_back(&(entry.name));
pointersToAdd.push_back(&(entry.attributes));
}
}
static objc_property_list_t<A>* newPropertyList(size_t newCount, uint32_t newEntsize) {
void *buf = ::calloc(byteSizeForCount(newCount, newEntsize), 1);
return new (buf) objc_property_list_t<A>(newCount, newEntsize);
}
void operator delete(void * p) {
::free(p);
}
objc_property_list_t(uint32_t newCount,
uint32_t newEntsize = sizeof(objc_property_t<A>))
: entsize(newEntsize), count(newCount)
{ }
};
template <typename A>
class objc_protocol_t {
typename A::P::uint_t isa;
typename A::P::uint_t name;
typename A::P::uint_t protocols;
typename A::P::uint_t instanceMethods;
typename A::P::uint_t classMethods;
typename A::P::uint_t optionalInstanceMethods;
typename A::P::uint_t optionalClassMethods;
typename A::P::uint_t instanceProperties;
public:
objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
objc_method_list_t<A> *getOptionalInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalInstanceMethods)); }
objc_method_list_t<A> *getOptionalClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(optionalClassMethods)); }
};
template <typename A>
class objc_protocol_list_t {
typedef typename A::P::uint_t pint_t;
pint_t count;
pint_t list[0];
// use newProtocolList instead
void* operator new (size_t) { return NULL; }
void* operator new (size_t, void* buf) { return buf; }
public:
pint_t getCount() const { return A::P::getP(count); }
objc_protocol_t<A>* get(SharedCache<A>* cache, pint_t i) {
return (objc_protocol_t<A>*)cache->mappedAddressForVMAddress(A::P::getP(list[i]));
}
void overwrite(pint_t& index, const objc_protocol_list_t<A>* src) {
pint_t srcCount = src->getCount();
memcpy(list+index, src->list, srcCount * sizeof(pint_t));
index += srcCount;
}
uint32_t byteSize() const {
return byteSizeForCount(getCount());
}
static uint32_t byteSizeForCount(pint_t c) {
return sizeof(objc_protocol_list_t<A>) + c*sizeof(pint_t);
}
void getPointers(std::set<void*>& pointersToRemove) {
for(int i=0 ; i < count; ++i) {
pointersToRemove.insert(&list[i]);
}
}
static void addPointers(uint8_t* protocolList, std::vector<void*>& pointersToAdd) {
objc_protocol_list_t<A>* plist = (objc_protocol_list_t<A>*)protocolList;
for(int i=0 ; i < plist->count; ++i) {
pointersToAdd.push_back(&plist->list[i]);
}
}
static objc_protocol_list_t<A>* newProtocolList(pint_t newCount) {
void *buf = ::calloc(byteSizeForCount(newCount), 1);
return new (buf) objc_protocol_list_t<A>(newCount);
}
void operator delete(void * p) {
::free(p);
}
objc_protocol_list_t(uint32_t newCount) : count(newCount) { }
};
template <typename A>
class objc_class_data_t {
uint32_t flags;
uint32_t instanceStart;
// Note there is 4-bytes of alignment padding between instanceSize and ivarLayout
// on 64-bit archs, but no padding on 32-bit archs.
// This union is a way to model that.
union {
uint32_t instanceSize;
typename A::P::uint_t pad;
} instanceSize;
typename A::P::uint_t ivarLayout;
typename A::P::uint_t name;
typename A::P::uint_t baseMethods;
typename A::P::uint_t baseProtocols;
typename A::P::uint_t ivars;
typename A::P::uint_t weakIvarLayout;
typename A::P::uint_t baseProperties;
public:
objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseMethods)); }
objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProtocols)); }
objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(baseProperties)); }
const char * getName(SharedCache<A>* cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
A::P::setP(baseMethods, cache->VMAddressForMappedAddress(mlist));
}
void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
A::P::setP(baseProtocols, cache->VMAddressForMappedAddress(protolist));
}
void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
A::P::setP(baseProperties, cache->VMAddressForMappedAddress(proplist));
}
void addMethodListPointer(std::vector<void*>& pointersToAdd) {
pointersToAdd.push_back(&this->baseMethods);
}
void addPropertyListPointer(std::vector<void*>& pointersToAdd) {
pointersToAdd.push_back(&this->baseProperties);
}
void addProtocolListPointer(std::vector<void*>& pointersToAdd) {
pointersToAdd.push_back(&this->baseProtocols);
}
};
template <typename A>
class objc_class_t {
typename A::P::uint_t isa;
typename A::P::uint_t superclass;
typename A::P::uint_t method_cache;
typename A::P::uint_t vtable;
typename A::P::uint_t data;
public:
objc_class_t<A> *getIsa(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(isa)); }
objc_class_data_t<A> *getData(SharedCache<A>* cache) const { return (objc_class_data_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(data)); }
objc_method_list_t<A> *getMethodList(SharedCache<A>* cache) const { return getData(cache)->getMethodList(cache); }
objc_protocol_list_t<A> *getProtocolList(SharedCache<A>* cache) const { return getData(cache)->getProtocolList(cache); }
objc_property_list_t<A> *getPropertyList(SharedCache<A>* cache) const { return getData(cache)->getPropertyList(cache); }
const char * getName(SharedCache<A>* cache) const {
return getData(cache)->getName(cache);
}
void setMethodList(SharedCache<A>* cache, objc_method_list_t<A>* mlist) {
getData(cache)->setMethodList(cache, mlist);
}
void setProtocolList(SharedCache<A>* cache, objc_protocol_list_t<A>* protolist) {
getData(cache)->setProtocolList(cache, protolist);
}
void setPropertyList(SharedCache<A>* cache, objc_property_list_t<A>* proplist) {
getData(cache)->setPropertyList(cache, proplist);
}
void addMethodListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
getData(cache)->addMethodListPointer(pointersToAdd);
}
void addPropertyListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
getData(cache)->addPropertyListPointer(pointersToAdd);
}
void addProtocolListPointer(SharedCache<A>* cache, std::vector<void*>& pointersToAdd) {
getData(cache)->addProtocolListPointer(pointersToAdd);
}
};
template <typename A>
class objc_category_t {
typename A::P::uint_t name;
typename A::P::uint_t cls;
typename A::P::uint_t instanceMethods;
typename A::P::uint_t classMethods;
typename A::P::uint_t protocols;
typename A::P::uint_t instanceProperties;
public:
const char * getName(SharedCache<A> *cache) const { return (const char *)cache->mappedAddressForVMAddress(A::P::getP(name)); }
objc_class_t<A> *getClass(SharedCache<A> *cache) const { return (objc_class_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(cls)); }
objc_method_list_t<A> *getInstanceMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceMethods)); }
objc_method_list_t<A> *getClassMethods(SharedCache<A>* cache) const { return (objc_method_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(classMethods)); }
objc_protocol_list_t<A> *getProtocols(SharedCache<A>* cache) const { return (objc_protocol_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(protocols)); }
objc_property_list_t<A> *getInstanceProperties(SharedCache<A>* cache) const { return (objc_property_list_t<A> *)cache->mappedAddressForVMAddress(A::P::getP(instanceProperties)); }
void getPointers(std::set<void*>& pointersToRemove) {
pointersToRemove.insert(&name);
pointersToRemove.insert(&cls);
pointersToRemove.insert(&instanceMethods);
pointersToRemove.insert(&classMethods);
pointersToRemove.insert(&protocols);
pointersToRemove.insert(&instanceProperties);
}
};
template <typename A>
class objc_message_ref_t {
typename A::P::uint_t imp;
typename A::P::uint_t sel;
public:
typename A::P::uint_t getName() const { return A::P::getP(sel); }
void setName(typename A::P::uint_t newName) { A::P::setP(sel, newName); }
};
// Call visitor.visitMethodList(mlist) on every method list in a header.
template <typename A, typename V>
class MethodListWalker {
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
V& mVisitor;
public:
MethodListWalker(V& visitor) : mVisitor(visitor) { }
void walk(SharedCache<A>* cache, const macho_header<P>* header)
{
// Method lists in classes
PointerSection<A, objc_class_t<A> *>
classes(cache, header, "__DATA", "__objc_classlist");
for (pint_t i = 0; i < classes.count(); i++) {
objc_class_t<A> *cls = classes.get(i);
objc_method_list_t<A> *mlist;
if ((mlist = cls->getMethodList(cache))) {
mVisitor.visitMethodList(mlist);
}
if ((mlist = cls->getIsa(cache)->getMethodList(cache))) {
mVisitor.visitMethodList(mlist);
}
}
// Method lists from categories
PointerSection<A, objc_category_t<A> *>
cats(cache, header, "__DATA", "__objc_catlist");
for (pint_t i = 0; i < cats.count(); i++) {
objc_category_t<A> *cat = cats.get(i);
objc_method_list_t<A> *mlist;
if ((mlist = cat->getInstanceMethods(cache))) {
mVisitor.visitMethodList(mlist);
}
if ((mlist = cat->getClassMethods(cache))) {
mVisitor.visitMethodList(mlist);
}
}
// Method description lists from protocols
PointerSection<A, objc_protocol_t<A> *>
protocols(cache, header, "__DATA", "__objc_protolist");
for (pint_t i = 0; i < protocols.count(); i++) {
objc_protocol_t<A> *proto = protocols.get(i);
objc_method_list_t<A> *mlist;
if ((mlist = proto->getInstanceMethods(cache))) {
mVisitor.visitMethodList(mlist);
}
if ((mlist = proto->getClassMethods(cache))) {
mVisitor.visitMethodList(mlist);
}
if ((mlist = proto->getOptionalInstanceMethods(cache))) {
mVisitor.visitMethodList(mlist);
}
if ((mlist = proto->getOptionalClassMethods(cache))) {
mVisitor.visitMethodList(mlist);
}
}
}
};
// Update selector references. The visitor performs recording and uniquing.
template <typename A, typename V>
class SelectorOptimizer {
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
V& mVisitor;
friend class MethodListWalker< A, SelectorOptimizer<A,V> >;
void visitMethodList(objc_method_list_t<A> *mlist)
{
// Gather selectors. Update method names.
for (pint_t m = 0; m < mlist->getCount(); m++) {
pint_t oldValue = mlist->get(m).getName();
pint_t newValue = mVisitor.visit(oldValue);
mlist->get(m).setName(newValue);
}
// Do not setFixedUp: the methods are not yet sorted.
}
public:
SelectorOptimizer(V& visitor) : mVisitor(visitor) { }
void optimize(SharedCache<A>* cache, const macho_header<P>* header)
{
// method lists of all kinds
MethodListWalker< A, SelectorOptimizer<A,V> > mw(*this);
mw.walk(cache, header);
// @selector references
PointerSection<A, const char *>
selrefs(cache, header, "__DATA", "__objc_selrefs");
for (pint_t i = 0; i < selrefs.count(); i++) {
pint_t oldValue = selrefs.getUnmapped(i);
pint_t newValue = mVisitor.visit(oldValue);
selrefs.set(i, newValue);
}
// message references
ArraySection<A, objc_message_ref_t<A> >
msgrefs(cache, header, "__DATA", "__objc_msgrefs");
for (pint_t i = 0; i < msgrefs.count(); i++) {
objc_message_ref_t<A>& msg = msgrefs.get(i);
pint_t oldValue = msg.getName();
pint_t newValue = mVisitor.visit(oldValue);
msg.setName(newValue);
}
// Mark image_info
const macho_section<P> *imageInfoSection =
header->getSection("__DATA", "__objc_imageinfo");
if (imageInfoSection) {
objc_image_info<A> *info = (objc_image_info<A> *)
cache->mappedAddressForVMAddress(imageInfoSection->addr());
info->setSelectorsPrebound();
}
}
};
// Sort methods in place by selector.
template <typename A>
class MethodListSorter {
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
friend class MethodListWalker<A, MethodListSorter<A> >;
void visitMethodList(objc_method_list_t<A> *mlist)
{
typename objc_method_t<A>::SortBySELAddress sorter;
std::stable_sort(mlist->begin(), mlist->end(), sorter);
mlist->setFixedUp();
}
public:
void optimize(SharedCache<A>* cache, macho_header<P>* header)
{
MethodListWalker<A, MethodListSorter<A> > mw(*this);
mw.walk(cache, header);
}
};
// Attach categories to classes in the same framework.
// Merge method and protocol and property lists.
template <typename A>
class CategoryAttacher {
typedef typename A::P P;
typedef typename A::P::uint_t pint_t;
uint8_t *mBytes;
ssize_t mBytesFree;
ssize_t mBytesUsed;
size_t mCategoriesAttached;
bool segmentContainsPointer(SharedCache<A>* cache,
const macho_segment_command<P>* seg, void *ptr)
{
if (!seg) return false;
void *start = (void*)
cache->mappedAddressForVMAddress(seg->vmaddr());
void *end = (uint8_t *)start + seg->filesize();
return (ptr >= start && ptr < end);
}
bool headerContainsPointer(SharedCache<A>* cache,
macho_header<P>* header, void *ptr)
{
return
segmentContainsPointer(cache, header->getSegment("__DATA"), ptr) ||
segmentContainsPointer(cache, header->getSegment("__TEXT"), ptr) ||
segmentContainsPointer(cache, header->getSegment("__OBJC"), ptr);
}
struct pointer_hash {
size_t operator () (void* ptr) const {
return __gnu_cxx::hash<long>()((long)ptr);
}
};
typedef std::deque<objc_category_t<A>*> CategoryList;
typedef std::vector<uint64_t> CategoryRefs;
struct ClassChanges {
CategoryList categories;
CategoryRefs catrefs;
objc_method_list_t<A>* instanceMethods;
objc_method_list_t<A>* classMethods;
objc_protocol_list_t<A>* protocols;
objc_property_list_t<A>* instanceProperties;
ClassChanges()
: instanceMethods(NULL), classMethods(NULL),
protocols(NULL), instanceProperties(NULL)
{ }
~ClassChanges() {
if (instanceMethods) delete instanceMethods;
if (classMethods) delete classMethods;
if (protocols) delete protocols;
if (instanceProperties) delete instanceProperties;
}
};
typedef __gnu_cxx::hash_map<objc_class_t<A>*, ClassChanges, pointer_hash> ClassMap;
class RangeArray {
typedef std::pair<uint8_t*,uint32_t> Range;
std::deque<Range> ranges;
class SizeFits {
private:
uint32_t mSize;
public:
SizeFits(uint32_t size) : mSize(size) { }
bool operator() (const Range& r) {
return r.second >= mSize;
}
};
struct AddressComp {
bool operator() (const Range& lhs, const Range& rhs) {
return (lhs.first < rhs.first);
}
};
public:
RangeArray() { }
void add(void* p, uint32_t size) {
add(Range((uint8_t *)p, size));
}
void add(const Range& r) {
// find insertion point
std::deque<Range>::iterator iter;
iter = upper_bound(ranges.begin(), ranges.end(), r, AddressComp());
// coalesce
// fixme doesn't fully coalesce if new range exactly fills a gap
if (iter != ranges.begin()) {
std::deque<Range>::iterator prev = iter - 1;
if ((*prev).first + (*prev).second == r.first) {
(*prev).second += r.second;
return;
}
}
if (iter != ranges.end() && iter+1 != ranges.end()) {
std::deque<Range>::iterator next = iter + 1;
if (r.first + r.second == (*next).first) {
(*next).second += r.second;
(*next).first = r.first;
return;
}
}
ranges.insert(iter, r);
}
uint8_t* remove(uint32_t size) {
// first-fit search
// this saves 50-75% of space overhead;
// a better algorithm might do better
std::deque<Range>::iterator iter;
iter = find_if(ranges.begin(), ranges.end(), SizeFits(size));
if (iter == ranges.end()) {
return NULL;
}
Range& found = *iter;
uint8_t *result = found.first;
if (found.second > size) {
// keep leftovers
found.first += size;
found.second -= size;
} else {
ranges.erase(iter);
}
return result;
}
};
void copyMethods(typename objc_method_list_t<A>::method_iterator& dst,
const objc_method_list_t<A>* srcList)
{
objc_method_list_t<A>::method_iterator::
overwrite(dst, srcList);
}
void copyProperties(typename objc_property_list_t<A>::property_iterator& dst,
const objc_property_list_t<A>* srcList)
{
objc_property_list_t<A>::property_iterator::
overwrite(dst, srcList);
}
void copyProtocols(objc_protocol_list_t<A>* dst, pint_t& dstIndex,
const objc_protocol_list_t<A>* src)
{
dst->overwrite(dstIndex, src);
}
class InSet
{
public:
InSet(std::set<void*>& deadPointers) : _deadPointers(deadPointers) {}
bool operator()(void* ptr) const {
return ( _deadPointers.count(ptr) != 0 );
}
private:
std::set<void*>& _deadPointers;
};
public:
CategoryAttacher(uint8_t *bytes, ssize_t bytesFree)
: mBytes(bytes), mBytesFree(bytesFree)
, mBytesUsed(0), mCategoriesAttached(0)
{ }
size_t count() const { return mCategoriesAttached; }
const char *optimize(SharedCache<A>* cache, macho_header<P>* header, std::vector<void*>& pointersInData)
{
// Build class=>cateories mapping.
// Disregard target classes that aren't in this binary.
ClassMap map;
PointerSection<A, objc_category_t<A> *>
nlcatsect(cache, header, "__DATA", "__objc_nlcatlist");
PointerSection<A, objc_category_t<A> *>
catsect(cache, header, "__DATA", "__objc_catlist");
for (pint_t i = 0; i < catsect.count(); i++) {
objc_category_t<A> *cat = catsect.get(i);
objc_class_t<A> *cls = cat->getClass(cache);
if (!cls) continue;
if (!headerContainsPointer(cache, header, cls)) continue;
if ( nlcatsect.count() !=0 ) {
// don't optimize categories also in __objc_nlcatlist
bool alsoInNlcatlist = false;
for (pint_t nli = 0; nli < nlcatsect.count(); nli++) {
if ( nlcatsect.get(nli) == cat ) {
//fprintf(stderr, "skipping cat in __objc_nlcatlist for mh=%p\n", header);
alsoInNlcatlist = true;
break;
}
}
if ( alsoInNlcatlist )
continue;
}
// The LAST category found is the FIRST to be processed later.
map[cls].categories.push_front(cat);
// We don't care about the category reference order.
map[cls].catrefs.push_back(i);
}
if (map.size() == 0) {
// No attachable categories.
return NULL;
}
// Process each class.
// Each class is all-or-nothing: either all of its categories
// are attached successfully, or none of them are. This preserves
// cache validity if we run out of space for more reallocations.
// unusedMemory stores memory ranges evacuated by now-unused metadata.
// It is available for re-use by other newly-added metadata.
// fixme could packing algorithm be improved?
RangeArray unusedMemory;
ssize_t reserve = 0;
// First: build new aggregated lists on the heap.
// Require enough space in mBytes for all of it.
std::set<void*> pointersToRemove;
for (typename ClassMap::iterator i = map.begin();
i != map.end();
++i)
{
objc_class_t<A>* cls = i->first;
objc_class_t<A>* meta = cls->getIsa(cache);
ClassChanges& changes = i->second;
CategoryList& cats = changes.categories;
// Count memory needed for all categories on this class.
uint32_t methodEntsize = 0;
uint32_t propertyEntsize = 0;
objc_method_list_t<A>* mlist;
objc_property_list_t<A>* proplist;
objc_protocol_list_t<A>* protolist;
uint32_t instanceMethodsCount = 0;
uint32_t classMethodsCount = 0;
uint32_t instancePropertyCount = 0;
pint_t protocolCount = 0;
bool addedInstanceMethods = false;
bool addedClassMethods = false;
bool addedInstanceProperties = false;
bool addedProtocols = false;
mlist = cls->getMethodList(cache);
if (mlist) {
instanceMethodsCount = mlist->getCount();
methodEntsize =
std::max(methodEntsize, mlist->getEntsize());
}
mlist = meta->getMethodList(cache);
if (mlist) {
classMethodsCount = mlist->getCount();
methodEntsize =
std::max(methodEntsize, mlist->getEntsize());
}
proplist = cls->getPropertyList(cache);
if (proplist) {
instancePropertyCount = proplist->getCount();
propertyEntsize =
std::max(propertyEntsize, proplist->getEntsize());
}
protolist = cls->getProtocolList(cache);
if (protolist) {
protocolCount = protolist->getCount();
}
typename CategoryList::iterator j;
for (j = cats.begin(); j != cats.end(); ++j) {
objc_category_t<A>* cat = *j;
mlist = cat->getInstanceMethods(cache);
if (mlist && mlist->getCount() > 0) {
addedInstanceMethods = true;
instanceMethodsCount += mlist->getCount();
methodEntsize =
std::max(methodEntsize, mlist->getEntsize());
}
mlist = cat->getClassMethods(cache);
if (mlist && mlist->getCount() > 0) {
addedClassMethods = true;
classMethodsCount += mlist->getCount();
methodEntsize =
std::max(methodEntsize, mlist->getEntsize());
}
proplist = cat->getInstanceProperties(cache);
if (proplist && proplist->getCount() > 0) {
addedInstanceProperties = true;
instancePropertyCount += proplist->getCount();
propertyEntsize =
std::max(propertyEntsize, proplist->getEntsize());
}
protolist = cat->getProtocols(cache);
if (protolist && protolist->getCount() > 0) {
addedProtocols = true;
protocolCount += protolist->getCount();
}
}
// Allocate memory for aggregated lists.
// Reserve the same amount of space from mBytes.
if (addedInstanceMethods) {
changes.instanceMethods = objc_method_list_t<A>::newMethodList(instanceMethodsCount, methodEntsize);
reserve = P::round_up(reserve + changes.instanceMethods->byteSize());
}
if (addedClassMethods) {
changes.classMethods = objc_method_list_t<A>::newMethodList(classMethodsCount, methodEntsize);
reserve = P::round_up(reserve + changes.classMethods->byteSize());
}
if (addedInstanceProperties) {
changes.instanceProperties = objc_property_list_t<A>::newPropertyList(instancePropertyCount, propertyEntsize);
reserve = P::round_up(reserve + changes.instanceProperties->byteSize());
}
if (addedProtocols) {
changes.protocols = objc_protocol_list_t<A>::newProtocolList(protocolCount);
reserve = P::round_up(reserve + changes.protocols->byteSize());
}
// Merge. The LAST category's contents ends up FIRST in each list.
// The aggregated lists are not sorted; a future pass does that.
typename objc_method_list_t<A>::method_iterator newInstanceMethods;
typename objc_method_list_t<A>::method_iterator newClassMethods;
typename objc_property_list_t<A>::property_iterator newInstanceProperties;
pint_t newProtocolIndex;
if (addedInstanceMethods) {
newInstanceMethods = changes.instanceMethods->begin();
}
if (addedClassMethods) {
newClassMethods = changes.classMethods->begin();
}
if (addedInstanceProperties) {
newInstanceProperties = changes.instanceProperties->begin();
}
if (addedProtocols) {
newProtocolIndex = 0;
}
for (j = cats.begin(); j != cats.end(); ++j) {
objc_category_t<A>* cat = *j;
mlist = cat->getInstanceMethods(cache);
if (mlist) {
copyMethods(newInstanceMethods, mlist);
mlist->getPointers(pointersToRemove);
unusedMemory.add(mlist, mlist->byteSize());
}
mlist = cat->getClassMethods(cache);
if (mlist) {
copyMethods(newClassMethods, mlist);
mlist->getPointers(pointersToRemove);
unusedMemory.add(mlist, mlist->byteSize());
}
proplist = cat->getInstanceProperties(cache);
if (proplist) {
copyProperties(newInstanceProperties, proplist);
proplist->getPointers(pointersToRemove);
unusedMemory.add(proplist, proplist->byteSize());
}
protolist = cat->getProtocols(cache);
if (protolist) {
copyProtocols(changes.protocols, newProtocolIndex, protolist);
protolist->getPointers(pointersToRemove);
unusedMemory.add(protolist, protolist->byteSize());
}
cat->getPointers(pointersToRemove);
unusedMemory.add(cat, sizeof(*cat));
}
if (addedInstanceMethods && (mlist = cls->getMethodList(cache))) {
copyMethods(newInstanceMethods, mlist);
mlist->getPointers(pointersToRemove);
unusedMemory.add(mlist, mlist->byteSize());
}
if (addedClassMethods && (mlist = meta->getMethodList(cache))) {
copyMethods(newClassMethods, mlist);
mlist->getPointers(pointersToRemove);
unusedMemory.add(mlist, mlist->byteSize());
}
if (addedInstanceProperties && (proplist = cls->getPropertyList(cache))) {
copyProperties(newInstanceProperties, proplist);
proplist->getPointers(pointersToRemove);
unusedMemory.add(proplist, proplist->byteSize());
}
if (addedProtocols && (protolist = cls->getProtocolList(cache))) {
copyProtocols(changes.protocols, newProtocolIndex, protolist);
protolist->getPointers(pointersToRemove);
unusedMemory.add(protolist, protolist->byteSize());
}
}
if (reserve > mBytesFree) {
return "insufficient space for category data (metadata not optimized)";
}
// update cache slide info and remove areas now longer containing pointers
//fprintf(stderr, "found %lu pointers in objc structures being moved\n", pointersToRemove.size());
pointersInData.erase(std::remove_if(pointersInData.begin(), pointersInData.end(), InSet(pointersToRemove)), pointersInData.end());
// All lists are now built.
// mBytes is big enough to hold everything if necessary.
// Everything in unusedMemory is now available for re-use.
// The original metadata is still untouched.
// Second: write lists into mBytes and unusedMemory,
// then disconnect categories.
for (typename ClassMap::iterator i = map.begin();
i != map.end();
++i)
{
objc_class_t<A>* cls = i->first;
objc_class_t<A>* meta = cls->getIsa(cache);
ClassChanges& changes = i->second;
// Write lists.
if (changes.instanceMethods) {
uint8_t *bytes;
uint32_t size = changes.instanceMethods->byteSize();
if (! (bytes = unusedMemory.remove(size))) {
bytes = mBytes + mBytesUsed;
mBytesFree -= size;
mBytesUsed += size;
}
memcpy(bytes, changes.instanceMethods, size);
objc_method_list_t<A>::addPointers(bytes, pointersInData);
cls->setMethodList(cache, (objc_method_list_t<A> *)bytes);
cls->addMethodListPointer(cache, pointersInData);
}
if (changes.classMethods) {
uint8_t *bytes;
uint32_t size = changes.classMethods->byteSize();
if (! (bytes = unusedMemory.remove(size))) {
bytes = mBytes + mBytesUsed;
mBytesFree -= size;
mBytesUsed += size;
}
memcpy(bytes, changes.classMethods, size);
objc_method_list_t<A>::addPointers(bytes, pointersInData);
meta->setMethodList(cache, (objc_method_list_t<A> *)bytes);
meta->addMethodListPointer(cache, pointersInData);
}
if (changes.instanceProperties) {
uint8_t *bytes;
uint32_t size = changes.instanceProperties->byteSize();
if (! (bytes = unusedMemory.remove(size))) {
bytes = mBytes + mBytesUsed;
mBytesFree -= size;
mBytesUsed += size;
}
memcpy(bytes, changes.instanceProperties, size);
objc_property_list_t<A>::addPointers(bytes, pointersInData);
cls->setPropertyList(cache, (objc_property_list_t<A> *)bytes);
cls->addPropertyListPointer(cache, pointersInData);
}
if (changes.protocols) {
uint8_t *bytes;
uint32_t size = changes.protocols->byteSize();
if (! (bytes = unusedMemory.remove(size))) {
bytes = mBytes + mBytesUsed;
mBytesFree -= size;
mBytesUsed += size;
}
memcpy(bytes, changes.protocols, size);
cls->setProtocolList(cache, (objc_protocol_list_t<A> *)bytes);
objc_protocol_list_t<A>::addPointers(bytes, pointersInData);
cls->addProtocolListPointer(cache, pointersInData);
meta->setProtocolList(cache, (objc_protocol_list_t<A> *)bytes);
meta->addProtocolListPointer(cache, pointersInData);
}
// Disavow all knowledge of the categories.
for (typename CategoryRefs::iterator j = changes.catrefs.begin();
j != changes.catrefs.end();
++j)
{
catsect.set(*j, 0);
}
mCategoriesAttached += changes.categories.size();
}
catsect.removeNulls();
return NULL;
}
ssize_t bytesUsed() { return mBytesUsed; }
};