diff --git a/.gitignore b/.gitignore index 968ed70d..631b8876 100644 --- a/.gitignore +++ b/.gitignore @@ -11,5 +11,6 @@ DICT fuzz-*.log crash-* oom-* +slow-unit-* out.bin prelinkedkernel.unpack diff --git a/Library/OcMachoPrelinkLib/Dependencies.c b/Library/OcMachoPrelinkLib/Dependencies.c deleted file mode 100644 index db2750e7..00000000 --- a/Library/OcMachoPrelinkLib/Dependencies.c +++ /dev/null @@ -1,1100 +0,0 @@ -#include - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "OcMachoPrelinkInternal.h" - -/** - Fills SymbolTable with the symbols provided in Symbols. For performance - reasons, the C++ symbols are continuously added to the top of the buffer. - Their order is not preserved. SymbolTable->SymbolTable is expected to be - a valid buffer that can store at least NumSymbols symbols. - - @param[in] MachoContext Context of the Mach-O. - @param[in] NumSymbols The number of symbols to copy. - @param[in] Symbols The source symbol array. - @param[in,out] SymbolTable The desination Symbol List. Must be able to - hold at least NumSymbols symbols. - -**/ -VOID -InternalFillSymbolTable64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN UINT32 NumSymbols, - IN CONST MACH_NLIST_64 *Symbols, - IN OUT OC_SYMBOL_TABLE_64 *SymbolTable - ) -{ - OC_SYMBOL_64 *WalkerBottom; - OC_SYMBOL_64 *WalkerTop; - UINT32 NumCxxSymbols; - UINT32 Index; - CONST MACH_NLIST_64 *Symbol; - CONST CHAR8 *Name; - BOOLEAN Result; - - ASSERT (MachoContext != NULL); - ASSERT (Symbols != NULL); - ASSERT (SymbolTable != NULL); - - WalkerBottom = &SymbolTable->Symbols[0]; - WalkerTop = &SymbolTable->Symbols[SymbolTable->NumSymbols - 1]; - - NumCxxSymbols = 0; - - for (Index = 0; Index < NumSymbols; ++Index) { - Symbol = &Symbols[Index]; - Name = MachoGetSymbolName64 (MachoContext, Symbol); - Result = MachoSymbolNameIsCxx (Name); - - if (!Result) { - WalkerBottom->StringIndex = Symbol->UnifiedName.StringIndex; - WalkerBottom->Value = Symbol->Value; - ++WalkerBottom; - } else { - WalkerTop->StringIndex = Symbol->UnifiedName.StringIndex; - WalkerTop->Value = Symbol->Value; - --WalkerTop; - - ++NumCxxSymbols; - } - } - - SymbolTable->NumSymbols = NumSymbols; - SymbolTable->NumCxxSymbols = NumCxxSymbols; -} - -STATIC -INTN -EFIAPI -AsciiKextNameCmp ( - IN CONST CHAR8 *FirstString, - IN CONST CHAR8 *SecondString - ) -{ - // - // ASSERT both strings are less long than PcdMaximumAsciiStringLength - // - ASSERT (AsciiStrSize (FirstString)); - ASSERT (AsciiStrSize (SecondString)); - - while ((*FirstString != '<') && (*FirstString == *SecondString)) { - FirstString++; - SecondString++; - } - - if ((*FirstString == '<') && (*SecondString == '0')) { - return 0; - } - - return (*FirstString - *SecondString); -} - -STATIC -CONST CHAR8 * -InternalFindPrelinkedKextPlist ( - IN CONST CHAR8 *PrelinkedPlist, - IN CONST CHAR8 *BundleId - ) -{ - CONST CHAR8 *Walker; - CONST CHAR8 *Walker2; - CONST CHAR8 *BundleIdKey; - INTN Result; - UINTN DictLevel; - // - // Iterate through all OS Bundle IDs. - // - BundleIdKey = "" OS_BUNDLE_IDENTIFIER_STR "<"; - - for ( - Walker = AsciiStrStr (PrelinkedPlist, BundleIdKey); - Walker != NULL; - Walker = AsciiStrStr (Walker, BundleIdKey) - ) { - Walker2 = AsciiStrStr (Walker, ""); - ASSERT (Walker2 != NULL); - Walker2 += L_STR_LEN (""); - - Result = AsciiKextNameCmp (Walker2, BundleId); - if (Result == 0) { - // - // The OS Bundle ID is in the first level of the KEXT PLIST dictionary. - // - DictLevel = 1; - - for (Walker -= L_STR_LEN (""); TRUE; --Walker) { - ASSERT (Walker >= PrelinkedPlist); - // - // Find the KEXT PLIST dictionary entry. - // - Result = AsciiStrnCmp (Walker, STR_N_HELPER ("")); - if (Result == 0) { - ++DictLevel; - Walker -= (L_STR_LEN ("") - 1); - continue; - } - - Result = AsciiStrnCmp (Walker, STR_N_HELPER ("")); - if (Result == 0) { - --DictLevel; - - if (DictLevel == 0) { - // - // Return the content within the KEXT PLIST dictionary. - // - return (Walker + L_STR_LEN ("")); - } - - Walker -= (L_STR_LEN ("") - 1); - continue; - } - } - } - - Walker = Walker2; - } - - return NULL; -} - -STATIC -CONST CHAR8 * -InternalKextCollectDependencies ( - IN CONST CHAR8 **BundleLibrariesStr, - OUT UINTN *NumberOfDependencies - ) -{ - CONST CHAR8 *Dependencies; - UINTN NumDependencies; - - CONST CHAR8 *Walker; - CHAR8 *DictEnd; - - Dependencies = NULL; - NumDependencies = 0; - DictEnd = NULL; - - Walker = *BundleLibrariesStr; - Walker += L_STR_LEN (OS_BUNDLE_LIBRARIES_STR ""); - // - // Retrieve the opening of the OSBundleLibraries key. - // - Walker = AsciiStrStr (Walker, "'); - ++Walker; - // - // Locate the closure of the OSBundleLibraries dict. Dicts inside - // OSBundleLibraries are not valid, so searching for the next match is - // fine. - // - DictEnd = AsciiStrStr (Walker, ""); - ASSERT (DictEnd != NULL); - // - // Temporarily terminate the PLIST string at the beginning of the - // OSBundleLibraries closing tag so that AsciiStrStr() calls do not exceed - // its bounds. - // - DictEnd[0] = '\0'; - // - // Cache the result to retrieve the dependencies from the prelinked PLIST - // later. - // - Dependencies = AsciiStrStr (Walker, ""); - ASSERT (Dependencies != NULL); - - for ( - Walker = Dependencies; - Walker != NULL; - Walker = AsciiStrStr (Walker, "") - ) { - ++NumDependencies; - // - // A dependency must have a non-empty name to be valid. - // A valid string-value for the version identifies is mandatory. - // - Walker += L_STR_LEN ("xx"); - } - // - // Restore the previously replaced opening brace. - // - DictEnd[0] = '<'; - } - - if (DictEnd != NULL) { - *BundleLibrariesStr = (DictEnd + L_STR_LEN ("")); - } else { - *BundleLibrariesStr = Walker; - } - - *NumberOfDependencies = NumDependencies; - - return Dependencies; -} - -STATIC -UINT16 -InternalStringToKextVersionWorker ( - IN CONST CHAR8 **VersionString - ) -{ - UINT16 Version; - - CONST CHAR8 *VersionStart; - CHAR8 *VersionEnd; - CONST CHAR8 *VersionReturn; - - VersionReturn = NULL; - - VersionStart = *VersionString; - VersionEnd = AsciiStrStr (VersionStart, "."); - - if (VersionEnd != NULL) { - *VersionEnd = '\0'; - } - - Version = (UINT16)AsciiStrDecimalToUintn (VersionStart); - - if (VersionEnd != NULL) { - *VersionEnd = '.'; - VersionReturn = (VersionEnd + 1); - ASSERT (*VersionReturn != '\0'); - } - - *VersionString = VersionReturn; - - return Version; -} - -STATIC -UINT64 -InternalStringToKextVersion ( - IN CONST CHAR8 *VersionString - ) -{ - OC_KEXT_VERSION KextVersion; - CONST CHAR8 *Walker; - - KextVersion.Value = 0; - - Walker = VersionString; - - KextVersion.Bits.Major = InternalStringToKextVersionWorker (&Walker); - ASSERT (KextVersion.Bits.Major != 0); - ASSERT (Walker != NULL); - - KextVersion.Bits.Minor = InternalStringToKextVersionWorker (&Walker); - ASSERT (KextVersion.Bits.Minor != 0); - - if (Walker == NULL) { - return KextVersion.Value; - } - - KextVersion.Bits.Revision = InternalStringToKextVersionWorker (&Walker); - ASSERT (KextVersion.Bits.Revision != 0); - - if (Walker == NULL) { - return KextVersion.Value; - } - - switch (Walker[0]) { - case 'd': - { - KextVersion.Bits.Stage = OcKextVersionStageDevelopment; - break; - } - - case 'a': - { - KextVersion.Bits.Stage = OcKextVersionStageAlpha; - break; - } - - case 'b': - { - KextVersion.Bits.Stage = OcKextVersionStageBeta; - break; - } - - case 'f': - { - KextVersion.Bits.Stage = OcKextVersionStageCandidate; - - if (Walker[1] == 'c') { - ++Walker; - } - ASSERT ((Walker[1] >= '0') && (Walker[1] <= '9')); - - break; - } - - default: - { - return 0; - } - } - - ++Walker; - - KextVersion.Bits.StageLevel = (UINT16)AsciiStrDecimalToUintn (Walker); - ASSERT (KextVersion.Bits.StageLevel != 0); - - return KextVersion.Value; -} - -VOID -InternalFreeDependencyEntry ( - IN OC_DEPENDENCY_INFO_ENTRY *Entry - ) -{ - if (Entry->Data.SymbolTable != NULL) { - FreePool (Entry->Data.SymbolTable); - } - - if (Entry->Data.Vtables != NULL) { - FreePool (Entry->Data.Vtables); - } - - RemoveEntryList (&Entry->Link); - - FreePool (Entry); -} - -LIST_ENTRY * -InternalRemoveDependency ( - IN CONST LIST_ENTRY *Dependencies, - IN UINTN NumRequests, - IN OUT OC_KEXT_REQUEST *Requests, - IN OC_DEPENDENCY_INFO_ENTRY *DependencyInfo - ) -{ - UINTN Index; - UINTN Index2; - OC_KEXT_REQUEST *Request; - OC_DEPENDENCY_INFO_ENTRY *Entry; - LIST_ENTRY *DependencyEntry; - OC_DEPENDENCY_INFO_ENTRY *DepWalker; - LIST_ENTRY *PreviousEntry; - BOOLEAN IsDependency; - - IsDependency = FALSE; - // - // Invalidate dependencies depending on this KEXT. - // - for ( - DependencyEntry = GetFirstNode (Dependencies); - !IsNull (DependencyEntry, Dependencies); - DependencyEntry = GetNextNode (Dependencies, DependencyEntry) - ) { - DepWalker = OC_DEP_INFO_FROM_LINK (DependencyEntry); - if (DepWalker == DependencyInfo) { - IsDependency = TRUE; - continue; - } - - for (Index = 0; Index < DepWalker->Info.NumDependencies; ++Index) { - Entry = DepWalker->Info.Dependencies[Index]; - if (Entry == DependencyInfo) { - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DepWalker - ); - break; - } - } - } - // - // Invalidate KEXT Requests depending on this KEXT. - // - for (Index = 0; Index < NumRequests; ++Index) { - Request = &Requests[Index]; - Entry = Request->Private.Info; - - if (Entry == DependencyInfo) { - continue; - } - - for (Index2 = 0; Index2 < Entry->Info.NumDependencies; ++Index2) { - if (Entry->Info.Dependencies[Index2] == DependencyInfo) { - InternalInvalidateKextRequest ( - Dependencies, - NumRequests, - Requests, - Request - ); - break; - } - } - } - // - // Prevent future linkage against this dependency. - // - if (IsDependency) { - // - // Return the dependency previous to this one, so that loops on the - // dependency list continue uninterrupted. - // - PreviousEntry = GetPreviousNode (Dependencies, &DependencyInfo->Link); - InternalFreeDependencyEntry (DependencyInfo); - return PreviousEntry; - } - - return NULL; -} - -VOID -InternalInvalidateKextRequest ( - IN CONST LIST_ENTRY *Dependencies, - IN UINTN NumRequests, - IN OUT OC_KEXT_REQUEST *Requests, - IN OC_KEXT_REQUEST *Request - ) -{ - OC_DEPENDENCY_INFO_ENTRY *DependencyInfo; - - DependencyInfo = Request->Private.Info; - - InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - - FreePool (DependencyInfo); - Request->Private.Info = NULL; -} - -STATIC -BOOLEAN -InternalConstructDependencyArraysWorker ( - IN UINTN NumDependencies, - IN OC_DEPENDENCY_INFO_ENTRY **Dependencies, - OUT OC_DEPENDENCY_DATA *DependencyData, - IN BOOLEAN IsTopLevel - ) -{ - OC_DEPENDENCY_INFO_ENTRY *Dependency; - OC_DEPENDENCY_INFO *Info; - UINTN Index; - BOOLEAN Result; - - for (Index = 0; Index < NumDependencies; ++Index) { - Dependency = Dependencies[Index]; - if (!Dependency->Prelinked) { - return FALSE; - } - - Info = &Dependency->Info; - - if (Dependency->Data.SymbolTable->Link.ForwardLink != NULL) { - // - // This dependency already has been added to the list and henceforth its - // dependencies as well. - // - continue; - } - - Result = InternalConstructDependencyArraysWorker ( - Info->NumDependencies, - Info->Dependencies, - DependencyData, - FALSE - ); - if (!Result) { - return FALSE; - } - - InsertHeadList ( - &DependencyData->SymbolTable->Link, - &Dependency->Data.SymbolTable->Link - ); - InsertHeadList ( - &DependencyData->Vtables->Link, - &Dependency->Data.Vtables->Link - ); - - Dependency->Data.SymbolTable->IsIndirect = !IsTopLevel; - } - - return TRUE; -} - -STATIC -OC_DEPENDENCY_INFO_ENTRY * -InternalGetDependencyInfoEntry ( - IN LIST_ENTRY *Dependencies, - IN UINTN NumRequests, OPTIONAL - IN OUT OC_KEXT_REQUEST *Requests, OPTIONAL - IN CONST CHAR8 *PrelinkedPlist, - IN CONST CHAR8 *DependencyString, - IN UINT64 KextsVirtual, - IN UINTN KextsPhysical - ) -{ - UINTN Index; - LIST_ENTRY *DependencyEntry; - OC_DEPENDENCY_INFO_ENTRY *DepWalker; - CONST CHAR8 *Name; - CONST CHAR8 *Version; - CHAR8 *VersionEnd; - UINT64 KextVersion; - CONST CHAR8 *KextPlist; - BOOLEAN Result; - - ASSERT (Dependencies != NULL); - ASSERT (PrelinkedPlist != NULL); - ASSERT (DependencyString != NULL); - ASSERT (KextsVirtual != 0); - ASSERT (KextsPhysical != 0); - - if (NumRequests > 0) { - ASSERT (Requests != NULL); - } - // - // Extract the information. - // - Name = (DependencyString + L_STR_LEN ("")); - - Version = AsciiStrStr ((Name + L_STR_LEN ("x")), "") + L_STR_LEN (">")); - - VersionEnd = AsciiStrStr (Version, "<"); - *VersionEnd = '\0'; - - KextVersion = InternalStringToKextVersion (Version); - - *VersionEnd = '<'; - // - // Find information among the already processed prelinked KEXTs. - // - for ( - DependencyEntry = GetFirstNode (Dependencies); - !IsNull (DependencyEntry, Dependencies); - DependencyEntry = GetNextNode (Dependencies, DependencyEntry) - ) { - DepWalker = OC_DEP_INFO_FROM_LINK (DependencyEntry); - if (AsciiKextNameCmp (Name, DepWalker->Info.Name) == 0) { - if ((KextVersion < DepWalker->Info.CompatibleVersion.Value) - || (KextVersion > DepWalker->Info.Version.Value)) { - return NULL; - } - - return DepWalker; - } - } - // - // Find information among the KEXTs to prelink. - // - for (Index = 0; Index < NumRequests; ++Index) { - DepWalker = Requests[Index].Private.Info; - if ((DepWalker != NULL) - && (AsciiKextNameCmp (Name, DepWalker->Info.Name) == 0)) { - if ((KextVersion < DepWalker->Info.CompatibleVersion.Value) - || (KextVersion > DepWalker->Info.Version.Value)) { - return NULL; - } - - Requests[Index].Private.IsDependedOn = TRUE; - return DepWalker; - } - } - // - // Find information among the remaining prelinked KEXTs. - // - KextPlist = InternalFindPrelinkedKextPlist (PrelinkedPlist, Name); - if (KextPlist == NULL) { - return NULL; - } - - DepWalker = InternalKextCollectInformation ( - KextPlist, - NULL, - KextsVirtual, - KextsPhysical, - KextVersion - ); - if (DepWalker != NULL) { - Result = InternalResolveDependencies ( - Dependencies, - 0, - NULL, - PrelinkedPlist, - DepWalker, - KextsVirtual, - KextsPhysical - ); - if (!Result) { - FreePool (DepWalker); - return NULL; - } - - DepWalker->Prelinked = TRUE; - InsertTailList (Dependencies, &DepWalker->Link); - return DepWalker; - } - - return NULL; -} - -STATIC -VOID -InternalDestructDependencyArraysWorker ( - OUT LIST_ENTRY *ListEntry - ) -{ - do { - // - // Only clear ForwardLink as BackLink is not explicitly checked. - // - ListEntry->ForwardLink = NULL; - ListEntry = ListEntry->BackLink; - } while (ListEntry->ForwardLink != NULL); -} - -OC_DEPENDENCY_INFO_ENTRY * -InternalKextCollectInformation ( - IN CONST CHAR8 *Plist, - IN OUT OC_MACHO_CONTEXT *MachoContext, OPTIONAL - IN UINT64 KextsVirtual, OPTIONAL - IN UINTN KextsPhysical, OPTIONAL - IN UINT64 RequestedVersion OPTIONAL - ) -{ - OC_DEPENDENCY_INFO DependencyInfoHdr; - CONST CHAR8 *Dependencies; - - MACH_HEADER_64 *MachHeader; - UINT32 MachoSize; - - OC_DEPENDENCY_INFO_ENTRY *DependencyEntry; - OC_DEPENDENCY_INFO *DependencyInfo; - - CONST CHAR8 *Walker; - INTN Result; - - UINT64 Value; - - ASSERT (Plist != NULL); - ASSERT (MachoContext != NULL); - - ZeroMem (&DependencyInfoHdr, sizeof (DependencyInfoHdr)); - Dependencies = NULL; - - if (MachoContext != NULL) { - MachHeader = MachoGetMachHeader64 (MachoContext); - ASSERT (MachHeader != NULL); - - MachoSize = MachoGetFileSize (MachoContext); - ASSERT (MachoSize != 0); - - DependencyInfoHdr.MachHeader = MachHeader; - DependencyInfoHdr.MachoSize = MachoSize; - } - // - // Skip to past the enclosing . - // - Plist = (AsciiStrStr (Plist, "") + L_STR_LEN ("")); - - for ( - Walker = InternalPlistStrStrSameLevelDown (Plist, STR_N_HELPER ("")); - Walker != NULL; - Walker = InternalPlistStrStrSameLevelDown (Walker, STR_N_HELPER ("")) - ) { - if ((DependencyInfoHdr.MachHeader != NULL) - && (DependencyInfoHdr.MachoSize != 0) - && (DependencyInfoHdr.Name != NULL) - && (DependencyInfoHdr.Version.Value != 0) - && (DependencyInfoHdr.CompatibleVersion.Value != 0) - && (Dependencies != NULL) - ) { - DependencyEntry = AllocatePool ( - sizeof (*DependencyEntry) - + (DependencyInfoHdr.NumDependencies - * sizeof (*DependencyInfo->Dependencies)) - ); - if (DependencyEntry != NULL) { - DependencyEntry->Signature = OC_DEPENDENCY_INFO_ENTRY_SIGNATURE; - DependencyEntry->Link.ForwardLink = NULL; - CopyMem ( - &DependencyEntry->Info, - &DependencyInfoHdr, - sizeof (DependencyEntry->Info) - ); - DependencyEntry->Prelinked = FALSE; - DependencyEntry->Data.SymbolTable = NULL; - DependencyEntry->Data.Vtables = NULL; - DependencyInfo = &DependencyEntry->Info; - // - // Temporarily store the Dependency strings there until resolution. - // - DependencyInfo->Dependencies[0] = (OC_DEPENDENCY_INFO_ENTRY *)( - Dependencies - ); - // - // Zero the other dependency values so they do not happen to equal to - // actual dependency info addresses. - // - ZeroMem ( - &DependencyInfo->Dependencies[1], - ((DependencyInfoHdr.NumDependencies - 1) - * sizeof (*DependencyInfo->Dependencies)) - ); - } - - return DependencyEntry; - } - - Walker += L_STR_LEN (""); - // - // Retrieve the KEXT's name. - // - if (DependencyInfoHdr.Name == NULL) { - Result = AsciiStrnCmp ( - Walker, - STR_N_HELPER (OS_BUNDLE_IDENTIFIER_STR "<") - ); - if (Result == 0) { - Walker += L_STR_LEN (OS_BUNDLE_IDENTIFIER_STR ""); - Walker = AsciiStrStr (Walker, ""); - ASSERT (Walker != NULL); - Walker += L_STR_LEN (""); - - DependencyInfoHdr.Name = Walker; - - Walker += L_STR_LEN ("x"); - continue; - } - } - // - // Retrieve the KEXT's version. - // - if (DependencyInfoHdr.Version.Value == 0) { - Result = AsciiStrnCmp ( - Walker, - STR_N_HELPER (OS_BUNDLE_COMPATIBLE_VERSION_STR "<") - ); - if (Result == 0) { - Walker += L_STR_LEN (OS_BUNDLE_COMPATIBLE_VERSION_STR ""); - Walker = AsciiStrStr (Walker, ""); - ASSERT (Walker != NULL); - Walker += L_STR_LEN (""); - - DependencyInfoHdr.Version.Value = InternalStringToKextVersion (Walker); - if ((DependencyInfoHdr.Version.Value == 0) - || ((RequestedVersion != 0) - && (RequestedVersion > DependencyInfoHdr.Version.Value)) - ) { - return NULL; - } - - Walker += L_STR_LEN ("x"); - continue; - } - } - // - // Retrieve the KEXT's compatible version. - // - if (DependencyInfoHdr.CompatibleVersion.Value == 0) { - Result = AsciiStrnCmp ( - Walker, - STR_N_HELPER (OS_BUNDLE_COMPATIBLE_VERSION_STR "<") - ); - if (Result == 0) { - Walker += L_STR_LEN (OS_BUNDLE_COMPATIBLE_VERSION_STR ""); - Walker = AsciiStrStr (Walker, ""); - ASSERT (Walker != NULL); - Walker += L_STR_LEN (""); - - DependencyInfoHdr.CompatibleVersion.Value = InternalStringToKextVersion ( - Walker - ); - if ((DependencyInfoHdr.Version.Value == 0) - || ((RequestedVersion != 0) - && (RequestedVersion < DependencyInfoHdr.CompatibleVersion.Value)) - ) { - return NULL; - } - - Walker += L_STR_LEN ("x"); - continue; - } - } - // - // Retrieve the KEXT's dependency information. - // - if (Dependencies == NULL) { - Result = AsciiStrnCmp ( - Walker, - STR_N_HELPER (OS_BUNDLE_LIBRARIES_STR "<") - ); - if (Result == 0) { - Dependencies = InternalKextCollectDependencies ( - &Walker, - &DependencyInfoHdr.NumDependencies - ); - } - } - - if (DependencyInfoHdr.MachHeader == NULL) { - Result = AsciiStrnCmp ( - Walker, - STR_N_HELPER ("_PrelinkExecutableSourceAddr<") - ); - if (Result == 0) { - Walker += L_STR_LEN ("_PrelinkExecutableSourceAddr"); - Walker = AsciiStrStr (Walker, " KextsVirtual); - DependencyInfoHdr.MachHeader = (MACH_HEADER_64 *)( - (UINTN)(Value - KextsVirtual) - + KextsPhysical - ); - } - } - - if (DependencyInfoHdr.MachoSize == 0) { - Result = AsciiStrnCmp ( - Walker, - STR_N_HELPER ("_PrelinkExecutableSize<") - ); - if (Result == 0) { - Walker += L_STR_LEN ("_PrelinkExecutableSize"); - Walker = AsciiStrStr (Walker, "Info.Dependencies[0]; - Index < KextInfo->Info.NumDependencies; - ++Index, DepWalker = AsciiStrStr (DepWalker, "") - ) { - DepEntry = InternalGetDependencyInfoEntry ( - Dependencies, - NumRequests, - Requests, - PrelinkedPlist, - DepWalker, - KextsVirtual, - KextsPhysical - ); - if (DepEntry == NULL) { - // - // Cleaning up cannot be done here as other KEXT Requests might still - // have the temporary strings as their dependency array and this function - // has no mean of verifying which do or do't. - // - return FALSE; - } - - KextInfo->Info.Dependencies[Index] = DepEntry; - } - - return TRUE; -} - -UINT64 -InternalGetNewPrelinkedKextLoadAddress ( - IN OUT OC_MACHO_CONTEXT *KernelContext, - IN CONST MACH_SEGMENT_COMMAND_64 *PrelinkedKextsSegment, - IN CONST CHAR8 *PrelinkedPlist - ) -{ - UINT64 Address; - - CONST MACH_HEADER_64 *KernelHeader; - UINTN KextSize; - CONST CHAR8 *Key; - CONST CHAR8 *HighestKey; - CONST CHAR8 *NextTag; - CONST CHAR8 *Walker; - UINT64 TempAddress; - BOOLEAN Result; - OC_MACHO_CONTEXT KextContext; - MACH_HEADER_64 *KextHeader; - - Address = 0; - NextTag = NULL; - HighestKey = NULL; - - for ( - Key = AsciiStrStr (PrelinkedPlist, "_PrelinkExecutableLoadAddr<"); - Key != NULL; - Key = AsciiStrStr (Walker, "_PrelinkExecutableLoadAddr<") - ) { - Walker = Key; - Walker += L_STR_LEN ("_PrelinkExecutableLoadAddr"); - Walker = AsciiStrStr (Walker, " Address) { - Address = TempAddress; - HighestKey = Key; - NextTag = Walker; - } - } - - if ((Address == 0) - || (Address < PrelinkedKextsSegment->VirtualAddress)) { - return FALSE; - } - - Walker = InternalPlistStrStrSameLevel ( - HighestKey, - STR_N_HELPER ("_PrelinkExecutableSize<"), - (NextTag - HighestKey) - ); - if (Walker == 0) { - return 0; - } - - Walker += L_STR_LEN ("_PrelinkExecutableSize"); - - KextSize = InternalPlistGetIntValue (&Walker); - ASSERT (KextSize != 0); - ASSERT (KextSize != MAX_UINT64); - - KernelHeader = MachoGetMachHeader64 (KernelContext); - ASSERT (KernelHeader != NULL); - - Result = OcOverflowAddUN ( - (Address - PrelinkedKextsSegment->VirtualAddress), - ((UINTN)KernelHeader + PrelinkedKextsSegment->FileOffset), - &Address - ); - if (Result) { - return FALSE; - } - - KextHeader = (MACH_HEADER_64 *)Address; - if (!OC_ALIGNED (KextHeader)) { - return FALSE; - } - - Result = MachoInitializeContext (&KextContext, KextHeader, KextSize); - if (!Result) { - return 0; - } - - return ALIGN_VALUE (MachoGetLastAddress64 (&KextContext), BASE_4KB); -} - -VOID -InternalDestructDependencyArrays ( - OUT CONST OC_DEPENDENCY_DATA *DependencyData - ) -{ - InternalDestructDependencyArraysWorker ( - &DependencyData->SymbolTable->Link - ); - InternalDestructDependencyArraysWorker ( - &DependencyData->Vtables->Link - ); -} - -BOOLEAN -InternalConstructDependencyArrays ( - IN UINTN NumDependencies, - IN OC_DEPENDENCY_INFO_ENTRY **Dependencies, - OUT OC_DEPENDENCY_DATA *DependencyData - ) -{ - OC_DEPENDENCY_INFO *Info; - BOOLEAN Result; - - if (!Dependencies[0]->Prelinked) { - return FALSE; - } - - Info = &Dependencies[0]->Info; - - DependencyData->SymbolTable = Dependencies[0]->Data.SymbolTable; - DependencyData->Vtables = Dependencies[0]->Data.Vtables; - - InitializeListHead (&DependencyData->SymbolTable->Link); - InitializeListHead (&DependencyData->Vtables->Link); - DependencyData->SymbolTable->IsIndirect = FALSE; - // - // Process the first dependency's dependencies as it itself has already been - // processed implicitly above. - // - Result = InternalConstructDependencyArraysWorker ( - Info->NumDependencies, - Info->Dependencies, - DependencyData, - FALSE - ); - if (Result) { - Result = InternalConstructDependencyArraysWorker ( - (NumDependencies - 1), - &Dependencies[1], - DependencyData, - TRUE - ); - } - - if (!Result) { - InternalDestructDependencyArrays (DependencyData); - } - - return Result; -} diff --git a/Library/OcMachoPrelinkLib/OcMachoPrelinkInternal.h b/Library/OcMachoPrelinkLib/OcMachoPrelinkInternal.h deleted file mode 100644 index 0fb37e61..00000000 --- a/Library/OcMachoPrelinkLib/OcMachoPrelinkInternal.h +++ /dev/null @@ -1,431 +0,0 @@ -#ifndef OC_MACHO_PRELINK_INTERNAL_H_ -#define OC_MACHO_PRELINK_INTERNAL_H_ - -#include - -#include - -#define KXLD_WEAK_TEST_SYMBOL "_gOSKextUnresolved" - -#define OS_METACLASS_VTABLE_NAME "__ZTV11OSMetaClass" - -#define X86_64_RIP_RELATIVE_LIMIT 0x80000000ULL - -#define SYM_MAX_NAME_LEN 256U - -#define VTABLE_ENTRY_SIZE_64 8U -#define VTABLE_HEADER_LEN_64 2U -#define VTABLE_HEADER_SIZE_64 (VTABLE_HEADER_LEN_64 * VTABLE_ENTRY_SIZE_64) - -#define OS_BUNDLE_LIBRARIES_STR "OSBundleLibraries" -#define OS_BUNDLE_IDENTIFIER_STR "CFBundleIdentifier" -#define OS_BUNDLE_VERSION_STR "CFBundleVersion" -#define OS_BUNDLE_COMPATIBLE_VERSION_STR "OSBundleCompatibleVersion" - -typedef struct { - UINT32 StringIndex; ///< index into the string table - UINT64 Value; ///< value of this symbol (or stab offset) -} OC_SYMBOL_64; - -#define OC_SYMBOL_TABLE_64_SIGNATURE SIGNATURE_32 ('O', 'S', '6', '4') - -/** - Gets the next element in a linked list of OC_SYMBOL_TABLE_64. - - @param[in] This The current ListEntry. - -**/ -#define GET_OC_SYMBOL_TABLE_64_FROM_LINK(This) \ - CR ( \ - (This), \ - OC_SYMBOL_TABLE_64, \ - Link, \ - OC_SYMBOL_TABLE_64_SIGNATURE \ - ) - -#define STR_LEN(String) (ARRAY_SIZE (String) - 1) -#define STR_N_HELPER(String) (String), STR_LEN (String) - -typedef struct { - /// - /// These data are used to construct linked lists of dependency information - /// for each KEXT. It is declared hear for every dependency will - /// eventually be part of a list and to save separate allocations per KEXT. - /// - UINT32 Signature; - LIST_ENTRY Link; - BOOLEAN IsIndirect; - /// - /// The String Table associated with this symbol table. - /// - CONST CHAR8 *StringTable; - /// - /// The number of symbols in the entire symbols buffer. - /// - UINT32 NumSymbols; - /// - /// The number of C++ symbols at the end of the symbols buffer. - /// - UINT32 NumCxxSymbols; - OC_SYMBOL_64 Symbols[]; ///< The symbol buffer. -} OC_SYMBOL_TABLE_64; - -typedef struct { - CONST CHAR8 *Name; ///< The symbol's name. - UINT64 Address; ///< The symbol's address. -} OC_VTABLE_ENTRY; - -#define GET_NEXT_OC_VTABLE(This) \ - ((OC_VTABLE *)(&(This)->Entries[(This)->NumEntries])) - -typedef struct { - CONST CHAR8 *Name; ///< The VTable's name. - UINT32 NumEntries; ///< The number of VTable entries. - OC_VTABLE_ENTRY Entries[]; ///< The VTable entries. -} OC_VTABLE; - -#define OC_VTABLE_ARRAY_SIGNATURE SIGNATURE_32 ('O', 'V', 'T', 'A') - -/** - Gets the next element in a linked list of OC_VTABLE_ARRAY. - - @param[in] This The current ListEntry. - -**/ -#define GET_OC_VTABLE_ARRAY_FROM_LINK(This) \ - CR ( \ - (This), \ - OC_VTABLE_ARRAY, \ - Link, \ - OC_VTABLE_ARRAY_SIGNATURE \ - ) - -#define GET_FIRST_OC_VTABLE(This) \ - ((OC_VTABLE *)((This) + 1)) - -typedef struct { - /// - /// These data are used to construct linked lists of dependency information - /// for each KEXT. It is declared hear for every dependency will - /// eventually be part of a list and to save separate allocations per KEXT. - /// - UINT32 Signature; - LIST_ENTRY Link; - /// - /// The number of VTables in the array. - /// - UINT32 NumVtables; - // - // NOTE: This is an array that cannot be declared as such as OC_VTABLE - // contains a flexible array itself. As the size is dynamic, do not - // try to use pointer arithmetics. - // - /// - /// VTable array. - /// - OC_VTABLE Vtables; -} OC_VTABLE_ARRAY; - -typedef union { - struct { - UINT32 Major : 14; - UINT32 Minor : 7; - UINT32 Revision : 7; - UINT32 Stage : 4; - UINT32 StageLevel : 8; - } Bits; - UINT64 Value; -} OC_KEXT_VERSION; - -typedef enum { - OcKextVersionStageDevelopment = 1, - OcKextVersionStageAlpha = 3, - OcKextVersionStageBeta = 5, - OcKextVersionStageCandidate = 7, - OcKextVersionStageRelease = 9 -} OC_KEXT_VERSION_STAGE; - -typedef struct { - OC_SYMBOL_TABLE_64 *SymbolTable; - OC_VTABLE_ARRAY *Vtables; -} OC_DEPENDENCY_DATA; - -#define OC_DEPENDENCY_INFO_ENTRY_SIGNATURE \ - SIGNATURE_32 ('O', 'D', 'I', 'E') - -#define OC_DEP_INFO_FROM_LINK(This) \ - (CR ( \ - (This), \ - OC_DEPENDENCY_INFO_ENTRY, \ - Link, \ - OC_DEPENDENCY_INFO_ENTRY_SIGNATURE \ - )) - -typedef struct OC_DEPENDENCY_INFO_ENTRY_ OC_DEPENDENCY_INFO_ENTRY; - -typedef struct { - MACH_HEADER_64 *MachHeader; - UINT32 MachoSize; - CONST CHAR8 *Name; - OC_KEXT_VERSION Version; - OC_KEXT_VERSION CompatibleVersion; - UINTN NumDependencies; - OC_DEPENDENCY_INFO_ENTRY *Dependencies[]; -} OC_DEPENDENCY_INFO; - -struct OC_DEPENDENCY_INFO_ENTRY_ { - UINT32 Signature; - LIST_ENTRY Link; - BOOLEAN Prelinked; - OC_DEPENDENCY_DATA Data; - OC_DEPENDENCY_INFO Info; -}; - -typedef struct { - struct { - MACH_HEADER_64 *MachHeader; - UINT32 MachoSize; - CONST CHAR8 *Plist; - } Input; - - struct { - BOOLEAN Linked; - } Output; - // - // Private data. - // - struct { - OC_DEPENDENCY_INFO_ENTRY *Info; - OC_MACHO_CONTEXT MachoContext; - MACH_SEGMENT_COMMAND_64 *LinkEdit; - BOOLEAN IsDependedOn; - } Private; -} OC_KEXT_REQUEST; - -typedef struct { - CONST MACH_NLIST_64 *Smcp; - CONST MACH_NLIST_64 *Vtable; - CONST MACH_NLIST_64 *MetaVtable; -} OC_VTABLE_PATCH_ENTRY; - -typedef struct { - UINTN NumEntries; - OC_VTABLE_PATCH_ENTRY Entries[]; -} OC_VTABLE_PATCH_ARRAY; - -typedef struct { - UINT32 NumSymbols; - CONST MACH_NLIST_64 *Symbols[]; -} OC_VTABLE_EXPORT_ARRAY; - -// -// Dependencies -// - -OC_DEPENDENCY_INFO_ENTRY * -InternalKextCollectInformation ( - IN CONST CHAR8 *Plist, - IN OUT OC_MACHO_CONTEXT *MachoContext, OPTIONAL - IN UINT64 KextsVirtual, OPTIONAL - IN UINTN KextsPhysical, OPTIONAL - IN UINT64 RequestedVersion OPTIONAL - ); - -VOID -InternalFreeDependencyEntry ( - IN OC_DEPENDENCY_INFO_ENTRY *Entry - ); - -BOOLEAN -InternalResolveDependencies ( - IN LIST_ENTRY *Dependencies, - IN UINTN NumRequests, OPTIONAL - IN OUT OC_KEXT_REQUEST *Requests, OPTIONAL - IN CONST CHAR8 *PrelinkedPlist, - IN OC_DEPENDENCY_INFO_ENTRY *KextInfo, - IN UINT64 KextsVirtual, - IN UINTN KextsPhysical - ); - -LIST_ENTRY * -InternalRemoveDependency ( - IN CONST LIST_ENTRY *Dependencies, - IN UINTN NumRequests, - IN OUT OC_KEXT_REQUEST *Requests, - IN OC_DEPENDENCY_INFO_ENTRY *DepInfo - ); - -VOID -InternalInvalidateKextRequest ( - IN CONST LIST_ENTRY *Dependencies, - IN UINTN NumRequests, - IN OUT OC_KEXT_REQUEST *Requests, - IN OC_KEXT_REQUEST *Request - ); - -BOOLEAN -InternalConstructDependencyArrays ( - IN UINTN NumDependencies, - IN OC_DEPENDENCY_INFO_ENTRY **Dependencies, - OUT OC_DEPENDENCY_DATA *DependencyData - ); - -VOID -InternalDestructDependencyArrays ( - OUT CONST OC_DEPENDENCY_DATA *DependencyData - ); - -UINT64 -InternalGetNewPrelinkedKextLoadAddress ( - IN OUT OC_MACHO_CONTEXT *KernelContext, - IN CONST MACH_SEGMENT_COMMAND_64 *PrelinkedKextsSegment, - IN CONST CHAR8 *PrelinkedPlist - ); - -/** - Fills SymbolTable with the symbols provided in Symbols. For performance - reasons, the C++ symbols are continuously added to the top of the buffer. - Their order is not preserved. SymbolTable->SymbolTable is expected to be - a valid buffer that can store at least NumSymbols symbols. - - @param[in] MachoContext Context of the Mach-O. - @param[in] NumSymbols The number of symbols to copy. - @param[in] Symbols The source symbol array. - @param[in,out] SymbolTable The desination Symbol List. Must be able to - hold at least NumSymbols symbols. - -**/ -VOID -InternalFillSymbolTable64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN UINT32 NumSymbols, - IN CONST MACH_NLIST_64 *Symbols, - IN OUT OC_SYMBOL_TABLE_64 *SymbolTable - ); - -// -// PLIST -// - -CONST CHAR8 * -InternalPlistStrStrSameLevelUp ( - IN CONST CHAR8 *AnchorString, - IN CONST CHAR8 *FindString, - IN UINTN FindStringLen - ); - -CONST CHAR8 * -InternalPlistStrStrSameLevelDown ( - IN CONST CHAR8 *AnchorString, - IN CONST CHAR8 *FindString, - IN UINTN FindStringLen - ); - -CONST CHAR8 * -InternalPlistStrStrSameLevel ( - IN CONST CHAR8 *AnchorString, - IN CONST CHAR8 *FindString, - IN UINTN FindStringLen, - IN UINTN DownwardsOffset - ); - -UINT64 -InternalPlistGetIntValue ( - IN CONST CHAR8 **Tag - ); - -// -// VTables -// - -UINT32 -InternalGetVtableSize64 ( - IN CONST UINT64 *VtableData - ); - -BOOLEAN -InternalGetVtableSizeWithRelocs64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST UINT64 *VtableData, - OUT UINT32 *VtableSize - ); - -BOOLEAN -InternalPrepareVtableCreationNonPrelinked64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN UINT32 NumSymbols, - IN CONST MACH_NLIST_64 *SymbolTable, - OUT OC_VTABLE_PATCH_ARRAY *PatchData - ); - -BOOLEAN -InternalCreateVtablesNonPrelinked64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_DEPENDENCY_DATA *DependencyData, - IN OC_VTABLE_PATCH_ARRAY *PatchData, - OUT OC_VTABLE_ARRAY *VtableArray - ); - -BOOLEAN -InternalPrepareCreateVtablesPrelinked64 ( - IN OC_MACHO_CONTEXT *MachoContext, - OUT OC_VTABLE_EXPORT_ARRAY *VtableExport, - IN UINT32 VtableExportSize - ); - -BOOLEAN -InternalCreateVtablesPrelinked64 ( - IN OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN OC_VTABLE_EXPORT_ARRAY *VtableExport, - OUT OC_VTABLE *VtableBuffer - ); - -// -// Prelink -// - -// TODO: Move? -CONST OC_SYMBOL_64 * -InternalOcGetSymbolByName ( - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN CONST CHAR8 *Name, - IN BOOLEAN CheckIndirect - ); - -VOID -InternalSolveSymbolValue64 ( - IN UINT64 Value, - OUT MACH_NLIST_64 *Symbol - ); - -/** - Prelinks the specified KEXT against the specified LinkAddress and the data - of its dependencies. - - @param[in,out] MachoContext Mach-O context of the KEXT to prelink. - @param[in] LinkEditSegment __LINKEDIT segment of the KEXT to prelink. - @param[in] LinkAddress The address this KEXT shall be linked - against. - @param[in] DependencyData List of data of all dependencies. - @param[in] ExposeSymbols Whether the symbol table shall be exposed. - @param[out] OutputData Buffer to output data into. - @param[out] ScratchMemory Scratch memory buffer that is at least as big - as the KEXT's __LINKEDIT segment. - - @retval Returned is whether the prelinking process has been successful. - The state of the KEXT is undefined in case this routine fails. - -**/ -BOOLEAN -InternalPrelinkKext64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN MACH_SEGMENT_COMMAND_64 *LinkEditSegment, - IN UINT64 LinkAddress, - IN OC_DEPENDENCY_DATA *DependencyData, - IN BOOLEAN ExposeSymbols, - IN OUT OC_DEPENDENCY_DATA *OutputData, - OUT VOID *ScratchMemory - ); - -#endif // OC_MACHO_PRELINK_INTERNAL_H_ diff --git a/Library/OcMachoPrelinkLib/OcMachoPrelinkLib.c b/Library/OcMachoPrelinkLib/OcMachoPrelinkLib.c deleted file mode 100644 index ad3b1729..00000000 --- a/Library/OcMachoPrelinkLib/OcMachoPrelinkLib.c +++ /dev/null @@ -1,476 +0,0 @@ -/** @file - Library handling KEXT prelinking. - Currently limited to Intel 64 architectures. - -Copyright (c) 2018, Download-Fritz. All rights reserved.
-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 - -#include - -#include -#include -#include -#include -#include - -#include "OcMachoPrelinkInternal.h" - -STATIC -BOOLEAN -InternalPrelinkKextsWorker ( - IN OC_MACHO_CONTEXT *KernelContext, - IN UINTN NumRequests, - IN OUT OC_KEXT_REQUEST *Requests, - IN LIST_ENTRY *Dependencies, - IN CONST MACH_SEGMENT_COMMAND_64 *PrelinkTextSegment, - IN CONST CHAR8 *PrelinkedPlist - ) -{ - UINTN Index; - OC_KEXT_REQUEST *Request; - LIST_ENTRY *DependencyEntry; - OC_DEPENDENCY_INFO_ENTRY *DependencyInfo; - OC_DEPENDENCY_INFO *Info; - OC_DEPENDENCY_DATA DependencyData; - UINT64 LoadAddress; - BOOLEAN Result; - BOOLEAN OneLinked; - BOOLEAN OneUnlinked; - MACH_SEGMENT_COMMAND_64 *LinkEdit; - UINTN ScratchSize; - VOID *ScratchMemory; - OC_DEPENDENCY_DATA *CurrentData; - OC_MACHO_CONTEXT MachoContext; - CONST MACH_HEADER_64 *MachHeader; - CONST MACH_NLIST_64 *SymbolTable; - UINT32 NumSymbols; - OC_SYMBOL_TABLE_64 *OcSymbolTable; - OC_VTABLE_ARRAY *Vtables; - OC_VTABLE_EXPORT_ARRAY *VtableExport; - - UINT32 VtableOffset; - CONST UINT64 *VtableData; - UINTN VtablesSize; - - ASSERT (KernelContext != NULL); - ASSERT (NumRequests > 0); - ASSERT (Requests != NULL); - ASSERT (Dependencies != NULL); - ASSERT (PrelinkTextSegment != NULL); - ASSERT (PrelinkedPlist != NULL); - - ScratchSize = 0; - - for (Index = 0; Index < NumRequests; ++Index) { - Request = &Requests[Index]; - if (Request->Private.Info == NULL) { - continue; - } - // - // Retrieve the __LINKEDIT segment. - // - Result = MachoGetSegmentByName64 ( - &Request->Private.MachoContext, - "__LINKEDIT", - &LinkEdit - ); - if (!Result || (LinkEdit == NULL)) { - InternalInvalidateKextRequest ( - Dependencies, - NumRequests, - Requests, - Request - ); - continue; - } - - Request->Private.LinkEdit = LinkEdit; - - if (ScratchSize < LinkEdit->FileSize) { - ScratchSize = LinkEdit->FileSize; - } - } - - if (ScratchSize == 0) { - return FALSE; - } - - ScratchMemory = AllocatePool (ScratchSize); - if (ScratchMemory == NULL) { - return FALSE; - } - - VtableExport = (OC_VTABLE_EXPORT_ARRAY *)ScratchMemory; - - for ( - DependencyEntry = GetFirstNode (Dependencies); - !IsNull (DependencyEntry, Dependencies); - DependencyEntry = GetNextNode (Dependencies, DependencyEntry), - InternalDestructDependencyArrays (&DependencyData) - ) { - DependencyInfo = OC_DEP_INFO_FROM_LINK (DependencyEntry); - CurrentData = &DependencyInfo->Data; - // - // Try to construct the dependency tree first. - // - Info = &DependencyInfo->Info; - Result = InternalConstructDependencyArrays ( - Info->NumDependencies, - Info->Dependencies, - &DependencyData - ); - if (!Result) { - // - // Prelinked dependencies can only depend on other already prelinked - // KEXTs. - // - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - continue; - } - // - // Create a Mach-O Context for this KEXT. - // - Result = MachoInitializeContext ( - &MachoContext, - Info->MachHeader, - Info->MachoSize - ); - if (!Result) { - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - continue; - } - - NumSymbols = MachoGetSymbolTable ( - &MachoContext, - &SymbolTable, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - ); - if (NumSymbols == 0) { - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - continue; - } - - OcSymbolTable = AllocatePool ( - sizeof (*OcSymbolTable) - + (NumSymbols * sizeof (*OcSymbolTable->Symbols)) - ); - if (OcSymbolTable == NULL) { - FreePool (ScratchMemory); - return FALSE; - } - - InternalFillSymbolTable64 ( - &MachoContext, - NumSymbols, - SymbolTable, - OcSymbolTable - ); - - Result = InternalPrepareCreateVtablesPrelinked64 ( - &MachoContext, - VtableExport, - ScratchSize - ); - if (!Result) { - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - FreePool (OcSymbolTable); - continue; - } - - MachHeader = MachoGetMachHeader64 (&MachoContext); - ASSERT (MachHeader != NULL); - - Result = TRUE; - VtablesSize = 0; - - for (Index = 0; Index < VtableExport->NumSymbols; ++Index) { - Result = MachoSymbolGetFileOffset64 ( - &MachoContext, - VtableExport->Symbols[Index], - &VtableOffset - ); - if (!Result) { - break; - } - - VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset); - if (!OC_ALIGNED (VtableData)) { - Result = FALSE; - break; - } - - VtablesSize += InternalGetVtableSize64 (VtableData); - } - - if (!Result) { - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - FreePool (OcSymbolTable); - continue; - } - - Vtables = AllocatePool (sizeof (*Vtables) + VtablesSize); - if (Vtables == NULL) { - FreePool (OcSymbolTable); - FreePool (ScratchMemory); - return FALSE; - } - - Result = InternalCreateVtablesPrelinked64 ( - &MachoContext, - DependencyData.SymbolTable, - VtableExport, - GET_FIRST_OC_VTABLE (Vtables) - ); - if (!Result) { - DependencyEntry = InternalRemoveDependency ( - Dependencies, - NumRequests, - Requests, - DependencyInfo - ); - FreePool (Vtables); - FreePool (OcSymbolTable); - continue; - } - - CurrentData->SymbolTable = OcSymbolTable; - CurrentData->Vtables = Vtables; - } - - do { - OneLinked = FALSE; - OneUnlinked = FALSE; - - for (Index = 0; Index < NumRequests; ++Index) { - Request = &Requests[Index]; - DependencyInfo = Request->Private.Info; - if ((DependencyInfo == NULL) || DependencyInfo->Prelinked) { - continue; - } - - Info = &DependencyInfo->Info; - Result = InternalConstructDependencyArrays ( - Info->NumDependencies, - Info->Dependencies, - &DependencyData - ); - if (!Result) { - // - // The KEXT depends on another KEXT staged for prelinking which has not - // been processed yet. Postpone to another iteration. - // - OneUnlinked = TRUE; - continue; - } - // - // Retrieve a loading address for the KEXT. - // - LoadAddress = InternalGetNewPrelinkedKextLoadAddress ( - KernelContext, - PrelinkTextSegment, - PrelinkedPlist - ); - if (LoadAddress == 0) { - FreePool (ScratchMemory); - return FALSE; - } - // - // Prelink the KEXT. - // - Result = InternalPrelinkKext64 ( - &Request->Private.MachoContext, - Request->Private.LinkEdit, - LoadAddress, - &DependencyData, - Request->Private.IsDependedOn, - &DependencyInfo->Data, - ScratchMemory - ); - if (Result) { - DependencyInfo->Prelinked = TRUE; - Request->Output.Linked = TRUE; - OneLinked = TRUE; - } else { - InternalInvalidateKextRequest ( - Dependencies, - NumRequests, - Requests, - Request - ); - } - } - // - // Continue while the loop makes progress and there are still KEXTs to - // link. - // - } while (OneLinked && OneUnlinked); - - FreePool (ScratchMemory); - - return TRUE; -} - -BOOLEAN -OcPrelinkKexts64 ( - IN OC_MACHO_CONTEXT *KernelContext, - IN UINTN NumRequests, - IN OUT OC_KEXT_REQUEST *Requests - ) -{ - CONST MACH_HEADER_64 *KernelHeader; - UINTN Index; - OC_KEXT_REQUEST *Request; - OC_DEPENDENCY_INFO_ENTRY *DependencyInfo; - BOOLEAN Result; - MACH_SEGMENT_COMMAND_64 *PrelinkTextSegment; - CONST CHAR8 *PrelinkedPlist; - LIST_ENTRY Dependencies; - LIST_ENTRY *DependencyEntry; - UINT64 KextAddress; - - ASSERT (KernelContext != NULL); - ASSERT (NumRequests > 0); - ASSERT (Requests != NULL); - - KernelHeader = MachoGetMachHeader64 (KernelContext); - ASSERT (KernelHeader != NULL); - // - // Collect the KEXT information for all KEXTs to be prelinked. Do not solve - // dependencies yet so we can verify successful dependency against other - // KEXTs to be prelinked in one go. - // - for (Index = 0; Index < NumRequests; ++Index) { - Request = &Requests[Index]; - // - // Create a Mach-O Context for this KEXT. - // - Result = MachoInitializeContext ( - &Request->Private.MachoContext, - Request->Input.MachHeader, - Request->Input.MachoSize - ); - if (Result) { - Request->Output.Linked = FALSE; - Request->Private.IsDependedOn = FALSE; - Request->Private.Info = InternalKextCollectInformation ( - Request->Input.Plist, - &Request->Private.MachoContext, - 0, - 0, - 0 - ); - } else { - Request->Private.Info = NULL; - } - } - - PrelinkedPlist = (CHAR8 *)((UINTN)KernelHeader + PrelinkInfoSection->Offset); - // - // Resolve dependencies. - // - InitializeListHead (&Dependencies); - - for (Index = 0; Index < NumRequests; ++Index) { - Request = &Requests[Index]; - if (Request->Private.Info != NULL) { - Result = FALSE; - - KextAddress = (UINTN)Request->Input.MachHeader; - KextAddress += PrelinkTextSegment->FileOffset; - if (KextAddress == (UINTN)KextAddress) { - Result = InternalResolveDependencies ( - &Dependencies, - NumRequests, - Requests, - PrelinkedPlist, - Request->Private.Info, - PrelinkTextSegment->VirtualAddress, - (UINTN)KextAddress - ); - } - - if (!Result) { - // - // Only pass the KEXT Requests that already had their dependencies - // resolved. - // - InternalRemoveDependency ( - &Dependencies, - Index, - Requests, - Request->Private.Info - ); - } - } - } - // - // Link the KEXTs. - // - Result = InternalPrelinkKextsWorker ( - KernelContext, - NumRequests, - Requests, - &Dependencies, - PrelinkTextSegment, - PrelinkedPlist - ); - // - // Free all resources. - // - for (Index = 0; Index < NumRequests; ++Index) { - Request = &Requests[Index]; - if (Request->Private.Info != NULL) { - InternalFreeDependencyEntry (Request->Private.Info); - } - } - - DependencyEntry = GetFirstNode (&Dependencies); - - while (!IsNull (DependencyEntry, &Dependencies)) { - DependencyInfo = OC_DEP_INFO_FROM_LINK (DependencyEntry); - DependencyEntry = GetNextNode (&Dependencies, DependencyEntry); - InternalFreeDependencyEntry (DependencyInfo); - } - - return Result; -} diff --git a/Library/OcMachoPrelinkLib/Plist.c b/Library/OcMachoPrelinkLib/Plist.c deleted file mode 100644 index 11d356d2..00000000 --- a/Library/OcMachoPrelinkLib/Plist.c +++ /dev/null @@ -1,181 +0,0 @@ -/** @file - PLIST helper functions. - -Copyright (c) 2018, Download-Fritz. All rights reserved.
-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 - -#include -#include -#include - -#include "OcMachoPrelinkInternal.h" - -CONST CHAR8 * -InternalPlistStrStrSameLevelUp ( - IN CONST CHAR8 *AnchorString, - IN CONST CHAR8 *FindString, - IN UINTN FindStringLen - ) -{ - CONST CHAR8 *Walker; - UINTN DictLevel; - UINTN ShortestMatch; - - ASSERT (AnchorString != NULL); - ASSERT (FindString != NULL); - ASSERT (AsciiStrLen (FindString) == FindStringLen); - - ShortestMatch = MIN (FindStringLen, L_STR_LEN ("")); - - for ( - Walker = (AnchorString - ShortestMatch); - AsciiStrnCmp (Walker, FindString, FindStringLen) != 0; - --Walker - ) { - for (DictLevel = 1; TRUE; --Walker) { - if (AsciiStrnCmp (Walker, STR_N_HELPER ("")) == 0) { - ++DictLevel; - Walker -= (ShortestMatch - 1); - continue; - } - - if (AsciiStrnCmp (Walker, STR_N_HELPER ("")) == 0) { - --DictLevel; - if (DictLevel == 0) { - return NULL; - } - } - - if (DictLevel == 1) { - break; - } - } - } - - return Walker; -} - -CONST CHAR8 * -InternalPlistStrStrSameLevelDown ( - IN CONST CHAR8 *AnchorString, - IN CONST CHAR8 *FindString, - IN UINTN FindStringLen - ) -{ - CONST CHAR8 *Walker; - UINTN DictLevel; - - ASSERT (AnchorString != NULL); - ASSERT (FindString != NULL); - ASSERT (AsciiStrLen (FindString) == FindStringLen); - - for ( - Walker = AnchorString; - AsciiStrnCmp (Walker, FindString, FindStringLen) != 0; - ++Walker - ) { - for (DictLevel = 1; TRUE; ++Walker) { - if (AsciiStrnCmp (Walker, STR_N_HELPER ("")) == 0) { - ++DictLevel; - Walker += (L_STR_LEN ("") - 1); - continue; - } - - if (AsciiStrnCmp (Walker, STR_N_HELPER ("")) == 0) { - --DictLevel; - if (DictLevel == 0) { - return NULL; - } - } - - if (DictLevel == 1) { - break; - } - } - } - - return Walker; -} - -CONST CHAR8 * -InternalPlistStrStrSameLevel ( - IN CONST CHAR8 *AnchorString, - IN CONST CHAR8 *FindString, - IN UINTN FindStringLen, - IN UINTN DownwardsOffset - ) -{ - CONST CHAR8 *Result; - - Result = InternalPlistStrStrSameLevelUp ( - AnchorString, - FindString, - FindStringLen - ); - if (Result == NULL) { - Result = InternalPlistStrStrSameLevelDown ( - (AnchorString + DownwardsOffset), - FindString, - FindStringLen - ); - } - - return Result; -} - -UINT64 -InternalPlistGetIntValue ( - IN CONST CHAR8 **Tag - ) -{ - UINT64 Value; - - CONST CHAR8 *Walker; - CHAR8 *Walker2; - - ASSERT (Tag != NULL); - ASSERT (*Tag != NULL); - ASSERT (AsciiStrnCmp (*Tag, STR_N_HELPER (""); - ASSERT (Walker != NULL); - ++Walker; - // - // Temporarily terminate after the integer definition. - // - Walker2 = AsciiStrStr (Walker, "<"); - ASSERT (Walker2 != NULL); - *Walker2 = '\0'; - // - // Assert that the prelinked PLIST always uses hexadecimal integers. - // - while ((*Walker == ' ') || (*Walker == '\t')) { - ++Walker; - } - ASSERT ((Walker[1] == 'x') || (Walker[1] == 'X')); - Value = AsciiStrHexToUint64 (Walker); - // - // Restore the previously replaced opening brace. - // - *Walker2 = '<'; - // - // Return the first character after the integer tag. - // - *Tag = (Walker2 + L_STR_LEN ("")); - - return Value; -} diff --git a/Library/OcMachoPrelinkLib/Prelink.c b/Library/OcMachoPrelinkLib/Prelink.c deleted file mode 100644 index 7f55260d..00000000 --- a/Library/OcMachoPrelinkLib/Prelink.c +++ /dev/null @@ -1,1473 +0,0 @@ -/** @file - Library handling KEXT prelinking. - Currently limited to Intel 64 architectures. - -Copyright (c) 2018, Download-Fritz. All rights reserved.
-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 - -#include - -#include -#include -#include -#include -#include -#include - -#include "OcMachoPrelinkInternal.h" - -// -// Symbols -// - -CONST OC_SYMBOL_64 * -InternalOcGetSymbolByName ( - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN CONST CHAR8 *Name, - IN BOOLEAN CheckIndirect - ) -{ - CONST OC_SYMBOL_TABLE_64 *SymbolsWalker; - CONST OC_SYMBOL_64 *Symbols; - UINT32 NumSymbols; - UINT32 CxxIndex; - UINT32 Index; - INTN Result; - - ASSERT (DefinedSymbols != NULL); - ASSERT (Name != NULL); - - SymbolsWalker = DefinedSymbols; - - do { - Symbols = SymbolsWalker->Symbols; - NumSymbols = SymbolsWalker->NumSymbols; - - if (SymbolsWalker->IsIndirect) { - if (!CheckIndirect) { - continue; - } - // - // Only consider C++ symbols for indirect dependencies. - // - CxxIndex = (SymbolsWalker->NumSymbols - SymbolsWalker->NumCxxSymbols); - Symbols = &Symbols[CxxIndex]; - NumSymbols = SymbolsWalker->NumCxxSymbols; - } - - for (Index = 0; Index < NumSymbols; ++Index) { - Result = AsciiStrCmp ( - Name, - (SymbolsWalker->StringTable + Symbols[Index].StringIndex) - ); - if (Result == 0) { - return &Symbols[Index]; - } - } - - SymbolsWalker = GET_OC_SYMBOL_TABLE_64_FROM_LINK ( - GetNextNode ( - &DefinedSymbols->Link, - &SymbolsWalker->Link - ) - ); - } while (!IsNull (&DefinedSymbols->Link, &SymbolsWalker->Link)); - - return NULL; -} - -/** - Patches Symbol with Value and marks it as solved. - - @param[in] Value The value to solve Symbol with. - @param[out] Symbol The symbol to solve. - -**/ -VOID -InternalSolveSymbolValue64 ( - IN UINT64 Value, - OUT MACH_NLIST_64 *Symbol - ) -{ - Symbol->Value = Value; - Symbol->Type = (MACH_N_TYPE_ABS | MACH_N_TYPE_EXT); - Symbol->Section = NO_SECT; -} - -/** - Worker function to solve Symbol against the specified DefinedSymbols. - It does not consider Symbol might be a weak reference. - - @param[in,out] MachoContext Context of the Mach-O. - @param[in] DefinedSymbols List of defined symbols of all dependencies. - @param[in] Name The name of the symbol to resolve against. - @param[in,out] Symbol The symbol to be resolved. - - @retval Returned is whether the symbol was solved successfully. - -**/ -STATIC -BOOLEAN -InternalSolveSymbolNonWeak64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN CONST CHAR8 *Name, - IN OUT MACH_NLIST_64 *Symbol - ) -{ - INTN Result; - CONST OC_SYMBOL_64 *ResolveSymbol; - - if ((Symbol->Type & MACH_N_TYPE_TYPE) != MACH_N_TYPE_UNDF) { - if ((Symbol->Type & MACH_N_TYPE_TYPE) != MACH_N_TYPE_INDR) { - // - // KXLD_WEAK_TEST_SYMBOL might have been resolved by the resolving code - // at the end of InternalSolveSymbol64. - // - Result = AsciiStrCmp ( - MachoGetSymbolName64 (MachoContext, Symbol), - KXLD_WEAK_TEST_SYMBOL - ); - if (Result == 0) { - // - // KXLD_WEAK_TEST_SYMBOL has been solved successfully already. - // - return TRUE; - } - // - // Any other symbols must be undefined or indirect. - // - ASSERT (FALSE); - } - } else if (Symbol->Value != 0) { - // - // Common symbols are not supported. - // - ASSERT (FALSE); - return FALSE; - } - - ResolveSymbol = InternalOcGetSymbolByName ( - DefinedSymbols, - Name, - FALSE - ); - if (ResolveSymbol != NULL) { - InternalSolveSymbolValue64 (ResolveSymbol->Value, Symbol); - return TRUE; - } - - return FALSE; -} - -/** - Solves Symbol against the specified DefinedSymbols. - - @param[in] MachoContext Context of the Mach-O. - @param[in] DefinedSymbols List of defined symbols of all dependencies. - @param[in] Name The name of the symbol to resolve against. - @param[in,out] Symbol The symbol to be resolved. - @param[in,out] WeakTestValue Points to the value of the Weak test symbol. - If it is 0 on input, it will get assigned - the value on the first resolution of a weak - symbol. - @param[in] UndefinedSymbols Array of the Mach-O'S undefined symbols. - @param[in] NumUndefinedSymbols Number of symbols in UndefinedSymbols. - - @retval Returned is whether the symbol was solved successfully. For weak - symbols, this includes solving with _gOSKextUnresolved. - -**/ -STATIC -BOOLEAN -InternalSolveSymbol64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN CONST CHAR8 *Name, - IN OUT MACH_NLIST_64 *Symbol, - IN OUT UINT64 *WeakTestValue, - IN CONST MACH_NLIST_64 *UndefinedSymbols, - IN UINT32 NumUndefinedSymbols - ) -{ - BOOLEAN Success; - UINT32 Index; - INTN Result; - UINT64 Value; - CONST MACH_NLIST_64 *WeakTestSymbol; - - ASSERT (Symbol != NULL); - ASSERT ((Symbol->Type & MACH_N_TYPE_STAB) == 0); - if (NumUndefinedSymbols != 0) { - ASSERT (UndefinedSymbols != NULL); - } - - Success = InternalSolveSymbolNonWeak64 ( - MachoContext, - DefinedSymbols, - Name, - Symbol - ); - if (Success) { - return TRUE; - } - - if (((Symbol->Type & MACH_N_TYPE_STAB) == 0) - && ((Symbol->Descriptor & MACH_N_WEAK_DEF) != 0)) { - // - // KXLD_WEAK_TEST_SYMBOL is not going to be defined or exposed by a KEXT - // prelinked by this library, hence only check the undefined symbols region - // for matches. - // - Value = *WeakTestValue; - - if (Value == 0) { - for (Index = 0; Index < NumUndefinedSymbols; ++Index) { - WeakTestSymbol = &UndefinedSymbols[Index]; - Result = AsciiStrCmp ( - MachoGetSymbolName64 (MachoContext, WeakTestSymbol), - KXLD_WEAK_TEST_SYMBOL - ); - if (Result == 0) { - if ((WeakTestSymbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_UNDF) { - Success = InternalSolveSymbolNonWeak64 ( - MachoContext, - DefinedSymbols, - Name, - Symbol - ); - if (!Success) { - return FALSE; - } - } - - Value = WeakTestSymbol->Value; - ASSERT (Value != 0); - - *WeakTestValue = Value; - break; - } - } - - if (Value != 0) { - InternalSolveSymbolValue64 (Value, Symbol); - return TRUE; - } - } - } - - return FALSE; -} - -// -// Relocations -// - -/** - Calculate the target address' displacement for the Intel 64 platform. - Instruction will be patched with the resulting address. - Logically matches XNU's calculate_displacement_x86_64. - - @param[in] Target The target address. - @param[in] Adjustment Adjustment to be subtracted from the - displacement. - @param[in,out] Instruction Pointer to the instruction to be patched. - - @retval Returned is whether the target instruction has been patched. - -**/ -STATIC -BOOLEAN -InternalCalculateDisplacementIntel64 ( - IN UINT64 Target, - IN UINT64 Adjustment, - IN OUT INT32 *Instruction - ) -{ - INT64 Displacement; - UINT64 Difference; - - ASSERT (Instruction != NULL); - - Displacement = ((*Instruction + Target) - Adjustment); - Difference = ABS (Displacement); - - if (Difference >= X86_64_RIP_RELATIVE_LIMIT) { - return FALSE; - } - - *Instruction = (INT32)Displacement; - - return TRUE; -} - -/** - Calculate the target addresses for Relocation and NextRelocation. - Logically matches XNU's calculate_targets. - - @param[in] MachoContext Mach-O context of the KEXT to relocate. - @param[in] LinkAddress The address to be linked against. - @param[in] Vtables List of all dependent VTables. - @param[in] Relocation The Relocation to be resolved. - @param[in] NextRelocation The Relocation following Relocation. - @param[out] Target Relocation's target address. - @param[out] PairTarget NextRelocation's target address. - @param[out] Vtable The VTable described by the symbol referenced by - Relocation. NULL, if there is none. - - @returns Whether the operation was completed successfully. - -**/ -STATIC -BOOLEAN -InternalCalculateTargetsIntel64 ( - IN OUT VOID *MachoContext, - IN UINT64 LinkAddress, - IN CONST OC_VTABLE_ARRAY *Vtables, - IN CONST MACH_RELOCATION_INFO *Relocation, - IN CONST MACH_RELOCATION_INFO *NextRelocation OPTIONAL, - OUT UINT64 *Target, - OUT UINT64 *PairTarget, - OUT CONST OC_VTABLE **Vtable OPTIONAL - ) -{ - BOOLEAN Success; - - UINT64 TargetAddress; - MACH_NLIST_64 *Symbol; - CONST CHAR8 *Name; - MACH_SECTION_64 *Section; - UINT64 PairAddress; - UINT64 PairDummy; - UINT32 Index; - CONST OC_VTABLE_ARRAY *VtablesWalker; - INTN Result; - CONST OC_VTABLE *VtableWalker; - - ASSERT (Target != NULL); - ASSERT (PairTarget != NULL); - - Section = NULL; - PairAddress = 0; - // - // Pull out the data from the relocation entries. The target_type depends - // on the Extern bit: - // Scattered -> Section Lookup by Address. - // Local (not extern) -> Section by Index - // Extern -> Symbolnum by Index - // - // Relocations referencing a symbol result in a Target of its resolved value. - // Relocations referencing a section result in a Target of - // (link_addr - base_addr), which should be LinkAddress aligned on the - // section's boundary. - // - TargetAddress = LinkAddress; - // - // Scattered Relocations are only supported by i386. - // - ASSERT (((UINT32)Relocation->Address & MACH_RELOC_SCATTERED) == 0); - - if (Relocation->Extern != 0) { - Success = MachoGetSymbolByIndex64 ( - MachoContext, - Relocation->SymbolNumber, - &Symbol - ); - if (!Success) { - return FALSE; - } - - Name = MachoGetSymbolName64 (MachoContext, Symbol); - // - // If this symbol is a padslot that has already been replaced, then the - // only way a relocation entry can still reference it is if there is a - // vtable that has not been patched. The vtable patcher uses the - // MetaClass structure to find classes for patching, so an unpatched - // vtable means that there is an OSObject-dervied class that is missing - // its OSDeclare/OSDefine macros. - // - if (MachoSymbolNameIsPadslot (Name)) { - return FALSE; - } - - if ((Vtable != NULL) && MachoSymbolNameIsVtable64 (Name)) { - VtablesWalker = Vtables; - - while (TRUE) { - for ( - Index = 0, VtableWalker = GET_FIRST_OC_VTABLE (VtablesWalker); - Index < VtablesWalker->NumVtables; - ++Index, VtableWalker = GET_NEXT_OC_VTABLE (VtableWalker) - ) { - Result = AsciiStrCmp (VtableWalker->Name, Name); - if (Result == 0) { - *Vtable = VtableWalker; - break; - } - } - - if (Index == VtablesWalker->NumVtables) { - break; - } - - VtablesWalker = GET_OC_VTABLE_ARRAY_FROM_LINK ( - GetNextNode ( - &Vtables->Link, - &VtablesWalker->Link - ) - ); - if (IsNull (&Vtables->Link, &VtablesWalker->Link)) { - return FALSE; - } - } - } - - TargetAddress = Symbol->Value; - } else { - Success = MachoGetSectionByIndex64 ( - MachoContext, - Relocation->SymbolNumber, - &Section - ); - if (!Success || (Section == NULL)) { - return FALSE; - } - } - - if (Section != NULL) { - TargetAddress = ALIGN_VALUE ( - (Section->Address + LinkAddress), - Section->Alignment - ); - TargetAddress -= Section->Address; - } - - if (MachoRelocationIsPairIntel64 (Relocation->Type)) { - ASSERT (NextRelocation != NULL); - ASSERT (MachoIsRelocationPairTypeIntel64 (NextRelocation->Type)); - // - // As this relocation is the second one in a pair, it cannot be the start - // of a pair itself. Pass dummy data for the related arguments. This call - // shall never reach this very branch. - // - Success = InternalCalculateTargetsIntel64 ( - MachoContext, - LinkAddress, - Vtables, - NextRelocation, - NULL, - &PairAddress, - &PairDummy, - NULL - ); - if (!Success) { - return FALSE; - } - } - - *Target = TargetAddress; - *PairTarget = PairAddress; - - return TRUE; -} - -/** - Returns whether the VTable entry at Offset is a direct pure virtual call. - Logically macthes XNU's check_for_direct_pure_virtual_call. - - @param[in] Vtable The current VTable to be inspected. - @param[in] Offset The VTable's entry offset. - -**/ -STATIC -BOOLEAN -InternalIsDirectPureVirtualCall64 ( - IN CONST OC_VTABLE *Vtable, - IN UINT64 Offset - ) -{ - UINT32 Index; - CONST OC_VTABLE_ENTRY *Entry; - - if ((Offset % sizeof (UINT64)) != 0) { - ASSERT (FALSE); - return FALSE; - } - - Index = ((Offset - VTABLE_ENTRY_SIZE_64) / sizeof (UINT64)); - Entry = &Vtable->Entries[Index]; - - if ((Index >= Vtable->NumEntries) || (Entry->Name == NULL)) { - return FALSE; - } - - return MachoSymbolNameIsPureVirtual (Entry->Name); -} - -/** - Relocates Relocation against the specified Symtab's Symbol Table and - LinkAddress. This logically matches KXLD's x86_64_process_reloc. - - @param[in] MachoContext Mach-O context of the KEXT to relocate. - @param[in] LinkAddress The address to be linked against. - @param[in] Vtables List of all dependent VTables. - @param[in] RelocationBase The Relocations base address. - @param[in] Relocation The Relocation to be processed. - @param[in] NextRelocation The Relocation following Relocation. - - @retval 0 The Relocation does not need to be preseved. - @retval 1 The Relocation must be preseved. - @retval 0 | BIT31 The Relocation does not need to be preseved. - The next Relocation shall be skipped. - @retval 1 | BIT31 The Relocation must be preseved. - The next Relocation shall be skipped. - @retval MAX_UINTN The Relocation's target cannot be determined or it is a - direct pure virtual call. - -**/ -STATIC -UINTN -InternalRelocateRelocationIntel64 ( - IN OC_MACHO_CONTEXT *MachoContext, - IN UINT64 LinkAddress, - IN CONST OC_VTABLE_ARRAY *Vtables, - IN UINTN RelocationBase, - IN CONST MACH_RELOCATION_INFO *Relocation, - IN CONST MACH_RELOCATION_INFO *NextRelocation OPTIONAL - ) -{ - UINTN ReturnValue; - - UINT8 Type; - INT32 *Instruction32Ptr; - INT32 Instruction32; - UINT64 *Instruction64Ptr; - UINT64 Instruction64; - UINT64 Target; - BOOLEAN IsPair; - UINT64 PairTarget; - CONST OC_VTABLE *Vtable; - UINT64 LinkPc; - UINT64 Adjustment; - UINT32 Length; - UINT32 Address; - UINT8 *InstructionPtr; - BOOLEAN PcRelative; - BOOLEAN IsNormalLocal; - BOOLEAN Result; - - ASSERT (RelocationBase != 0); - ASSERT (Relocation != NULL); - // - // Scattered Relocations are only supported by i386. - // - ASSERT (((UINT32)Relocation->Address & MACH_RELOC_SCATTERED) == 0); - - IsPair = FALSE; - Adjustment = 0; - IsNormalLocal = FALSE; - - if ((Relocation->Extern == 0) - && (Relocation->SymbolNumber == MACH_RELOC_ABSOLUTE)) { - // - // A section-based relocation entry can be skipped for absolute - // symbols. - // - return FALSE; - } - - Address = Relocation->Address; - Length = Relocation->Size; - Type = Relocation->Type; - PcRelative = (Relocation->PcRelative != 0); - - if (Relocation->Extern == 0) { - IsNormalLocal = TRUE; - } - - ASSERT ((Length == 2) || (Length == 3)); - - LinkPc = (Address + LinkAddress); - InstructionPtr = (UINT8 *)(RelocationBase + Address); - - Vtable = NULL; - Result = InternalCalculateTargetsIntel64 ( - MachoContext, - LinkAddress, - Vtables, - Relocation, - NextRelocation, - &Target, - &PairTarget, - &Vtable - ); - if (!Result) { - return MAX_UINTN; - } - - if (Length == 2) { - Instruction32Ptr = (INT32 *)InstructionPtr; - Instruction32 = *Instruction32Ptr; - - if ((Vtable != NULL) - && InternalIsDirectPureVirtualCall64 (Vtable, Instruction32)) { - return MAX_UINTN; - } - // - // There are a number of different small adjustments for pc-relative - // relocation entries. The general case is to subtract the size of the - // relocation (represented by the length parameter), and it applies to - // the GOT types and external SIGNED types. The non-external signed types - // have a different adjustment corresponding to the specific type. - // - switch (Type) { - case MachX8664RelocSigned: - if (IsNormalLocal) { - Adjustment = 0; - break; - } - // Fall through - case MachX8664RelocSigned1: - if (IsNormalLocal) { - Adjustment = 1; - break; - } - // Fall through - case MachX8664RelocSigned2: - if (IsNormalLocal) { - Adjustment = 2; - break; - } - // Fall through - case MachX8664RelocSigned4: - if (IsNormalLocal) { - Adjustment = 4; - break; - } - // Fall through - case MachX8664RelocBranch: - case MachX8664RelocGot: - case MachX8664RelocGotLoad: - { - Adjustment = (1ULL << Length); - break; - } - - default: - { - break; - } - } - // - // Perform the actual relocation. All of the 32-bit relocations are - // pc-relative except for SUBTRACTOR, so a good chunk of the logic is - // stuck in calculate_displacement_x86_64. The signed relocations are - // a special case, because when they are non-external, the instruction - // already contains the pre-relocation displacement, so we only need to - // find the difference between how far the PC was relocated, and how - // far the target is relocated. Since the target variable already - // contains the difference between the target's base and link - // addresses, we add the difference between the PC's base and link - // addresses to the adjustment variable. This will yield the - // appropriate displacement in calculate_displacement. - // - switch (Type) { - case MachX8664RelocBranch: - { - ASSERT (PcRelative); - Adjustment += LinkPc; - break; - } - - case MachX8664RelocSigned: - case MachX8664RelocSigned1: - case MachX8664RelocSigned2: - case MachX8664RelocSigned4: - { - ASSERT (PcRelative); - Adjustment += (IsNormalLocal ? LinkAddress : LinkPc); - break; - } - - case MachX8664RelocGot: - case MachX8664RelocGotLoad: - { - ASSERT (PcRelative); - Adjustment += LinkPc; - Target = PairTarget; - IsPair = TRUE; - break; - } - - case MachX8664RelocSubtractor: - { - ASSERT (!PcRelative); - Instruction32 = (INT32)(Target - PairTarget); - IsPair = TRUE; - break; - } - - default: - { - return FALSE; - } - } - - if (PcRelative) { - Result = InternalCalculateDisplacementIntel64 ( - Target, - Adjustment, - &Instruction32 - ); - if (!Result) { - return FALSE; - } - } - - *Instruction32Ptr = Instruction32; - } else { - Instruction64Ptr = (UINT64 *)InstructionPtr; - Instruction64 = *Instruction64Ptr; - - if ((Vtable != NULL) - && InternalIsDirectPureVirtualCall64 (Vtable, Instruction64)) { - return MAX_UINTN; - } - - switch (Type) { - case MachX8664RelocUnsigned: - { - ASSERT (!PcRelative); - Instruction64 += Target; - break; - } - - case MachX8664RelocSubtractor: - { - ASSERT (!PcRelative); - Instruction64 = (Target - PairTarget); - IsPair = TRUE; - break; - } - - default: - { - return FALSE; - } - } - - *Instruction64Ptr = Instruction64; - } - - ReturnValue = (MachoPreserveRelocationIntel64 (Type) ? 1 : 0); - - if (IsPair) { - ReturnValue |= BIT31; - } - - return ReturnValue; -} - -/** - Relocates all Mach-O Relocations and copies the ones to be preserved after - prelinking to TargetRelocations. - - @param[in] MachoContext Mach-O context of the KEXT to relocate. - @param[in] LinkAddress The address to be linked against. - @param[in] Vtables The patched VTables of this KEXT and its - dependencies. - @param[in] RelocationBase The Relocations base address. - @param[in] SourceRelocations The Relocations source buffer. - @param[in] NumRelocations On input, the number of source Relocations. - On output, the number of Relocations to - preserve. - @param[out] TargetRelocations The Relocations destination buffer. - - @retval Returned is the number of preserved Relocations. - -**/ -STATIC -BOOLEAN -InternalRelocateAndCopyRelocations64 ( - IN OC_MACHO_CONTEXT *MachoContext, - IN UINT64 LinkAddress, - IN CONST OC_VTABLE_ARRAY *Vtables, - IN UINTN RelocationBase, - IN CONST MACH_RELOCATION_INFO *SourceRelocations, - IN OUT UINT32 *NumRelocations, - OUT MACH_RELOCATION_INFO *TargetRelocations - ) -{ - UINT32 PreservedRelocations; - - UINT32 Index; - CONST MACH_RELOCATION_INFO *NextRelocation; - UINTN Result; - MACH_RELOCATION_INFO *Relocation; - - ASSERT (MachoContext != NULL); - ASSERT (RelocationBase != 0); - ASSERT (SourceRelocations != NULL); - ASSERT (NumRelocations != NULL); - ASSERT (NumRelocations > 0); - ASSERT (TargetRelocations != NULL); - - PreservedRelocations = 0; - - for (Index = 0; Index < *NumRelocations; ++Index) { - NextRelocation = &SourceRelocations[Index + 1]; - // - // The last Relocation does not have a successor. - // - if (Index == (*NumRelocations - 1)) { - NextRelocation = NULL; - } - // - // Relocate the relocation. - // - Result = InternalRelocateRelocationIntel64 ( - MachoContext, - LinkAddress, - Vtables, - RelocationBase, - &SourceRelocations[Index], - NextRelocation - ); - if (Result == MAX_UINTN) { - return FALSE; - } - // - // Copy the Relocation to the destination buffer if it shall be preserved. - // - if ((Result & ~(UINTN)BIT31) != 0) { - Relocation = &TargetRelocations[PreservedRelocations]; - // - // Scattered Relocations are only supported by i386. - // - ASSERT (((UINT32)Relocation->Address & MACH_RELOC_SCATTERED) == 0); - - CopyMem (Relocation, &SourceRelocations[Index], sizeof (*Relocation)); - - if (Relocation->Extern != 0) { - // - // All relocation targets have been updated with symbol values. - // Convert to a local relocation as only sliding is supported from now - // on. - // - Relocation->Extern = 0; - // - // Assertion: The entire kext will be slid by the same offset. - // This is asserted by KXLD as well. - // - Relocation->SymbolNumber = 1; - } - - ++PreservedRelocations; - } - // - // Skip the next Relocation as instructed by - // InternalRelocateRelocationIntel64(). - // - if ((Result & BIT31) != 0) { - ++Index; - } - } - - *NumRelocations = PreservedRelocations; - - return TRUE; -} - -// -// MACH header -// - -/** - Strip superfluous Load Commands from the Mach-O header. This includes the - Code Signature Load Command which must be removed for the binary has been - modified by the prelinking routines. - - @param[in,out] MachHeader Mach-O header to strip the Load Commands from. - -**/ -STATIC -VOID -InternalStripLoadCommands64 ( - IN OUT MACH_HEADER_64 *MachHeader - ) -{ - STATIC CONST MACH_LOAD_COMMAND_TYPE LoadCommandsToStrip[] = { - MACH_LOAD_COMMAND_CODE_SIGNATURE, - MACH_LOAD_COMMAND_DYLD_INFO, - MACH_LOAD_COMMAND_DYLD_INFO_ONLY, - MACH_LOAD_COMMAND_FUNCTION_STARTS, - MACH_LOAD_COMMAND_DATA_IN_CODE, - MACH_LOAD_COMMAND_DYLIB_CODE_SIGN_DRS - }; - - UINT32 Index; - UINT32 Index2; - MACH_LOAD_COMMAND *LoadCommand; - UINT32 SizeOfLeftCommands; - // - // Delete the Code Signature Load Command if existent as we modified the - // binary, as well as linker metadata not needed for runtime operation. - // - LoadCommand = MachHeader->Commands; - SizeOfLeftCommands = MachHeader->CommandsSize; - - for (Index = 0; Index < MachHeader->NumCommands; ++Index) { - // - // LC_UNIXTHREAD and LC_MAIN are technically stripped in KXLD, but they are - // not supposed to be present in the first place. - // - ASSERT ((LoadCommand->CommandType != MACH_LOAD_COMMAND_UNIX_THREAD) - && (LoadCommand->CommandType != MACH_LOAD_COMMAND_MAIN)); - - SizeOfLeftCommands -= LoadCommand->CommandSize; - - for (Index2 = 0; Index < ARRAY_SIZE (LoadCommandsToStrip); ++Index2) { - if (LoadCommand->CommandType == LoadCommandsToStrip[Index2]) { - if (Index != (MachHeader->NumCommands - 1)) { - // - // If the current Load Command is not the last one, relocate the - // subsequent ones. - // - CopyMem ( - LoadCommand, - NEXT_MACH_LOAD_COMMAND (LoadCommand), - SizeOfLeftCommands - ); - } - - --MachHeader->NumCommands; - MachHeader->CommandsSize -= LoadCommand->CommandSize; - - break; - } - } - - LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand); - } -} - -/** - Prelinks the specified KEXT against the specified LinkAddress and the data - of its dependencies. - - @param[in,out] MachoContext Mach-O context of the KEXT to prelink. - @param[in] LinkEditSegment __LINKEDIT segment of the KEXT to prelink. - @param[in] LinkAddress The address this KEXT shall be linked - against. - @param[in] DependencyData List of data of all dependencies. - @param[in] ExposeSymbols Whether the symbol table shall be exposed. - @param[out] OutputData Buffer to output data into. - @param[out] ScratchMemory Scratch memory buffer that is at least as big - as the KEXT's __LINKEDIT segment. - - @retval Returned is whether the prelinking process has been successful. - The state of the KEXT is undefined in case this routine fails. - -**/ -BOOLEAN -InternalPrelinkKext64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN MACH_SEGMENT_COMMAND_64 *LinkEditSegment, - IN UINT64 LinkAddress, - IN OC_DEPENDENCY_DATA *DependencyData, - IN BOOLEAN ExposeSymbols, - IN OUT OC_DEPENDENCY_DATA *OutputData, - OUT VOID *ScratchMemory - ) -{ - MACH_HEADER_64 *MachHeader; - - MACH_SEGMENT_COMMAND_64 *Segment; - MACH_SECTION_64 *Section; - - UINT32 Index; - BOOLEAN Result; - UINT32 SymtabSize; - UINT32 SymtabSize2; - - MACH_SYMTAB_COMMAND *Symtab; - MACH_DYSYMTAB_COMMAND *DySymtab; - MACH_NLIST_64 *Symbol; - CONST CHAR8 *SymbolName; - CONST MACH_NLIST_64 *SymbolTable; - CONST CHAR8 *StringTable; - UINT32 NumSymbols; - CONST MACH_NLIST_64 *IndirectSymtab; - UINT32 NumIndirectSymbols; - CONST MACH_NLIST_64 *LocalSymtab; - UINT32 NumLocalSymbols; - CONST MACH_NLIST_64 *ExternalSymtab; - UINT32 NumExternalSymbols; - CONST MACH_NLIST_64 *UndefinedSymtab; - UINT32 NumUndefinedSymbols; - UINT64 WeakTestValue; - OC_VTABLE_PATCH_ARRAY *PatchData; - CONST OC_VTABLE_PATCH_ENTRY *VtablePatchEntry; - UINT32 VtableOffset; - CONST UINT64 *VtableData; - UINT32 VtableSize; - UINT32 VtablesSize; - OC_VTABLE_ARRAY *Vtables; - - UINT32 NumRelocations; - UINT32 NumRelocations2; - UINTN RelocationBase; - CONST MACH_RELOCATION_INFO *Relocations; - MACH_RELOCATION_INFO *TargetRelocation; - MACH_SEGMENT_COMMAND_64 *FirstSegment; - - VOID *LinkEdit; - UINT32 SymbolTableOffset; - UINT32 SymbolTableSize; - UINT32 RelocationsOffset; - UINT32 RelocationsSize; - UINT32 StringTableOffset; - UINT32 StringTableSize; - - UINT32 SegmentOffset; - UINT32 SegmentSize; - - ASSERT (MachoContext != NULL); - ASSERT (LinkAddress != 0); - ASSERT (DependencyData != NULL); - ASSERT (DependencyData->SymbolTable != NULL); - ASSERT (DependencyData->Vtables != NULL); - ASSERT (OutputData != NULL); - - MachHeader = MachoGetMachHeader64 (MachoContext); - // - // Only perform actions when the kext is flag'd to be dynamically linked. - // - if ((MachHeader->Flags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) == 0) { - ASSERT (FALSE); - return FALSE; - } - // - // Strip superfluous Load Commands. - // - InternalStripLoadCommands64 (MachHeader); - // - // Retrieve the symbol tables required for most following operations. - // - NumSymbols = MachoGetSymbolTable ( - MachoContext, - &SymbolTable, - &LocalSymtab, - &NumLocalSymbols, - &ExternalSymtab, - &NumExternalSymbols, - &UndefinedSymtab, - &NumUndefinedSymbols - ); - StringTable = MachoContext->StringTable; - // - // Solve indirect symbols. - // - WeakTestValue = 0; - NumIndirectSymbols = MachoGetIndirectSymbolTable ( - MachoContext, - &IndirectSymtab - ); - for (Index = 0; Index < NumIndirectSymbols; ++Index) { - Symbol = (MACH_NLIST_64 *)&IndirectSymtab[Index]; - SymbolName = MachoGetIndirectSymbolName64 (MachoContext, Symbol); - if (SymbolName == NULL) { - return FALSE; - } - - Result = InternalSolveSymbol64 ( - MachoContext, - DependencyData->SymbolTable, - SymbolName, - Symbol, - &WeakTestValue, - UndefinedSymtab, - NumUndefinedSymbols - ); - if (!Result) { - ASSERT (FALSE); - return FALSE; - } - } - // - // Solve undefined symbols. - // - for (Index = 0; Index < NumUndefinedSymbols; ++Index) { - Symbol = (MACH_NLIST_64 *)&UndefinedSymtab[Index]; - // - // Undefined symbols are solved via their name. - // - Result = InternalSolveSymbol64 ( - MachoContext, - DependencyData->SymbolTable, - MachoGetSymbolName64 (MachoContext, Symbol), - Symbol, - &WeakTestValue, - UndefinedSymtab, - NumUndefinedSymbols - ); - if (!Result) { - ASSERT (FALSE); - return FALSE; - } - } - // - // Create and patch the KEXT's VTables. - // ScratchMemory is at least as big as __LINKEDIT, so it can store all - // symbols. - // - PatchData = (OC_VTABLE_PATCH_ARRAY *)ScratchMemory; - Result = InternalPrepareVtableCreationNonPrelinked64 ( - MachoContext, - NumSymbols, - SymbolTable, - PatchData - ); - if (!Result) { - return FALSE; - } - // - // Then we double the number of vtables we're expecting, because every - // pointer will have a class vtable and a MetaClass vtable. - // - VtablesSize = 0; - for (Index = 0; Index < PatchData->NumEntries; ++Index) { - VtablePatchEntry = &PatchData->Entries[Index]; - - Result = MachoSymbolGetFileOffset64 ( - MachoContext, - VtablePatchEntry->Vtable, - &VtableOffset - ); - if (!Result || (VtableOffset == 0)) { - return FALSE; - } - - VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset); - if (!OC_ALIGNED (VtableData)) { - return FALSE; - } - - Result = InternalGetVtableSizeWithRelocs64 ( - MachoContext, - VtableData, - &VtableSize - ); - if (!Result) { - return FALSE; - } - - VtablesSize += VtableSize; - - Result = MachoSymbolGetFileOffset64 ( - MachoContext, - VtablePatchEntry->MetaVtable, - &VtableOffset - ); - if (!Result || (VtableOffset == 0)) { - return FALSE; - } - - VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset); - if (!OC_ALIGNED (VtableData)) { - return FALSE; - } - - Result = InternalGetVtableSizeWithRelocs64 ( - MachoContext, - VtableData, - &VtableSize - ); - if (!Result) { - return FALSE; - } - - VtablesSize += VtableSize; - } - - Vtables = AllocatePool (sizeof (*Vtables) + VtablesSize); - if (Vtables == NULL) { - return FALSE; - } - // - // From this point onwards it will be free'd via OutputData. - // - OutputData->Vtables = Vtables; - - Result = InternalCreateVtablesNonPrelinked64 ( - MachoContext, - DependencyData, - PatchData, - Vtables - ); - if (!Result) { - return FALSE; - } - - Vtables->Link.ForwardLink = NULL; - // - // Relocate local and external symbols. - // - for (Index = 0; Index < NumLocalSymbols; ++Index) { - Result = MachoRelocateSymbol64 ( - MachoContext, - LinkAddress, - (MACH_NLIST_64 *)&LocalSymtab[Index] - ); - if (!Result) { - return FALSE; - } - } - - for (Index = 0; Index < NumExternalSymbols; ++Index) { - Result = MachoRelocateSymbol64 ( - MachoContext, - LinkAddress, - (MACH_NLIST_64 *)&ExternalSymtab[Index] - ); - if (!Result) { - return FALSE; - } - } - // - // Prepare constructing a new __LINKEDIT section to... - // 1. strip undefined symbols for they are not allowed in prelinked - // binaries, - // 2. merge local and external relocations as only sliding is allowed from - // this point onwards, - // 3. strip Code Signature because we modified the binary, as well as other - // linker metadata stripped by KXLD as well, probably for space reasons. - // - // Example original layout: - // Local relocations - Symbol Table - external relocations - String Table - - // Code Signature - // - // Example prelinked layout - // Symbol Table - relocations (external -> local) - String Table - // - SymbolTableOffset = 0; - SymbolTableSize = (NumSymbols - NumUndefinedSymbols); - SymbolTableSize *= sizeof (MACH_NLIST_64); - RelocationsOffset = (SymbolTableOffset + SymbolTableSize); - StringTableSize = MachoContext->Symtab->StringsSize; - // - // For the allocation, assume all relocations will be preserved to simplify - // the code, the memory is only temporarily allocated anyway. - // - NumRelocations = MachoContext->DySymtab->NumOfLocalRelocations; - NumRelocations += MachoContext->DySymtab->NumExternalRelocations; - RelocationsSize = (NumRelocations * sizeof (MACH_RELOCATION_INFO)); - - LinkEdit = ScratchMemory; - - FirstSegment = NULL; - Result = MachoGetNextSegment64 (MachoContext, &FirstSegment); - if (!Result) { - return FALSE; - } - // - // Copy the relocations to be reserved and adapt the symbol number they - // reference in case it has been relocated above. - // - TargetRelocation = (MACH_RELOCATION_INFO *)( - (UINTN)LinkEdit + RelocationsOffset - ); - - ASSERT (FirstSegment->FileOffset == (UINTN)FirstSegment->FileOffset); - RelocationBase = ((UINTN)MachHeader + (UINTN)FirstSegment->FileOffset); - // - // Relocate and copy local and external relocations. - // - Relocations = MachoContext->LocalRelocations; - NumRelocations = MachoContext->DySymtab->NumOfLocalRelocations; - Result = InternalRelocateAndCopyRelocations64 ( - MachoContext, - LinkAddress, - DependencyData->Vtables, - RelocationBase, - Relocations, - &NumRelocations, - &TargetRelocation[0] - ); - if (!Result) { - ASSERT (FALSE); - return FALSE; - } - - Relocations = MachoContext->ExternRelocations; - NumRelocations2 = MachoContext->DySymtab->NumExternalRelocations; - Result = InternalRelocateAndCopyRelocations64 ( - MachoContext, - LinkAddress, - DependencyData->Vtables, - RelocationBase, - Relocations, - &NumRelocations2, - &TargetRelocation[NumRelocations] - ); - if (!Result) { - ASSERT (FALSE); - return FALSE; - } - NumRelocations += NumRelocations2; - // - // Expose the external Symbol Table if requested. - // - if (ExposeSymbols) { - OutputData->SymbolTable = AllocatePool ( - sizeof (OutputData->SymbolTable) - + (NumExternalSymbols - * sizeof (*OutputData->SymbolTable->Symbols)) - ); - InternalFillSymbolTable64 ( - MachoContext, - NumExternalSymbols, - ExternalSymtab, - OutputData->SymbolTable - ); - } - // - // Copy the entire symbol table excluding the area for undefined symbols. - // - SymtabSize = ((UndefinedSymtab - SymbolTable) * sizeof (MACH_NLIST_64)); - if (SymtabSize != 0) { - CopyMem ( - (VOID *)((UINTN)LinkEdit + SymbolTableOffset), - SymbolTable, - SymtabSize - ); - } - - SymtabSize2 = (&SymbolTable[NumSymbols] - &UndefinedSymtab[NumUndefinedSymbols]); - SymtabSize2 *= sizeof (MACH_NLIST_64); - if (SymtabSize2 != 0) { - CopyMem ( - (VOID *)((UINTN)LinkEdit + SymbolTableOffset + SymtabSize), - (VOID *)&UndefinedSymtab[NumUndefinedSymbols], - SymtabSize2 - ); - } - - NumSymbols -= NumUndefinedSymbols; - // - // Copy the String Table. Don't strip it for the saved bytes are unlikely - // worth the time required. - // - StringTableOffset = (RelocationsOffset + RelocationsSize); - CopyMem ( - (VOID *)((UINTN)LinkEdit + StringTableOffset), - StringTable, - StringTableSize - ); - // - // Set up the tables with the new offsets and Symbol Table length. - // - Symtab = MachoContext->Symtab; - DySymtab = MachoContext->DySymtab; - - Symtab->SymbolsOffset = SymbolTableOffset; - Symtab->NumSymbols -= NumSymbols; - Symtab->StringsOffset = StringTableOffset; - - DySymtab->LocalRelocationsOffset = RelocationsOffset; - DySymtab->NumOfLocalRelocations = NumRelocations; - // - // Clear dynamic linker information. - // - DySymtab->LocalSymbolsIndex = 0; - DySymtab->NumLocalSymbols = 0; - DySymtab->ExternalSymbolsIndex = 0; - DySymtab->NumExternalRelocations = 0; - DySymtab->UndefinedSymbolsIndex = 0; - DySymtab->NumUndefinedSymbols = 0; - DySymtab->IndirectSymbolsOffset = 0; - DySymtab->NumIndirectSymbols = 0; - // - // Copy the new __LINKEDIT segment into the binary and fix its Load Command. - // - LinkEditSegment->FileSize = (SymbolTableSize + RelocationsSize + StringTableSize); - ASSERT ((UINTN)LinkEditSegment->FileSize == LinkEditSegment->FileSize); - - LinkEditSegment->Size = ALIGN_VALUE (LinkEditSegment->FileSize, BASE_4KB); - - CopyMem ( - (VOID *)((UINTN)MachHeader + (UINTN)LinkEditSegment->FileOffset), - LinkEdit, - (UINTN)LinkEditSegment->FileSize - ); - // - // Adapt the link addresses of all Segments and their Sections. - // - SegmentOffset = 0; - SegmentSize = 0; - - Segment = NULL; - while (MachoGetNextSegment64 (MachoContext, &Segment)) { - if (Segment == NULL) { - // - // Adapt the Mach-O header to signal being prelinked. - // - MachHeader->Flags = MACH_HEADER_FLAG_NO_UNDEFINED_REFERENCES; - // - // Reinitialize the Mach-O context to account for the changed __LINKEDIT - // segment and file size. - // - MachoInitializeContext ( - MachoContext, - MachHeader, - (SegmentOffset + SegmentSize) - ); - - return TRUE; - } - - Result = FALSE; - - Section = NULL; - while (MachoGetNextSection64 (MachoContext, Segment, &Section)) { - if (Section == NULL) { - Result = TRUE; - break; - } - - Section->Address = ALIGN_VALUE ( - (Section->Address + LinkAddress), - Section->Alignment - ); - ++Section; - } - - if (!Result) { - return FALSE; - } - - Segment->VirtualAddress += LinkAddress; - - if (Segment->FileOffset > SegmentOffset) { - SegmentOffset = (UINT32)Segment->FileOffset; - ASSERT (SegmentOffset == Segment->FileOffset); - - SegmentSize = (UINT32)Segment->FileSize; - ASSERT (SegmentSize == Segment->FileSize); - } - } - // - // Reaching here means one or more segments are malformed. - // - return FALSE; -} diff --git a/Library/OcMachoPrelinkLib/Vtables.c b/Library/OcMachoPrelinkLib/Vtables.c deleted file mode 100644 index 8c04f883..00000000 --- a/Library/OcMachoPrelinkLib/Vtables.c +++ /dev/null @@ -1,859 +0,0 @@ -/** @file - Library handling KEXT prelinking. - Currently limited to Intel 64 architectures. - -Copyright (c) 2018, Download-Fritz. All rights reserved.
-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 - -#include - -#include -#include -#include -#include - -#include "OcMachoPrelinkInternal.h" - -STATIC -CONST OC_VTABLE * -InternalGetOcVtableByName ( - IN CONST OC_VTABLE_ARRAY *Vtables, - IN CONST CHAR8 *Name - ) -{ - CONST OC_VTABLE_ARRAY *VtableWalker; - CONST OC_VTABLE *Vtable; - UINT32 Index; - INTN Result; - - VtableWalker = Vtables; - - do { - Vtable = GET_FIRST_OC_VTABLE (VtableWalker); - - for (Index = 0; Index < VtableWalker->NumVtables; ++Index) { - Result = AsciiStrCmp (Vtable->Name, Name); - if (Result == 0) { - return Vtable; - } - - Vtable = GET_NEXT_OC_VTABLE (Vtable); - } - - VtableWalker = GET_OC_VTABLE_ARRAY_FROM_LINK ( - GetNextNode (&Vtables->Link, &VtableWalker->Link) - ); - } while (!IsNull (&Vtables->Link, &VtableWalker->Link)); - - return NULL; -} - -STATIC -BOOLEAN -InternalConstructVtablePrelinked64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN CONST MACH_NLIST_64 *VtableSymbol, - OUT OC_VTABLE *Vtable - ) -{ - BOOLEAN Result; - CONST MACH_HEADER_64 *MachHeader; - UINT32 VtableOffset; - CONST UINT64 *VtableData; - UINT64 Value; - CONST OC_SYMBOL_TABLE_64 *SymbolsWalker; - CONST CHAR8 *StringTable; - UINT32 Index; - UINT32 Index2; - UINT32 CxxIndex; - CONST OC_SYMBOL_64 *Symbol; - - ASSERT (MachoContext != NULL); - ASSERT (DefinedSymbols != NULL); - ASSERT (VtableSymbol != NULL); - ASSERT (Vtable != NULL); - - MachHeader = MachoGetMachHeader64 (MachoContext); - ASSERT (MachHeader != NULL); - - Result = MachoSymbolGetFileOffset64 ( - MachoContext, - VtableSymbol, - &VtableOffset - ); - if (!Result) { - return FALSE; - } - - VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset); - if (!OC_ALIGNED (VtableData)) { - return FALSE; - } - - Vtable->Name = MachoGetSymbolName64 (MachoContext, VtableSymbol); - // - // Initialize the VTable by entries. - // - SymbolsWalker = DefinedSymbols; - - do { - StringTable = SymbolsWalker->StringTable; - CxxIndex = ( - SymbolsWalker->NumSymbols - - SymbolsWalker->NumCxxSymbols - ); - // - // Assumption: Not ARM (ARM requires an alignment to the function pointer - // retrieved from VtableData. - // - for ( - Index = VTABLE_HEADER_LEN_64; - (Value = VtableData[Index]) != 0; - ++Index - ) { - for ( - Index2 = CxxIndex; - Index2 < SymbolsWalker->NumSymbols; - ++Index2 - ) { - Symbol = &SymbolsWalker->Symbols[Index2]; - - if (Symbol->Value == Value) { - Vtable->Entries[Index].Address = Value; - Vtable->Entries[Index].Name = (StringTable + Symbol->StringIndex); - break; - } - } - // - // 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. - // - if (Index == SymbolsWalker->NumCxxSymbols) { - Vtable->Entries[Index].Address = 0; - Vtable->Entries[Index].Name = NULL; - } - } - - SymbolsWalker = GET_OC_SYMBOL_TABLE_64_FROM_LINK ( - GetNextNode ( - &DefinedSymbols->Link, - &SymbolsWalker->Link - ) - ); - } while (!IsNull (&DefinedSymbols->Link, &SymbolsWalker->Link)); - - return TRUE; -} - -UINT32 -InternalGetVtableSize64 ( - IN CONST UINT64 *VtableData - ) -{ - UINT32 Index; - - ASSERT (VtableData != NULL); - // - // Assumption: Not ARM (ARM requires an alignment to the function pointer - // retrieved from VtableData. - // - for (Index = VTABLE_HEADER_LEN_64; VtableData[Index] != 0; ++Index) { - ; - } - - return (Index * VTABLE_ENTRY_SIZE_64); -} - -BOOLEAN -InternalGetVtableSizeWithRelocs64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST UINT64 *VtableData, - OUT UINT32 *VtableSize - ) -{ - UINT32 Size; - MACH_NLIST_64 *Symbol; - - Size = InternalGetVtableSize64 (VtableData); - - Symbol = MachoGetSymbolByExternRelocationOffset64 ( - MachoContext, - ((UINTN)VtableData + Size) - ); - if (Symbol == NULL) { - return FALSE; - } - - *VtableSize = Size; - - return TRUE; -} - -BOOLEAN -InternalPrepareCreateVtablesPrelinked64 ( - IN OC_MACHO_CONTEXT *MachoContext, - OUT OC_VTABLE_EXPORT_ARRAY *VtableExport, - IN UINT32 VtableExportSize - ) -{ - UINT32 NumVtables; - - CONST MACH_NLIST_64 *SymbolTable; - CONST MACH_NLIST_64 *Symbol; - CONST CHAR8 *Name; - UINT32 NumSymbols; - UINT32 Index; - BOOLEAN Result; - - ASSERT (MachoContext != NULL); - - NumVtables = 0; - - NumSymbols = MachoGetSymbolTable ( - MachoContext, - &SymbolTable, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL - ); - for (Index = 0; Index < NumSymbols; ++Index) { - Symbol = &SymbolTable[Index]; - Name = MachoGetSymbolName64 (MachoContext, Symbol); - if (MachoSymbolNameIsVtable64 (Name)) { - Result = MachoIsSymbolValueInRange64 ( - MachoContext, - VtableExport->Symbols[Index] - ); - if (!Result) { - return FALSE; - } - - ++NumVtables; - - if (VtableExportSize < (NumVtables * sizeof (*VtableExport))) { - ASSERT (FALSE); - return FALSE; - } - - VtableExport->Symbols[NumVtables] = Symbol; - } - } - - VtableExport->NumSymbols = NumVtables; - - return TRUE; -} - -BOOLEAN -InternalCreateVtablesPrelinked64 ( - IN OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN OC_VTABLE_EXPORT_ARRAY *VtableExport, - OUT OC_VTABLE *VtableBuffer - ) -{ - CONST MACH_NLIST_64 *Symbol; - UINT32 Index; - BOOLEAN Result; - - ASSERT (MachoContext != NULL); - ASSERT (DefinedSymbols != NULL); - - for (Index = 0; Index < VtableExport->NumSymbols; ++Index) { - Symbol = VtableExport->Symbols[Index]; - Result = InternalConstructVtablePrelinked64 ( - MachoContext, - DefinedSymbols, - Symbol, - VtableBuffer - ); - if (!Result) { - return FALSE; - } - - VtableBuffer = GET_NEXT_OC_VTABLE (VtableBuffer); - } - - return TRUE; -} - -/** - * kxld_vtable_patch - */ -STATIC -BOOLEAN -InternalPatchVtableSymbol ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_VTABLE_ENTRY *ParentEntry, - IN CONST CHAR8 *VtableName, - OUT MACH_NLIST_64 *Symbol - ) -{ - CONST CHAR8 *Name; - INTN Result; - BOOLEAN Success; - CONST CHAR8 *ClassName; - CHAR8 FunctionPrefix[SYM_MAX_NAME_LEN]; - - ASSERT (Symbol != NULL); - ASSERT (ParentEntry != NULL); - // - // 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) { - return TRUE; - } - // - // 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 MachoIsSymbolValueInRange64 (MachoContext, Symbol); - } - // - // 1) If the symbol is defined locally, do not patch - // - if (MachoSymbolIsLocalDefined (MachoContext, Symbol)) { - return MachoIsSymbolValueInRange64 (MachoContext, Symbol); - } - - Name = MachoGetSymbolName64 (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 MachoIsSymbolValueInRange64 (MachoContext, Symbol); - } - // - // 3) If the symbols are the same, do not patch - // - Result = AsciiStrCmp (Name, ParentEntry->Name); - if (Result == 0) { - return MachoIsSymbolValueInRange64 (MachoContext, Symbol); - } - // - // 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. - // - ASSERT (FALSE); - 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. - // - InternalSolveSymbolValue64 (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; - ASSERT (MachoSymbolNameIsPureVirtual (Name) || ((Symbol->Value & 1U) == 0)); - - return TRUE; -} - -STATIC -BOOLEAN -InternalInitializeVtableByEntriesAndRelocations64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols, - IN CONST OC_VTABLE *SuperVtable, - IN CONST MACH_NLIST_64 *VtableSymbol, - IN CONST UINT64 *VtableData, - OUT OC_VTABLE *Vtable - ) -{ - UINT32 NumEntries; - UINT32 Index; - UINT32 CxxIndex; - UINT32 EntryOffset; - UINT64 EntryValue; - CONST OC_SYMBOL_TABLE_64 *SymbolsWalker; - CONST OC_SYMBOL_64 *OcSymbol; - CONST CHAR8 *StringTable; - MACH_NLIST_64 *Symbol; - BOOLEAN Result; - CONST CHAR8 *Name; - // - // Assumption: Not ARM (ARM requires an alignment to the function pointer - // retrieved from VtableData. - // - for ( - NumEntries = 0, EntryOffset = VTABLE_HEADER_LEN_64; - TRUE; - ++NumEntries, ++EntryOffset - ) { - EntryValue = VtableData[EntryOffset]; - // - // 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. - // - if (EntryValue != 0) { - SymbolsWalker = DefinedSymbols; - CxxIndex = ( - SymbolsWalker->NumSymbols - - SymbolsWalker->NumCxxSymbols - ); - do { - StringTable = SymbolsWalker->StringTable; - // - // Imported symbols are implicitely locally defined and hence do not - // need to be patched. - // - for ( - Index = CxxIndex; - Index < SymbolsWalker->NumSymbols; - ++Index - ) { - OcSymbol = &SymbolsWalker->Symbols[Index]; - - if (OcSymbol->Value == EntryValue) { - Name = (StringTable + OcSymbol->StringIndex); - Vtable->Entries[NumEntries].Name = Name; - Vtable->Entries[NumEntries].Address = OcSymbol->Value; - break; - } - } - - if (Index != SymbolsWalker->NumSymbols) { - break; - } - - SymbolsWalker = GET_OC_SYMBOL_TABLE_64_FROM_LINK ( - GetNextNode ( - &DefinedSymbols->Link, - &SymbolsWalker->Link - ) - ); - if (IsNull (&DefinedSymbols->Link, &SymbolsWalker->Link)) { - ASSERT (FALSE); - return FALSE; - } - } while (TRUE); - } else if (NumEntries < SuperVtable->NumEntries) { - Symbol = MachoGetSymbolByExternRelocationOffset64 ( - MachoContext, - (VtableSymbol->Value + EntryOffset) - ); - - if (Symbol == NULL) { - // - // When the VTable entry is 0 and it is not referenced by a Relocation, - // it is the end of the table. - // - break; - } - - Result = InternalPatchVtableSymbol ( - MachoContext, - &SuperVtable->Entries[NumEntries], - MachoGetSymbolName64 (MachoContext, VtableSymbol), - Symbol - ); - if (!Result) { - return FALSE; - } - - Name = MachoGetSymbolName64 (MachoContext, Symbol); - Vtable->Entries[NumEntries].Name = Name; - Vtable->Entries[NumEntries].Address = Symbol->Value; - } - } - - Vtable->NumEntries = NumEntries; - - return TRUE; -} - -BOOLEAN -InternalPrepareVtableCreationNonPrelinked64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN UINT32 NumSymbols, - IN CONST MACH_NLIST_64 *SymbolTable, - OUT OC_VTABLE_PATCH_ARRAY *PatchData - ) -{ - CONST MACH_HEADER_64 *MachHeader; - UINT32 Index; - UINT32 NumTables; - BOOLEAN Result; - CONST MACH_NLIST_64 *Smcp; - CONST CHAR8 *Name; - CONST MACH_NLIST_64 *VtableSymbol; - CONST MACH_NLIST_64 *MetaVtableSymbol; - - MachHeader = MachoGetMachHeader64 (MachoContext); - ASSERT (MachHeader != NULL); - - NumTables = 0; - - for (Index = 0; Index < NumSymbols; ++Index) { - Smcp = &SymbolTable[Index]; - Name = MachoGetSymbolName64 (MachoContext, Smcp); - if (MachoSymbolNameIsSmcp64 (MachoContext, Name)) { - // - // 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 = MachoGetVtableSymbolsFromSmcp64 ( - MachoContext, - Name, - &VtableSymbol, - &MetaVtableSymbol - ); - if (!Result) { - return FALSE; - } - - PatchData[NumTables].Entries->Smcp = Smcp; - PatchData[NumTables].Entries->Vtable = VtableSymbol; - PatchData[NumTables].Entries->MetaVtable = MetaVtableSymbol; - ++NumTables; - } - } - - PatchData->NumEntries = NumTables; - - return TRUE; -} - -BOOLEAN -InternalCreateVtablesNonPrelinked64 ( - IN OUT OC_MACHO_CONTEXT *MachoContext, - IN CONST OC_DEPENDENCY_DATA *DependencyData, - IN OC_VTABLE_PATCH_ARRAY *PatchData, - OUT OC_VTABLE_ARRAY *VtableArray - ) -{ - OC_VTABLE *VtableBuffer; - CONST MACH_HEADER_64 *MachHeader; - UINT32 Index; - UINT32 NumPatched; - BOOLEAN Result; - CONST MACH_NLIST_64 *Smcp; - CONST CHAR8 *Name; - UINT32 VtableOffset; - CONST MACH_NLIST_64 *VtableSymbol; - CONST MACH_NLIST_64 *MetaVtableSymbol; - CONST MACH_NLIST_64 *MetaClass; - CONST UINT64 *VtableData; - CONST OC_VTABLE *SuperVtable; - CONST OC_VTABLE *MetaVtable; - CONST OC_SYMBOL_64 *OcSymbolDummy; - MACH_NLIST_64 *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; - - MachHeader = MachoGetMachHeader64 (MachoContext); - ASSERT (MachHeader != NULL); - - VtableBuffer = GET_FIRST_OC_VTABLE (VtableArray); - NumPatched = 0; - - while (NumPatched < PatchData->NumEntries) { - SuccessfulIteration = FALSE; - - for (Index = 0; Index < PatchData->NumEntries; ++Index) { - Smcp = PatchData->Entries[Index].Smcp; - Name = MachoGetSymbolName64 (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 (MachoSymbolNameIsSmcp64 (MachoContext, Name)); - VtableSymbol = PatchData->Entries[Index].Vtable; - MetaVtableSymbol = PatchData->Entries[Index].MetaVtable; - // - // 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 = MachoGetMetaclassSymbolFromSmcpSymbol64 ( - MachoContext, - Smcp - ); - if (MetaClass == NULL) { - return FALSE; - } - // - // Get the super class name from the super metaclass - // - Result = MachoGetClassNameFromMetaClassPointer ( - MachoContext, - MachoGetSymbolName64 (MachoContext, MetaClass), - sizeof (SuperClassName), - SuperClassName - ); - if (!Result) { - return FALSE; - } - // - // Get the super vtable if it's been patched - // - SuperVtable = InternalGetOcVtableByName ( - DependencyData->Vtables, - 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 = InternalOcGetSymbolByName ( - DependencyData->SymbolTable, - FinalSymbolName, - TRUE - ); - if (OcSymbolDummy != NULL) { - return FALSE; - } - - Result = MachoGetLocalDefinedSymbolByName ( - MachoContext, - FinalSymbolName, - &SymbolDummy - ); - if (!Result || (SymbolDummy != NULL)) { - return FALSE; - } - // - // Patch the class's vtable - // - Result = MachoSymbolGetFileOffset64 ( - MachoContext, - VtableSymbol, - &VtableOffset - ); - if (!Result) { - return FALSE; - } - - VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset); - if (!OC_ALIGNED (VtableData)) { - return FALSE; - } - - Result = InternalInitializeVtableByEntriesAndRelocations64 ( - MachoContext, - DependencyData->SymbolTable, - SuperVtable, - VtableSymbol, - VtableData, - VtableBuffer - ); - if (!Result) { - return FALSE; - } - - VtableBuffer->Name = MachoGetSymbolName64 (MachoContext, VtableSymbol); - // - // Add the class's vtable to the set of patched vtables - // This is done implicitely as we're operating on an array. - // - VtableBuffer = GET_NEXT_OC_VTABLE (VtableBuffer); - // - // Get the meta vtable name from the class name - // - Result = MachoGetMetaVtableNameFromClassName ( - ClassName, - sizeof (VtableName), - VtableName - ); - if (!Result) { - return FALSE; - } - - MetaVtable = InternalGetOcVtableByName ( - DependencyData->Vtables, - 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 ( - DependencyData->Vtables, - 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 = MachoSymbolGetFileOffset64 ( - MachoContext, - MetaVtableSymbol, - &VtableOffset - ); - if (!Result) { - return FALSE; - } - - VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset); - if (!OC_ALIGNED (VtableData)) { - return FALSE; - } - - Result = InternalInitializeVtableByEntriesAndRelocations64 ( - MachoContext, - DependencyData->SymbolTable, - SuperVtable, - MetaVtableSymbol, - VtableData, - VtableBuffer - ); - if (!Result) { - return FALSE; - } - - VtableBuffer->Name = MachoGetSymbolName64 (MachoContext, MetaVtableSymbol); - // - // Add the MetaClass's vtable to the set of patched vtables - // This is done implicitely as we're operating on an array. - // - VtableBuffer = GET_NEXT_OC_VTABLE (VtableBuffer); - - ++NumPatched; - SuccessfulIteration = TRUE; - } - // - // Exit when there are unpatched VTables left, but there are none patched - // in a full iteration. - // - if (!SuccessfulIteration) { - ASSERT (FALSE); - return FALSE; - } - } - - VtableArray->NumVtables = (NumPatched * 2); - - return TRUE; -} diff --git a/TestsUser/Prelinked/Prelinked.c b/TestsUser/Prelinked/Prelinked.c index c99e1c30..7fd8ffef 100644 --- a/TestsUser/Prelinked/Prelinked.c +++ b/TestsUser/Prelinked/Prelinked.c @@ -453,14 +453,25 @@ int main(int argc, char** argv) { DEBUG ((DEBUG_WARN, "TestDriver.kext injected - %zx\n", Status)); + UINT8 *TestData = LiluKextData; + UINT32 TestDataSize = LiluKextDataSize; + + if (argc > 2) { + TestData = readFile(argv[2], &TestDataSize); + if (TestData == NULL) { + printf("Read fail\n"); + return -1; + } + } + Status = PrelinkedInjectKext ( &Context, "/Library/Extensions/Lilu.kext", LiluKextInfoPlistData, LiluKextInfoPlistDataSize, "Contents/MacOS/Lilu", - LiluKextData, - LiluKextDataSize + TestData, + TestDataSize ); DEBUG ((DEBUG_WARN, "Lilu.kext injected - %r\n", Status)); diff --git a/TestsUser/Prelinked/Prelinked_ b/TestsUser/Prelinked/Prelinked_ new file mode 100755 index 00000000..9b119ec5 Binary files /dev/null and b/TestsUser/Prelinked/Prelinked_ differ