/* * Copyright (c) 2000-2016 Apple Inc. All rights reserved. * * @APPLE_OSREFERENCE_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. The rights granted to you under the License * may not be used to create, or enable the creation or redistribution of, * unlawful or unlicensed copies of an Apple operating system, or to * circumvent, violate, or enable the circumvention or violation of, any * terms of an Apple operating system software license agreement. * * 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_OSREFERENCE_LICENSE_HEADER_END@ */ /* OSMetaClass.cpp created by gvdl on Fri 1998-11-17 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include __BEGIN_DECLS #include #include #include #include #include #include #include #include #if PRAGMA_MARK #pragma mark Macros #endif /* PRAGMA_MARK */ /********************************************************************* * Macros *********************************************************************/ __END_DECLS #if PRAGMA_MARK #pragma mark Internal constants & data structs #endif /* PRAGMA_MARK */ /********************************************************************* * Internal constants & data structs *********************************************************************/ OSKextLogSpec kOSMetaClassLogSpec = kOSKextLogErrorLevel | kOSKextLogLoadFlag | kOSKextLogKextBookkeepingFlag; static enum { kCompletedBootstrap = 0, kNoDictionaries = 1, kMakingDictionaries = 2 } sBootstrapState = kNoDictionaries; static const int kClassCapacityIncrement = 40; static const int kKModCapacityIncrement = 10; static OSDictionary * sAllClassesDict; static unsigned int sDeepestClass; IOLock * sAllClassesLock = NULL; IOLock * sInstancesLock = NULL; /* * While loading a kext and running all its constructors to register * all OSMetaClass classes, the classes are queued up here. Only one * kext can be in flight at a time, guarded by sStalledClassesLock */ static struct StalledData { const char * kextIdentifier; OSReturn result; unsigned int capacity; unsigned int count; OSMetaClass ** classes; } * sStalled; IOLock * sStalledClassesLock = NULL; struct ExpansionData { OSOrderedSet * instances; OSKext * kext; uint32_t retain; #if IOTRACKING IOTrackingQueue * tracking; #endif }; #if PRAGMA_MARK #pragma mark OSMetaClassBase #endif /* PRAGMA_MARK */ /********************************************************************* * OSMetaClassBase. *********************************************************************/ #if APPLE_KEXT_VTABLE_PADDING /********************************************************************* * Reserved vtable functions. *********************************************************************/ #if defined(__arm64__) || defined(__arm__) void OSMetaClassBase::_RESERVEDOSMetaClassBase0() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 0); } void OSMetaClassBase::_RESERVEDOSMetaClassBase1() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 1); } void OSMetaClassBase::_RESERVEDOSMetaClassBase2() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 2); } void OSMetaClassBase::_RESERVEDOSMetaClassBase3() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 3); } #endif /* defined(__arm64__) || defined(__arm__) */ // As these slots are used move them up inside the #if above void OSMetaClassBase::_RESERVEDOSMetaClassBase4() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 4); } void OSMetaClassBase::_RESERVEDOSMetaClassBase5() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 5); } void OSMetaClassBase::_RESERVEDOSMetaClassBase6() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 6); } #endif /********************************************************************* *********************************************************************/ #if defined(__arm__) || defined(__arm64__) #if defined(HAS_APPLE_PAC) #include #endif /* defined(HAS_APPLE_PAC) */ /* * IHI0059A "C++ Application Binary Interface Standard for the ARM 64 - bit Architecture": * * 3.2.1 Representation of pointer to member function The generic C++ ABI [GC++ABI] * specifies that a pointer to member function is a pair of words . The * least significant bit of ptr discriminates between (0) the address of a non- * virtual member function and (1) the offset in the class's virtual table of the * address of a virtual function. This encoding cannot work for the AArch64 * instruction set where the architecture reserves all bits of code addresses. This * ABI specifies that adj contains twice the this adjustment, plus 1 if the member * function is virtual. The least significant bit of adj then makes exactly the * same discrimination as the least significant bit of ptr does for Itanium. A * pointer to member function is NULL when ptr = 0 and the least significant bit of * adj is zero. */ OSMetaClassBase::_ptf_t #if defined(HAS_APPLE_PAC) && \ __has_feature(ptrauth_member_function_pointer_type_discrimination) OSMetaClassBase::_ptmf2ptf(const OSMetaClassBase *self __attribute__((unused)), void (OSMetaClassBase::*func)(void)) #else OSMetaClassBase::_ptmf2ptf(const OSMetaClassBase *self, void (OSMetaClassBase::*func)(void)) #endif { struct ptmf_t { _ptf_t fPFN; ptrdiff_t delta; }; union { void (OSMetaClassBase::*fIn)(void); struct ptmf_t pTMF; } map; _ptf_t pfn; map.fIn = func; pfn = map.pTMF.fPFN; #if defined(HAS_APPLE_PAC) && \ __has_feature(ptrauth_member_function_pointer_type_discrimination) // Authenticate 'pfn' using the member function pointer type discriminator // and resign it as a C function pointer. 'pfn' can point to either a // non-virtual function or a virtual member function thunk. // It can also be NULL. if (pfn) { pfn = ptrauth_auth_and_resign(pfn, ptrauth_key_function_pointer, ptrauth_type_discriminator(__typeof__(func)), ptrauth_key_function_pointer, ptrauth_function_pointer_type_discriminator(_ptf_t)); } return pfn; #else if (map.pTMF.delta & 1) { // virtual union { const OSMetaClassBase *fObj; _ptf_t **vtablep; } u; u.fObj = self; // Virtual member function so dereference table #if defined(HAS_APPLE_PAC) // The entity hash is stored in the top 32-bits of the vtable offset of a // member function pointer. uint32_t entity_hash = ((uintptr_t)pfn) >> 32; pfn = (_ptf_t)(((uintptr_t) pfn) & 0xFFFFFFFF); #if __has_builtin(__builtin_get_vtable_pointer) const _ptf_t *vtablep = (const _ptf_t *)__builtin_get_vtable_pointer(u.fObj); #else // Authenticate the vtable pointer. const _ptf_t *vtablep = ptrauth_auth_data(*u.vtablep, ptrauth_key_cxx_vtable_pointer, 0); #endif // Calculate the address of the vtable entry. _ptf_t *vtentryp = (_ptf_t *)(((uintptr_t)vtablep) + (uintptr_t)pfn); // Load the pointer from the vtable entry. pfn = *vtentryp; // Finally, resign the vtable entry as a function pointer. uintptr_t auth_data = ptrauth_blend_discriminator(vtentryp, entity_hash); pfn = ptrauth_auth_and_resign(pfn, ptrauth_key_function_pointer, auth_data, ptrauth_key_function_pointer, ptrauth_function_pointer_type_discriminator(_ptf_t)); #else /* defined(HAS_APPLE_PAC) */ pfn = *(_ptf_t *)(((uintptr_t)*u.vtablep) + (uintptr_t)pfn); #endif /* !defined(HAS_APPLE_PAC) */ return pfn; } else { // Not virtual, i.e. plain member func return pfn; } #endif } #endif /* defined(__arm__) || defined(__arm64__) */ /********************************************************************* * These used to be inline in the header but gcc didn't believe us * Now we MUST pull the inline out at least until the compiler is * repaired. * * Helper inlines for runtime type preprocessor macros *********************************************************************/ /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClassBase::safeMetaCast( const OSMetaClassBase * me, const OSMetaClass * toType) { return (me)? me->metaCast(toType) : NULL; } /// A helper function to crash with a kernel panic. __attribute__((cold, not_tail_called, noreturn)) static inline void panic_crash_fail_cast(const OSMetaClassBase *me, const OSMetaClass *toType) { panic("Unexpected cast fail: from %p to %p", me, toType); __builtin_unreachable(); } OSMetaClassBase * OSMetaClassBase::requiredMetaCast( const OSMetaClassBase * me, const OSMetaClass * toType) { if (!me) { return NULL; } OSMetaClassBase *tmp = safeMetaCast(me, toType); if (!tmp) { panic_crash_fail_cast(me, toType); } return tmp; } /********************************************************************* *********************************************************************/ bool OSMetaClassBase::checkTypeInst( const OSMetaClassBase * inst, const OSMetaClassBase * typeinst) { const OSMetaClass * toType = OSTypeIDInst(typeinst); return typeinst && inst && (NULL != inst->metaCast(toType)); } /********************************************************************* *********************************************************************/ void OSMetaClassBase:: initialize() { sAllClassesLock = IOLockAlloc(); sStalledClassesLock = IOLockAlloc(); sInstancesLock = IOLockAlloc(); } #if APPLE_KEXT_VTABLE_PADDING /********************************************************************* * If you need this slot you had better setup an IOCTL style interface. * 'Cause the whole kernel world depends on OSMetaClassBase and YOU * CANT change the VTABLE size ever. *********************************************************************/ void OSMetaClassBase::_RESERVEDOSMetaClassBase7() { panic("OSMetaClassBase::_RESERVEDOSMetaClassBase%d called.", 7); } #endif /********************************************************************* *********************************************************************/ OSMetaClassBase::OSMetaClassBase() { } /********************************************************************* *********************************************************************/ OSMetaClassBase::~OSMetaClassBase() { void ** thisVTable; thisVTable = (void **) this; *thisVTable = (void *) -1UL; } /********************************************************************* *********************************************************************/ bool OSMetaClassBase::isEqualTo(const OSMetaClassBase * anObj) const { return this == anObj; } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClassBase::metaCast(const OSMetaClass * toMeta) const { return toMeta->checkMetaCast(this); } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClassBase::metaCast(const OSSymbol * toMetaSymb) const { return OSMetaClass::checkMetaCastWithName(toMetaSymb, this); } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClassBase::metaCast(const OSString * toMetaStr) const { const OSSymbol * tempSymb = OSSymbol::withString(toMetaStr); OSMetaClassBase * ret = NULL; if (tempSymb) { ret = metaCast(tempSymb); tempSymb->release(); } return ret; } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClassBase::metaCast(const char * toMetaCStr) const { const OSSymbol * tempSymb = OSSymbol::withCString(toMetaCStr); OSMetaClassBase * ret = NULL; if (tempSymb) { ret = metaCast(tempSymb); tempSymb->release(); } return ret; } #if PRAGMA_MARK #pragma mark OSMetaClassMeta #endif /* PRAGMA_MARK */ /********************************************************************* * OSMetaClassMeta - the bootstrap metaclass of OSMetaClass *********************************************************************/ class OSMetaClassMeta : public OSMetaClass { public: OSMetaClassMeta(); OSObject * alloc() const; }; OSMetaClassMeta::OSMetaClassMeta() : OSMetaClass("OSMetaClass", NULL, sizeof(OSMetaClass)) { } OSObject * OSMetaClassMeta::alloc() const { return NULL; } static OSMetaClassMeta sOSMetaClassMeta; const OSMetaClass * const OSMetaClass::metaClass = &sOSMetaClassMeta; const OSMetaClass * OSMetaClass::getMetaClass() const { return &sOSMetaClassMeta; } #if PRAGMA_MARK #pragma mark OSMetaClass #endif /* PRAGMA_MARK */ /********************************************************************* * OSMetaClass *********************************************************************/ #if APPLE_KEXT_VTABLE_PADDING /********************************************************************* * Reserved functions. *********************************************************************/ void OSMetaClass::_RESERVEDOSMetaClass0() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 0); } void OSMetaClass::_RESERVEDOSMetaClass1() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 1); } void OSMetaClass::_RESERVEDOSMetaClass2() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 2); } void OSMetaClass::_RESERVEDOSMetaClass3() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 3); } void OSMetaClass::_RESERVEDOSMetaClass4() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 4); } void OSMetaClass::_RESERVEDOSMetaClass5() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 5); } void OSMetaClass::_RESERVEDOSMetaClass6() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 6); } void OSMetaClass::_RESERVEDOSMetaClass7() { panic("OSMetaClass::_RESERVEDOSMetaClass%d called", 7); } #endif /********************************************************************* *********************************************************************/ static void OSMetaClassLogErrorForKext( OSReturn error, OSKext * aKext) { const char * message = NULL; switch (error) { case kOSReturnSuccess: return; case kOSMetaClassNoInit: // xxx - never returned; logged at fail site message = "OSMetaClass: preModLoad() wasn't called (runtime internal error)."; break; case kOSMetaClassNoDicts: message = "OSMetaClass: Allocation failure for OSMetaClass internal dictionaries."; break; case kOSMetaClassNoKModSet: message = "OSMetaClass: Allocation failure for internal kext recording set/set missing."; break; case kOSMetaClassNoInsKModSet: message = "OSMetaClass: Failed to record class in kext."; break; case kOSMetaClassDuplicateClass: message = "OSMetaClass: Duplicate class encountered."; break; case kOSMetaClassNoSuper: // xxx - never returned message = "OSMetaClass: Can't associate a class with its superclass."; break; case kOSMetaClassInstNoSuper: // xxx - never returned message = "OSMetaClass: Instance construction error; unknown superclass."; break; case kOSMetaClassNoKext: message = "OSMetaClass: Kext not found for metaclass."; break; case kOSMetaClassInternal: default: message = "OSMetaClass: Runtime internal error."; break; } if (message) { OSKextLog(aKext, kOSMetaClassLogSpec, "%s", message); } return; } void OSMetaClass::logError(OSReturn error) { OSMetaClassLogErrorForKext(error, NULL); } /********************************************************************* * The core constructor for a MetaClass (defined with this name always * but within the scope of its represented class). * * MetaClass constructors are invoked in OSRuntimeInitializeCPP(), * in between calls to OSMetaClass::preModLoad(), which sets up for * registration, and OSMetaClass::postModLoad(), which actually * records all the class/kext relationships of the new MetaClasses. *********************************************************************/ OSMetaClass::OSMetaClass( const char * inClassName, const OSMetaClass * inSuperClass, unsigned int inClassSize) { instanceCount = 0; classSize = inClassSize; superClassLink = inSuperClass; reserved = IOMallocType(ExpansionData); #if IOTRACKING uint32_t numSiteQs = 0; if ((this == &OSSymbol ::gMetaClass) || (this == &OSString ::gMetaClass) || (this == &OSNumber ::gMetaClass) || (this == &OSString ::gMetaClass) || (this == &OSData ::gMetaClass) || (this == &OSDictionary::gMetaClass) || (this == &OSArray ::gMetaClass) || (this == &OSSet ::gMetaClass)) { numSiteQs = 27; } reserved->tracking = IOTrackingQueueAlloc(inClassName, (uintptr_t) this, inClassSize, 0, kIOTrackingQueueTypeAlloc, numSiteQs); #endif /* Hack alert: We are just casting inClassName and storing it in * an OSString * instance variable. This may be because you can't * create C++ objects in static constructors, but I really don't know! */ className = (const OSSymbol *)inClassName; // sStalledClassesLock taken in preModLoad if (!sStalled) { /* There's no way we can look up the kext here, unfortunately. */ OSKextLog(/* kext */ NULL, kOSMetaClassLogSpec, "OSMetaClass: preModLoad() wasn't called for class %s " "(runtime internal error).", inClassName); } else if (!sStalled->result) { // Grow stalled array if neccessary if (sStalled->count >= sStalled->capacity) { OSMetaClass **oldStalled = sStalled->classes; int oldCount = sStalled->capacity; int newCount = oldCount + kKModCapacityIncrement; sStalled->classes = kalloc_type_tag(OSMetaClass *, newCount, Z_WAITOK_ZERO, VM_KERN_MEMORY_OSKEXT); if (!sStalled->classes) { sStalled->classes = oldStalled; sStalled->result = kOSMetaClassNoTempData; return; } sStalled->capacity = newCount; memmove(sStalled->classes, oldStalled, sizeof(OSMetaClass *) * oldCount); kfree_type(OSMetaClass *, oldCount, oldStalled); OSMETA_ACCUMSIZE(sizeof(OSMetaClass *) * (newCount - oldCount)); } sStalled->classes[sStalled->count++] = this; } } OSMetaClass::OSMetaClass( const char * inClassName, const OSMetaClass * inSuperClass, unsigned int inClassSize, zone_t * inZone, const char * zone_name, zone_create_flags_t zflags) : OSMetaClass(inClassName, inSuperClass, inClassSize) { if (!(kIOTracking & gIOKitDebug)) { *inZone = zone_create(zone_name, inClassSize, (zone_create_flags_t) (ZC_ZFREE_CLEARMEM | zflags)); } } /********************************************************************* *********************************************************************/ OSMetaClass::~OSMetaClass() { OSKext * myKext = reserved->kext; // do not release /* Hack alert: 'className' is a C string during early C++ init, and * is converted to a real OSSymbol only when we record the OSKext in * OSMetaClass::postModLoad(). So only do this bit if we have an OSKext. * We can't safely cast or check 'className'. * * Also, release className *after* calling into the kext, * as removeClass() may access className. */ IOLockLock(sAllClassesLock); if (sAllClassesDict) { if (myKext) { sAllClassesDict->removeObject(className); } else { sAllClassesDict->removeObject((const char *)className); } } IOLockUnlock(sAllClassesLock); if (myKext) { if (myKext->removeClass(this) != kOSReturnSuccess) { // xxx - what can we do? } className->release(); } // sStalledClassesLock taken in preModLoad if (sStalled) { unsigned int i; /* First pass find class in stalled list. If we find it that means * we started C++ init with constructors but now we're tearing down * because of some failure. */ for (i = 0; i < sStalled->count; i++) { if (this == sStalled->classes[i]) { break; } } /* Remove this metaclass from the stalled list so postModLoad() doesn't * try to register it. */ if (i < sStalled->count) { sStalled->count--; if (i < sStalled->count) { memmove(&sStalled->classes[i], &sStalled->classes[i + 1], (sStalled->count - i) * sizeof(OSMetaClass *)); } } } #if IOTRACKING IOTrackingQueueFree(reserved->tracking); #endif IOFreeType(reserved, ExpansionData); } /********************************************************************* * Empty overrides. *********************************************************************/ void OSMetaClass::retain() const { } void OSMetaClass::release() const { } void OSMetaClass::release(__unused int when) const { } void OSMetaClass::taggedRetain(__unused const void * tag) const { } void OSMetaClass::taggedRelease(__unused const void * tag) const { } void OSMetaClass::taggedRelease(__unused const void * tag, __unused const int when) const { } int OSMetaClass::getRetainCount() const { return 0; } /********************************************************************* *********************************************************************/ const char * OSMetaClass::getClassName() const { if (!className) { return NULL; } return className->getCStringNoCopy(); } /********************************************************************* *********************************************************************/ const OSSymbol * OSMetaClass::getClassNameSymbol() const { return className; } /********************************************************************* *********************************************************************/ unsigned int OSMetaClass::getClassSize() const { return classSize; } /********************************************************************* *********************************************************************/ void * OSMetaClass::preModLoad(const char * kextIdentifier) { IOLockLock(sStalledClassesLock); assert(sStalled == NULL); sStalled = kalloc_type(StalledData, Z_WAITOK_ZERO_NOFAIL); sStalled->classes = kalloc_type_tag(OSMetaClass *, kKModCapacityIncrement, Z_WAITOK_ZERO, VM_KERN_MEMORY_OSKEXT); if (!sStalled->classes) { kfree_type(StalledData, sStalled); return NULL; } OSMETA_ACCUMSIZE((kKModCapacityIncrement * sizeof(OSMetaClass *)) + sizeof(*sStalled)); sStalled->result = kOSReturnSuccess; sStalled->capacity = kKModCapacityIncrement; sStalled->count = 0; sStalled->kextIdentifier = kextIdentifier; // keep sStalledClassesLock locked until postModLoad return sStalled; } /********************************************************************* *********************************************************************/ bool OSMetaClass::checkModLoad(void * loadHandle) { return sStalled && loadHandle == sStalled && sStalled->result == kOSReturnSuccess; } /********************************************************************* *********************************************************************/ OSReturn OSMetaClass::postModLoad(void * loadHandle) { OSReturn result = kOSReturnSuccess; OSSymbol * myKextName = NULL;// must release OSKext * myKext = NULL;// must release if (!sStalled || loadHandle != sStalled) { result = kOSMetaClassInternal; goto finish; } if (sStalled->result) { result = sStalled->result; } else { switch (sBootstrapState) { case kNoDictionaries: sBootstrapState = kMakingDictionaries; // No break; fall through [[clang::fallthrough]]; case kMakingDictionaries: sAllClassesDict = OSDictionary::withCapacity(kClassCapacityIncrement); if (!sAllClassesDict) { result = kOSMetaClassNoDicts; break; } sAllClassesDict->setOptions(OSCollection::kSort, OSCollection::kSort); // No break; fall through [[clang::fallthrough]]; case kCompletedBootstrap: { unsigned int i; myKextName = const_cast(OSSymbol::withCStringNoCopy( sStalled->kextIdentifier)); if (!sStalled->count) { break; // Nothing to do so just get out } myKext = OSKext::lookupKextWithIdentifier(myKextName); if (!myKext) { result = kOSMetaClassNoKext; /* Log this error here so we can include the kext name. */ OSKextLog(/* kext */ NULL, kOSMetaClassLogSpec, "OSMetaClass: Can't record classes for kext %s - kext not found.", sStalled->kextIdentifier); break; } /* First pass checking classes aren't already loaded. If any already * exist, we don't register any, and so we don't technically have * to do any C++ teardown. * * Hack alert: me->className has been a C string until now. * We only release the OSSymbol if we store the kext. */ IOLockLock(sAllClassesLock); for (i = 0; i < sStalled->count; i++) { const OSMetaClass * me = sStalled->classes[i]; OSMetaClass * orig = OSDynamicCast(OSMetaClass, sAllClassesDict->getObject((const char *)me->className)); if (orig) { /* Log this error here so we can include the class name. * xxx - we should look up the other kext that defines the class */ #if defined(XNU_TARGET_OS_OSX) OSKextLog(myKext, kOSMetaClassLogSpec, #else panic( #endif /* defined(XNU_TARGET_OS_OSX) */ "OSMetaClass: Kext %s class %s is a duplicate;" "kext %s already has a class by that name.", sStalled->kextIdentifier, (const char *)me->className, ((OSKext *)orig->reserved->kext)->getIdentifierCString()); result = kOSMetaClassDuplicateClass; break; } unsigned int depth = 1; while ((me = me->superClassLink)) { depth++; } if (depth > sDeepestClass) { sDeepestClass = depth; } } IOLockUnlock(sAllClassesLock); /* Bail if we didn't go through the entire list of new classes * (if we hit a duplicate). */ if (i != sStalled->count) { break; } // Second pass symbolling strings and inserting classes in dictionary IOLockLock(sAllClassesLock); for (i = 0; i < sStalled->count; i++) { OSMetaClass * me = sStalled->classes[i]; /* Hack alert: me->className has been a C string until now. * We only release the OSSymbol in ~OSMetaClass() * if we set the reference to the kext. */ me->className = OSSymbol::withCStringNoCopy((const char *)me->className); // xxx - I suppose if these fail we're going to panic soon.... sAllClassesDict->setObject(me->className, me); /* Do not retain the kext object here. */ me->reserved->kext = myKext; if (myKext) { result = myKext->addClass(me, sStalled->count); if (result != kOSReturnSuccess) { /* OSKext::addClass() logs with kOSMetaClassNoInsKModSet. */ break; } } } IOLockUnlock(sAllClassesLock); sBootstrapState = kCompletedBootstrap; break; } default: result = kOSMetaClassInternal; break; } } finish: /* Don't call logError() for success or the conditions logged above * or by called function. */ if (result != kOSReturnSuccess && result != kOSMetaClassNoInsKModSet && result != kOSMetaClassDuplicateClass && result != kOSMetaClassNoKext) { OSMetaClassLogErrorForKext(result, myKext); } OSSafeReleaseNULL(myKextName); OSSafeReleaseNULL(myKext); if (sStalled) { OSMETA_ACCUMSIZE(-(sStalled->capacity * sizeof(OSMetaClass *) + sizeof(*sStalled))); kfree_type(OSMetaClass *, sStalled->capacity, sStalled->classes); kfree_type(StalledData, sStalled); sStalled = NULL; } IOLockUnlock(sStalledClassesLock); return result; } /********************************************************************* *********************************************************************/ void OSMetaClass::instanceConstructed() const { // if ((0 == OSIncrementAtomic(&(((OSMetaClass *) this)->instanceCount))) && superClassLink) if ((0 == OSIncrementAtomic(&instanceCount)) && superClassLink) { superClassLink->instanceConstructed(); } } /********************************************************************* *********************************************************************/ void OSMetaClass::instanceDestructed() const { if ((1 == OSDecrementAtomic(&instanceCount)) && superClassLink) { superClassLink->instanceDestructed(); } if (((int)instanceCount) < 0) { OSKext * myKext = reserved->kext; OSKextLog(myKext, kOSMetaClassLogSpec, // xxx - this phrasing is rather cryptic "OSMetaClass: Class %s - bad retain (%d)", getClassName(), instanceCount); } } /********************************************************************* *********************************************************************/ bool OSMetaClass::modHasInstance(const char * kextIdentifier) { bool result = false; OSKext * theKext = NULL; // must release theKext = OSKext::lookupKextWithIdentifier(kextIdentifier); if (!theKext) { goto finish; } result = theKext->hasOSMetaClassInstances(); finish: OSSafeReleaseNULL(theKext); return result; } /********************************************************************* *********************************************************************/ void OSMetaClass::reportModInstances(const char * kextIdentifier) { OSKext::reportOSMetaClassInstances(kextIdentifier, kOSKextLogExplicitLevel); return; } /********************************************************************* *********************************************************************/ void OSMetaClass::addInstance(const OSObject * instance, bool super) const { if (!super) { IOLockLock(sInstancesLock); } if (!reserved->instances) { reserved->instances = OSOrderedSet::withCapacity(16); if (superClassLink) { superClassLink->addInstance(reserved->instances, true); } } reserved->instances->setLastObject(instance); if (!super) { IOLockUnlock(sInstancesLock); } } void OSMetaClass::removeInstance(const OSObject * instance, bool super) const { if (!super) { IOLockLock(sInstancesLock); } if (reserved->instances) { reserved->instances->removeObject(instance); if (0 == reserved->instances->getCount()) { if (superClassLink) { superClassLink->removeInstance(reserved->instances, true); } IOLockLock(sAllClassesLock); reserved->instances->release(); reserved->instances = NULL; IOLockUnlock(sAllClassesLock); } } if (!super) { IOLockUnlock(sInstancesLock); } } void OSMetaClass::applyToInstances(OSOrderedSet * set, OSMetaClassInstanceApplierFunction applier, void * context) { enum { kLocalDepth = 24 }; unsigned int _nextIndex[kLocalDepth]; OSOrderedSet * _sets[kLocalDepth]; unsigned int * nextIndex = &_nextIndex[0]; OSOrderedSet ** sets = &_sets[0]; OSObject * obj; OSOrderedSet * childSet; unsigned int maxDepth; unsigned int idx; unsigned int level; bool done; maxDepth = sDeepestClass; if (maxDepth > kLocalDepth) { nextIndex = IONewData(typeof(nextIndex[0]), maxDepth); sets = IONew(typeof(sets[0]), maxDepth); } done = false; level = 0; idx = 0; do{ while (!done && (obj = set->getObject(idx++))) { if ((childSet = OSDynamicCast(OSOrderedSet, obj))) { if (level >= maxDepth) { panic(">maxDepth"); } sets[level] = set; nextIndex[level] = idx; level++; set = childSet; idx = 0; break; } done = (*applier)(obj, context); } if (!obj) { if (!done && level) { level--; set = sets[level]; idx = nextIndex[level]; } else { done = true; } } }while (!done); if (maxDepth > kLocalDepth) { IODeleteData(nextIndex, typeof(nextIndex[0]), maxDepth); IODelete(sets, typeof(sets[0]), maxDepth); } } void OSMetaClass::applyToInstances(OSMetaClassInstanceApplierFunction applier, void * context) const { IOLockLock(sInstancesLock); if (reserved->instances) { applyToInstances(reserved->instances, applier, context); } IOLockUnlock(sInstancesLock); } void OSMetaClass::applyToInstancesOfClassName( const OSSymbol * name, OSMetaClassInstanceApplierFunction applier, void * context) { OSMetaClass * meta; OSOrderedSet * set = NULL; IOLockLock(sAllClassesLock); if (sAllClassesDict && (meta = (OSMetaClass *) sAllClassesDict->getObject(name)) && (set = meta->reserved->instances)) { set->retain(); } IOLockUnlock(sAllClassesLock); if (!set) { return; } IOLockLock(sInstancesLock); applyToInstances(set, applier, context); IOLockUnlock(sInstancesLock); set->release(); } /********************************************************************* *********************************************************************/ void OSMetaClass::considerUnloads() { OSKext::considerUnloads(); } /********************************************************************* *********************************************************************/ bool OSMetaClass::removeClasses(OSCollection * metaClasses) { OSCollectionIterator * classIterator; OSMetaClass * checkClass; bool result; classIterator = OSCollectionIterator::withCollection(metaClasses); if (!classIterator) { return false; } IOLockLock(sAllClassesLock); result = false; do{ while ((checkClass = (OSMetaClass *)classIterator->getNextObject()) && !checkClass->getInstanceCount() && !checkClass->reserved->retain) { } if (checkClass) { break; } classIterator->reset(); while ((checkClass = (OSMetaClass *)classIterator->getNextObject())) { sAllClassesDict->removeObject(checkClass->className); } result = true; }while (false); IOLockUnlock(sAllClassesLock); OSSafeReleaseNULL(classIterator); return result; } /********************************************************************* *********************************************************************/ const OSMetaClass * OSMetaClass::getMetaClassWithName(const OSSymbol * name) { OSMetaClass * retMeta = NULL; if (!name) { return NULL; } IOLockLock(sAllClassesLock); if (sAllClassesDict) { retMeta = (OSMetaClass *) sAllClassesDict->getObject(name); } IOLockUnlock(sAllClassesLock); return retMeta; } /********************************************************************* *********************************************************************/ const OSMetaClass * OSMetaClass::copyMetaClassWithName(const OSSymbol * name) { const OSMetaClass * meta; if (!name) { return NULL; } meta = NULL; IOLockLock(sAllClassesLock); if (sAllClassesDict) { meta = (OSMetaClass *) sAllClassesDict->getObject(name); if (meta) { OSIncrementAtomic(&meta->reserved->retain); } } IOLockUnlock(sAllClassesLock); return meta; } /********************************************************************* *********************************************************************/ void OSMetaClass::releaseMetaClass() const { OSDecrementAtomic(&reserved->retain); } /********************************************************************* *********************************************************************/ OSObject * OSMetaClass::allocClassWithName(const OSSymbol * name) { const OSMetaClass * meta; OSObject * result; result = NULL; meta = copyMetaClassWithName(name); if (meta) { result = meta->alloc(); meta->releaseMetaClass(); } return result; } /********************************************************************* *********************************************************************/ OSObject * OSMetaClass::allocClassWithName(const OSString * name) { const OSSymbol * tmpKey = OSSymbol::withString(name); OSObject * result = allocClassWithName(tmpKey); tmpKey->release(); return result; } /********************************************************************* *********************************************************************/ OSObject * OSMetaClass::allocClassWithName(const char * name) { const OSSymbol * tmpKey = OSSymbol::withCStringNoCopy(name); OSObject * result = allocClassWithName(tmpKey); tmpKey->release(); return result; } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClass::checkMetaCastWithName( const OSSymbol * name, const OSMetaClassBase * in) { OSMetaClassBase * result = NULL; const OSMetaClass * const meta = getMetaClassWithName(name); if (meta) { result = meta->checkMetaCast(in); } return result; } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClass:: checkMetaCastWithName( const OSString * name, const OSMetaClassBase * in) { const OSSymbol * tmpKey = OSSymbol::withString(name); OSMetaClassBase * result = checkMetaCastWithName(tmpKey, in); tmpKey->release(); return result; } /********************************************************************* *********************************************************************/ OSMetaClassBase * OSMetaClass::checkMetaCastWithName( const char * name, const OSMetaClassBase * in) { const OSSymbol * tmpKey = OSSymbol::withCStringNoCopy(name); OSMetaClassBase * result = checkMetaCastWithName(tmpKey, in); tmpKey->release(); return result; } /********************************************************************* * OSMetaClass::checkMetaCast() * Check to see if the 'check' object has this object in its metaclass chain. * Returns check if it is indeed a kind of the current meta class, 0 otherwise. * * Generally this method is not invoked directly but is used to implement * the OSMetaClassBase::metaCast member function. * * See also OSMetaClassBase::metaCast *********************************************************************/ OSMetaClassBase * OSMetaClass::checkMetaCast( const OSMetaClassBase * check) const { const OSMetaClass * const toMeta = this; const OSMetaClass * fromMeta; for (fromMeta = check->getMetaClass();; fromMeta = fromMeta->superClassLink) { if (toMeta == fromMeta) { return const_cast(check); // Discard const } if (!fromMeta->superClassLink) { break; } } return NULL; } /********************************************************************* *********************************************************************/ __dead2 void OSMetaClass::reservedCalled(int ind) const { const char * cname = className->getCStringNoCopy(); panic("%s::_RESERVED%s%d called.", cname, cname, ind); } /********************************************************************* *********************************************************************/ const OSMetaClass * OSMetaClass::getSuperClass() const { return superClassLink; } /********************************************************************* *********************************************************************/ const OSSymbol * OSMetaClass::getKmodName() const { OSKext * myKext = reserved ? reserved->kext : NULL; if (myKext) { return myKext->getIdentifier(); } return OSSymbol::withCStringNoCopy("unknown"); } /********************************************************************* *********************************************************************/ OSKext * OSMetaClass::getKext() const { return reserved ? reserved->kext : NULL; } /********************************************************************* *********************************************************************/ unsigned int OSMetaClass::getInstanceCount() const { return instanceCount; } /********************************************************************* *********************************************************************/ /* static */ void OSMetaClass::printInstanceCounts() { OSCollectionIterator * classes; OSSymbol * className; OSMetaClass * meta; IOLockLock(sAllClassesLock); classes = OSCollectionIterator::withCollection(sAllClassesDict); assert(classes); while ((className = (OSSymbol *)classes->getNextObject())) { meta = (OSMetaClass *)sAllClassesDict->getObject(className); assert(meta); printf("%24s count: %03d x 0x%03x = 0x%06x\n", className->getCStringNoCopy(), meta->getInstanceCount(), meta->getClassSize(), meta->getInstanceCount() * meta->getClassSize()); } printf("\n"); classes->release(); IOLockUnlock(sAllClassesLock); return; } /********************************************************************* *********************************************************************/ OSDictionary * OSMetaClass::getClassDictionary() { panic("OSMetaClass::getClassDictionary() is obsoleted."); return NULL; } /********************************************************************* *********************************************************************/ bool OSMetaClass::serialize(__unused OSSerialize * s) const { panic("OSMetaClass::serialize(): Obsoleted"); return false; } /********************************************************************* *********************************************************************/ /* static */ void OSMetaClass::serializeClassDictionary(OSDictionary * serializeDictionary) { OSDictionary * classDict = NULL; IOLockLock(sAllClassesLock); classDict = OSDictionary::withCapacity(sAllClassesDict->getCount()); if (!classDict) { goto finish; } do { OSCollectionIterator * classes; const OSSymbol * className; classes = OSCollectionIterator::withCollection(sAllClassesDict); if (!classes) { break; } while ((className = (const OSSymbol *)classes->getNextObject())) { const OSMetaClass * meta; OSNumber * count; meta = (OSMetaClass *)sAllClassesDict->getObject(className); count = OSNumber::withNumber(meta->getInstanceCount(), 32); if (count) { classDict->setObject(className, count); count->release(); } } classes->release(); serializeDictionary->setObject("Classes", classDict); } while (0); finish: OSSafeReleaseNULL(classDict); IOLockUnlock(sAllClassesLock); return; } /********************************************************************* *********************************************************************/ #if IOTRACKING __typed_allocators_ignore_push void * OSMetaClass::trackedNew(size_t size) { IOTracking * mem; mem = (typeof(mem))kheap_alloc(KHEAP_DEFAULT, size + sizeof(IOTracking), Z_VM_TAG_BT(Z_WAITOK, VM_KERN_MEMORY_LIBKERN)); assert(mem); if (!mem) { return mem; } memset(mem, 0, size + sizeof(IOTracking)); mem++; OSIVAR_ACCUMSIZE(size); return mem; } void OSMetaClass::trackedDelete(void * instance, size_t size) { IOTracking * mem = (typeof(mem))instance; mem--; kheap_free(KHEAP_DEFAULT, mem, size + sizeof(IOTracking)); OSIVAR_ACCUMSIZE(-size); } __typed_allocators_ignore_pop void OSMetaClass::trackedInstance(OSObject * instance) const { IOTracking * mem = (typeof(mem))instance; mem--; return IOTrackingAdd(reserved->tracking, mem, classSize, false, VM_KERN_MEMORY_NONE); } void OSMetaClass::trackedFree(OSObject * instance) const { IOTracking * mem = (typeof(mem))instance; mem--; return IOTrackingRemove(reserved->tracking, mem, classSize); } void OSMetaClass::trackedAccumSize(OSObject * instance, size_t size) const { IOTracking * mem = (typeof(mem))instance; mem--; return IOTrackingAccumSize(reserved->tracking, mem, size); } IOTrackingQueue * OSMetaClass::getTracking() const { return reserved->tracking; } #endif /* IOTRACKING */