mirror of
https://github.com/acidanthera/OpenCorePkg.git
synced 2025-12-08 19:25:01 +00:00
1472 lines
42 KiB
C
1472 lines
42 KiB
C
/** @file
|
|
Library handling KEXT prelinking.
|
|
Currently limited to Intel 64 architectures.
|
|
|
|
Copyright (c) 2018, Download-Fritz. All rights reserved.<BR>
|
|
This program and the accompanying materials are licensed and made available
|
|
under the terms and conditions of the BSD License which accompanies this
|
|
distribution. The full text of the license may be found at
|
|
http://opensource.org/licenses/bsd-license.php.
|
|
|
|
THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,
|
|
WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.
|
|
|
|
**/
|
|
|
|
#include <Base.h>
|
|
|
|
#include <IndustryStandard/AppleMachoImage.h>
|
|
|
|
#include <Library/BaseLib.h>
|
|
#include <Library/BaseMemoryLib.h>
|
|
#include <Library/DebugLib.h>
|
|
#include <Library/MemoryAllocationLib.h>
|
|
#include <Library/OcGuardLib.h>
|
|
#include <Library/OcMachoLib.h>
|
|
#include <Library/OcAppleKernelLib.h>
|
|
|
|
#include "Link.h"
|
|
|
|
//
|
|
// Symbols
|
|
//
|
|
|
|
CONST OC_SYMBOL_64 *
|
|
InternalOcGetSymbolByName (
|
|
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
|
|
IN CONST CHAR8 *Name,
|
|
IN BOOLEAN CheckIndirect
|
|
)
|
|
{
|
|
CONST OC_SYMBOL_TABLE_64 *SymbolsWalker;
|
|
CONST OC_SYMBOL_64 *Symbols;
|
|
UINT32 NumSymbols;
|
|
UINT32 CxxIndex;
|
|
UINT32 Index;
|
|
INTN Result;
|
|
|
|
ASSERT (DefinedSymbols != NULL);
|
|
ASSERT (Name != NULL);
|
|
|
|
SymbolsWalker = DefinedSymbols;
|
|
|
|
do {
|
|
Symbols = SymbolsWalker->Symbols;
|
|
NumSymbols = SymbolsWalker->NumSymbols;
|
|
|
|
if (SymbolsWalker->IsIndirect) {
|
|
if (!CheckIndirect) {
|
|
continue;
|
|
}
|
|
//
|
|
// Only consider C++ symbols for indirect dependencies.
|
|
//
|
|
CxxIndex = (SymbolsWalker->NumSymbols - SymbolsWalker->NumCxxSymbols);
|
|
Symbols = &Symbols[CxxIndex];
|
|
NumSymbols = SymbolsWalker->NumCxxSymbols;
|
|
}
|
|
|
|
for (Index = 0; Index < NumSymbols; ++Index) {
|
|
Result = AsciiStrCmp (
|
|
Name,
|
|
(SymbolsWalker->StringTable + Symbols[Index].StringIndex)
|
|
);
|
|
if (Result == 0) {
|
|
return &Symbols[Index];
|
|
}
|
|
}
|
|
|
|
SymbolsWalker = GET_OC_SYMBOL_TABLE_64_FROM_LINK (
|
|
GetNextNode (
|
|
&DefinedSymbols->Link,
|
|
&SymbolsWalker->Link
|
|
)
|
|
);
|
|
} while (!IsNull (&DefinedSymbols->Link, &SymbolsWalker->Link));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
Patches Symbol with Value and marks it as solved.
|
|
|
|
@param[in] Value The value to solve Symbol with.
|
|
@param[out] Symbol The symbol to solve.
|
|
|
|
**/
|
|
VOID
|
|
InternalSolveSymbolValue64 (
|
|
IN UINT64 Value,
|
|
OUT MACH_NLIST_64 *Symbol
|
|
)
|
|
{
|
|
Symbol->Value = Value;
|
|
Symbol->Type = (MACH_N_TYPE_ABS | MACH_N_TYPE_EXT);
|
|
Symbol->Section = NO_SECT;
|
|
}
|
|
|
|
/**
|
|
Worker function to solve Symbol against the specified DefinedSymbols.
|
|
It does not consider Symbol might be a weak reference.
|
|
|
|
@param[in,out] MachoContext Context of the Mach-O.
|
|
@param[in] DefinedSymbols List of defined symbols of all dependencies.
|
|
@param[in] Name The name of the symbol to resolve against.
|
|
@param[in,out] Symbol The symbol to be resolved.
|
|
|
|
@retval Returned is whether the symbol was solved successfully.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
InternalSolveSymbolNonWeak64 (
|
|
IN OUT OC_MACHO_CONTEXT *MachoContext,
|
|
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
|
|
IN CONST CHAR8 *Name,
|
|
IN OUT MACH_NLIST_64 *Symbol
|
|
)
|
|
{
|
|
INTN Result;
|
|
CONST OC_SYMBOL_64 *ResolveSymbol;
|
|
|
|
if (Symbol->Type != MACH_N_TYPE_UNDF) {
|
|
if (Symbol->Type != MACH_N_TYPE_INDR) {
|
|
//
|
|
// KXLD_WEAK_TEST_SYMBOL might have been resolved by the resolving code
|
|
// at the end of InternalSolveSymbol64.
|
|
//
|
|
Result = AsciiStrCmp (
|
|
MachoGetSymbolName64 (MachoContext, Symbol),
|
|
KXLD_WEAK_TEST_SYMBOL
|
|
);
|
|
if (Result == 0) {
|
|
//
|
|
// KXLD_WEAK_TEST_SYMBOL has been solved successfully already.
|
|
//
|
|
return TRUE;
|
|
}
|
|
//
|
|
// Any other symbols must be undefined or indirect.
|
|
//
|
|
ASSERT (FALSE);
|
|
}
|
|
} else if (Symbol->Value != 0) {
|
|
//
|
|
// Common symbols are not supported.
|
|
//
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
ResolveSymbol = InternalOcGetSymbolByName (
|
|
DefinedSymbols,
|
|
Name,
|
|
FALSE
|
|
);
|
|
if (ResolveSymbol != NULL) {
|
|
InternalSolveSymbolValue64 (ResolveSymbol->Value, Symbol);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
Solves Symbol against the specified DefinedSymbols.
|
|
|
|
@param[in] MachoContext Context of the Mach-O.
|
|
@param[in] DefinedSymbols List of defined symbols of all dependencies.
|
|
@param[in] Name The name of the symbol to resolve against.
|
|
@param[in,out] Symbol The symbol to be resolved.
|
|
@param[in,out] WeakTestValue Points to the value of the Weak test symbol.
|
|
If it is 0 on input, it will get assigned
|
|
the value on the first resolution of a weak
|
|
symbol.
|
|
@param[in] UndefinedSymbols Array of the Mach-O'S undefined symbols.
|
|
@param[in] NumUndefinedSymbols Number of symbols in UndefinedSymbols.
|
|
|
|
@retval Returned is whether the symbol was solved successfully. For weak
|
|
symbols, this includes solving with _gOSKextUnresolved.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
InternalSolveSymbol64 (
|
|
IN OUT OC_MACHO_CONTEXT *MachoContext,
|
|
IN CONST OC_SYMBOL_TABLE_64 *DefinedSymbols,
|
|
IN CONST CHAR8 *Name,
|
|
IN OUT MACH_NLIST_64 *Symbol,
|
|
IN OUT UINT64 *WeakTestValue,
|
|
IN CONST MACH_NLIST_64 *UndefinedSymbols,
|
|
IN UINT32 NumUndefinedSymbols
|
|
)
|
|
{
|
|
BOOLEAN Success;
|
|
UINT32 Index;
|
|
INTN Result;
|
|
UINT64 Value;
|
|
CONST MACH_NLIST_64 *WeakTestSymbol;
|
|
|
|
ASSERT (Symbol != NULL);
|
|
ASSERT ((Symbol->Type & MACH_N_TYPE_STAB) == 0);
|
|
if (NumUndefinedSymbols != 0) {
|
|
ASSERT (UndefinedSymbols != NULL);
|
|
}
|
|
|
|
Success = InternalSolveSymbolNonWeak64 (
|
|
MachoContext,
|
|
DefinedSymbols,
|
|
Name,
|
|
Symbol
|
|
);
|
|
if (Success) {
|
|
return TRUE;
|
|
}
|
|
|
|
if (((Symbol->Type & MACH_N_TYPE_STAB) == 0)
|
|
&& ((Symbol->Descriptor & MACH_N_WEAK_DEF) != 0)) {
|
|
//
|
|
// KXLD_WEAK_TEST_SYMBOL is not going to be defined or exposed by a KEXT
|
|
// prelinked by this library, hence only check the undefined symbols region
|
|
// for matches.
|
|
//
|
|
Value = *WeakTestValue;
|
|
|
|
if (Value == 0) {
|
|
for (Index = 0; Index < NumUndefinedSymbols; ++Index) {
|
|
WeakTestSymbol = &UndefinedSymbols[Index];
|
|
Result = AsciiStrCmp (
|
|
MachoGetSymbolName64 (MachoContext, WeakTestSymbol),
|
|
KXLD_WEAK_TEST_SYMBOL
|
|
);
|
|
if (Result == 0) {
|
|
if (WeakTestSymbol->Type == MACH_N_TYPE_UNDF) {
|
|
Success = InternalSolveSymbolNonWeak64 (
|
|
MachoContext,
|
|
DefinedSymbols,
|
|
Name,
|
|
Symbol
|
|
);
|
|
if (!Success) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
Value = WeakTestSymbol->Value;
|
|
ASSERT (Value != 0);
|
|
|
|
*WeakTestValue = Value;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Value != 0) {
|
|
InternalSolveSymbolValue64 (Value, Symbol);
|
|
return TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Relocations
|
|
//
|
|
|
|
/**
|
|
Calculate the target address' displacement for the Intel 64 platform.
|
|
Instruction will be patched with the resulting address.
|
|
Logically matches XNU's calculate_displacement_x86_64.
|
|
|
|
@param[in] Target The target address.
|
|
@param[in] Adjustment Adjustment to be subtracted from the
|
|
displacement.
|
|
@param[in,out] Instruction Pointer to the instruction to be patched.
|
|
|
|
@retval Returned is whether the target instruction has been patched.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
InternalCalculateDisplacementIntel64 (
|
|
IN UINT64 Target,
|
|
IN UINT64 Adjustment,
|
|
IN OUT INT32 *Instruction
|
|
)
|
|
{
|
|
INT64 Displacement;
|
|
UINT64 Difference;
|
|
|
|
ASSERT (Instruction != NULL);
|
|
|
|
Displacement = ((*Instruction + Target) - Adjustment);
|
|
Difference = ABS (Displacement);
|
|
|
|
if (Difference >= X86_64_RIP_RELATIVE_LIMIT) {
|
|
return FALSE;
|
|
}
|
|
|
|
*Instruction = (INT32)Displacement;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Calculate the target addresses for Relocation and NextRelocation.
|
|
Logically matches XNU's calculate_targets.
|
|
|
|
@param[in] MachoContext Mach-O context of the KEXT to relocate.
|
|
@param[in] LinkAddress The address to be linked against.
|
|
@param[in] Vtables List of all dependent VTables.
|
|
@param[in] Relocation The Relocation to be resolved.
|
|
@param[in] NextRelocation The Relocation following Relocation.
|
|
@param[out] Target Relocation's target address.
|
|
@param[out] PairTarget NextRelocation's target address.
|
|
@param[out] Vtable The VTable described by the symbol referenced by
|
|
Relocation. NULL, if there is none.
|
|
|
|
@returns Whether the operation was completed successfully.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
InternalCalculateTargetsIntel64 (
|
|
IN OUT VOID *MachoContext,
|
|
IN UINT64 LinkAddress,
|
|
IN CONST OC_VTABLE_ARRAY *Vtables,
|
|
IN CONST MACH_RELOCATION_INFO *Relocation,
|
|
IN CONST MACH_RELOCATION_INFO *NextRelocation OPTIONAL,
|
|
OUT UINT64 *Target,
|
|
OUT UINT64 *PairTarget,
|
|
OUT CONST OC_VTABLE **Vtable OPTIONAL
|
|
)
|
|
{
|
|
BOOLEAN Success;
|
|
|
|
UINT64 TargetAddress;
|
|
MACH_NLIST_64 *Symbol;
|
|
CONST CHAR8 *Name;
|
|
MACH_SECTION_64 *Section;
|
|
UINT64 PairAddress;
|
|
UINT64 PairDummy;
|
|
UINT32 Index;
|
|
CONST OC_VTABLE_ARRAY *VtablesWalker;
|
|
INTN Result;
|
|
CONST OC_VTABLE *VtableWalker;
|
|
|
|
ASSERT (Target != NULL);
|
|
ASSERT (PairTarget != NULL);
|
|
|
|
Section = NULL;
|
|
PairAddress = 0;
|
|
//
|
|
// Pull out the data from the relocation entries. The target_type depends
|
|
// on the Extern bit:
|
|
// Scattered -> Section Lookup by Address.
|
|
// Local (not extern) -> Section by Index
|
|
// Extern -> Symbolnum by Index
|
|
//
|
|
// Relocations referencing a symbol result in a Target of its resolved value.
|
|
// Relocations referencing a section result in a Target of
|
|
// (link_addr - base_addr), which should be LinkAddress aligned on the
|
|
// section's boundary.
|
|
//
|
|
TargetAddress = LinkAddress;
|
|
//
|
|
// Scattered Relocations are only supported by i386.
|
|
//
|
|
ASSERT (((UINT32)Relocation->Address & MACH_RELOC_SCATTERED) == 0);
|
|
|
|
if (Relocation->Extern != 0) {
|
|
Symbol = MachoGetSymbolByIndex64 (
|
|
MachoContext,
|
|
Relocation->SymbolNumber
|
|
);
|
|
if (Symbol == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Name = MachoGetSymbolName64 (MachoContext, Symbol);
|
|
//
|
|
// If this symbol is a padslot that has already been replaced, then the
|
|
// only way a relocation entry can still reference it is if there is a
|
|
// vtable that has not been patched. The vtable patcher uses the
|
|
// MetaClass structure to find classes for patching, so an unpatched
|
|
// vtable means that there is an OSObject-dervied class that is missing
|
|
// its OSDeclare/OSDefine macros.
|
|
//
|
|
if (MachoSymbolNameIsPadslot (Name)) {
|
|
return FALSE;
|
|
}
|
|
|
|
if ((Vtable != NULL) && MachoSymbolNameIsVtable64 (Name)) {
|
|
VtablesWalker = Vtables;
|
|
|
|
while (TRUE) {
|
|
for (
|
|
Index = 0, VtableWalker = GET_FIRST_OC_VTABLE (VtablesWalker);
|
|
Index < VtablesWalker->NumVtables;
|
|
++Index, VtableWalker = GET_NEXT_OC_VTABLE (VtableWalker)
|
|
) {
|
|
Result = AsciiStrCmp (VtableWalker->Name, Name);
|
|
if (Result == 0) {
|
|
*Vtable = VtableWalker;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (Index == VtablesWalker->NumVtables) {
|
|
break;
|
|
}
|
|
|
|
VtablesWalker = GET_OC_VTABLE_ARRAY_FROM_LINK (
|
|
GetNextNode (
|
|
&Vtables->Link,
|
|
&VtablesWalker->Link
|
|
)
|
|
);
|
|
if (IsNull (&Vtables->Link, &VtablesWalker->Link)) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
TargetAddress = Symbol->Value;
|
|
} else {
|
|
Section = MachoGetSectionByIndex64 (
|
|
MachoContext,
|
|
Relocation->SymbolNumber
|
|
);
|
|
if (Section == NULL) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (Section != NULL) {
|
|
TargetAddress = ALIGN_VALUE (
|
|
(Section->Address + LinkAddress),
|
|
Section->Alignment
|
|
);
|
|
TargetAddress -= Section->Address;
|
|
}
|
|
|
|
if (MachoRelocationIsPairIntel64 (Relocation->Type)) {
|
|
ASSERT (NextRelocation != NULL);
|
|
ASSERT (MachoIsRelocationPairTypeIntel64 (NextRelocation->Type));
|
|
//
|
|
// As this relocation is the second one in a pair, it cannot be the start
|
|
// of a pair itself. Pass dummy data for the related arguments. This call
|
|
// shall never reach this very branch.
|
|
//
|
|
Success = InternalCalculateTargetsIntel64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
Vtables,
|
|
NextRelocation,
|
|
NULL,
|
|
&PairAddress,
|
|
&PairDummy,
|
|
NULL
|
|
);
|
|
if (!Success) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*Target = TargetAddress;
|
|
*PairTarget = PairAddress;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
Returns whether the VTable entry at Offset is a direct pure virtual call.
|
|
Logically macthes XNU's check_for_direct_pure_virtual_call.
|
|
|
|
@param[in] Vtable The current VTable to be inspected.
|
|
@param[in] Offset The VTable's entry offset.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
InternalIsDirectPureVirtualCall64 (
|
|
IN CONST OC_VTABLE *Vtable,
|
|
IN UINT64 Offset
|
|
)
|
|
{
|
|
UINT32 Index;
|
|
CONST OC_VTABLE_ENTRY *Entry;
|
|
|
|
if ((Offset % sizeof (UINT64)) != 0) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
Index = ((Offset - VTABLE_ENTRY_SIZE_64) / sizeof (UINT64));
|
|
Entry = &Vtable->Entries[Index];
|
|
|
|
if ((Index >= Vtable->NumEntries) || (Entry->Name == NULL)) {
|
|
return FALSE;
|
|
}
|
|
|
|
return MachoSymbolNameIsPureVirtual (Entry->Name);
|
|
}
|
|
|
|
/**
|
|
Relocates Relocation against the specified Symtab's Symbol Table and
|
|
LinkAddress. This logically matches KXLD's x86_64_process_reloc.
|
|
|
|
@param[in] MachoContext Mach-O context of the KEXT to relocate.
|
|
@param[in] LinkAddress The address to be linked against.
|
|
@param[in] Vtables List of all dependent VTables.
|
|
@param[in] RelocationBase The Relocations base address.
|
|
@param[in] Relocation The Relocation to be processed.
|
|
@param[in] NextRelocation The Relocation following Relocation.
|
|
|
|
@retval 0 The Relocation does not need to be preseved.
|
|
@retval 1 The Relocation must be preseved.
|
|
@retval 0 | BIT31 The Relocation does not need to be preseved.
|
|
The next Relocation shall be skipped.
|
|
@retval 1 | BIT31 The Relocation must be preseved.
|
|
The next Relocation shall be skipped.
|
|
@retval MAX_UINTN The Relocation's target cannot be determined or it is a
|
|
direct pure virtual call.
|
|
|
|
**/
|
|
STATIC
|
|
UINTN
|
|
InternalRelocateRelocationIntel64 (
|
|
IN OC_MACHO_CONTEXT *MachoContext,
|
|
IN UINT64 LinkAddress,
|
|
IN CONST OC_VTABLE_ARRAY *Vtables,
|
|
IN UINTN RelocationBase,
|
|
IN CONST MACH_RELOCATION_INFO *Relocation,
|
|
IN CONST MACH_RELOCATION_INFO *NextRelocation OPTIONAL
|
|
)
|
|
{
|
|
UINTN ReturnValue;
|
|
|
|
UINT8 Type;
|
|
INT32 *Instruction32Ptr;
|
|
INT32 Instruction32;
|
|
UINT64 *Instruction64Ptr;
|
|
UINT64 Instruction64;
|
|
UINT64 Target;
|
|
BOOLEAN IsPair;
|
|
UINT64 PairTarget;
|
|
CONST OC_VTABLE *Vtable;
|
|
UINT64 LinkPc;
|
|
UINT64 Adjustment;
|
|
UINT32 Length;
|
|
UINT32 Address;
|
|
UINT8 *InstructionPtr;
|
|
BOOLEAN PcRelative;
|
|
BOOLEAN IsNormalLocal;
|
|
BOOLEAN Result;
|
|
|
|
ASSERT (RelocationBase != 0);
|
|
ASSERT (Relocation != NULL);
|
|
//
|
|
// Scattered Relocations are only supported by i386.
|
|
//
|
|
ASSERT (((UINT32)Relocation->Address & MACH_RELOC_SCATTERED) == 0);
|
|
|
|
IsPair = FALSE;
|
|
Adjustment = 0;
|
|
IsNormalLocal = FALSE;
|
|
|
|
if ((Relocation->Extern == 0)
|
|
&& (Relocation->SymbolNumber == MACH_RELOC_ABSOLUTE)) {
|
|
//
|
|
// A section-based relocation entry can be skipped for absolute
|
|
// symbols.
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
Address = Relocation->Address;
|
|
Length = Relocation->Size;
|
|
Type = Relocation->Type;
|
|
PcRelative = (Relocation->PcRelative != 0);
|
|
|
|
if (Relocation->Extern == 0) {
|
|
IsNormalLocal = TRUE;
|
|
}
|
|
|
|
ASSERT ((Length == 2) || (Length == 3));
|
|
|
|
LinkPc = (Address + LinkAddress);
|
|
InstructionPtr = (UINT8 *)(RelocationBase + Address);
|
|
|
|
Vtable = NULL;
|
|
Result = InternalCalculateTargetsIntel64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
Vtables,
|
|
Relocation,
|
|
NextRelocation,
|
|
&Target,
|
|
&PairTarget,
|
|
&Vtable
|
|
);
|
|
if (!Result) {
|
|
return MAX_UINTN;
|
|
}
|
|
|
|
if (Length == 2) {
|
|
Instruction32Ptr = (INT32 *)InstructionPtr;
|
|
Instruction32 = *Instruction32Ptr;
|
|
|
|
if ((Vtable != NULL)
|
|
&& InternalIsDirectPureVirtualCall64 (Vtable, Instruction32)) {
|
|
return MAX_UINTN;
|
|
}
|
|
//
|
|
// There are a number of different small adjustments for pc-relative
|
|
// relocation entries. The general case is to subtract the size of the
|
|
// relocation (represented by the length parameter), and it applies to
|
|
// the GOT types and external SIGNED types. The non-external signed types
|
|
// have a different adjustment corresponding to the specific type.
|
|
//
|
|
switch (Type) {
|
|
case MachX8664RelocSigned:
|
|
if (IsNormalLocal) {
|
|
Adjustment = 0;
|
|
break;
|
|
}
|
|
// Fall through
|
|
case MachX8664RelocSigned1:
|
|
if (IsNormalLocal) {
|
|
Adjustment = 1;
|
|
break;
|
|
}
|
|
// Fall through
|
|
case MachX8664RelocSigned2:
|
|
if (IsNormalLocal) {
|
|
Adjustment = 2;
|
|
break;
|
|
}
|
|
// Fall through
|
|
case MachX8664RelocSigned4:
|
|
if (IsNormalLocal) {
|
|
Adjustment = 4;
|
|
break;
|
|
}
|
|
// Fall through
|
|
case MachX8664RelocBranch:
|
|
case MachX8664RelocGot:
|
|
case MachX8664RelocGotLoad:
|
|
{
|
|
Adjustment = (1ULL << Length);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
//
|
|
// Perform the actual relocation. All of the 32-bit relocations are
|
|
// pc-relative except for SUBTRACTOR, so a good chunk of the logic is
|
|
// stuck in calculate_displacement_x86_64. The signed relocations are
|
|
// a special case, because when they are non-external, the instruction
|
|
// already contains the pre-relocation displacement, so we only need to
|
|
// find the difference between how far the PC was relocated, and how
|
|
// far the target is relocated. Since the target variable already
|
|
// contains the difference between the target's base and link
|
|
// addresses, we add the difference between the PC's base and link
|
|
// addresses to the adjustment variable. This will yield the
|
|
// appropriate displacement in calculate_displacement.
|
|
//
|
|
switch (Type) {
|
|
case MachX8664RelocBranch:
|
|
{
|
|
ASSERT (PcRelative);
|
|
Adjustment += LinkPc;
|
|
break;
|
|
}
|
|
|
|
case MachX8664RelocSigned:
|
|
case MachX8664RelocSigned1:
|
|
case MachX8664RelocSigned2:
|
|
case MachX8664RelocSigned4:
|
|
{
|
|
ASSERT (PcRelative);
|
|
Adjustment += (IsNormalLocal ? LinkAddress : LinkPc);
|
|
break;
|
|
}
|
|
|
|
case MachX8664RelocGot:
|
|
case MachX8664RelocGotLoad:
|
|
{
|
|
ASSERT (PcRelative);
|
|
Adjustment += LinkPc;
|
|
Target = PairTarget;
|
|
IsPair = TRUE;
|
|
break;
|
|
}
|
|
|
|
case MachX8664RelocSubtractor:
|
|
{
|
|
ASSERT (!PcRelative);
|
|
Instruction32 = (INT32)(Target - PairTarget);
|
|
IsPair = TRUE;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (PcRelative) {
|
|
Result = InternalCalculateDisplacementIntel64 (
|
|
Target,
|
|
Adjustment,
|
|
&Instruction32
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*Instruction32Ptr = Instruction32;
|
|
} else {
|
|
Instruction64Ptr = (UINT64 *)InstructionPtr;
|
|
Instruction64 = *Instruction64Ptr;
|
|
|
|
if ((Vtable != NULL)
|
|
&& InternalIsDirectPureVirtualCall64 (Vtable, Instruction64)) {
|
|
return MAX_UINTN;
|
|
}
|
|
|
|
switch (Type) {
|
|
case MachX8664RelocUnsigned:
|
|
{
|
|
ASSERT (!PcRelative);
|
|
Instruction64 += Target;
|
|
break;
|
|
}
|
|
|
|
case MachX8664RelocSubtractor:
|
|
{
|
|
ASSERT (!PcRelative);
|
|
Instruction64 = (Target - PairTarget);
|
|
IsPair = TRUE;
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
*Instruction64Ptr = Instruction64;
|
|
}
|
|
|
|
ReturnValue = (MachoPreserveRelocationIntel64 (Type) ? 1 : 0);
|
|
|
|
if (IsPair) {
|
|
ReturnValue |= BIT31;
|
|
}
|
|
|
|
return ReturnValue;
|
|
}
|
|
|
|
/**
|
|
Relocates all Mach-O Relocations and copies the ones to be preserved after
|
|
prelinking to TargetRelocations.
|
|
|
|
@param[in] MachoContext Mach-O context of the KEXT to relocate.
|
|
@param[in] LinkAddress The address to be linked against.
|
|
@param[in] Vtables The patched VTables of this KEXT and its
|
|
dependencies.
|
|
@param[in] RelocationBase The Relocations base address.
|
|
@param[in] SourceRelocations The Relocations source buffer.
|
|
@param[in] NumRelocations On input, the number of source Relocations.
|
|
On output, the number of Relocations to
|
|
preserve.
|
|
@param[out] TargetRelocations The Relocations destination buffer.
|
|
|
|
@retval Returned is the number of preserved Relocations.
|
|
|
|
**/
|
|
STATIC
|
|
BOOLEAN
|
|
InternalRelocateAndCopyRelocations64 (
|
|
IN OC_MACHO_CONTEXT *MachoContext,
|
|
IN UINT64 LinkAddress,
|
|
IN CONST OC_VTABLE_ARRAY *Vtables,
|
|
IN UINTN RelocationBase,
|
|
IN CONST MACH_RELOCATION_INFO *SourceRelocations,
|
|
IN OUT UINT32 *NumRelocations,
|
|
OUT MACH_RELOCATION_INFO *TargetRelocations
|
|
)
|
|
{
|
|
UINT32 PreservedRelocations;
|
|
|
|
UINT32 Index;
|
|
CONST MACH_RELOCATION_INFO *NextRelocation;
|
|
UINTN Result;
|
|
MACH_RELOCATION_INFO *Relocation;
|
|
|
|
ASSERT (MachoContext != NULL);
|
|
ASSERT (RelocationBase != 0);
|
|
ASSERT (SourceRelocations != NULL);
|
|
ASSERT (NumRelocations != NULL);
|
|
ASSERT (NumRelocations > 0);
|
|
ASSERT (TargetRelocations != NULL);
|
|
|
|
PreservedRelocations = 0;
|
|
|
|
for (Index = 0; Index < *NumRelocations; ++Index) {
|
|
NextRelocation = &SourceRelocations[Index + 1];
|
|
//
|
|
// The last Relocation does not have a successor.
|
|
//
|
|
if (Index == (*NumRelocations - 1)) {
|
|
NextRelocation = NULL;
|
|
}
|
|
//
|
|
// Relocate the relocation.
|
|
//
|
|
Result = InternalRelocateRelocationIntel64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
Vtables,
|
|
RelocationBase,
|
|
&SourceRelocations[Index],
|
|
NextRelocation
|
|
);
|
|
if (Result == MAX_UINTN) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Copy the Relocation to the destination buffer if it shall be preserved.
|
|
//
|
|
if ((Result & ~(UINTN)BIT31) != 0) {
|
|
Relocation = &TargetRelocations[PreservedRelocations];
|
|
//
|
|
// Scattered Relocations are only supported by i386.
|
|
//
|
|
ASSERT (((UINT32)Relocation->Address & MACH_RELOC_SCATTERED) == 0);
|
|
|
|
CopyMem (Relocation, &SourceRelocations[Index], sizeof (*Relocation));
|
|
|
|
if (Relocation->Extern != 0) {
|
|
//
|
|
// All relocation targets have been updated with symbol values.
|
|
// Convert to a local relocation as only sliding is supported from now
|
|
// on.
|
|
//
|
|
Relocation->Extern = 0;
|
|
//
|
|
// Assertion: The entire kext will be slid by the same offset.
|
|
// This is asserted by KXLD as well.
|
|
//
|
|
Relocation->SymbolNumber = 1;
|
|
}
|
|
|
|
++PreservedRelocations;
|
|
}
|
|
//
|
|
// Skip the next Relocation as instructed by
|
|
// InternalRelocateRelocationIntel64().
|
|
//
|
|
if ((Result & BIT31) != 0) {
|
|
++Index;
|
|
}
|
|
}
|
|
|
|
*NumRelocations = PreservedRelocations;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// MACH header
|
|
//
|
|
|
|
/**
|
|
Strip superfluous Load Commands from the Mach-O header. This includes the
|
|
Code Signature Load Command which must be removed for the binary has been
|
|
modified by the prelinking routines.
|
|
|
|
@param[in,out] MachHeader Mach-O header to strip the Load Commands from.
|
|
|
|
**/
|
|
STATIC
|
|
VOID
|
|
InternalStripLoadCommands64 (
|
|
IN OUT MACH_HEADER_64 *MachHeader
|
|
)
|
|
{
|
|
STATIC CONST MACH_LOAD_COMMAND_TYPE LoadCommandsToStrip[] = {
|
|
MACH_LOAD_COMMAND_CODE_SIGNATURE,
|
|
MACH_LOAD_COMMAND_DYLD_INFO,
|
|
MACH_LOAD_COMMAND_DYLD_INFO_ONLY,
|
|
MACH_LOAD_COMMAND_FUNCTION_STARTS,
|
|
MACH_LOAD_COMMAND_DATA_IN_CODE,
|
|
MACH_LOAD_COMMAND_DYLIB_CODE_SIGN_DRS
|
|
};
|
|
|
|
UINT32 Index;
|
|
UINT32 Index2;
|
|
MACH_LOAD_COMMAND *LoadCommand;
|
|
UINT32 SizeOfLeftCommands;
|
|
//
|
|
// Delete the Code Signature Load Command if existent as we modified the
|
|
// binary, as well as linker metadata not needed for runtime operation.
|
|
//
|
|
LoadCommand = MachHeader->Commands;
|
|
SizeOfLeftCommands = MachHeader->CommandsSize;
|
|
|
|
for (Index = 0; Index < MachHeader->NumCommands; ++Index) {
|
|
//
|
|
// LC_UNIXTHREAD and LC_MAIN are technically stripped in KXLD, but they are
|
|
// not supposed to be present in the first place.
|
|
//
|
|
ASSERT ((LoadCommand->CommandType != MACH_LOAD_COMMAND_UNIX_THREAD)
|
|
&& (LoadCommand->CommandType != MACH_LOAD_COMMAND_MAIN));
|
|
|
|
SizeOfLeftCommands -= LoadCommand->CommandSize;
|
|
|
|
for (Index2 = 0; Index < ARRAY_SIZE (LoadCommandsToStrip); ++Index2) {
|
|
if (LoadCommand->CommandType == LoadCommandsToStrip[Index2]) {
|
|
if (Index != (MachHeader->NumCommands - 1)) {
|
|
//
|
|
// If the current Load Command is not the last one, relocate the
|
|
// subsequent ones.
|
|
//
|
|
CopyMem (
|
|
LoadCommand,
|
|
NEXT_MACH_LOAD_COMMAND (LoadCommand),
|
|
SizeOfLeftCommands
|
|
);
|
|
}
|
|
|
|
--MachHeader->NumCommands;
|
|
MachHeader->CommandsSize -= LoadCommand->CommandSize;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
LoadCommand = NEXT_MACH_LOAD_COMMAND (LoadCommand);
|
|
}
|
|
}
|
|
|
|
/**
|
|
Prelinks the specified KEXT against the specified LinkAddress and the data
|
|
of its dependencies.
|
|
|
|
@param[in,out] MachoContext Mach-O context of the KEXT to prelink.
|
|
@param[in] LinkEditSegment __LINKEDIT segment of the KEXT to prelink.
|
|
@param[in] LinkAddress The address this KEXT shall be linked
|
|
against.
|
|
@param[in] DependencyData List of data of all dependencies.
|
|
@param[in] ExposeSymbols Whether the symbol table shall be exposed.
|
|
@param[out] OutputData Buffer to output data into.
|
|
@param[out] ScratchMemory Scratch memory buffer that is at least as big
|
|
as the KEXT's __LINKEDIT segment.
|
|
|
|
@retval Returned is whether the prelinking process has been successful.
|
|
The state of the KEXT is undefined in case this routine fails.
|
|
|
|
**/
|
|
BOOLEAN
|
|
InternalPrelinkKext64 (
|
|
IN OUT OC_MACHO_CONTEXT *MachoContext,
|
|
IN MACH_SEGMENT_COMMAND_64 *LinkEditSegment,
|
|
IN UINT64 LinkAddress,
|
|
IN OC_DEPENDENCY_DATA *DependencyData,
|
|
IN BOOLEAN ExposeSymbols,
|
|
IN OUT OC_DEPENDENCY_DATA *OutputData,
|
|
OUT VOID *ScratchMemory
|
|
)
|
|
{
|
|
MACH_HEADER_64 *MachHeader;
|
|
|
|
MACH_SEGMENT_COMMAND_64 *Segment;
|
|
MACH_SECTION_64 *Section;
|
|
|
|
UINT32 Index;
|
|
BOOLEAN Result;
|
|
UINT32 SymtabSize;
|
|
UINT32 SymtabSize2;
|
|
|
|
MACH_SYMTAB_COMMAND *Symtab;
|
|
MACH_DYSYMTAB_COMMAND *DySymtab;
|
|
MACH_NLIST_64 *Symbol;
|
|
CONST CHAR8 *SymbolName;
|
|
CONST MACH_NLIST_64 *SymbolTable;
|
|
CONST CHAR8 *StringTable;
|
|
UINT32 NumSymbols;
|
|
CONST MACH_NLIST_64 *IndirectSymtab;
|
|
UINT32 NumIndirectSymbols;
|
|
CONST MACH_NLIST_64 *LocalSymtab;
|
|
UINT32 NumLocalSymbols;
|
|
CONST MACH_NLIST_64 *ExternalSymtab;
|
|
UINT32 NumExternalSymbols;
|
|
CONST MACH_NLIST_64 *UndefinedSymtab;
|
|
UINT32 NumUndefinedSymbols;
|
|
UINT64 WeakTestValue;
|
|
OC_VTABLE_PATCH_ARRAY *PatchData;
|
|
CONST OC_VTABLE_PATCH_ENTRY *VtablePatchEntry;
|
|
UINT32 VtableOffset;
|
|
CONST UINT64 *VtableData;
|
|
UINT32 VtableSize;
|
|
UINT32 VtablesSize;
|
|
OC_VTABLE_ARRAY *Vtables;
|
|
|
|
UINT32 NumRelocations;
|
|
UINT32 NumRelocations2;
|
|
UINTN RelocationBase;
|
|
CONST MACH_RELOCATION_INFO *Relocations;
|
|
MACH_RELOCATION_INFO *TargetRelocation;
|
|
MACH_SEGMENT_COMMAND_64 *FirstSegment;
|
|
|
|
VOID *LinkEdit;
|
|
UINT32 SymbolTableOffset;
|
|
UINT32 SymbolTableSize;
|
|
UINT32 RelocationsOffset;
|
|
UINT32 RelocationsSize;
|
|
UINT32 StringTableOffset;
|
|
UINT32 StringTableSize;
|
|
|
|
UINT32 SegmentOffset;
|
|
UINT32 SegmentSize;
|
|
|
|
ASSERT (MachoContext != NULL);
|
|
ASSERT (LinkAddress != 0);
|
|
ASSERT (DependencyData != NULL);
|
|
ASSERT (DependencyData->SymbolTable != NULL);
|
|
ASSERT (DependencyData->Vtables != NULL);
|
|
ASSERT (OutputData != NULL);
|
|
|
|
MachHeader = MachoGetMachHeader64 (MachoContext);
|
|
//
|
|
// Only perform actions when the kext is flag'd to be dynamically linked.
|
|
//
|
|
if ((MachHeader->Flags & MACH_HEADER_FLAG_DYNAMIC_LINKER_LINK) == 0) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Strip superfluous Load Commands.
|
|
//
|
|
InternalStripLoadCommands64 (MachHeader);
|
|
//
|
|
// Retrieve the symbol tables required for most following operations.
|
|
//
|
|
NumSymbols = MachoGetSymbolTable (
|
|
MachoContext,
|
|
&SymbolTable,
|
|
&LocalSymtab,
|
|
&NumLocalSymbols,
|
|
&ExternalSymtab,
|
|
&NumExternalSymbols,
|
|
&UndefinedSymtab,
|
|
&NumUndefinedSymbols
|
|
);
|
|
StringTable = MachoContext->StringTable;
|
|
//
|
|
// Solve indirect symbols.
|
|
//
|
|
WeakTestValue = 0;
|
|
NumIndirectSymbols = MachoGetIndirectSymbolTable (
|
|
MachoContext,
|
|
&IndirectSymtab
|
|
);
|
|
for (Index = 0; Index < NumIndirectSymbols; ++Index) {
|
|
Symbol = (MACH_NLIST_64 *)&IndirectSymtab[Index];
|
|
SymbolName = MachoGetIndirectSymbolName64 (MachoContext, Symbol);
|
|
if (SymbolName == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
Result = InternalSolveSymbol64 (
|
|
MachoContext,
|
|
DependencyData->SymbolTable,
|
|
SymbolName,
|
|
Symbol,
|
|
&WeakTestValue,
|
|
UndefinedSymtab,
|
|
NumUndefinedSymbols
|
|
);
|
|
if (!Result) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Solve undefined symbols.
|
|
//
|
|
for (Index = 0; Index < NumUndefinedSymbols; ++Index) {
|
|
Symbol = (MACH_NLIST_64 *)&UndefinedSymtab[Index];
|
|
//
|
|
// Undefined symbols are solved via their name.
|
|
//
|
|
Result = InternalSolveSymbol64 (
|
|
MachoContext,
|
|
DependencyData->SymbolTable,
|
|
MachoGetSymbolName64 (MachoContext, Symbol),
|
|
Symbol,
|
|
&WeakTestValue,
|
|
UndefinedSymtab,
|
|
NumUndefinedSymbols
|
|
);
|
|
if (!Result) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Create and patch the KEXT's VTables.
|
|
// ScratchMemory is at least as big as __LINKEDIT, so it can store all
|
|
// symbols.
|
|
//
|
|
PatchData = (OC_VTABLE_PATCH_ARRAY *)ScratchMemory;
|
|
Result = InternalPrepareVtableCreationNonPrelinked64 (
|
|
MachoContext,
|
|
NumSymbols,
|
|
SymbolTable,
|
|
PatchData
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Then we double the number of vtables we're expecting, because every
|
|
// pointer will have a class vtable and a MetaClass vtable.
|
|
//
|
|
VtablesSize = 0;
|
|
for (Index = 0; Index < PatchData->NumEntries; ++Index) {
|
|
VtablePatchEntry = &PatchData->Entries[Index];
|
|
|
|
Result = MachoSymbolGetFileOffset64 (
|
|
MachoContext,
|
|
VtablePatchEntry->Vtable,
|
|
&VtableOffset
|
|
);
|
|
if (!Result || (VtableOffset == 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset);
|
|
if (!OC_ALIGNED (VtableData)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Result = InternalGetVtableSizeWithRelocs64 (
|
|
MachoContext,
|
|
VtableData,
|
|
&VtableSize
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
|
|
VtablesSize += VtableSize;
|
|
|
|
Result = MachoSymbolGetFileOffset64 (
|
|
MachoContext,
|
|
VtablePatchEntry->MetaVtable,
|
|
&VtableOffset
|
|
);
|
|
if (!Result || (VtableOffset == 0)) {
|
|
return FALSE;
|
|
}
|
|
|
|
VtableData = (UINT64 *)((UINTN)MachHeader + VtableOffset);
|
|
if (!OC_ALIGNED (VtableData)) {
|
|
return FALSE;
|
|
}
|
|
|
|
Result = InternalGetVtableSizeWithRelocs64 (
|
|
MachoContext,
|
|
VtableData,
|
|
&VtableSize
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
|
|
VtablesSize += VtableSize;
|
|
}
|
|
|
|
Vtables = AllocatePool (sizeof (*Vtables) + VtablesSize);
|
|
if (Vtables == NULL) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// From this point onwards it will be free'd via OutputData.
|
|
//
|
|
OutputData->Vtables = Vtables;
|
|
|
|
Result = InternalCreateVtablesNonPrelinked64 (
|
|
MachoContext,
|
|
DependencyData,
|
|
PatchData,
|
|
Vtables
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
|
|
Vtables->Link.ForwardLink = NULL;
|
|
//
|
|
// Relocate local and external symbols.
|
|
//
|
|
for (Index = 0; Index < NumLocalSymbols; ++Index) {
|
|
Result = MachoRelocateSymbol64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
(MACH_NLIST_64 *)&LocalSymtab[Index]
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
for (Index = 0; Index < NumExternalSymbols; ++Index) {
|
|
Result = MachoRelocateSymbol64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
(MACH_NLIST_64 *)&ExternalSymtab[Index]
|
|
);
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
}
|
|
//
|
|
// Prepare constructing a new __LINKEDIT section to...
|
|
// 1. strip undefined symbols for they are not allowed in prelinked
|
|
// binaries,
|
|
// 2. merge local and external relocations as only sliding is allowed from
|
|
// this point onwards,
|
|
// 3. strip Code Signature because we modified the binary, as well as other
|
|
// linker metadata stripped by KXLD as well, probably for space reasons.
|
|
//
|
|
// Example original layout:
|
|
// Local relocations - Symbol Table - external relocations - String Table -
|
|
// Code Signature
|
|
//
|
|
// Example prelinked layout
|
|
// Symbol Table - relocations (external -> local) - String Table
|
|
//
|
|
SymbolTableOffset = 0;
|
|
SymbolTableSize = (NumSymbols - NumUndefinedSymbols);
|
|
SymbolTableSize *= sizeof (MACH_NLIST_64);
|
|
RelocationsOffset = (SymbolTableOffset + SymbolTableSize);
|
|
StringTableSize = MachoContext->Symtab->StringsSize;
|
|
//
|
|
// For the allocation, assume all relocations will be preserved to simplify
|
|
// the code, the memory is only temporarily allocated anyway.
|
|
//
|
|
NumRelocations = MachoContext->DySymtab->NumOfLocalRelocations;
|
|
NumRelocations += MachoContext->DySymtab->NumExternalRelocations;
|
|
RelocationsSize = (NumRelocations * sizeof (MACH_RELOCATION_INFO));
|
|
|
|
LinkEdit = ScratchMemory;
|
|
|
|
FirstSegment = MachoGetNextSegment64 (MachoContext, NULL);
|
|
if (FirstSegment == NULL) {
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Copy the relocations to be reserved and adapt the symbol number they
|
|
// reference in case it has been relocated above.
|
|
//
|
|
TargetRelocation = (MACH_RELOCATION_INFO *)(
|
|
(UINTN)LinkEdit + RelocationsOffset
|
|
);
|
|
|
|
ASSERT (FirstSegment->FileOffset == (UINTN)FirstSegment->FileOffset);
|
|
RelocationBase = ((UINTN)MachHeader + (UINTN)FirstSegment->FileOffset);
|
|
//
|
|
// Relocate and copy local and external relocations.
|
|
//
|
|
Relocations = MachoContext->LocalRelocations;
|
|
NumRelocations = MachoContext->DySymtab->NumOfLocalRelocations;
|
|
Result = InternalRelocateAndCopyRelocations64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
DependencyData->Vtables,
|
|
RelocationBase,
|
|
Relocations,
|
|
&NumRelocations,
|
|
&TargetRelocation[0]
|
|
);
|
|
if (!Result) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
|
|
Relocations = MachoContext->ExternRelocations;
|
|
NumRelocations2 = MachoContext->DySymtab->NumExternalRelocations;
|
|
Result = InternalRelocateAndCopyRelocations64 (
|
|
MachoContext,
|
|
LinkAddress,
|
|
DependencyData->Vtables,
|
|
RelocationBase,
|
|
Relocations,
|
|
&NumRelocations2,
|
|
&TargetRelocation[NumRelocations]
|
|
);
|
|
if (!Result) {
|
|
ASSERT (FALSE);
|
|
return FALSE;
|
|
}
|
|
NumRelocations += NumRelocations2;
|
|
//
|
|
// Expose the external Symbol Table if requested.
|
|
//
|
|
if (ExposeSymbols) {
|
|
OutputData->SymbolTable = AllocatePool (
|
|
sizeof (OutputData->SymbolTable)
|
|
+ (NumExternalSymbols
|
|
* sizeof (*OutputData->SymbolTable->Symbols))
|
|
);
|
|
InternalFillSymbolTable64 (
|
|
MachoContext,
|
|
NumExternalSymbols,
|
|
ExternalSymtab,
|
|
OutputData->SymbolTable
|
|
);
|
|
}
|
|
//
|
|
// Copy the entire symbol table excluding the area for undefined symbols.
|
|
//
|
|
SymtabSize = ((UndefinedSymtab - SymbolTable) * sizeof (MACH_NLIST_64));
|
|
if (SymtabSize != 0) {
|
|
CopyMem (
|
|
(VOID *)((UINTN)LinkEdit + SymbolTableOffset),
|
|
SymbolTable,
|
|
SymtabSize
|
|
);
|
|
}
|
|
|
|
SymtabSize2 = (&SymbolTable[NumSymbols] - &UndefinedSymtab[NumUndefinedSymbols]);
|
|
SymtabSize2 *= sizeof (MACH_NLIST_64);
|
|
if (SymtabSize2 != 0) {
|
|
CopyMem (
|
|
(VOID *)((UINTN)LinkEdit + SymbolTableOffset + SymtabSize),
|
|
(VOID *)&UndefinedSymtab[NumUndefinedSymbols],
|
|
SymtabSize2
|
|
);
|
|
}
|
|
|
|
NumSymbols -= NumUndefinedSymbols;
|
|
//
|
|
// Copy the String Table. Don't strip it for the saved bytes are unlikely
|
|
// worth the time required.
|
|
//
|
|
StringTableOffset = (RelocationsOffset + RelocationsSize);
|
|
CopyMem (
|
|
(VOID *)((UINTN)LinkEdit + StringTableOffset),
|
|
StringTable,
|
|
StringTableSize
|
|
);
|
|
//
|
|
// Set up the tables with the new offsets and Symbol Table length.
|
|
//
|
|
Symtab = MachoContext->Symtab;
|
|
DySymtab = MachoContext->DySymtab;
|
|
|
|
Symtab->SymbolsOffset = SymbolTableOffset;
|
|
Symtab->NumSymbols -= NumSymbols;
|
|
Symtab->StringsOffset = StringTableOffset;
|
|
|
|
DySymtab->LocalRelocationsOffset = RelocationsOffset;
|
|
DySymtab->NumOfLocalRelocations = NumRelocations;
|
|
//
|
|
// Clear dynamic linker information.
|
|
//
|
|
DySymtab->LocalSymbolsIndex = 0;
|
|
DySymtab->NumLocalSymbols = 0;
|
|
DySymtab->ExternalSymbolsIndex = 0;
|
|
DySymtab->NumExternalRelocations = 0;
|
|
DySymtab->UndefinedSymbolsIndex = 0;
|
|
DySymtab->NumUndefinedSymbols = 0;
|
|
DySymtab->IndirectSymbolsOffset = 0;
|
|
DySymtab->NumIndirectSymbols = 0;
|
|
//
|
|
// Copy the new __LINKEDIT segment into the binary and fix its Load Command.
|
|
//
|
|
LinkEditSegment->FileSize = (SymbolTableSize + RelocationsSize + StringTableSize);
|
|
ASSERT ((UINTN)LinkEditSegment->FileSize == LinkEditSegment->FileSize);
|
|
|
|
LinkEditSegment->Size = ALIGN_VALUE (LinkEditSegment->FileSize, BASE_4KB);
|
|
|
|
CopyMem (
|
|
(VOID *)((UINTN)MachHeader + (UINTN)LinkEditSegment->FileOffset),
|
|
LinkEdit,
|
|
(UINTN)LinkEditSegment->FileSize
|
|
);
|
|
//
|
|
// Adapt the link addresses of all Segments and their Sections.
|
|
//
|
|
SegmentOffset = 0;
|
|
SegmentSize = 0;
|
|
|
|
Segment = NULL;
|
|
while ((Segment = MachoGetNextSegment64 (MachoContext, Segment))) {
|
|
if (Segment == NULL) {
|
|
//
|
|
// Adapt the Mach-O header to signal being prelinked.
|
|
//
|
|
MachHeader->Flags = MACH_HEADER_FLAG_NO_UNDEFINED_REFERENCES;
|
|
//
|
|
// Reinitialize the Mach-O context to account for the changed __LINKEDIT
|
|
// segment and file size.
|
|
//
|
|
MachoInitializeContext (
|
|
MachoContext,
|
|
MachHeader,
|
|
(SegmentOffset + SegmentSize)
|
|
);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
Result = FALSE;
|
|
|
|
Section = NULL;
|
|
while ((Section = MachoGetNextSection64 (MachoContext, Segment, Section))) {
|
|
if (Section == NULL) {
|
|
Result = TRUE;
|
|
break;
|
|
}
|
|
|
|
Section->Address = ALIGN_VALUE (
|
|
(Section->Address + LinkAddress),
|
|
Section->Alignment
|
|
);
|
|
++Section;
|
|
}
|
|
|
|
if (!Result) {
|
|
return FALSE;
|
|
}
|
|
|
|
Segment->VirtualAddress += LinkAddress;
|
|
|
|
if (Segment->FileOffset > SegmentOffset) {
|
|
SegmentOffset = (UINT32)Segment->FileOffset;
|
|
ASSERT (SegmentOffset == Segment->FileOffset);
|
|
|
|
SegmentSize = (UINT32)Segment->FileSize;
|
|
ASSERT (SegmentSize == Segment->FileSize);
|
|
}
|
|
}
|
|
//
|
|
// Reaching here means one or more segments are malformed.
|
|
//
|
|
return FALSE;
|
|
}
|