mirror of
https://github.com/fail0verflow/hbc.git
synced 2024-11-17 23:29:21 +01:00
284 lines
6.3 KiB
C
284 lines
6.3 KiB
C
|
#include <stdio.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include <ogcsys.h>
|
||
|
#include <ogc/machine/processor.h>
|
||
|
|
||
|
#include "../config.h"
|
||
|
#include "loader.h"
|
||
|
|
||
|
#include "elf_abi.h"
|
||
|
#include "asm.h"
|
||
|
|
||
|
extern void __exception_closeall ();
|
||
|
|
||
|
typedef struct _dolheader {
|
||
|
u32 text_pos[7];
|
||
|
u32 data_pos[11];
|
||
|
u32 text_start[7];
|
||
|
u32 data_start[11];
|
||
|
u32 text_size[7];
|
||
|
u32 data_size[11];
|
||
|
u32 bss_start;
|
||
|
u32 bss_size;
|
||
|
u32 entry_point;
|
||
|
} dolheader;
|
||
|
|
||
|
static void patch_crt0 (entry_point *ep) {
|
||
|
u8 *p = (u8 *) *ep;
|
||
|
|
||
|
if (p[0x20] == 0x41) {
|
||
|
gprintf ("patching crt0\n");
|
||
|
p[0x20] = 0x40;
|
||
|
|
||
|
DCFlushRange ((void *) &p[0x20], 1);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void set_argv (entry_point *ep, const char *args, u16 arg_len) {
|
||
|
u32 *p = (u32 *) *ep;
|
||
|
struct __argv *argv;
|
||
|
char *cmdline;
|
||
|
|
||
|
if (p[1] != ARGV_MAGIC) {
|
||
|
gprintf ("application does not support argv\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
gprintf ("setting argv\n");
|
||
|
|
||
|
argv = (struct __argv *) &p[2];
|
||
|
cmdline = (char *) LD_ARGS_ADDR;
|
||
|
|
||
|
memcpy (cmdline, args, arg_len);
|
||
|
|
||
|
#ifdef DEBUG_APP
|
||
|
size_t i;
|
||
|
|
||
|
gprintf ("cmdline='");
|
||
|
for (i = 0; i < arg_len; ++i)
|
||
|
if (cmdline[i] == 0)
|
||
|
gprintf ("\\0");
|
||
|
else
|
||
|
gprintf ("%c", cmdline[i]);
|
||
|
gprintf ("'\n");
|
||
|
#endif
|
||
|
|
||
|
argv->argvMagic = ARGV_MAGIC;
|
||
|
argv->commandLine = cmdline;
|
||
|
argv->length = arg_len;
|
||
|
|
||
|
DCFlushRange (&p[2], 4);
|
||
|
DCFlushRange ((void *) LD_ARGS_ADDR, arg_len);
|
||
|
}
|
||
|
|
||
|
static bool reloc_dol (entry_point *ep, const u8 *addr, u32 size,
|
||
|
bool check_overlap) {
|
||
|
u32 i;
|
||
|
dolheader *dolfile;
|
||
|
|
||
|
dolfile = (dolheader *) addr;
|
||
|
for (i = 0; i < 7; i++) {
|
||
|
if (!dolfile->text_size[i])
|
||
|
continue;
|
||
|
|
||
|
gprintf ("loading text section %lu @ 0x%08lx (0x%08lx bytes)\n", i,
|
||
|
dolfile->text_start[i], dolfile->text_size[i]);
|
||
|
|
||
|
if (dolfile->text_pos[i] + dolfile->text_size[i] > size)
|
||
|
return false;
|
||
|
|
||
|
if (check_overlap && ((dolfile->text_start[i] < LD_MIN_ADDR) ||
|
||
|
((dolfile->text_start[i] + dolfile->text_size[i] >
|
||
|
LD_MAX_ADDR))))
|
||
|
return false;
|
||
|
|
||
|
|
||
|
memmove ((void *) dolfile->text_start[i], addr + dolfile->text_pos[i],
|
||
|
dolfile->text_size[i]);
|
||
|
|
||
|
DCFlushRange ((void *) dolfile->text_start[i], dolfile->text_size[i]);
|
||
|
ICInvalidateRange ((void *) dolfile->text_start[i],
|
||
|
dolfile->text_size[i]);
|
||
|
}
|
||
|
|
||
|
for(i = 0; i < 11; i++) {
|
||
|
if (!dolfile->data_size[i])
|
||
|
continue;
|
||
|
|
||
|
gprintf ("loading data section %lu @ 0x%08lx (0x%08lx bytes)\n", i,
|
||
|
dolfile->data_start[i], dolfile->data_size[i]);
|
||
|
|
||
|
if (dolfile->data_pos[i] + dolfile->data_size[i] > size)
|
||
|
return false;
|
||
|
|
||
|
if (check_overlap && ((dolfile->data_start[i] < LD_MIN_ADDR) ||
|
||
|
(dolfile->data_start[i] + dolfile->data_size[i] > LD_MAX_ADDR)))
|
||
|
return false;
|
||
|
|
||
|
memmove ((void*) dolfile->data_start[i], addr + dolfile->data_pos[i],
|
||
|
dolfile->data_size[i]);
|
||
|
|
||
|
DCFlushRange ((void *) dolfile->data_start[i], dolfile->data_size[i]);
|
||
|
}
|
||
|
|
||
|
*ep = (entry_point) dolfile->entry_point;
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
static s8 is_valid_elf (const u8 *addr, u32 size) {
|
||
|
Elf32_Ehdr *ehdr; /* Elf header structure pointer */
|
||
|
|
||
|
ehdr = (Elf32_Ehdr *) addr;
|
||
|
|
||
|
if (!IS_ELF (*ehdr))
|
||
|
return 0;
|
||
|
|
||
|
if (ehdr->e_ident[EI_CLASS] != ELFCLASS32)
|
||
|
return -1;
|
||
|
|
||
|
if (ehdr->e_ident[EI_DATA] != ELFDATA2MSB)
|
||
|
return -1;
|
||
|
|
||
|
if (ehdr->e_ident[EI_VERSION] != EV_CURRENT)
|
||
|
return -1;
|
||
|
|
||
|
if (ehdr->e_type != ET_EXEC)
|
||
|
return -1;
|
||
|
|
||
|
if (ehdr->e_machine != EM_PPC)
|
||
|
return -1;
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
|
||
|
static bool reloc_elf (entry_point *ep, const u8 *addr, u32 size,
|
||
|
bool check_overlap) {
|
||
|
Elf32_Ehdr *ehdr;
|
||
|
Elf32_Phdr *phdrs;
|
||
|
u8 *image;
|
||
|
int i;
|
||
|
|
||
|
ehdr = (Elf32_Ehdr *) addr;
|
||
|
|
||
|
if(ehdr->e_phoff == 0 || ehdr->e_phnum == 0) {
|
||
|
gprintf("ELF has no phdrs\n");
|
||
|
return false;
|
||
|
}
|
||
|
if(ehdr->e_phentsize != sizeof(Elf32_Phdr)) {
|
||
|
gprintf("Invalid ELF phdr size\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
phdrs = (Elf32_Phdr*)(addr + ehdr->e_phoff);
|
||
|
|
||
|
for(i=0;i<ehdr->e_phnum;i++) {
|
||
|
|
||
|
if(phdrs[i].p_type != PT_LOAD) {
|
||
|
gprintf("skip PHDR %d of type %ld\n", i, phdrs[i].p_type);
|
||
|
} else {
|
||
|
phdrs[i].p_paddr &= 0x3FFFFFFF;
|
||
|
phdrs[i].p_paddr |= 0x80000000;
|
||
|
|
||
|
gprintf ("PHDR %d 0x%08lx [0x%lx] -> 0x%08lx [0x%lx] <", i,
|
||
|
phdrs[i].p_offset, phdrs[i].p_filesz,
|
||
|
phdrs[i].p_paddr, phdrs[i].p_memsz);
|
||
|
|
||
|
if(phdrs[i].p_flags & PF_R)
|
||
|
gprintf("R");
|
||
|
if(phdrs[i].p_flags & PF_W)
|
||
|
gprintf("W");
|
||
|
if(phdrs[i].p_flags & PF_X)
|
||
|
gprintf("X");
|
||
|
gprintf(">\n");
|
||
|
|
||
|
if(phdrs[i].p_filesz > phdrs[i].p_memsz) {
|
||
|
gprintf ("-> file size > mem size\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if(phdrs[i].p_filesz) {
|
||
|
if (check_overlap && ((phdrs[i].p_paddr < LD_MIN_ADDR) ||
|
||
|
(phdrs[i].p_paddr + phdrs[i].p_filesz) > LD_MAX_ADDR)) {
|
||
|
gprintf ("-> failed overlap check\n");
|
||
|
return false;
|
||
|
}
|
||
|
gprintf ("-> load 0x%lx\n", phdrs[i].p_filesz);
|
||
|
image = (u8 *) (addr + phdrs[i].p_offset);
|
||
|
memmove ((void *) phdrs[i].p_paddr, (const void *) image,
|
||
|
phdrs[i].p_filesz);
|
||
|
|
||
|
DCFlushRange ((void *) phdrs[i].p_paddr, phdrs[i].p_memsz);
|
||
|
if(phdrs[i].p_flags & PF_X)
|
||
|
ICInvalidateRange ((void *) phdrs[i].p_paddr, phdrs[i].p_memsz);
|
||
|
} else {
|
||
|
gprintf ("-> skip\n");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
*ep = (entry_point) ((ehdr->e_entry & 0x3FFFFFFF) | 0x80000000);
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool loader_reloc (entry_point *ep, const u8 *addr, u32 size, const char *args,
|
||
|
u16 arg_len, bool check_overlap) {
|
||
|
s8 res;
|
||
|
bool b;
|
||
|
|
||
|
res = is_valid_elf (addr, size);
|
||
|
|
||
|
if (res < 0)
|
||
|
return false;
|
||
|
|
||
|
if (res == 1)
|
||
|
b = reloc_elf (ep, addr, size, check_overlap);
|
||
|
else
|
||
|
b = reloc_dol (ep, addr, size, check_overlap);
|
||
|
|
||
|
if (b) {
|
||
|
patch_crt0 (ep);
|
||
|
|
||
|
if (args && arg_len)
|
||
|
set_argv (ep, args, arg_len);
|
||
|
}
|
||
|
|
||
|
return b;
|
||
|
}
|
||
|
|
||
|
static const u32 exec_stub[] = {
|
||
|
0x7c6903a6, // mtctr r3
|
||
|
0x3d208133, // lis r9, 0x8133
|
||
|
0x3d408180, // lis r10, 0x8180
|
||
|
0x90090000, // 1: stw r0, 0(r9)
|
||
|
0x39290004, // addi r9, r9, 4
|
||
|
0x7c295000, // cmpd r9, r10
|
||
|
0x4180fff4, // blt 1b
|
||
|
0x4e800420 // bctr
|
||
|
};
|
||
|
|
||
|
void loader_exec (entry_point ep) {
|
||
|
gprintf ("shutting down services and vectoring...\n");
|
||
|
SYS_ResetSystem (SYS_SHUTDOWN, 0, 0);
|
||
|
|
||
|
__exception_closeall ();
|
||
|
|
||
|
// these pokes make ninty SDK dols work, I'm told
|
||
|
*(vu32*)0x800000F8 = 0x0E7BE2C0; // Bus Clock Speed
|
||
|
*(vu32*)0x800000FC = 0x2B73A840; // CPU Clock Speed
|
||
|
|
||
|
void *target = (void *)((int)(SYS_GetArena2Hi() - sizeof exec_stub) & ~31);
|
||
|
void (*f_exec_stub) (int) = target;
|
||
|
|
||
|
memcpy(target, exec_stub, sizeof exec_stub);
|
||
|
DCFlushRange(target, sizeof exec_stub);
|
||
|
ICInvalidateRange(target, sizeof exec_stub);
|
||
|
|
||
|
f_exec_stub((u32)ep);
|
||
|
|
||
|
gprintf ("this cant be good\n");
|
||
|
}
|
||
|
|