mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
960 lines
24 KiB
C
960 lines
24 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 <Library/BaseLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/OcGuardLib.h>
|
|
#include <Library/OcMachoLib.h>
|
|
|
|
#include "OcMachoLibInternal.h"
|
|
|
|
/**
|
|
Returns the size of a Mach-O Context.
|
|
|
|
**/
|
|
UINTN
|
|
MachoGetContextSize (
|
|
VOID
|
|
)
|
|
{
|
|
return sizeof (OC_MACHO_CONTEXT);
|
|
}
|
|
|
|
/**
|
|
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;
|
|
}
|
|
|
|
/**
|
|
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);
|
|
//
|
|
// Verify Mach-O Header sanity.
|
|
// The initial checks on the input data are ASSERTs because the caller is
|
|
// expected to submit a valid buffer.
|
|
//
|
|
MachHeader = (MACH_HEADER_64 *)FileData;
|
|
TopOfFile = ((UINTN)MachHeader + FileSize);
|
|
|
|
ASSERT (FileSize >= sizeof (*MachHeader));
|
|
ASSERT (OC_ALIGNED (MachHeader));
|
|
ASSERT (TopOfFile > (UINTN)MachHeader);
|
|
|
|
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) {
|
|
ASSERT (FALSE);
|
|
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))) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
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;
|
|
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
UINT64 Address;
|
|
|
|
ASSERT (Context != NULL);
|
|
|
|
LastAddress = 0;
|
|
|
|
Segment = NULL;
|
|
while (MachoGetNextSegment64 (Context, &Segment)) {
|
|
if (Segment == NULL) {
|
|
return LastAddress;
|
|
}
|
|
|
|
Address = (Segment->VirtualAddress + Segment->Size);
|
|
|
|
if (Address > LastAddress) {
|
|
LastAddress = Address;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
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.
|
|
@param[out] Uuid The pointer the UUID command is returned into.
|
|
NULL if unavailable. Undefined if FALSE is returned.
|
|
|
|
@return Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetUuid64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
OUT MACH_UUID_COMMAND **Uuid
|
|
)
|
|
{
|
|
MACH_UUID_COMMAND *UuidCommand;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Uuid != NULL);
|
|
|
|
UuidCommand = (MACH_UUID_COMMAND *)(
|
|
InternalGetNextCommand64 (
|
|
Context,
|
|
MACH_LOAD_COMMAND_UUID,
|
|
NULL
|
|
)
|
|
);
|
|
if ((UuidCommand != NULL)
|
|
&& (!OC_ALIGNED (UuidCommand)
|
|
|| (UuidCommand->CommandSize != sizeof (*UuidCommand)))) {
|
|
return FALSE;
|
|
}
|
|
|
|
*Uuid = UuidCommand;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
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.
|
|
@param[out] Segment Pointer the segment is returned in.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@return Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetSegmentByName64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN CONST CHAR8 *SegmentName,
|
|
OUT MACH_SEGMENT_COMMAND_64 **Segment
|
|
)
|
|
{
|
|
MACH_SEGMENT_COMMAND_64 *SegmentTemp;
|
|
INTN Result;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (SegmentName != NULL);
|
|
ASSERT (Segment != NULL);
|
|
|
|
Result = 0;
|
|
|
|
SegmentTemp = NULL;
|
|
while (MachoGetNextSegment64 (Context, &SegmentTemp)) {
|
|
if (SegmentTemp != NULL) {
|
|
Result = AsciiStrnCmp (
|
|
SegmentTemp->SegmentName,
|
|
SegmentName,
|
|
ARRAY_SIZE (SegmentTemp->SegmentName)
|
|
);
|
|
}
|
|
|
|
if (Result == 0) {
|
|
*Segment = SegmentTemp;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
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
|
|
)
|
|
{
|
|
UINT32 FileSize;
|
|
UINT32 TopOffset;
|
|
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) {
|
|
return FALSE;
|
|
}
|
|
|
|
TopOfSegment = (Segment->VirtualAddress + Segment->Size);
|
|
Result = OcOverflowAddU64 (
|
|
Section->Address,
|
|
Section->Size,
|
|
&TopOfSection
|
|
);
|
|
if (!Result || (TopOfSection > TopOfSegment)) {
|
|
return FALSE;
|
|
}
|
|
|
|
FileSize = MachoGetFileSize (Context);
|
|
Result = OcOverflowAddU32 (
|
|
Section->Offset,
|
|
Section->Size,
|
|
&TopOffset
|
|
);
|
|
if (!Result || (TopOffset > FileSize)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (Section->NumRelocations != 0) {
|
|
Result = OcOverflowMulAddU32 (
|
|
Section->NumRelocations,
|
|
sizeof (MACH_RELOCATION_INFO),
|
|
Section->RelocationsOffset,
|
|
&TopOffset
|
|
);
|
|
if (!Result || (TopOffset > 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.
|
|
@param[out] Section Pointer the section is returned in.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@return Whether all inspected sections are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetSectionByName64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN MACH_SEGMENT_COMMAND_64 *Segment,
|
|
IN CONST CHAR8 *SectionName,
|
|
OUT MACH_SECTION_64 **Section
|
|
)
|
|
{
|
|
INTN Result;
|
|
MACH_SECTION_64 *SectionTemp;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Segment != NULL);
|
|
ASSERT (SectionName != NULL);
|
|
ASSERT (Section != NULL);
|
|
|
|
SectionTemp = NULL;
|
|
while (MachoGetNextSection64 (Context, Segment, &SectionTemp)) {
|
|
if (SectionTemp != NULL) {
|
|
Result = AsciiStrnCmp (
|
|
SectionTemp->SectionName,
|
|
SectionName,
|
|
ARRAY_SIZE (SectionTemp->SectionName)
|
|
);
|
|
if (Result != 0) {
|
|
continue;
|
|
}
|
|
//
|
|
// 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.
|
|
//
|
|
DEBUG_CODE (
|
|
Result = AsciiStrnCmp (
|
|
SectionTemp->SegmentName,
|
|
Segment->SegmentName,
|
|
MIN (
|
|
ARRAY_SIZE (SectionTemp->SegmentName),
|
|
ARRAY_SIZE (Segment->SegmentName)
|
|
)
|
|
);
|
|
ASSERT (Result == 0);
|
|
);
|
|
}
|
|
|
|
*Section = SectionTemp;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
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.
|
|
@param[out] Section Pointer the section is returned in.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@retval Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetSegmentSectionByName64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN CONST CHAR8 *SegmentName,
|
|
IN CONST CHAR8 *SectionName,
|
|
OUT MACH_SECTION_64 **Section
|
|
)
|
|
{
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (SegmentName != NULL);
|
|
ASSERT (SectionName != NULL);
|
|
ASSERT (Section != NULL);
|
|
|
|
if (!MachoGetSegmentByName64 (Context, SegmentName, &Segment)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if (Segment == NULL) {
|
|
*Section = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
return MachoGetSectionByName64 (
|
|
Context,
|
|
Segment,
|
|
SectionName,
|
|
Section
|
|
);
|
|
}
|
|
|
|
/**
|
|
Retrieves the next segment.
|
|
|
|
@param[in,out] Context Context of the Mach-O.
|
|
@param[in] Segment On input, the segment to retrieve the successor of.
|
|
If NULL, the first segment is returned.
|
|
On output, the following segment is returned. If no
|
|
more segment is defined, NULL is returned.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@retval Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetNextSegment64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN MACH_SEGMENT_COMMAND_64 **Segment
|
|
)
|
|
{
|
|
MACH_SEGMENT_COMMAND_64 *SegmentTemp;
|
|
CONST MACH_HEADER_64 *MachHeader;
|
|
UINTN TopOfCommands;
|
|
BOOLEAN Result;
|
|
UINT64 TopOfSegment;
|
|
UINTN TopOfSections;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Segment != NULL);
|
|
|
|
ASSERT (Context->MachHeader != NULL);
|
|
ASSERT (Context->FileSize > 0);
|
|
|
|
SegmentTemp = *Segment;
|
|
|
|
if (SegmentTemp != NULL) {
|
|
MachHeader = Context->MachHeader;
|
|
TopOfCommands = ((UINTN)MachHeader->Commands + MachHeader->CommandsSize);
|
|
ASSERT (
|
|
((UINTN)SegmentTemp >= (UINTN)&MachHeader->Commands[0])
|
|
&& ((UINTN)SegmentTemp < TopOfCommands)
|
|
);
|
|
}
|
|
|
|
SegmentTemp = (MACH_SEGMENT_COMMAND_64 *)(
|
|
InternalGetNextCommand64 (
|
|
Context,
|
|
MACH_LOAD_COMMAND_SEGMENT_64,
|
|
(MACH_LOAD_COMMAND *)SegmentTemp
|
|
)
|
|
);
|
|
if (SegmentTemp != NULL) {
|
|
if (!OC_ALIGNED (SegmentTemp)
|
|
|| (SegmentTemp->CommandSize != sizeof (*SegmentTemp))) {
|
|
return FALSE;
|
|
}
|
|
|
|
Result = OcOverflowMulAddUN (
|
|
SegmentTemp->NumSections,
|
|
sizeof (*SegmentTemp->Sections),
|
|
(UINTN)SegmentTemp->Sections,
|
|
&TopOfSections
|
|
);
|
|
if (!Result
|
|
|| (((UINTN)SegmentTemp + SegmentTemp->CommandSize) != TopOfSections)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Result = OcOverflowAddU64 (
|
|
SegmentTemp->FileOffset,
|
|
SegmentTemp->FileSize,
|
|
&TopOfSegment
|
|
);
|
|
if (!Result || (TopOfSegment > Context->FileSize)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*Segment = SegmentTemp;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
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 On input, The section to get the successor of.
|
|
If NULL, the first section is returned.
|
|
On output, the successor of the input section.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@return Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetNextSection64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN MACH_SEGMENT_COMMAND_64 *Segment,
|
|
IN MACH_SECTION_64 **Section
|
|
)
|
|
{
|
|
MACH_SECTION_64 *SectionTemp;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Segment != NULL);
|
|
ASSERT (Section != NULL);
|
|
|
|
SectionTemp = *Section;
|
|
|
|
if (SectionTemp != NULL) {
|
|
ASSERT (SectionTemp >= Segment->Sections);
|
|
++SectionTemp;
|
|
} else {
|
|
SectionTemp = &Segment->Sections[0];
|
|
}
|
|
|
|
if (SectionTemp >= &Segment->Sections[Segment->NumSections]) {
|
|
*Section = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
if (!InternalSectionIsSane (Context, SectionTemp, Segment)) {
|
|
return FALSE;
|
|
}
|
|
|
|
*Section = SectionTemp;
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Retrieves a section by its index.
|
|
|
|
@param[in,out] Context Context of the Mach-O.
|
|
@param[in] Index Index of the section to retrieve.
|
|
@param[out] Section Pointer the section is returned into.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@return Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetSectionByIndex64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN UINT32 Index,
|
|
OUT MACH_SECTION_64 **Section
|
|
)
|
|
{
|
|
MACH_SECTION_64 *SectionTemp;
|
|
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
UINT32 SectionIndex;
|
|
UINT32 NextSectionIndex;
|
|
BOOLEAN Result;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Section != NULL);
|
|
|
|
SectionIndex = 0;
|
|
|
|
Segment = NULL;
|
|
while (MachoGetNextSegment64 (Context, &Segment)) {
|
|
if (Segment == NULL) {
|
|
*Section = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
Result = OcOverflowAddU32 (
|
|
SectionIndex,
|
|
Segment->NumSections,
|
|
&NextSectionIndex
|
|
);
|
|
//
|
|
// If NextSectionIndex is wrapping around, Index must be contained.
|
|
//
|
|
if (!Result || (Index < NextSectionIndex)) {
|
|
SectionTemp = &Segment->Sections[Index - SectionIndex];
|
|
if (!InternalSectionIsSane (Context, SectionTemp, Segment)) {
|
|
break;
|
|
}
|
|
|
|
*Section = SectionTemp;
|
|
return TRUE;
|
|
}
|
|
|
|
SectionIndex = NextSectionIndex;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Retrieves a section by its address.
|
|
|
|
@param[in,out] Context Context of the Mach-O.
|
|
@param[in] Address Address of the section to retrieve.
|
|
@param[out] Section Pointer the section is returned in.
|
|
If FALSE is returned, the output is undefined.
|
|
|
|
@return Whether the inspected binary elements are sane.
|
|
|
|
**/
|
|
BOOLEAN
|
|
MachoGetSectionByAddress64 (
|
|
IN OUT OC_MACHO_CONTEXT *Context,
|
|
IN UINT64 Address,
|
|
OUT MACH_SECTION_64 **Section
|
|
)
|
|
{
|
|
BOOLEAN Result;
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
MACH_SECTION_64 *SectionWalker;
|
|
UINT64 TopOfSegment;
|
|
|
|
ASSERT (Context != NULL);
|
|
ASSERT (Section != NULL);
|
|
|
|
Segment = NULL;
|
|
while (MachoGetNextSegment64 (Context, &Segment)) {
|
|
if (Segment == NULL) {
|
|
*Section = NULL;
|
|
return TRUE;
|
|
}
|
|
|
|
TopOfSegment = (Segment->VirtualAddress + Segment->Size);
|
|
if ((Address >= Segment->VirtualAddress) && (Address < TopOfSegment)) {
|
|
Result = FALSE;
|
|
|
|
SectionWalker = NULL;
|
|
while (MachoGetNextSection64 (Context, Segment, &SectionWalker)) {
|
|
if (SectionWalker == NULL) {
|
|
Result = TRUE;
|
|
break;
|
|
}
|
|
|
|
if ((Address >= SectionWalker->Address)
|
|
&& (Address < (SectionWalker->Address + SectionWalker->Size))) {
|
|
*Section = SectionWalker;
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
if (!Result) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
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);
|
|
ASSERT (Context->SymbolTable == NULL);
|
|
|
|
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 (StringTable[(Symtab->StringsSize / sizeof (*StringTable)) - 1] != '\0') {
|
|
return FALSE;
|
|
}
|
|
|
|
SymbolTable = (MACH_NLIST_64 *)(MachoAddress + Symtab->SymbolsOffset);
|
|
if (!OC_ALIGNED (SymbolTable)) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// 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->IndirectSymbolTable = IndirectSymtab;
|
|
Context->LocalRelocations = LocalRelocations;
|
|
Context->ExternRelocations = ExternRelocations;
|
|
|
|
return TRUE;
|
|
}
|