From 74062d4e0f2f954fd7d5577f6841775514287f34 Mon Sep 17 00:00:00 2001 From: John Davis Date: Sun, 13 Sep 2020 14:57:10 -0500 Subject: [PATCH] OcAppleKernelLib: Add 32-bit Mach-O and patching support (#118) --- Docs/Configuration.tex | 20 +- .../Acidanthera/Library/OcAppleKernelLib.h | 109 +- Include/Acidanthera/Library/OcMachoLib.h | 672 ++++++++- Include/Acidanthera/OpenCore.h | 3 +- .../Apple/IndustryStandard/AppleMachoImage.h | 19 +- Library/OcAppleKernelLib/CachelessContext.c | 95 +- Library/OcAppleKernelLib/CachelessInternal.h | 4 + Library/OcAppleKernelLib/CommonPatches.c | 557 +------ Library/OcAppleKernelLib/CpuidPatches.c | 918 ++++++++++++ Library/OcAppleKernelLib/KernelCollection.c | 6 +- Library/OcAppleKernelLib/KextPatcher.c | 275 +++- Library/OcAppleKernelLib/Link.c | 10 +- Library/OcAppleKernelLib/MkextContext.c | 31 +- Library/OcAppleKernelLib/OcAppleKernelLib.inf | 1 + Library/OcAppleKernelLib/PrelinkedContext.c | 97 +- Library/OcAppleKernelLib/PrelinkedKext.c | 6 +- Library/OcAppleKernelLib/Vtables.c | 12 +- Library/OcMachoLib/CxxSymbols.c | 237 +-- Library/OcMachoLib/CxxSymbolsX.h | 114 ++ Library/OcMachoLib/Header.c | 1326 ++--------------- Library/OcMachoLib/HeaderX.h | 1156 ++++++++++++++ Library/OcMachoLib/Macho32.c | 22 + Library/OcMachoLib/Macho64.c | 17 + Library/OcMachoLib/MachoX.h | 68 + Library/OcMachoLib/OcMachoLib.inf | 6 + Library/OcMachoLib/OcMachoLibInternal.h | 218 ++- Library/OcMachoLib/Symbols.c | 693 +-------- Library/OcMachoLib/SymbolsX.h | 643 ++++++++ Platform/OpenCore/OpenCoreKernel.c | 518 +++---- Platform/OpenCore/OpenCoreKernelPatch.c | 46 +- User/Makefile | 2 +- Utilities/TestKextInject/KextInject.c | 12 +- Utilities/TestKextInject/Makefile | 1 + Utilities/TestMacho/Macho.c | 38 +- 34 files changed, 4795 insertions(+), 3157 deletions(-) create mode 100644 Library/OcAppleKernelLib/CpuidPatches.c create mode 100644 Library/OcMachoLib/CxxSymbolsX.h create mode 100644 Library/OcMachoLib/HeaderX.h create mode 100644 Library/OcMachoLib/Macho32.c create mode 100644 Library/OcMachoLib/Macho64.c create mode 100644 Library/OcMachoLib/MachoX.h create mode 100644 Library/OcMachoLib/SymbolsX.h diff --git a/Docs/Configuration.tex b/Docs/Configuration.tex index 07c8b3be..bd3090a1 100755 --- a/Docs/Configuration.tex +++ b/Docs/Configuration.tex @@ -2166,7 +2166,7 @@ blocking. \texttt{AppleCpuPmCfgLock}\\ \textbf{Type}: \texttt{plist\ boolean}\\ \textbf{Failsafe}: \texttt{false}\\ - \textbf{Requirement}: 10.6 (64-bit)\\ + \textbf{Requirement}: 10.4\\ \textbf{Description}: Disables \texttt{PKG\_CST\_CONFIG\_CONTROL} (\texttt{0xE2}) MSR modification in AppleIntelCPUPowerManagement.kext, commonly causing early kernel panic, when it is locked from writing. @@ -2247,7 +2247,7 @@ blocking. \texttt{CustomSMBIOSGuid}\\ \textbf{Type}: \texttt{plist\ boolean}\\ \textbf{Failsafe}: \texttt{false}\\ - \textbf{Requirement}: 10.6 (64-bit)\\ + \textbf{Requirement}: 10.4\\ \textbf{Description}: Performs GUID patching for \texttt{UpdateSMBIOSMode} \texttt{Custom} mode. Usually relevant for Dell laptops. @@ -2278,7 +2278,7 @@ blocking. \texttt{DisableRtcChecksum}\\ \textbf{Type}: \texttt{plist\ boolean}\\ \textbf{Failsafe}: \texttt{false}\\ - \textbf{Requirement}: 10.6 (64-bit)\\ + \textbf{Requirement}: 10.4\\ \textbf{Description}: Disables primary checksum (\texttt{0x58}-\texttt{0x59}) writing in AppleRTC. @@ -2294,7 +2294,7 @@ blocking. \texttt{DummyPowerManagement}\\ \textbf{Type}: \texttt{plist\ boolean}\\ \textbf{Failsafe}: \texttt{false}\\ - \textbf{Requirement}: 10.6 (64-bit)\\ + \textbf{Requirement}: 10.4\\ \textbf{Description}: Disables \texttt{AppleIntelCpuPowerManagement}. \emph{Note}: This option is a preferred alternative to @@ -2305,7 +2305,7 @@ blocking. \texttt{ExternalDiskIcons}\\ \textbf{Type}: \texttt{plist\ boolean}\\ \textbf{Failsafe}: \texttt{false}\\ - \textbf{Requirement}: 10.6 (64-bit)\\ + \textbf{Requirement}: 10.4\\ \textbf{Description}: Apply icon type patches to AppleAHCIPort.kext to force internal disk icons for all AHCI disks. @@ -2354,7 +2354,7 @@ blocking. \texttt{ThirdPartyDrives}\\ \textbf{Type}: \texttt{plist\ boolean}\\ \textbf{Failsafe}: \texttt{false}\\ - \textbf{Requirement}: 10.6 (64-bit, not required for older)\\ + \textbf{Requirement}: 10.6 (not required for older)\\ \textbf{Description}: Apply vendor patches to IOAHCIBlockStorage.kext to enable native features for third-party drives, such as TRIM on SSDs or hibernation support on 10.15 and newer. @@ -2515,13 +2515,13 @@ refer to \hyperref[legacyapple]{Legacy Apple OS}. \hline \textbf{macOS} & \textbf{i386 NC} & \textbf{i386 MK} & \textbf{i386 PK} & \textbf{x86\_64 NC} & \textbf{x86\_64 MK} & \textbf{x86\_64 PK} & \textbf{x86\_64 KC} \\ \hline - 10.4 & NO & NO (V1) & NO & --- & --- & --- & --- \\ + 10.4 & YES & YES (V1) & NO & --- & --- & --- & --- \\ \hline - 10.5 & NO & NO (V1) & NO & --- & --- & --- & --- \\ + 10.5 & YES & YES (V1) & NO & --- & --- & --- & --- \\ \hline - 10.6 & NO & NO (V2) & NO & YES & YES (V2) & YES & --- \\ + 10.6 & YES & YES (V2) & NO & YES & YES (V2) & YES & --- \\ \hline - 10.7 & NO & --- & NO & YES & --- & YES & --- \\ + 10.7 & YES & --- & NO & YES & --- & YES & --- \\ \hline 10.8-10.9 & --- & --- & --- & YES & --- & YES & --- \\ \hline diff --git a/Include/Acidanthera/Library/OcAppleKernelLib.h b/Include/Acidanthera/Library/OcAppleKernelLib.h index 240baf68..d8913bef 100644 --- a/Include/Acidanthera/Library/OcAppleKernelLib.h +++ b/Include/Acidanthera/Library/OcAppleKernelLib.h @@ -74,6 +74,7 @@ // Kernel cache types. // typedef enum KERNEL_CACHE_TYPE_ { + CacheTypeNone, CacheTypeCacheless, CacheTypeMkext, CacheTypePrelinked @@ -85,7 +86,7 @@ typedef enum KERNEL_CACHE_TYPE_ { #define PRINT_KERNEL_CACHE_TYPE(a) ( \ (a) == CacheTypeCacheless ? "Cacheless" : \ ((a) == CacheTypeMkext ? "Mkext" : \ - (((a) == CacheTypePrelinked ? "Prelinked" : "Unknown")))) + (((a) == CacheTypePrelinked ? "Prelinked" : "Kernel")))) // // As PageCount is UINT16, we can only index 2^16 * 4096 Bytes with one chain. @@ -311,10 +312,14 @@ typedef struct { // OC_MACHO_CONTEXT MachContext; // - // Virtual base to subtract to obtain file offset. + // Virtual base of text segment. // UINT64 VirtualBase; // + // File offset. + // + UINT64 FileOffset; + // // Virtual kmod_info_t address. // UINT64 VirtualKmod; @@ -327,9 +332,13 @@ typedef struct { // UINT32 KxldStateSize; // + // Binary is 32-bit. + // + BOOLEAN Is32Bit; + // // Patcher context is contained within a kernel collection. // - BOOLEAN IsKernelCollection; + BOOLEAN IsKernelCollection; } PATCHER_CONTEXT; // @@ -411,6 +420,10 @@ typedef struct { // UINT32 KernelVersion; // + // System is booting in 32-bit mode. + // + BOOLEAN Is32Bit; + // // Flag to indicate if above list is valid. List is built during the first read from SLE. // BOOLEAN BuiltInKextsValid; @@ -772,6 +785,7 @@ PrelinkedInjectComplete ( @param[in] InfoPlistSize Kext Info.plist size. @param[in] Executable Kext executable, optional. @param[in] ExecutableSize Kext executable size, optional. + @param[in] Is32Bit TRUE to process 32-bit kext. @return EFI_SUCCESS on success. **/ @@ -781,7 +795,8 @@ PrelinkedReserveKextSize ( IN OUT UINT32 *ReservedExeSize, IN UINT32 InfoPlistSize, IN UINT8 *Executable OPTIONAL, - IN UINT32 ExecutableSize OPTIONAL + IN UINT32 ExecutableSize OPTIONAL, + IN BOOLEAN Is32Bit ); /** @@ -843,6 +858,20 @@ PrelinkedContextApplyQuirk ( IN UINT32 KernelVersion ); +/** + Block kext in prelinked. + + @param[in,out] Context Prelinked context. + @param[in] Identifier Kext bundle identifier. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +PrelinkedContextBlock ( + IN OUT PRELINKED_CONTEXT *Context, + IN CONST CHAR8 *Identifier + ); + /** Update Mach-O header with new commands. @@ -980,6 +1009,7 @@ PatcherInitContextFromMkext( @param[in,out] Context Patcher context. @param[in,out] Buffer Kernel buffer (could be prelinked). @param[in] BufferSize Kernel buffer size. + @param[in] Is32Bit TRUE to use 32-bit. @return EFI_SUCCESS on success. **/ @@ -987,7 +1017,8 @@ EFI_STATUS PatcherInitContextFromBuffer ( IN OUT PATCHER_CONTEXT *Context, IN OUT UINT8 *Buffer, - IN UINT32 BufferSize + IN UINT32 BufferSize, + IN BOOLEAN Use32Bit ); /** @@ -1032,13 +1063,32 @@ PatcherBlockKext ( IN OUT PATCHER_CONTEXT *Context ); +/** + Find kmod address. + + @param[in] ExecutableContext Mach-O context. + @param[in] LoadAddress Load address. + @param[in] Size Executable size. + @param[out] Kmod Pointer to kmod. + + @return TRUE on success. +**/ +BOOLEAN +KextFindKmodAddress ( + IN OC_MACHO_CONTEXT *ExecutableContext, + IN UINT64 LoadAddress, + IN UINT32 Size, + OUT UINT64 *Kmod + ); + /** Apply modification to CPUID 1. - @param Patcher Patcher context. - @param CpuInfo CPU information. - @param Data 4 32-bit integers with CPUID data. - @param DataMask 4 32-bit integers with CPUID enabled overrides data. + @param Patcher Patcher context. + @param CpuInfo CPU information. + @param Data 4 32-bit integers with CPUID data. + @param DataMask 4 32-bit integers with CPUID enabled overrides data. + @param KernelVersion Curent kernel version. @return EFI_SUCCESS on success. **/ @@ -1047,7 +1097,8 @@ PatchKernelCpuId ( IN OUT PATCHER_CONTEXT *Patcher, IN OC_CPU_INFO *CpuInfo, IN UINT32 *Data, - IN UINT32 *DataMask + IN UINT32 *DataMask, + IN UINT32 KernelVersion ); /** @@ -1058,6 +1109,7 @@ PatchKernelCpuId ( @param[in] FileName Extensions directory filename. @param[in] ExtensionsDir Extensions directory EFI_FILE_PROTOCOL. @param[in] KernelVersion Current kernel version. + @param[in] Is32Bit TRUE if booting in 32-bit kernel mode. @return EFI_SUCCESS on success. **/ @@ -1066,7 +1118,8 @@ CachelessContextInit ( IN OUT CACHELESS_CONTEXT *Context, IN CONST CHAR16 *FileName, IN EFI_FILE_PROTOCOL *ExtensionsDir, - IN UINT32 KernelVersion + IN UINT32 KernelVersion, + IN BOOLEAN Is32Bit ); /** @@ -1145,6 +1198,20 @@ CachelessContextAddQuirk ( IN KERNEL_QUIRK_NAME Quirk ); +/** + Add block request to cacheless context to be applied later on. + + @param[in,out] Context Cacheless context. + @param[in] Identifier Kext bundle identifier. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +CachelessContextBlock ( + IN OUT CACHELESS_CONTEXT *Context, + IN CONST CHAR8 *Identifier + ); + /** Creates virtual directory overlay EFI_FILE_PROTOCOL from cacheless context. @@ -1275,6 +1342,7 @@ MkextContextFree ( @param[in] InfoPlistSize Kext Info.plist size. @param[in] Executable Kext executable, optional. @param[in] ExecutableSize Kext executable size, optional. + @param[in] Is32Bit TRUE to process 32-bit kext. @return EFI_SUCCESS on success. **/ @@ -1283,8 +1351,9 @@ MkextReserveKextSize ( IN OUT UINT32 *ReservedInfoSize, IN OUT UINT32 *ReservedExeSize, IN UINT32 InfoPlistSize, - IN UINT8 *Executable, - IN UINT32 ExecutableSize OPTIONAL + IN UINT8 *Executable OPTIONAL, + IN UINT32 ExecutableSize OPTIONAL, + IN BOOLEAN Is32Bit ); /** @@ -1344,6 +1413,20 @@ MkextContextApplyQuirk ( IN UINT32 KernelVersion ); +/** + Block kext in mkext. + + @param[in,out] Context Mkext context. + @param[in] Identifier Kext bundle identifier. + + @return EFI_SUCCESS on success. +**/ +EFI_STATUS +MkextContextBlock ( + IN OUT MKEXT_CONTEXT *Context, + IN CONST CHAR8 *Identifier + ); + /** Refresh plist and checksum after kext injection and/or patching. diff --git a/Include/Acidanthera/Library/OcMachoLib.h b/Include/Acidanthera/Library/OcMachoLib.h index bbb1de9a..1175cf13 100644 --- a/Include/Acidanthera/Library/OcMachoLib.h +++ b/Include/Acidanthera/Library/OcMachoLib.h @@ -32,18 +32,61 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. /// only. Members are not guaranteed to be sane. /// typedef struct { - MACH_HEADER_64 *MachHeader; + MACH_HEADER_ANY *MachHeader; UINT32 FileSize; + UINT32 ContainerOffset; MACH_SYMTAB_COMMAND *Symtab; - MACH_NLIST_64 *SymbolTable; + MACH_NLIST_ANY *SymbolTable; CHAR8 *StringTable; MACH_DYSYMTAB_COMMAND *DySymtab; - MACH_NLIST_64 *IndirectSymbolTable; + MACH_NLIST_ANY *IndirectSymbolTable; MACH_RELOCATION_INFO *LocalRelocations; MACH_RELOCATION_INFO *ExternRelocations; + + BOOLEAN Is32Bit; } OC_MACHO_CONTEXT; +/** + Initializes a 32-bit Mach-O Context. + + @param[out] Context Mach-O Context to initialize. + @param[in] FileData Pointer to the file's expected Mach-O header. + @param[in] FileSize File size of FileData. + @param[in] ContainerOffset The amount of Bytes the Mach-O header is offset + from the base (container, e.g. KC) of the file. + + @return Whether Context has been initialized successfully. + +**/ +BOOLEAN +MachoInitializeContext32 ( + OUT OC_MACHO_CONTEXT *Context, + IN VOID *FileData, + IN UINT32 FileSize, + IN UINT32 ContainerOffset + ); + +/** + Initializes a 64-bit Mach-O Context. + + @param[out] Context Mach-O Context to initialize. + @param[in] FileData Pointer to the file's expected Mach-O header. + @param[in] FileSize File size of FileData. + @param[in] ContainerOffset The amount of Bytes the Mach-O header is offset + from the base (container, e.g. KC) of the file. + + @return Whether Context has been initialized successfully. + +**/ +BOOLEAN +MachoInitializeContext64 ( + OUT OC_MACHO_CONTEXT *Context, + IN VOID *FileData, + IN UINT32 FileSize, + IN UINT32 ContainerOffset + ); + /** Initializes a Mach-O Context. @@ -52,6 +95,7 @@ typedef struct { @param[in] FileSize File size of FileData. @param[in] ContainerOffset The amount of Bytes the Mach-O header is offset from the base (container, e.g. KC) of the file. + @param[in] Is32Bit TRUE if Mach-O is 32-bit. @return Whether Context has been initialized successfully. @@ -61,11 +105,34 @@ MachoInitializeContext ( OUT OC_MACHO_CONTEXT *Context, IN VOID *FileData, IN UINT32 FileSize, - IN UINT32 ContainerOffset + IN UINT32 ContainerOffset, + IN BOOLEAN Is32Bit + ); + +/** + Returns the universal Mach-O Header structure. + + @param[in,out] Context Context of the Mach-O. + +**/ +MACH_HEADER_ANY * +MachoGetMachHeader ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Returns the 32-bit Mach-O Header structure. + + @param[in,out] Context Context of the Mach-O. + +**/ +MACH_HEADER * +MachoGetMachHeader32 ( + IN OUT OC_MACHO_CONTEXT *Context ); /** - Returns the Mach-O Header structure. + Returns the 64-bit Mach-O Header structure. @param[in,out] Context Context of the Mach-O. @@ -93,12 +160,25 @@ MachoGetFileSize ( **/ UINT32 -MachoGetVmSize64 ( +MachoGetVmSize ( IN OUT OC_MACHO_CONTEXT *Context ); /** - Returns the last virtual address of a Mach-O. + Returns the last virtual address of a 32-bit Mach-O. + + @param[in] Context Context of the Mach-O. + + @retval 0 The binary is malformed. + +**/ +UINT32 +MachoGetLastAddress32 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Returns the last virtual address of a 64-bit Mach-O. @param[in] Context Context of the Mach-O. @@ -119,12 +199,27 @@ MachoGetLastAddress64 ( **/ MACH_UUID_COMMAND * -MachoGetUuid64 ( +MachoGetUuid ( IN OUT OC_MACHO_CONTEXT *Context ); /** - Retrieves the first segment by the name of SegmentName. + Retrieves the first 32-bit segment by the name of SegmentName. + + @param[in,out] Context Context of the Mach-O. + @param[in] SegmentName Segment name to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND * +MachoGetSegmentByName32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName + ); + +/** + Retrieves the first 64-bit segment by the name of SegmentName. @param[in,out] Context Context of the Mach-O. @param[in] SegmentName Segment name to search for. @@ -147,6 +242,40 @@ MachoGetSegmentByName64 ( @retval NULL NULL is returned on failure. +**/ +MACH_SECTION_ANY * +MachoGetSectionByName ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_ANY *Segment, + IN CONST CHAR8 *SectionName + ); + +/** + Retrieves the first 32-bit section by the name of SectionName. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to search in. + @param[in] SectionName Section name to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION * +MachoGetSectionByName32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND *Segment, + IN CONST CHAR8 *SectionName + ); + +/** + Retrieves the first 64-bit section by the name of SectionName. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to search in. + @param[in] SectionName Section name to search for. + + @retval NULL NULL is returned on failure. + **/ MACH_SECTION_64 * MachoGetSectionByName64 ( @@ -156,7 +285,24 @@ MachoGetSectionByName64 ( ); /** - Retrieves a section within a segment by the name of SegmentName. + Retrieves a 32-bit section within a segment by the name of SegmentName. + + @param[in,out] Context Context of the Mach-O. + @param[in] SegmentName The name of the segment to search in. + @param[in] SectionName The name of the section to search for. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION * +MachoGetSegmentSectionByName32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName, + IN CONST CHAR8 *SectionName + ); + +/** + Retrieves a 64-bit section within a segment by the name of SegmentName. @param[in,out] Context Context of the Mach-O. @param[in] SegmentName The name of the segment to search in. @@ -179,7 +325,39 @@ MachoGetSegmentSectionByName64 ( @param[in] Segment Segment to retrieve the successor of. if NULL, the first segment is returned. - @retal NULL NULL is returned on failure. + @retval NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND_ANY * +MachoGetNextSegment ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SEGMENT_COMMAND_ANY *Segment OPTIONAL + ); + +/** + Retrieves the next 32-bit segment. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to retrieve the successor of. + if NULL, the first segment is returned. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SEGMENT_COMMAND * +MachoGetNextSegment32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SEGMENT_COMMAND *Segment OPTIONAL + ); + +/** + Retrieves the next 64-bit segment. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment Segment to retrieve the successor of. + if NULL, the first segment is returned. + + @retval NULL NULL is returned on failure. **/ MACH_SEGMENT_COMMAND_64 * @@ -189,7 +367,24 @@ MachoGetNextSegment64 ( ); /** - Retrieves the next section of a segment. + Retrieves the next 32-bit section of a segment. + + @param[in,out] Context Context of the Mach-O. + @param[in] Segment The segment to get the section of. + @param[in] Section The section to get the successor of. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION * +MachoGetNextSection32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND *Segment, + IN MACH_SECTION *Section OPTIONAL + ); + +/** + Retrieves the next 64-bit section of a segment. @param[in,out] Context Context of the Mach-O. @param[in] Segment The segment to get the section of. @@ -206,7 +401,22 @@ MachoGetNextSection64 ( ); /** - Retrieves a section by its index. + Retrieves a 32-bit section by its index. + + @param[in,out] Context Context of the Mach-O. + @param[in] Index Index of the section to retrieve. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION * +MachoGetSectionByIndex32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ); + +/** + Retrieves a 64-bit section by its index. @param[in,out] Context Context of the Mach-O. @param[in] Index Index of the section to retrieve. @@ -221,7 +431,22 @@ MachoGetSectionByIndex64 ( ); /** - Retrieves a section by its address. + Retrieves a 32-bit section by its address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address of the section to retrieve. + + @retval NULL NULL is returned on failure. + +**/ +MACH_SECTION * +MachoGetSectionByAddress32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Address + ); + +/** + Retrieves a 64-bit section by its address. @param[in,out] Context Context of the Mach-O. @param[in] Address Address of the section to retrieve. @@ -245,61 +470,124 @@ MachoGetSectionByAddress64 ( **/ BOOLEAN -MachoMergeSegments64 ( +MachoMergeSegments ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *Prefix ); /** - Returns whether Symbol describes a section. + Returns whether 32-bit Symbol describes a section. @param[in] Symbol Symbol to evaluate. **/ BOOLEAN -MachoSymbolIsSection ( +MachoSymbolIsSection32 ( + IN CONST MACH_NLIST *Symbol + ); + +/** + Returns whether 64-bit Symbol describes a section. + + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsSection64 ( IN CONST MACH_NLIST_64 *Symbol ); /** - Returns whether Symbol is defined. + Returns whether 32-bit Symbol is defined. @param[in] Symbol Symbol to evaluate. **/ BOOLEAN -MachoSymbolIsDefined ( +MachoSymbolIsDefined32 ( + IN CONST MACH_NLIST *Symbol + ); + +/** + Returns whether 64-bit Symbol is defined. + + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsDefined64 ( IN CONST MACH_NLIST_64 *Symbol ); /** - Returns whether Symbol is defined locally. + Returns whether 32-bit Symbol is defined locally. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to evaluate. + +**/ +BOOLEAN +MachoSymbolIsLocalDefined32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Symbol + ); + +/** + Returns whether 64-bit Symbol is defined locally. @param[in,out] Context Context of the Mach-O. @param[in] Symbol Symbol to evaluate. **/ BOOLEAN -MachoSymbolIsLocalDefined ( +MachoSymbolIsLocalDefined64 ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST MACH_NLIST_64 *Symbol ); /** - Retrieves a locally defined symbol by its name. + Retrieves a locally defined 32-bit symbol by its name. + + @param[in] Context Context of the Mach-O. + @param[in] Name Name of the symbol to locate. + +**/ +MACH_NLIST * +MachoGetLocalDefinedSymbolByName32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Name + ); + +/** + Retrieves a locally defined 64-bit symbol by its name. @param[in] Context Context of the Mach-O. @param[in] Name Name of the symbol to locate. **/ MACH_NLIST_64 * -MachoGetLocalDefinedSymbolByName ( +MachoGetLocalDefinedSymbolByName64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Name + ); + +/** + Retrieves a 32-bit symbol by its index. + + @param[in] Context Context of the Mach-O. + @param[in] Index Index of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST * +MachoGetSymbolByIndex32 ( IN OUT OC_MACHO_CONTEXT *Context, - IN CONST CHAR8 *Name + IN UINT32 Index ); /** - Retrieves a symbol by its index. + Retrieves a 64-bit symbol by its index. @param[in] Context Context of the Mach-O. @param[in] Index Index of the symbol to locate. @@ -314,7 +602,37 @@ MachoGetSymbolByIndex64 ( ); /** - Retrieves Symbol's name. + Retrieves a symbol by its index. + + @param[in] Context Context of the Mach-O. + @param[in] Index Index of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST_ANY * +MachoGetSymbolByIndex ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ); + +/** + Retrieves a 32-bit Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the name of. + + @retval symbol name. + +**/ +CONST CHAR8 * +MachoGetSymbolName32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Symbol + ); + +/** + Retrieves a 64-bit Symbol's name. @param[in,out] Context Context of the Mach-O. @param[in] Symbol Symbol to retrieve the name of. @@ -329,7 +647,37 @@ MachoGetSymbolName64 ( ); /** - Retrieves Symbol's name. + Retrieves a Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the name of. + + @retval symbol name. + +**/ +CONST CHAR8 * +MachoGetSymbolName ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_ANY *Symbol + ); + +/** + Retrieves a 32-bit Symbol's name. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Indirect symbol to retrieve the name of. + + @retval NULL NULL is returned on failure. + +**/ +CONST CHAR8 * +MachoGetIndirectSymbolName32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Symbol + ); + +/** + Retrieves a 64-bit Symbol's name. @param[in,out] Context Context of the Mach-O. @param[in] Symbol Indirect symbol to retrieve the name of. @@ -350,6 +698,34 @@ MachoGetIndirectSymbolName64 ( @param[in,out] Context Context of the Mach-O. @param[in] Symbol Symbol to verify the value of. +**/ +BOOLEAN +MachoIsSymbolValueInRange ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_ANY *Symbol + ); + +/** + Returns whether the 32-bit symbol's value is a valid address within the Mach-O + referenced to by Context. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to verify the value of. + +**/ +BOOLEAN +MachoIsSymbolValueInRange32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Symbol + ); + +/** + Returns whether the 64-bit symbol's value is a valid address within the Mach-O + referenced to by Context. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to verify the value of. + **/ BOOLEAN MachoIsSymbolValueInRange64 ( @@ -358,7 +734,26 @@ MachoIsSymbolValueInRange64 ( ); /** - Retrieves the symbol referenced by the Relocation targeting Address. + Retrieves the 32-bit symbol referenced by the Relocation targeting Address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address to search for. + @param[out] Symbol Buffer to output the symbol referenced by the + Relocation into. The output is undefined when FALSE + is returned. May be NULL. + + @returns Whether the Relocation exists. + +**/ +BOOLEAN +MachoGetSymbolByRelocationOffset32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Address, + OUT MACH_NLIST **Symbol + ); + +/** + Retrieves the 64-bit symbol referenced by the Relocation targeting Address. @param[in,out] Context Context of the Mach-O. @param[in] Address Address to search for. @@ -371,13 +766,32 @@ MachoIsSymbolValueInRange64 ( **/ BOOLEAN MachoGetSymbolByRelocationOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT MACH_NLIST_64 **Symbol + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT MACH_NLIST_64 **Symbol + ); + +/** + Retrieves the 32-bit symbol referenced by the extern Relocation targeting Address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Address to search for. + @param[out] Symbol Buffer to output the symbol referenced by the + Relocation into. The output is undefined when FALSE + is returned. May be NULL. + + @returns Whether the Relocation exists. + +**/ +BOOLEAN +MachoGetSymbolByExternRelocationOffset32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Address, + OUT MACH_NLIST **Symbol ); /** - Retrieves the symbol referenced by the extern Relocation targeting Address. + Retrieves the 64-bit symbol referenced by the extern Relocation targeting Address. @param[in,out] Context Context of the Mach-O. @param[in] Address Address to search for. @@ -390,13 +804,30 @@ MachoGetSymbolByRelocationOffset64 ( **/ BOOLEAN MachoGetSymbolByExternRelocationOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT MACH_NLIST_64 **Symbol + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT MACH_NLIST_64 **Symbol ); /** - Relocate Symbol to be against LinkAddress. + Relocate 32-bit Symbol to be against LinkAddress. + + @param[in,out] Context Context of the Mach-O. + @param[in] LinkAddress The address to be linked against. + @param[in,out] Symbol The symbol to be relocated. + + @returns Whether the operation has been completed successfully. + +**/ +BOOLEAN +MachoRelocateSymbol32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 LinkAddress, + IN OUT MACH_NLIST *Symbol + ); + +/** + Relocate 64-bit Symbol to be against LinkAddress. @param[in,out] Context Context of the Mach-O. @param[in] LinkAddress The address to be linked against. @@ -407,13 +838,33 @@ MachoGetSymbolByExternRelocationOffset64 ( **/ BOOLEAN MachoRelocateSymbol64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 LinkAddress, - IN OUT MACH_NLIST_64 *Symbol + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 LinkAddress, + IN OUT MACH_NLIST_64 *Symbol ); /** - Retrieves the Mach-O file offset of the address pointed to by Symbol. + Retrieves the Mach-O file offset of the address pointed to by a 32-bit Symbol. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the offset of. + @param[out] FileOffset Pointer the file offset is returned into. + If FALSE is returned, the output is undefined. + @param[out] MaxSize Maximum data safely available from FileOffset. + + @retval 0 0 is returned on failure. + +**/ +BOOLEAN +MachoSymbolGetFileOffset32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Retrieves the Mach-O file offset of the address pointed to by a 64-bit Symbol. @param[in,out] Context Context of the Mach-O. @param[in] Symbol Symbol to retrieve the offset of. @@ -426,14 +877,34 @@ MachoRelocateSymbol64 ( **/ BOOLEAN MachoSymbolGetFileOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol, - OUT UINT32 *FileOffset, - OUT UINT32 *MaxSize OPTIONAL + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_64 *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Retrieves the Mach-O file offset of the address pointed to by a Symbol. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol to retrieve the offset of. + @param[out] FileOffset Pointer the file offset is returned into. + If FALSE is returned, the output is undefined. + @param[out] MaxSize Maximum data safely available from FileOffset. + + @retval 0 0 is returned on failure. + +**/ +BOOLEAN +MachoSymbolGetFileOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_ANY *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL ); /** - Retrieves the Mach-O file offset of the address pointed to by Symbol. + Retrieves the Mach-O file offset of the address pointed to by a Symbol. @param[in,out] Context Context of the Mach-O. @param[in] Address Virtual address to retrieve the offset of. @@ -445,11 +916,11 @@ MachoSymbolGetFileOffset64 ( **/ BOOLEAN -MachoSymbolGetDirectFileOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT UINT32 *FileOffset, - OUT UINT32 *MaxSize OPTIONAL +MachoSymbolGetDirectFileOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL ); /** @@ -482,7 +953,7 @@ MachoSymbolNameIsPadslot ( **/ BOOLEAN -MachoSymbolNameIsSmcp64 ( +MachoSymbolNameIsSmcp ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SymbolName ); @@ -495,7 +966,7 @@ MachoSymbolNameIsSmcp64 ( **/ BOOLEAN -MachoSymbolNameIsMetaclassPointer64 ( +MachoSymbolNameIsMetaclassPointer ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SymbolName ); @@ -624,7 +1095,7 @@ MachoGetFinalSymbolNameFromClassName ( **/ BOOLEAN -MachoSymbolNameIsVtable64 ( +MachoSymbolNameIsVtable ( IN CONST CHAR8 *SymbolName ); @@ -640,7 +1111,22 @@ MachoSymbolNameIsCxx ( ); /** - Retrieves Metaclass symbol of a SMCP. + Retrieves 32-bit Metaclass symbol of a SMCP. + + @param[in,out] Context Context of the Mach-O. + @param[in] Smcp The SMCP to evaluate. + + @retval NULL NULL is returned on failure. + +**/ +MACH_NLIST * +MachoGetMetaclassSymbolFromSmcpSymbol32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Smcp + ); + +/** + Retrieves 64-bit Metaclass symbol of a SMCP. @param[in,out] Context Context of the Mach-O. @param[in] Smcp The SMCP to evaluate. @@ -655,7 +1141,25 @@ MachoGetMetaclassSymbolFromSmcpSymbol64 ( ); /** - Retrieves VTable and Meta VTable of a SMCP. + Retrieves 32-bit VTable and Meta VTable of a SMCP. + Logically matches XNU's get_vtable_syms_from_smcp. + + @param[in,out] Context Context of the Mach-O. + @param[in] SmcpName SMCP Symbol mame to retrieve the VTables from. + @param[out] Vtable Output buffer for the VTable symbol pointer. + @param[out] MetaVtable Output buffer for the Meta VTable symbol pointer. + +**/ +BOOLEAN +MachoGetVtableSymbolsFromSmcp32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SmcpName, + OUT CONST MACH_NLIST **Vtable, + OUT CONST MACH_NLIST **MetaVtable + ); + +/** + Retrieves 64-bit VTable and Meta VTable of a SMCP. Logically matches XNU's get_vtable_syms_from_smcp. @param[in,out] Context Context of the Mach-O. @@ -716,13 +1220,13 @@ MachoPreserveRelocationIntel64 ( @returns Whether the operation was successful. */ BOOLEAN -MachoInitialiseSymtabsExternal64 ( +MachoInitialiseSymtabsExternal ( IN OUT OC_MACHO_CONTEXT *Context, IN OC_MACHO_CONTEXT *SymsContext ); /** - Obtain symbol tables. + Obtain 32-bit symbol tables. @param[in] Context Context of the Mach-O. @param[out] SymbolTable Symbol table. @@ -737,7 +1241,35 @@ MachoInitialiseSymtabsExternal64 ( @return number of symbols in symbol table or 0. **/ UINT32 -MachoGetSymbolTable ( +MachoGetSymbolTable32 ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST **SymbolTable, + OUT CONST CHAR8 **StringTable OPTIONAL, + OUT CONST MACH_NLIST **LocalSymbols OPTIONAL, + OUT UINT32 *NumLocalSymbols OPTIONAL, + OUT CONST MACH_NLIST **ExternalSymbols OPTIONAL, + OUT UINT32 *NumExternalSymbols OPTIONAL, + OUT CONST MACH_NLIST **UndefinedSymbols OPTIONAL, + OUT UINT32 *NumUndefinedSymbols OPTIONAL + ); + +/** + Obtain 64-bit symbol tables. + + @param[in] Context Context of the Mach-O. + @param[out] SymbolTable Symbol table. + @param[out] StringTable String table for that symbol table. + @param[out] LocalSymbols Local symbol table. + @param[out] NumLocalSymbols Number of symbols in local symbol table. + @param[out] ExternalSymbols External symbol table. + @param[out] NumExternalSymbols Number of symbols in external symbol table. + @param[out] UndefinedSymbols Undefined symbol table. + @param[out] NumUndefinedSymbols Number of symbols in undefined symbol table. + + @return number of symbols in symbol table or 0. +**/ +UINT32 +MachoGetSymbolTable64 ( IN OUT OC_MACHO_CONTEXT *Context, OUT CONST MACH_NLIST_64 **SymbolTable, OUT CONST CHAR8 **StringTable OPTIONAL, @@ -750,7 +1282,21 @@ MachoGetSymbolTable ( ); /** - Obtain indirect symbol table. + Obtain indirect 32-bit symbol table. + + @param[in] Context Context of the Mach-O. + @param[in,out] SymbolTable Indirect symbol table. + + @return number of symbols in indirect symbol table or 0. +**/ +UINT32 +MachoGetIndirectSymbolTable32 ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST **SymbolTable + ); + +/** + Obtain indirect 64-bit symbol table. @param[in] Context Context of the Mach-O. @param[in,out] SymbolTable Indirect symbol table. @@ -758,7 +1304,7 @@ MachoGetSymbolTable ( @return number of symbols in indirect symbol table or 0. **/ UINT32 -MachoGetIndirectSymbolTable ( +MachoGetIndirectSymbolTable64 ( IN OUT OC_MACHO_CONTEXT *Context, OUT CONST MACH_NLIST_64 **SymbolTable ); @@ -773,7 +1319,7 @@ MachoGetIndirectSymbolTable ( **/ VOID * -MachoGetFilePointerByAddress64 ( +MachoGetFilePointerByAddress ( IN OUT OC_MACHO_CONTEXT *Context, IN UINT64 Address, OUT UINT32 *MaxSize OPTIONAL @@ -791,7 +1337,7 @@ MachoGetFilePointerByAddress64 ( **/ UINT32 -MachoExpandImage64 ( +MachoExpandImage ( IN OC_MACHO_CONTEXT *Context, OUT UINT8 *Destination, IN UINT32 DestinationSize, @@ -822,7 +1368,7 @@ MachoRuntimeGetEntryAddress ( @retval NULL NULL is returned on failure. **/ MACH_LOAD_COMMAND * -MachoGetNextCommand64 ( +MachoGetNextCommand ( IN OUT OC_MACHO_CONTEXT *Context, IN MACH_LOAD_COMMAND_TYPE LoadCommandType, IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL diff --git a/Include/Acidanthera/OpenCore.h b/Include/Acidanthera/OpenCore.h index 29995fdd..df6398f9 100644 --- a/Include/Acidanthera/OpenCore.h +++ b/Include/Acidanthera/OpenCore.h @@ -155,7 +155,8 @@ OcKernelBlockKexts ( IN OC_GLOBAL_CONFIG *Config, IN UINT32 DarwinVersion, IN BOOLEAN Is32Bit, - IN PRELINKED_CONTEXT *Context + IN KERNEL_CACHE_TYPE CacheType, + IN VOID *Context ); /** diff --git a/Include/Apple/IndustryStandard/AppleMachoImage.h b/Include/Apple/IndustryStandard/AppleMachoImage.h index 4c87d43f..ea577a7e 100644 --- a/Include/Apple/IndustryStandard/AppleMachoImage.h +++ b/Include/Apple/IndustryStandard/AppleMachoImage.h @@ -858,8 +858,8 @@ typedef struct { UINT32 Size; ///< size in bytes of this section UINT32 Offset; ///< file offset of this section UINT32 Alignment; ///< section alignment (power of 2) - UINT32 RelocationEntriesOffset; ///< file offset of relocation entries - UINT32 NumRelocationEntries; ///< number of relocation entries + UINT32 RelocationsOffset; ///< file offset of relocation entries + UINT32 NumRelocations; ///< number of relocation entries UINT32 Flags; ///< flags (section type and attributes) UINT32 Reserved1; ///< reserved (for offset or index) UINT32 Reserved2; ///< reserved (for count or sizeof) @@ -883,6 +883,11 @@ typedef struct { UINT32 Reserved3; ///< reserved } MACH_SECTION_64; +typedef union { + MACH_SECTION Section32; + MACH_SECTION_64 Section64; +} MACH_SECTION_ANY; + #define NEXT_MACH_SEGMENT(Segment) \ (MACH_SEGMENT_COMMAND *)((UINTN)(Segment) + (Segment)->Command.Size) @@ -939,6 +944,11 @@ typedef struct { MACH_SECTION_64 Sections[]; } MACH_SEGMENT_COMMAND_64; +typedef union { + MACH_SEGMENT_COMMAND Segment32; + MACH_SEGMENT_COMMAND_64 Segment64; +} MACH_SEGMENT_COMMAND_ANY; + /// /// A fixed virtual shared library (filetype == MH_FVMLIB in the mach header) /// contains a fvmlib_command (cmd == LC_IDFVMLIB) to identify the library. @@ -2109,6 +2119,11 @@ typedef struct { UINT64 Value; ///< value of this symbol (or stab offset) } MACH_NLIST_64; +typedef union { + MACH_NLIST Symbol32; + MACH_NLIST_64 Symbol64; +} MACH_NLIST_ANY; + // // Symbols with a index into the string table of zero (n_un.n_strx == 0) are // defined to have a null, "", name. Therefore all string indexes to non null diff --git a/Library/OcAppleKernelLib/CachelessContext.c b/Library/OcAppleKernelLib/CachelessContext.c index f80c4aa5..62f5a077 100644 --- a/Library/OcAppleKernelLib/CachelessContext.c +++ b/Library/OcAppleKernelLib/CachelessContext.c @@ -586,6 +586,35 @@ ScanDependencies ( return EFI_SUCCESS; } +STATIC +EFI_STATUS +InternalAddPatchedKext ( + IN OUT CACHELESS_CONTEXT *Context, + IN CONST CHAR8 *Identifier, + OUT PATCHED_KEXT **Kext + ) +{ + PATCHED_KEXT *PatchedKext; + + PatchedKext = AllocateZeroPool (sizeof (*PatchedKext)); + if (PatchedKext == NULL) { + return EFI_OUT_OF_RESOURCES; + } + + PatchedKext->Signature = PATCHED_KEXT_SIGNATURE; + PatchedKext->Identifier = AllocateCopyPool (AsciiStrSize (Identifier), Identifier); + if (PatchedKext->Identifier == NULL) { + FreePool (PatchedKext); + return EFI_OUT_OF_RESOURCES; + } + InitializeListHead (&PatchedKext->Patches); + + InsertTailList (&Context->PatchedKexts, &PatchedKext->Link); + + *Kext = PatchedKext; + return EFI_SUCCESS; +} + STATIC EFI_STATUS InternalAddKextPatch ( @@ -595,6 +624,7 @@ InternalAddKextPatch ( IN KERNEL_QUIRK_NAME QuirkName ) { + EFI_STATUS Status; PATCHED_KEXT *PatchedKext; KEXT_PATCH *KextPatch; KERNEL_QUIRK *KernelQuirk; @@ -611,20 +641,10 @@ InternalAddKextPatch ( // PatchedKext = LookupPatchedKextForIdentifier (Context, Identifier); if (PatchedKext == NULL) { - PatchedKext = AllocateZeroPool (sizeof (*PatchedKext)); - if (PatchedKext == NULL) { - return EFI_OUT_OF_RESOURCES; - } - - PatchedKext->Signature = PATCHED_KEXT_SIGNATURE; - PatchedKext->Identifier = AllocateCopyPool (AsciiStrSize (Identifier), Identifier); - if (PatchedKext->Identifier == NULL) { - FreePool (PatchedKext); - return EFI_OUT_OF_RESOURCES; + Status = InternalAddPatchedKext (Context, Identifier, &PatchedKext); + if (EFI_ERROR (Status)) { + return Status; } - InitializeListHead (&PatchedKext->Patches); - - InsertTailList (&Context->PatchedKexts, &PatchedKext->Link); } // @@ -657,7 +677,8 @@ CachelessContextInit ( IN OUT CACHELESS_CONTEXT *Context, IN CONST CHAR16 *FileName, IN EFI_FILE_PROTOCOL *ExtensionsDir, - IN UINT32 KernelVersion + IN UINT32 KernelVersion, + IN BOOLEAN Is32Bit ) { ASSERT (Context != NULL); @@ -669,6 +690,7 @@ CachelessContextInit ( Context->ExtensionsDir = ExtensionsDir; Context->ExtensionsDirFileName = FileName; Context->KernelVersion = KernelVersion; + Context->Is32Bit = Is32Bit; InitializeListHead (&Context->InjectedKexts); InitializeListHead (&Context->InjectedDependencies); @@ -986,6 +1008,31 @@ CachelessContextAddQuirk ( return InternalAddKextPatch (Context, NULL, NULL, Quirk); } +EFI_STATUS +CachelessContextBlock ( + IN OUT CACHELESS_CONTEXT *Context, + IN CONST CHAR8 *Identifier + ) +{ + EFI_STATUS Status; + PATCHED_KEXT *PatchedKext; + + // + // Check if bundle is already present. If not, add to list. + // + PatchedKext = LookupPatchedKextForIdentifier (Context, Identifier); + if (PatchedKext == NULL) { + Status = InternalAddPatchedKext (Context, Identifier, &PatchedKext); + if (EFI_ERROR (Status)) { + return Status; + } + } + + PatchedKext->Block = TRUE; + + return EFI_SUCCESS; +} + EFI_STATUS CachelessContextOverlayExtensionsDir ( IN OUT CACHELESS_CONTEXT *Context, @@ -1439,7 +1486,7 @@ CachelessContextHookBuiltin ( return Status; } - Status = PatcherInitContextFromBuffer (&Patcher, Buffer, BufferSize); + Status = PatcherInitContextFromBuffer (&Patcher, Buffer, BufferSize, Context->Is32Bit); if (EFI_ERROR (Status)) { FreePool (Buffer); return Status; @@ -1456,7 +1503,7 @@ CachelessContextHookBuiltin ( Status = KernelApplyQuirk (KextPatch->QuirkName, &Patcher, Context->KernelVersion); DEBUG (( EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, - "OCAK: Kernel quirk result for %a (%u) - %r\n", + "OCAK: Cacheless kernel quirk result for %a (%u) - %r\n", PatchedKext->Identifier, KextPatch->QuirkName, Status @@ -1465,7 +1512,7 @@ CachelessContextHookBuiltin ( Status = PatcherApplyGenericPatch (&Patcher, &KextPatch->Patch); DEBUG (( EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, - "OCAK: Kext patcher result for %a (%a) - %r\n", + "OCAK: Cacheless patcher result for %a (%a) - %r\n", PatchedKext->Identifier, KextPatch->Patch.Comment, Status @@ -1475,6 +1522,20 @@ CachelessContextHookBuiltin ( KextLink = GetNextNode (&PatchedKext->Patches, KextLink); } + // + // Block kext if requested. + // + if (PatchedKext->Block) { + Status = PatcherBlockKext (&Patcher); + DEBUG (( + EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, + "OCAK: Cacheless blocker result for %a (%a) - %r\n", + PatchedKext->Identifier, + KextPatch->Patch.Comment, + Status + )); + } + // // Virtualize patched binary. // diff --git a/Library/OcAppleKernelLib/CachelessInternal.h b/Library/OcAppleKernelLib/CachelessInternal.h index e9ec05f6..ccf9e4cf 100644 --- a/Library/OcAppleKernelLib/CachelessInternal.h +++ b/Library/OcAppleKernelLib/CachelessInternal.h @@ -135,6 +135,10 @@ typedef struct { // List of patches to apply. // LIST_ENTRY Patches; + // + // Block kext. + // + BOOLEAN Block; } PATCHED_KEXT; // diff --git a/Library/OcAppleKernelLib/CommonPatches.c b/Library/OcAppleKernelLib/CommonPatches.c index 4a91e9e8..5d098cf7 100644 --- a/Library/OcAppleKernelLib/CommonPatches.c +++ b/Library/OcAppleKernelLib/CommonPatches.c @@ -54,7 +54,7 @@ PatchAppleCpuPmCfgLock ( } Count = 0; - Walker = (UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext); + Walker = (UINT8 *) MachoGetMachHeader (&Patcher->MachContext); WalkerEnd = Walker + MachoGetFileSize (&Patcher->MachContext) - mWrmsrMaxDistance; // @@ -218,7 +218,7 @@ PatchAppleXcpmCfgLock ( return EFI_SUCCESS; } - Last = (XCPM_MSR_RECORD *) ((UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext) + Last = (XCPM_MSR_RECORD *) ((UINT8 *) MachoGetMachHeader (&Patcher->MachContext) + MachoGetFileSize (&Patcher->MachContext) - sizeof (XCPM_MSR_RECORD)); Replacements = 0; @@ -349,7 +349,7 @@ PatchAppleXcpmExtraMsrs ( return EFI_SUCCESS; } - Last = (XCPM_MSR_RECORD *) ((UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext) + Last = (XCPM_MSR_RECORD *) ((UINT8 *) MachoGetMachHeader (&Patcher->MachContext) + MachoGetFileSize (&Patcher->MachContext) - sizeof (XCPM_MSR_RECORD)); Replacements = 0; @@ -476,7 +476,7 @@ PatchAppleXcpmForceBoost ( return EFI_SUCCESS; } - Start = (UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext); + Start = (UINT8 *) MachoGetMachHeader (&Patcher->MachContext); Last = Start + MachoGetFileSize (&Patcher->MachContext) - EFI_PAGE_SIZE*2; Start += EFI_PAGE_SIZE; Current = Start; @@ -1069,496 +1069,6 @@ PatchIncreasePciBarSize ( return Status; } - -STATIC -CONST UINT8 -mKernelCpuIdFindRelNew[] = { - 0xB9, 0x8B, 0x00, 0x00, 0x00, ///< mov ecx, 8Bh - 0x31, 0xC0, ///< xor eax, eax - 0x31, 0xD2, ///< xor edx, edx - 0x0F, 0x30, ///< wrmsr - 0xB8, 0x01, 0x00, 0x00, 0x00, ///< mov eax, 1 - 0x31, 0xDB, ///< xor ebx, ebx - 0x31, 0xC9, ///< xor ecx, ecx - 0x31, 0xD2, ///< xor edx, edx - 0x0F, 0xA2 ///< cpuid -}; - -STATIC -CONST UINT8 -mKernelCpuIdFindRelOld[] = { - 0xB9, 0x8B, 0x00, 0x00, 0x00, ///< mov ecx, 8Bh - 0x31, 0xD2, ///< xor edx, edx - 0x0F, 0x30, ///< wrmsr - 0xB8, 0x01, 0x00, 0x00, 0x00, ///< mov eax, 1 - 0x31, 0xDB, ///< xor ebx, ebx - 0x31, 0xC9, ///< xor ecx, ecx - 0x31, 0xD2, ///< xor edx, edx - 0x0F, 0xA2 ///< cpuid -}; - -STATIC -CONST UINT8 -mKernelCpuidFindMcRel[] = { - 0xB9, 0x8B, 0x00, 0x00, 0x00, 0x0F, 0x32 -}; - -/** - cpu->cpuid_signature = 0x11111111; - cpu->cpuid_stepping = 0x22; - cpu->cpuid_model = 0x33; - cpu->cpuid_family = 0x44; - cpu->cpuid_type = 0x55555555; - cpu->cpuid_extmodel = 0x66; - cpu->cpuid_extfamily = 0x77; - cpu->cpuid_features = 0x8888888888888888; - cpu->cpuid_logical_per_package = 0x99999999; - cpu->cpuid_cpufamily = 0xAAAAAAAA; - return 0xAAAAAAAA; -**/ - - -STATIC -CONST UINT8 -mKernelCpuidReplaceDbg[] = { - 0xC7, 0x47, 0x68, 0x11, 0x11, 0x11, 0x11, ///< mov dword ptr [rdi+68h], 11111111h - 0xC6, 0x47, 0x50, 0x22, ///< mov byte ptr [rdi+50h], 22h - 0x48, 0xB8, 0x55, 0x55, 0x55, 0x55, 0x44, 0x33, 0x66, 0x77, ///< mov rax, 7766334455555555h - 0x48, 0x89, 0x47, 0x48, ///< mov [rdi+48h], rax - 0x48, 0xB8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, ///< mov rax, 8888888888888888h - 0x48, 0x89, 0x47, 0x58, ///< mov [rdi+58h], rax - 0xC7, 0x87, 0xCC, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, ///< mov dword ptr [rdi+0CCh], 99999999h - 0xC7, 0x87, 0x88, 0x01, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, ///< mov dword ptr [rdi+188h], 0AAAAAAAAh - 0xB8, 0xAA, 0xAA, 0xAA, 0xAA, ///< mov eax, 0AAAAAAAAh - 0xC3 ///< retn -}; - -#pragma pack(push, 1) - -typedef struct { - UINT8 Code1[3]; - UINT32 Signature; - UINT8 Code2[3]; - UINT8 Stepping; - UINT8 Code3[2]; - UINT32 Type; - UINT8 Family; - UINT8 Model; - UINT8 ExtModel; - UINT8 ExtFamily; - UINT8 Code4[6]; - UINT64 Features; - UINT8 Code5[10]; - UINT32 LogicalPerPkg; - UINT8 Code6[6]; - UINT32 AppleFamily1; - UINT8 Code7; - UINT32 AppleFamily2; - UINT8 Code8; -} INTERNAL_CPUID_FN_PATCH; - -STATIC_ASSERT ( - sizeof (INTERNAL_CPUID_FN_PATCH) == sizeof (mKernelCpuidReplaceDbg), - "Check your CPUID patch layout" - ); - -typedef struct { - UINT8 EaxCmd; - UINT32 EaxVal; - UINT8 EbxCmd; - UINT32 EbxVal; - UINT8 EcxCmd; - UINT32 EcxVal; - UINT8 EdxCmd; - UINT32 EdxVal; -} INTERNAL_CPUID_PATCH; - -typedef struct { - UINT8 EdxCmd; - UINT32 EdxVal; -} INTERNAL_MICROCODE_PATCH; - -#pragma pack(pop) - -STATIC -EFI_STATUS -PatchKernelCpuIdLegacy ( - IN OUT PATCHER_CONTEXT *Patcher, - IN OC_CPU_INFO *CpuInfo, - IN UINT32 *Data, - IN UINT32 *DataMask, - IN UINT8 *Record - ) -{ - UINT8 *StartPointer; - UINT8 *EndPointer; - UINT8 *Location; - UINT8 *LocationEnd; - UINT32 Signature[3]; - UINT32 Index; - UINT32 MaxExt; - INT32 Delta; - INTERNAL_CPUID_PATCH Patch; - - STATIC CONST UINT8 mKernelCpuidFindLegacyStart[8] = { - 0xBA, 0x04, 0x00, 0x00, 0x00, ///< mov edx, 4 - 0x48, 0x8D, 0x35 ///< lea rsi from lea rsi, byte_FFFFFF80006C5900 - }; - - for (Index = 0; Index < EFI_PAGE_SIZE; ++Index, ++Record) { - if (Record[0] == mKernelCpuidFindLegacyStart[0] - && Record[1] == mKernelCpuidFindLegacyStart[1] - && Record[2] == mKernelCpuidFindLegacyStart[2] - && Record[3] == mKernelCpuidFindLegacyStart[3] - && Record[4] == mKernelCpuidFindLegacyStart[4] - && Record[5] == mKernelCpuidFindLegacyStart[5] - && Record[6] == mKernelCpuidFindLegacyStart[6] - && Record[7] == mKernelCpuidFindLegacyStart[7]) { - break; - } - } - - if (Index >= EFI_PAGE_SIZE) { - return EFI_NOT_FOUND; - } - - StartPointer = Record + sizeof (mKernelCpuidFindLegacyStart) + sizeof (UINT32); - - STATIC CONST UINT8 mKernelCpuidFindLegacyEnd1[6] = { - 0x00, ///< 0 from mov cs:byte_FFFFFF80006C590C, 0 - 0xB8, 0x00, 0x00, 0x00, 0x80, ///< mov eax, 80000000h - }; - - for (; Index < EFI_PAGE_SIZE; ++Index, ++Record) { - if (Record[0] == mKernelCpuidFindLegacyEnd1[0] - && Record[1] == mKernelCpuidFindLegacyEnd1[1] - && Record[2] == mKernelCpuidFindLegacyEnd1[2] - && Record[3] == mKernelCpuidFindLegacyEnd1[3] - && Record[4] == mKernelCpuidFindLegacyEnd1[4] - && Record[5] == mKernelCpuidFindLegacyEnd1[5]) { - break; - } - } - - if (Index >= EFI_PAGE_SIZE) { - return EFI_NOT_FOUND; - } - - STATIC CONST UINT8 mKernelCpuidFindLegacyEnd2[3] = { - 0x0F, 0xA2, ///< cpuid - 0x89 ///< mov prefix from mov [rbp+var_40], eax - }; - - for (; Index < EFI_PAGE_SIZE; ++Index, ++Record) { - if (Record[0] == mKernelCpuidFindLegacyEnd2[0] - && Record[1] == mKernelCpuidFindLegacyEnd2[1] - && Record[2] == mKernelCpuidFindLegacyEnd2[2]) { - break; - } - } - - if (Index >= EFI_PAGE_SIZE) { - return EFI_NOT_FOUND; - } - - EndPointer = Record - 3; - - STATIC CONST UINT8 mKernelCpuidFindLegacyLoc1[5] = { - 0xB8, 0x01, 0x00, 0x00, 0x00, ///< mov eax, 1 - }; - - for (; Index < EFI_PAGE_SIZE; ++Index, ++Record) { - if (Record[0] == mKernelCpuidFindLegacyLoc1[0] - && Record[1] == mKernelCpuidFindLegacyLoc1[1] - && Record[2] == mKernelCpuidFindLegacyLoc1[2] - && Record[3] == mKernelCpuidFindLegacyLoc1[3] - && Record[4] == mKernelCpuidFindLegacyLoc1[4]) { - break; - } - } - - if (Index >= EFI_PAGE_SIZE) { - return EFI_NOT_FOUND; - } - - Location = Record; - - STATIC CONST UINT8 mKernelCpuidFindLegacyLoc2[3] = { - 0x0F, 0xA2, ///< cpuid - 0x89 ///< mov prefix from mov [rbp+var_40], eax - }; - - for (; Index < EFI_PAGE_SIZE; ++Index, ++Record) { - if (Record[0] == mKernelCpuidFindLegacyLoc2[0] - && Record[1] == mKernelCpuidFindLegacyLoc2[1] - && Record[2] == mKernelCpuidFindLegacyLoc2[2]) { - break; - } - } - - if (Index >= EFI_PAGE_SIZE) { - return EFI_NOT_FOUND; - } - - LocationEnd = Record + 2; - - // - // Free 2+ more bytes in the end by assigning EAX directly. - // - AsmCpuid (0x80000000, &MaxExt, NULL, NULL, NULL); - EndPointer[0] = 0xB8; - CopyMem (&EndPointer[1], &MaxExt, sizeof (MaxExt)); - - // - // Short-write CPU signature at RSI save space. - // - AsmCpuid (0, NULL, &Signature[0], &Signature[2], &Signature[1]); - for (Index = 0; Index < 3; ++Index) { - // - // mov eax, - // - *StartPointer++ = 0xB8; - CopyMem (StartPointer, &Signature[Index], sizeof (Signature[0])); - StartPointer += sizeof (Signature[0]); - // - // mov [rsi], eax - // - *StartPointer++ = 0x89; - *StartPointer++ = 0x06; - if (Index < 2) { - // - // add rsi, 4 - // - *StartPointer++ = 0x48; - *StartPointer++ = 0x83; - *StartPointer++ = 0xC6; - *StartPointer++ = 0x04; - } - } - - // - // Ensure that we still have room, which is within 2-byte jmp (127) - // and has at least 2-byte jmp and patch + 5-byte jmp area. - // Should have around 25 bytes here. - // - if (StartPointer >= EndPointer - || EndPointer - StartPointer > 128 - || (UINTN) (EndPointer - StartPointer) < sizeof (INTERNAL_CPUID_PATCH) + 7) { - return EFI_OUT_OF_RESOURCES; - } - - // - // Short jmp to EndPointer - // - StartPointer[0] = 0xEB; - StartPointer[1] = (UINT8) (EndPointer - StartPointer - 2); - StartPointer += 2; - - // - // Workaround incorrect OSXSAVE handling, see below. - // - if (CpuInfo->CpuidVerEcx.Bits.XSAVE != 0 - && CpuInfo->CpuidVerEcx.Bits.OSXSAVE == 0 - && CpuInfo->CpuidVerEcx.Bits.AVX != 0) { - CpuInfo->CpuidVerEcx.Bits.OSXSAVE = 1; - } - - // - // Jump from location to patch area. - // - Delta = (INT32) (StartPointer - (Location + 5)); - *Location++ = 0xE9; - CopyMem (Location, &Delta, sizeof (Delta)); - Location += sizeof (Delta); - while (Location < LocationEnd) { - *Location++ = 0x90; - } - - // - // Write virtualised CPUID. - // - Patch.EaxCmd = 0xB8; - Patch.EaxVal = (Data[0] & DataMask[0]) | (CpuInfo->CpuidVerEax.Uint32 & ~DataMask[0]); - Patch.EbxCmd = 0xBB; - Patch.EbxVal = (Data[1] & DataMask[1]) | (CpuInfo->CpuidVerEbx.Uint32 & ~DataMask[1]); - Patch.EcxCmd = 0xB9; - Patch.EcxVal = (Data[2] & DataMask[2]) | (CpuInfo->CpuidVerEcx.Uint32 & ~DataMask[2]); - Patch.EdxCmd = 0xBA; - Patch.EdxVal = (Data[3] & DataMask[3]) | (CpuInfo->CpuidVerEdx.Uint32 & ~DataMask[3]); - CopyMem (StartPointer, &Patch, sizeof (Patch)); - StartPointer += sizeof (Patch); - - // - // Jump to the end of location (5-byte mov + 2-byte cpuid). - // - Delta = (INT32) (Location - (StartPointer + 5)); - *StartPointer++ = 0xE9; - CopyMem (StartPointer, &Delta, sizeof (Delta)); - - return EFI_SUCCESS; -} - -EFI_STATUS -PatchKernelCpuId ( - IN OUT PATCHER_CONTEXT *Patcher, - IN OC_CPU_INFO *CpuInfo, - IN UINT32 *Data, - IN UINT32 *DataMask - ) -{ - EFI_STATUS Status; - UINT8 *CpuidSetInfo; - UINT8 *Record; - UINT8 *Last; - UINT32 Index; - UINT32 FoundSize; - INTERNAL_CPUID_PATCH *CpuidPatch; - INTERNAL_MICROCODE_PATCH *McPatch; - INTERNAL_CPUID_FN_PATCH *FnPatch; - CPUID_VERSION_INFO_EAX Eax; - CPUID_VERSION_INFO_EBX Ebx; - CPUID_VERSION_INFO_ECX Ecx; - CPUID_VERSION_INFO_EDX Edx; - BOOLEAN FoundReleaseKernel; - - ASSERT (Patcher != NULL); - - STATIC_ASSERT ( - sizeof (mKernelCpuIdFindRelNew) > sizeof (mKernelCpuIdFindRelOld), - "Kernel CPUID patch seems wrong" - ); - - ASSERT (mKernelCpuIdFindRelNew[0] == mKernelCpuIdFindRelOld[0] - && mKernelCpuIdFindRelNew[1] == mKernelCpuIdFindRelOld[1] - && mKernelCpuIdFindRelNew[2] == mKernelCpuIdFindRelOld[2] - && mKernelCpuIdFindRelNew[3] == mKernelCpuIdFindRelOld[3] - ); - - Last = ((UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext) - + MachoGetFileSize (&Patcher->MachContext) - EFI_PAGE_SIZE*2 - sizeof (mKernelCpuIdFindRelNew)); - - Status = PatcherGetSymbolAddress (Patcher, "_cpuid_set_info", (UINT8 **) &CpuidSetInfo); - if (EFI_ERROR (Status) || CpuidSetInfo >= Last) { - DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_set_info (%p) - %r\n", CpuidSetInfo, Status)); - return EFI_NOT_FOUND; - } - - Record = CpuidSetInfo; - FoundSize = 0; - - for (Index = 0; Index < EFI_PAGE_SIZE; ++Index, ++Record) { - if (Record[0] == mKernelCpuIdFindRelNew[0] - && Record[1] == mKernelCpuIdFindRelNew[1] - && Record[2] == mKernelCpuIdFindRelNew[2] - && Record[3] == mKernelCpuIdFindRelNew[3]) { - - if (CompareMem (Record, mKernelCpuIdFindRelNew, sizeof (mKernelCpuIdFindRelNew)) == 0) { - FoundSize = sizeof (mKernelCpuIdFindRelNew); - break; - } else if (CompareMem (Record, mKernelCpuIdFindRelOld, sizeof (mKernelCpuIdFindRelOld)) == 0) { - FoundSize = sizeof (mKernelCpuIdFindRelOld); - break; - } - } - } - - FoundReleaseKernel = FoundSize > 0; - - if (!FoundReleaseKernel) { - Status = PatchKernelCpuIdLegacy (Patcher, CpuInfo, Data, DataMask, CpuidSetInfo); - if (!EFI_ERROR (Status)) { - return EFI_SUCCESS; - } - } - - // - // When patching the release kernel we do not allow reevaluating CPUID information, - // which is used to report OSXSAVE availability. This causes issues with some programs, - // like Docker using Hypervisor.framework, which rely on sysctl to track CPU feature. - // - // To workaround this we make sure to always report OSXSAVE bit when it is available - // regardless of the reevaluation performed by init_fpu in XNU. - // - // REF: https://github.com/acidanthera/bugtracker/issues/1035 - // - if (FoundReleaseKernel - && CpuInfo->CpuidVerEcx.Bits.XSAVE != 0 - && CpuInfo->CpuidVerEcx.Bits.OSXSAVE == 0 - && CpuInfo->CpuidVerEcx.Bits.AVX != 0) { - CpuInfo->CpuidVerEcx.Bits.OSXSAVE = 1; - } - - Eax.Uint32 = (Data[0] & DataMask[0]) | (CpuInfo->CpuidVerEax.Uint32 & ~DataMask[0]); - Ebx.Uint32 = (Data[1] & DataMask[1]) | (CpuInfo->CpuidVerEbx.Uint32 & ~DataMask[1]); - Ecx.Uint32 = (Data[2] & DataMask[2]) | (CpuInfo->CpuidVerEcx.Uint32 & ~DataMask[2]); - Edx.Uint32 = (Data[3] & DataMask[3]) | (CpuInfo->CpuidVerEdx.Uint32 & ~DataMask[3]); - - if (FoundReleaseKernel) { - CpuidPatch = (INTERNAL_CPUID_PATCH *) Record; - CpuidPatch->EaxCmd = 0xB8; - CpuidPatch->EaxVal = Eax.Uint32; - CpuidPatch->EbxCmd = 0xBB; - CpuidPatch->EbxVal = Ebx.Uint32; - CpuidPatch->EcxCmd = 0xB9; - CpuidPatch->EcxVal = Ecx.Uint32; - CpuidPatch->EdxCmd = 0xBA; - CpuidPatch->EdxVal = Edx.Uint32; - SetMem ( - Record + sizeof (INTERNAL_CPUID_PATCH), - FoundSize - sizeof (INTERNAL_CPUID_PATCH), - 0x90 - ); - Record += FoundSize; - - for (Index = 0; Index < EFI_PAGE_SIZE - sizeof (mKernelCpuidFindMcRel); ++Index, ++Record) { - if (CompareMem (Record, mKernelCpuidFindMcRel, sizeof (mKernelCpuidFindMcRel)) == 0) { - McPatch = (INTERNAL_MICROCODE_PATCH *) Record; - McPatch->EdxCmd = 0xBA; - McPatch->EdxVal = CpuInfo->MicrocodeRevision; - SetMem ( - Record + sizeof (INTERNAL_MICROCODE_PATCH), - sizeof (mKernelCpuidFindMcRel) - sizeof (INTERNAL_MICROCODE_PATCH), - 0x90 - ); - return EFI_SUCCESS; - } - } - } else { - // - // Handle debug kernel here... - // - Status = PatcherGetSymbolAddress (Patcher, "_cpuid_set_cpufamily", (UINT8 **) &Record); - if (EFI_ERROR (Status) || Record >= Last) { - DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_set_cpufamily (%p) - %r\n", Record, Status)); - return EFI_NOT_FOUND; - } - - CopyMem (Record, mKernelCpuidReplaceDbg, sizeof (mKernelCpuidReplaceDbg)); - FnPatch = (INTERNAL_CPUID_FN_PATCH *) Record; - FnPatch->Signature = Eax.Uint32; - FnPatch->Stepping = (UINT8) Eax.Bits.SteppingId; - FnPatch->ExtModel = (UINT8) Eax.Bits.ExtendedModelId; - FnPatch->Model = (UINT8) Eax.Bits.Model | (UINT8) (Eax.Bits.ExtendedModelId << 4U); - FnPatch->Family = (UINT8) Eax.Bits.FamilyId; - FnPatch->Type = (UINT8) Eax.Bits.ProcessorType; - FnPatch->ExtFamily = (UINT8) Eax.Bits.ExtendedFamilyId; - FnPatch->Features = LShiftU64 (Ecx.Uint32, 32) | (UINT64) Edx.Uint32; - if (FnPatch->Features & CPUID_FEATURE_HTT) { - FnPatch->LogicalPerPkg = (UINT16) Ebx.Bits.MaximumAddressableIdsForLogicalProcessors; - } else { - FnPatch->LogicalPerPkg = 1; - } - - FnPatch->AppleFamily1 = FnPatch->AppleFamily2 = OcCpuModelToAppleFamily (Eax); - - return EFI_SUCCESS; - } - - DEBUG ((DEBUG_WARN, "OCAK: Failed to find either CPUID patch (%u)\n", FoundSize)); - - return EFI_UNSUPPORTED; -} - STATIC CONST UINT8 mCustomSmbiosGuidPatchFind[] = { @@ -1652,7 +1162,7 @@ PatchPanicKextDump ( return EFI_SUCCESS; } - Last = ((UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext) + Last = ((UINT8 *) MachoGetMachHeader (&Patcher->MachContext) + MachoGetFileSize (&Patcher->MachContext) - EFI_PAGE_SIZE); // @@ -1913,35 +1423,70 @@ PatchPowerStateTimeout ( // we patch-out __ZN8AppleRTC8rtcWriteEjh call arguments (0x58 and 0x59) with // invalid (out of range) value 0xFFFF in 4 places. // +// 10.5 and below do not have __ZN8AppleRTC19rtcRecordTracePointEjjj. +// + +STATIC +UINT8 +mAppleRtcChecksumPatchFind32[] = { + 0xC7, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00 +}; + +STATIC +UINT8 +mAppleRtcChecksumPatchMask32[] = { + 0xFF, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF +}; STATIC UINT8 -mAppleRtcChecksumPatchFind[] = { +mAppleRtcChecksumPatchReplace32[] = { + 0xC7, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0x00, 0x00 +}; + +STATIC +UINT8 +mAppleRtcChecksumPatchFind64[] = { 0xBE, 0x58, 0x00, 0x00, 0x00 }; STATIC UINT8 -mAppleRtcChecksumPatchMask[] = { +mAppleRtcChecksumPatchMask64[] = { 0xFF, 0xFE, 0xFF, 0xFF, 0xFF }; STATIC UINT8 -mAppleRtcChecksumPatchReplace[] = { +mAppleRtcChecksumPatchReplace64[] = { 0xBE, 0xFF, 0xFF, 0x00, 0x00 }; STATIC PATCHER_GENERIC_PATCH -mAppleRtcChecksumPatch = { - .Comment = DEBUG_POINTER ("DisableRtcChecksum"), +mAppleRtcChecksumPatch32 = { + .Comment = DEBUG_POINTER ("DisableRtcChecksum32"), + .Base = NULL, + .Find = mAppleRtcChecksumPatchFind32, + .Mask = mAppleRtcChecksumPatchMask32, + .Replace = mAppleRtcChecksumPatchReplace32, + .ReplaceMask = mAppleRtcChecksumPatchMask32, + .Size = sizeof (mAppleRtcChecksumPatchFind32), + .Count = 4, + .Skip = 0, + .Limit = 0 +}; + +STATIC +PATCHER_GENERIC_PATCH +mAppleRtcChecksumPatch64 = { + .Comment = DEBUG_POINTER ("DisableRtcChecksum64"), .Base = NULL, - .Find = mAppleRtcChecksumPatchFind, - .Mask = mAppleRtcChecksumPatchMask, - .Replace = mAppleRtcChecksumPatchReplace, + .Find = mAppleRtcChecksumPatchFind64, + .Mask = mAppleRtcChecksumPatchMask64, + .Replace = mAppleRtcChecksumPatchReplace64, .ReplaceMask = NULL, - .Size = sizeof (mAppleRtcChecksumPatchFind), + .Size = sizeof (mAppleRtcChecksumPatchFind64), .Count = 4, .Skip = 0, .Limit = 0 @@ -1960,7 +1505,7 @@ PatchAppleRtcChecksum ( return EFI_NOT_FOUND; } - Status = PatcherApplyGenericPatch (Patcher, &mAppleRtcChecksumPatch); + Status = PatcherApplyGenericPatch (Patcher, Patcher->Is32Bit ? &mAppleRtcChecksumPatch32 : &mAppleRtcChecksumPatch64); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_INFO, "OCAK: Failed to apply patch com.apple.driver.AppleRTC DisableRtcChecksum - %r\n", Status)); } else { @@ -1992,7 +1537,7 @@ PatchSegmentJettison ( return EFI_SUCCESS; } - Last = (UINT8 *) MachoGetMachHeader64 (&Patcher->MachContext) + Last = (UINT8 *) MachoGetMachHeader (&Patcher->MachContext) + MachoGetFileSize (&Patcher->MachContext) - sizeof (EFI_PAGE_SIZE) * 2; Status = PatcherGetSymbolAddress (Patcher, "__ZN6OSKext19removeKextBootstrapEv", (UINT8 **) &RemoveBs); diff --git a/Library/OcAppleKernelLib/CpuidPatches.c b/Library/OcAppleKernelLib/CpuidPatches.c new file mode 100644 index 00000000..fd169cc7 --- /dev/null +++ b/Library/OcAppleKernelLib/CpuidPatches.c @@ -0,0 +1,918 @@ +/** @file + CPUID kernel patches. + +Copyright (c) 2018-2020, vit9696, Goldfish64. 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 + +STATIC +CONST UINT8 +mKernelCpuIdFindRelNew[] = { + 0xB9, 0x8B, 0x00, 0x00, 0x00, // mov ecx, 8Bh + 0x31, 0xC0, // xor eax, eax + 0x31, 0xD2, // xor edx, edx + 0x0F, 0x30, // wrmsr + 0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1 + 0x31, 0xDB, // xor ebx, ebx + 0x31, 0xC9, // xor ecx, ecx + 0x31, 0xD2, // xor edx, edx + 0x0F, 0xA2 // cpuid +}; + +STATIC +CONST UINT8 +mKernelCpuIdFindRelOld[] = { + 0xB9, 0x8B, 0x00, 0x00, 0x00, // mov ecx, 8Bh + 0x31, 0xD2, // xor edx, edx + 0x0F, 0x30, // wrmsr + 0xB8, 0x01, 0x00, 0x00, 0x00, // mov eax, 1 + 0x31, 0xDB, // xor ebx, ebx + 0x31, 0xC9, // xor ecx, ecx + 0x31, 0xD2, // xor edx, edx + 0x0F, 0xA2 // cpuid +}; + +STATIC +CONST UINT8 +mKernelCpuidFindMcRel[] = { + 0xB9, 0x8B, 0x00, 0x00, 0x00, // mov ecx, 8Bh + 0x0F, 0x32 // rdmsr +}; + +/** + cpu->cpuid_signature = 0x11111111; + cpu->cpuid_stepping = 0x22; + cpu->cpuid_model = 0x33; + cpu->cpuid_family = 0x44; + cpu->cpuid_type = 0x55555555; + cpu->cpuid_extmodel = 0x66; + cpu->cpuid_extfamily = 0x77; + cpu->cpuid_features = 0x8888888888888888; + cpu->cpuid_logical_per_package = 0x99999999; + cpu->cpuid_cpufamily = 0xAAAAAAAA; + return 0xAAAAAAAA; +**/ + + +STATIC +CONST UINT8 +mKernelCpuidReplaceDbg[] = { + 0xC7, 0x47, 0x68, 0x11, 0x11, 0x11, 0x11, // mov dword ptr [rdi+68h], 11111111h + 0xC6, 0x47, 0x50, 0x22, // mov byte ptr [rdi+50h], 22h + 0x48, 0xB8, 0x55, 0x55, 0x55, 0x55, 0x44, 0x33, 0x66, 0x77, // mov rax, 7766334455555555h + 0x48, 0x89, 0x47, 0x48, // mov [rdi+48h], rax + 0x48, 0xB8, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, // mov rax, 8888888888888888h + 0x48, 0x89, 0x47, 0x58, // mov [rdi+58h], rax + 0xC7, 0x87, 0xCC, 0x00, 0x00, 0x00, 0x99, 0x99, 0x99, 0x99, // mov dword ptr [rdi+0CCh], 99999999h + 0xC7, 0x87, 0x88, 0x01, 0x00, 0x00, 0xAA, 0xAA, 0xAA, 0xAA, // mov dword ptr [rdi+188h], 0AAAAAAAAh + 0xB8, 0xAA, 0xAA, 0xAA, 0xAA, // mov eax, 0AAAAAAAAh + 0xC3 // retn +}; + +#pragma pack(push, 1) + +typedef struct { + UINT8 Code1[3]; + UINT32 Signature; + UINT8 Code2[3]; + UINT8 Stepping; + UINT8 Code3[2]; + UINT32 Type; + UINT8 Family; + UINT8 Model; + UINT8 ExtModel; + UINT8 ExtFamily; + UINT8 Code4[6]; + UINT64 Features; + UINT8 Code5[10]; + UINT32 LogicalPerPkg; + UINT8 Code6[6]; + UINT32 AppleFamily1; + UINT8 Code7; + UINT32 AppleFamily2; + UINT8 Code8; +} INTERNAL_CPUID_FN_PATCH; + +STATIC_ASSERT ( + sizeof (INTERNAL_CPUID_FN_PATCH) == sizeof (mKernelCpuidReplaceDbg), + "Check your CPUID patch layout" + ); + +typedef struct { + UINT8 EaxCmd; + UINT32 EaxVal; + UINT8 EbxCmd; + UINT32 EbxVal; + UINT8 EcxCmd; + UINT32 EcxVal; + UINT8 EdxCmd; + UINT32 EdxVal; +} INTERNAL_CPUID_PATCH; + +typedef struct { + UINT8 EdxCmd; + UINT32 EdxVal; +} INTERNAL_MICROCODE_PATCH; + +#pragma pack(pop) + +STATIC +EFI_STATUS +PatchKernelCpuIdLegacy ( + IN OUT PATCHER_CONTEXT *Patcher, + IN UINT32 KernelVersion, + IN OC_CPU_INFO *CpuInfo, + IN UINT32 *Data, + IN UINT32 *DataMask, + IN UINT8 *Start, + IN UINT8 *Last + ) +{ + EFI_STATUS Status; + + UINT8 *Record; + UINT8 *BlockClearFunc; + UINT8 *StartPointer; + UINT8 *EndPointer; + UINT32 StructAddr; + UINT8 *Location; + UINT8 *LocationEnd; + UINT8 *LocationTsc; + UINT8 *LocationTscEnd; + UINT8 *LocationSnow32; + UINT32 Signature[3]; + BOOLEAN IsTiger; + BOOLEAN IsLeopard; + BOOLEAN IsSnow; + BOOLEAN IsLion; + UINT32 Index; + UINT32 MaxExt; + INT32 Delta; + INTERNAL_CPUID_PATCH Patch; + + IsTiger = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_TIGER_MIN, KERNEL_VERSION_TIGER_MAX); + IsLeopard = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LEOPARD_MIN, KERNEL_VERSION_LEOPARD_MAX); + IsSnow = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_SNOW_LEOPARD_MIN, KERNEL_VERSION_SNOW_LEOPARD_MAX); + IsLion = OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LION_MIN, KERNEL_VERSION_LION_MAX); + StructAddr = 0; + + LocationSnow32 = NULL; + LocationTsc = NULL; + LocationTscEnd = NULL; + + // + // Locate _cpuid_set_info or _cpuid_get_info. + // _cpuid_set_info is also patched in 10.4, and is located below _cpuid_get_info. + // + Status = PatcherGetSymbolAddress (Patcher, IsTiger ? "_cpuid_get_info" : "_cpuid_set_info", (UINT8 **) &Record); + if (EFI_ERROR (Status) || Record >= Last) { + DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_%a_info (%p) - %r\n", IsTiger ? "get" : "set", Record, Status)); + return EFI_NOT_FOUND; + } + + // + // Start of patch area. + // + // The existing code here will be replaced with a hardcoded vendor string (CPUID (0)). + // We'll also hardcode the CPUID (1) data here, jumped to from the next patch location area. + // + // 10.4 is in _cpuid_get_info. + // Others are in _cpuid_set_info. + // + // 10.4 has the struct in esi. + // 10.5 and 32-bit 10.6 have the struct hardcoded. This can be + // pulled right below the call to _blkclr, as below. + // 64-bit 10.6 has the struct in rdi, a bit before lea rsi... below. + // 32-bit 10.7 has the struct hardcoded, and can be pulled right + // before the call to _bzero + // + STATIC CONST UINT8 mKernelCpuidFindPatchTigerStart[4] = { + 0x8D, 0x45, 0xEC, // lea eax, dword [...] + 0xC7 // mov from mov dword [...], 4 + }; + + STATIC CONST UINT8 mKernelCpuidFindPatchLeoSnowLionStruct32[2] = { + 0x00, // 0 from end of mov dword [...], [struct address] + 0xE8 // call from call _blkclr or _bzero + }; + + STATIC CONST UINT8 mKernelCpuidFindPatchLeoSnowLionStart32[5] = { + 0x04, 0x00, 0x00, 0x00, // 4 from mov dword [...], 4 + 0xC7 // mov... + }; + + STATIC CONST UINT8 mKernelCpuidFindPatchSnowStart64[8] = { + 0xBA, 0x04, 0x00, 0x00, 0x00, // mov edx, 4 + 0x48, 0x8D, 0x35 // lea rsi, ... + }; + + if (Patcher->Is32Bit) { + if (IsTiger) { + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchTigerStart[0] + && Record[1] == mKernelCpuidFindPatchTigerStart[1] + && Record[2] == mKernelCpuidFindPatchTigerStart[2] + && Record[3] == mKernelCpuidFindPatchTigerStart[3]) { + break; + } + } + } else { + // + // We need to get the address of _blkclr or _bzero first for proper matching. + // + Status = PatcherGetSymbolAddress (Patcher, IsLion ? "_bzero" : "_blkclr", (UINT8 **) &BlockClearFunc); + if (EFI_ERROR (Status) || Record >= Last) { + DEBUG ((DEBUG_WARN, "OCAK: Failed to locate %a (%p) - %r\n", IsLion ? "_bzero" : "_blkclr", Record, Status)); + return EFI_NOT_FOUND; + } + + // + // Get struct address first. + // + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchLeoSnowLionStruct32[0] + && Record[1] == mKernelCpuidFindPatchLeoSnowLionStruct32[1] + && *((INT32 *) &Record[2]) == (INT32) (BlockClearFunc - (Record + sizeof (mKernelCpuidFindPatchLeoSnowLionStruct32) + sizeof (UINT32)))) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + StructAddr = *((UINT32 *) (Record - 3)); + + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchLeoSnowLionStart32[0] + && Record[1] == mKernelCpuidFindPatchLeoSnowLionStart32[1] + && Record[2] == mKernelCpuidFindPatchLeoSnowLionStart32[2] + && Record[3] == mKernelCpuidFindPatchLeoSnowLionStart32[3] + && Record[4] == mKernelCpuidFindPatchLeoSnowLionStart32[4]) { + break; + } + } + } + } else { + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchSnowStart64[0] + && Record[1] == mKernelCpuidFindPatchSnowStart64[1] + && Record[2] == mKernelCpuidFindPatchSnowStart64[2] + && Record[3] == mKernelCpuidFindPatchSnowStart64[3] + && Record[4] == mKernelCpuidFindPatchSnowStart64[4] + && Record[5] == mKernelCpuidFindPatchSnowStart64[5] + && Record[6] == mKernelCpuidFindPatchSnowStart64[6] + && Record[7] == mKernelCpuidFindPatchSnowStart64[7]) { + break; + } + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + if (Patcher->Is32Bit) { + StartPointer = IsTiger ? Record : Record - 4 ; + } else { + StartPointer = Record + sizeof (mKernelCpuidFindPatchSnowStart64) + sizeof (UINT32); + } + + // + // End of patch area. + // + STATIC CONST UINT8 mKernelCpuidFindPatchTigerEnd[4] = { + 0x00, // 0 from mov byte [...], 0 + 0x31, 0xDB, // xor ebx, ebx + 0x8B // mov ... + }; + + STATIC UINT8 mKernelCpuidFindPatchSnowLionEnd32[7] = { + 0xC6, 0x05, 0xFF, 0xFF, 0xFF, 0xFF, 0x00 // mov byte [struct_addr], 0 + }; + STATIC UINT32 *mKernelCpuidFindPatchSnowLionEndPtr32 = (UINT32 *) &mKernelCpuidFindPatchSnowLionEnd32[2]; + *mKernelCpuidFindPatchSnowLionEndPtr32 = StructAddr + sizeof (Signature[0]) * 3; + + STATIC CONST UINT8 mKernelCpuidFindPatchLeoEnd1[5] = { + 0xB8, 0x00, 0x00, 0x00, 0x80 // mov eax/edx, 80000000h + }; + STATIC CONST UINT8 mKernelCpuidFindPatchLeoEnd1Mask = 0xFD; + + if (IsTiger) { + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchTigerEnd[0] + && Record[1] == mKernelCpuidFindPatchTigerEnd[1] + && Record[2] == mKernelCpuidFindPatchTigerEnd[2] + && Record[3] == mKernelCpuidFindPatchTigerEnd[3]) { + break; + } + } + } else if ((IsSnow || IsLion) && Patcher->Is32Bit) { + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchSnowLionEnd32[0] + && Record[1] == mKernelCpuidFindPatchSnowLionEnd32[1] + && Record[2] == mKernelCpuidFindPatchSnowLionEnd32[2] + && Record[3] == mKernelCpuidFindPatchSnowLionEnd32[3] + && Record[4] == mKernelCpuidFindPatchSnowLionEnd32[4] + && Record[5] == mKernelCpuidFindPatchSnowLionEnd32[5] + && Record[6] == mKernelCpuidFindPatchSnowLionEnd32[6]) { + break; + } + } + } else { + for (Index = 0; Index < EFI_PAGE_SIZE; Index++, Record++) { + if ((Record[0] & mKernelCpuidFindPatchLeoEnd1Mask) == mKernelCpuidFindPatchLeoEnd1[0] + && Record[1] == mKernelCpuidFindPatchLeoEnd1[1] + && Record[2] == mKernelCpuidFindPatchLeoEnd1[2] + && Record[3] == mKernelCpuidFindPatchLeoEnd1[3] + && Record[4] == mKernelCpuidFindPatchLeoEnd1[4]) { + break; + } + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + if (IsSnow && !Patcher->Is32Bit) { + STATIC CONST UINT8 mKernelCpuidFindPatchLeoEnd2[3] = { + 0x0F, 0xA2, // cpuid + 0x89 // mov ... + }; + + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindPatchLeoEnd2[0] + && Record[1] == mKernelCpuidFindPatchLeoEnd2[1] + && Record[2] == mKernelCpuidFindPatchLeoEnd2[2]) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + } + + EndPointer = IsTiger ? Record - 3 : Record; + + // + // Start of CPUID location. + // + // We'll replace this with a call to the previous patched section to + // populate the CPUID (1) info. + // + // 32-bit 10.6 has two different mov eax, 0x1 pairs for 32-bit and 64-bit. + // The first is patched out with nops, and we'll use the second for the jmp. + // + // 32-bit 10.7 has a call to a function that is very similar to 32-bit 10.6, and calls + // cpuid in 64-bit mode if supported. We can simply replace this call with one to the patched area. + // + STATIC CONST UINT8 mKernelCpuidFindLocLeoTigerStart[7] = { + 0xB9, 0x01, 0x00, 0x00, 0x00, // mov ecx, 1 + 0x89, 0xC8 // mov eax, ecx + }; + + STATIC CONST UINT8 mKernelCpuidFindLocSnowStart[5] = { + 0xB8, 0x01, 0x00, 0x00, 0x00 // mov eax, 1 + }; + + STATIC CONST UINT8 mKernelCpuidFindLocSnowStart32[6] = { + 0xE8, 0xFF, 0xFF, 0xFF, 0xFF, // call _cpuid64 + 0xEB // jmp ... + }; + + STATIC CONST UINT8 mKernelCpuidFindLocLionStart32[8] = { + 0xB9, 0x01, 0x00, 0x00, 0x00, // mov ecx, 1 + 0x8D, 0x55, 0xD0 // lea edx, dword [...] + }; + + if (IsTiger || IsLeopard) { + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLocLeoTigerStart[0] + && Record[1] == mKernelCpuidFindLocLeoTigerStart[1] + && Record[2] == mKernelCpuidFindLocLeoTigerStart[2] + && Record[3] == mKernelCpuidFindLocLeoTigerStart[3] + && Record[4] == mKernelCpuidFindLocLeoTigerStart[4] + && Record[5] == mKernelCpuidFindLocLeoTigerStart[5] + && Record[6] == mKernelCpuidFindLocLeoTigerStart[6]) { + break; + } + } + } else if (IsSnow) { + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLocSnowStart[0] + && Record[1] == mKernelCpuidFindLocSnowStart[1] + && Record[2] == mKernelCpuidFindLocSnowStart[2] + && Record[3] == mKernelCpuidFindLocSnowStart[3] + && Record[4] == mKernelCpuidFindLocSnowStart[4]) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + // + // Find where call _cpuid64 is located. + // We'll then look for the second mov eax, 0x1 after that. + // + if (Patcher->Is32Bit) { + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLocSnowStart32[0] + && Record[5] == mKernelCpuidFindLocSnowStart32[5]) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + LocationSnow32 = Record; + + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLocSnowStart[0] + && Record[1] == mKernelCpuidFindLocSnowStart[1] + && Record[2] == mKernelCpuidFindLocSnowStart[2] + && Record[3] == mKernelCpuidFindLocSnowStart[3] + && Record[4] == mKernelCpuidFindLocSnowStart[4]) { + break; + } + } + } + } else { + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLocLionStart32[0] + && Record[1] == mKernelCpuidFindLocLionStart32[1] + && Record[2] == mKernelCpuidFindLocLionStart32[2] + && Record[3] == mKernelCpuidFindLocLionStart32[3] + && Record[4] == mKernelCpuidFindLocLionStart32[4] + && Record[5] == mKernelCpuidFindLocLionStart32[5] + && Record[6] == mKernelCpuidFindLocLionStart32[6] + && Record[7] == mKernelCpuidFindLocLionStart32[7]) { + break; + } + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + Location = IsLion ? Record + sizeof (mKernelCpuidFindLocLionStart32): Record; + + // + // End of CPUID location. Not applicable to 10.7. + // + STATIC CONST UINT8 mKernelCpuidFindLegacyLocEnd[3] = { + 0x0F, 0xA2, // cpuid + 0x89 // mov ... + }; + + if (!IsLion) { + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLegacyLocEnd[0] + && Record[1] == mKernelCpuidFindLegacyLocEnd[1] + && Record[2] == mKernelCpuidFindLegacyLocEnd[2]) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + LocationEnd = Record + 2; + } else { + LocationEnd = Location; + } + + // + // Locate _tsc_init on 10.4, as there is a CPUID (1) call that needs to be patched. + // + if (IsTiger) { + Status = PatcherGetSymbolAddress (Patcher, "_tsc_init", (UINT8 **) &Record); + if (EFI_ERROR (Status) || Record >= Last) { + DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _tsc_init (%p) - %r\n", Record, Status)); + return EFI_NOT_FOUND; + } + + // + // Start of _tsc_init CPUID location. + // + // We'll replace this with a call to the previous patched section to + // populate the CPUID (1) info. + // + STATIC CONST UINT8 mKernelCpuidFindTscLocTigerStart[7] = { + 0xBA, 0x01, 0x00, 0x00, 0x00, // mov edx, 1 + 0x89, 0xD0 // mov eax, edx + }; + + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindTscLocTigerStart[0] + && Record[1] == mKernelCpuidFindTscLocTigerStart[1] + && Record[2] == mKernelCpuidFindTscLocTigerStart[2] + && Record[3] == mKernelCpuidFindTscLocTigerStart[3] + && Record[4] == mKernelCpuidFindTscLocTigerStart[4] + && Record[5] == mKernelCpuidFindTscLocTigerStart[5] + && Record[6] == mKernelCpuidFindTscLocTigerStart[6]) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + LocationTsc = Record; + + // + // End of _tsc_init CPUID location. + // + for (; Index < EFI_PAGE_SIZE; Index++, Record++) { + if (Record[0] == mKernelCpuidFindLegacyLocEnd[0] + && Record[1] == mKernelCpuidFindLegacyLocEnd[1] + && Record[2] == mKernelCpuidFindLegacyLocEnd[2]) { + break; + } + } + + if (Index >= EFI_PAGE_SIZE) { + return EFI_NOT_FOUND; + } + + LocationTscEnd = Record + 2; + } + + DEBUG (( + DEBUG_INFO, + "OCAK: Legacy CPUID patch %p:%p, loc %p:%p, tsc loc %p:%p struct @ 0x%X\n", + StartPointer - Start, + EndPointer - Start, + Location - Start, + LocationEnd - Start, + IsTiger ? LocationTsc - Start : 0, + IsTiger ? LocationTscEnd - Start : 0, + StructAddr + )); + + // + // Free 2 more bytes in the end by assigning EAX directly. + // + if (IsSnow && !Patcher->Is32Bit) { + AsmCpuid (0x80000000, &MaxExt, NULL, NULL, NULL); + EndPointer[0] = 0xB8; + CopyMem (&EndPointer[1], &MaxExt, sizeof (MaxExt)); + } + + // + // Short-write CPU signature to create space for CPUID (1) patch. + // + // 29 bytes. + // + AsmCpuid (0, NULL, &Signature[0], &Signature[2], &Signature[1]); + for (Index = 0; Index < 3; ++Index) { + // + // mov eax, + // + *StartPointer++ = 0xB8; + CopyMem (StartPointer, &Signature[Index], sizeof (Signature[0])); + StartPointer += sizeof (Signature[0]); + + if (IsLeopard || ((IsSnow || IsLion) && Patcher->Is32Bit)) { + // + // mov dword [struct_addr], eax + // + *StartPointer++ = 0xA3; + CopyMem (StartPointer, &StructAddr, sizeof (StructAddr)); + StartPointer += sizeof (StructAddr); + StructAddr += sizeof (Signature[0]); + + } else if (IsTiger) { + // + // mov [esi+offset], eax + // + *StartPointer++ = 0x89; + *StartPointer++ = 0x46; + *StartPointer++ = (UINT8) (Index * sizeof (Signature[0])); + + } else { + // + // mov [rsi], eax + // + *StartPointer++ = 0x89; + *StartPointer++ = 0x06; + + if (Index < 2) { + // + // add rsi, 4 + // + *StartPointer++ = 0x48; + *StartPointer++ = 0x83; + *StartPointer++ = 0xC6; + *StartPointer++ = 0x04; + } + } + } + + // + // Ensure that we still have room, which is within 2-byte jmp (127) + // and has at least 2-byte jmp and patch + 1-byte ret. + // + if (StartPointer >= EndPointer + || EndPointer - StartPointer > 128 + || (UINTN) (EndPointer - StartPointer) < sizeof (INTERNAL_CPUID_PATCH) + 3) { + return EFI_OUT_OF_RESOURCES; + } + + // + // Short jmp to EndPointer. + // + StartPointer[0] = 0xEB; + StartPointer[1] = (UINT8) (EndPointer - StartPointer - 2); + StartPointer += 2; + + // + // Workaround incorrect OSXSAVE handling, see below. + // + if (CpuInfo->CpuidVerEcx.Bits.XSAVE != 0 + && CpuInfo->CpuidVerEcx.Bits.OSXSAVE == 0 + && CpuInfo->CpuidVerEcx.Bits.AVX != 0) { + CpuInfo->CpuidVerEcx.Bits.OSXSAVE = 1; + } + + // + // NOP out call _cpuid64 in 32-bit 10.6. + // + if (IsSnow && Patcher->Is32Bit) { + while (LocationSnow32 < LocationEnd) { + *LocationSnow32++ = 0x90; + } + } + + // + // Call from location to patch area. + // + Delta = (INT32) (StartPointer - (Location + 5)); + *Location++ = 0xE8; + CopyMem (Location, &Delta, sizeof (Delta)); + Location += sizeof (Delta); + while (Location < LocationEnd) { + *Location++ = 0x90; + } + + // + // In 10.4, we need to replace a call to CPUID (1) with a call to + // the patch area like above in _tsc_init. + // + if (IsTiger) { + Delta = (INT32) (StartPointer - (LocationTsc + 5)); + *LocationTsc++ = 0xE8; + CopyMem (LocationTsc, &Delta, sizeof (Delta)); + LocationTsc += sizeof (Delta); + while (LocationTsc < LocationTscEnd) { + *LocationTsc++ = 0x90; + } + } + + // + // Write virtualised CPUID. + // 20 bytes. + // 10.7 requires the registers to be copied to a memory location in EDX, so we'll use ESI instead. + // + Patch.EaxCmd = 0xB8; + Patch.EaxVal = (Data[0] & DataMask[0]) | (CpuInfo->CpuidVerEax.Uint32 & ~DataMask[0]); + Patch.EbxCmd = 0xBB; + Patch.EbxVal = (Data[1] & DataMask[1]) | (CpuInfo->CpuidVerEbx.Uint32 & ~DataMask[1]); + Patch.EcxCmd = 0xB9; + Patch.EcxVal = (Data[2] & DataMask[2]) | (CpuInfo->CpuidVerEcx.Uint32 & ~DataMask[2]); + Patch.EdxCmd = IsLion ? 0xBE : 0xBA; + Patch.EdxVal = (Data[3] & DataMask[3]) | (CpuInfo->CpuidVerEdx.Uint32 & ~DataMask[3]); + CopyMem (StartPointer, &Patch, sizeof (Patch)); + StartPointer += sizeof (Patch); + + // + // Under 10.7, we need to copy registers to memory in EDX. + // + if (IsLion) { + // + // mov [edx], eax + // + *StartPointer++ = 0x89; + *StartPointer++ = 0x02; + // + // mov [edx+4], ebx + // + *StartPointer++ = 0x89; + *StartPointer++ = 0x5A; + *StartPointer++ = 0x04; + // + // mov [edx+8], ecx + // + *StartPointer++ = 0x89; + *StartPointer++ = 0x4A; + *StartPointer++ = 0x08; + // + // mov [edx+12], esi + // + *StartPointer++ = 0x89; + *StartPointer++ = 0x72; + *StartPointer++ = 0x0C; + } + + // + // Return to the end of location. + // + *StartPointer++ = 0xC3; + + DEBUG ((DEBUG_INFO, "OCAK: Legacy CPUID patch completed @ %p\n", StartPointer - Start)); + return EFI_SUCCESS; +} + +EFI_STATUS +PatchKernelCpuId ( + IN OUT PATCHER_CONTEXT *Patcher, + IN OC_CPU_INFO *CpuInfo, + IN UINT32 *Data, + IN UINT32 *DataMask, + IN UINT32 KernelVersion + ) +{ + EFI_STATUS Status; + UINT8 *CpuidSetInfo; + UINT8 *Record; + UINT8 *Start; + UINT8 *Last; + UINT32 Index; + UINT32 FoundSize; + INTERNAL_CPUID_PATCH *CpuidPatch; + INTERNAL_MICROCODE_PATCH *McPatch; + INTERNAL_CPUID_FN_PATCH *FnPatch; + CPUID_VERSION_INFO_EAX Eax; + CPUID_VERSION_INFO_EBX Ebx; + CPUID_VERSION_INFO_ECX Ecx; + CPUID_VERSION_INFO_EDX Edx; + BOOLEAN FoundReleaseKernel; + + ASSERT (Patcher != NULL); + + STATIC_ASSERT ( + sizeof (mKernelCpuIdFindRelNew) > sizeof (mKernelCpuIdFindRelOld), + "Kernel CPUID patch seems wrong" + ); + + ASSERT (mKernelCpuIdFindRelNew[0] == mKernelCpuIdFindRelOld[0] + && mKernelCpuIdFindRelNew[1] == mKernelCpuIdFindRelOld[1] + && mKernelCpuIdFindRelNew[2] == mKernelCpuIdFindRelOld[2] + && mKernelCpuIdFindRelNew[3] == mKernelCpuIdFindRelOld[3] + ); + + Start = ((UINT8 *) MachoGetMachHeader (&Patcher->MachContext)); + Last = Start + MachoGetFileSize (&Patcher->MachContext) - EFI_PAGE_SIZE * 2 - sizeof (mKernelCpuIdFindRelNew); + + // + // Do legacy patching for 32-bit 10.7, and 10.6 and older. + // + if ((OcMatchDarwinVersion (KernelVersion, KERNEL_VERSION_LION_MIN, KERNEL_VERSION_LION_MAX) && Patcher->Is32Bit) + || OcMatchDarwinVersion (KernelVersion, 0, KERNEL_VERSION_SNOW_LEOPARD_MAX)) { + Status = PatchKernelCpuIdLegacy (Patcher, KernelVersion, CpuInfo, Data, DataMask, Start, Last); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_WARN, "OCAK: Failed to patch legacy CPUID - %r\n", Status)); + } + return Status; + } + + Status = PatcherGetSymbolAddress (Patcher, "_cpuid_set_info", (UINT8 **) &CpuidSetInfo); + if (EFI_ERROR (Status) || CpuidSetInfo >= Last) { + DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_set_info (%p) - %r\n", CpuidSetInfo, Status)); + return EFI_NOT_FOUND; + } + + Record = CpuidSetInfo; + FoundSize = 0; + + for (Index = 0; Index < EFI_PAGE_SIZE; ++Index, ++Record) { + if (Record[0] == mKernelCpuIdFindRelNew[0] + && Record[1] == mKernelCpuIdFindRelNew[1] + && Record[2] == mKernelCpuIdFindRelNew[2] + && Record[3] == mKernelCpuIdFindRelNew[3]) { + + if (CompareMem (Record, mKernelCpuIdFindRelNew, sizeof (mKernelCpuIdFindRelNew)) == 0) { + FoundSize = sizeof (mKernelCpuIdFindRelNew); + break; + } else if (CompareMem (Record, mKernelCpuIdFindRelOld, sizeof (mKernelCpuIdFindRelOld)) == 0) { + FoundSize = sizeof (mKernelCpuIdFindRelOld); + break; + } + } + } + + FoundReleaseKernel = FoundSize > 0; + + // + // When patching the release kernel we do not allow reevaluating CPUID information, + // which is used to report OSXSAVE availability. This causes issues with some programs, + // like Docker using Hypervisor.framework, which rely on sysctl to track CPU feature. + // + // To workaround this we make sure to always report OSXSAVE bit when it is available + // regardless of the reevaluation performed by init_fpu in XNU. + // + // REF: https://github.com/acidanthera/bugtracker/issues/1035 + // + if (FoundReleaseKernel + && CpuInfo->CpuidVerEcx.Bits.XSAVE != 0 + && CpuInfo->CpuidVerEcx.Bits.OSXSAVE == 0 + && CpuInfo->CpuidVerEcx.Bits.AVX != 0) { + CpuInfo->CpuidVerEcx.Bits.OSXSAVE = 1; + } + + Eax.Uint32 = (Data[0] & DataMask[0]) | (CpuInfo->CpuidVerEax.Uint32 & ~DataMask[0]); + Ebx.Uint32 = (Data[1] & DataMask[1]) | (CpuInfo->CpuidVerEbx.Uint32 & ~DataMask[1]); + Ecx.Uint32 = (Data[2] & DataMask[2]) | (CpuInfo->CpuidVerEcx.Uint32 & ~DataMask[2]); + Edx.Uint32 = (Data[3] & DataMask[3]) | (CpuInfo->CpuidVerEdx.Uint32 & ~DataMask[3]); + + if (FoundReleaseKernel) { + CpuidPatch = (INTERNAL_CPUID_PATCH *) Record; + CpuidPatch->EaxCmd = 0xB8; + CpuidPatch->EaxVal = Eax.Uint32; + CpuidPatch->EbxCmd = 0xBB; + CpuidPatch->EbxVal = Ebx.Uint32; + CpuidPatch->EcxCmd = 0xB9; + CpuidPatch->EcxVal = Ecx.Uint32; + CpuidPatch->EdxCmd = 0xBA; + CpuidPatch->EdxVal = Edx.Uint32; + SetMem ( + Record + sizeof (INTERNAL_CPUID_PATCH), + FoundSize - sizeof (INTERNAL_CPUID_PATCH), + 0x90 + ); + Record += FoundSize; + + for (Index = 0; Index < EFI_PAGE_SIZE - sizeof (mKernelCpuidFindMcRel); ++Index, ++Record) { + if (CompareMem (Record, mKernelCpuidFindMcRel, sizeof (mKernelCpuidFindMcRel)) == 0) { + McPatch = (INTERNAL_MICROCODE_PATCH *) Record; + McPatch->EdxCmd = 0xBA; + McPatch->EdxVal = CpuInfo->MicrocodeRevision; + SetMem ( + Record + sizeof (INTERNAL_MICROCODE_PATCH), + sizeof (mKernelCpuidFindMcRel) - sizeof (INTERNAL_MICROCODE_PATCH), + 0x90 + ); + return EFI_SUCCESS; + } + } + } else { + // + // Handle debug kernel here... + // + Status = PatcherGetSymbolAddress (Patcher, "_cpuid_set_cpufamily", (UINT8 **) &Record); + if (EFI_ERROR (Status) || Record >= Last) { + DEBUG ((DEBUG_WARN, "OCAK: Failed to locate _cpuid_set_cpufamily (%p) - %r\n", Record, Status)); + return EFI_NOT_FOUND; + } + + CopyMem (Record, mKernelCpuidReplaceDbg, sizeof (mKernelCpuidReplaceDbg)); + FnPatch = (INTERNAL_CPUID_FN_PATCH *) Record; + FnPatch->Signature = Eax.Uint32; + FnPatch->Stepping = (UINT8) Eax.Bits.SteppingId; + FnPatch->ExtModel = (UINT8) Eax.Bits.ExtendedModelId; + FnPatch->Model = (UINT8) Eax.Bits.Model | (UINT8) (Eax.Bits.ExtendedModelId << 4U); + FnPatch->Family = (UINT8) Eax.Bits.FamilyId; + FnPatch->Type = (UINT8) Eax.Bits.ProcessorType; + FnPatch->ExtFamily = (UINT8) Eax.Bits.ExtendedFamilyId; + FnPatch->Features = LShiftU64 (Ecx.Uint32, 32) | (UINT64) Edx.Uint32; + if (FnPatch->Features & CPUID_FEATURE_HTT) { + FnPatch->LogicalPerPkg = (UINT16) Ebx.Bits.MaximumAddressableIdsForLogicalProcessors; + } else { + FnPatch->LogicalPerPkg = 1; + } + + FnPatch->AppleFamily1 = FnPatch->AppleFamily2 = OcCpuModelToAppleFamily (Eax); + + return EFI_SUCCESS; + } + + DEBUG ((DEBUG_WARN, "OCAK: Failed to find either CPUID patch (%u)\n", FoundSize)); + + return EFI_UNSUPPORTED; +} diff --git a/Library/OcAppleKernelLib/KernelCollection.c b/Library/OcAppleKernelLib/KernelCollection.c index 3723533e..d0e916d0 100644 --- a/Library/OcAppleKernelLib/KernelCollection.c +++ b/Library/OcAppleKernelLib/KernelCollection.c @@ -242,7 +242,7 @@ KcRebuildMachHeader ( // hardware reset (like on iOS) and they now really try to require W^X: // https://developer.apple.com/videos/play/wwdc2020/10686/ // - if (!MachoMergeSegments64 (&Context->PrelinkedMachContext, KC_REGION_SEGMENT_PREFIX)) { + if (!MachoMergeSegments (&Context->PrelinkedMachContext, KC_REGION_SEGMENT_PREFIX)) { DEBUG ((DEBUG_INFO, "OCAK: Segment expansion failure\n")); return EFI_UNSUPPORTED; } @@ -316,7 +316,7 @@ KcInitKextFixupChains ( "Alignment is not guaranteed." ); - DyldChainedFixups = (CONST MACH_LINKEDIT_DATA_COMMAND *) MachoGetNextCommand64 ( + DyldChainedFixups = (CONST MACH_LINKEDIT_DATA_COMMAND *) MachoGetNextCommand ( &Context->PrelinkedMachContext, MACH_LOAD_COMMAND_DYLD_CHAINED_FIXUPS, NULL @@ -583,7 +583,7 @@ KcKextIndexFixups ( // re-initialisation in mind. We really don't want to sanitise everything // again, so avoid the dedicated API for now. // - DySymtab = (MACH_DYSYMTAB_COMMAND *) MachoGetNextCommand64 ( + DySymtab = (MACH_DYSYMTAB_COMMAND *) MachoGetNextCommand ( MachContext, MACH_LOAD_COMMAND_DYSYMTAB, NULL diff --git a/Library/OcAppleKernelLib/KextPatcher.c b/Library/OcAppleKernelLib/KextPatcher.c index 9997b389..b7164228 100644 --- a/Library/OcAppleKernelLib/KextPatcher.c +++ b/Library/OcAppleKernelLib/KextPatcher.c @@ -27,6 +27,76 @@ #include "MkextInternal.h" #include "PrelinkedInternal.h" +STATIC +BOOLEAN +GetTextBaseOffset ( + IN OC_MACHO_CONTEXT *ExecutableContext, + OUT UINT64 *Address, + OUT UINT64 *Offset + ) +{ + MACH_SEGMENT_COMMAND *Segment32; + MACH_SECTION *Section32; + MACH_SEGMENT_COMMAND_64 *Segment64; + + UINT64 VirtualAddress; + UINT64 FileOffset; + + // + // 32-bit can be of type MH_OBJECT, which has all sections in a single unnamed segment. + // We'll fallback to that if there is no __TEXT segment. + // + if (ExecutableContext->Is32Bit) { + Segment32 = MachoGetNextSegment32 (ExecutableContext, NULL); + if (Segment32 == NULL) { + return FALSE; + } + + if (AsciiStrCmp (Segment32->SegmentName, "") == 0) { + Section32 = MachoGetSectionByName32 ( + ExecutableContext, + Segment32, + "__text" + ); + if (Section32 == NULL) { + return FALSE; + } + + VirtualAddress = Section32->Address; + FileOffset = Section32->Offset; + + } else { + Segment32 = MachoGetSegmentByName32 ( + ExecutableContext, + "__TEXT" + ); + if (Segment32 == NULL || Segment32->VirtualAddress < Segment32->FileOffset) { + return FALSE; + } + + VirtualAddress = Segment32->VirtualAddress; + FileOffset = Segment32->FileOffset; + } + + } else { + Segment64 = MachoGetSegmentByName64 ( + ExecutableContext, + "__TEXT" + ); + if (Segment64 == NULL || Segment64->VirtualAddress < Segment64->FileOffset) { + return FALSE; + } + + VirtualAddress = Segment64->VirtualAddress; + FileOffset = Segment64->FileOffset; + } + + *Address = VirtualAddress; + *Offset = FileOffset; + + return TRUE; +} + EFI_STATUS PatcherInitContextFromPrelinked ( IN OUT PATCHER_CONTEXT *Context, @@ -59,19 +129,22 @@ PatcherInitContextFromMkext( return EFI_NOT_FOUND; } - return PatcherInitContextFromBuffer (Context, &Mkext->Mkext[Kext->BinaryOffset], Kext->BinarySize); + return PatcherInitContextFromBuffer (Context, &Mkext->Mkext[Kext->BinaryOffset], Kext->BinarySize, Mkext->Is32Bit); } EFI_STATUS PatcherInitContextFromBuffer ( IN OUT PATCHER_CONTEXT *Context, IN OUT UINT8 *Buffer, - IN UINT32 BufferSize + IN UINT32 BufferSize, + IN BOOLEAN Is32Bit ) { - EFI_STATUS Status; - OC_MACHO_CONTEXT InnerContext; - MACH_SEGMENT_COMMAND_64 *Segment; + EFI_STATUS Status; + OC_MACHO_CONTEXT InnerContext; + + UINT64 VirtualAddress; + UINT64 FileOffset; ASSERT (Context != NULL); ASSERT (Buffer != NULL); @@ -84,36 +157,58 @@ PatcherInitContextFromBuffer ( // and request PRELINK_KERNEL_IDENTIFIER. // - if (!MachoInitializeContext (&Context->MachContext, Buffer, BufferSize, 0)) { - DEBUG ((DEBUG_INFO, "OCAK: Patcher init from buffer %p %u has unsupported mach-o\n", Buffer, BufferSize)); + if (!MachoInitializeContext (&Context->MachContext, Buffer, BufferSize, 0, Is32Bit)) { + DEBUG (( + DEBUG_INFO, + "OCAK: %a-bit patcher init from buffer %p %u has unsupported mach-o\n", + Is32Bit ? "32" : "64", + Buffer, + BufferSize + )); return EFI_INVALID_PARAMETER; } - Segment = MachoGetSegmentByName64 ( - &Context->MachContext, - "__TEXT" - ); - if (Segment == NULL || Segment->VirtualAddress < Segment->FileOffset) { + if (!GetTextBaseOffset (&Context->MachContext, &VirtualAddress, &FileOffset)) { return EFI_NOT_FOUND; } - Context->VirtualBase = Segment->VirtualAddress - Segment->FileOffset; + Context->Is32Bit = Is32Bit; + Context->VirtualBase = VirtualAddress; + Context->FileOffset = FileOffset; Context->VirtualKmod = 0; Context->KxldState = NULL; Context->KxldStateSize = 0; Context->IsKernelCollection = FALSE; - Status = InternalConnectExternalSymtab ( + KextFindKmodAddress ( &Context->MachContext, - &InnerContext, - Buffer, + 0, BufferSize, - NULL + &Context->VirtualKmod ); - if (EFI_ERROR (Status)) { - return Status; + + if (!Context->Is32Bit) { + Status = InternalConnectExternalSymtab ( + &Context->MachContext, + &InnerContext, + Buffer, + BufferSize, + NULL + ); + if (EFI_ERROR (Status)) { + return Status; + } } + DEBUG (( + DEBUG_VERBOSE, + "OCAK: %a-bit patcher base 0x%llX kmod 0x%llX file 0x%llX\n", + Is32Bit ? "32" : "64", + Context->VirtualBase, + Context->VirtualKmod, + Context->FileOffset + )); + return EFI_SUCCESS; } @@ -124,11 +219,11 @@ PatcherGetSymbolAddress ( IN OUT UINT8 **Address ) { - MACH_NLIST_64 *Symbol; - CONST CHAR8 *SymbolName; - UINT64 SymbolAddress; - UINT32 Offset; - UINT32 Index; + MACH_NLIST_ANY *Symbol; + CONST CHAR8 *SymbolName; + UINT64 SymbolAddress; + UINT32 Offset; + UINT32 Index; Index = 0; Offset = 0; @@ -136,7 +231,7 @@ PatcherGetSymbolAddress ( // // Try the usual way first via SYMTAB. // - Symbol = MachoGetSymbolByIndex64 (&Context->MachContext, Index); + Symbol = MachoGetSymbolByIndex (&Context->MachContext, Index); if (Symbol == NULL) { // // If we have KxldState, use it. @@ -150,8 +245,7 @@ PatcherGetSymbolAddress ( // // If we have a symbol, get its ondisk offset. // - if (SymbolAddress != 0 - && MachoSymbolGetDirectFileOffset64 (&Context->MachContext, SymbolAddress, &Offset, NULL)) { + if (SymbolAddress != 0 && MachoSymbolGetDirectFileOffset (&Context->MachContext, SymbolAddress, &Offset, NULL)) { // // Proceed to success. // @@ -162,12 +256,12 @@ PatcherGetSymbolAddress ( return EFI_NOT_FOUND; } - SymbolName = MachoGetSymbolName64 (&Context->MachContext, Symbol); + SymbolName = MachoGetSymbolName (&Context->MachContext, Symbol); if (SymbolName != NULL && AsciiStrCmp (Name, SymbolName) == 0) { // // Once we have a symbol, get its ondisk offset. // - if (MachoSymbolGetFileOffset64 (&Context->MachContext, Symbol, &Offset, NULL)) { + if (MachoSymbolGetFileOffset (&Context->MachContext, Symbol, &Offset, NULL)) { // // Proceed to success. // @@ -180,7 +274,7 @@ PatcherGetSymbolAddress ( Index++; } - *Address = (UINT8 *)MachoGetMachHeader64 (&Context->MachContext) + Offset; + *Address = (UINT8 *) MachoGetMachHeader (&Context->MachContext) + Offset; return EFI_SUCCESS; } @@ -195,28 +289,30 @@ PatcherApplyGenericPatch ( UINT32 Size; UINT32 ReplaceCount; - Base = (UINT8 *)MachoGetMachHeader64 (&Context->MachContext); + Base = (UINT8 *) MachoGetMachHeader (&Context->MachContext); Size = MachoGetFileSize (&Context->MachContext); if (Patch->Base != NULL) { Status = PatcherGetSymbolAddress (Context, Patch->Base, &Base); if (EFI_ERROR (Status)) { DEBUG (( DEBUG_INFO, - "OCAK: %a base lookup failure %r\n", + "OCAK: %a-bit %a base lookup failure %r\n", + Context->Is32Bit ? "32" : "64", Patch->Comment != NULL ? Patch->Comment : "Patch", Status )); return Status; } - Size -= (UINT32)(Base - (UINT8 *)MachoGetMachHeader64 (&Context->MachContext)); + Size -= (UINT32)(Base - (UINT8 *) MachoGetMachHeader (&Context->MachContext)); } if (Patch->Find == NULL) { if (Size < Patch->Size) { DEBUG (( DEBUG_INFO, - "OCAK: %a is borked, not found\n", + "OCAK: %a-bit %a is borked, not found\n", + Context->Is32Bit ? "32" : "64", Patch->Comment != NULL ? Patch->Comment : "Patch" )); return EFI_NOT_FOUND; @@ -243,7 +339,8 @@ PatcherApplyGenericPatch ( DEBUG (( DEBUG_INFO, - "OCAK: %a replace count - %u\n", + "OCAK: %a-bit %a replace count - %u\n", + Context->Is32Bit ? "32" : "64", Patch->Comment != NULL ? Patch->Comment : "Patch", ReplaceCount )); @@ -251,7 +348,8 @@ PatcherApplyGenericPatch ( if (ReplaceCount > 0 && Patch->Count > 0 && ReplaceCount != Patch->Count) { DEBUG (( DEBUG_INFO, - "OCAK: %a performed only %u replacements out of %u\n", + "OCAK: %a-bit %a performed only %u replacements out of %u\n", + Context->Is32Bit ? "32" : "64", Patch->Comment != NULL ? Patch->Comment : "Patch", ReplaceCount, Patch->Count @@ -270,11 +368,13 @@ PatcherBlockKext ( IN OUT PATCHER_CONTEXT *Context ) { - UINT64 KmodOffset; - UINT64 StartAddr; - UINT64 TmpOffset; - KMOD_INFO_64_V1 *KmodInfo; - UINT8 *PatchAddr; + UINT8 *MachBase; + UINT32 MachSize; + UINT64 KmodOffset; + UINT64 StartAddr; + UINT64 TmpOffset; + UINT8 *KmodInfo; + UINT8 *PatchAddr; // // Kernel has 0 kmod. @@ -283,26 +383,47 @@ PatcherBlockKext ( return EFI_UNSUPPORTED; } + MachBase = (UINT8 *) MachoGetMachHeader (&Context->MachContext); + MachSize = MachoGetFileSize (&Context->MachContext); + + // + // Determine offset of kmod within file. + // KmodOffset = Context->VirtualKmod - Context->VirtualBase; - KmodInfo = (KMOD_INFO_64_V1 *)((UINT8 *) MachoGetMachHeader64 (&Context->MachContext) + KmodOffset); - StartAddr = KmodInfo->StartAddr; - if (Context->IsKernelCollection) { - StartAddr = KcFixupValue (StartAddr, NULL); + if (OcOverflowAddU64 (KmodOffset, Context->FileOffset, &KmodOffset) + || OcOverflowAddU64 (KmodOffset, Context->Is32Bit ? sizeof (KMOD_INFO_32_V1) : sizeof (KMOD_INFO_64_V1), &TmpOffset) + || TmpOffset > MachSize) { + return EFI_INVALID_PARAMETER; } - if (OcOverflowAddU64 (KmodOffset, sizeof (KMOD_INFO_64_V1), &TmpOffset) - || TmpOffset > MachoGetFileSize (&Context->MachContext) - || StartAddr == 0 - || Context->VirtualBase > StartAddr) { + KmodInfo = MachBase + KmodOffset; + if (Context->Is32Bit) { + StartAddr = ((KMOD_INFO_32_V1 *) KmodInfo)->StartAddr; + } else { + StartAddr = ((KMOD_INFO_64_V1 *) KmodInfo)->StartAddr; + if (Context->IsKernelCollection) { + StartAddr = KcFixupValue (StartAddr, NULL); + } + } + + if (StartAddr == 0 || Context->VirtualBase > StartAddr) { return EFI_INVALID_PARAMETER; } TmpOffset = StartAddr - Context->VirtualBase; - if (TmpOffset > MachoGetFileSize (&Context->MachContext) - 6) { + if (OcOverflowAddU64 (TmpOffset, Context->FileOffset, &TmpOffset) + || TmpOffset > MachSize - 6) { return EFI_BUFFER_TOO_SMALL; } - PatchAddr = (UINT8 *)MachoGetMachHeader64 (&Context->MachContext) + TmpOffset; + DEBUG (( + DEBUG_VERBOSE, + "OCAK: %a-bit blocker start @ 0x%llX\n", + Context->Is32Bit ? "32" : "64", + TmpOffset + )); + + PatchAddr = MachBase + TmpOffset; // // mov eax, KMOD_RETURN_FAILURE @@ -317,3 +438,55 @@ PatcherBlockKext ( return EFI_SUCCESS; } + +BOOLEAN +KextFindKmodAddress ( + IN OC_MACHO_CONTEXT *ExecutableContext, + IN UINT64 LoadAddress, + IN UINT32 Size, + OUT UINT64 *Kmod + ) +{ + BOOLEAN Is32Bit; + MACH_NLIST_ANY *Symbol; + CONST CHAR8 *SymbolName; + UINT64 Address; + UINT64 FileOffset; + UINT32 Index; + + Is32Bit = ExecutableContext->Is32Bit; + Index = 0; + + while (TRUE) { + Symbol = MachoGetSymbolByIndex (ExecutableContext, Index); + if (Symbol == NULL) { + *Kmod = 0; + return TRUE; + } + + if (((Is32Bit ? Symbol->Symbol32.Type : Symbol->Symbol64.Type) & MACH_N_TYPE_STAB) == 0) { + SymbolName = MachoGetSymbolName (ExecutableContext, Symbol); + if (SymbolName && AsciiStrCmp (SymbolName, "_kmod_info") == 0) { + if (!MachoIsSymbolValueInRange (ExecutableContext, Symbol)) { + return FALSE; + } + break; + } + } + + Index++; + } + + if (!GetTextBaseOffset (ExecutableContext, &Address, &FileOffset)) { + return FALSE; + } + + if (OcOverflowTriAddU64 (Address, LoadAddress, Is32Bit ? Symbol->Symbol32.Value : Symbol->Symbol64.Value, &Address) + || Address > LoadAddress + Size - (Is32Bit ? sizeof (KMOD_INFO_32_V1) : sizeof (KMOD_INFO_64_V1)) + || (Is32Bit && Address > MAX_UINT32)) { + return FALSE; + } + + *Kmod = Address; + return TRUE; +} diff --git a/Library/OcAppleKernelLib/Link.c b/Library/OcAppleKernelLib/Link.c index 14597a20..46b61951 100644 --- a/Library/OcAppleKernelLib/Link.c +++ b/Library/OcAppleKernelLib/Link.c @@ -588,7 +588,7 @@ InternalCalculateTargetsIntel64 ( // the originals patched and then updates the referencing reloc. // - if ((Vtable != NULL) && MachoSymbolNameIsVtable64 (Name)) { + if ((Vtable != NULL) && MachoSymbolNameIsVtable (Name)) { *Vtable = InternalGetOcVtableByName (Context, Kext, Name); } @@ -746,7 +746,7 @@ InternalRelocateRelocationIntel64 ( return MAX_UINTN; } - InstructionPtr = MachoGetFilePointerByAddress64 ( + InstructionPtr = MachoGetFilePointerByAddress ( &Kext->Context.MachContext, (RelocationBase + Address), &MaxSize @@ -1312,7 +1312,7 @@ InternalPrelinkKext64 ( // // Retrieve the symbol tables required for most following operations. // - NumSymbols = MachoGetSymbolTable ( + NumSymbols = MachoGetSymbolTable64 ( MachoContext, &SymbolTable, &StringTable, @@ -1373,7 +1373,7 @@ InternalPrelinkKext64 ( // Solve indirect symbols. // WeakTestValue = 0; - NumIndirectSymbols = MachoGetIndirectSymbolTable ( + NumIndirectSymbols = MachoGetIndirectSymbolTable64 ( MachoContext, &IndirectSymtab ); @@ -1657,7 +1657,7 @@ InternalPrelinkKext64 ( // Reinitialize the Mach-O context to account for the changed __LINKEDIT // segment and file size. // - if (!MachoInitializeContext (MachoContext, MachHeader, (SegmentOffset + SegmentSize), MachoContext->ContainerOffset)) { + if (!MachoInitializeContext64 (MachoContext, MachHeader, (SegmentOffset + SegmentSize), MachoContext->ContainerOffset)) { // // This should never failed under normal and abnormal conditions. // diff --git a/Library/OcAppleKernelLib/MkextContext.c b/Library/OcAppleKernelLib/MkextContext.c index 5ddd733b..1fc6d29a 100644 --- a/Library/OcAppleKernelLib/MkextContext.c +++ b/Library/OcAppleKernelLib/MkextContext.c @@ -1089,8 +1089,9 @@ MkextReserveKextSize ( IN OUT UINT32 *ReservedInfoSize, IN OUT UINT32 *ReservedExeSize, IN UINT32 InfoPlistSize, - IN UINT8 *Executable, - IN UINT32 ExecutableSize OPTIONAL + IN UINT8 *Executable OPTIONAL, + IN UINT32 ExecutableSize OPTIONAL, + IN BOOLEAN Is32Bit ) { OC_MACHO_CONTEXT Context; @@ -1102,11 +1103,11 @@ MkextReserveKextSize ( if (Executable != NULL) { ASSERT (ExecutableSize > 0); - if (!MachoInitializeContext (&Context, Executable, ExecutableSize, 0)) { + if (!MachoInitializeContext (&Context, Executable, ExecutableSize, 0, Is32Bit)) { return EFI_INVALID_PARAMETER; } - ExecutableSize = MachoGetVmSize64 (&Context); + ExecutableSize = MachoGetVmSize (&Context); if (ExecutableSize == 0) { return EFI_INVALID_PARAMETER; } @@ -1370,7 +1371,7 @@ MkextContextApplyPatch ( Status = PatcherInitContextFromMkext (&Patcher, Context, Identifier); if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_INFO, "OCAK: Failed to find %a - %r\n", Identifier, Status)); + DEBUG ((DEBUG_INFO, "OCAK: Failed to mkext find %a - %r\n", Identifier, Status)); return Status; } @@ -1403,7 +1404,27 @@ MkextContextApplyQuirk ( // DEBUG ((DEBUG_INFO, "OCAK: Failed to mkext find %a - %r\n", KernelQuirk->Identifier, Status)); return KernelQuirk->PatchFunction (NULL, KernelVersion); +} + +EFI_STATUS +MkextContextBlock ( + IN OUT MKEXT_CONTEXT *Context, + IN CONST CHAR8 *Identifier + ) +{ + EFI_STATUS Status; + PATCHER_CONTEXT Patcher; + + ASSERT (Context != NULL); + ASSERT (Identifier != NULL); + + Status = PatcherInitContextFromMkext (&Patcher, Context, Identifier); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OCAK: Failed to mkext find %a - %r\n", Identifier, Status)); + return Status; + } + return PatcherBlockKext (&Patcher); } EFI_STATUS diff --git a/Library/OcAppleKernelLib/OcAppleKernelLib.inf b/Library/OcAppleKernelLib/OcAppleKernelLib.inf index fe315717..21c6a9a8 100644 --- a/Library/OcAppleKernelLib/OcAppleKernelLib.inf +++ b/Library/OcAppleKernelLib/OcAppleKernelLib.inf @@ -42,6 +42,7 @@ Vtables.c CachelessContext.c MkextContext.c + CpuidPatches.c [Packages] MdePkg/MdePkg.dec diff --git a/Library/OcAppleKernelLib/PrelinkedContext.c b/Library/OcAppleKernelLib/PrelinkedContext.c index 6d28ff85..f8fffc16 100644 --- a/Library/OcAppleKernelLib/PrelinkedContext.c +++ b/Library/OcAppleKernelLib/PrelinkedContext.c @@ -95,57 +95,6 @@ PrelinkedFindLastLoadAddress ( return LoadAddress; } -STATIC -BOOLEAN -PrelinkedFindKmodAddress ( - IN OC_MACHO_CONTEXT *ExecutableContext, - IN UINT64 LoadAddress, - IN UINT32 Size, - OUT UINT64 *Kmod - ) -{ - MACH_NLIST_64 *Symbol; - CONST CHAR8 *SymbolName; - MACH_SEGMENT_COMMAND_64 *TextSegment; - UINT64 Address; - UINT32 Index; - - Index = 0; - while (TRUE) { - Symbol = MachoGetSymbolByIndex64 (ExecutableContext, Index); - if (Symbol == NULL) { - *Kmod = 0; - return TRUE; - } - - if ((Symbol->Type & MACH_N_TYPE_STAB) == 0) { - SymbolName = MachoGetSymbolName64 (ExecutableContext, Symbol); - if (SymbolName && AsciiStrCmp (SymbolName, "_kmod_info") == 0) { - if (!MachoIsSymbolValueInRange64 (ExecutableContext, Symbol)) { - return FALSE; - } - break; - } - } - - Index++; - } - - TextSegment = MachoGetSegmentByName64 (ExecutableContext, "__TEXT"); - if (TextSegment == NULL || TextSegment->FileOffset > TextSegment->VirtualAddress) { - return FALSE; - } - - Address = TextSegment->VirtualAddress - TextSegment->FileOffset; - if (OcOverflowTriAddU64 (Address, LoadAddress, Symbol->Value, &Address) - || Address > LoadAddress + Size - sizeof (KMOD_INFO_64_V1)) { - return FALSE; - } - - *Kmod = Address; - return TRUE; -} - STATIC EFI_STATUS PrelinkedGetSegmentsFromMacho ( @@ -222,7 +171,7 @@ InternalConnectExternalSymtab ( return EFI_INVALID_PARAMETER; } - if (!MachoInitializeContext ( + if (!MachoInitializeContext64 ( InnerContext, &Buffer[Segment->FileOffset], (UINT32) (BufferSize - Segment->FileOffset), @@ -236,7 +185,7 @@ InternalConnectExternalSymtab ( return EFI_INVALID_PARAMETER; } - if (!MachoInitialiseSymtabsExternal64 (Context, InnerContext)) { + if (!MachoInitialiseSymtabsExternal (Context, InnerContext)) { DEBUG (( DEBUG_INFO, "OCAK: KC symtab failed getting symtab from inner %Lx %x\n", @@ -292,7 +241,7 @@ PrelinkedContextInit ( // // Initialise primary context. // - if (!MachoInitializeContext (&Context->PrelinkedMachContext, Prelinked, PrelinkedSize, 0)) { + if (!MachoInitializeContext64 (&Context->PrelinkedMachContext, Prelinked, PrelinkedSize, 0)) { return EFI_INVALID_PARAMETER; } @@ -857,8 +806,9 @@ PrelinkedReserveKextSize ( IN OUT UINT32 *ReservedInfoSize, IN OUT UINT32 *ReservedExeSize, IN UINT32 InfoPlistSize, - IN UINT8 *Executable, - IN UINT32 ExecutableSize OPTIONAL + IN UINT8 *Executable OPTIONAL, + IN UINT32 ExecutableSize OPTIONAL, + IN BOOLEAN Is32Bit ) { OC_MACHO_CONTEXT Context; @@ -874,11 +824,11 @@ PrelinkedReserveKextSize ( if (Executable != NULL) { ASSERT (ExecutableSize > 0); - if (!MachoInitializeContext (&Context, Executable, ExecutableSize, 0)) { + if (!MachoInitializeContext (&Context, Executable, ExecutableSize, 0, Is32Bit)) { return EFI_INVALID_PARAMETER; } - ExecutableSize = MachoGetVmSize64 (&Context); + ExecutableSize = MachoGetVmSize (&Context); if (ExecutableSize == 0) { return EFI_INVALID_PARAMETER; } @@ -956,7 +906,7 @@ PrelinkedInjectKext ( // if (Executable != NULL) { ASSERT (ExecutableSize > 0); - if (!MachoInitializeContext (&ExecutableContext, (UINT8 *)Executable, ExecutableSize, 0)) { + if (!MachoInitializeContext64 (&ExecutableContext, (UINT8 *)Executable, ExecutableSize, 0)) { DEBUG ((DEBUG_INFO, "OCAK: Injected kext %a/%a is not a supported executable\n", BundlePath, ExecutablePath)); return EFI_INVALID_PARAMETER; } @@ -965,7 +915,7 @@ PrelinkedInjectKext ( // KextOffset = Context->PrelinkedSize; - ExecutableSize = MachoExpandImage64 ( + ExecutableSize = MachoExpandImage ( &ExecutableContext, &Context->Prelinked[KextOffset], Context->PrelinkedAllocSize - KextOffset, @@ -985,11 +935,11 @@ PrelinkedInjectKext ( AlignedExecutableSize - ExecutableSize ); - if (!MachoInitializeContext (&ExecutableContext, &Context->Prelinked[KextOffset], ExecutableSize, 0)) { + if (!MachoInitializeContext64 (&ExecutableContext, &Context->Prelinked[KextOffset], ExecutableSize, 0)) { return EFI_INVALID_PARAMETER; } - Result = PrelinkedFindKmodAddress (&ExecutableContext, Context->PrelinkedLastLoadAddress, ExecutableSize, &KmodAddress); + Result = KextFindKmodAddress (&ExecutableContext, Context->PrelinkedLastLoadAddress, ExecutableSize, &KmodAddress); if (!Result) { return EFI_INVALID_PARAMETER; } @@ -1210,6 +1160,27 @@ PrelinkedContextApplyQuirk ( // // It is up to the function to decide whether this is critical or not. // - DEBUG ((DEBUG_INFO, "OCAK: Failed to find %a - %r\n", KernelQuirk->Identifier, Status)); + DEBUG ((DEBUG_INFO, "OCAK: Failed to pk find %a - %r\n", KernelQuirk->Identifier, Status)); return KernelQuirk->PatchFunction (NULL, KernelVersion); } + +EFI_STATUS +PrelinkedContextBlock ( + IN OUT PRELINKED_CONTEXT *Context, + IN CONST CHAR8 *Identifier + ) +{ + EFI_STATUS Status; + PATCHER_CONTEXT Patcher; + + ASSERT (Context != NULL); + ASSERT (Identifier != NULL); + + Status = PatcherInitContextFromPrelinked (&Patcher, Context, Identifier); + if (EFI_ERROR (Status)) { + DEBUG ((DEBUG_INFO, "OCAK: Failed to pk find %a - %r\n", Identifier, Status)); + return Status; + } + + return PatcherBlockKext (&Patcher); +} diff --git a/Library/OcAppleKernelLib/PrelinkedKext.c b/Library/OcAppleKernelLib/PrelinkedKext.c index 6618be1e..eba7ed71 100644 --- a/Library/OcAppleKernelLib/PrelinkedKext.c +++ b/Library/OcAppleKernelLib/PrelinkedKext.c @@ -216,7 +216,7 @@ InternalCreatePrelinkedKext ( if (Prelinked != NULL && HasExe - && !MachoInitializeContext (&NewKext->Context.MachContext, &Prelinked->Prelinked[SourceBase], (UINT32)SourceSize, ContainerOffset)) { + && !MachoInitializeContext64 (&NewKext->Context.MachContext, &Prelinked->Prelinked[SourceBase], (UINT32)SourceSize, ContainerOffset)) { FreePool (NewKext); return NULL; } @@ -289,7 +289,7 @@ InternalScanCurrentPrelinkedKextLinkInfo ( } if (Kext->SymbolTable == NULL && Kext->NumberOfSymbols == 0) { - Kext->NumberOfSymbols = MachoGetSymbolTable ( + Kext->NumberOfSymbols = MachoGetSymbolTable64 ( &Kext->Context.MachContext, &Kext->SymbolTable, &Kext->StringTable, @@ -477,7 +477,7 @@ InternalScanBuildLinkedVtables ( // need to abort anyway when the value is out of its bounds, we can // just locate it by address in the first place. // - Tmp = MachoGetFilePointerByAddress64 ( + Tmp = MachoGetFilePointerByAddress ( &Kext->Context.MachContext, VtableLookups[Index].Vtable.Value, &VtableMaxSize diff --git a/Library/OcAppleKernelLib/Vtables.c b/Library/OcAppleKernelLib/Vtables.c index e56ca1d0..b7cf3274 100644 --- a/Library/OcAppleKernelLib/Vtables.c +++ b/Library/OcAppleKernelLib/Vtables.c @@ -198,7 +198,7 @@ InternalPrepareCreateVtablesPrelinked64 ( ++Index ) { Symbol = &Kext->LinkedSymbolTable[Index]; - if (MachoSymbolNameIsVtable64 (Symbol->Name)) { + if (MachoSymbolNameIsVtable (Symbol->Name)) { // // This seems to be valid for KC format as some symbols may be kernel imports?! // Observed with IOACPIFamily when injecting VirtualSMC: @@ -285,7 +285,7 @@ InternalPatchVtableSymbol ( // // 1) If the symbol is defined locally, do not patch // - if (MachoSymbolIsLocalDefined (MachoContext, Symbol)) { + if (MachoSymbolIsLocalDefined64 (MachoContext, Symbol)) { return TRUE; } @@ -325,7 +325,7 @@ InternalPatchVtableSymbol ( // declared as part of the class and not inherited, which means we // should not patch it. // - if (!MachoSymbolIsDefined (Symbol)) { + if (!MachoSymbolIsDefined64 (Symbol)) { ClassName = MachoGetClassNameFromVtableName (VtableName); Success = MachoGetFunctionPrefixFromClassName ( @@ -627,7 +627,7 @@ InternalPatchByVtables64 ( ) { Name = MachoGetSymbolName64 (MachoContext, Smcp); if (((Smcp->Type & MACH_N_TYPE_STAB) == 0) - && MachoSymbolNameIsSmcp64 (MachoContext, Name)) { + && MachoSymbolNameIsSmcp (MachoContext, Name)) { if (MaxSize < sizeof (*EntryWalker)) { return FALSE; } @@ -722,7 +722,7 @@ InternalPatchByVtables64 ( // number of vtables we're expecting, because every pointer will have a // class vtable and a MetaClass vtable. // - ASSERT (MachoSymbolNameIsSmcp64 (MachoContext, Name)); + ASSERT (MachoSymbolNameIsSmcp (MachoContext, Name)); // // Get the class name from the smc pointer // @@ -815,7 +815,7 @@ InternalPatchByVtables64 ( return FALSE; } - SymbolDummy = MachoGetLocalDefinedSymbolByName ( + SymbolDummy = MachoGetLocalDefinedSymbolByName64 ( MachoContext, FinalSymbolName ); diff --git a/Library/OcMachoLib/CxxSymbols.c b/Library/OcMachoLib/CxxSymbols.c index 7281b584..06bdbb6d 100644 --- a/Library/OcMachoLib/CxxSymbols.c +++ b/Library/OcMachoLib/CxxSymbols.c @@ -38,14 +38,6 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #define VTABLE_HEADER_LEN_64 2U #define VTABLE_HEADER_SIZE_64 (VTABLE_HEADER_LEN_64 * VTABLE_ENTRY_SIZE_64) -#define SYM_MAX_NAME_LEN 256U - -/** - Returns whether Name is pure virtual. - - @param[in] Name The name to evaluate. - -**/ BOOLEAN MachoSymbolNameIsPureVirtual ( IN CONST CHAR8 *Name @@ -55,12 +47,6 @@ MachoSymbolNameIsPureVirtual ( return (AsciiStrCmp (Name, CXX_PURE_VIRTUAL) == 0); } -/** - Returns whether Name is a Padslot. - - @param[in] Name The name to evaluate. - -**/ BOOLEAN MachoSymbolNameIsPadslot ( IN CONST CHAR8 *Name @@ -70,15 +56,8 @@ MachoSymbolNameIsPadslot ( return (AsciiStrStr (Name, RESERVED_TOKEN) != NULL); } -/** - Returns whether SymbolName defines a Super Metaclass Pointer. - - @param[in,out] Context Context of the Mach-O. - @param[in] SymbolName The symbol name to check. - -**/ BOOLEAN -MachoSymbolNameIsSmcp64 ( +MachoSymbolNameIsSmcp ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SymbolName ) @@ -104,15 +83,8 @@ MachoSymbolNameIsSmcp64 ( return TRUE; } -/** - Returns whether SymbolName defines a Super Metaclass Pointer. - - @param[in,out] Context Context of the Mach-O. - @param[in] SymbolName The symbol name to check. - -**/ BOOLEAN -MachoSymbolNameIsMetaclassPointer64 ( +MachoSymbolNameIsMetaclassPointer ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST CHAR8 *SymbolName ) @@ -138,17 +110,6 @@ MachoSymbolNameIsMetaclassPointer64 ( return TRUE; } -/** - Retrieves the class name of a Super Meta Class Pointer. - - @param[in,out] Context Context of the Mach-O. - @param[in] SmcpName SMCP Symbol name to get the class name of. - @param[in] ClassNameSize The size of ClassName. - @param[out] ClassName The output buffer for the class name. - - @returns Whether the name has been retrieved successfully. - -**/ BOOLEAN MachoGetClassNameFromSuperMetaClassPointer ( IN OUT OC_MACHO_CONTEXT *Context, @@ -168,7 +129,7 @@ MachoGetClassNameFromSuperMetaClassPointer ( ASSERT (Context->StringTable != NULL); - ASSERT (MachoSymbolNameIsSmcp64 (Context, SmcpName)); + ASSERT (MachoSymbolNameIsSmcp (Context, SmcpName)); PrefixSize = L_STR_LEN (OSOBJ_PREFIX); SuffixSize = L_STR_LEN (SMCP_TOKEN); @@ -185,35 +146,19 @@ MachoGetClassNameFromSuperMetaClassPointer ( return TRUE; } -/** - Retrieves the class name of a VTable. - - @param[out] VtableName The VTable's name. - -**/ CONST CHAR8 * MachoGetClassNameFromVtableName ( IN CONST CHAR8 *VtableName ) { ASSERT (VtableName != NULL); - ASSERT (MachoSymbolNameIsVtable64 (VtableName)); + ASSERT (MachoSymbolNameIsVtable (VtableName)); // // As there is no suffix, just return a pointer from within VtableName. // return &VtableName[L_STR_LEN (VTABLE_PREFIX)]; } -/** - Retrieves the function prefix of a class name. - - @param[in] ClassName The class name to evaluate. - @param[in] FunctionPrefixSize The size of FunctionPrefix. - @param[out] FunctionPrefix The output buffer for the function prefix. - - @returns Whether the name has been retrieved successfully. - -**/ BOOLEAN MachoGetFunctionPrefixFromClassName ( IN CONST CHAR8 *ClassName, @@ -253,17 +198,6 @@ MachoGetFunctionPrefixFromClassName ( return TRUE; } -/** - Retrieves the class name of a Meta Class Pointer. - - @param[in,out] Context Context of the Mach-O. - @param[in] MetaClassName MCP Symbol name to get the class name of. - @param[in] ClassNameSize The size of ClassName. - @param[out] ClassName The output buffer for the class name. - - @returns Whether the name has been retrieved successfully. - -**/ BOOLEAN MachoGetClassNameFromMetaClassPointer ( IN OUT OC_MACHO_CONTEXT *Context, @@ -283,7 +217,7 @@ MachoGetClassNameFromMetaClassPointer ( ASSERT (Context->StringTable != NULL); - ASSERT (MachoSymbolNameIsMetaclassPointer64 (Context, MetaClassName)); + ASSERT (MachoSymbolNameIsMetaclassPointer (Context, MetaClassName)); PrefixSize = L_STR_LEN (OSOBJ_PREFIX); SuffixSize = L_STR_LEN (METACLASS_TOKEN); @@ -299,16 +233,6 @@ MachoGetClassNameFromMetaClassPointer ( return TRUE; } -/** - Retrieves the VTable name of a class name. - - @param[in] ClassName Class name to get the VTable name of. - @param[in] VtableNameSize The size of VtableName. - @param[out] VtableName The output buffer for the VTable name. - - @returns Whether the name has been retrieved successfully. - -**/ BOOLEAN MachoGetVtableNameFromClassName ( IN CONST CHAR8 *ClassName, @@ -349,16 +273,6 @@ MachoGetVtableNameFromClassName ( return TRUE; } -/** - Retrieves the Meta VTable name of a class name. - - @param[in] ClassName Class name to get the Meta VTable name of. - @param[in] VtableNameSize The size of VtableName. - @param[out] VtableName The output buffer for the VTable name. - - @returns Whether the name has been retrieved successfully. - -**/ BOOLEAN MachoGetMetaVtableNameFromClassName ( IN CONST CHAR8 *ClassName, @@ -407,16 +321,6 @@ MachoGetMetaVtableNameFromClassName ( return TRUE; } -/** - Retrieves the final symbol name of a class name. - - @param[in] ClassName Class name to get the final symbol name of. - @param[in] FinalSymbolNameSize The size of FinalSymbolName. - @param[out] FinalSymbolName The output buffer for the final symbol name. - - @returns Whether the name has been retrieved successfully. - -**/ BOOLEAN MachoGetFinalSymbolNameFromClassName ( IN CONST CHAR8 *ClassName, @@ -469,15 +373,8 @@ MachoGetFinalSymbolNameFromClassName ( return TRUE; } -/** - Returns whether SymbolName defines a VTable. - - @param[in,out] Context Context of the Mach-O. - @param[in] SymbolName The symbol name to check. - -**/ BOOLEAN -MachoSymbolNameIsVtable64 ( +MachoSymbolNameIsVtable ( IN CONST CHAR8 *SymbolName ) { @@ -488,12 +385,6 @@ MachoSymbolNameIsVtable64 ( return AsciiStrnCmp (SymbolName, VTABLE_PREFIX, L_STR_LEN (VTABLE_PREFIX)) == 0; } -/** - Returns whether the symbol name describes a C++ symbol. - - @param[in] Name The name to evaluate. - -**/ BOOLEAN MachoSymbolNameIsCxx ( IN CONST CHAR8 *Name @@ -502,119 +393,3 @@ MachoSymbolNameIsCxx ( ASSERT (Name != NULL); return AsciiStrnCmp (Name, CXX_PREFIX, L_STR_LEN (CXX_PREFIX)) == 0; } - -/** - Retrieves Metaclass symbol of a SMCP. - - @param[in,out] Context Context of the Mach-O. - @param[in] Smcp The SMCP to evaluate. - - @retval NULL NULL is returned on failure. - -**/ -MACH_NLIST_64 * -MachoGetMetaclassSymbolFromSmcpSymbol64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Smcp - ) -{ - MACH_NLIST_64 *Symbol; - BOOLEAN Result; - - ASSERT (Context != NULL); - ASSERT (Smcp != NULL); - - Result = MachoGetSymbolByRelocationOffset64 ( - Context, - Smcp->Value, - &Symbol - ); - if (Result && (Symbol != NULL)) { - Result = MachoSymbolNameIsMetaclassPointer64 ( - Context, - MachoGetSymbolName64 (Context, Symbol) - ); - if (Result) { - return Symbol; - } - } - - return NULL; -} - -/** - Retrieves VTable and Meta VTable of a SMCP. - Logically matches XNU's get_vtable_syms_from_smcp. - - @param[in,out] Context Context of the Mach-O. - @param[in] SmcpName SMCP Symbol mame to retrieve the VTables from. - @param[out] Vtable Output buffer for the VTable symbol pointer. - @param[out] MetaVtable Output buffer for the Meta VTable symbol pointer. - -**/ -BOOLEAN -MachoGetVtableSymbolsFromSmcp64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST CHAR8 *SmcpName, - OUT CONST MACH_NLIST_64 **Vtable, - OUT CONST MACH_NLIST_64 **MetaVtable - ) -{ - CHAR8 ClassName[SYM_MAX_NAME_LEN]; - CHAR8 VtableName[SYM_MAX_NAME_LEN]; - CHAR8 MetaVtableName[SYM_MAX_NAME_LEN]; - BOOLEAN Result; - MACH_NLIST_64 *VtableSymbol; - MACH_NLIST_64 *MetaVtableSymbol; - - ASSERT (Context != NULL); - ASSERT (SmcpName != NULL); - ASSERT (Vtable != NULL); - ASSERT (MetaVtable != NULL); - - Result = MachoGetClassNameFromSuperMetaClassPointer ( - Context, - SmcpName, - sizeof (ClassName), - ClassName - ); - if (!Result) { - return FALSE; - } - - Result = MachoGetVtableNameFromClassName ( - ClassName, - sizeof (VtableName), - VtableName - ); - if (!Result) { - return FALSE; - } - - VtableSymbol = MachoGetLocalDefinedSymbolByName (Context, VtableName); - if (VtableSymbol == NULL) { - return FALSE; - } - - Result = MachoGetMetaVtableNameFromClassName ( - ClassName, - sizeof (MetaVtableName), - MetaVtableName - ); - if (!Result) { - return FALSE; - } - - MetaVtableSymbol = MachoGetLocalDefinedSymbolByName ( - Context, - MetaVtableName - ); - if (MetaVtableSymbol == NULL) { - return FALSE; - } - - *Vtable = VtableSymbol; - *MetaVtable = MetaVtableSymbol; - - return TRUE; -} diff --git a/Library/OcMachoLib/CxxSymbolsX.h b/Library/OcMachoLib/CxxSymbolsX.h new file mode 100644 index 00000000..524ac9cd --- /dev/null +++ b/Library/OcMachoLib/CxxSymbolsX.h @@ -0,0 +1,114 @@ +/** @file + Provides services for C++ symbols. + +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 "MachoX.h" + +MACH_NLIST_X * +MACH_X (MachoGetMetaclassSymbolFromSmcpSymbol) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Smcp + ) +{ + MACH_NLIST_X *Symbol; + BOOLEAN Result; + + ASSERT (Context != NULL); + ASSERT (Smcp != NULL); + MACH_ASSERT_X (Context); + + Result = MACH_X (MachoGetSymbolByRelocationOffset) ( + Context, + Smcp->Value, + &Symbol + ); + if (Result && (Symbol != NULL)) { + Result = MachoSymbolNameIsMetaclassPointer ( + Context, + MACH_X (MachoGetSymbolName) (Context, Symbol) + ); + if (Result) { + return Symbol; + } + } + + return NULL; +} + +BOOLEAN +MACH_X (MachoGetVtableSymbolsFromSmcp) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SmcpName, + OUT CONST MACH_NLIST_X **Vtable, + OUT CONST MACH_NLIST_X **MetaVtable + ) +{ + CHAR8 ClassName[SYM_MAX_NAME_LEN]; + CHAR8 VtableName[SYM_MAX_NAME_LEN]; + CHAR8 MetaVtableName[SYM_MAX_NAME_LEN]; + BOOLEAN Result; + MACH_NLIST_X *VtableSymbol; + MACH_NLIST_X *MetaVtableSymbol; + + ASSERT (Context != NULL); + ASSERT (SmcpName != NULL); + ASSERT (Vtable != NULL); + ASSERT (MetaVtable != NULL); + MACH_ASSERT_X (Context); + + Result = MachoGetClassNameFromSuperMetaClassPointer ( + Context, + SmcpName, + sizeof (ClassName), + ClassName + ); + if (!Result) { + return FALSE; + } + + Result = MachoGetVtableNameFromClassName ( + ClassName, + sizeof (VtableName), + VtableName + ); + if (!Result) { + return FALSE; + } + + VtableSymbol = MACH_X (MachoGetLocalDefinedSymbolByName) (Context, VtableName); + if (VtableSymbol == NULL) { + return FALSE; + } + + Result = MachoGetMetaVtableNameFromClassName ( + ClassName, + sizeof (MetaVtableName), + MetaVtableName + ); + if (!Result) { + return FALSE; + } + + MetaVtableSymbol = MACH_X (MachoGetLocalDefinedSymbolByName) ( + Context, + MetaVtableName + ); + if (MetaVtableSymbol == NULL) { + return FALSE; + } + + *Vtable = VtableSymbol; + *MetaVtable = MetaVtableSymbol; + + return TRUE; +} diff --git a/Library/OcMachoLib/Header.c b/Library/OcMachoLib/Header.c index 8fef7544..9a8b7479 100644 --- a/Library/OcMachoLib/Header.c +++ b/Library/OcMachoLib/Header.c @@ -25,15 +25,23 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include "OcMachoLibInternal.h" -/** - Returns the Mach-O Header structure. - - @param[in,out] Context Context of the Mach-O. +BOOLEAN +MachoInitializeContext ( + OUT OC_MACHO_CONTEXT *Context, + IN VOID *FileData, + IN UINT32 FileSize, + IN UINT32 ContainerOffset, + IN BOOLEAN Is32Bit + ) +{ + return Is32Bit ? + MachoInitializeContext32 (Context, FileData, FileSize, ContainerOffset) : + MachoInitializeContext64 (Context, FileData, FileSize, ContainerOffset); +} -**/ -MACH_HEADER_64 * -MachoGetMachHeader64 ( - IN OUT OC_MACHO_CONTEXT *Context +MACH_HEADER_ANY * +MachoGetMachHeader ( + IN OUT OC_MACHO_CONTEXT *Context ) { ASSERT (Context != NULL); @@ -42,12 +50,6 @@ MachoGetMachHeader64 ( return Context->MachHeader; } -/** - Returns the Mach-O's file size. - - @param[in,out] Context Context of the Mach-O. - -**/ UINT32 MachoGetFileSize ( IN OUT OC_MACHO_CONTEXT *Context @@ -59,683 +61,91 @@ MachoGetFileSize ( return Context->FileSize; } -/** - Returns the Mach-O's virtual address space size. - - @param[out] Context Context of the Mach-O. - -**/ UINT32 -MachoGetVmSize64 ( - IN OUT OC_MACHO_CONTEXT *Context - ) -{ - UINT64 VmSize; - MACH_SEGMENT_COMMAND_64 *Segment; - - ASSERT (Context != NULL); - ASSERT (Context->FileSize != 0); - - VmSize = 0; - - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - if (OcOverflowAddU64 (VmSize, Segment->Size, &VmSize)) { - return 0; - } - VmSize = MACHO_ALIGN (VmSize); - } - - if (VmSize > MAX_UINT32) { - return 0; - } - - return (UINT32) VmSize; -} - -/** - Initializes a Mach-O Context. - - @param[out] Context Mach-O Context to initialize. - @param[in] FileData Pointer to the file's data. - @param[in] FileSize File size of FileData. - - @return Whether Context has been initialized successfully. -**/ -BOOLEAN -MachoInitializeContext ( - OUT OC_MACHO_CONTEXT *Context, - IN VOID *FileData, - IN UINT32 FileSize, - IN UINT32 ContainerOffset - ) -{ - EFI_STATUS Status; - MACH_HEADER_64 *MachHeader; - UINTN TopOfFile; - UINTN TopOfCommands; - UINT32 Index; - CONST MACH_LOAD_COMMAND *Command; - UINTN TopOfCommand; - UINT32 CommandsSize; - BOOLEAN Result; - - ASSERT (FileData != NULL); - ASSERT (FileSize > 0); - ASSERT (Context != NULL); - - TopOfFile = ((UINTN)FileData + FileSize); - ASSERT (TopOfFile > (UINTN)FileData); - - Status = FatFilterArchitecture64 ((UINT8 **) &FileData, &FileSize); - if (EFI_ERROR (Status)) { - return FALSE; - } - - if (FileSize < sizeof (*MachHeader) - || !OC_TYPE_ALIGNED (MACH_HEADER_64, FileData)) { - return FALSE; - } - MachHeader = (MACH_HEADER_64 *)FileData; - if (MachHeader->Signature != MACH_HEADER_64_SIGNATURE) { - return FALSE; - } - - Result = OcOverflowAddUN ( - (UINTN)MachHeader->Commands, - MachHeader->CommandsSize, - &TopOfCommands - ); - if (Result || (TopOfCommands > TopOfFile)) { - return FALSE; - } - - CommandsSize = 0; - - for ( - Index = 0, Command = MachHeader->Commands; - Index < MachHeader->NumCommands; - ++Index, Command = NEXT_MACH_LOAD_COMMAND (Command) - ) { - Result = OcOverflowAddUN ( - (UINTN)Command, - sizeof (*Command), - &TopOfCommand - ); - if (Result - || (TopOfCommand > TopOfCommands) - || (Command->CommandSize < sizeof (*Command)) - || ((Command->CommandSize % sizeof (UINT64)) != 0) // Assumption: 64-bit, see below. - ) { - return FALSE; - } - - Result = OcOverflowAddU32 ( - CommandsSize, - Command->CommandSize, - &CommandsSize - ); - if (Result) { - return FALSE; - } - } - - if (MachHeader->CommandsSize != CommandsSize) { - return FALSE; - } - // - // Verify assumptions made by this library. - // Carefully audit all "Assumption:" remarks before modifying these checks. - // - if ((MachHeader->CpuType != MachCpuTypeX8664) - || ((MachHeader->FileType != MachHeaderFileTypeKextBundle) - && (MachHeader->FileType != MachHeaderFileTypeExecute) - && (MachHeader->FileType != MachHeaderFileTypeFileSet))) { - return FALSE; - } - - ZeroMem (Context, sizeof (*Context)); - - Context->MachHeader = MachHeader; - Context->FileSize = FileSize; - Context->ContainerOffset = ContainerOffset; - - return TRUE; -} - -/** - Returns the last virtual address of a Mach-O. - - @param[in,out] Context Context of the Mach-O. - - @retval 0 The binary is malformed. - -**/ -UINT64 -MachoGetLastAddress64 ( +MachoGetVmSize ( IN OUT OC_MACHO_CONTEXT *Context ) { - UINT64 LastAddress; - - CONST MACH_SEGMENT_COMMAND_64 *Segment; - UINT64 Address; - ASSERT (Context != NULL); - LastAddress = 0; - - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - Address = (Segment->VirtualAddress + Segment->Size); - - if (Address > LastAddress) { - LastAddress = Address; - } - } - - return LastAddress; + return Context->Is32Bit ? + InternalMachoGetVmSize32 (Context) : InternalMachoGetVmSize64 (Context); } MACH_LOAD_COMMAND * -MachoGetNextCommand64 ( +MachoGetNextCommand ( IN OUT OC_MACHO_CONTEXT *Context, IN MACH_LOAD_COMMAND_TYPE LoadCommandType, IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL ) { - MACH_LOAD_COMMAND *Command; - MACH_HEADER_64 *MachHeader; - UINTN TopOfCommands; - ASSERT (Context != NULL); - MachHeader = Context->MachHeader; - ASSERT (MachHeader != NULL); - - TopOfCommands = ((UINTN)MachHeader->Commands + MachHeader->CommandsSize); - - if (LoadCommand != NULL) { - ASSERT ( - (LoadCommand >= &MachHeader->Commands[0]) - && ((UINTN)LoadCommand <= TopOfCommands) - ); - Command = NEXT_MACH_LOAD_COMMAND (LoadCommand); - } else { - Command = &MachHeader->Commands[0]; - } - - for ( - ; - (UINTN)Command < TopOfCommands; - Command = NEXT_MACH_LOAD_COMMAND (Command) - ) { - if (Command->CommandType == LoadCommandType) { - return Command; - } - } - - return NULL; + return Context->Is32Bit ? + InternalMachoGetNextCommand32 (Context, LoadCommandType, LoadCommand) : + InternalMachoGetNextCommand64 (Context, LoadCommandType, LoadCommand); } -/** - Retrieves the first UUID Load Command. - - @param[in,out] Context Context of the Mach-O. - - @retval NULL NULL is returned on failure. - -**/ MACH_UUID_COMMAND * -MachoGetUuid64 ( +MachoGetUuid ( IN OUT OC_MACHO_CONTEXT *Context ) { MACH_UUID_COMMAND *UuidCommand; ASSERT (Context != NULL); - // - // Context initialisation guarantees the command size is a multiple of 8. - // - STATIC_ASSERT ( - OC_ALIGNOF (MACH_UUID_COMMAND) <= sizeof (UINT64), - "Alignment is not guaranteed." - ); - - UuidCommand = (MACH_UUID_COMMAND *) (VOID *) MachoGetNextCommand64 ( - Context, - MACH_LOAD_COMMAND_UUID, - NULL - ); - if (UuidCommand == NULL || UuidCommand->CommandSize != sizeof (*UuidCommand)) { - return NULL; - } - return UuidCommand; -} - -/** - Retrieves the first segment by the name of SegmentName. - - @param[in,out] Context Context of the Mach-O. - @param[in] SegmentName Segment name to search for. - - @retval NULL NULL is returned on failure. - -**/ -MACH_SEGMENT_COMMAND_64 * -MachoGetSegmentByName64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST CHAR8 *SegmentName - ) -{ - MACH_SEGMENT_COMMAND_64 *Segment; - INTN Result; - - ASSERT (Context != NULL); - ASSERT (SegmentName != NULL); - - Result = 0; - - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - Result = AsciiStrnCmp ( - Segment->SegmentName, - SegmentName, - ARRAY_SIZE (Segment->SegmentName) - ); - if (Result == 0) { - return Segment; - } - } - - return NULL; -} - -/** - Returns whether Section is sane. - - @param[in,out] Context Context of the Mach-O. - @param[in] Section Section to verify. - @param[in] Segment Segment the section is part of. - -**/ -STATIC -BOOLEAN -InternalSectionIsSane ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_SECTION_64 *Section, - IN CONST MACH_SEGMENT_COMMAND_64 *Segment - ) -{ - UINT64 TopOffset64; - UINT32 TopOffset32; - UINT64 TopOfSegment; - BOOLEAN Result; - UINT64 TopOfSection; - ASSERT (Context != NULL); - ASSERT (Section != NULL); - ASSERT (Segment != NULL); // - // Section->Alignment is stored as a power of 2. + // Context initialisation guarantees the command size is a multiple of 4 or 8. // - if ((Section->Alignment > 31) - || ((Section->Offset != 0) && (Section->Offset < Segment->FileOffset))) { - return FALSE; - } - - TopOfSegment = (Segment->VirtualAddress + Segment->Size); - Result = OcOverflowAddU64 ( - Section->Address, - Section->Size, - &TopOfSection - ); - if (Result || (TopOfSection > TopOfSegment)) { - return FALSE; - } - - Result = OcOverflowAddU64 ( - Section->Offset, - Section->Size, - &TopOffset64 - ); - if (Result || (TopOffset64 > (Segment->FileOffset + Segment->FileSize))) { - return FALSE; - } - - if (Section->NumRelocations != 0) { - Result = OcOverflowSubU32 ( - Section->RelocationsOffset, - Context->ContainerOffset, - &TopOffset32 - ); - Result |= OcOverflowMulAddU32 ( - Section->NumRelocations, - sizeof (MACH_RELOCATION_INFO), - TopOffset32, - &TopOffset32 - ); - if (Result || (TopOffset32 > Context->FileSize)) { - return FALSE; - } - } - - return TRUE; -} - -/** - Retrieves the first section by the name of SectionName. - - @param[in,out] Context Context of the Mach-O. - @param[in] Segment Segment to search in. - @param[in] SectionName Section name to search for. - - @retval NULL NULL is returned on failure. - -**/ -MACH_SECTION_64 * -MachoGetSectionByName64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN MACH_SEGMENT_COMMAND_64 *Segment, - IN CONST CHAR8 *SectionName - ) -{ - MACH_SECTION_64 *Section; - INTN Result; - - ASSERT (Context != NULL); - ASSERT (Segment != NULL); - ASSERT (SectionName != NULL); - - for ( - Section = MachoGetNextSection64 (Context, Segment, NULL); - Section != NULL; - Section = MachoGetNextSection64 (Context, Segment, Section) - ) { - // - // Assumption: Mach-O is not of type MH_OBJECT. - // MH_OBJECT might have sections in segments they do not belong in for - // performance reasons. This library does not support intermediate - // objects. - // - Result = AsciiStrnCmp ( - Section->SectionName, - SectionName, - ARRAY_SIZE (Section->SectionName) - ); - if (Result == 0) { - return Section; - } - } - - return NULL; -} - -/** - Retrieves a section within a segment by the name of SegmentName. - - @param[in,out] Context Context of the Mach-O. - @param[in] SegmentName The name of the segment to search in. - @param[in] SectionName The name of the section to search for. - - @retval NULL NULL is returned on failure. - -**/ -MACH_SECTION_64 * -MachoGetSegmentSectionByName64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST CHAR8 *SegmentName, - IN CONST CHAR8 *SectionName - ) -{ - MACH_SEGMENT_COMMAND_64 *Segment; - - ASSERT (Context != NULL); - ASSERT (SegmentName != NULL); - ASSERT (SectionName != NULL); - - Segment = MachoGetSegmentByName64 (Context, SegmentName); - - if (Segment != NULL) { - return MachoGetSectionByName64 (Context, Segment, SectionName); - } - - return NULL; -} - -/** - Retrieves the next segment. - - @param[in,out] Context Context of the Mach-O. - @param[in] Segment Segment to retrieve the successor of. - if NULL, the first segment is returned. - - @retal NULL NULL is returned on failure. - -**/ -MACH_SEGMENT_COMMAND_64 * -MachoGetNextSegment64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_SEGMENT_COMMAND_64 *Segment OPTIONAL - ) -{ - MACH_SEGMENT_COMMAND_64 *NextSegment; - - CONST MACH_HEADER_64 *MachHeader; - UINTN TopOfCommands; - BOOLEAN Result; - UINT64 TopOfSegment; - UINTN TopOfSections; - - ASSERT (Context != NULL); - - ASSERT (Context->MachHeader != NULL); - ASSERT (Context->FileSize > 0); - - if (Segment != NULL) { - MachHeader = Context->MachHeader; - TopOfCommands = ((UINTN) MachHeader->Commands + MachHeader->CommandsSize); - ASSERT ( - ((UINTN) Segment >= (UINTN) &MachHeader->Commands[0]) - && ((UINTN) Segment < TopOfCommands) + if (Context->Is32Bit) { + STATIC_ASSERT ( + OC_ALIGNOF (MACH_UUID_COMMAND) <= sizeof (UINT32), + "Alignment is not guaranteed." ); - } - // - // Context initialisation guarantees the command size is a multiple of 8. - // - STATIC_ASSERT ( - OC_ALIGNOF (MACH_SEGMENT_COMMAND_64) <= sizeof (UINT64), - "Alignment is not guaranteed." - ); - NextSegment = (MACH_SEGMENT_COMMAND_64 *) (VOID *) MachoGetNextCommand64 ( - Context, - MACH_LOAD_COMMAND_SEGMENT_64, - (CONST MACH_LOAD_COMMAND *) Segment - ); - if (NextSegment == NULL || NextSegment->CommandSize < sizeof (*NextSegment)) { - return NULL; - } - - Result = OcOverflowMulAddUN ( - NextSegment->NumSections, - sizeof (*NextSegment->Sections), - (UINTN) NextSegment->Sections, - &TopOfSections - ); - if (Result || (((UINTN) NextSegment + NextSegment->CommandSize) < TopOfSections)) { - return NULL; - } - - Result = OcOverflowSubU64 ( - NextSegment->FileOffset, - Context->ContainerOffset, - &TopOfSegment - ); - Result |= OcOverflowAddU64 ( - TopOfSegment, - NextSegment->FileSize, - &TopOfSegment - ); - if (Result || (TopOfSegment > Context->FileSize)) { - return NULL; - } - - if (NextSegment->VirtualAddress + NextSegment->Size < NextSegment->VirtualAddress) { - return NULL; - } - - return NextSegment; -} - -/** - Retrieves the next section of a segment. - - - @param[in,out] Context Context of the Mach-O. - @param[in] Segment The segment to get the section of. - @param[in] Section The section to get the successor of. - If NULL, the first section is returned. - @retval NULL NULL is returned on failure. - -**/ -MACH_SECTION_64 * -MachoGetNextSection64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN MACH_SEGMENT_COMMAND_64 *Segment, - IN MACH_SECTION_64 *Section OPTIONAL - ) -{ - ASSERT (Context != NULL); - ASSERT (Segment != NULL); - - if (Section != NULL) { - ASSERT (Section >= Segment->Sections); - - ++Section; - - if (Section >= &Segment->Sections[Segment->NumSections]) { - return NULL; - } - } else if (Segment->NumSections > 0) { - Section = &Segment->Sections[0]; } else { - return NULL; + STATIC_ASSERT ( + OC_ALIGNOF (MACH_UUID_COMMAND) <= sizeof (UINT64), + "Alignment is not guaranteed." + ); } - if (!InternalSectionIsSane (Context, Section, Segment)) { + UuidCommand = (MACH_UUID_COMMAND *) (VOID *) MachoGetNextCommand ( + Context, + MACH_LOAD_COMMAND_UUID, + NULL + ); + if (UuidCommand == NULL || UuidCommand->CommandSize != sizeof (*UuidCommand)) { return NULL; } - - return Section; + return UuidCommand; } -/** - Retrieves a section by its index. - - @param[in,out] Context Context of the Mach-O. - @param[in] Index Index of the section to retrieve. - - @retval NULL NULL is returned on failure. - -**/ -MACH_SECTION_64 * -MachoGetSectionByIndex64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT32 Index +MACH_SEGMENT_COMMAND_ANY * +MachoGetNextSegment ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SEGMENT_COMMAND_ANY *Segment OPTIONAL ) { - MACH_SECTION_64 *Section; - - MACH_SEGMENT_COMMAND_64 *Segment; - UINT32 SectionIndex; - UINT32 NextSectionIndex; - BOOLEAN Result; - ASSERT (Context != NULL); - SectionIndex = 0; - - Segment = NULL; - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - Result = OcOverflowAddU32 ( - SectionIndex, - Segment->NumSections, - &NextSectionIndex - ); - // - // If NextSectionIndex is wrapping around, Index must be contained. - // - if (Result || (Index < NextSectionIndex)) { - Section = &Segment->Sections[Index - SectionIndex]; - if (!InternalSectionIsSane (Context, Section, Segment)) { - return NULL; - } - - return Section; - } - - SectionIndex = NextSectionIndex; - } - - return NULL; + return Context->Is32Bit ? + (MACH_SEGMENT_COMMAND_ANY *) MachoGetNextSegment32 (Context, Segment != NULL ? &Segment->Segment32 : NULL) : + (MACH_SEGMENT_COMMAND_ANY *) MachoGetNextSegment64 (Context, Segment != NULL ? &Segment->Segment64 : NULL); } -/** - Retrieves a section by its address. - - @param[in,out] Context Context of the Mach-O. - @param[in] Address Address of the section to retrieve. - - @retval NULL NULL is returned on failure. - -**/ -MACH_SECTION_64 * -MachoGetSectionByAddress64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address +MACH_SECTION_ANY * +MachoGetSectionByName ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_ANY *Segment, + IN CONST CHAR8 *SectionName ) { - MACH_SEGMENT_COMMAND_64 *Segment; - MACH_SECTION_64 *Section; - UINT64 TopOfSegment; - UINT64 TopOfSection; - ASSERT (Context != NULL); - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - TopOfSegment = (Segment->VirtualAddress + Segment->Size); - if ((Address >= Segment->VirtualAddress) && (Address < TopOfSegment)) { - for ( - Section = MachoGetNextSection64 (Context, Segment, NULL); - Section != NULL; - Section = MachoGetNextSection64 (Context, Segment, Section) - ) { - TopOfSection = (Section->Address + Section->Size); - if ((Address >= Section->Address) && (Address < TopOfSection)) { - return Section; - } - } - } - } - - return NULL; + return Context->Is32Bit ? + (MACH_SECTION_ANY *) MachoGetSectionByName32 (Context, &Segment->Segment32, SectionName) : + (MACH_SECTION_ANY *) MachoGetSectionByName64 (Context, &Segment->Segment64, SectionName); } /** @@ -750,7 +160,7 @@ MachoGetSectionByAddress64 ( **/ STATIC BOOLEAN -InternalInitialiseSymtabs64 ( +InternalInitialiseSymtabs ( IN OUT OC_MACHO_CONTEXT *Context, IN MACH_SYMTAB_COMMAND *Symtab, IN MACH_DYSYMTAB_COMMAND *DySymtab @@ -767,8 +177,8 @@ InternalInitialiseSymtabs64 ( UINT32 IndirectSymbolsOffset; UINT32 LocalRelocationsOffset; UINT32 ExternalRelocationsOffset; - MACH_NLIST_64 *SymbolTable; - MACH_NLIST_64 *IndirectSymtab; + MACH_NLIST_ANY *SymbolTable; + MACH_NLIST_ANY *IndirectSymtab; MACH_RELOCATION_INFO *LocalRelocations; MACH_RELOCATION_INFO *ExternRelocations; @@ -779,7 +189,7 @@ InternalInitialiseSymtabs64 ( ASSERT (Context->FileSize > 0); ASSERT (Context->SymbolTable == NULL); - FileSize = Context->FileSize; + FileSize = Context->FileSize; Result = OcOverflowSubU32 ( Symtab->SymbolsOffset, @@ -788,7 +198,7 @@ InternalInitialiseSymtabs64 ( ); Result |= OcOverflowMulAddU32 ( Symtab->NumSymbols, - sizeof (MACH_NLIST_64), + Context->Is32Bit ? sizeof (MACH_NLIST) : sizeof (MACH_NLIST_64), SymbolsOffset, &OffsetTop ); @@ -817,12 +227,14 @@ InternalInitialiseSymtabs64 ( return FALSE; } + SymbolTable = NULL; + Tmp = (VOID *)(MachoAddress + SymbolsOffset); - if (!OC_TYPE_ALIGNED (MACH_NLIST_64, Tmp)) { + if (!(Context->Is32Bit ? OC_TYPE_ALIGNED (MACH_NLIST, Tmp) : OC_TYPE_ALIGNED (MACH_NLIST_64, Tmp))) { return FALSE; } - SymbolTable = (MACH_NLIST_64 *)Tmp; + SymbolTable = (MACH_NLIST_ANY *)Tmp; IndirectSymtab = NULL; LocalRelocations = NULL; ExternRelocations = NULL; @@ -867,7 +279,7 @@ InternalInitialiseSymtabs64 ( ); Result |= OcOverflowMulAddU32 ( DySymtab->NumIndirectSymbols, - sizeof (MACH_NLIST_64), + Context->Is32Bit ? sizeof (MACH_NLIST) : sizeof (MACH_NLIST_64), IndirectSymbolsOffset, &OffsetTop ); @@ -876,10 +288,10 @@ InternalInitialiseSymtabs64 ( } Tmp = (VOID *)(MachoAddress + IndirectSymbolsOffset); - if (!OC_TYPE_ALIGNED (MACH_NLIST_64, Tmp)) { + if (!(Context->Is32Bit ? OC_TYPE_ALIGNED (MACH_NLIST, Tmp) : OC_TYPE_ALIGNED (MACH_NLIST_64, Tmp))) { return FALSE; } - IndirectSymtab = (MACH_NLIST_64 *)Tmp; + IndirectSymtab = (MACH_NLIST_ANY *)Tmp; } if (DySymtab->NumOfLocalRelocations > 0 && DySymtab->LocalRelocationsOffset != 0) { @@ -933,19 +345,19 @@ InternalInitialiseSymtabs64 ( // Store the symbol information. // Context->Symtab = Symtab; - Context->SymbolTable = SymbolTable; Context->StringTable = StringTable; Context->DySymtab = DySymtab; - Context->IndirectSymbolTable = IndirectSymtab; - Context->LocalRelocations = LocalRelocations; - Context->ExternRelocations = ExternRelocations; + Context->LocalRelocations = LocalRelocations; + Context->ExternRelocations = ExternRelocations; + Context->SymbolTable = SymbolTable; + Context->IndirectSymbolTable = IndirectSymtab; return TRUE; } BOOLEAN -MachoInitialiseSymtabsExternal64 ( +MachoInitialiseSymtabsExternal ( IN OUT OC_MACHO_CONTEXT *Context, IN OC_MACHO_CONTEXT *SymsContext ) @@ -953,17 +365,31 @@ MachoInitialiseSymtabsExternal64 ( MACH_SYMTAB_COMMAND *Symtab; MACH_DYSYMTAB_COMMAND *DySymtab; BOOLEAN IsDyld; + MACH_HEADER_FLAGS MachFlags; + MACH_HEADER_FLAGS SymsMachFlags; + + ASSERT (Context != NULL); + ASSERT (Context->MachHeader != NULL); + ASSERT (SymsContext != NULL); if (Context->SymbolTable != NULL) { return TRUE; } + + MachFlags = Context->Is32Bit ? + Context->MachHeader->Header32.Flags : + Context->MachHeader->Header64.Flags; + SymsMachFlags = SymsContext->Is32Bit ? + SymsContext->MachHeader->Header32.Flags : + SymsContext->MachHeader->Header64.Flags; + // // We cannot use SymsContext's symbol tables if Context is flagged for DYLD // and SymsContext is not. // - IsDyld = (Context->MachHeader->Flags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) != 0; + IsDyld = (MachFlags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) != 0; if (IsDyld - && (SymsContext->MachHeader->Flags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) == 0) { + && (SymsMachFlags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) == 0) { return FALSE; } @@ -974,10 +400,11 @@ MachoInitialiseSymtabsExternal64 ( OC_ALIGNOF (MACH_SYMTAB_COMMAND) <= sizeof (UINT64), "Alignment is not guaranteed." ); + // // Retrieve SYMTAB. // - Symtab = (MACH_SYMTAB_COMMAND *) (VOID *) MachoGetNextCommand64 ( + Symtab = (MACH_SYMTAB_COMMAND *) (VOID *) MachoGetNextCommand ( SymsContext, MACH_LOAD_COMMAND_SYMTAB, NULL @@ -996,10 +423,11 @@ MachoInitialiseSymtabsExternal64 ( OC_ALIGNOF (MACH_DYSYMTAB_COMMAND) <= sizeof (UINT64), "Alignment is not guaranteed." ); + // // Retrieve DYSYMTAB. // - DySymtab = (MACH_DYSYMTAB_COMMAND *) (VOID *) MachoGetNextCommand64 ( + DySymtab = (MACH_DYSYMTAB_COMMAND *) (VOID *) MachoGetNextCommand ( SymsContext, MACH_LOAD_COMMAND_DYSYMTAB, NULL @@ -1009,447 +437,18 @@ MachoInitialiseSymtabsExternal64 ( } } - return InternalInitialiseSymtabs64 (Context, Symtab, DySymtab); + return InternalInitialiseSymtabs (Context, Symtab, DySymtab); } BOOLEAN -InternalRetrieveSymtabs64 ( +InternalRetrieveSymtabs ( IN OUT OC_MACHO_CONTEXT *Context ) { // // Retrieve the symbol information for Context from itself. // - return MachoInitialiseSymtabsExternal64 (Context, Context); -} - -UINT32 -MachoGetSymbolTable ( - IN OUT OC_MACHO_CONTEXT *Context, - OUT CONST MACH_NLIST_64 **SymbolTable, - OUT CONST CHAR8 **StringTable OPTIONAL, - OUT CONST MACH_NLIST_64 **LocalSymbols, OPTIONAL - OUT UINT32 *NumLocalSymbols, OPTIONAL - OUT CONST MACH_NLIST_64 **ExternalSymbols, OPTIONAL - OUT UINT32 *NumExternalSymbols, OPTIONAL - OUT CONST MACH_NLIST_64 **UndefinedSymbols, OPTIONAL - OUT UINT32 *NumUndefinedSymbols OPTIONAL - ) -{ - UINT32 Index; - CONST MACH_NLIST_64 *SymTab; - UINT32 NoLocalSymbols; - UINT32 NoExternalSymbols; - UINT32 NoUndefinedSymbols; - - ASSERT (Context != NULL); - - if (!InternalRetrieveSymtabs64 (Context)) { - return 0; - } - - if (Context->Symtab->NumSymbols == 0) { - return 0; - } - - SymTab = Context->SymbolTable; - - for (Index = 0; Index < Context->Symtab->NumSymbols; ++Index) { - if (!InternalSymbolIsSane (Context, &SymTab[Index])) { - return 0; - } - } - - *SymbolTable = Context->SymbolTable; - - if (StringTable != NULL) { - *StringTable = Context->StringTable; - } - - NoLocalSymbols = 0; - NoExternalSymbols = 0; - NoUndefinedSymbols = 0; - - if (Context->DySymtab != NULL) { - NoLocalSymbols = Context->DySymtab->NumLocalSymbols; - NoExternalSymbols = Context->DySymtab->NumExternalSymbols; - NoUndefinedSymbols = Context->DySymtab->NumUndefinedSymbols; - } - - if (NumLocalSymbols != NULL) { - ASSERT (LocalSymbols != NULL); - *NumLocalSymbols = NoLocalSymbols; - if (NoLocalSymbols != 0) { - *LocalSymbols = &SymTab[Context->DySymtab->LocalSymbolsIndex]; - } - } - - if (NumExternalSymbols != NULL) { - ASSERT (ExternalSymbols != NULL); - *NumExternalSymbols = NoExternalSymbols; - if (NoExternalSymbols != 0) { - *ExternalSymbols = &SymTab[Context->DySymtab->ExternalSymbolsIndex]; - } - } - - if (NumUndefinedSymbols != NULL) { - ASSERT (UndefinedSymbols != NULL); - *NumUndefinedSymbols = NoUndefinedSymbols; - if (NoUndefinedSymbols != 0) { - *UndefinedSymbols = &SymTab[Context->DySymtab->UndefinedSymbolsIndex]; - } - } - - return Context->Symtab->NumSymbols; -} - -UINT32 -MachoGetIndirectSymbolTable ( - IN OUT OC_MACHO_CONTEXT *Context, - OUT CONST MACH_NLIST_64 **SymbolTable - ) -{ - UINT32 Index; - - if (!InternalRetrieveSymtabs64 (Context)) { - return 0; - } - - for (Index = 0; Index < Context->DySymtab->NumIndirectSymbols; ++Index) { - if ( - !InternalSymbolIsSane (Context, &Context->IndirectSymbolTable[Index]) - ) { - return 0; - } - } - - *SymbolTable = Context->IndirectSymbolTable; - - return Context->DySymtab->NumIndirectSymbols; -} - -/** - Returns a pointer to the Mach-O file at the specified virtual address. - - @param[in,out] Context Context of the Mach-O. - @param[in] Address Virtual address to look up. - @param[out] MaxSize Maximum data safely available from FileOffset. - If NULL is returned, the output is undefined. - -**/ -VOID * -MachoGetFilePointerByAddress64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT UINT32 *MaxSize OPTIONAL - ) -{ - CONST MACH_SEGMENT_COMMAND_64 *Segment; - UINT64 Offset; - - Segment = NULL; - while ((Segment = MachoGetNextSegment64 (Context, Segment)) != NULL) { - if ((Address >= Segment->VirtualAddress) - && (Address < Segment->VirtualAddress + Segment->Size)) { - Offset = (Address - Segment->VirtualAddress); - - if (MaxSize != NULL) { - *MaxSize = (UINT32)(Segment->Size - Offset); - } - - Offset += Segment->FileOffset - Context->ContainerOffset; - return (VOID *)((UINTN)Context->MachHeader + (UINTN)Offset); - } - } - - return NULL; -} - -/** - 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; - UINT32 OriginalCommandSize; - // - // 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; - OriginalCommandSize = SizeOfLeftCommands; - - for (Index = 0; Index < MachHeader->NumCommands; ++Index) { - // - // Assertion: LC_UNIXTHREAD and LC_MAIN are technically stripped in KXLD, - // but they are not supposed to be present in the first place. - // - if ((LoadCommand->CommandType == MACH_LOAD_COMMAND_UNIX_THREAD) - || (LoadCommand->CommandType == MACH_LOAD_COMMAND_MAIN)) { - DEBUG ((DEBUG_WARN, "OCMCO: UNIX Thread and Main LCs are unsupported\n")); - } - - SizeOfLeftCommands -= LoadCommand->CommandSize; - - for (Index2 = 0; Index2 < 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); - } - - ZeroMem (LoadCommand, OriginalCommandSize - MachHeader->CommandsSize); -} - -UINT32 -MachoExpandImage64 ( - IN OC_MACHO_CONTEXT *Context, - OUT UINT8 *Destination, - IN UINT32 DestinationSize, - IN BOOLEAN Strip - ) -{ - MACH_HEADER_64 *Header; - UINT8 *Source; - UINT32 HeaderSize; - UINT64 CopyFileOffset; - UINT64 CopyFileSize; - UINT64 CopyVmSize; - UINT32 CurrentDelta; - UINT32 OriginalDelta; - UINT64 CurrentSize; - UINT32 FileSize; - MACH_SEGMENT_COMMAND_64 *Segment; - MACH_SEGMENT_COMMAND_64 *FirstSegment; - MACH_SEGMENT_COMMAND_64 *DstSegment; - MACH_SYMTAB_COMMAND *Symtab; - MACH_DYSYMTAB_COMMAND *DySymtab; - UINT32 Index; - - ASSERT (Context != NULL); - ASSERT (Context->FileSize != 0); - - // - // Header is valid, copy it first. - // - Header = MachoGetMachHeader64 (Context); - Source = (UINT8 *) Header; - HeaderSize = sizeof (*Header) + Header->CommandsSize; - if (HeaderSize > DestinationSize) { - return 0; - } - CopyMem (Destination, Header, HeaderSize); - - CurrentDelta = 0; - FirstSegment = NULL; - CurrentSize = 0; - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - // - // Align delta by x86 page size, this is what our lib expects. - // - OriginalDelta = CurrentDelta; - CurrentDelta = MACHO_ALIGN (CurrentDelta); - if (Segment->FileSize > Segment->Size) { - return 0; - } - - if (FirstSegment == NULL) { - FirstSegment = Segment; - } - - // - // Do not overwrite header. - // - CopyFileOffset = Segment->FileOffset - Context->ContainerOffset; - CopyFileSize = Segment->FileSize; - CopyVmSize = Segment->Size; - if (CopyFileOffset <= HeaderSize) { - CopyFileOffset = HeaderSize; - CopyFileSize = Segment->FileSize - CopyFileOffset; - CopyVmSize = Segment->Size - CopyFileOffset; - if (CopyFileSize > Segment->FileSize || CopyVmSize > Segment->Size) { - // - // Header must fit in 1 segment. - // - return 0; - } - } - // - // Ensure that it still fits. In legit files segments are ordered. - // We do not care for other (the file will be truncated). - // - if (OcOverflowTriAddU64 (CopyFileOffset, CurrentDelta, CopyVmSize, &CurrentSize) - || CurrentSize > DestinationSize) { - return 0; - } - - // - // Copy and zero fill file data. We can do this because only last sections can have 0 file size. - // - ASSERT (CopyFileSize <= MAX_UINTN && CopyVmSize <= MAX_UINTN); - ZeroMem (&Destination[CopyFileOffset + OriginalDelta], CurrentDelta - OriginalDelta); - CopyMem (&Destination[CopyFileOffset + CurrentDelta], &Source[CopyFileOffset], (UINTN)CopyFileSize); - ZeroMem (&Destination[CopyFileOffset + CurrentDelta + CopyFileSize], (UINTN)(CopyVmSize - CopyFileSize)); - // - // Refresh destination segment size and offsets. - // - DstSegment = (MACH_SEGMENT_COMMAND_64 *) ((UINT8 *) Segment - Source + Destination); - DstSegment->FileOffset += CurrentDelta; - DstSegment->FileSize = DstSegment->Size; - - if (DstSegment->VirtualAddress - (DstSegment->FileOffset - Context->ContainerOffset) != FirstSegment->VirtualAddress) { - return 0; - } - - // - // We need to update fields in SYMTAB and DYSYMTAB. Tables have to be present before 0 FileSize - // sections as they have data, so we update them before parsing sections. - // Note: There is an assumption they are in __LINKEDIT segment, another option is to check addresses. - // - if (AsciiStrnCmp (DstSegment->SegmentName, "__LINKEDIT", ARRAY_SIZE (DstSegment->SegmentName)) == 0) { - Symtab = (MACH_SYMTAB_COMMAND *)( - MachoGetNextCommand64 ( - Context, - MACH_LOAD_COMMAND_SYMTAB, - NULL - ) - ); - - if (Symtab != NULL) { - Symtab = (MACH_SYMTAB_COMMAND *) ((UINT8 *) Symtab - Source + Destination); - if (Symtab->SymbolsOffset != 0) { - Symtab->SymbolsOffset += CurrentDelta; - } - if (Symtab->StringsOffset != 0) { - Symtab->StringsOffset += CurrentDelta; - } - } - - DySymtab = (MACH_DYSYMTAB_COMMAND *)( - MachoGetNextCommand64 ( - Context, - MACH_LOAD_COMMAND_DYSYMTAB, - NULL - ) - ); - - if (DySymtab != NULL) { - DySymtab = (MACH_DYSYMTAB_COMMAND *) ((UINT8 *) DySymtab - Source + Destination); - if (DySymtab->TableOfContentsNumEntries != 0) { - DySymtab->TableOfContentsNumEntries += CurrentDelta; - } - if (DySymtab->ModuleTableFileOffset != 0) { - DySymtab->ModuleTableFileOffset += CurrentDelta; - } - if (DySymtab->ReferencedSymbolTableFileOffset != 0) { - DySymtab->ReferencedSymbolTableFileOffset += CurrentDelta; - } - if (DySymtab->IndirectSymbolsOffset != 0) { - DySymtab->IndirectSymbolsOffset += CurrentDelta; - } - if (DySymtab->ExternalRelocationsOffset != 0) { - DySymtab->ExternalRelocationsOffset += CurrentDelta; - } - if (DySymtab->LocalRelocationsOffset != 0) { - DySymtab->LocalRelocationsOffset += CurrentDelta; - } - } - } - // - // These may well wrap around with invalid data. - // But we do not care, as we do not access these fields ourselves, - // and later on the section values are checked by MachoLib. - // Note: There is an assumption that 'CopyFileOffset + CurrentDelta' is aligned. - // - OriginalDelta = CurrentDelta; - CopyFileOffset = Segment->FileOffset; - for (Index = 0; Index < DstSegment->NumSections; ++Index) { - if (DstSegment->Sections[Index].Offset == 0) { - DstSegment->Sections[Index].Offset = (UINT32) CopyFileOffset + CurrentDelta; - CurrentDelta += (UINT32) DstSegment->Sections[Index].Size; - } else { - DstSegment->Sections[Index].Offset += CurrentDelta; - CopyFileOffset = DstSegment->Sections[Index].Offset + DstSegment->Sections[Index].Size; - } - } - - CurrentDelta = OriginalDelta + (UINT32)(Segment->Size - Segment->FileSize); - } - // - // CurrentSize will only be 0 if there are no valid segments, which is the - // case for Kernel Resource KEXTs. In this case, try to use the raw file. - // - if (CurrentSize == 0) { - FileSize = MachoGetFileSize (Context); - // - // HeaderSize must be at most as big as the file size by OcMachoLib - // guarantees. It's sanity-checked to ensure the safety of the subtraction. - // - ASSERT (FileSize >= HeaderSize); - - if (FileSize > DestinationSize) { - return 0; - } - - CopyMem ( - Destination + HeaderSize, - (UINT8 *)Header + HeaderSize, - FileSize - HeaderSize - ); - - CurrentSize = FileSize; - } - - if (Strip) { - InternalStripLoadCommands64 ((MACH_HEADER_64 *) Destination); - } - // - // This cast is safe because CurrentSize is verified against DestinationSize. - // - return (UINT32) CurrentSize; + return MachoInitialiseSymtabsExternal (Context, Context); } UINT64 @@ -1507,115 +506,48 @@ MachoRuntimeGetEntryAddress ( return Address; } -BOOLEAN -MachoMergeSegments64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST CHAR8 *Prefix +VOID * +MachoGetFilePointerByAddress ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *MaxSize OPTIONAL ) { - UINT32 LcIndex; - MACH_LOAD_COMMAND *LoadCommand; - MACH_SEGMENT_COMMAND_64 *Segment; - MACH_SEGMENT_COMMAND_64 *FirstSegment; - MACH_HEADER_64 *Header; - UINTN PrefixLength; - UINTN RemainingArea; - UINT32 SkipCount; - ASSERT (Context != NULL); - ASSERT (Context->FileSize != 0); - ASSERT (Prefix != NULL); - - Header = MachoGetMachHeader64 (Context); - PrefixLength = AsciiStrLen (Prefix); - FirstSegment = NULL; - - SkipCount = 0; - - LoadCommand = &Header->Commands[0]; - - for (LcIndex = 0; LcIndex < Header->NumCommands; ++LcIndex) { - // - // Either skip or stop at unrelated commands. - // - Segment = (MACH_SEGMENT_COMMAND_64 *) (VOID *) LoadCommand; - - if (LoadCommand->CommandType != MACH_LOAD_COMMAND_SEGMENT_64 - || AsciiStrnCmp (Segment->SegmentName, Prefix, PrefixLength) != 0) { - if (FirstSegment != NULL) { - break; - } - - LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand); - continue; - } - - // - // We have a segment starting with the prefix. - // - - // - // Do not support this for now as it will require changes in the file. - // - if (Segment->Size != Segment->FileSize) { - return FALSE; - } - - // - // Remember the first segment or assume it is a skip. - // - if (FirstSegment == NULL) { - FirstSegment = Segment; - } else { - ++SkipCount; - - // - // Expand the first segment. - // TODO: Do we need to check these for overflow for our own purposes? - // - FirstSegment->Size = Segment->VirtualAddress - FirstSegment->VirtualAddress + Segment->Size; - FirstSegment->FileSize = Segment->FileOffset - FirstSegment->FileOffset + Segment->FileSize; - - // - // Add new segment protection to the first segment. - // - FirstSegment->InitialProtection |= Segment->InitialProtection; - FirstSegment->MaximumProtection |= Segment->MaximumProtection; - } - LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand); + if (Context->Is32Bit) { + ASSERT (Address > MAX_UINT32); } - // - // The segment does not exist. - // - if (FirstSegment == NULL) { - return FALSE; - } + return Context->Is32Bit ? + InternalMachoGetFilePointerByAddress32 (Context, (UINT32) Address, MaxSize) : + InternalMachoGetFilePointerByAddress64 (Context, Address, MaxSize); +} - // - // The segment is only one. - // - if (SkipCount == 0) { - return FALSE; - } +UINT32 +MachoExpandImage ( + IN OC_MACHO_CONTEXT *Context, + OUT UINT8 *Destination, + IN UINT32 DestinationSize, + IN BOOLEAN Strip + ) +{ + ASSERT (Context != NULL); - // - // Move back remaining commands ontop of the skipped ones and zero this area. - // - RemainingArea = Header->CommandsSize - ((UINTN) LoadCommand - (UINTN) &Header->Commands[0]); - CopyMem ( - (UINT8 *) FirstSegment + FirstSegment->CommandSize, - LoadCommand, - RemainingArea - ); - ZeroMem (LoadCommand, RemainingArea); + return Context->Is32Bit ? + InternalMachoExpandImage32 (Context, Destination, DestinationSize, Strip) : + InternalMachoExpandImage64 (Context, Destination, DestinationSize, Strip); +} - // - // Account for dropped commands in the header. - // - Header->NumCommands -= SkipCount; - Header->CommandsSize -= (UINT32) (sizeof (MACH_SEGMENT_COMMAND_64) * SkipCount); +BOOLEAN +MachoMergeSegments ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Prefix + ) +{ + ASSERT (Context != NULL); - return TRUE; + return Context->Is32Bit ? + InternalMachoMergeSegments32 (Context, Prefix) : + InternalMachoMergeSegments64 (Context, Prefix); } diff --git a/Library/OcMachoLib/HeaderX.h b/Library/OcMachoLib/HeaderX.h new file mode 100644 index 00000000..72683688 --- /dev/null +++ b/Library/OcMachoLib/HeaderX.h @@ -0,0 +1,1156 @@ +/** + Provides services for Mach-O headers. + +Copyright (C) 2016 - 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 "MachoX.h" + +/** + Returns whether Section is sane. + + @param[in,out] Context Context of the Mach-O. + @param[in] Section Section to verify. + @param[in] Segment Segment the section is part of. + +**/ +STATIC +BOOLEAN +InternalSectionIsSane ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SECTION_X *Section, + IN CONST MACH_SEGMENT_COMMAND_X *Segment + ) +{ + MACH_UINT_X TopOffsetX; + UINT32 TopOffset32; + MACH_UINT_X TopOfSegment; + BOOLEAN Result; + MACH_UINT_X TopOfSection; + + ASSERT (Context != NULL); + ASSERT (Section != NULL); + ASSERT (Segment != NULL); + MACH_ASSERT_X (Context); + + // + // Section->Alignment is stored as a power of 2. + // + if ((Section->Alignment > 31) + || ((Section->Offset != 0) && (Section->Offset < Segment->FileOffset))) { + return FALSE; + } + + TopOfSegment = (Segment->VirtualAddress + Segment->Size); + Result = MACH_X (OcOverflowAddU) ( + Section->Address, + Section->Size, + &TopOfSection + ); + if (Result || (TopOfSection > TopOfSegment)) { + return FALSE; + } + + Result = MACH_X (OcOverflowAddU) ( + Section->Offset, + Section->Size, + &TopOffsetX + ); + if (Result || (TopOffsetX > (Segment->FileOffset + Segment->FileSize))) { + return FALSE; + } + + if (Section->NumRelocations != 0) { + Result = OcOverflowSubU32 ( + Section->RelocationsOffset, + Context->ContainerOffset, + &TopOffset32 + ); + Result |= OcOverflowMulAddU32 ( + Section->NumRelocations, + sizeof (MACH_RELOCATION_INFO), + TopOffset32, + &TopOffset32 + ); + if (Result || (TopOffset32 > Context->FileSize)) { + return FALSE; + } + } + + return TRUE; +} + +/** + 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 +InternalStripLoadCommands ( + IN OUT MACH_HEADER_X *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; + UINT32 OriginalCommandSize; + // + // 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; + OriginalCommandSize = SizeOfLeftCommands; + + for (Index = 0; Index < MachHeader->NumCommands; ++Index) { + // + // Assertion: LC_UNIXTHREAD and LC_MAIN are technically stripped in KXLD, + // but they are not supposed to be present in the first place. + // + if ((LoadCommand->CommandType == MACH_LOAD_COMMAND_UNIX_THREAD) + || (LoadCommand->CommandType == MACH_LOAD_COMMAND_MAIN)) { + DEBUG ((DEBUG_WARN, "OCMCO: UNIX Thread and Main LCs are unsupported\n")); + } + + SizeOfLeftCommands -= LoadCommand->CommandSize; + + for (Index2 = 0; Index2 < 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); + } + + ZeroMem (LoadCommand, OriginalCommandSize - MachHeader->CommandsSize); +} + +UINT32 +MACH_X (InternalMachoGetVmSize) ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + MACH_UINT_X VmSize; + MACH_SEGMENT_COMMAND_X *Segment; + + ASSERT (Context != NULL); + ASSERT (Context->FileSize != 0); + MACH_ASSERT_X (Context); + + VmSize = 0; + + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + if (MACH_X (OcOverflowAddU) (VmSize, Segment->Size, &VmSize)) { + return 0; + } + VmSize = MACHO_ALIGN (VmSize); + } + +#ifndef MACHO_LIB_32 + if (VmSize > MAX_UINT32) { + return 0; + } +#endif + + return MACH_X_TO_UINT32 (VmSize); +} + +MACH_LOAD_COMMAND * +MACH_X (InternalMachoGetNextCommand) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_LOAD_COMMAND_TYPE LoadCommandType, + IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL + ) +{ + MACH_LOAD_COMMAND *Command; + MACH_HEADER_X *MachHeader; + UINTN TopOfCommands; + + ASSERT (Context != NULL); + ASSERT (Context->MachHeader != NULL); + MACH_ASSERT_X (Context); + + MachHeader = MACH_X (&Context->MachHeader->Header); + + TopOfCommands = ((UINTN)MachHeader->Commands + MachHeader->CommandsSize); + + if (LoadCommand != NULL) { + ASSERT ( + (LoadCommand >= &MachHeader->Commands[0]) + && ((UINTN)LoadCommand <= TopOfCommands) + ); + Command = NEXT_MACH_LOAD_COMMAND (LoadCommand); + } else { + Command = &MachHeader->Commands[0]; + } + + for ( + ; + (UINTN)Command < TopOfCommands; + Command = NEXT_MACH_LOAD_COMMAND (Command) + ) { + if (Command->CommandType == LoadCommandType) { + return Command; + } + } + + return NULL; +} + +VOID * +MACH_X (InternalMachoGetFilePointerByAddress) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Address, + OUT UINT32 *MaxSize OPTIONAL + ) +{ + CONST MACH_SEGMENT_COMMAND_X *Segment; + MACH_UINT_X Offset; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + Segment = NULL; + while ((Segment = MACH_X (MachoGetNextSegment) (Context, Segment)) != NULL) { + if ((Address >= Segment->VirtualAddress) + && (Address < Segment->VirtualAddress + Segment->Size)) { + Offset = (Address - Segment->VirtualAddress); + + if (MaxSize != NULL) { + *MaxSize = MACH_X_TO_UINT32 (Segment->Size - Offset); + } + + Offset += Segment->FileOffset - Context->ContainerOffset; + return (VOID *)((UINTN)Context->MachHeader + (UINTN)Offset); + } + } + + return NULL; +} + +UINT32 +MACH_X (InternalMachoExpandImage) ( + IN OC_MACHO_CONTEXT *Context, + OUT UINT8 *Destination, + IN UINT32 DestinationSize, + IN BOOLEAN Strip + ) +{ + MACH_HEADER_X *Header; + UINT8 *Source; + UINT32 HeaderSize; + MACH_UINT_X CopyFileOffset; + MACH_UINT_X CopyFileSize; + MACH_UINT_X CopyVmSize; + UINT32 CurrentDelta; + UINT32 OriginalDelta; + MACH_UINT_X CurrentSize; + UINT32 FileSize; + MACH_SEGMENT_COMMAND_X *Segment; + MACH_SEGMENT_COMMAND_X *FirstSegment; + MACH_SEGMENT_COMMAND_X *DstSegment; + MACH_SYMTAB_COMMAND *Symtab; + MACH_DYSYMTAB_COMMAND *DySymtab; + UINT32 Index; + + ASSERT (Context != NULL); + ASSERT (Context->FileSize != 0); + MACH_ASSERT_X (Context); + + // + // Header is valid, copy it first. + // + Header = MACH_X (MachoGetMachHeader) (Context); + Source = (UINT8 *) Header; + HeaderSize = sizeof (*Header) + Header->CommandsSize; + if (HeaderSize > DestinationSize) { + return 0; + } + CopyMem (Destination, Header, HeaderSize); + + CurrentDelta = 0; + FirstSegment = NULL; + CurrentSize = 0; + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + // + // Align delta by x86 page size, this is what our lib expects. + // + OriginalDelta = CurrentDelta; + CurrentDelta = MACHO_ALIGN (CurrentDelta); + if (Segment->FileSize > Segment->Size) { + return 0; + } + + if (FirstSegment == NULL) { + FirstSegment = Segment; + } + + // + // Do not overwrite header. + // + CopyFileOffset = Segment->FileOffset - Context->ContainerOffset; + CopyFileSize = Segment->FileSize; + CopyVmSize = Segment->Size; + if (CopyFileOffset <= HeaderSize) { + CopyFileOffset = HeaderSize; + CopyFileSize = Segment->FileSize - CopyFileOffset; + CopyVmSize = Segment->Size - CopyFileOffset; + if (CopyFileSize > Segment->FileSize || CopyVmSize > Segment->Size) { + // + // Header must fit in 1 segment. + // + return 0; + } + } + // + // Ensure that it still fits. In legit files segments are ordered. + // We do not care for other (the file will be truncated). + // + if (MACH_X (OcOverflowTriAddU) (CopyFileOffset, CurrentDelta, CopyVmSize, &CurrentSize) + || CurrentSize > DestinationSize) { + return 0; + } + + // + // Copy and zero fill file data. We can do this because only last sections can have 0 file size. + // +#ifndef MACHO_LIB_32 + ASSERT (CopyFileSize <= MAX_UINTN && CopyVmSize <= MAX_UINTN); +#endif + ZeroMem (&Destination[CopyFileOffset + OriginalDelta], CurrentDelta - OriginalDelta); + CopyMem (&Destination[CopyFileOffset + CurrentDelta], &Source[CopyFileOffset], (UINTN)CopyFileSize); + ZeroMem (&Destination[CopyFileOffset + CurrentDelta + CopyFileSize], (UINTN)(CopyVmSize - CopyFileSize)); + // + // Refresh destination segment size and offsets. + // + DstSegment = (MACH_SEGMENT_COMMAND_X *) ((UINT8 *) Segment - Source + Destination); + DstSegment->FileOffset += CurrentDelta; + DstSegment->FileSize = DstSegment->Size; + + if (DstSegment->VirtualAddress - (DstSegment->FileOffset - Context->ContainerOffset) != FirstSegment->VirtualAddress) { + return 0; + } + + // + // We need to update fields in SYMTAB and DYSYMTAB. Tables have to be present before 0 FileSize + // sections as they have data, so we update them before parsing sections. + // Note: There is an assumption they are in __LINKEDIT segment, another option is to check addresses. + // + if (AsciiStrnCmp (DstSegment->SegmentName, "__LINKEDIT", ARRAY_SIZE (DstSegment->SegmentName)) == 0) { + Symtab = (MACH_SYMTAB_COMMAND *)( + MachoGetNextCommand ( + Context, + MACH_LOAD_COMMAND_SYMTAB, + NULL + ) + ); + + if (Symtab != NULL) { + Symtab = (MACH_SYMTAB_COMMAND *) ((UINT8 *) Symtab - Source + Destination); + if (Symtab->SymbolsOffset != 0) { + Symtab->SymbolsOffset += CurrentDelta; + } + if (Symtab->StringsOffset != 0) { + Symtab->StringsOffset += CurrentDelta; + } + } + + DySymtab = (MACH_DYSYMTAB_COMMAND *)( + MachoGetNextCommand ( + Context, + MACH_LOAD_COMMAND_DYSYMTAB, + NULL + ) + ); + + if (DySymtab != NULL) { + DySymtab = (MACH_DYSYMTAB_COMMAND *) ((UINT8 *) DySymtab - Source + Destination); + if (DySymtab->TableOfContentsNumEntries != 0) { + DySymtab->TableOfContentsNumEntries += CurrentDelta; + } + if (DySymtab->ModuleTableFileOffset != 0) { + DySymtab->ModuleTableFileOffset += CurrentDelta; + } + if (DySymtab->ReferencedSymbolTableFileOffset != 0) { + DySymtab->ReferencedSymbolTableFileOffset += CurrentDelta; + } + if (DySymtab->IndirectSymbolsOffset != 0) { + DySymtab->IndirectSymbolsOffset += CurrentDelta; + } + if (DySymtab->ExternalRelocationsOffset != 0) { + DySymtab->ExternalRelocationsOffset += CurrentDelta; + } + if (DySymtab->LocalRelocationsOffset != 0) { + DySymtab->LocalRelocationsOffset += CurrentDelta; + } + } + } + // + // These may well wrap around with invalid data. + // But we do not care, as we do not access these fields ourselves, + // and later on the section values are checked by MachoLib. + // Note: There is an assumption that 'CopyFileOffset + CurrentDelta' is aligned. + // + OriginalDelta = CurrentDelta; + CopyFileOffset = Segment->FileOffset; + for (Index = 0; Index < DstSegment->NumSections; ++Index) { + if (DstSegment->Sections[Index].Offset == 0) { + DstSegment->Sections[Index].Offset = MACH_X_TO_UINT32 (CopyFileOffset + CurrentDelta); + CurrentDelta += MACH_X_TO_UINT32 (DstSegment->Sections[Index].Size); + } else { + DstSegment->Sections[Index].Offset += CurrentDelta; + CopyFileOffset = DstSegment->Sections[Index].Offset + DstSegment->Sections[Index].Size; + } + } + + CurrentDelta = OriginalDelta + MACH_X_TO_UINT32 (Segment->Size - Segment->FileSize); + } + // + // CurrentSize will only be 0 if there are no valid segments, which is the + // case for Kernel Resource KEXTs. In this case, try to use the raw file. + // + if (CurrentSize == 0) { + FileSize = MachoGetFileSize (Context); + // + // HeaderSize must be at most as big as the file size by OcMachoLib + // guarantees. It's sanity-checked to ensure the safety of the subtraction. + // + ASSERT (FileSize >= HeaderSize); + + if (FileSize > DestinationSize) { + return 0; + } + + CopyMem ( + Destination + HeaderSize, + (UINT8 *)Header + HeaderSize, + FileSize - HeaderSize + ); + + CurrentSize = FileSize; + } + + if (Strip) { + InternalStripLoadCommands ((MACH_HEADER_X *) Destination); + } + // + // This cast is safe because CurrentSize is verified against DestinationSize. + // + return MACH_X_TO_UINT32 (CurrentSize); +} + +BOOLEAN +MACH_X (InternalMachoMergeSegments) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Prefix + ) +{ + UINT32 LcIndex; + MACH_LOAD_COMMAND *LoadCommand; + MACH_SEGMENT_COMMAND_X *Segment; + MACH_SEGMENT_COMMAND_X *FirstSegment; + MACH_HEADER_X *Header; + UINTN PrefixLength; + UINTN RemainingArea; + UINT32 SkipCount; + + ASSERT (Context != NULL); + ASSERT (Context->FileSize != 0); + ASSERT (Prefix != NULL); + MACH_ASSERT_X (Context); + + Header = MACH_X (MachoGetMachHeader) (Context); + PrefixLength = AsciiStrLen (Prefix); + FirstSegment = NULL; + + SkipCount = 0; + + LoadCommand = &Header->Commands[0]; + + for (LcIndex = 0; LcIndex < Header->NumCommands; ++LcIndex) { + // + // Either skip or stop at unrelated commands. + // + Segment = (MACH_SEGMENT_COMMAND_X *) (VOID *) LoadCommand; + + if (LoadCommand->CommandType != MACH_LOAD_COMMAND_SEGMENT_X + || AsciiStrnCmp (Segment->SegmentName, Prefix, PrefixLength) != 0) { + if (FirstSegment != NULL) { + break; + } + + LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand); + continue; + } + + // + // We have a segment starting with the prefix. + // + + // + // Do not support this for now as it will require changes in the file. + // + if (Segment->Size != Segment->FileSize) { + return FALSE; + } + + // + // Remember the first segment or assume it is a skip. + // + if (FirstSegment == NULL) { + FirstSegment = Segment; + } else { + ++SkipCount; + + // + // Expand the first segment. + // TODO: Do we need to check these for overflow for our own purposes? + // + FirstSegment->Size = Segment->VirtualAddress - FirstSegment->VirtualAddress + Segment->Size; + FirstSegment->FileSize = Segment->FileOffset - FirstSegment->FileOffset + Segment->FileSize; + + // + // Add new segment protection to the first segment. + // + FirstSegment->InitialProtection |= Segment->InitialProtection; + FirstSegment->MaximumProtection |= Segment->MaximumProtection; + } + + LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand); + } + + // + // The segment does not exist. + // + if (FirstSegment == NULL) { + return FALSE; + } + + // + // The segment is only one. + // + if (SkipCount == 0) { + return FALSE; + } + + // + // Move back remaining commands ontop of the skipped ones and zero this area. + // + RemainingArea = Header->CommandsSize - ((UINTN) LoadCommand - (UINTN) &Header->Commands[0]); + CopyMem ( + (UINT8 *) FirstSegment + FirstSegment->CommandSize, + LoadCommand, + RemainingArea + ); + ZeroMem (LoadCommand, RemainingArea); + + // + // Account for dropped commands in the header. + // + Header->NumCommands -= SkipCount; + Header->CommandsSize -= (UINT32) (sizeof (MACH_SEGMENT_COMMAND_X) * SkipCount); + + return TRUE; +} + +BOOLEAN +MACH_X (MachoInitializeContext) ( + OUT OC_MACHO_CONTEXT *Context, + IN VOID *FileData, + IN UINT32 FileSize, + IN UINT32 ContainerOffset + ) +{ + EFI_STATUS Status; + MACH_HEADER_X *MachHeader; + UINTN TopOfFile; + UINTN TopOfCommands; + UINT32 Index; + CONST MACH_LOAD_COMMAND *Command; + UINTN TopOfCommand; + UINT32 CommandsSize; + BOOLEAN Result; + + ASSERT (FileData != NULL); + ASSERT (FileSize > 0); + ASSERT (Context != NULL); + + TopOfFile = ((UINTN)FileData + FileSize); + ASSERT (TopOfFile > (UINTN)FileData); + +#ifdef MACHO_LIB_32 + Status = FatFilterArchitecture32 ((UINT8 **) &FileData, &FileSize); +#else + Status = FatFilterArchitecture64 ((UINT8 **) &FileData, &FileSize); +#endif + if (EFI_ERROR (Status)) { + return FALSE; + } + + if (FileSize < sizeof (*MachHeader) + || !OC_TYPE_ALIGNED (MACH_HEADER_X, FileData)) { + return FALSE; + } + MachHeader = (MACH_HEADER_X *)FileData; +#ifdef MACHO_LIB_32 + if (MachHeader->Signature != MACH_HEADER_SIGNATURE) { +#else + if (MachHeader->Signature != MACH_HEADER_64_SIGNATURE) { +#endif + return FALSE; + } + + Result = OcOverflowAddUN ( + (UINTN)MachHeader->Commands, + MachHeader->CommandsSize, + &TopOfCommands + ); + if (Result || (TopOfCommands > TopOfFile)) { + return FALSE; + } + + CommandsSize = 0; + + for ( + Index = 0, Command = MachHeader->Commands; + Index < MachHeader->NumCommands; + ++Index, Command = NEXT_MACH_LOAD_COMMAND (Command) + ) { + Result = OcOverflowAddUN ( + (UINTN)Command, + sizeof (*Command), + &TopOfCommand + ); + if (Result + || (TopOfCommand > TopOfCommands) + || (Command->CommandSize < sizeof (*Command)) + || ((Command->CommandSize % sizeof (MACH_UINT_X)) != 0) + ) { + return FALSE; + } + + Result = OcOverflowAddU32 ( + CommandsSize, + Command->CommandSize, + &CommandsSize + ); + if (Result) { + return FALSE; + } + } + + if (MachHeader->CommandsSize != CommandsSize) { + return FALSE; + } + // + // Verify assumptions made by this library. + // Carefully audit all "Assumption:" remarks before modifying these checks. + // +#ifdef MACHO_LIB_32 + if ((MachHeader->CpuType != MachCpuTypeI386) +#else + if ((MachHeader->CpuType != MachCpuTypeX8664) +#endif + || ((MachHeader->FileType != MachHeaderFileTypeKextBundle) + && (MachHeader->FileType != MachHeaderFileTypeExecute) + && (MachHeader->FileType != MachHeaderFileTypeFileSet) +#ifdef MACHO_LIB_32 + && (MachHeader->FileType != MachHeaderFileTypeObject))) { +#else + )) { +#endif + return FALSE; + } + + ZeroMem (Context, sizeof (*Context)); + + Context->MachHeader = (MACH_HEADER_ANY*)MachHeader; + Context->Is32Bit = MachHeader->CpuType == MachCpuTypeI386; + Context->FileSize = FileSize; + Context->ContainerOffset = ContainerOffset; + + return TRUE; +} + +MACH_HEADER_X * +MACH_X (MachoGetMachHeader) ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + ASSERT (Context != NULL); + ASSERT (Context->MachHeader != NULL); + MACH_ASSERT_X (Context); + + return MACH_X (&Context->MachHeader->Header); +} + +MACH_UINT_X +MACH_X (MachoGetLastAddress) ( + IN OUT OC_MACHO_CONTEXT *Context + ) +{ + MACH_UINT_X LastAddress; + + CONST MACH_SEGMENT_COMMAND_X *Segment; + MACH_UINT_X Address; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + LastAddress = 0; + + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + Address = (Segment->VirtualAddress + Segment->Size); + + if (Address > LastAddress) { + LastAddress = Address; + } + } + + return LastAddress; +} + +MACH_SEGMENT_COMMAND_X * +MACH_X (MachoGetSegmentByName) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName + ) +{ + MACH_SEGMENT_COMMAND_X *Segment; + INTN Result; + + ASSERT (Context != NULL); + ASSERT (SegmentName != NULL); + MACH_ASSERT_X (Context); + + Result = 0; + + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + Result = AsciiStrnCmp ( + Segment->SegmentName, + SegmentName, + ARRAY_SIZE (Segment->SegmentName) + ); + if (Result == 0) { + return Segment; + } + } + + return NULL; +} + +MACH_SECTION_X * +MACH_X (MachoGetSectionByName) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_X *Segment, + IN CONST CHAR8 *SectionName + ) +{ + MACH_SECTION_X *Section; + INTN Result; + + ASSERT (Context != NULL); + ASSERT (Segment != NULL); + ASSERT (SectionName != NULL); + MACH_ASSERT_X (Context); + + for ( + Section = MACH_X (MachoGetNextSection) (Context, Segment, NULL); + Section != NULL; + Section = MACH_X (MachoGetNextSection) (Context, Segment, Section) + ) { + // + // Assumption: Mach-O is not of type MH_OBJECT. + // MH_OBJECT might have sections in segments they do not belong in for + // performance reasons. This library does not support intermediate + // objects. + // + Result = AsciiStrnCmp ( + Section->SectionName, + SectionName, + ARRAY_SIZE (Section->SectionName) + ); + if (Result == 0) { + return Section; + } + } + + return NULL; +} + +MACH_SECTION_X * +MACH_X (MachoGetSegmentSectionByName) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *SegmentName, + IN CONST CHAR8 *SectionName + ) +{ + MACH_SEGMENT_COMMAND_X *Segment; + + ASSERT (Context != NULL); + ASSERT (SegmentName != NULL); + ASSERT (SectionName != NULL); + MACH_ASSERT_X (Context); + + Segment = MACH_X (MachoGetSegmentByName) (Context, SegmentName); + + if (Segment != NULL) { + return MACH_X (MachoGetSectionByName) (Context, Segment, SectionName); + } + + return NULL; +} + +MACH_SEGMENT_COMMAND_X * +MACH_X (MachoGetNextSegment) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_SEGMENT_COMMAND_X *Segment OPTIONAL + ) +{ + MACH_SEGMENT_COMMAND_X *NextSegment; + + CONST MACH_HEADER_X *MachHeader; + UINTN TopOfCommands; + BOOLEAN Result; + MACH_UINT_X TopOfSegment; + UINTN TopOfSections; + + ASSERT (Context != NULL); + ASSERT (Context->FileSize > 0); + MACH_ASSERT_X (Context); + + if (Segment != NULL) { + MachHeader = MACH_X (MachoGetMachHeader) (Context); + TopOfCommands = ((UINTN) MachHeader->Commands + MachHeader->CommandsSize); + ASSERT ( + ((UINTN) Segment >= (UINTN) &MachHeader->Commands[0]) + && ((UINTN) Segment < TopOfCommands) + ); + } + // + // Context initialisation guarantees the command size is a multiple of 8. + // + STATIC_ASSERT ( + OC_ALIGNOF (MACH_SEGMENT_COMMAND_X) <= sizeof (UINT64), + "Alignment is not guaranteed." + ); + NextSegment = (MACH_SEGMENT_COMMAND_X *) (VOID *) MachoGetNextCommand ( + Context, + MACH_LOAD_COMMAND_SEGMENT_X, + (CONST MACH_LOAD_COMMAND *) Segment + ); + if (NextSegment == NULL || NextSegment->CommandSize < sizeof (*NextSegment)) { + return NULL; + } + + Result = OcOverflowMulAddUN ( + NextSegment->NumSections, + sizeof (*NextSegment->Sections), + (UINTN) NextSegment->Sections, + &TopOfSections + ); + if (Result || (((UINTN) NextSegment + NextSegment->CommandSize) < TopOfSections)) { + return NULL; + } + + Result = MACH_X (OcOverflowSubU) ( + NextSegment->FileOffset, + Context->ContainerOffset, + &TopOfSegment + ); + Result |= MACH_X (OcOverflowAddU) ( + TopOfSegment, + NextSegment->FileSize, + &TopOfSegment + ); + if (Result || (TopOfSegment > Context->FileSize)) { + return NULL; + } + + if (NextSegment->VirtualAddress + NextSegment->Size < NextSegment->VirtualAddress) { + return NULL; + } + + return NextSegment; +} + +MACH_SECTION_X * +MACH_X (MachoGetNextSection) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_SEGMENT_COMMAND_X *Segment, + IN MACH_SECTION_X *Section OPTIONAL + ) +{ + ASSERT (Context != NULL); + ASSERT (Segment != NULL); + MACH_ASSERT_X (Context); + + if (Section != NULL) { + ASSERT (Section >= Segment->Sections); + + ++Section; + + if (Section >= &Segment->Sections[Segment->NumSections]) { + return NULL; + } + } else if (Segment->NumSections > 0) { + Section = &Segment->Sections[0]; + } else { + return NULL; + } + + if (!InternalSectionIsSane (Context, Section, Segment)) { + return NULL; + } + + return Section; +} + +MACH_SECTION_X * +MACH_X (MachoGetSectionByIndex) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ) +{ + MACH_SECTION_X *Section; + + MACH_SEGMENT_COMMAND_X *Segment; + UINT32 SectionIndex; + UINT32 NextSectionIndex; + BOOLEAN Result; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + SectionIndex = 0; + + Segment = NULL; + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + Result = OcOverflowAddU32 ( + SectionIndex, + Segment->NumSections, + &NextSectionIndex + ); + // + // If NextSectionIndex is wrapping around, Index must be contained. + // + if (Result || (Index < NextSectionIndex)) { + Section = &Segment->Sections[Index - SectionIndex]; + if (!InternalSectionIsSane (Context, Section, Segment)) { + return NULL; + } + + return Section; + } + + SectionIndex = NextSectionIndex; + } + + return NULL; +} + +MACH_SECTION_X * +MACH_X (MachoGetSectionByAddress) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Address + ) +{ + MACH_SEGMENT_COMMAND_X *Segment; + MACH_SECTION_X *Section; + MACH_UINT_X TopOfSegment; + MACH_UINT_X TopOfSection; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + TopOfSegment = (Segment->VirtualAddress + Segment->Size); + if ((Address >= Segment->VirtualAddress) && (Address < TopOfSegment)) { + for ( + Section = MACH_X (MachoGetNextSection) (Context, Segment, NULL); + Section != NULL; + Section = MACH_X (MachoGetNextSection) (Context, Segment, Section) + ) { + TopOfSection = (Section->Address + Section->Size); + if ((Address >= Section->Address) && (Address < TopOfSection)) { + return Section; + } + } + } + } + + return NULL; +} + +UINT32 +MACH_X (MachoGetSymbolTable) ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST_X **SymbolTable, + OUT CONST CHAR8 **StringTable OPTIONAL, + OUT CONST MACH_NLIST_X **LocalSymbols, OPTIONAL + OUT UINT32 *NumLocalSymbols, OPTIONAL + OUT CONST MACH_NLIST_X **ExternalSymbols, OPTIONAL + OUT UINT32 *NumExternalSymbols, OPTIONAL + OUT CONST MACH_NLIST_X **UndefinedSymbols, OPTIONAL + OUT UINT32 *NumUndefinedSymbols OPTIONAL + ) +{ + UINT32 Index; + CONST MACH_NLIST_X *SymTab; + UINT32 NoLocalSymbols; + UINT32 NoExternalSymbols; + UINT32 NoUndefinedSymbols; + + ASSERT (Context != NULL); + ASSERT (SymbolTable != NULL); + MACH_ASSERT_X (Context); + + if (!InternalRetrieveSymtabs (Context)) { + return 0; + } + + if (Context->Symtab->NumSymbols == 0) { + return 0; + } + + SymTab = MACH_X (&Context->SymbolTable->Symbol); + + for (Index = 0; Index < Context->Symtab->NumSymbols; ++Index) { + if (!MACH_X (InternalSymbolIsSane) (Context, &SymTab[Index])) { + return 0; + } + } + + *SymbolTable = MACH_X (&Context->SymbolTable->Symbol); + + if (StringTable != NULL) { + *StringTable = Context->StringTable; + } + + NoLocalSymbols = 0; + NoExternalSymbols = 0; + NoUndefinedSymbols = 0; + + if (Context->DySymtab != NULL) { + NoLocalSymbols = Context->DySymtab->NumLocalSymbols; + NoExternalSymbols = Context->DySymtab->NumExternalSymbols; + NoUndefinedSymbols = Context->DySymtab->NumUndefinedSymbols; + } + + if (NumLocalSymbols != NULL) { + ASSERT (LocalSymbols != NULL); + *NumLocalSymbols = NoLocalSymbols; + if (NoLocalSymbols != 0) { + *LocalSymbols = &SymTab[Context->DySymtab->LocalSymbolsIndex]; + } + } + + if (NumExternalSymbols != NULL) { + ASSERT (ExternalSymbols != NULL); + *NumExternalSymbols = NoExternalSymbols; + if (NoExternalSymbols != 0) { + *ExternalSymbols = &SymTab[Context->DySymtab->ExternalSymbolsIndex]; + } + } + + if (NumUndefinedSymbols != NULL) { + ASSERT (UndefinedSymbols != NULL); + *NumUndefinedSymbols = NoUndefinedSymbols; + if (NoUndefinedSymbols != 0) { + *UndefinedSymbols = &SymTab[Context->DySymtab->UndefinedSymbolsIndex]; + } + } + + return Context->Symtab->NumSymbols; +} + +UINT32 +MACH_X (MachoGetIndirectSymbolTable) ( + IN OUT OC_MACHO_CONTEXT *Context, + OUT CONST MACH_NLIST_X **SymbolTable + ) +{ + UINT32 Index; + + ASSERT (Context != NULL); + ASSERT (SymbolTable != NULL); + MACH_ASSERT_X (Context); + + if (!InternalRetrieveSymtabs (Context)) { + return 0; + } + + for (Index = 0; Index < Context->DySymtab->NumIndirectSymbols; ++Index) { + if ( + !MACH_X (InternalSymbolIsSane) (Context, &(MACH_X (&Context->IndirectSymbolTable->Symbol))[Index]) + ) { + return 0; + } + } + + *SymbolTable = MACH_X (&Context->IndirectSymbolTable->Symbol); + + return Context->DySymtab->NumIndirectSymbols; +} diff --git a/Library/OcMachoLib/Macho32.c b/Library/OcMachoLib/Macho32.c new file mode 100644 index 00000000..1d328460 --- /dev/null +++ b/Library/OcMachoLib/Macho32.c @@ -0,0 +1,22 @@ +/** + 32-bit Mach-O library functions layer. + +Copyright (C) 2020, Goldfish64. 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. + +**/ + +// +// Enable 32-bit mode for library functions. +// +#define MACHO_LIB_32 + +#include "CxxSymbolsX.h" +#include "HeaderX.h" +#include "SymbolsX.h" diff --git a/Library/OcMachoLib/Macho64.c b/Library/OcMachoLib/Macho64.c new file mode 100644 index 00000000..b16d72d7 --- /dev/null +++ b/Library/OcMachoLib/Macho64.c @@ -0,0 +1,17 @@ +/** + 64-bit Mach-O library functions layer. + +Copyright (C) 2020, Goldfish64. 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 "CxxSymbolsX.h" +#include "HeaderX.h" +#include "SymbolsX.h" diff --git a/Library/OcMachoLib/MachoX.h b/Library/OcMachoLib/MachoX.h new file mode 100644 index 00000000..5ace060c --- /dev/null +++ b/Library/OcMachoLib/MachoX.h @@ -0,0 +1,68 @@ +/** + Mach-O library functions layer. + +Copyright (C) 2020, Goldfish64. 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. + +**/ + +#ifndef MACHO_X_INTERNAL_H +#define MACHO_X_INTERNAL_H + +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "OcMachoLibInternal.h" + +// +// 32-bit functions +// +#ifdef MACHO_LIB_32 + +#define MACH_UINT_X UINT32 +#define MACH_HEADER_X MACH_HEADER +#define MACH_SECTION_X MACH_SECTION +#define MACH_SEGMENT_COMMAND_X MACH_SEGMENT_COMMAND +#define MACH_NLIST_X MACH_NLIST + +#define MACH_LOAD_COMMAND_SEGMENT_X MACH_LOAD_COMMAND_SEGMENT + +#define MACH_X(a) a##32 +#define MACH_ASSERT_X(a) ASSERT ((a)->Is32Bit) + +#define MACH_X_TO_UINT32(a) (a) + +// +// 64-bit functions +// +#else + +#define MACH_UINT_X UINT64 +#define MACH_HEADER_X MACH_HEADER_64 +#define MACH_SECTION_X MACH_SECTION_64 +#define MACH_SEGMENT_COMMAND_X MACH_SEGMENT_COMMAND_64 +#define MACH_NLIST_X MACH_NLIST_64 + +#define MACH_LOAD_COMMAND_SEGMENT_X MACH_LOAD_COMMAND_SEGMENT_64 + +#define MACH_X(a) a##64 +#define MACH_ASSERT_X(a) ASSERT (!(a)->Is32Bit) + +#define MACH_X_TO_UINT32(a) (UINT32)(a) +#endif + +#endif // MACHO_X_INTERNAL_H diff --git a/Library/OcMachoLib/OcMachoLib.inf b/Library/OcMachoLib/OcMachoLib.inf index 186b6c28..d36e0f8b 100644 --- a/Library/OcMachoLib/OcMachoLib.inf +++ b/Library/OcMachoLib/OcMachoLib.inf @@ -30,8 +30,14 @@ [Sources] CxxSymbols.c + CxxSymbolsX.h Fat.c Header.c + HeaderX.h OcMachoLibInternal.h + Macho32.c + Macho64.c + MachoX.h Relocations.c Symbols.c + SymbolsX.h diff --git a/Library/OcMachoLib/OcMachoLibInternal.h b/Library/OcMachoLib/OcMachoLibInternal.h index 2f11ac7c..a951b772 100644 --- a/Library/OcMachoLib/OcMachoLibInternal.h +++ b/Library/OcMachoLib/OcMachoLibInternal.h @@ -19,6 +19,8 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include +#define SYM_MAX_NAME_LEN 256U + /** Retrieves the SYMTAB command. @@ -27,7 +29,7 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. @retval NULL NULL is returned on failure. **/ BOOLEAN -InternalRetrieveSymtabs64 ( +InternalRetrieveSymtabs ( IN OUT OC_MACHO_CONTEXT *Context ); @@ -61,7 +63,21 @@ InternalGetLocalRelocationByOffset ( ); /** - Check symbol validity. + Check 32-bit symbol validity. + + @param[in,out] Context Context of the Mach-O. + @param[in] Symbol Symbol from some table. + + @retval TRUE on success. +**/ +BOOLEAN +InternalSymbolIsSane32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST *Symbol + ); + +/** + Check 64-bit symbol validity. @param[in,out] Context Context of the Mach-O. @param[in] Symbol Symbol from some table. @@ -69,9 +85,205 @@ InternalGetLocalRelocationByOffset ( @retval TRUE on success. **/ BOOLEAN -InternalSymbolIsSane ( +InternalSymbolIsSane64 ( IN OUT OC_MACHO_CONTEXT *Context, IN CONST MACH_NLIST_64 *Symbol ); +/** + Retrieves the Mach-O file offset of the address pointed to by a 32-bit Symbol. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Virtual address to retrieve the offset of. + @param[out] FileOffset Pointer the file offset is returned into. + If FALSE is returned, the output is undefined. + @param[out] MaxSize Maximum data safely available from FileOffset. + + @retval 0 0 is returned on failure. + +**/ +BOOLEAN +InternalMachoSymbolGetDirectFileOffset32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Address, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Retrieves the Mach-O file offset of the address pointed to by a 64-bit Symbol. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Virtual address to retrieve the offset of. + @param[out] FileOffset Pointer the file offset is returned into. + If FALSE is returned, the output is undefined. + @param[out] MaxSize Maximum data safely available from FileOffset. + + @retval 0 0 is returned on failure. + +**/ +BOOLEAN +InternalMachoSymbolGetDirectFileOffset64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Returns the 32-bit Mach-O's virtual address space size. + + @param[out] Context Context of the Mach-O. + +**/ +UINT32 +InternalMachoGetVmSize32 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Returns the 64-bit Mach-O's virtual address space size. + + @param[out] Context Context of the Mach-O. + +**/ +UINT32 +InternalMachoGetVmSize64 ( + IN OUT OC_MACHO_CONTEXT *Context + ); + +/** + Retrieves the next 32-bit Load Command of type LoadCommandType. + + @param[in,out] Context Context of the Mach-O. + @param[in] LoadCommandType Type of the Load Command to retrieve. + @param[in] LoadCommand Previous Load Command. + If NULL, the first match is returned. + + @retval NULL NULL is returned on failure. +**/ +MACH_LOAD_COMMAND * +InternalMachoGetNextCommand32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_LOAD_COMMAND_TYPE LoadCommandType, + IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL + ); + +/** + Retrieves the next 64-bit Load Command of type LoadCommandType. + + @param[in,out] Context Context of the Mach-O. + @param[in] LoadCommandType Type of the Load Command to retrieve. + @param[in] LoadCommand Previous Load Command. + If NULL, the first match is returned. + + @retval NULL NULL is returned on failure. +**/ +MACH_LOAD_COMMAND * +InternalMachoGetNextCommand64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_LOAD_COMMAND_TYPE LoadCommandType, + IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL + ); + +/** + Returns a pointer to the 32-bit Mach-O file at the specified virtual address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Virtual address to look up. + @param[out] MaxSize Maximum data safely available from FileOffset. + If NULL is returned, the output is undefined. + +**/ +VOID * +InternalMachoGetFilePointerByAddress32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Address, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Returns a pointer to the 64-bit Mach-O file at the specified virtual address. + + @param[in,out] Context Context of the Mach-O. + @param[in] Address Virtual address to look up. + @param[out] MaxSize Maximum data safely available from FileOffset. + If NULL is returned, the output is undefined. + +**/ +VOID * +InternalMachoGetFilePointerByAddress64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *MaxSize OPTIONAL + ); + +/** + Expand 32-bit Mach-O image to Destination (make segment file sizes equal to vm sizes). + + @param[in] Context Context of the Mach-O. + @param[out] Destination Output buffer. + @param[in] DestinationSize Output buffer maximum size. + @param[in] Strip Output with stripped prelink commands. + + @returns New image size or 0 on failure. + +**/ +UINT32 +InternalMachoExpandImage32 ( + IN OC_MACHO_CONTEXT *Context, + OUT UINT8 *Destination, + IN UINT32 DestinationSize, + IN BOOLEAN Strip + ); + +/** + Expand 64-bit Mach-O image to Destination (make segment file sizes equal to vm sizes). + + @param[in] Context Context of the Mach-O. + @param[out] Destination Output buffer. + @param[in] DestinationSize Output buffer maximum size. + @param[in] Strip Output with stripped prelink commands. + + @returns New image size or 0 on failure. + +**/ +UINT32 +InternalMachoExpandImage64 ( + IN OC_MACHO_CONTEXT *Context, + OUT UINT8 *Destination, + IN UINT32 DestinationSize, + IN BOOLEAN Strip + ); + +/** + Merge 32-bit Mach-O segments into one with lowest protection. + + @param[in,out] Context Context of the Mach-O. + @param[in] Prefix Segment prefix to merge. + + @retval TRUE on success + +**/ +BOOLEAN +InternalMachoMergeSegments32 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Prefix + ); + +/** + Merge 64-bit Mach-O segments into one with lowest protection. + + @param[in,out] Context Context of the Mach-O. + @param[in] Prefix Segment prefix to merge. + + @retval TRUE on success + +**/ +BOOLEAN +InternalMachoMergeSegments64 ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Prefix + ); + #endif // OC_MACHO_LIB_INTERNAL_H_ diff --git a/Library/OcMachoLib/Symbols.c b/Library/OcMachoLib/Symbols.c index bb43d76f..9e3ef192 100644 --- a/Library/OcMachoLib/Symbols.c +++ b/Library/OcMachoLib/Symbols.c @@ -17,704 +17,81 @@ WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. #include #include +#include #include #include #include #include "OcMachoLibInternal.h" -BOOLEAN -InternalSymbolIsSane ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol - ) -{ - ASSERT (Context != NULL); - ASSERT (Symbol != NULL); - - ASSERT (Context->SymbolTable != NULL); - ASSERT (Context->Symtab->NumSymbols > 0); - - ASSERT (((Symbol >= &Context->SymbolTable[0]) - && (Symbol < &Context->SymbolTable[Context->Symtab->NumSymbols])) - || ((Context->DySymtab != NULL) - && (Symbol >= &Context->IndirectSymbolTable[0]) - && (Symbol < &Context->IndirectSymbolTable[Context->DySymtab->NumIndirectSymbols]))); - // - // Symbol->Section is implicitly verified by MachoGetSectionByIndex64() when - // passed to it. - // - if (Symbol->UnifiedName.StringIndex >= Context->Symtab->StringsSize) { - return FALSE; - } - - return TRUE; -} - -/** - Returns whether the symbol's value is a valid address within the Mach-O - referenced to by Context. - - @param[in,out] Context Context of the Mach-O. - @param[in] Symbol Symbol to verify the value of. - -**/ -BOOLEAN -MachoIsSymbolValueInRange64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol - ) -{ - CONST MACH_SEGMENT_COMMAND_64 *Segment; - - if (MachoSymbolIsLocalDefined (Context, Symbol)) { - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - if ((Symbol->Value >= Segment->VirtualAddress) - && (Symbol->Value < (Segment->VirtualAddress + Segment->Size))) { - return TRUE; - } - } - - return FALSE; - } - - return TRUE; -} - -/** - Returns whether Symbol describes a section type. - - @param[in] Symbol Symbol to evaluate. - -**/ -STATIC -BOOLEAN -InternalSymbolIsSectionType ( - IN CONST MACH_NLIST_64 *Symbol - ) -{ - ASSERT (Symbol != NULL); - - if ((Symbol->Type & MACH_N_TYPE_STAB) != 0) { - switch (Symbol->Type) { - // - // Labeled as MACH_N_sect in stab.h - // - case MACH_N_FUN: - case MACH_N_STSYM: - case MACH_N_LCSYM: - case MACH_N_BNSYM: - case MACH_N_SLINE: - case MACH_N_ENSYM: - case MACH_N_SO: - case MACH_N_SOL: - case MACH_N_ENTRY: - case MACH_N_ECOMM: - case MACH_N_ECOML: - // - // These are labeled as NO_SECT in stab.h, but they are actually - // section-based on OS X. We must mark them as such so they get - // relocated. - // - case MACH_N_RBRAC: - case MACH_N_LBRAC: - { - return TRUE; - } - - default: - { - break; - } - } - } else if ((Symbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_SECT) { - return TRUE; - } - - return FALSE; -} - -/** - Returns whether Symbol describes a section. - - @param[in] Symbol Symbol to evaluate. - -**/ -BOOLEAN -MachoSymbolIsSection ( - IN CONST MACH_NLIST_64 *Symbol - ) -{ - ASSERT (Symbol != NULL); - return (InternalSymbolIsSectionType (Symbol) && (Symbol->Section != NO_SECT)); -} - -/** - Returns whether Symbol is defined. - - @param[in] Symbol Symbol to evaluate. - -**/ -BOOLEAN -MachoSymbolIsDefined ( - IN CONST MACH_NLIST_64 *Symbol - ) -{ - ASSERT (Symbol != NULL); - - return (((Symbol->Type & MACH_N_TYPE_STAB) == 0) - && (((Symbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_ABS) - || InternalSymbolIsSectionType (Symbol))); -} - -/** - Returns whether Symbol is defined locally. - - @param[in,out] Context Context of the Mach-O. - @param[in] Symbol Symbol to evaluate. - -**/ -BOOLEAN -MachoSymbolIsLocalDefined ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol - ) -{ - CONST MACH_DYSYMTAB_COMMAND *DySymtab; - CONST MACH_NLIST_64 *UndefinedSymbols; - CONST MACH_NLIST_64 *UndefinedSymbolsTop; - CONST MACH_NLIST_64 *IndirectSymbols; - CONST MACH_NLIST_64 *IndirectSymbolsTop; - - ASSERT (Context != NULL); - ASSERT (Symbol != NULL); - - DySymtab = Context->DySymtab; - ASSERT (Context->SymbolTable != NULL); - - if ((DySymtab == NULL) || (DySymtab->NumUndefinedSymbols == 0)) { - return TRUE; - } - // - // The symbol must have been declared locally prior to solving. As there is - // no information on whether the symbol has been solved explicitely, check - // its storage location for Undefined or Indirect. - // - UndefinedSymbols = &Context->SymbolTable[DySymtab->UndefinedSymbolsIndex]; - UndefinedSymbolsTop = &UndefinedSymbols[DySymtab->NumUndefinedSymbols]; - - if ((Symbol >= UndefinedSymbols) && (Symbol < UndefinedSymbolsTop)) { - return FALSE; - } - - IndirectSymbols = Context->IndirectSymbolTable; - IndirectSymbolsTop = &IndirectSymbols[DySymtab->NumIndirectSymbols]; - - if ((Symbol >= IndirectSymbols) && (Symbol < IndirectSymbolsTop)) { - return FALSE; - } - - return MachoSymbolIsDefined (Symbol); -} - -/** - Retrieves a symbol by its index. - - @param[in] Context Context of the Mach-O. - @param[in] Index Index of the symbol to locate. - - @retval NULL NULL is returned on failure. - -**/ -MACH_NLIST_64 * -MachoGetSymbolByIndex64 ( +MACH_NLIST_ANY * +MachoGetSymbolByIndex ( IN OUT OC_MACHO_CONTEXT *Context, IN UINT32 Index ) -{ - MACH_NLIST_64 *Symbol; - - ASSERT (Context != NULL); - - if (!InternalRetrieveSymtabs64 (Context)) { - return NULL; - } - - ASSERT (Context->SymbolTable != NULL); - - if (Index < Context->Symtab->NumSymbols) { - Symbol = &Context->SymbolTable[Index]; - if (InternalSymbolIsSane (Context, Symbol)) { - return Symbol; - } - } - - return NULL; -} - -/** - Retrieves Symbol's name. - - @param[in,out] Context Context of the Mach-O. - @param[in] Symbol Symbol to retrieve the name of. - - @retval NULL NULL is returned on failure. - -**/ -CONST CHAR8 * -MachoGetSymbolName64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol - ) { ASSERT (Context != NULL); - ASSERT (Symbol != NULL); - ASSERT (Context->SymbolTable != NULL); - ASSERT (Context->Symtab->StringsSize > Symbol->UnifiedName.StringIndex); - - return (Context->StringTable + Symbol->UnifiedName.StringIndex); + return Context->Is32Bit ? + (MACH_NLIST_ANY *) MachoGetSymbolByIndex32 (Context, Index) : + (MACH_NLIST_ANY *) MachoGetSymbolByIndex64 (Context, Index); } -/** - Retrieves Symbol's name. - - @param[in,out] Context Context of the Mach-O. - @param[in] Symbol Indirect symbol to retrieve the name of. - - @retval NULL NULL is returned on failure. - -**/ CONST CHAR8 * -MachoGetIndirectSymbolName64 ( +MachoGetSymbolName ( IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol - ) -{ - ASSERT (Context != NULL); - ASSERT (Symbol != NULL); - - ASSERT (Context->SymbolTable != NULL); - - if ((Symbol->Type & MACH_N_TYPE_STAB) != 0 - || (Symbol->Type & MACH_N_TYPE_TYPE) != MACH_N_TYPE_INDR) { - return NULL; - } - - if (Context->Symtab->StringsSize <= Symbol->Value) { - return NULL; - } - - return (Context->StringTable + Symbol->Value); -} - -/** - Retrieves a symbol by its value. - - @param[in] Context Context of the Mach-O. - @param[in] Value Value of the symbol to locate. - - @retval NULL NULL is returned on failure. - -**/ -STATIC -MACH_NLIST_64 * -InternalGetSymbolByValue ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Value - ) -{ - UINT32 Index; - - ASSERT (Context->SymbolTable != NULL); - ASSERT (Context->Symtab != NULL); - - for (Index = 0; Index < Context->Symtab->NumSymbols; ++Index) { - if (Context->SymbolTable[Index].Value == Value) { - return &Context->SymbolTable[Index]; - } - } - - return NULL; -} - -BOOLEAN -InternalGetSymbolByExternRelocationOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT MACH_NLIST_64 **Symbol - ) -{ - CONST MACH_RELOCATION_INFO *Relocation; - - ASSERT (Context != NULL); - - Relocation = InternalGetExternRelocationByOffset (Context, Address); - if (Relocation != NULL) { - *Symbol = MachoGetSymbolByIndex64 (Context, Relocation->SymbolNumber); - return TRUE; - } - - return FALSE; -} - -/** - Retrieves the symbol referenced by the extern Relocation targeting Address. - - @param[in,out] Context Context of the Mach-O. - @param[in] Address Address to search for. - @param[out] Symbol Buffer to output the symbol referenced by the - Relocation into. The output is undefined when FALSE - is returned. May be NULL. - - @returns Whether the Relocation exists. - -**/ -BOOLEAN -MachoGetSymbolByExternRelocationOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT MACH_NLIST_64 **Symbol - ) -{ - if (Address >= MachoGetFileSize (Context)) { - return FALSE; - } - - return InternalGetSymbolByExternRelocationOffset64 ( - Context, - Address, - Symbol - ); -} - -/** - Retrieves the symbol referenced by the Relocation targeting Address. - - @param[in,out] Context Context of the Mach-O. - @param[in] Address Address to search for. - @param[out] Symbol Buffer to output the symbol referenced by the - Relocation into. The output is undefined when FALSE - is returned. May be NULL. - - @returns Whether the Relocation exists. - -**/ -BOOLEAN -MachoGetSymbolByRelocationOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT MACH_NLIST_64 **Symbol - ) -{ - BOOLEAN Result; - CONST MACH_RELOCATION_INFO *Relocation; - CONST UINT64 *Data; - MACH_NLIST_64 *Sym; - UINT64 AddressTop; - - VOID *Tmp; - - ASSERT (Context != NULL); - - Result = OcOverflowAddU64 (Address, sizeof (UINT64), &AddressTop); - if (Result || AddressTop > MachoGetFileSize (Context)) { - return FALSE; - } - - Result = InternalGetSymbolByExternRelocationOffset64 ( - Context, - Address, - Symbol - ); - if (Result) { - return TRUE; - } - - Relocation = InternalGetLocalRelocationByOffset (Context, Address); - if (Relocation != NULL) { - Sym = NULL; - - Tmp = (VOID *)((UINTN)Context->MachHeader + (UINTN)Address); - - if (OC_TYPE_ALIGNED (UINT64, Tmp)) { - Data = (UINT64 *)Tmp; - - // FIXME: Only C++ symbols. - Sym = InternalGetSymbolByValue (Context, *Data); - if ((Sym != NULL) && !InternalSymbolIsSane (Context, Sym)) { - Sym = NULL; - } - } - - *Symbol = Sym; - return TRUE; - } - - return FALSE; -} - -/** - Retrieves a symbol by its name. - - @param[in] Context Context of the Mach-O. - @param[in] SymbolTable Symbol Table of the Mach-O. - @param[in] NumberOfSymbols Number of symbols in SymbolTable. - @param[in] Name Name of the symbol to locate. - - @retval NULL NULL is returned on failure. - -**/ -STATIC -MACH_NLIST_64 * -InternalGetLocalDefinedSymbolByNameWorker ( - IN OUT OC_MACHO_CONTEXT *Context, - IN MACH_NLIST_64 *SymbolTable, - IN UINT32 NumberOfSymbols, - IN CONST CHAR8 *Name - ) -{ - UINT32 Index; - CONST CHAR8 *TmpName; - - ASSERT (SymbolTable != NULL); - ASSERT (Name != NULL); - - for (Index = 0; Index < NumberOfSymbols; ++Index) { - if (!InternalSymbolIsSane (Context, &SymbolTable[Index])) { - break; - } - - if (!MachoSymbolIsDefined (&SymbolTable[Index])) { - continue; - } - - TmpName = MachoGetSymbolName64 (Context, &SymbolTable[Index]); - if (AsciiStrCmp (Name, TmpName) == 0) { - return &SymbolTable[Index]; - } - } - - return NULL; -} - -/** - Retrieves a locally defined symbol by its name. - - @param[in,out] Context Context of the Mach-O. - @param[in] Name Name of the symbol to locate. - -**/ -MACH_NLIST_64 * -MachoGetLocalDefinedSymbolByName ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST CHAR8 *Name + IN CONST MACH_NLIST_ANY *Symbol ) { - MACH_NLIST_64 *SymbolTable; - CONST MACH_DYSYMTAB_COMMAND *DySymtab; - MACH_NLIST_64 *Symbol; - ASSERT (Context != NULL); - ASSERT (Name != NULL); - - if (!InternalRetrieveSymtabs64 (Context)) { - return NULL; - } - - SymbolTable = Context->SymbolTable; - ASSERT (SymbolTable != NULL); - - DySymtab = Context->DySymtab; - - if (DySymtab != NULL) { - Symbol = InternalGetLocalDefinedSymbolByNameWorker ( - Context, - &SymbolTable[DySymtab->LocalSymbolsIndex], - DySymtab->NumLocalSymbols, - Name - ); - if (Symbol == NULL) { - Symbol = InternalGetLocalDefinedSymbolByNameWorker ( - Context, - &SymbolTable[DySymtab->ExternalSymbolsIndex], - DySymtab->NumExternalSymbols, - Name - ); - } - } else { - ASSERT (Context->Symtab != NULL); - Symbol = InternalGetLocalDefinedSymbolByNameWorker ( - Context, - SymbolTable, - Context->Symtab->NumSymbols, - Name - ); - } - return Symbol; + return Context->Is32Bit ? + MachoGetSymbolName32 (Context, &Symbol->Symbol32) : + MachoGetSymbolName64 (Context, &Symbol->Symbol64); } BOOLEAN -MachoRelocateSymbol64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 LinkAddress, - IN OUT MACH_NLIST_64 *Symbol +MachoIsSymbolValueInRange ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_ANY *Symbol ) { - CONST MACH_SECTION_64 *Section; - UINT64 Value; - BOOLEAN Result; - ASSERT (Context != NULL); - ASSERT (Symbol != NULL); - - // - // Symbols are relocated when they describe sections. - // - if (MachoSymbolIsSection (Symbol)) { - Section = MachoGetSectionByIndex64 (Context, (Symbol->Section - 1)); - if (Section == NULL) { - return FALSE; - } - - Value = ALIGN_VALUE ( - (Section->Address + LinkAddress), - (UINT64)(1U << Section->Alignment) - ); - Value -= Section->Address; - // - // The overflow arithmetic functions are not used as an overflow within the - // ALIGN_VALUE addition and a subsequent "underflow" of the section address - // subtraction is valid, hence just verify whether the final result - // overflew. - // - if (Value < LinkAddress) { - return FALSE; - } - - Result = OcOverflowAddU64 (Symbol->Value, Value, &Value); - if (Result) { - return FALSE; - } - Symbol->Value = Value; - } - - return TRUE; + return Context->Is32Bit ? + MachoIsSymbolValueInRange32 (Context, &Symbol->Symbol32) : + MachoIsSymbolValueInRange64 (Context, &Symbol->Symbol64); } BOOLEAN -MachoSymbolGetFileOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN CONST MACH_NLIST_64 *Symbol, - OUT UINT32 *FileOffset, - OUT UINT32 *MaxSize OPTIONAL +MachoSymbolGetFileOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_ANY *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL ) { - UINT64 Offset; - UINT64 Base; - UINT64 Size; - MACH_SEGMENT_COMMAND_64 *Segment; - MACH_SECTION_64 *Section; - ASSERT (Context != NULL); - ASSERT (Symbol != NULL); - ASSERT (FileOffset != NULL); - - if (Symbol->Section == NO_SECT) { - return FALSE; - } - - Section = MachoGetSectionByIndex64 ( - Context, - (Symbol->Section - 1) - ); - if (Section == NULL || Section->Size == 0) { - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - if ((Symbol->Value >= Segment->VirtualAddress) - && (Symbol->Value < (Segment->VirtualAddress + Segment->Size))) { - break; - } - } - - if (Segment == NULL) { - return FALSE; - } - Offset = Symbol->Value - Segment->VirtualAddress; - Base = Segment->FileOffset; - Size = Segment->Size; - } else { - if (Symbol->Value < Section->Address) { - return FALSE; - } - - Offset = Symbol->Value - Section->Address; - Base = Section->Offset; - Size = Section->Size; - if (Offset > Section->Size) { - return FALSE; - } - } - - *FileOffset = (UINT32) (Base - Context->ContainerOffset + Offset); - - if (MaxSize != NULL) { - *MaxSize = (UINT32) (Size - Offset); - } - - return TRUE; + return Context->Is32Bit ? + MachoSymbolGetFileOffset32 (Context, &Symbol->Symbol32, FileOffset, MaxSize) : + MachoSymbolGetFileOffset64 (Context, &Symbol->Symbol64, FileOffset, MaxSize); } BOOLEAN -MachoSymbolGetDirectFileOffset64 ( - IN OUT OC_MACHO_CONTEXT *Context, - IN UINT64 Address, - OUT UINT32 *FileOffset, - OUT UINT32 *MaxSize OPTIONAL +MachoSymbolGetDirectFileOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT64 Address, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL ) { - UINT64 Offset; - UINT64 Base; - UINT64 Size; - MACH_SEGMENT_COMMAND_64 *Segment; - ASSERT (Context != NULL); - ASSERT (FileOffset != NULL); - - for ( - Segment = MachoGetNextSegment64 (Context, NULL); - Segment != NULL; - Segment = MachoGetNextSegment64 (Context, Segment) - ) { - if ((Address >= Segment->VirtualAddress) - && (Address < (Segment->VirtualAddress + Segment->Size))) { - break; - } - } - - if (Segment == NULL) { - return FALSE; - } - - Offset = Address - Segment->VirtualAddress; - Base = Segment->FileOffset; - Size = Segment->Size; - - *FileOffset = (UINT32) (Base - Context->ContainerOffset + Offset); - - if (MaxSize != NULL) { - *MaxSize = (UINT32) (Size - Offset); + if (Context->Is32Bit) { + ASSERT (Address < MAX_UINT32); } - return TRUE; + return Context->Is32Bit ? + InternalMachoSymbolGetDirectFileOffset32 (Context, (UINT32) Address, FileOffset, MaxSize) : + InternalMachoSymbolGetDirectFileOffset64 (Context, Address, FileOffset, MaxSize); } diff --git a/Library/OcMachoLib/SymbolsX.h b/Library/OcMachoLib/SymbolsX.h new file mode 100644 index 00000000..6e81da67 --- /dev/null +++ b/Library/OcMachoLib/SymbolsX.h @@ -0,0 +1,643 @@ +/** @file + Provides services for symbols. + +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 "MachoX.h" + +/** + Returns whether Symbol describes a section type. + + @param[in] Symbol Symbol to evaluate. + +**/ +STATIC +BOOLEAN +InternalSymbolIsSectionType ( + IN CONST MACH_NLIST_X *Symbol + ) +{ + ASSERT (Symbol != NULL); + + if ((Symbol->Type & MACH_N_TYPE_STAB) != 0) { + switch (Symbol->Type) { + // + // Labeled as MACH_N_sect in stab.h + // + case MACH_N_FUN: + case MACH_N_STSYM: + case MACH_N_LCSYM: + case MACH_N_BNSYM: + case MACH_N_SLINE: + case MACH_N_ENSYM: + case MACH_N_SO: + case MACH_N_SOL: + case MACH_N_ENTRY: + case MACH_N_ECOMM: + case MACH_N_ECOML: + // + // These are labeled as NO_SECT in stab.h, but they are actually + // section-based on OS X. We must mark them as such so they get + // relocated. + // + case MACH_N_RBRAC: + case MACH_N_LBRAC: + { + return TRUE; + } + + default: + { + break; + } + } + } else if ((Symbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_SECT) { + return TRUE; + } + + return FALSE; +} + +/** + Retrieves a symbol by its value. + + @param[in] Context Context of the Mach-O. + @param[in] Value Value of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +STATIC +MACH_NLIST_X * +MACH_X (InternalGetSymbolByValue) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Value + ) +{ + UINT32 Index; + + ASSERT (Context->SymbolTable != NULL); + ASSERT (Context->Symtab != NULL); + + for (Index = 0; Index < Context->Symtab->NumSymbols; ++Index) { + if ((MACH_X (&Context->SymbolTable->Symbol))[Index].Value == Value) { + return &(MACH_X (&Context->SymbolTable->Symbol))[Index]; + } + } + + return NULL; +} + +STATIC +BOOLEAN +InternalGetSymbolByExternRelocationOffset ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Address, + OUT MACH_NLIST_X **Symbol + ) +{ + CONST MACH_RELOCATION_INFO *Relocation; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + Relocation = InternalGetExternRelocationByOffset (Context, Address); + if (Relocation != NULL) { + *Symbol = MACH_X (MachoGetSymbolByIndex) (Context, Relocation->SymbolNumber); + return TRUE; + } + + return FALSE; +} + +/** + Retrieves a symbol by its name. + + @param[in] Context Context of the Mach-O. + @param[in] SymbolTable Symbol Table of the Mach-O. + @param[in] NumberOfSymbols Number of symbols in SymbolTable. + @param[in] Name Name of the symbol to locate. + + @retval NULL NULL is returned on failure. + +**/ +STATIC +MACH_NLIST_X * +InternalGetLocalDefinedSymbolByNameWorker ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_NLIST_X *SymbolTable, + IN UINT32 NumberOfSymbols, + IN CONST CHAR8 *Name + ) +{ + UINT32 Index; + CONST CHAR8 *TmpName; + + ASSERT (SymbolTable != NULL); + ASSERT (Name != NULL); + + for (Index = 0; Index < NumberOfSymbols; ++Index) { + if (!MACH_X (InternalSymbolIsSane) (Context, &SymbolTable[Index])) { + break; + } + + if (!MACH_X (MachoSymbolIsDefined) (&SymbolTable[Index])) { + continue; + } + + TmpName = MACH_X (MachoGetSymbolName) (Context, &SymbolTable[Index]); + if (AsciiStrCmp (Name, TmpName) == 0) { + return &SymbolTable[Index]; + } + } + + return NULL; +} + +BOOLEAN +MACH_X (InternalSymbolIsSane) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Symbol + ) +{ + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + MACH_ASSERT_X (Context); + + ASSERT (Context->SymbolTable != NULL); + ASSERT (Context->Symtab->NumSymbols > 0); + + ASSERT (((Symbol >= &(MACH_X (&Context->SymbolTable->Symbol))[0]) + && (Symbol < &(MACH_X (&Context->SymbolTable->Symbol))[Context->Symtab->NumSymbols])) + || ((Context->DySymtab != NULL) + && (Symbol >= &(MACH_X (&Context->IndirectSymbolTable->Symbol))[0]) + && (Symbol < &(MACH_X (&Context->IndirectSymbolTable->Symbol))[Context->DySymtab->NumIndirectSymbols]))); + // + // Symbol->Section is implicitly verified by MachoGetSectionByIndex() when + // passed to it. + // + if (Symbol->UnifiedName.StringIndex >= Context->Symtab->StringsSize) { + return FALSE; + } + + return TRUE; +} + +BOOLEAN +MACH_X (InternalMachoSymbolGetDirectFileOffset) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Address, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ) +{ + MACH_UINT_X Offset; + MACH_UINT_X Base; + MACH_UINT_X Size; + MACH_SEGMENT_COMMAND_X *Segment; + + ASSERT (Context != NULL); + ASSERT (FileOffset != NULL); + MACH_ASSERT_X (Context); + + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + if ((Address >= Segment->VirtualAddress) + && (Address < (Segment->VirtualAddress + Segment->Size))) { + break; + } + } + + if (Segment == NULL) { + return FALSE; + } + + Offset = Address - Segment->VirtualAddress; + Base = Segment->FileOffset; + Size = Segment->Size; + + *FileOffset = MACH_X_TO_UINT32 (Base - Context->ContainerOffset + Offset); + + if (MaxSize != NULL) { + *MaxSize = MACH_X_TO_UINT32 (Size - Offset); + } + + return TRUE; +} + +BOOLEAN +MACH_X (MachoIsSymbolValueInRange) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Symbol + ) +{ + CONST MACH_SEGMENT_COMMAND_X *Segment; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + MACH_ASSERT_X (Context); + + if (MACH_X (MachoSymbolIsLocalDefined) (Context, Symbol)) { + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + if ((Symbol->Value >= Segment->VirtualAddress) + && (Symbol->Value < (Segment->VirtualAddress + Segment->Size))) { + return TRUE; + } + } + + return FALSE; + } + + return TRUE; +} + +BOOLEAN +MACH_X (MachoSymbolIsSection) ( + IN CONST MACH_NLIST_X *Symbol + ) +{ + ASSERT (Symbol != NULL); + return (InternalSymbolIsSectionType (Symbol) && (Symbol->Section != NO_SECT)); +} + +BOOLEAN +MACH_X (MachoSymbolIsDefined) ( + IN CONST MACH_NLIST_X *Symbol + ) +{ + ASSERT (Symbol != NULL); + + return (((Symbol->Type & MACH_N_TYPE_STAB) == 0) + && (((Symbol->Type & MACH_N_TYPE_TYPE) == MACH_N_TYPE_ABS) + || InternalSymbolIsSectionType (Symbol))); +} + +BOOLEAN +MACH_X (MachoSymbolIsLocalDefined) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Symbol + ) +{ + CONST MACH_DYSYMTAB_COMMAND *DySymtab; + CONST MACH_NLIST_X *UndefinedSymbols; + CONST MACH_NLIST_X *UndefinedSymbolsTop; + CONST MACH_NLIST_X *IndirectSymbols; + CONST MACH_NLIST_X *IndirectSymbolsTop; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + MACH_ASSERT_X (Context); + + DySymtab = Context->DySymtab; + ASSERT (Context->SymbolTable != NULL); + + if ((DySymtab == NULL) || (DySymtab->NumUndefinedSymbols == 0)) { + return TRUE; + } + // + // The symbol must have been declared locally prior to solving. As there is + // no information on whether the symbol has been solved explicitely, check + // its storage location for Undefined or Indirect. + // + UndefinedSymbols = &(MACH_X (&Context->SymbolTable->Symbol))[DySymtab->UndefinedSymbolsIndex]; + UndefinedSymbolsTop = &UndefinedSymbols[DySymtab->NumUndefinedSymbols]; + + if ((Symbol >= UndefinedSymbols) && (Symbol < UndefinedSymbolsTop)) { + return FALSE; + } + + IndirectSymbols = MACH_X (&Context->IndirectSymbolTable->Symbol); + IndirectSymbolsTop = &IndirectSymbols[DySymtab->NumIndirectSymbols]; + + if ((Symbol >= IndirectSymbols) && (Symbol < IndirectSymbolsTop)) { + return FALSE; + } + + return MACH_X (MachoSymbolIsDefined) (Symbol); +} + +MACH_NLIST_X * +MACH_X (MachoGetSymbolByIndex) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN UINT32 Index + ) +{ + MACH_NLIST_X *Symbol; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + if (!InternalRetrieveSymtabs (Context)) { + return NULL; + } + + ASSERT (Context->SymbolTable != NULL); + + if (Index < Context->Symtab->NumSymbols) { + Symbol = &(MACH_X (&Context->SymbolTable->Symbol))[Index]; + if (MACH_X (InternalSymbolIsSane) (Context, Symbol)) { + return Symbol; + } + } + + return NULL; +} + +CONST CHAR8 * +MACH_X (MachoGetSymbolName) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Symbol + ) +{ + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + MACH_ASSERT_X (Context); + + ASSERT (Context->SymbolTable != NULL); + ASSERT (Context->Symtab->StringsSize > Symbol->UnifiedName.StringIndex); + + return (Context->StringTable + Symbol->UnifiedName.StringIndex); +} + +CONST CHAR8 * +MACH_X (MachoGetIndirectSymbolName) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Symbol + ) +{ + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + MACH_ASSERT_X (Context); + + ASSERT (Context->SymbolTable != NULL); + + if ((Symbol->Type & MACH_N_TYPE_STAB) != 0 + || (Symbol->Type & MACH_N_TYPE_TYPE) != MACH_N_TYPE_INDR) { + return NULL; + } + + if (Context->Symtab->StringsSize <= Symbol->Value) { + return NULL; + } + + return (Context->StringTable + Symbol->Value); +} + +BOOLEAN +MACH_X (MachoGetSymbolByExternRelocationOffset) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Address, + OUT MACH_NLIST_X **Symbol + ) +{ + if (Address >= MachoGetFileSize (Context)) { + return FALSE; + } + + return InternalGetSymbolByExternRelocationOffset ( + Context, + Address, + Symbol + ); +} + +BOOLEAN +MACH_X (MachoGetSymbolByRelocationOffset) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X Address, + OUT MACH_NLIST_X **Symbol + ) +{ + BOOLEAN Result; + CONST MACH_RELOCATION_INFO *Relocation; + CONST MACH_UINT_X *Data; + MACH_NLIST_X *Sym; + MACH_UINT_X AddressTop; + + VOID *Tmp; + + ASSERT (Context != NULL); + MACH_ASSERT_X (Context); + + Result = MACH_X (OcOverflowAddU) (Address, sizeof (MACH_UINT_X), &AddressTop); + if (Result || AddressTop > MachoGetFileSize (Context)) { + return FALSE; + } + + Result = InternalGetSymbolByExternRelocationOffset ( + Context, + Address, + Symbol + ); + if (Result) { + return TRUE; + } + + Relocation = InternalGetLocalRelocationByOffset (Context, Address); + if (Relocation != NULL) { + Sym = NULL; + + Tmp = (VOID *)((UINTN)Context->MachHeader + (UINTN)Address); + + if (OC_TYPE_ALIGNED (MACH_UINT_X, Tmp)) { + Data = (MACH_UINT_X *)Tmp; + + // FIXME: Only C++ symbols. + Sym = MACH_X (InternalGetSymbolByValue) (Context, *Data); + if ((Sym != NULL) && !MACH_X (InternalSymbolIsSane) (Context, Sym)) { + Sym = NULL; + } + } + + *Symbol = Sym; + return TRUE; + } + + return FALSE; +} + +MACH_NLIST_X * +MACH_X (MachoGetLocalDefinedSymbolByName) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST CHAR8 *Name + ) +{ + MACH_NLIST_X *SymbolTable; + CONST MACH_DYSYMTAB_COMMAND *DySymtab; + MACH_NLIST_X *Symbol; + + ASSERT (Context != NULL); + ASSERT (Name != NULL); + MACH_ASSERT_X (Context); + + if (!InternalRetrieveSymtabs (Context)) { + return NULL; + } + + ASSERT (Context->SymbolTable != NULL); + SymbolTable = MACH_X (&Context->SymbolTable->Symbol); + + DySymtab = Context->DySymtab; + + if (DySymtab != NULL) { + Symbol = InternalGetLocalDefinedSymbolByNameWorker ( + Context, + &SymbolTable[DySymtab->LocalSymbolsIndex], + DySymtab->NumLocalSymbols, + Name + ); + if (Symbol == NULL) { + Symbol = InternalGetLocalDefinedSymbolByNameWorker ( + Context, + &SymbolTable[DySymtab->ExternalSymbolsIndex], + DySymtab->NumExternalSymbols, + Name + ); + } + } else { + ASSERT (Context->Symtab != NULL); + Symbol = InternalGetLocalDefinedSymbolByNameWorker ( + Context, + SymbolTable, + Context->Symtab->NumSymbols, + Name + ); + } + + return Symbol; +} + +BOOLEAN +MACH_X (MachoRelocateSymbol) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN MACH_UINT_X LinkAddress, + IN OUT MACH_NLIST_X *Symbol + ) +{ + CONST MACH_SECTION_X *Section; + MACH_UINT_X Value; + BOOLEAN Result; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + MACH_ASSERT_X (Context); + + // + // Symbols are relocated when they describe sections. + // + if (MACH_X (MachoSymbolIsSection) (Symbol)) { + Section = MACH_X (MachoGetSectionByIndex) (Context, (Symbol->Section - 1)); + if (Section == NULL) { + return FALSE; + } + + Value = ALIGN_VALUE ( + (Section->Address + LinkAddress), + (MACH_UINT_X)(1U << Section->Alignment) + ); + Value -= Section->Address; + + // + // The overflow arithmetic functions are not used as an overflow within the + // ALIGN_VALUE addition and a subsequent "underflow" of the section address + // subtraction is valid, hence just verify whether the final result + // overflew. + // + if (Value < LinkAddress) { + return FALSE; + } + + Result = MACH_X (OcOverflowAddU) (Symbol->Value, Value, &Value); + if (Result) { + return FALSE; + } + + Symbol->Value = Value; + } + + return TRUE; +} + +BOOLEAN +MACH_X (MachoSymbolGetFileOffset) ( + IN OUT OC_MACHO_CONTEXT *Context, + IN CONST MACH_NLIST_X *Symbol, + OUT UINT32 *FileOffset, + OUT UINT32 *MaxSize OPTIONAL + ) +{ + MACH_UINT_X Offset; + MACH_UINT_X Base; + MACH_UINT_X Size; + MACH_SEGMENT_COMMAND_X *Segment; + MACH_SECTION_X *Section; + + ASSERT (Context != NULL); + ASSERT (Symbol != NULL); + ASSERT (FileOffset != NULL); + MACH_ASSERT_X (Context); + + if (Symbol->Section == NO_SECT) { + return FALSE; + } + + Section = MACH_X (MachoGetSectionByIndex) ( + Context, + (Symbol->Section - 1) + ); + if (Section == NULL || Section->Size == 0) { + for ( + Segment = MACH_X (MachoGetNextSegment) (Context, NULL); + Segment != NULL; + Segment = MACH_X (MachoGetNextSegment) (Context, Segment) + ) { + if ((Symbol->Value >= Segment->VirtualAddress) + && (Symbol->Value < (Segment->VirtualAddress + Segment->Size))) { + break; + } + } + + if (Segment == NULL) { + return FALSE; + } + + Offset = Symbol->Value - Segment->VirtualAddress; + Base = Segment->FileOffset; + Size = Segment->Size; + } else { + if (Symbol->Value < Section->Address) { + return FALSE; + } + + Offset = Symbol->Value - Section->Address; + Base = Section->Offset; + Size = Section->Size; + if (Offset > Section->Size) { + return FALSE; + } + } + + *FileOffset = MACH_X_TO_UINT32 (Base - Context->ContainerOffset + Offset); + + if (MaxSize != NULL) { + *MaxSize = MACH_X_TO_UINT32 (Size - Offset); + } + + return TRUE; +} diff --git a/Platform/OpenCore/OpenCoreKernel.c b/Platform/OpenCore/OpenCoreKernel.c index 6e2fabd9..b60e13a5 100644 --- a/Platform/OpenCore/OpenCoreKernel.c +++ b/Platform/OpenCore/OpenCoreKernel.c @@ -200,323 +200,300 @@ OcKernelConfigureCapabilities ( } STATIC -EFI_STATUS -OcKernelLoadKextsAndReserve ( - IN EFI_FILE_PROTOCOL *RootFile, - IN OC_STORAGE_CONTEXT *Storage, - IN OC_GLOBAL_CONFIG *Config, - IN KERNEL_CACHE_TYPE CacheType, - OUT UINT32 *ReservedExeSize, - OUT UINT32 *ReservedInfoSize, - OUT UINT32 *NumReservedKexts +VOID +OcKernelLoadAndReserveKext ( + IN OC_KERNEL_ADD_ENTRY *Kext, + IN UINT32 Index, + IN BOOLEAN IsForced, + IN EFI_FILE_PROTOCOL *RootFile, + IN OC_STORAGE_CONTEXT *Storage, + IN OC_GLOBAL_CONFIG *Config, + IN KERNEL_CACHE_TYPE CacheType, + IN BOOLEAN Is32Bit, + IN OUT UINT32 *ReservedExeSize, + IN OUT UINT32 *ReservedInfoSize, + IN OUT UINT32 *NumReservedKexts ) { EFI_STATUS Status; - UINT32 Index; CHAR8 *Identifier; CHAR8 *BundlePath; CHAR8 *Comment; + CONST CHAR8 *Arch; CHAR8 *PlistPath; CHAR8 *ExecutablePath; CHAR16 FullPath[OC_STORAGE_SAFE_PATH_MAX]; - OC_KERNEL_ADD_ENTRY *Kext; - *ReservedInfoSize = PRELINK_INFO_RESERVE_SIZE; - *ReservedExeSize = 0; - *NumReservedKexts = 0; + if (!Kext->Enabled) { + return; + } // - // Process system kexts to be force injected. + // Free existing data if present, but only for forced kexts. + // Injected kexts will never change. // - for (Index = 0; Index < Config->Kernel.Force.Count; Index++) { - Kext = Config->Kernel.Force.Values[Index]; - - if (!Kext->Enabled) { - continue; + if (IsForced && Kext->PlistData != NULL) { + FreePool (Kext->PlistData); + Kext->PlistDataSize = 0; + Kext->PlistData = NULL; + + if (Kext->ImageData != NULL) { + FreePool (Kext->ImageData); + Kext->ImageDataSize = 0; + Kext->ImageData = NULL; } + } - // - // Free existing data if present. - // - if (Kext->PlistData != NULL) { - FreePool (Kext->PlistData); - Kext->PlistDataSize = 0; - Kext->PlistData = NULL; - - if (Kext->ImageData != NULL) { - FreePool (Kext->ImageData); - Kext->ImageDataSize = 0; - Kext->ImageData = NULL; - } - } + Identifier = OC_BLOB_GET (&Kext->Identifier); + BundlePath = OC_BLOB_GET (&Kext->BundlePath); + Comment = OC_BLOB_GET (&Kext->Comment); + Arch = OC_BLOB_GET (&Kext->Arch); + PlistPath = OC_BLOB_GET (&Kext->PlistPath); + if (BundlePath[0] == '\0' || PlistPath[0] == '\0' || (IsForced && Identifier[0] == '\0')) { + DEBUG (( + DEBUG_ERROR, + "OC: %s kext %u (%a) has invalid info\n", + IsForced ? L"Forced" : L"Injected", + Index, + Comment + )); + Kext->Enabled = FALSE; + return; + } - Identifier = OC_BLOB_GET (&Kext->Identifier); - BundlePath = OC_BLOB_GET (&Kext->BundlePath); - Comment = OC_BLOB_GET (&Kext->Comment); - PlistPath = OC_BLOB_GET (&Kext->PlistPath); - if (Identifier[0] == '\0' || BundlePath[0] == '\0' || PlistPath[0] == '\0') { - DEBUG ((DEBUG_ERROR, "OC: Forced kext %u (%a) has invalid info\n", Index, Comment)); - Kext->Enabled = FALSE; - continue; - } + if (AsciiStrCmp (Arch, Is32Bit ? "x86_64" : "i386") == 0) { + DEBUG (( + DEBUG_INFO, + "OC: %s kext %a (%a) at %u skipped due to arch %a != %a\n", + IsForced ? L"Forced" : L"Injected", + BundlePath, + Comment, + Index, + Arch, + Is32Bit ? "i386" : "x86_64" + )); + return; + } - // - // Required for possible cacheless force injection later on. - // - AsciiUefiSlashes (BundlePath); + // + // Required for possible cacheless force injection later on. + // + AsciiUefiSlashes (BundlePath); - Status = OcUnicodeSafeSPrint ( - FullPath, - sizeof (FullPath), - L"%a\\%a", + // + // Get plist path and data. + // + Status = OcUnicodeSafeSPrint ( + FullPath, + sizeof (FullPath), + IsForced ? L"%a\\%a" : OPEN_CORE_KEXT_PATH "%a\\%a", + BundlePath, + PlistPath + ); + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_WARN, + "OC: Failed to fit %s kext path %s%a\\%a", + IsForced ? L"forced" : L"injected", + IsForced ? L"" : OPEN_CORE_KEXT_PATH, BundlePath, PlistPath - ); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_WARN, - "OC: Failed to fit kext path %a\\%a", - BundlePath, - PlistPath - )); - continue; - } + )); + Kext->Enabled = IsForced; + return; + } - UnicodeUefiSlashes (FullPath); + UnicodeUefiSlashes (FullPath); + if (IsForced) { Kext->PlistData = ReadFileFromFile ( RootFile, FullPath, &Kext->PlistDataSize, 0 ); + } else { + Kext->PlistData = OcStorageReadFileUnicode ( + Storage, + FullPath, + &Kext->PlistDataSize + ); + } - if (Kext->PlistData == NULL) { + if (Kext->PlistData == NULL) { + DEBUG (( + IsForced ? DEBUG_INFO : DEBUG_ERROR, + "OC: Plist %s is missing for %s kext %a (%a)\n", + FullPath, + IsForced ? L"forced" : L"injected", + BundlePath, + Comment + )); + Kext->Enabled = IsForced; + return; + } + + // + // Get executable path and data, if present. + // + ExecutablePath = OC_BLOB_GET (&Kext->ExecutablePath); + if (ExecutablePath[0] != '\0') { + Status = OcUnicodeSafeSPrint ( + FullPath, + sizeof (FullPath), + IsForced ? L"%a\\%a" : OPEN_CORE_KEXT_PATH "%a\\%a", + BundlePath, + ExecutablePath + ); + if (EFI_ERROR (Status)) { DEBUG (( - DEBUG_INFO, - "OC: Plist %s is missing for forced kext %a (%a)\n", - FullPath, + DEBUG_WARN, + "OC: Failed to fit %s kext path %s%a\\%a", + IsForced ? L"forced" : L"injected", + IsForced ? L"" : OPEN_CORE_KEXT_PATH, BundlePath, - Comment + ExecutablePath )); - continue; + Kext->Enabled = IsForced; + FreePool (Kext->PlistData); + Kext->PlistData = NULL; + return; } - ExecutablePath = OC_BLOB_GET (&Kext->ExecutablePath); - if (ExecutablePath[0] != '\0') { - Status = OcUnicodeSafeSPrint ( - FullPath, - sizeof (FullPath), - L"%a\\%a", - BundlePath, - ExecutablePath - ); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_WARN, - "OC: Failed to fit kext path %a\\%a", - BundlePath, - ExecutablePath - )); - FreePool (Kext->PlistData); - Kext->PlistData = NULL; - continue; - } - - UnicodeUefiSlashes (FullPath); + UnicodeUefiSlashes (FullPath); + if (IsForced) { Kext->ImageData = ReadFileFromFile ( RootFile, FullPath, &Kext->ImageDataSize, 0 ); - - if (Kext->ImageData == NULL) { - DEBUG (( - DEBUG_INFO, - "OC: Image %s is missing for kext %a (%a)\n", - FullPath, - BundlePath, - Comment - )); - FreePool (Kext->PlistData); - Kext->PlistData = NULL; - continue; - } - } - - if (CacheType == CacheTypeCacheless || CacheType == CacheTypeMkext) { - Status = MkextReserveKextSize ( - ReservedInfoSize, - ReservedExeSize, - Kext->PlistDataSize, - Kext->ImageData, - Kext->ImageDataSize - ); - } else if (CacheType == CacheTypePrelinked) { - Status = PrelinkedReserveKextSize ( - ReservedInfoSize, - ReservedExeSize, - Kext->PlistDataSize, - Kext->ImageData, - Kext->ImageDataSize + } else { + Kext->ImageData = OcStorageReadFileUnicode ( + Storage, + FullPath, + &Kext->ImageDataSize ); } - if (EFI_ERROR (Status)) { + if (Kext->ImageData == NULL) { DEBUG (( - DEBUG_ERROR, - "OC: Failed to fit kext %a (%a)\n", + IsForced ? DEBUG_INFO : DEBUG_ERROR, + "OC: Image %s is missing for %s kext %a (%a)\n", + FullPath, + IsForced ? L"forced" : L"injected", BundlePath, Comment )); + Kext->Enabled = IsForced; FreePool (Kext->PlistData); Kext->PlistData = NULL; - continue; + return; } - - (*NumReservedKexts)++; } - // - // Process kexts to be injected. - // - for (Index = 0; Index < Config->Kernel.Add.Count; Index++) { - Kext = Config->Kernel.Add.Values[Index]; + if (CacheType == CacheTypeCacheless || CacheType == CacheTypeMkext) { + Status = MkextReserveKextSize ( + ReservedInfoSize, + ReservedExeSize, + Kext->PlistDataSize, + Kext->ImageData, + Kext->ImageDataSize, + Is32Bit + ); + } else if (CacheType == CacheTypePrelinked) { + Status = PrelinkedReserveKextSize ( + ReservedInfoSize, + ReservedExeSize, + Kext->PlistDataSize, + Kext->ImageData, + Kext->ImageDataSize, + Is32Bit + ); + } - if (!Kext->Enabled) { - continue; + if (EFI_ERROR (Status)) { + DEBUG (( + DEBUG_INFO, + "OC: Failed to fit %s kext %a (%a) - %r\n", + Is32Bit ? L"32-bit" : L"64-bit", + BundlePath, + Comment, + Status + )); + if (Kext->ImageData != NULL) { + FreePool (Kext->ImageData); + Kext->ImageData = NULL; } + FreePool (Kext->PlistData); + Kext->PlistData = NULL; + return; + } - if (Kext->PlistData == NULL) { - BundlePath = OC_BLOB_GET (&Kext->BundlePath); - Comment = OC_BLOB_GET (&Kext->Comment); - PlistPath = OC_BLOB_GET (&Kext->PlistPath); - if ( BundlePath[0] == '\0' || PlistPath[0] == '\0') { - DEBUG ((DEBUG_ERROR, "OC: Injected kext %u (%a) has invalid info\n", Index, Comment)); - Kext->Enabled = FALSE; - continue; - } - - Status = OcUnicodeSafeSPrint ( - FullPath, - sizeof (FullPath), - OPEN_CORE_KEXT_PATH "%a\\%a", - BundlePath, - PlistPath - ); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_WARN, - "OC: Failed to fit kext path %s%a\\%a", - OPEN_CORE_KEXT_PATH, - BundlePath, - PlistPath - )); - Kext->Enabled = FALSE; - continue; - } - - UnicodeUefiSlashes (FullPath); - - Kext->PlistData = OcStorageReadFileUnicode ( - Storage, - FullPath, - &Kext->PlistDataSize - ); + (*NumReservedKexts)++; +} - if (Kext->PlistData == NULL) { - DEBUG (( - DEBUG_ERROR, - "OC: Plist %s is missing for kext %a (%a)\n", - FullPath, - BundlePath, - Comment - )); - Kext->Enabled = FALSE; - continue; - } +STATIC +EFI_STATUS +OcKernelLoadKextsAndReserve ( + IN EFI_FILE_PROTOCOL *RootFile, + IN OC_STORAGE_CONTEXT *Storage, + IN OC_GLOBAL_CONFIG *Config, + IN KERNEL_CACHE_TYPE CacheType, + IN BOOLEAN Is32Bit, + OUT UINT32 *ReservedExeSize, + OUT UINT32 *ReservedInfoSize, + OUT UINT32 *NumReservedKexts + ) +{ + UINT32 Index; + OC_KERNEL_ADD_ENTRY *Kext; - ExecutablePath = OC_BLOB_GET (&Kext->ExecutablePath); - if (ExecutablePath[0] != '\0') { - Status = OcUnicodeSafeSPrint ( - FullPath, - sizeof (FullPath), - OPEN_CORE_KEXT_PATH "%a\\%a", - BundlePath, - ExecutablePath - ); - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_WARN, - "OC: Failed to fit kext path %s%a\\%a", - OPEN_CORE_KEXT_PATH, - BundlePath, - ExecutablePath - )); - Kext->Enabled = FALSE; - FreePool (Kext->PlistData); - Kext->PlistData = NULL; - continue; - } + *ReservedInfoSize = PRELINK_INFO_RESERVE_SIZE; + *ReservedExeSize = 0; + *NumReservedKexts = 0; - UnicodeUefiSlashes (FullPath); - - Kext->ImageData = OcStorageReadFileUnicode ( - Storage, - FullPath, - &Kext->ImageDataSize - ); - - if (Kext->ImageData == NULL) { - DEBUG (( - DEBUG_ERROR, - "OC: Image %s is missing for kext %a (%a)\n", - FullPath, - BundlePath, - Comment - )); - Kext->Enabled = FALSE; - FreePool (Kext->PlistData); - Kext->PlistData = NULL; - continue; - } - } - } + // + // Process system kexts to be force injected. + // + for (Index = 0; Index < Config->Kernel.Force.Count; Index++) { + Kext = Config->Kernel.Force.Values[Index]; - if (CacheType == CacheTypeCacheless || CacheType == CacheTypeMkext) { - Status = MkextReserveKextSize ( - ReservedInfoSize, - ReservedExeSize, - Kext->PlistDataSize, - Kext->ImageData, - Kext->ImageDataSize - ); - } else if (CacheType == CacheTypePrelinked) { - Status = PrelinkedReserveKextSize ( - ReservedInfoSize, - ReservedExeSize, - Kext->PlistDataSize, - Kext->ImageData, - Kext->ImageDataSize - ); - } + OcKernelLoadAndReserveKext ( + Kext, + Index, + TRUE, + RootFile, + Storage, + Config, + CacheType, + Is32Bit, + ReservedExeSize, + ReservedInfoSize, + NumReservedKexts + ); + } - if (EFI_ERROR (Status)) { - DEBUG (( - DEBUG_ERROR, - "OC: Failed to fit kext %a (%a)\n", - BundlePath, - Comment - )); - Kext->Enabled = FALSE; - FreePool (Kext->PlistData); - Kext->PlistData = NULL; - continue; - } + // + // Process kexts to be injected. + // + for (Index = 0; Index < Config->Kernel.Add.Count; Index++) { + Kext = Config->Kernel.Add.Values[Index]; - (*NumReservedKexts)++; + OcKernelLoadAndReserveKext ( + Kext, + Index, + FALSE, + RootFile, + Storage, + Config, + CacheType, + Is32Bit, + ReservedExeSize, + ReservedInfoSize, + NumReservedKexts + ); } if (CacheType == CacheTypePrelinked) { @@ -551,7 +528,6 @@ OcKernelInjectKext ( CONST CHAR8 *BundlePath; CONST CHAR8 *ExecutablePath; CONST CHAR8 *Comment; - CONST CHAR8 *Arch; CHAR8 FullPath[OC_STORAGE_SAFE_PATH_MAX]; UINT32 MaxKernel; UINT32 MinKernel; @@ -563,25 +539,9 @@ OcKernelInjectKext ( Identifier = OC_BLOB_GET (&Kext->Identifier); BundlePath = OC_BLOB_GET (&Kext->BundlePath); Comment = OC_BLOB_GET (&Kext->Comment); - Arch = OC_BLOB_GET (&Kext->Arch); MaxKernel = OcParseDarwinVersion (OC_BLOB_GET (&Kext->MaxKernel)); MinKernel = OcParseDarwinVersion (OC_BLOB_GET (&Kext->MinKernel)); - if (AsciiStrCmp (Arch, Is32Bit ? "x86_64" : "i386") == 0) { - DEBUG (( - DEBUG_INFO, - "OC: %a%a injection skips %a (%a) kext at %u due to arch %a != %a\n", - PRINT_KERNEL_CACHE_TYPE (CacheType), - IsForced ? " force" : "", - BundlePath, - Comment, - Index, - Arch, - Is32Bit ? "i386" : "x86_64" - )); - return; - } - if (!OcMatchDarwinVersion (DarwinVersion, MinKernel, MaxKernel)) { DEBUG (( DEBUG_INFO, @@ -763,7 +723,7 @@ OcKernelProcessPrelinked ( OcKernelApplyPatches (Config, mOcCpuInfo, DarwinVersion, Is32Bit, CacheTypePrelinked, &Context, NULL, 0); - OcKernelBlockKexts (Config, DarwinVersion, Is32Bit, &Context); + OcKernelBlockKexts (Config, DarwinVersion, Is32Bit, CacheTypePrelinked, &Context); *KernelSize = Context.PrelinkedSize; @@ -796,6 +756,8 @@ OcKernelProcessMkext ( OcKernelApplyPatches (Config, mOcCpuInfo, DarwinVersion, Is32Bit, CacheTypeMkext, &Context, NULL, 0); + OcKernelBlockKexts (Config, DarwinVersion, Is32Bit, CacheTypeMkext, &Context); + MkextInjectPatchComplete (&Context); *MkextSize = Context.MkextSize; @@ -822,7 +784,8 @@ OcKernelInitCacheless ( Context, FileName, ExtensionsDir, - DarwinVersion + DarwinVersion, + Is32Bit ); if (EFI_ERROR (Status)) { return Status; @@ -832,6 +795,8 @@ OcKernelInitCacheless ( OcKernelApplyPatches (Config, mOcCpuInfo, DarwinVersion, Is32Bit, CacheTypeCacheless, Context, NULL, 0); + OcKernelBlockKexts (Config, DarwinVersion, Is32Bit, CacheTypeCacheless, Context); + return CachelessContextOverlayExtensionsDir (Context, File); } @@ -865,6 +830,7 @@ OcKernelReadAppleKernel ( mOcStorage, mOcConfiguration, CacheTypePrelinked, + Is32Bit, ReservedExeSize, &ReservedInfoSize, &NumReservedKexts @@ -1228,7 +1194,7 @@ OcKernelFileOpen ( mOcCpuInfo, mOcDarwinVersion, mUse32BitKernel, - 0, + CacheTypeNone, NULL, Kernel, KernelSize @@ -1295,6 +1261,7 @@ OcKernelFileOpen ( mOcStorage, mOcConfiguration, CacheTypeMkext, + mUse32BitKernel, &ReservedExeSize, &ReservedInfoSize, &NumReservedKexts @@ -1379,6 +1346,7 @@ OcKernelFileOpen ( mOcStorage, mOcConfiguration, CacheTypeCacheless, + mUse32BitKernel, &ReservedExeSize, &ReservedInfoSize, &NumReservedKexts diff --git a/Platform/OpenCore/OpenCoreKernelPatch.c b/Platform/OpenCore/OpenCoreKernelPatch.c index 317482b6..a14c46a9 100644 --- a/Platform/OpenCore/OpenCoreKernelPatch.c +++ b/Platform/OpenCore/OpenCoreKernelPatch.c @@ -91,7 +91,8 @@ OcKernelApplyPatches ( Status = PatcherInitContextFromBuffer ( &KernelPatcher, Kernel, - Size + Size, + Is32Bit ); if (EFI_ERROR (Status)) { @@ -116,7 +117,8 @@ OcKernelApplyPatches ( if (AsciiStrCmp (Arch, Is32Bit ? "x86_64" : "i386") == 0) { DEBUG (( DEBUG_INFO, - "OC: Kernel patcher skips %a (%a) patch at %u due to arch %a != %a\n", + "OC: %a patcher skips %a (%a) patch at %u due to arch %a != %a\n", + PRINT_KERNEL_CACHE_TYPE (CacheType), Target, Comment, Index, @@ -129,7 +131,8 @@ OcKernelApplyPatches ( if (!OcMatchDarwinVersion (DarwinVersion, MinKernel, MaxKernel)) { DEBUG (( DEBUG_INFO, - "OC: Kernel patcher skips %a (%a) patch at %u due to version %u <= %u <= %u\n", + "OC: %a patcher skips %a (%a) patch at %u due to version %u <= %u <= %u\n", + PRINT_KERNEL_CACHE_TYPE (CacheType), Target, Comment, Index, @@ -198,7 +201,8 @@ OcKernelApplyPatches ( DEBUG (( EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, - "OC: Kernel patcher result %u for %a (%a) - %r\n", + "OC: %a patcher result %u for %a (%a) - %r\n", + PRINT_KERNEL_CACHE_TYPE (CacheType), Index, Target, Comment, @@ -270,7 +274,8 @@ OcKernelApplyPatches ( &KernelPatcher, CpuInfo, Config->Kernel.Emulate.Cpuid1Data, - Config->Kernel.Emulate.Cpuid1Mask + Config->Kernel.Emulate.Cpuid1Mask, + DarwinVersion ); } @@ -293,11 +298,11 @@ OcKernelBlockKexts ( IN OC_GLOBAL_CONFIG *Config, IN UINT32 DarwinVersion, IN BOOLEAN Is32Bit, - IN PRELINKED_CONTEXT *Context + IN KERNEL_CACHE_TYPE CacheType, + IN VOID *Context ) { EFI_STATUS Status; - PATCHER_CONTEXT Patcher; UINT32 Index; OC_KERNEL_BLOCK_ENTRY *Kext; CONST CHAR8 *Target; @@ -322,7 +327,8 @@ OcKernelBlockKexts ( if (AsciiStrCmp (Arch, Is32Bit ? "x86_64" : "i386") == 0) { DEBUG (( DEBUG_INFO, - "OC: Prelink blocker skips %a (%a) block at %u due to arch %a != %a\n", + "OC: %a blocker skips %a (%a) block at %u due to arch %a != %a\n", + PRINT_KERNEL_CACHE_TYPE (CacheType), Target, Comment, Index, @@ -335,7 +341,8 @@ OcKernelBlockKexts ( if (!OcMatchDarwinVersion (DarwinVersion, MinKernel, MaxKernel)) { DEBUG (( DEBUG_INFO, - "OC: Prelink blocker skips %a (%a) block at %u due to version %u <= %u <= %u\n", + "OC: %a blocker skips %a (%a) block at %u due to version %u <= %u <= %u\n", + PRINT_KERNEL_CACHE_TYPE (CacheType), Target, Comment, Index, @@ -346,22 +353,19 @@ OcKernelBlockKexts ( continue; } - Status = PatcherInitContextFromPrelinked ( - &Patcher, - Context, - Target - ); - - if (EFI_ERROR (Status)) { - DEBUG ((DEBUG_WARN, "OC: Prelink blocker %a (%a) init failure - %r\n", Target, Comment, Status)); - continue; + if (CacheType == CacheTypeCacheless) { + Status = CachelessContextBlock (Context, Target); + } else if (CacheType == CacheTypeMkext) { + Status = MkextContextBlock (Context, Target); + } else if (CacheType == CacheTypePrelinked) { + Status = PrelinkedContextBlock (Context, Target); } - Status = PatcherBlockKext (&Patcher); - DEBUG (( EFI_ERROR (Status) ? DEBUG_WARN : DEBUG_INFO, - "OC: Prelink blocker %a (%a) - %r\n", + "OC: %a blocker result %u for %a (%a) - %r\n", + PRINT_KERNEL_CACHE_TYPE (CacheType), + Index, Target, Comment, Status diff --git a/User/Makefile b/User/Makefile index 08449d25..861eab99 100644 --- a/User/Makefile +++ b/User/Makefile @@ -136,7 +136,7 @@ ifneq ($(STANDALONE),1) # # OcMachoLib targets. # - OBJS += CxxSymbols.o Fat.o Header.o Symbols.o Relocations.o + OBJS += CxxSymbols.o Fat.o Header.o Macho32.o Macho64.o Relocations.o Symbols.o # # OcAppleKeysLib targets. # diff --git a/Utilities/TestKextInject/KextInject.c b/Utilities/TestKextInject/KextInject.c index e85c27a9..6f31bf14 100644 --- a/Utilities/TestKextInject/KextInject.c +++ b/Utilities/TestKextInject/KextInject.c @@ -393,7 +393,8 @@ ApplyKernelPatches ( Status = PatcherInitContextFromBuffer ( &Patcher, Kernel, - Size + Size, + FALSE ); if (!EFI_ERROR (Status)) { @@ -414,7 +415,8 @@ ApplyKernelPatches ( &Patcher, &CpuInfo, VirtualCpuid, - VirtualCpuidMask + VirtualCpuidMask, + KernelVersion ); if (EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "[FAIL] CPUID kernel patch - %r\n", Status)); @@ -575,7 +577,8 @@ int wrap_main(int argc, char** argv) { &ReservedExeSize, TestPlistSize, TestData, - TestDataSize + TestDataSize, + FALSE ); free(TestData); @@ -636,7 +639,8 @@ int wrap_main(int argc, char** argv) { Status = PatcherInitContextFromBuffer ( &Patcher, Prelinked, - PrelinkedSize + PrelinkedSize, + FALSE ); if (!EFI_ERROR (Status)) { DEBUG ((DEBUG_WARN, "[OK] Patcher init success\n")); diff --git a/Utilities/TestKextInject/Makefile b/Utilities/TestKextInject/Makefile index b95f1304..579719e6 100644 --- a/Utilities/TestKextInject/Makefile +++ b/Utilities/TestKextInject/Makefile @@ -7,6 +7,7 @@ PROJECT = KextInject PRODUCT = $(PROJECT)$(SUFFIX) OBJS = $(PROJECT).o \ CommonPatches.o \ + CpuidPatches.o \ KernelVersion.o \ KextPatcher.o \ KxldState.o \ diff --git a/Utilities/TestMacho/Macho.c b/Utilities/TestMacho/Macho.c index 25c30ab6..f352b3da 100644 --- a/Utilities/TestMacho/Macho.c +++ b/Utilities/TestMacho/Macho.c @@ -39,7 +39,7 @@ MACH_UUID_COMMAND Uuid; static int FeedMacho(void *file, uint32_t size) { OC_MACHO_CONTEXT Context; - if (!MachoInitializeContext (&Context, file, size, 0)) { + if (!MachoInitializeContext64 (&Context, file, size, 0)) { return -1; } @@ -51,7 +51,7 @@ static int FeedMacho(void *file, uint32_t size) { code++; } - MACH_UUID_COMMAND *Cmd = MachoGetUuid64(&Context); + MACH_UUID_COMMAND *Cmd = MachoGetUuid(&Context); if (Cmd) { memcpy(&Uuid, Cmd, sizeof(Uuid)); code++; @@ -86,13 +86,13 @@ static int FeedMacho(void *file, uint32_t size) { (Indirect && !AsciiStrCmp (Indirect, "__hack"))) { code++; } - if (MachoSymbolIsSection (Symbol)) { + if (MachoSymbolIsSection64 (Symbol)) { code++; } - if (MachoSymbolIsDefined (Symbol)) { + if (MachoSymbolIsDefined64 (Symbol)) { code++; } - if (MachoSymbolIsLocalDefined (&Context, Symbol)) { + if (MachoSymbolIsLocalDefined64 (&Context, Symbol)) { code++; } @@ -113,21 +113,21 @@ static int FeedMacho(void *file, uint32_t size) { code++; } - if (MachoSymbolNameIsSmcp64 (&Context, MachoGetSymbolName64 (&Context, Symbol))) { + if (MachoSymbolNameIsSmcp (&Context, MachoGetSymbolName64 (&Context, Symbol))) { code++; } - if (MachoSymbolNameIsMetaclassPointer64 (&Context, MachoGetSymbolName64 (&Context, Symbol))) { + if (MachoSymbolNameIsMetaclassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol))) { code++; } char out[64]; - if (MachoSymbolNameIsSmcp64 (&Context, MachoGetSymbolName64 (&Context, Symbol)) + if (MachoSymbolNameIsSmcp (&Context, MachoGetSymbolName64 (&Context, Symbol)) && MachoGetClassNameFromSuperMetaClassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol), sizeof(out), out)) { code++; } - if (MachoSymbolNameIsVtable64 (MachoGetSymbolName64 (&Context, Symbol))) { + if (MachoSymbolNameIsVtable (MachoGetSymbolName64 (&Context, Symbol))) { if (AsciiStrCmp(MachoGetClassNameFromVtableName (MachoGetSymbolName64 (&Context, Symbol)), "sym")) { code++; } @@ -138,7 +138,7 @@ static int FeedMacho(void *file, uint32_t size) { code++; } - if (MachoSymbolNameIsMetaclassPointer64 (&Context, MachoGetSymbolName64 (&Context, Symbol)) + if (MachoSymbolNameIsMetaclassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol)) && MachoGetClassNameFromMetaClassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol), sizeof(out), out) && !AsciiStrCmp("SomeReallyLongStringJustInCaseToCheckIt", out)) { code++; @@ -185,20 +185,20 @@ static int FeedMacho(void *file, uint32_t size) { MachoRelocateSymbol64 (&Context, 0x100000000, &SSSS); } - Symbol = MachoGetLocalDefinedSymbolByName (&Context, "_Assert"); + Symbol = MachoGetLocalDefinedSymbolByName64 (&Context, "_Assert"); if (Symbol) { CONST CHAR8 *Indirect = MachoGetIndirectSymbolName64 (&Context, Symbol); if (!AsciiStrCmp (MachoGetSymbolName64 (&Context, Symbol), "__hack") || (Indirect && !AsciiStrCmp (Indirect, "__hack"))) { code++; } - if (MachoSymbolIsSection (Symbol)) { + if (MachoSymbolIsSection64 (Symbol)) { code++; } - if (MachoSymbolIsDefined (Symbol)) { + if (MachoSymbolIsDefined64 (Symbol)) { code++; } - if (MachoSymbolIsLocalDefined (&Context, Symbol)) { + if (MachoSymbolIsLocalDefined64 (&Context, Symbol)) { code++; } @@ -218,22 +218,22 @@ static int FeedMacho(void *file, uint32_t size) { code++; } - if (MachoSymbolNameIsSmcp64 (&Context, MachoGetSymbolName64 (&Context, Symbol))) { + if (MachoSymbolNameIsSmcp (&Context, MachoGetSymbolName64 (&Context, Symbol))) { code++; } - if (MachoSymbolNameIsMetaclassPointer64 (&Context, MachoGetSymbolName64 (&Context, Symbol))) { + if (MachoSymbolNameIsMetaclassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol))) { code++; } char out[64]; - if (MachoSymbolNameIsSmcp64 (&Context, MachoGetSymbolName64 (&Context, Symbol)) + if (MachoSymbolNameIsSmcp (&Context, MachoGetSymbolName64 (&Context, Symbol)) && MachoGetClassNameFromSuperMetaClassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol), sizeof(out), out) && !AsciiStrCmp("SomeReallyLongStringJustInCaseToCheckIt", out)) { code++; } - if (MachoSymbolNameIsVtable64 (MachoGetSymbolName64 (&Context, Symbol))) { + if (MachoSymbolNameIsVtable (MachoGetSymbolName64 (&Context, Symbol))) { if (AsciiStrCmp(MachoGetClassNameFromVtableName (MachoGetSymbolName64 (&Context, Symbol)), "sym")) { code++; } @@ -244,7 +244,7 @@ static int FeedMacho(void *file, uint32_t size) { code++; } - if (MachoSymbolNameIsMetaclassPointer64 (&Context, MachoGetSymbolName64 (&Context, Symbol)) + if (MachoSymbolNameIsMetaclassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol)) && MachoGetClassNameFromMetaClassPointer (&Context, MachoGetSymbolName64 (&Context, Symbol), sizeof(out), out) && !AsciiStrCmp("SomeReallyLongStringJustInCaseToCheckIt", out)) { code++; -- GitLab