mirror of
https://github.com/Wack0/entii-for-workcubes.git
synced 2025-12-22 03:12:54 -05:00
837 lines
31 KiB
C
837 lines
31 KiB
C
#include <stddef.h>
|
|
#include <memory.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include "arc.h"
|
|
#include "arcio.h"
|
|
#include "arcmem.h"
|
|
#include "coff.h"
|
|
#include "ppcinst.h"
|
|
#include "oslhooks.h"
|
|
#include "runtime.h"
|
|
|
|
enum {
|
|
STYP_REG = 0x00000000,
|
|
STYP_TEXT = 0x00000020,
|
|
STYP_INIT = 0x80000000,
|
|
STYP_RDATA = 0x00000100,
|
|
STYP_DATA = 0x00000040,
|
|
STYP_LIT8 = 0x08000000,
|
|
STYP_LIT4 = 0x10000000,
|
|
STYP_SDATA = 0x00000200,
|
|
STYP_SBSS = 0x00000080,
|
|
STYP_BSS = 0x00000400,
|
|
STYP_LIB = 0x40000000,
|
|
STYP_UCODE = 0x00000800,
|
|
S_NRELOC_OVFL = 0x20000000,
|
|
|
|
SECTION_REQUIRES_LOAD = STYP_TEXT | STYP_INIT | STYP_RDATA | STYP_DATA | STYP_SDATA,
|
|
SECTION_REQUIRES_ZERO = STYP_BSS | STYP_SBSS,
|
|
|
|
SECTION_REQUIRES_LOAD_PE = IMAGE_SCN_CNT_CODE | IMAGE_SCN_CNT_INITIALIZED_DATA,
|
|
SECTION_REQUIRES_ZERO_PE = IMAGE_SCN_CNT_UNINITIALIZED_DATA
|
|
};
|
|
|
|
enum {
|
|
R_SN_TEXT = 1,
|
|
R_SN_RDATA,
|
|
R_SN_DATA,
|
|
R_SN_SDATA,
|
|
R_SN_SBSS,
|
|
R_SN_BSS,
|
|
R_SN_INIT,
|
|
R_SN_LIT8,
|
|
R_SN_LIT4,
|
|
R_SN_MAX
|
|
};
|
|
|
|
enum {
|
|
SECTOR_SIZE = 0x200
|
|
};
|
|
|
|
typedef struct _SECTION_RELOCATION_ENTRY {
|
|
ULONG FixupValue;
|
|
ULONG PointerToRelocations;
|
|
USHORT NumberOfRelocations;
|
|
} SECTION_RELOCATION_ENTRY, * PSECTION_RELOCATION_ENTRY;
|
|
|
|
#define MAX_ARGUMENT (512 - sizeof(ULONG) - 16*sizeof(PUCHAR))
|
|
typedef struct _SAVED_ARGUMENTS {
|
|
ULONG Argc;
|
|
U32LE Argv[16];
|
|
U32LE Envp[16];
|
|
UCHAR Arguments[MAX_ARGUMENT];
|
|
} SAVED_ARGUMENTS, * PSAVED_ARGUMENTS;
|
|
|
|
static SAVED_ARGUMENTS SavedArgs;
|
|
|
|
static PVOID ScratchAddress = NULL;
|
|
static bool s_OldCoffLoaded = false;
|
|
static bool s_MemoryMapFixedForOldCoff = false;
|
|
static bool s_MemoryMapFixedForAnyCoff = false;
|
|
|
|
static inline ARC_FORCEINLINE ULONG ScratchEnd() {
|
|
return (ULONG)ScratchAddress + ARCFW_MEM2_SIZE;
|
|
}
|
|
|
|
PVOID ArcLoadGetScratchAddress(void) {
|
|
return ScratchAddress;
|
|
}
|
|
|
|
#define mfpvr() ({u32 _rval; \
|
|
__asm__ __volatile__ ("mfpvr %0" : "=r"(_rval)); _rval;})
|
|
|
|
static void InstructionPerformPatch(
|
|
PU32LE SectionBaseLittle,
|
|
ULONG Offset
|
|
) {
|
|
ULONG instruction = SectionBaseLittle[Offset].v;
|
|
|
|
static ULONG PvrVersion = 0;
|
|
if (PvrVersion == 0) PvrVersion = mfpvr() >> 16;
|
|
// NT 4 osloader checks pvr, if it's not in hardcoded list then panic
|
|
// so patch this check :)
|
|
if (PvrVersion > 9 && PvrVersion != 20) {
|
|
PPC_INSTRUCTION Insn;
|
|
Insn.Long = instruction;
|
|
if (Insn.Primary_Op == X31_OP && Insn.XFXform_XO == MFSPR_OP && Insn.XFXform_spr == 1000) {
|
|
// Make NT believe this is an Arthur derivative
|
|
// addis rx, 0, 8
|
|
PPC_INSTRUCTION Patch;
|
|
Patch.Long = 0;
|
|
Patch.Primary_Op = ADDIS_OP;
|
|
Patch.Dform_RT = Insn.XFXform_RT;
|
|
Patch.Dform_RA = 0;
|
|
Patch.Dform_D = 8;
|
|
instruction = Patch.Long;
|
|
SectionBaseLittle[Offset].v = instruction;
|
|
}
|
|
}
|
|
}
|
|
|
|
static ARC_STATUS RelocatePEBlock(ULONG VirtualAddress, ULONG Length, PU16LE Block, LONG Diff, bool IsLittleEndian) {
|
|
for (ULONG Index = 0; Index < Length; Index++) {
|
|
USHORT Offset = Block[Index].v;
|
|
USHORT Type = Offset >> 12;
|
|
Offset &= (ARC_BIT(12) - 1);
|
|
ULONG FixupVA = VirtualAddress + Offset;
|
|
PU16BE DataBig = (PU16BE)FixupVA;
|
|
PU16LE DataLittle = (PU16LE)FixupVA;
|
|
switch (Type) {
|
|
case IMAGE_REL_BASED_HIGHLOW:
|
|
// 32-bit relocation
|
|
{
|
|
LONG Base;
|
|
memcpy(&Base, (PVOID)FixupVA, sizeof(Base));
|
|
if (!IsLittleEndian) {
|
|
Base = SwapEndianness32(Base);
|
|
}
|
|
Base += Diff;
|
|
if (!IsLittleEndian) {
|
|
Base = SwapEndianness32(Base);
|
|
}
|
|
memcpy((PVOID)FixupVA, &Base, sizeof(Base));
|
|
}
|
|
break;
|
|
|
|
case IMAGE_REL_BASED_HIGH:
|
|
// high 16-bit relocation
|
|
{
|
|
ULONG Temp = (IsLittleEndian ? DataLittle->v : DataBig->v) << 16;
|
|
Temp += Diff;
|
|
if (IsLittleEndian) DataLittle->v = (Temp >> 16);
|
|
else DataBig->v = (Temp >> 16);
|
|
}
|
|
break;
|
|
|
|
case IMAGE_REL_BASED_HIGHADJ:
|
|
// high 16-bit relocation with adjustment
|
|
{
|
|
if (Index == Length) {
|
|
// whoops, better not overflow
|
|
printf("HighAdj relocation overflows table\n");
|
|
return _EBADF;
|
|
}
|
|
ULONG Temp = (IsLittleEndian ? DataLittle->v : DataBig->v) << 16;
|
|
Index++;
|
|
PU16BE BlockBig = (PU16BE)(ULONG)Block;
|
|
// tfw MS seriously did this
|
|
Temp += (IsLittleEndian ? Block[Index].v : BlockBig[Index].v);
|
|
Temp += Diff;
|
|
Temp += INT16_MAX + 1;
|
|
|
|
if (IsLittleEndian) DataLittle->v = (Temp >> 16);
|
|
else DataBig->v = (Temp >> 16);
|
|
}
|
|
break;
|
|
|
|
case IMAGE_REL_BASED_LOW:
|
|
// low 16-bit relocation
|
|
{
|
|
ULONG Temp = (IsLittleEndian ? DataLittle->v : DataBig->v);
|
|
Temp += Diff;
|
|
if (IsLittleEndian) DataLittle->v = Temp;
|
|
else DataBig->v = Temp;
|
|
}
|
|
break;
|
|
|
|
//case IMAGE_REL_BASED_MIPS_JMPADDR: // not valid for PowerPC, osloader does it for all architectures for some reason
|
|
|
|
case IMAGE_REL_BASED_ABSOLUTE:
|
|
// no fixup required
|
|
break;
|
|
|
|
default:
|
|
// invalid for powerpc
|
|
printf("Invalid relocation %x\n", Type);
|
|
return _EBADF;
|
|
}
|
|
}
|
|
|
|
// all good
|
|
return _ESUCCESS;
|
|
}
|
|
|
|
static ARC_STATUS RelocatePE(ULONG ImageBase, PIMAGE_FILE_HEADER FileHeader, PIMAGE_OPTIONAL_HEADER OptionalHeader, PIMAGE_SECTION_HEADER Sections) {
|
|
bool IsLittleEndian = FileHeader->Machine == IMAGE_FILE_MACHINE_POWERPC;
|
|
|
|
PIMAGE_DATA_DIRECTORY RelocDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
|
bool HasRelocations = RelocDir->VirtualAddress != 0 && RelocDir->Size != 0;
|
|
PIMAGE_DATA_DIRECTORY ExceptionDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
|
bool HasExceptions = ExceptionDir->VirtualAddress != 0 && ExceptionDir->Size != 0;
|
|
|
|
if (ImageBase == OptionalHeader->ImageBase) {
|
|
// no need to relocate
|
|
HasRelocations = false;
|
|
}
|
|
else if (HasRelocations) {
|
|
// no relocations yet base moved
|
|
printf("Needs relocation but image has none\n");
|
|
return _EBADF;
|
|
}
|
|
|
|
if (!HasRelocations && !IsLittleEndian) {
|
|
// nothing needs to be done here
|
|
return _ESUCCESS;
|
|
}
|
|
|
|
ARC_STATUS Status;
|
|
|
|
// apply all PE relocations first. remember endianness.
|
|
if (HasRelocations) {
|
|
ULONG OldBase = OptionalHeader->ImageBase;
|
|
PIMAGE_BASE_RELOCATION Block = (PIMAGE_BASE_RELOCATION)(ImageBase + RelocDir->VirtualAddress);
|
|
ULONG BlockStart = (ULONG)Block;
|
|
ULONG RelocLength = RelocDir->Size;
|
|
|
|
for (ULONG Offset = 0; Offset < RelocLength;) {
|
|
Offset += Block->SizeOfBlock;
|
|
ULONG SizeOfBlock = Block->SizeOfBlock - sizeof(IMAGE_BASE_RELOCATION);
|
|
PU16LE Entries = (PU16LE)(ULONG)&Block[1];
|
|
|
|
Status = RelocatePEBlock(
|
|
ImageBase + Block->VirtualAddress,
|
|
SizeOfBlock / sizeof(USHORT),
|
|
Entries,
|
|
ImageBase - OldBase,
|
|
IsLittleEndian
|
|
);
|
|
|
|
if (ARC_FAIL(Status)) return Status;
|
|
|
|
Block = (PIMAGE_BASE_RELOCATION)(BlockStart + Offset);
|
|
}
|
|
}
|
|
|
|
// Instructions need patching here.
|
|
{
|
|
// Exception data is never present, just check every 32 bit value of every section containing code.
|
|
for (int i = 0; i < FileHeader->NumberOfSections - 1; i++) {
|
|
if ((Sections[i].Characteristics & IMAGE_SCN_CNT_CODE) == 0) continue;
|
|
|
|
ULONG SectionBase = ImageBase + Sections[i].VirtualAddress;
|
|
PU32BE SectionBaseBig = (PU32BE)SectionBase;
|
|
PU32LE SectionBaseLittle = (PU32LE)SectionBase;
|
|
|
|
for (ULONG off = 0; off < Sections[i].SizeOfRawData / sizeof(ULONG); off++) {
|
|
InstructionPerformPatch(SectionBaseLittle, off);
|
|
}
|
|
}
|
|
}
|
|
|
|
return _ESUCCESS;
|
|
}
|
|
|
|
// Reads the COFF symbol table and string table to scratch buffer
|
|
static ARC_STATUS ReadCoffSymbolTable(ULONG FileId, ULONG PointerToSymbolTable, ULONG NumberOfSymbols) {
|
|
PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS();
|
|
|
|
// If no scratch address was allocated, error
|
|
if (ScratchAddress == NULL) {
|
|
return _ENOMEM;
|
|
}
|
|
|
|
PULONG SymbolCount = (PULONG)ScratchAddress;
|
|
*SymbolCount = NumberOfSymbols;
|
|
|
|
PVOID ScratchAddress = (SymbolCount + 1);
|
|
|
|
// Get file length
|
|
FILE_INFORMATION Info;
|
|
ARC_STATUS Status = Api->GetFileInformationRoutine(FileId, &Info);
|
|
if (ARC_FAIL(Status)) return Status;
|
|
|
|
// Seek to symbol table
|
|
LARGE_INTEGER Offset = Int64ToLargeInteger(PointerToSymbolTable);
|
|
Status = Api->SeekRoutine(FileId, &Offset, SeekAbsolute);
|
|
if (ARC_FAIL(Status)) return Status;
|
|
|
|
int64_t ExpectedLength64 = Info.EndingAddress.QuadPart - PointerToSymbolTable;
|
|
// Needs to fit in ULONG
|
|
if (ExpectedLength64 > UINT32_MAX) return _E2BIG;
|
|
ULONG ExpectedLength = (ULONG)ExpectedLength64;
|
|
|
|
// If the symbol table is too long for the scratch memory, error
|
|
if ((ULONG)ScratchAddress + ExpectedLength > ScratchEnd()) {
|
|
return _ENOMEM;
|
|
}
|
|
|
|
// Read to scratch memory
|
|
U32LE Count;
|
|
Status = Api->ReadRoutine(FileId, ScratchAddress, ExpectedLength, &Count);
|
|
if (ARC_FAIL(Status)) return Status;
|
|
if (Count.v != ExpectedLength) return _EBADF;
|
|
|
|
return _ESUCCESS;
|
|
}
|
|
|
|
// Gets the address of a symbol using the loaded COFF symbol table in scratch
|
|
static PVOID GetSymbolEntryCoff(ULONG ImageBase, const char* SymbolName) {
|
|
PIMAGE_SYMBOL SymbolTable = (PIMAGE_SYMBOL)((ULONG)ScratchAddress + sizeof(ULONG));
|
|
ULONG SymbolCount = *(PULONG)ScratchAddress;
|
|
PCHAR StringTable = (PCHAR)&SymbolTable[SymbolCount];
|
|
|
|
// for each symbol
|
|
for (ULONG i = 0; i < SymbolCount; i += (1 + SymbolTable[i].NumberOfAuxSymbols)) {
|
|
// storage class must be external for a function
|
|
if (SymbolTable[i].StorageClass != IMAGE_SYM_CLASS_EXTERNAL) continue;
|
|
// symbol cannot be absolute
|
|
if (SymbolTable[i].SectionNumber == 0) continue;
|
|
if (SymbolTable[i].N.Name.Short == 0) {
|
|
// offset into string-table
|
|
if (!strcmp(SymbolName, &StringTable[SymbolTable[i].N.Name.Long])) {
|
|
// found it
|
|
return (PVOID)(ImageBase + SymbolTable[i].Value);
|
|
}
|
|
continue;
|
|
}
|
|
else {
|
|
// short symbol
|
|
if (
|
|
!memcmp(SymbolName, SymbolTable[i].N.ShortName, sizeof(SymbolTable[i].N.ShortName)) || // 8 character names aren't null terminated
|
|
!strcmp(SymbolName, SymbolTable[i].N.ShortName)
|
|
) {
|
|
// found it
|
|
return (PVOID)(ImageBase + SymbolTable[i].Value);
|
|
}
|
|
continue;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static ARC_STATUS ArcLoadImpl(
|
|
IN PCHAR ImagePath,
|
|
IN ULONG TopAddress,
|
|
OUT PULONG EntryAddress,
|
|
OUT PULONG LowAddress,
|
|
OUT PULONG ImageBasePage,
|
|
OUT PULONG ImageSizePage
|
|
) {
|
|
if (EntryAddress == NULL || LowAddress == NULL) {
|
|
printf("Required pointers are NULL\n");
|
|
return _EFAULT;
|
|
}
|
|
PSECTION_RELOCATION_ENTRY RelocationTable = NULL;
|
|
BYTE LocalBuffer[2 * SECTOR_SIZE + 0x40];
|
|
// Align the COFF header to a dcache line.
|
|
PBYTE LocalPointer = (PVOID)(((ULONG)(&LocalBuffer[DCACHE_LINE_SIZE - 1])) & ~(DCACHE_LINE_SIZE - 1));
|
|
|
|
// Initialise the entry address to NULL.
|
|
*EntryAddress = 0;
|
|
|
|
PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS();
|
|
|
|
// Open the file to load readonly.
|
|
U32LE _FileId;
|
|
ARC_STATUS Status = Api->OpenRoutine(ImagePath, ArcOpenReadOnly, &_FileId);
|
|
if (ARC_FAIL(Status)) {
|
|
return Status;
|
|
}
|
|
ULONG FileId = _FileId.v;
|
|
|
|
do {
|
|
// Read two sectors to get the COFF header.
|
|
// Two sectors can fit COFF file header + full PE optional header + 19 section headers.
|
|
U32LE Count;
|
|
Status = Api->ReadRoutine(FileId, LocalPointer, SECTOR_SIZE * 2, &Count);
|
|
if (ARC_FAIL(Status)) break;
|
|
if (Count.v != SECTOR_SIZE * 2) {
|
|
printf("Tried to read %x bytes and read %x bytes", SECTOR_SIZE * 2, Count.v);
|
|
Status = _EFAULT;
|
|
break;
|
|
}
|
|
|
|
PIMAGE_FILE_HEADER FileHeader = (PIMAGE_FILE_HEADER)LocalPointer;
|
|
PIMAGE_DOS_HEADER DosHeader = (PIMAGE_DOS_HEADER)LocalPointer;
|
|
bool IsFullPE = (DosHeader->e_magic == IMAGE_DOS_SIGNATURE);
|
|
if (IsFullPE) {
|
|
// this is full PE
|
|
Status = _EBADF;
|
|
break;
|
|
#if 0
|
|
if ((DosHeader->e_lfanew + sizeof(IMAGE_NT_HEADERS)) > Count.v) {
|
|
printf("Bad PE\n");
|
|
Status = _EBADF;
|
|
break;
|
|
}
|
|
NtHeaders = (PIMAGE_NT_HEADERS)(LocalPointer + DosHeader->e_lfanew);
|
|
FileHeader = &NtHeaders->FileHeader;
|
|
#endif
|
|
}
|
|
if (
|
|
// Don't accept any COFF that isn't for PPC(LE).
|
|
(FileHeader->Machine != IMAGE_FILE_MACHINE_POWERPC) || // && FileHeader->Machine != IMAGE_FILE_MACHINE_POWERPCBE) ||
|
|
// Don't accept a COFF that isn't executable.
|
|
(FileHeader->Characteristics & IMAGE_FILE_EXECUTABLE_IMAGE) == 0 ||
|
|
// Don't accept a COFF with an optional header that's too small.
|
|
FileHeader->SizeOfOptionalHeader < sizeof(IMAGE_OPTIONAL_HEADER) // sizeof(IMAGE_OPTIONAL_HEADER_COFF)
|
|
) {
|
|
printf("Bad COFF\n");
|
|
Status = _EBADF;
|
|
break;
|
|
}
|
|
|
|
// NT kernels before (somewhere between 1234 and 1314) hardcode setting BATs to the first 8MB of RAM.
|
|
// So we need to set our memchunks up to handle this if we load an older NT bootloader.
|
|
// 1314 bootloaders were compiled mid-April 1996.
|
|
// NT 3.51 SP5 provides a newer veneer, but not a newer osloader.
|
|
// Thus, we can rely on the TimeDateStamp for this.
|
|
bool IsOldCoff = FileHeader->TimeDateStamp < 828316800; // 1996-04-01
|
|
|
|
bool IsLittleEndian = FileHeader->Machine == IMAGE_FILE_MACHINE_POWERPC;
|
|
//bool HasPEOptionalHeader = FileHeader->SizeOfOptionalHeader == sizeof(IMAGE_OPTIONAL_HEADER);
|
|
bool HasRelocations = (FileHeader->Characteristics & IMAGE_FILE_RELOCS_STRIPPED) == 0;
|
|
bool HasExceptions = false;
|
|
USHORT NumberOfSections = FileHeader->NumberOfSections;
|
|
PIMAGE_OPTIONAL_HEADER OptionalHeader = (PIMAGE_OPTIONAL_HEADER)&FileHeader[1];
|
|
PIMAGE_SECTION_HEADER Sections = (PIMAGE_SECTION_HEADER)((size_t)OptionalHeader + FileHeader->SizeOfOptionalHeader);
|
|
|
|
//if (HasPEOptionalHeader)
|
|
{
|
|
if (
|
|
// Optional header magic must be PE32
|
|
OptionalHeader->Magic != IMAGE_NT_OPTIONAL_HDR_MAGIC ||
|
|
// With the full PE optional header, the header size is known.
|
|
// If this is greater than what was read, then don't accept this file.
|
|
OptionalHeader->SizeOfHeaders > Count.v
|
|
) {
|
|
printf("Bad opthdr\n");
|
|
Status = _EBADF;
|
|
break;
|
|
}
|
|
|
|
// If base relocation directory addr or size is 0, then this executable has no relocations
|
|
PIMAGE_DATA_DIRECTORY RelocDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_BASERELOC];
|
|
HasRelocations = RelocDir->VirtualAddress != 0 && RelocDir->Size != 0;
|
|
PIMAGE_DATA_DIRECTORY ExceptionDir = &OptionalHeader->DataDirectory[IMAGE_DIRECTORY_ENTRY_EXCEPTION];
|
|
HasExceptions = ExceptionDir->VirtualAddress != 0 && ExceptionDir->Size != 0;
|
|
}
|
|
#if 0
|
|
else if (HasRelocations) {
|
|
// Walk through all sections, if one section has relocations then reloc is allowed.
|
|
HasRelocations = false;
|
|
for (int i = 0; i < NumberOfSections; i++) {
|
|
if (Sections[i].NumberOfRelocations != 0) {
|
|
HasRelocations = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ULONG SizeOfImage;
|
|
ULONG ImageBase;
|
|
|
|
//if (HasPEOptionalHeader)
|
|
{
|
|
// Ignore the caller.
|
|
// This is a COFF with a full PE optional header, and has a known base.
|
|
ImageBase = OptionalHeader->ImageBase & ~0xC0000000;
|
|
SizeOfImage = OptionalHeader->SizeOfImage;
|
|
}
|
|
#if 0
|
|
else if (HasRelocations) {
|
|
SizeOfImage = OptionalHeader->SizeOfCode + OptionalHeader->SizeOfInitializedData + OptionalHeader->SizeOfUninitializedData;
|
|
ImageBase = (TopAddress - SizeOfImage) - ~(PAGE_SIZE - 1);
|
|
}
|
|
else {
|
|
// a COFF with no relocations
|
|
ImageBase = OptionalHeader->BaseOfCode;
|
|
SizeOfImage = OptionalHeader->BaseOfData + OptionalHeader->SizeOfInitializedData - ImageBase;
|
|
}
|
|
#endif
|
|
|
|
//ULONG ImageBasePage = (ImageBase & 0x1FFFFFFF) >> PAGE_SHIFT;
|
|
|
|
// If the last section is named ".debug", don't load it
|
|
if (!strcmp(&Sections[NumberOfSections - 1].Name, ".debug")) {
|
|
NumberOfSections--;
|
|
SizeOfImage -= Sections[NumberOfSections].SizeOfRawData;
|
|
OptionalHeader->SizeOfImage = SizeOfImage;
|
|
FileHeader->NumberOfSections = NumberOfSections;
|
|
}
|
|
|
|
if (NumberOfSections == 0) {
|
|
// nothing to load?
|
|
printf("No sections to load\n");
|
|
Status = _EBADF;
|
|
break;
|
|
}
|
|
|
|
ULONG ImageBaseK0 = (ULONG)MEM_PHYSICAL_TO_K0(ImageBase);
|
|
*LowAddress = ImageBaseK0;
|
|
*EntryAddress = ImageBaseK0 + OptionalHeader->AddressOfEntryPoint - OptionalHeader->BaseOfCode;
|
|
if (ImageBasePage) *ImageBasePage = ImageBase >> PAGE_SHIFT;
|
|
if (ImageSizePage) *ImageSizePage = (SizeOfImage + PAGE_SIZE - 1) >> PAGE_SHIFT;
|
|
|
|
// Allocate and zero the relocation table if needed.
|
|
#if 0
|
|
if (HasRelocations && !HasPEOptionalHeader) {
|
|
size_t LenRelocs = sizeof(RelocationTable[0]) * NumberOfSections;
|
|
RelocationTable = malloc(LenRelocs);
|
|
if (RelocationTable == NULL) {
|
|
Status = _ENOMEM;
|
|
break;
|
|
}
|
|
memset(RelocationTable, 0, LenRelocs);
|
|
}
|
|
#endif
|
|
|
|
ULONG SectionOffset = 0;
|
|
|
|
// Load sections into memory.
|
|
USHORT NumberOfSectionsToLoad = NumberOfSections;
|
|
//if (IsLittleEndian) NumberOfSectionsToLoad--;
|
|
for (int i = 0; i < NumberOfSectionsToLoad; i++) {
|
|
ULONG Flags = Sections[i].Characteristics;
|
|
ULONG SectionBase = ImageBaseK0 + SectionOffset;
|
|
|
|
#if 0
|
|
if (HasRelocations && !HasPEOptionalHeader) {
|
|
PSECTION_RELOCATION_ENTRY RelocEntry = &RelocationTable[i];
|
|
RelocEntry->FixupValue = SectionBase - Sections[i].VirtualAddress;
|
|
RelocEntry->NumberOfRelocations = Sections[i].NumberOfRelocations;
|
|
RelocEntry->PointerToRelocations = Sections[i].PointerToRelocations;
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
SectionBase = Sections[i].VirtualAddress;
|
|
//if (HasPEOptionalHeader)
|
|
{
|
|
SectionBase += ImageBaseK0;
|
|
}
|
|
}
|
|
|
|
// If needed, read this section into memory
|
|
bool IncreaseOffset = false;
|
|
if ((Flags & SECTION_REQUIRES_LOAD) != 0) {
|
|
LARGE_INTEGER SeekPosition = Int64ToLargeInteger(Sections[i].PointerToRawData);
|
|
Status = Api->SeekRoutine(FileId, &SeekPosition, SeekAbsolute);
|
|
if (ARC_FAIL(Status)) break;
|
|
Status = Api->ReadRoutine(FileId, (PVOID)SectionBase, Sections[i].SizeOfRawData, &Count);
|
|
if (ARC_FAIL(Status)) break;
|
|
if (Count.v != Sections[i].SizeOfRawData) {
|
|
printf("Tried to read %x bytes and read %x bytes\n", Sections[i].SizeOfRawData, Count.v);
|
|
Status = _EFAULT;
|
|
break;
|
|
}
|
|
IncreaseOffset = true;
|
|
}
|
|
else if ((Flags & SECTION_REQUIRES_ZERO) != 0) {
|
|
memset((PVOID)SectionBase, 0, Sections[i].SizeOfRawData);
|
|
IncreaseOffset = true;
|
|
}
|
|
|
|
if (IncreaseOffset) {
|
|
SectionOffset += Sections[i].SizeOfRawData;
|
|
}
|
|
}
|
|
if (ARC_FAIL(Status)) break;
|
|
|
|
// Relocate the COFF.
|
|
#if 0 // Required code patching is done here (to fix overly strict processor checks). Enforce it.
|
|
if (HasRelocations || IsLittleEndian)
|
|
#endif
|
|
{
|
|
Status = RelocatePE(ImageBaseK0, FileHeader, OptionalHeader, Sections);
|
|
#if 0
|
|
if (HasPEOptionalHeader) {
|
|
Status = RelocatePE(ImageBaseK0, FileHeader, OptionalHeader, Sections);
|
|
}
|
|
else {
|
|
Status = RelocateCoff(FileId, RelocationTable, NumberOfSections, FileHeader->PointerToSymbolTable);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
if (ARC_FAIL(Status)) break;
|
|
s_OldCoffLoaded = IsOldCoff;
|
|
|
|
#if 1 // no more hooking any more, no more hooking any more
|
|
// For a COFF binary with a PE optional header; we need to hook things.
|
|
if (s_RuntimePointers[RUNTIME_SYSTEM_TYPE].v >= ARTX_SYSTEM_VEGAS)
|
|
{
|
|
// Read entire symbol table to scratch memory in DDR
|
|
Status = ReadCoffSymbolTable(FileId, FileHeader->PointerToSymbolTable, FileHeader->NumberOfSymbols);
|
|
if (ARC_FAIL(Status)) break;
|
|
|
|
//printf("Image Base: %08x\r\n", ImageBaseK0);
|
|
// Get address of BlOpen, BlFileTable, BlSetupForNt using COFF symbol table
|
|
PVOID BlOpen = GetSymbolEntryCoff(ImageBaseK0, "..BlOpen");
|
|
PVOID BlFileTable = GetSymbolEntryCoff(ImageBaseK0, "BlFileTable");
|
|
PVOID BlSetupForNt = GetSymbolEntryCoff(ImageBaseK0, "..BlSetupForNt");
|
|
PVOID BlReadSignature = GetSymbolEntryCoff(ImageBaseK0, "..BlReadSignature");
|
|
if (BlOpen != NULL && BlFileTable != NULL && BlSetupForNt != NULL && BlReadSignature != NULL) {
|
|
// This must be an osloader binary (linked with ARC bootlib)
|
|
OslHookInit(BlOpen, BlFileTable, BlSetupForNt, BlReadSignature);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// Flush all caches.
|
|
Api->FlushAllCachesRoutine();
|
|
} while (false);
|
|
if (RelocationTable != NULL) free(RelocationTable);
|
|
Api->CloseRoutine(FileId);
|
|
return Status;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Loads a COFF executable.
|
|
/// </summary>
|
|
/// <param name="ImagePath">Path of the executable to load.</param>
|
|
/// <param name="TopAddress">Address to load the COFF to</param>
|
|
/// <param name="EntryAddress">Entry point is written here</param>
|
|
/// <param name="LowAddress">End address of the loaded executable is written here</param>
|
|
/// <returns>ARC status code</returns>
|
|
static ARC_STATUS ArcLoad(
|
|
IN PCHAR ImagePath,
|
|
IN ULONG TopAddress,
|
|
OUT PU32LE EntryAddress,
|
|
OUT PU32LE LowAddress
|
|
) {
|
|
ULONG LocalEntry, LocalLow;
|
|
ARC_STATUS Status = ArcLoadImpl(ImagePath, TopAddress, &LocalEntry, &LocalLow, NULL, NULL);
|
|
if (ARC_FAIL(Status)) return Status;
|
|
if (EntryAddress != NULL) EntryAddress->v = LocalEntry;
|
|
if (LowAddress != NULL) LowAddress->v = LocalLow;
|
|
return Status;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Calls the entry point of a previously loaded program.
|
|
/// </summary>
|
|
/// <param name="EntryAddress">Entry point to call.</param>
|
|
/// <param name="StackAddress">Stack pointer to set.</param>
|
|
/// <param name="Argc">Number of arguments.</param>
|
|
/// <param name="Argv">Array of arguments.</param>
|
|
/// <param name="Envp">Environment variables.</param>
|
|
/// <returns>ARC status code.</returns>
|
|
static ARC_STATUS ArcInvoke(
|
|
IN ULONG EntryAddress,
|
|
IN ULONG StackAddress,
|
|
IN ULONG Argc,
|
|
IN PCHAR Argv[],
|
|
IN PCHAR Envp[]
|
|
) {
|
|
// Both entry point and stack pointer must be aligned to 32 bits.
|
|
if ((EntryAddress & 3) != 0 || (StackAddress & 3) != 0) {
|
|
printf("Unaligned entry/stack addrs\n");
|
|
return _EFAULT;
|
|
}
|
|
|
|
// If we have loaded an older COFF executable, fix the memory chunks to set anything > 8MB and < 256MB as LoadedProgram.
|
|
if (s_OldCoffLoaded && !s_MemoryMapFixedForOldCoff) {
|
|
PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS();
|
|
for (
|
|
PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL);
|
|
MemChunk != NULL;
|
|
MemChunk = Api->MemoryRoutine(MemChunk)
|
|
) {
|
|
// Looking for a free memchunk
|
|
if (MemChunk->MemoryType != MemoryFree) continue;
|
|
|
|
// Starting from 8MB
|
|
if (MemChunk->BasePage < (0x800000 / PAGE_SIZE)) continue;
|
|
|
|
// Ending at or before 256MB
|
|
ULONG ChunkEnd = MemChunk->BasePage + MemChunk->PageCount;
|
|
if (ChunkEnd > (256 * (0x100000 / PAGE_SIZE))) continue;
|
|
|
|
// Mark it as LoadedProgram.
|
|
// (Motorola and IBM ARC firmware implementations do this)
|
|
// Older implementations use FirmwareTemporary, but if we have a false positive then newer NT bootloaders will detect LoadedProgram here and treat it as free.
|
|
MemChunk->MemoryType = MemoryLoadedProgram;
|
|
}
|
|
|
|
s_MemoryMapFixedForOldCoff = true;
|
|
}
|
|
|
|
if (!s_MemoryMapFixedForAnyCoff) {
|
|
PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS();
|
|
for (
|
|
PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL);
|
|
MemChunk != NULL;
|
|
MemChunk = Api->MemoryRoutine(MemChunk)
|
|
) {
|
|
// Looking for a free memchunk
|
|
if (MemChunk->MemoryType != MemoryFree) continue;
|
|
|
|
// Starting from 256MB
|
|
if (MemChunk->BasePage < (0x10000000 / PAGE_SIZE)) continue;
|
|
|
|
// Mark it as FirmwareTemporary
|
|
MemChunk->MemoryType = MemoryFirmwareTemporary;
|
|
}
|
|
|
|
s_MemoryMapFixedForAnyCoff = true;
|
|
}
|
|
|
|
|
|
// Stack pointer is otherwise unused, do not change it.
|
|
|
|
PU32LE CallingConv = (PU32LE)EntryAddress;
|
|
//printf("Entry point: %08x - toc: %08x\r\n", CallingConv[0].v, CallingConv[1].v);
|
|
// Read from the entry point to make sure it's mapped, yay for having pagetables instead of BATs!
|
|
*(volatile ULONG*)(CallingConv[0].v);
|
|
extern void __ArcInvokeImpl(ULONG EntryAddress, ULONG Toc, ULONG Argc, PCHAR Argv[], PCHAR Envp[]);
|
|
__ArcInvokeImpl(CallingConv[0].v, CallingConv[1].v, Argc, Argv, Envp);
|
|
return _ESUCCESS;
|
|
}
|
|
|
|
// Copy arguments to a buffer handled by the ARC firmware implementation
|
|
static ARC_STATUS CopyArguments(ULONG Argc, PCHAR Argv[], PCHAR Envp[]) {
|
|
SavedArgs.Argc = Argc;
|
|
memset(SavedArgs.Arguments, 0, sizeof(SavedArgs.Arguments));
|
|
ULONG Length = sizeof(SavedArgs.Arguments);
|
|
ULONG Offset = 0;
|
|
for (ULONG Arg = 0; Arg < Argc; Arg++) {
|
|
ULONG RemainingLength = Length - Offset;
|
|
int err = snprintf(&SavedArgs.Arguments[Offset], RemainingLength, "%s", Argv[Arg]);
|
|
if (err < 0) {
|
|
printf("Could not copy argument %d\n", Arg);
|
|
return _EFAULT;
|
|
}
|
|
if (err >= RemainingLength) return _E2BIG;
|
|
SavedArgs.Argv[Arg].v = (ULONG)&SavedArgs.Arguments[Offset];
|
|
Offset += err;
|
|
SavedArgs.Arguments[Offset] = 0;
|
|
Offset++;
|
|
}
|
|
memset(SavedArgs.Envp, 0, sizeof(SavedArgs.Envp));
|
|
for (ULONG Arg = 0; Envp[Arg] != NULL; Arg++) {
|
|
SavedArgs.Envp[Arg].v = (ULONG)Envp[Arg];
|
|
}
|
|
return _ESUCCESS;
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Loads and executes a COFF executable.
|
|
/// </summary>
|
|
/// <param name="ImagePath">Path of the executable to load.</param>
|
|
/// <param name="Argc">Number of arguments.</param>
|
|
/// <param name="Argv">Array of arguments.</param>
|
|
/// <param name="Envp">Environment variables.</param>
|
|
/// <returns>ARC status code.</returns>
|
|
static ARC_STATUS ArcExecute(
|
|
IN PCHAR ImagePath,
|
|
IN ULONG Argc,
|
|
IN PCHAR Argv[],
|
|
IN PCHAR Envp[]
|
|
) {
|
|
PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS();
|
|
char CopyPath[260];
|
|
|
|
// Copy the arguments and path so the provided buffers can be overwritten if needed
|
|
ARC_STATUS Status = CopyArguments(Argc, Argv, Envp);
|
|
if (ARC_FAIL(Status)) return Status;
|
|
int err = snprintf(CopyPath, sizeof(CopyPath), "%s", ImagePath);
|
|
if (err < 0) return _EFAULT;
|
|
if (err >= sizeof(CopyPath)) return _E2BIG;
|
|
|
|
// Find an area of memory.
|
|
for (
|
|
PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL);
|
|
MemChunk != NULL;
|
|
MemChunk = Api->MemoryRoutine(MemChunk)
|
|
) {
|
|
// Looking for a free memchunk
|
|
if (MemChunk->MemoryType != MemoryFree) continue;
|
|
// At least 4MB
|
|
if (MemChunk->PageCount < (ARC_MB(4) / PAGE_SIZE)) continue;
|
|
|
|
// Try to load here
|
|
ULONG EntryPoint, BaseAddress, ImageBasePage, ImageSizePage;
|
|
Status = ArcLoadImpl(CopyPath, MemChunk->BasePage + MemChunk->PageCount, &EntryPoint, &BaseAddress, &ImageBasePage, &ImageSizePage);
|
|
if (ARC_FAIL(Status)) {
|
|
if (Status != _ENOMEM) return Status;
|
|
continue;
|
|
}
|
|
|
|
// Find the memory descriptor used.
|
|
PMEMORY_DESCRIPTOR UsedChunk = ArcMemFindChunk(ImageBasePage, ImageSizePage);
|
|
|
|
if (UsedChunk == NULL) {
|
|
// what?
|
|
printf("Could not find used mem chunk\n");
|
|
return _EFAULT;
|
|
}
|
|
|
|
// Initialise a memchunk for this executable
|
|
Status = ArcMemAllocateFromFreeChunk(UsedChunk, ImageBasePage, ImageSizePage, MemoryLoadedProgram);
|
|
if (ARC_FAIL(Status)) return Status;
|
|
|
|
// Dump memchunks for debug.
|
|
#if 0
|
|
printf("Prg: %08x-%08x\r\n", ImageBasePage * PAGE_SIZE, (ImageBasePage + ImageSizePage) * PAGE_SIZE);
|
|
for (PMEMORY_DESCRIPTOR MemChunk = Api->MemoryRoutine(NULL); MemChunk != NULL; MemChunk = Api->MemoryRoutine(MemChunk)) {
|
|
printf("%x: %08x-%08x\r\n", MemChunk->MemoryType, MemChunk->BasePage * PAGE_SIZE, (MemChunk->BasePage + MemChunk->PageCount) * PAGE_SIZE);
|
|
}
|
|
IOSKBD_ReadChar();
|
|
#endif
|
|
|
|
Status = Api->InvokeRoutine(EntryPoint, BaseAddress, SavedArgs.Argc, (PCHAR*) SavedArgs.Argv, (PCHAR*) SavedArgs.Envp);
|
|
if (ARC_SUCCESS(Status)) UsedChunk->MemoryType = MemoryLoadedProgram;
|
|
return Status;
|
|
}
|
|
|
|
return _ENOMEM;
|
|
}
|
|
|
|
void ArcLoadInit(void) {
|
|
PVENDOR_VECTOR_TABLE Api = ARC_VENDOR_VECTORS();
|
|
Api->LoadRoutine = ArcLoad;
|
|
Api->InvokeRoutine = ArcInvoke;
|
|
Api->ExecuteRoutine = ArcExecute;
|
|
|
|
// Initialise the scratch address.
|
|
// Try to allocate some memory for this, hopefully in an area that cannot be used by boot.
|
|
ScratchAddress = ArcMemAllocTemp(ARCFW_MEM2_SIZE, true);
|
|
if (ScratchAddress == NULL) ScratchAddress = ArcMemAllocTemp(ARCFW_MEM2_SIZE, false);
|
|
} |