mirror of
https://github.com/CloverHackyColor/CloverBootloader.git
synced 2024-11-27 12:15:19 +01:00
1450 lines
38 KiB
C
1450 lines
38 KiB
C
|
/**
|
||
|
Provides services for Mach-O headers.
|
||
|
|
||
|
Copyright (C) 2016 - 2018, Download-Fritz. All rights reserved.<BR>
|
||
|
This program and the accompanying materials are licensed and made available
|
||
|
under the terms and conditions of the BSD License which accompanies this
|
||
|
distribution. The full text of the license may be found at
|
||
|
http://opensource.org/licenses/bsd-license.php.
|
||
|
|
||
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
||
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
||
|
|
||
|
**/
|
||
|
|
||
|
#include <Base.h>
|
||
|
|
||
|
#include <IndustryStandard/AppleMachoImage.h>
|
||
|
#include <IndustryStandard/AppleFatBinaryImage.h>
|
||
|
|
||
|
#include <Library/BaseLib.h>
|
||
|
#include <Library/BaseMemoryLib.h>
|
||
|
#include <Library/DebugLib.h>
|
||
|
#include <Library/OcGuardLib.h>
|
||
|
#include <Library/MachoLib.h>
|
||
|
|
||
|
#include "MachoLibInternal.h"
|
||
|
|
||
|
/**
|
||
|
Returns the Mach-O Header structure.
|
||
|
|
||
|
@param[in,out] Context Context of the Mach-O.
|
||
|
|
||
|
**/
|
||
|
MACH_HEADER_64 *
|
||
|
MachoGetMachHeader64 (
|
||
|
IN OUT OC_MACHO_CONTEXT *Context
|
||
|
)
|
||
|
{
|
||
|
ASSERT (Context != NULL);
|
||
|
ASSERT (Context->MachHeader != NULL);
|
||
|
|
||
|
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
|
||
|
)
|
||
|
{
|
||
|
ASSERT (Context != NULL);
|
||
|
ASSERT (Context->FileSize != 0);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Moves file pointer and size to point to x86_64 slice in case
|
||
|
FAT Mach-O is used.
|
||
|
|
||
|
@param[in,out] FileData Pointer to pointer of the file's data.
|
||
|
@param[in,out] FileSize Pointer to file size of FileData.
|
||
|
|
||
|
@return FALSE is not valid FAT image.
|
||
|
**/
|
||
|
STATIC
|
||
|
BOOLEAN
|
||
|
MachoFilterFatArchitecture64 (
|
||
|
IN OUT UINT8 **FileData,
|
||
|
IN OUT UINT32 *FileSize
|
||
|
)
|
||
|
{
|
||
|
BOOLEAN SwapBytes;
|
||
|
MACH_FAT_HEADER *FatHeader;
|
||
|
UINT32 NumberOfFatArch;
|
||
|
UINT32 Offset;
|
||
|
MACH_CPU_TYPE CpuType;
|
||
|
UINT32 TmpSize;
|
||
|
UINT32 Index;
|
||
|
UINT32 Size;
|
||
|
|
||
|
FatHeader = (MACH_FAT_HEADER *) *FileData;
|
||
|
|
||
|
if (!OC_ALIGNED (FatHeader)
|
||
|
|| *FileSize < sizeof (MACH_FAT_HEADER)
|
||
|
|| (FatHeader->Signature != MACH_FAT_BINARY_INVERT_SIGNATURE
|
||
|
&& FatHeader->Signature != MACH_FAT_BINARY_SIGNATURE))
|
||
|
{
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
SwapBytes = FatHeader->Signature == MACH_FAT_BINARY_INVERT_SIGNATURE;
|
||
|
NumberOfFatArch = FatHeader->NumberOfFatArch;
|
||
|
if (SwapBytes) {
|
||
|
NumberOfFatArch = SwapBytes32 (NumberOfFatArch);
|
||
|
}
|
||
|
|
||
|
if (OcOverflowMulAddU32 (NumberOfFatArch, sizeof (MACH_FAT_ARCH), sizeof (MACH_FAT_HEADER), &TmpSize)
|
||
|
|| TmpSize > *FileSize) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// TODO: extend the interface to support MachCpuSubtypeX8664H some day.
|
||
|
//
|
||
|
for (Index = 0; Index < NumberOfFatArch; ++Index) {
|
||
|
CpuType = FatHeader->FatArch[Index].CpuType;
|
||
|
if (SwapBytes) {
|
||
|
CpuType = SwapBytes32 (CpuType);
|
||
|
}
|
||
|
if (CpuType == MachCpuTypeX8664) {
|
||
|
Offset = FatHeader->FatArch[Index].Offset;
|
||
|
Size = FatHeader->FatArch[Index].Size;
|
||
|
if (SwapBytes) {
|
||
|
Offset = SwapBytes32 (Offset);
|
||
|
Size = SwapBytes32 (Size);
|
||
|
}
|
||
|
|
||
|
if (Offset == 0
|
||
|
|| OcOverflowAddU32 (Offset, Size, &TmpSize)
|
||
|
|| TmpSize > *FileSize) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
*FileData = *FileData + Offset;
|
||
|
*FileSize = Size;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
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
|
||
|
)
|
||
|
{
|
||
|
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);
|
||
|
|
||
|
MachoFilterFatArchitecture64 ((UINT8 **) &FileData, &FileSize);
|
||
|
|
||
|
MachHeader = (MACH_HEADER_64 *) FileData;
|
||
|
TopOfFile = ((UINTN)MachHeader + FileSize);
|
||
|
|
||
|
ASSERT (TopOfFile > (UINTN)MachHeader);
|
||
|
|
||
|
if (FileSize < sizeof (*MachHeader)
|
||
|
|| !OC_ALIGNED (MachHeader)
|
||
|
|| 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))) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ZeroMem (Context, sizeof (*Context));
|
||
|
|
||
|
Context->MachHeader = MachHeader;
|
||
|
Context->FileSize = FileSize;
|
||
|
|
||
|
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 (
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Retrieves the first 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.
|
||
|
|
||
|
**/
|
||
|
STATIC
|
||
|
CONST MACH_LOAD_COMMAND *
|
||
|
InternalGetNextCommand64 (
|
||
|
IN OUT OC_MACHO_CONTEXT *Context,
|
||
|
IN MACH_LOAD_COMMAND_TYPE LoadCommandType,
|
||
|
IN CONST MACH_LOAD_COMMAND *LoadCommand OPTIONAL
|
||
|
)
|
||
|
{
|
||
|
CONST MACH_LOAD_COMMAND *Command;
|
||
|
CONST 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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
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 (
|
||
|
IN OUT OC_MACHO_CONTEXT *Context
|
||
|
)
|
||
|
{
|
||
|
MACH_UUID_COMMAND *UuidCommand;
|
||
|
|
||
|
ASSERT (Context != NULL);
|
||
|
|
||
|
UuidCommand = (MACH_UUID_COMMAND *)(
|
||
|
InternalGetNextCommand64 (
|
||
|
Context,
|
||
|
MACH_LOAD_COMMAND_UUID,
|
||
|
NULL
|
||
|
)
|
||
|
);
|
||
|
|
||
|
if ((UuidCommand != NULL)
|
||
|
&& OC_ALIGNED (UuidCommand)
|
||
|
&& (UuidCommand->CommandSize == sizeof (*UuidCommand))) {
|
||
|
return UuidCommand;
|
||
|
}
|
||
|
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
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.
|
||
|
//
|
||
|
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 = OcOverflowMulAddU32 (
|
||
|
Section->NumRelocations,
|
||
|
sizeof (MACH_RELOCATION_INFO),
|
||
|
Section->RelocationsOffset,
|
||
|
&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)
|
||
|
);
|
||
|
}
|
||
|
|
||
|
NextSegment = (MACH_SEGMENT_COMMAND_64 *)(
|
||
|
InternalGetNextCommand64 (
|
||
|
Context,
|
||
|
MACH_LOAD_COMMAND_SEGMENT_64,
|
||
|
(MACH_LOAD_COMMAND *) Segment
|
||
|
)
|
||
|
);
|
||
|
if ((NextSegment == NULL)
|
||
|
|| !OC_ALIGNED (NextSegment)
|
||
|
|| (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 = OcOverflowAddU64 (
|
||
|
NextSegment->FileOffset,
|
||
|
NextSegment->FileSize,
|
||
|
&TopOfSegment
|
||
|
);
|
||
|
if (Result || (TopOfSegment > Context->FileSize)) {
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
if (!InternalSectionIsSane (Context, Section, Segment)) {
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
return Section;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
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_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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
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_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;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
Retrieves the SYMTAB command.
|
||
|
|
||
|
@param[in,out] Context Context of the Mach-O.
|
||
|
|
||
|
@retval NULL NULL is returned on failure.
|
||
|
|
||
|
**/
|
||
|
BOOLEAN
|
||
|
InternalRetrieveSymtabs64 (
|
||
|
IN OUT OC_MACHO_CONTEXT *Context
|
||
|
)
|
||
|
{
|
||
|
UINTN MachoAddress;
|
||
|
MACH_SYMTAB_COMMAND *Symtab;
|
||
|
MACH_DYSYMTAB_COMMAND *DySymtab;
|
||
|
CHAR8 *StringTable;
|
||
|
UINT32 FileSize;
|
||
|
UINT32 OffsetTop;
|
||
|
BOOLEAN Result;
|
||
|
|
||
|
MACH_NLIST_64 *SymbolTable;
|
||
|
MACH_NLIST_64 *IndirectSymtab;
|
||
|
MACH_RELOCATION_INFO *LocalRelocations;
|
||
|
MACH_RELOCATION_INFO *ExternRelocations;
|
||
|
|
||
|
ASSERT (Context != NULL);
|
||
|
ASSERT (Context->MachHeader != NULL);
|
||
|
ASSERT (Context->FileSize > 0);
|
||
|
|
||
|
if (Context->SymbolTable != NULL) {
|
||
|
return TRUE;
|
||
|
}
|
||
|
//
|
||
|
// Retrieve SYMTAB.
|
||
|
//
|
||
|
Symtab = (MACH_SYMTAB_COMMAND *)(
|
||
|
InternalGetNextCommand64 (
|
||
|
Context,
|
||
|
MACH_LOAD_COMMAND_SYMTAB,
|
||
|
NULL
|
||
|
)
|
||
|
);
|
||
|
if ((Symtab == NULL)
|
||
|
|| !OC_ALIGNED (Symtab)
|
||
|
|| (Symtab->CommandSize != sizeof (*Symtab))) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
FileSize = Context->FileSize;
|
||
|
|
||
|
Result = OcOverflowMulAddU32 (
|
||
|
Symtab->NumSymbols,
|
||
|
sizeof (MACH_NLIST_64),
|
||
|
Symtab->SymbolsOffset,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > FileSize)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowAddU32 (
|
||
|
Symtab->StringsOffset,
|
||
|
Symtab->StringsSize,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > FileSize)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
MachoAddress = (UINTN)Context->MachHeader;
|
||
|
StringTable = (CHAR8 *)(MachoAddress + Symtab->StringsOffset);
|
||
|
|
||
|
if (Symtab->StringsSize == 0 || StringTable[Symtab->StringsSize - 1] != '\0') {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
SymbolTable = (MACH_NLIST_64 *)(MachoAddress + Symtab->SymbolsOffset);
|
||
|
if (!OC_ALIGNED (SymbolTable)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
DySymtab = NULL;
|
||
|
IndirectSymtab = NULL;
|
||
|
LocalRelocations = NULL;
|
||
|
ExternRelocations = NULL;
|
||
|
|
||
|
if ((Context->MachHeader->Flags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) != 0) {
|
||
|
//
|
||
|
// Retrieve DYSYMTAB.
|
||
|
//
|
||
|
DySymtab = (MACH_DYSYMTAB_COMMAND *)(
|
||
|
InternalGetNextCommand64 (
|
||
|
Context,
|
||
|
MACH_LOAD_COMMAND_DYSYMTAB,
|
||
|
NULL
|
||
|
)
|
||
|
);
|
||
|
if ((DySymtab == NULL)
|
||
|
|| !OC_ALIGNED (DySymtab)
|
||
|
|| (DySymtab->CommandSize != sizeof (*DySymtab))) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowAddU32 (
|
||
|
DySymtab->LocalSymbolsIndex,
|
||
|
DySymtab->NumLocalSymbols,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > Symtab->NumSymbols)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowAddU32 (
|
||
|
DySymtab->ExternalSymbolsIndex,
|
||
|
DySymtab->NumExternalSymbols,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > Symtab->NumSymbols)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowAddU32 (
|
||
|
DySymtab->UndefinedSymbolsIndex,
|
||
|
DySymtab->NumUndefinedSymbols,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > Symtab->NumSymbols)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowMulAddU32 (
|
||
|
DySymtab->NumIndirectSymbols,
|
||
|
sizeof (MACH_NLIST_64),
|
||
|
DySymtab->IndirectSymbolsOffset,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > FileSize)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowMulAddU32 (
|
||
|
DySymtab->NumOfLocalRelocations,
|
||
|
sizeof (MACH_RELOCATION_INFO),
|
||
|
DySymtab->LocalRelocationsOffset,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > FileSize)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
Result = OcOverflowMulAddU32 (
|
||
|
DySymtab->NumExternalRelocations,
|
||
|
sizeof (MACH_RELOCATION_INFO),
|
||
|
DySymtab->ExternalRelocationsOffset,
|
||
|
&OffsetTop
|
||
|
);
|
||
|
if (Result || (OffsetTop > FileSize)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
IndirectSymtab = (MACH_NLIST_64 *)(
|
||
|
MachoAddress + DySymtab->IndirectSymbolsOffset
|
||
|
);
|
||
|
LocalRelocations = (MACH_RELOCATION_INFO *)(
|
||
|
MachoAddress + DySymtab->LocalRelocationsOffset
|
||
|
);
|
||
|
ExternRelocations = (MACH_RELOCATION_INFO *)(
|
||
|
MachoAddress + DySymtab->ExternalRelocationsOffset
|
||
|
);
|
||
|
if (!OC_ALIGNED (IndirectSymtab)
|
||
|
|| !OC_ALIGNED (LocalRelocations)
|
||
|
|| !OC_ALIGNED (ExternRelocations)) {
|
||
|
return FALSE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
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)
|
||
|
|| (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;
|
||
|
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, "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;
|
||
|
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;
|
||
|
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.
|
||
|
//
|
||
|
ZeroMem (&Destination[CopyFileOffset + OriginalDelta], CurrentDelta - OriginalDelta);
|
||
|
CopyMem (&Destination[CopyFileOffset + CurrentDelta], &Source[CopyFileOffset], CopyFileSize);
|
||
|
ZeroMem (&Destination[CopyFileOffset + CurrentDelta + CopyFileSize], 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 != 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 *)(
|
||
|
InternalGetNextCommand64 (
|
||
|
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 *)(
|
||
|
InternalGetNextCommand64 (
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
if (Strip) {
|
||
|
InternalStripLoadCommands64 ((MACH_HEADER_64 *) Destination);
|
||
|
}
|
||
|
|
||
|
return (UINT32) CurrentSize;
|
||
|
}
|
||
|
|
||
|
UINTN
|
||
|
MachoRuntimeGetEntryAddress (
|
||
|
IN VOID *Image
|
||
|
)
|
||
|
{
|
||
|
MACH_HEADER_ANY *Header;
|
||
|
BOOLEAN Is64Bit;
|
||
|
UINT32 NumCmds;
|
||
|
MACH_LOAD_COMMAND *Cmd;
|
||
|
UINTN Index;
|
||
|
MACH_THREAD_COMMAND *ThreadCmd;
|
||
|
MACH_X86_THREAD_STATE *ThreadState;
|
||
|
UINTN Address;
|
||
|
|
||
|
Address = 0;
|
||
|
Header = (MACH_HEADER_ANY *) Image;
|
||
|
|
||
|
if (Header->Signature == MACH_HEADER_SIGNATURE) {
|
||
|
//
|
||
|
// 32-bit header.
|
||
|
//
|
||
|
Is64Bit = FALSE;
|
||
|
NumCmds = Header->Header32.NumCommands;
|
||
|
Cmd = &Header->Header32.Commands[0];
|
||
|
} else if (Header->Signature == MACH_HEADER_64_SIGNATURE) {
|
||
|
//
|
||
|
// 64-bit header.
|
||
|
//
|
||
|
Is64Bit = TRUE;
|
||
|
NumCmds = Header->Header64.NumCommands;
|
||
|
Cmd = &Header->Header64.Commands[0];
|
||
|
} else {
|
||
|
//
|
||
|
// Invalid Mach-O image.
|
||
|
//
|
||
|
return Address;
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// Iterate over load commands.
|
||
|
//
|
||
|
for (Index = 0; Index < NumCmds; ++Index) {
|
||
|
if (Cmd->CommandType == MACH_LOAD_COMMAND_UNIX_THREAD) {
|
||
|
ThreadCmd = (MACH_THREAD_COMMAND *) Cmd;
|
||
|
ThreadState = (MACH_X86_THREAD_STATE *) &ThreadCmd->ThreadState[0];
|
||
|
Address = Is64Bit ? ThreadState->State64.rip : ThreadState->State32.eip;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Cmd = NEXT_MACH_LOAD_COMMAND (Cmd);
|
||
|
}
|
||
|
|
||
|
return Address;
|
||
|
}
|