[app][uefi] refactor relocation logic to a separate file

This commit is contained in:
Kelvin Zhang
2025-02-05 12:34:12 -08:00
committed by Travis Geiselbrecht
parent 3543217461
commit c7576472f2
4 changed files with 198 additions and 179 deletions

190
lib/uefi/relocation.cpp Normal file
View File

@@ -0,0 +1,190 @@
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include "pe.h"
constexpr size_t BIT26 = 1 << 26;
constexpr size_t BIT11 = 1 << 11;
constexpr size_t BIT10 = 1 << 10;
/**
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_t ThumbMovtImmediateAddress(const uint16_t *Instruction) {
uint32_t Movt{};
uint16_t 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 = static_cast<uint16_t>(Movt & 0x000000ff); // imm8
Address |= static_cast<uint16_t>((Movt >> 4) & 0x0000f700); // imm4 imm3
Address |= (((Movt & BIT26) != 0) ? BIT11 : 0); // i
return Address;
}
/**
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_t ThumbMovwMovtImmediateAddress(uint16_t *Instructions) {
uint16_t *Word{};
uint16_t *Top{};
Word = Instructions; // MOVW
Top = Word + 2; // MOVT
return (ThumbMovtImmediateAddress(Top) << 16) +
ThumbMovtImmediateAddress(Word);
}
/**
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(uint16_t *Instruction, uint16_t Address) {
uint16_t 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;
}
/**
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(uint16_t *Instructions, uint32_t Address) {
uint16_t *Word{};
uint16_t *Top{};
Word = Instructions; // MOVW
Top = Word + 2; // MOVT
ThumbMovtImmediatePatch(Word, static_cast<uint16_t>(Address & 0xffff));
ThumbMovtImmediatePatch(Top, static_cast<uint16_t>(Address >> 16));
}
int relocate_image(char *image) {
const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER *>(image);
const auto pe_header = dos_header->GetPEHeader();
const auto optional_header = &pe_header->OptionalHeader;
const auto reloc_directory =
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (reloc_directory.Size == 0) {
printf("Relocation section empty\n");
return 0;
}
auto RelocBase = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(
image + reloc_directory.VirtualAddress);
const auto RelocBaseEnd = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(
reinterpret_cast<char *>(RelocBase) + reloc_directory.Size);
const auto Adjust =
reinterpret_cast<size_t>(image - optional_header->ImageBase);
//
// Run this relocation record
//
while (RelocBase < RelocBaseEnd) {
auto Reloc =
reinterpret_cast<uint16_t *>(reinterpret_cast<char *>(RelocBase) +
sizeof(EFI_IMAGE_BASE_RELOCATION));
auto RelocEnd = reinterpret_cast<uint16_t *>(
reinterpret_cast<char *>(RelocBase) + RelocBase->SizeOfBlock);
if (RelocBase->SizeOfBlock == 0) {
printf("Found relocation block of size 0, this is wrong\n");
return -1;
}
while (Reloc < RelocEnd) {
auto Fixup = image + RelocBase->VirtualAddress + (*Reloc & 0xFFF);
if (Fixup == nullptr) {
return 0;
}
auto Fixup16 = reinterpret_cast<uint16_t *>(Fixup);
auto Fixup32 = reinterpret_cast<uint32_t *>(Fixup);
auto Fixup64 = reinterpret_cast<uint64_t *>(Fixup);
uint32_t FixupVal = 0;
switch ((*Reloc) >> 12) {
case EFI_IMAGE_REL_BASED_ABSOLUTE:
break;
case EFI_IMAGE_REL_BASED_HIGH:
*Fixup16 = static_cast<uint16_t>(
*Fixup16 +
(static_cast<uint16_t>(static_cast<uint32_t>(Adjust) >> 16)));
break;
case EFI_IMAGE_REL_BASED_LOW:
*Fixup16 = static_cast<uint16_t>(
*Fixup16 + (static_cast<uint16_t>(Adjust) & 0xffff));
break;
case EFI_IMAGE_REL_BASED_HIGHLOW:
*Fixup32 = *Fixup32 + static_cast<uint32_t>(Adjust);
break;
case EFI_IMAGE_REL_BASED_DIR64:
*Fixup64 = *Fixup64 + static_cast<uint64_t>(Adjust);
break;
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
FixupVal = ThumbMovwMovtImmediateAddress(Fixup16) +
static_cast<uint32_t>(Adjust);
ThumbMovwMovtImmediatePatch(Fixup16, FixupVal);
break;
case EFI_IMAGE_REL_BASED_ARM_MOV32A:
printf("Unsupported relocation type: EFI_IMAGE_REL_BASED_ARM_MOV32A\n");
// break omitted - ARM instruction encoding not implemented
break;
default:
printf("Unsupported relocation type: %d\n", (*Reloc) >> 12);
return -1;
}
//
// Next relocation record
//
Reloc += 1;
}
RelocBase = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(RelocEnd);
}
optional_header->ImageBase = reinterpret_cast<size_t>(image);
return 0;
}

6
lib/uefi/relocation.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef __LIB_UEFI_RELOCATION_H_
#define __LIB_UEFI_RELOCATION_H_
int relocate_image(char *image);
#endif

View File

@@ -8,6 +8,7 @@ MODULE_DEFINES=MSPACES=1
MODULE_SRCS += \
$(LOCAL_DIR)/uefi.cpp \
$(LOCAL_DIR)/relocation.cpp \
$(LOCAL_DIR)/text_protocol.cpp \
$(LOCAL_DIR)/boot_service_provider.cpp \
$(LOCAL_DIR)/runtime_service_provider.cpp \

View File

@@ -19,6 +19,7 @@
#include "boot_service_provider.h"
#include "defer.h"
#include "pe.h"
#include "relocation.h"
#include <lib/bio.h>
#include <lib/heap.h>
@@ -58,185 +59,6 @@ template <typename T> void fill(T *data, size_t skip, uint8_t begin = 0) {
}
}
constexpr size_t BIT26 = 1 << 26;
constexpr size_t BIT11 = 1 << 11;
constexpr size_t BIT10 = 1 << 10;
/**
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_t ThumbMovtImmediateAddress(const uint16_t *Instruction) {
uint32_t Movt;
uint16_t 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_t)(Movt & 0x000000ff); // imm8
Address |= (uint16_t)((Movt >> 4) & 0x0000f700); // imm4 imm3
Address |= (((Movt & BIT26) != 0) ? BIT11 : 0); // i
return Address;
}
/**
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_t ThumbMovwMovtImmediateAddress(uint16_t *Instructions) {
uint16_t *Word;
uint16_t *Top;
Word = Instructions; // MOVW
Top = Word + 2; // MOVT
return (ThumbMovtImmediateAddress(Top) << 16) +
ThumbMovtImmediateAddress(Word);
}
/**
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(uint16_t *Instruction, uint16_t Address) {
uint16_t 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;
}
/**
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(uint16_t *Instructions, uint32_t Address) {
uint16_t *Word;
uint16_t *Top;
Word = Instructions; // MOVW
Top = Word + 2; // MOVT
ThumbMovtImmediatePatch(Word, (uint16_t)(Address & 0xffff));
ThumbMovtImmediatePatch(Top, (uint16_t)(Address >> 16));
}
int relocate_image(char *image) {
const auto dos_header = reinterpret_cast<IMAGE_DOS_HEADER *>(image);
const auto pe_header = dos_header->GetPEHeader();
const auto optional_header = &pe_header->OptionalHeader;
const auto reloc_directory =
optional_header->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
if (reloc_directory.Size == 0) {
printf("Relocation section empty\n");
return 0;
}
auto RelocBase = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(
image + reloc_directory.VirtualAddress);
const auto RelocBaseEnd = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(
(char *)RelocBase + reloc_directory.Size);
const auto Adjust =
reinterpret_cast<size_t>(image - optional_header->ImageBase);
//
// Run this relocation record
//
while (RelocBase < RelocBaseEnd) {
auto Reloc =
(uint16_t *)((char *)RelocBase + sizeof(EFI_IMAGE_BASE_RELOCATION));
auto RelocEnd = reinterpret_cast<uint16_t *>((char *)RelocBase +
RelocBase->SizeOfBlock);
if (RelocBase->SizeOfBlock == 0) {
printf("Found relocation block of size 0, this is wrong\n");
return -1;
}
while (Reloc < RelocEnd) {
auto Fixup = image + RelocBase->VirtualAddress + (*Reloc & 0xFFF);
if (Fixup == nullptr) {
return 0;
}
auto Fixup16 = reinterpret_cast<uint16_t *>(Fixup);
auto Fixup32 = reinterpret_cast<uint32_t *>(Fixup);
auto Fixup64 = reinterpret_cast<uint64_t *>(Fixup);
uint32_t FixupVal = 0;
switch ((*Reloc) >> 12) {
case EFI_IMAGE_REL_BASED_ABSOLUTE:
break;
case EFI_IMAGE_REL_BASED_HIGH:
*Fixup16 = (uint16_t)(*Fixup16 + ((uint16_t)((uint32_t)Adjust >> 16)));
break;
case EFI_IMAGE_REL_BASED_LOW:
*Fixup16 = (uint16_t)(*Fixup16 + ((uint16_t)Adjust & 0xffff));
break;
case EFI_IMAGE_REL_BASED_HIGHLOW:
*Fixup32 = *Fixup32 + (uint32_t)Adjust;
break;
case EFI_IMAGE_REL_BASED_DIR64:
*Fixup64 = *Fixup64 + (uint64_t)Adjust;
break;
case EFI_IMAGE_REL_BASED_ARM_MOV32T:
FixupVal = ThumbMovwMovtImmediateAddress(Fixup16) + (uint32_t)Adjust;
ThumbMovwMovtImmediatePatch(Fixup16, FixupVal);
break;
case EFI_IMAGE_REL_BASED_ARM_MOV32A:
printf("Unsupported relocation type: EFI_IMAGE_REL_BASED_ARM_MOV32A\n");
// break omitted - ARM instruction encoding not implemented
break;
default:
printf("Unsupported relocation type: %d\n", (*Reloc) >> 12);
return -1;
}
//
// Next relocation record
//
Reloc += 1;
}
RelocBase = reinterpret_cast<EFI_IMAGE_BASE_RELOCATION *>(RelocEnd);
}
optional_header->ImageBase = reinterpret_cast<size_t>(image);
return 0;
}
int load_sections_and_execute(bdev_t *dev,
const IMAGE_NT_HEADERS64 *pe_header) {
const auto file_header = &pe_header->FileHeader;