#include #include #include #include #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 %u @ 0x%08x (0x%08x 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 %u @ 0x%08x (0x%08x 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;ie_phnum;i++) { if(phdrs[i].p_type != PT_LOAD) { gprintf("skip PHDR %d of type %d\n", i, phdrs[i].p_type); } else { phdrs[i].p_paddr &= 0x3FFFFFFF; phdrs[i].p_paddr |= 0x80000000; gprintf ("PHDR %d 0x%08x [0x%x] -> 0x%08x [0x%x] <", 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%x\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"); }