2022-07-08 20:06:15 +02:00

938 lines
27 KiB
C

/** @file
Library handling KEXT prelinking.
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
This program and the accompanying materials are licensed and made available
under the terms and conditions of the BSD License which accompanies this
distribution. The full text of the license may be found at
http://opensource.org/licenses/bsd-license.php.
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
**/
#include <Base.h>
#include <IndustryStandard/AppleMachoImage.h>
#include <Library/BaseLib.h>
#include <Library/DebugLib.h>
#include <Library/MemoryAllocationLib.h>
#include <Library/OcAppleKernelLib.h>
#include <Library/OcGuardLib.h>
#include <Library/OcMachoLib.h>
#include "PrelinkedInternal.h"
CONST PRELINKED_VTABLE *
InternalGetOcVtableByNameWorker (
IN PRELINKED_CONTEXT *Context,
IN PRELINKED_KEXT *Kext,
IN CONST CHAR8 *Name
)
{
CONST PRELINKED_VTABLE *Vtable;
UINTN Index;
PRELINKED_KEXT *Dependency;
INTN Result;
Kext->Processed = TRUE;
for (
Index = 0, Vtable = Kext->LinkedVtables;
Index < Kext->NumberOfVtables;
++Index, Vtable = GET_NEXT_PRELINKED_VTABLE (Vtable)
)
{
Result = AsciiStrCmp (Vtable->Name, Name);
if (Result == 0) {
return Vtable;
}
}
for (Index = 0; Index < ARRAY_SIZE (Kext->Dependencies); ++Index) {
Dependency = Kext->Dependencies[Index];
if (Dependency == NULL) {
break;
}
if (Dependency->Processed) {
continue;
}
Vtable = InternalGetOcVtableByName (Context, Dependency, Name);
if (Vtable != NULL) {
return Vtable;
}
}
return NULL;
}
CONST PRELINKED_VTABLE *
InternalGetOcVtableByName (
IN PRELINKED_CONTEXT *Context,
IN PRELINKED_KEXT *Kext,
IN CONST CHAR8 *Name
)
{
CONST PRELINKED_VTABLE *Vtable;
Vtable = InternalGetOcVtableByNameWorker (Context, Kext, Name);
InternalUnlockContextKexts (Context);
return Vtable;
}
STATIC
VOID
InternalConstructVtablePrelinked (
IN PRELINKED_CONTEXT *Context,
IN OUT PRELINKED_KEXT *Kext,
IN CONST OC_PRELINKED_VTABLE_LOOKUP_ENTRY *VtableLookup,
OUT PRELINKED_VTABLE *Vtable
)
{
CONST VOID *VtableData;
UINT64 Value;
UINT32 Index;
CONST PRELINKED_KEXT_SYMBOL *Symbol;
ASSERT (Kext != NULL);
ASSERT (VtableLookup != NULL);
ASSERT (Vtable != NULL);
VtableData = VtableLookup->Vtable.Data;
Vtable->Name = VtableLookup->Name;
//
// Initialize the VTable by entries.
//
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
// VTable bounds are verified in InternalGetVtableEntries() called earlier.
// The buffer is allocated to be able to hold all entries.
//
for (
Index = 0;
(Value = VTABLE_ENTRY_X (Context->Is32Bit, VtableData, Index + VTABLE_HEADER_LEN)) != 0;
++Index
)
{
if (Context->IsKernelCollection) {
Value = KcFixupValue (Value, Kext->Identifier);
}
//
// If we can't find the symbol, it means that the virtual function was
// defined inline. There's not much I can do about this; it just means
// I can't patch this function.
//
Symbol = InternalOcGetSymbolValue (Context, Kext, Value, OcGetSymbolOnlyCxx);
if (Symbol != NULL) {
Vtable->Entries[Index].Address = Value;
Vtable->Entries[Index].Name = Symbol->Name;
} else {
Vtable->Entries[Index].Address = 0;
Vtable->Entries[Index].Name = NULL;
}
}
Vtable->NumEntries = Index;
}
BOOLEAN
InternalGetVtableEntries (
IN BOOLEAN Is32Bit,
IN CONST VOID *VtableData,
IN UINT32 MaxSize,
OUT UINT32 *NumEntries
)
{
UINT32 Index;
ASSERT (VtableData != NULL);
ASSERT (NumEntries != NULL);
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
//
MaxSize /= VTABLE_ENTRY_SIZE_X (Is32Bit);
Index = VTABLE_HEADER_LEN;
do {
if (Index >= MaxSize) {
return FALSE;
}
} while (VTABLE_ENTRY_X (Is32Bit, VtableData, Index++) != 0);
*NumEntries = (Index - VTABLE_HEADER_LEN);
return TRUE;
}
BOOLEAN
InternalPrepareCreateVtablesPrelinked (
IN PRELINKED_KEXT *Kext,
IN UINT32 MaxSize,
OUT UINT32 *NumVtables,
OUT OC_PRELINKED_VTABLE_LOOKUP_ENTRY *Vtables
)
{
UINT32 VtableIndex;
CONST PRELINKED_KEXT_SYMBOL *Symbol;
UINT32 Index;
ASSERT (Kext != NULL);
VtableIndex = 0;
MaxSize /= sizeof (*Vtables);
for (
Index = (Kext->NumberOfSymbols - Kext->NumberOfCxxSymbols);
Index < Kext->NumberOfSymbols;
++Index
)
{
Symbol = &Kext->LinkedSymbolTable[Index];
if (MachoSymbolNameIsVtable (Symbol->Name)) {
//
// This seems to be valid for KC format as some symbols may be kernel imports?!
// Observed with IOACPIFamily when injecting VirtualSMC:
// __ZTV18IODTPlatformExpert (zero value)
// __ZTV16IOPlatformDevice (zero value)
// __ZTVN20IOACPIPlatformExpert9MetaClassE
// __ZTVN20IOACPIPlatformDevice9MetaClassE
// __ZTV20IOACPIPlatformExpert
// __ZTV20IOACPIPlatformDevice
//
if (Symbol->Value == 0) {
DEBUG ((DEBUG_VERBOSE, "OCAK: Skipping %a with NULL value\n", Symbol->Name));
continue;
}
if (VtableIndex >= MaxSize) {
return FALSE;
}
Vtables[VtableIndex].Name = Symbol->Name;
Vtables[VtableIndex].Vtable.Value = Symbol->Value;
++VtableIndex;
}
}
*NumVtables = VtableIndex;
return TRUE;
}
VOID
InternalCreateVtablesPrelinked (
IN PRELINKED_CONTEXT *Context,
IN OUT PRELINKED_KEXT *Kext,
IN UINT32 NumVtables,
IN CONST OC_PRELINKED_VTABLE_LOOKUP_ENTRY *VtableLookups,
OUT PRELINKED_VTABLE *VtableBuffer
)
{
UINT32 Index;
ASSERT (Kext != NULL);
for (Index = 0; Index < NumVtables; ++Index) {
InternalConstructVtablePrelinked (
Context,
Kext,
&VtableLookups[Index],
VtableBuffer
);
VtableBuffer = GET_NEXT_PRELINKED_VTABLE (VtableBuffer);
}
}
/**
* kxld_vtable_patch
*/
STATIC
BOOLEAN
InternalPatchVtableSymbol (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST PRELINKED_VTABLE_ENTRY *ParentEntry,
IN CONST CHAR8 *VtableName,
OUT MACH_NLIST_ANY *Symbol
)
{
CONST CHAR8 *Name;
INTN Result;
BOOLEAN Success;
CONST CHAR8 *ClassName;
CHAR8 FunctionPrefix[SYM_MAX_NAME_LEN];
ASSERT (Symbol != NULL);
ASSERT (ParentEntry != NULL);
//
// It's possible for the patched parent entry not to have a symbol
// (e.g. when the definition is inlined). We can't patch this entry no
// matter what, so we'll just skip it and die later if it's a problem
// (which is not likely).
//
if (ParentEntry->Name == NULL) {
return TRUE;
}
//
// 1) If the symbol is defined locally, do not patch
//
if (MachoSymbolIsLocalDefined (MachoContext, Symbol)) {
return TRUE;
}
Name = MachoGetSymbolName (MachoContext, Symbol);
//
// 2) If the child is a pure virtual function, do not patch.
// In general, we want to proceed with patching when the symbol is
// externally defined because pad slots fall into this category.
// The pure virtual function symbol is special case, as the pure
// virtual property itself overrides the parent's implementation.
//
if (MachoSymbolNameIsPureVirtual (Name)) {
return TRUE;
}
//
// 3) If the symbols are the same, do not patch
//
Result = AsciiStrCmp (Name, ParentEntry->Name);
if (Result == 0) {
return TRUE;
}
//
// 4) If the parent vtable entry is a pad slot, and the child does not
// match it, then the child was built against a newer version of the
// libraries, so it is binary-incompatible.
//
if (MachoSymbolNameIsPadslot (ParentEntry->Name)) {
return FALSE;
}
//
// 5) If we are doing strict patching, we prevent kexts from declaring
// virtual functions and not implementing them. We can tell if a
// virtual function is declared but not implemented because we resolve
// symbols before patching; an unimplemented function will still be
// undefined at this point. We then look at whether the symbol has
// the same class prefix as the vtable. If it does, the symbol was
// declared as part of the class and not inherited, which means we
// should not patch it.
//
if (!MachoSymbolIsDefined (MachoContext, Symbol)) {
ClassName = MachoGetClassNameFromVtableName (VtableName);
Success = MachoGetFunctionPrefixFromClassName (
ClassName,
sizeof (FunctionPrefix),
FunctionPrefix
);
if (!Success) {
return FALSE;
}
Result = AsciiStrCmp (Name, FunctionPrefix);
if (Result == 0) {
//
// The VTable's class declares a method without providing an
// implementation.
//
return FALSE;
}
}
//
// 6) The child symbol is unresolved and different from its parent, so
// we need to patch it up. We do this by modifying the relocation
// entry of the vtable entry to point to the symbol of the parent
// vtable entry. If that symbol does not exist (i.e. we got the data
// from a link state object's vtable representation), then we create a
// new symbol in the symbol table and point the relocation entry to
// that.
//
// NOTE: The original logic has been altered significantly. Instead of
// declaring a symbol as "replaced" and either changing the
// associated relocation's index to the parent's or adding a new symbol
// based on a match, the symbol is actually overwritten. This looks
// fine for the rest of the control flow. The symbol name is not
// changed for the symbol value is already resolved and nothing but a
// VTable Relocation should reference it.
//
InternalSolveSymbolValue (MachoContext->Is32Bit, ParentEntry->Address, Symbol);
//
// The C++ ABI requires that functions be aligned on a 2-byte boundary:
// http://www.codesourcery.com/public/cxx-abi/abi.html#member-pointers
// If the LSB of any virtual function's link address is 1, then the
// compiler has violated that part of the ABI, and we're going to panic
// in _ptmf2ptf() (in OSMetaClass.h). Better to panic here with some
// context.
//
Name = ParentEntry->Name;
if ( !MachoSymbolNameIsPureVirtual (Name)
&& (((MachoContext->Is32Bit ? Symbol->Symbol32.Value : Symbol->Symbol64.Value) & 1U) != 0))
{
DEBUG ((DEBUG_WARN, "OCAK: Prelink: Invalid VTable symbol\n"));
}
return TRUE;
}
STATIC
BOOLEAN
InternalInitializeVtableByEntriesAndRelocations (
IN PRELINKED_CONTEXT *Context,
IN PRELINKED_KEXT *Kext,
IN CONST PRELINKED_VTABLE *SuperVtable,
IN CONST MACH_NLIST_ANY *VtableSymbol,
IN CONST VOID *VtableData,
IN UINT32 NumSolveSymbols,
IN OUT MACH_NLIST_ANY **SolveSymbols,
OUT PRELINKED_VTABLE *Vtable
)
{
OC_MACHO_CONTEXT *MachoContext;
CONST CHAR8 *VtableName;
CONST PRELINKED_KEXT_SYMBOL *OcSymbol;
UINT32 Index;
PRELINKED_VTABLE_ENTRY *VtableEntries;
UINT64 EntryValue;
UINT32 SolveSymbolIndex;
BOOLEAN Result;
MACH_NLIST_ANY *Symbol;
MachoContext = &Kext->Context.MachContext;
VtableEntries = Vtable->Entries;
VtableName = MachoGetSymbolName (MachoContext, VtableSymbol);
SolveSymbolIndex = 0;
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
//
for (Index = 0; Index < SuperVtable->NumEntries; ++Index) {
EntryValue = VTABLE_ENTRY_X (Context->Is32Bit, VtableData, Index + VTABLE_HEADER_LEN);
if (EntryValue != 0) {
//
// If we can't find a symbol, it means it is a locally-defined,
// non-external symbol that has been stripped. We don't patch over
// locally-defined symbols, so we leave the symbol as NULL and just
// skip it. We won't be able to patch subclasses with this symbol,
// but there isn't much we can do about that.
//
OcSymbol = InternalOcGetSymbolValue (
Context,
Kext,
EntryValue,
OcGetSymbolOnlyCxx
);
if (OcSymbol != NULL) {
VtableEntries[Index].Name = OcSymbol->Name;
VtableEntries[Index].Address = OcSymbol->Value;
continue;
}
} else {
if (SolveSymbolIndex >= NumSolveSymbols) {
//
// When no more symbols are left to resolve with, this marks the end.
//
break;
}
Symbol = SolveSymbols[SolveSymbolIndex];
++SolveSymbolIndex;
//
// The child entry can be NULL when a locally-defined, non-external
// symbol is stripped. We wouldn't patch this entry anyway, so we
// just skip it.
//
if (Symbol != NULL) {
Result = InternalPatchVtableSymbol (
MachoContext,
&SuperVtable->Entries[Index],
VtableName,
Symbol
);
if (!Result) {
DEBUG ((DEBUG_INFO, "OCAK: Failed to patch symbol %a for vtable %a\n", MachoGetSymbolName (MachoContext, Symbol), VtableName));
return FALSE;
}
VtableEntries[Index].Name = MachoGetSymbolName (MachoContext, Symbol);
VtableEntries[Index].Address = Context->Is32Bit ? Symbol->Symbol32.Value : Symbol->Symbol64.Value;
continue;
}
}
VtableEntries[Index].Name = NULL;
VtableEntries[Index].Address = 0;
}
Vtable->Name = VtableName;
Vtable->NumEntries = Index;
return TRUE;
}
STATIC
BOOLEAN
InternalInitializeVtablePatchData (
IN OUT OC_MACHO_CONTEXT *MachoContext,
IN CONST MACH_NLIST_ANY *VtableSymbol,
IN OUT UINT32 *MaxSize,
OUT VOID **VtableDataPtr,
OUT UINT32 *NumEntries,
OUT UINT32 *NumSymbols,
OUT MACH_NLIST_ANY **SolveSymbols
)
{
BOOLEAN Result;
UINT32 VtableOffset;
UINT32 VtableMaxSize;
VOID *FileData;
VOID *VtableData;
UINT32 SymIndex;
UINT32 EntryOffset;
UINT64 FileOffset;
UINT32 MaxSymbols;
MACH_NLIST_ANY *Symbol;
Result = MachoSymbolGetFileOffset (
MachoContext,
VtableSymbol,
&VtableOffset,
&VtableMaxSize
);
if (!Result) {
return FALSE;
}
FileData = MachoGetFileData (MachoContext);
ASSERT (FileData != NULL);
VtableData = (VOID *)((UINTN)FileData + VtableOffset);
if (MachoContext->Is32Bit ? !OC_TYPE_ALIGNED (UINT32, VtableData) : !OC_TYPE_ALIGNED (UINT64, VtableData)) {
return FALSE;
}
//
// Assumption: Not ARM (ARM requires an alignment to the function pointer
// retrieved from VtableData.
//
MaxSymbols = (*MaxSize / (MachoContext->Is32Bit ? sizeof (MACH_NLIST) : sizeof (MACH_NLIST_64)));
VtableMaxSize /= VTABLE_ENTRY_SIZE_X (MachoContext->Is32Bit);
SymIndex = 0;
for (
EntryOffset = VTABLE_HEADER_LEN;
EntryOffset < VtableMaxSize;
++EntryOffset
)
{
if (VTABLE_ENTRY_X (MachoContext->Is32Bit, VtableData, EntryOffset) == 0) {
Result = OcOverflowAddU64 (
MachoContext->Is32Bit ? VtableSymbol->Symbol32.Value : VtableSymbol->Symbol64.Value,
(EntryOffset * VTABLE_ENTRY_SIZE_X (MachoContext->Is32Bit)),
&FileOffset
);
if (Result) {
return FALSE;
}
Result = MachoGetSymbolByExternRelocationOffset (
MachoContext,
FileOffset,
&Symbol
);
if (!Result) {
//
// If the VTable entry is 0 and it is not referenced by a Relocation,
// it is the end of the table.
//
*MaxSize -= (SymIndex * (MachoContext->Is32Bit ? sizeof (MACH_NLIST) : sizeof (MACH_NLIST_64)));
*VtableDataPtr = VtableData;
*NumEntries = (EntryOffset - VTABLE_HEADER_LEN);
*NumSymbols = SymIndex;
return TRUE;
}
if (SymIndex >= MaxSymbols) {
return FALSE;
}
SolveSymbols[SymIndex] = Symbol;
++SymIndex;
}
}
return FALSE;
}
BOOLEAN
InternalPatchByVtables (
IN PRELINKED_CONTEXT *Context,
IN OUT PRELINKED_KEXT *Kext
)
{
OC_VTABLE_PATCH_ENTRY *Entries;
OC_VTABLE_PATCH_ENTRY *EntryWalker;
UINT32 MaxSize;
OC_MACHO_CONTEXT *MachoContext;
UINT32 Index;
UINT32 NumTables;
UINT32 NumEntries;
UINT32 NumEntriesTemp;
UINT32 NumPatched;
BOOLEAN Result;
CONST MACH_NLIST_ANY *Smcp;
CONST CHAR8 *Name;
CONST MACH_NLIST_ANY *MetaClass;
CONST PRELINKED_VTABLE *SuperVtable;
CONST PRELINKED_VTABLE *MetaVtable;
CONST VOID *OcSymbolDummy;
MACH_NLIST_ANY *SymbolDummy;
CHAR8 ClassName[SYM_MAX_NAME_LEN];
CHAR8 SuperClassName[SYM_MAX_NAME_LEN];
CHAR8 VtableName[SYM_MAX_NAME_LEN];
CHAR8 SuperVtableName[SYM_MAX_NAME_LEN];
CHAR8 FinalSymbolName[SYM_MAX_NAME_LEN];
BOOLEAN SuccessfulIteration;
PRELINKED_VTABLE *CurrentVtable;
//
// LinkBuffer is at least as big as __LINKEDIT, so it can store all symbols.
//
Entries = Context->LinkBuffer;
MaxSize = Context->LinkBufferSize;
MachoContext = &Kext->Context.MachContext;
//
// Retrieve all SMCPs.
//
EntryWalker = Entries;
NumTables = 0;
NumEntries = 0;
for (
Index = 0;
(Smcp = MachoGetSymbolByIndex (MachoContext, Index)) != NULL;
++Index
)
{
Name = MachoGetSymbolName (MachoContext, Smcp);
if ( (((Context->Is32Bit ? Smcp->Symbol32.Type : Smcp->Symbol64.Type) & MACH_N_TYPE_STAB) == 0)
&& MachoSymbolNameIsSmcp (MachoContext, Name))
{
if (MaxSize < sizeof (*EntryWalker)) {
return FALSE;
}
//
// We walk over the super metaclass pointer symbols because classes
// with them are the only ones that need patching. Then we double the
// number of vtables we're expecting, because every pointer will have a
// class vtable and a MetaClass vtable.
//
Result = MachoGetVtableSymbolsFromSmcp (
MachoContext,
Name,
&EntryWalker->Vtable,
&EntryWalker->MetaVtable
);
if (!Result) {
return FALSE;
}
EntryWalker->Smcp = Smcp;
Result = InternalInitializeVtablePatchData (
MachoContext,
EntryWalker->Vtable,
&MaxSize,
&EntryWalker->VtableData,
&NumEntriesTemp,
&EntryWalker->MetaSymsIndex,
EntryWalker->SolveSymbols
);
if (!Result) {
return FALSE;
}
NumEntries += NumEntriesTemp;
Result = InternalInitializeVtablePatchData (
MachoContext,
EntryWalker->MetaVtable,
&MaxSize,
&EntryWalker->MetaVtableData,
&NumEntriesTemp,
&EntryWalker->NumSolveSymbols,
&EntryWalker->SolveSymbols[EntryWalker->MetaSymsIndex]
);
if (!Result) {
return FALSE;
}
NumEntries += NumEntriesTemp;
EntryWalker->NumSolveSymbols += EntryWalker->MetaSymsIndex;
++NumTables;
EntryWalker = GET_NEXT_OC_VTABLE_PATCH_ENTRY (EntryWalker);
}
}
//
// One structure contains two VTables, hence (NumTables * 2).
//
Kext->LinkedVtables = AllocatePool (
((NumTables * 2) * sizeof (*Kext->LinkedVtables))
+ (NumEntries * sizeof (*Kext->LinkedVtables->Entries))
);
if (Kext->LinkedVtables == NULL) {
return FALSE;
}
CurrentVtable = Kext->LinkedVtables;
//
// Patch via the previously retrieved SMCPs.
//
NumPatched = 0;
while (NumPatched < NumTables) {
SuccessfulIteration = FALSE;
for (
Index = 0, EntryWalker = Entries;
Index < NumTables;
++Index, EntryWalker = GET_NEXT_OC_VTABLE_PATCH_ENTRY (EntryWalker)
)
{
Smcp = EntryWalker->Smcp;
if (Smcp == NULL) {
continue;
}
Name = MachoGetSymbolName (MachoContext, Smcp);
//
// We walk over the super metaclass pointer symbols because classes
// with them are the only ones that need patching. Then we double the
// number of vtables we're expecting, because every pointer will have a
// class vtable and a MetaClass vtable.
//
ASSERT (MachoSymbolNameIsSmcp (MachoContext, Name));
//
// Get the class name from the smc pointer
//
Result = MachoGetClassNameFromSuperMetaClassPointer (
MachoContext,
Name,
sizeof (ClassName),
ClassName
);
if (!Result) {
return FALSE;
}
//
// Get the vtable name from the class name
//
Result = MachoGetVtableNameFromClassName (
ClassName,
sizeof (VtableName),
VtableName
);
if (!Result) {
return FALSE;
}
//
// Find the SMCP's meta class symbol
//
MetaClass = MachoGetMetaclassSymbolFromSmcpSymbol (
MachoContext,
Smcp
);
if (MetaClass == NULL) {
return FALSE;
}
//
// Get the super class name from the super metaclass
//
Result = MachoGetClassNameFromMetaClassPointer (
MachoContext,
MachoGetSymbolName (MachoContext, MetaClass),
sizeof (SuperClassName),
SuperClassName
);
if (!Result) {
return FALSE;
}
Result = MachoGetVtableNameFromClassName (
SuperClassName,
sizeof (SuperVtableName),
SuperVtableName
);
if (!Result) {
return FALSE;
}
//
// Get the super vtable if it's been patched
//
SuperVtable = InternalGetOcVtableByName (
Context,
Kext,
SuperVtableName
);
if (SuperVtable == NULL) {
continue;
}
//
// Get the final symbol's name from the super vtable
//
Result = MachoGetFinalSymbolNameFromClassName (
SuperClassName,
sizeof (FinalSymbolName),
FinalSymbolName
);
if (!Result) {
return FALSE;
}
//
// Verify that the final symbol does not exist. First check
// all the externally defined symbols, then check locally.
//
OcSymbolDummy = InternalOcGetSymbolName (
Context,
Kext,
FinalSymbolName,
OcGetSymbolAnyLevel
);
if (OcSymbolDummy != NULL) {
return FALSE;
}
SymbolDummy = MachoGetLocalDefinedSymbolByName (
MachoContext,
FinalSymbolName
);
if (SymbolDummy != NULL) {
return FALSE;
}
//
// Patch the class's vtable
//
Result = InternalInitializeVtableByEntriesAndRelocations (
Context,
Kext,
SuperVtable,
EntryWalker->Vtable,
EntryWalker->VtableData,
EntryWalker->MetaSymsIndex,
EntryWalker->SolveSymbols,
CurrentVtable
);
if (!Result) {
DEBUG ((DEBUG_INFO, "OCAK: Failed to patch vtable for superclass %a\n", SuperClassName));
return FALSE;
}
CurrentVtable = GET_NEXT_PRELINKED_VTABLE (CurrentVtable);
//
// Get the meta vtable name from the class name
//
Result = MachoGetMetaVtableNameFromClassName (
ClassName,
sizeof (VtableName),
VtableName
);
if (!Result) {
return FALSE;
}
MetaVtable = InternalGetOcVtableByName (
Context,
Kext,
VtableName
);
if (MetaVtable != NULL) {
return FALSE;
}
//
// There is no way to look up a metaclass vtable at runtime, but
// we know that every class's metaclass inherits directly from
// OSMetaClass, so we just hardcode that vtable name here.
//
SuperVtable = InternalGetOcVtableByName (
Context,
Kext,
OS_METACLASS_VTABLE_NAME
);
if (SuperVtable == NULL) {
return FALSE;
}
//
// meta_vtable_sym will be null when we don't support strict
// patching and can't find the metaclass vtable. If that's the
// case, we just reduce the expect number of vtables by 1.
// Only i386 does not support strict patchting.
//
Result = InternalInitializeVtableByEntriesAndRelocations (
Context,
Kext,
SuperVtable,
EntryWalker->MetaVtable,
EntryWalker->MetaVtableData,
(EntryWalker->NumSolveSymbols - EntryWalker->MetaSymsIndex),
&EntryWalker->SolveSymbols[EntryWalker->MetaSymsIndex],
CurrentVtable
);
if (!Result) {
DEBUG ((DEBUG_INFO, "OCAK: Failed to patch meta vtable for superclass %a\n", SuperClassName));
return FALSE;
}
CurrentVtable = GET_NEXT_PRELINKED_VTABLE (CurrentVtable);
Kext->NumberOfVtables += 2;
EntryWalker->Smcp = NULL;
++NumPatched;
SuccessfulIteration = TRUE;
}
//
// Exit when there are unpatched VTables left, but there are none patched
// in a full iteration.
//
if (!SuccessfulIteration) {
return FALSE;
}
}
return TRUE;
}