/** @file Specific relocation fixups for ARM architecture. Copyright (c) 2006 - 2018, Intel Corporation. All rights reserved.
Portions copyright (c) 2008 - 2010, Apple Inc. All rights reserved.
SPDX-License-Identifier: BSD-2-Clause-Patent **/ #include "BasePeCoffLibInternals.h" #include /** Pass in a pointer to an ARM MOVT or MOVW immediate instruciton and return the immediate data encoded in the instruction. @param Instruction Pointer to ARM MOVT or MOVW immediate instruction @return Immediate address encoded in the instruction **/ UINT16 ThumbMovtImmediateAddress ( IN UINT16 *Instruction ) { UINT32 Movt; UINT16 Address; // Thumb2 is two 16-bit instructions working together. Not a single 32-bit instruction // Example MOVT R0, #0 is 0x0000f2c0 or 0xf2c0 0x0000 Movt = (*Instruction << 16) | (*(Instruction + 1)); // imm16 = imm4:i:imm3:imm8 // imm4 -> Bit19:Bit16 // i -> Bit26 // imm3 -> Bit14:Bit12 // imm8 -> Bit7:Bit0 Address = (UINT16)(Movt & 0x000000ff); // imm8 Address |= (UINT16)((Movt >> 4) & 0x0000f700); // imm4 imm3 Address |= (((Movt & BIT26) != 0) ? BIT11 : 0); // i return Address; } /** Update an ARM MOVT or MOVW immediate instruction immediate data. @param Instruction Pointer to ARM MOVT or MOVW immediate instruction @param Address New addres to patch into the instruction **/ VOID ThumbMovtImmediatePatch ( IN OUT UINT16 *Instruction, IN UINT16 Address ) { UINT16 Patch; // First 16-bit chunk of instruciton Patch = ((Address >> 12) & 0x000f); // imm4 Patch |= (((Address & BIT11) != 0) ? BIT10 : 0); // i // Mask out instruction bits and or in address *(Instruction) = (*Instruction & ~0x040f) | Patch; // Second 16-bit chunk of instruction Patch = Address & 0x000000ff; // imm8 Patch |= ((Address << 4) & 0x00007000); // imm3 // Mask out instruction bits and or in address Instruction++; *Instruction = (*Instruction & ~0x70ff) | Patch; } /** Pass in a pointer to an ARM MOVW/MOVT instruciton pair and return the immediate data encoded in the two` instruction. @param Instructions Pointer to ARM MOVW/MOVT insturction pair @return Immediate address encoded in the instructions **/ UINT32 ThumbMovwMovtImmediateAddress ( IN UINT16 *Instructions ) { UINT16 *Word; UINT16 *Top; Word = Instructions; // MOVW Top = Word + 2; // MOVT return (ThumbMovtImmediateAddress (Top) << 16) + ThumbMovtImmediateAddress (Word); } /** Update an ARM MOVW/MOVT immediate instruction instruction pair. @param Instructions Pointer to ARM MOVW/MOVT instruction pair @param Address New addres to patch into the instructions **/ VOID ThumbMovwMovtImmediatePatch ( IN OUT UINT16 *Instructions, IN UINT32 Address ) { UINT16 *Word; UINT16 *Top; Word = Instructions; // MOVW Top = Word + 2; // MOVT ThumbMovtImmediatePatch (Word, (UINT16)(Address & 0xffff)); ThumbMovtImmediatePatch (Top, (UINT16)(Address >> 16)); } /** Performs an ARM-based specific relocation fixup and is a no-op on other instruction sets. @param Reloc The pointer to the relocation record. @param Fixup The pointer to the address to fix up. @param FixupData The pointer to a buffer to log the fixups. @param Adjust The offset to adjust the fixup. @return Status code. **/ RETURN_STATUS PeCoffLoaderRelocateImageEx ( IN UINT16 *Reloc, IN OUT CHAR8 *Fixup, IN OUT CHAR8 **FixupData, IN UINT64 Adjust ) { UINT16 *Fixup16; UINT32 FixupVal; Fixup16 = (UINT16 *) Fixup; switch ((*Reloc) >> 12) { case EFI_IMAGE_REL_BASED_ARM_MOV32T: FixupVal = ThumbMovwMovtImmediateAddress (Fixup16) + (UINT32)Adjust; ThumbMovwMovtImmediatePatch (Fixup16, FixupVal); if (*FixupData != NULL) { *FixupData = ALIGN_POINTER(*FixupData, sizeof(UINT64)); // Fixup16 is not aligned so we must copy it. Thumb instructions are streams of 16 bytes. CopyMem (*FixupData, Fixup16, sizeof (UINT64)); *FixupData = *FixupData + sizeof(UINT64); } break; case EFI_IMAGE_REL_BASED_ARM_MOV32A: ASSERT (FALSE); // break omitted - ARM instruction encoding not implemented default: return RETURN_UNSUPPORTED; } return RETURN_SUCCESS; } /** Returns TRUE if the machine type of PE/COFF image is supported. Supported does not mean the image can be executed it means the PE/COFF loader supports loading and relocating of the image type. It's up to the caller to support the entry point. @param Machine Machine type from the PE Header. @return TRUE if this PE/COFF loader can load the image **/ BOOLEAN PeCoffLoaderImageFormatSupported ( IN UINT16 Machine ) { if ((Machine == IMAGE_FILE_MACHINE_ARMTHUMB_MIXED) || (Machine == IMAGE_FILE_MACHINE_EBC)) { return TRUE; } return FALSE; } /** Performs an ARM-based specific re-relocation fixup and is a no-op on other instruction sets. This is used to re-relocated the image into the EFI virtual space for runtime calls. @param Reloc The pointer to the relocation record. @param Fixup The pointer to the address to fix up. @param FixupData The pointer to a buffer to log the fixups. @param Adjust The offset to adjust the fixup. @return Status code. **/ RETURN_STATUS PeHotRelocateImageEx ( IN UINT16 *Reloc, IN OUT CHAR8 *Fixup, IN OUT CHAR8 **FixupData, IN UINT64 Adjust ) { UINT16 *Fixup16; UINT32 FixupVal; Fixup16 = (UINT16 *)Fixup; switch ((*Reloc) >> 12) { case EFI_IMAGE_REL_BASED_ARM_MOV32T: *FixupData = ALIGN_POINTER (*FixupData, sizeof (UINT64)); if (*(UINT64 *) (*FixupData) == ReadUnaligned64 ((UINT64 *)Fixup16)) { FixupVal = ThumbMovwMovtImmediateAddress (Fixup16) + (UINT32)Adjust; ThumbMovwMovtImmediatePatch (Fixup16, FixupVal); } *FixupData = *FixupData + sizeof(UINT64); break; case EFI_IMAGE_REL_BASED_ARM_MOV32A: ASSERT (FALSE); // break omitted - ARM instruction encoding not implemented default: DEBUG ((EFI_D_ERROR, "PeHotRelocateEx:unknown fixed type\n")); return RETURN_UNSUPPORTED; } return RETURN_SUCCESS; }