mirror of
https://github.com/Maschell/WiiUPluginLoader.git
synced 2024-09-29 16:08:35 +02:00
501 lines
16 KiB
C++
501 lines
16 KiB
C++
|
/* based on module.c
|
||
|
* by Alex Chadwick
|
||
|
*
|
||
|
* Copyright (C) 2014, Alex Chadwick
|
||
|
* Modified 2018, Maschell
|
||
|
*
|
||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||
|
* of this software and associated documentation files (the "Software"), to deal
|
||
|
* in the Software without restriction, including without limitation the rights
|
||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||
|
* copies of the Software, and to permit persons to whom the Software is
|
||
|
* furnished to do so, subject to the following conditions:
|
||
|
*
|
||
|
* The above copyright notice and this permission notice shall be included in
|
||
|
* all copies or substantial portions of the Software.
|
||
|
*
|
||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||
|
* SOFTWARE.
|
||
|
*/
|
||
|
|
||
|
#include "ElfTools.h"
|
||
|
#include "RelocationData.h"
|
||
|
#include "ImportRPLInformation.h"
|
||
|
#include <string.h>
|
||
|
#include <malloc.h>
|
||
|
#include <libelf.h>
|
||
|
#include <utils/logger.h>
|
||
|
|
||
|
bool ElfTools::elfLoadSection(const Elf *elf, Elf_Scn *scn, const Elf32_Shdr *shdr,void *destination) {
|
||
|
if (destination == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (shdr->sh_type) {
|
||
|
case SHT_SYMTAB:
|
||
|
case SHT_PROGBITS: {
|
||
|
Elf_Data *data;
|
||
|
size_t n;
|
||
|
|
||
|
n = 0;
|
||
|
for (data = elf_getdata(scn, NULL); data != NULL; data = elf_getdata(scn, data)) {
|
||
|
memcpy((char *)destination + n, data->d_buf, data->d_size);
|
||
|
n += data->d_size;
|
||
|
}
|
||
|
return true;
|
||
|
}
|
||
|
case SHT_NOBITS: {
|
||
|
memset(destination, 0, shdr->sh_size);
|
||
|
return true;
|
||
|
}
|
||
|
default:
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bool ElfTools::loadElfSymtab(Elf *elf, Elf32_Sym **symtab, size_t *symtab_count, size_t *symtab_strndx) {
|
||
|
Elf_Scn *scn;
|
||
|
bool result = false;
|
||
|
|
||
|
for (scn = elf_nextscn(elf, NULL); scn != NULL; scn = elf_nextscn(elf, scn)) {
|
||
|
|
||
|
Elf32_Shdr *shdr;
|
||
|
|
||
|
shdr = elf32_getshdr(scn);
|
||
|
if (shdr == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (shdr->sh_type == SHT_SYMTAB) {
|
||
|
size_t sym;
|
||
|
|
||
|
if (*symtab != NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
*symtab = (Elf32_Sym *)malloc(shdr->sh_size);
|
||
|
if (*symtab == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
*symtab_count = shdr->sh_size / sizeof(Elf32_Sym);
|
||
|
*symtab_strndx = shdr->sh_link;
|
||
|
|
||
|
if (!elfLoadSection(elf, scn, shdr, *symtab)) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
for (sym = 0; sym < *symtab_count; sym++) {
|
||
|
(*symtab)[sym].st_other = 0;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (*symtab == NULL) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
result = true;
|
||
|
exit_error:
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
|
||
|
void ElfTools::elfLoadSymbols(size_t shndx, const void *destination, Elf32_Sym *symtab, size_t symtab_count) {
|
||
|
size_t i;
|
||
|
|
||
|
/* use the st_other field (no defined meaning) to indicate whether or not a
|
||
|
* symbol address has been calculated. */
|
||
|
for (i = 0; i < symtab_count; i++) {
|
||
|
if (symtab[i].st_shndx == shndx &&
|
||
|
symtab[i].st_other == 0) {
|
||
|
|
||
|
symtab[i].st_value += (Elf32_Addr)destination;
|
||
|
symtab[i].st_other = 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
bool ElfTools::elfLink(Elf *elf, size_t shndx, void *destination, Elf32_Sym *symtab, size_t symtab_count, size_t symtab_strndx, bool allow_globals, PluginData * pluginData) {
|
||
|
Elf_Scn *scn;
|
||
|
|
||
|
for (scn = elf_nextscn(elf, NULL); scn != NULL; scn = elf_nextscn(elf, scn)) {
|
||
|
Elf32_Shdr *shdr;
|
||
|
|
||
|
shdr = elf32_getshdr(scn);
|
||
|
if (shdr == NULL)
|
||
|
continue;
|
||
|
|
||
|
switch (shdr->sh_type) {
|
||
|
case SHT_REL: {
|
||
|
const Elf32_Rel *rel;
|
||
|
Elf_Data *data;
|
||
|
size_t i;
|
||
|
|
||
|
if (shdr->sh_info != shndx) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
data = elf_getdata(scn, NULL);
|
||
|
if (data == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
rel = (const Elf32_Rel *) data->d_buf;
|
||
|
|
||
|
for (i = 0; i < shdr->sh_size / sizeof(Elf32_Rel); i++) {
|
||
|
uint32_t symbol_addr;
|
||
|
size_t symbol;
|
||
|
|
||
|
symbol = ELF32_R_SYM(rel[i].r_info);
|
||
|
|
||
|
if (symbol > symtab_count) {
|
||
|
DEBUG_FUNCTION_LINE("symbol > symtab_count\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (symtab[symbol].st_shndx) {
|
||
|
case SHN_ABS: {
|
||
|
symbol_addr = symtab[symbol].st_value;
|
||
|
break;
|
||
|
}
|
||
|
case SHN_COMMON: {
|
||
|
DEBUG_FUNCTION_LINE("case SHN_COMMON\n");
|
||
|
return false;
|
||
|
}
|
||
|
case SHN_UNDEF: {
|
||
|
|
||
|
if (allow_globals) {
|
||
|
DEBUG_FUNCTION_LINE("The elf still have unresolved relocations. This is not supported.");
|
||
|
/*
|
||
|
Not support and not needed.
|
||
|
|
||
|
module_unresolved_relocation_t *reloc;
|
||
|
char *name;
|
||
|
|
||
|
reloc = (module_unresolved_relocation_t *) Module_ListAllocate(
|
||
|
&module_relocations,
|
||
|
sizeof(module_unresolved_relocation_t), 1,
|
||
|
&module_relocations_capacity,
|
||
|
&module_relocations_count,
|
||
|
PLUGIN_RELOCATIONS_CAPCITY_DEFAULT);
|
||
|
if (reloc == NULL)
|
||
|
return false;
|
||
|
|
||
|
name = elf_strptr(
|
||
|
elf, symtab_strndx, symtab[symbol].st_name);
|
||
|
|
||
|
if (name == NULL) {
|
||
|
module_relocations_count--;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
reloc->name = strdup(name);
|
||
|
if (reloc->name == NULL) {
|
||
|
module_relocations_count--;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
reloc->module = index;
|
||
|
reloc->address = destination;
|
||
|
reloc->offset = rel[i].r_offset;
|
||
|
reloc->type = ELF32_R_TYPE(rel[i].r_info);
|
||
|
reloc->addend = *(int32_t *)((char *)destination + rel[i].r_offset);
|
||
|
|
||
|
continue;
|
||
|
*/
|
||
|
return false;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE("case SHN_UNDEF with !allow_globals\n");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
default: {
|
||
|
if (symtab[symbol].st_other != 1) {
|
||
|
|
||
|
DEBUG_FUNCTION_LINE("symtab[symbol].st_other != 1. it's %d %08X\n",symtab[symbol].st_other,symtab[symbol].st_other);
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
symbol_addr = symtab[symbol].st_value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ElfTools::elfLinkOne(ELF32_R_TYPE(rel[i].r_info), rel[i].r_offset, *(int32_t *)((char *)destination + rel[i].r_offset), destination, symbol_addr)) {
|
||
|
DEBUG_FUNCTION_LINE("elfLinkOne failed\n");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
case SHT_RELA: {
|
||
|
const Elf32_Rela *rela;
|
||
|
Elf_Data *data;
|
||
|
size_t i;
|
||
|
|
||
|
if (shdr->sh_info != shndx)
|
||
|
continue;
|
||
|
|
||
|
data = elf_getdata(scn, NULL);
|
||
|
if (data == NULL)
|
||
|
continue;
|
||
|
|
||
|
rela = (const Elf32_Rela *) data->d_buf;
|
||
|
|
||
|
for (i = 0; i < shdr->sh_size / sizeof(Elf32_Rela); i++) {
|
||
|
uint32_t symbol_addr;
|
||
|
size_t symbol;
|
||
|
|
||
|
symbol = ELF32_R_SYM(rela[i].r_info);
|
||
|
|
||
|
if (symbol > symtab_count) {
|
||
|
DEBUG_FUNCTION_LINE("symbol > symtab_count\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
switch (symtab[symbol].st_shndx) {
|
||
|
case SHN_ABS: {
|
||
|
symbol_addr = symtab[symbol].st_value;
|
||
|
break;
|
||
|
}
|
||
|
case SHN_COMMON: {
|
||
|
uint32_t align = symtab[symbol].st_value;
|
||
|
uint32_t size = symtab[symbol].st_size;
|
||
|
|
||
|
uint32_t address = pluginData->getMemoryForCommonBySymbol(symbol, align, size);
|
||
|
if(address == 0){
|
||
|
DEBUG_FUNCTION_LINE("Failed to get memory for common relocation\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (!ElfTools::elfLinkOne(ELF32_R_TYPE(rela[i].r_info), rela[i].r_offset,rela[i].r_addend, destination, address)) {
|
||
|
DEBUG_FUNCTION_LINE("elfLinkOne failed\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
break;
|
||
|
}
|
||
|
case SHN_UNDEF: {
|
||
|
if (allow_globals) {
|
||
|
char *name = elf_strptr(elf, symtab_strndx, symtab[symbol].st_name);
|
||
|
DEBUG_FUNCTION_LINE("The elf still have unresolved relocations. This is not supported: %s \n",name);
|
||
|
|
||
|
/*
|
||
|
|
||
|
char *name = elf_strptr(elf, symtab_strndx, symtab[symbol].st_name);
|
||
|
DEBUG_FUNCTION_LINE("%s %08X\n",name,symtab[symbol].st_value);
|
||
|
|
||
|
Not support and not needed.
|
||
|
module_unresolved_relocation_t *reloc;
|
||
|
char *name;
|
||
|
|
||
|
reloc = (module_unresolved_relocation_t *) Module_ListAllocate(
|
||
|
&module_relocations,
|
||
|
sizeof(module_unresolved_relocation_t), 1,
|
||
|
&module_relocations_capacity,
|
||
|
&module_relocations_count,
|
||
|
PLUGIN_RELOCATIONS_CAPCITY_DEFAULT);
|
||
|
if (reloc == NULL)
|
||
|
return false;
|
||
|
|
||
|
name = elf_strptr(
|
||
|
elf, symtab_strndx, symtab[symbol].st_name);
|
||
|
|
||
|
if (name == NULL) {
|
||
|
module_relocations_count--;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
reloc->name = strdup(name);
|
||
|
if (reloc->name == NULL) {
|
||
|
module_relocations_count--;
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
DEBUG_FUNCTION_LINE("Adding relocation!\n");
|
||
|
|
||
|
reloc->module = index;
|
||
|
reloc->address = destination;
|
||
|
reloc->offset = rela[i].r_offset;
|
||
|
reloc->type = ELF32_R_TYPE(rela[i].r_info);
|
||
|
reloc->addend = rela[i].r_addend;
|
||
|
|
||
|
continue;*/
|
||
|
continue;
|
||
|
} else {
|
||
|
DEBUG_FUNCTION_LINE("case SHN_UNDEF with !allow_global\n");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
default: {
|
||
|
if (symtab[symbol].st_other != 1) {
|
||
|
char *name = elf_strptr(elf, symtab_strndx, symtab[symbol].st_name);
|
||
|
if(pluginData == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("No plugin data provided, but we (probably) need it.\n");
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
ImportRPLInformation * rplInfo = pluginData->getImportRPLInformationBySectionHeaderIndex(symtab[symbol].st_shndx);
|
||
|
if(rplInfo == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Couldn't find ImportRPLInformation for section header index %d \n", symtab[symbol].st_shndx);
|
||
|
return false;
|
||
|
}
|
||
|
RelocationData * relocationData = new RelocationData(ELF32_R_TYPE(rela[i].r_info), rela[i].r_offset,rela[i].r_addend, destination, name, rplInfo);
|
||
|
pluginData->addRelocationData(relocationData);
|
||
|
|
||
|
continue;
|
||
|
}
|
||
|
symbol_addr = symtab[symbol].st_value;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!ElfTools::elfLinkOne(ELF32_R_TYPE(rela[i].r_info), rela[i].r_offset,rela[i].r_addend, destination, symbol_addr)) {
|
||
|
DEBUG_FUNCTION_LINE("elfLinkOne failed\n");
|
||
|
return false;
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
bool ElfTools::elfLinkOne(char type, size_t offset, int32_t addend, void *destination, uint32_t symbol_addr) {
|
||
|
int32_t value;
|
||
|
char *target = (char *)destination + offset;
|
||
|
bool result = false;
|
||
|
|
||
|
switch (type) {
|
||
|
case R_PPC_ADDR32:
|
||
|
case R_PPC_ADDR24:
|
||
|
case R_PPC_ADDR16:
|
||
|
case R_PPC_ADDR16_HI:
|
||
|
case R_PPC_ADDR16_HA:
|
||
|
case R_PPC_ADDR16_LO:
|
||
|
case R_PPC_ADDR14:
|
||
|
case R_PPC_ADDR14_BRTAKEN:
|
||
|
case R_PPC_ADDR14_BRNTAKEN:
|
||
|
case R_PPC_UADDR32:
|
||
|
case R_PPC_UADDR16: {
|
||
|
value = (int32_t)symbol_addr + addend;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_REL24:
|
||
|
case R_PPC_PLTREL24:
|
||
|
case R_PPC_LOCAL24PC:
|
||
|
case R_PPC_REL14:
|
||
|
case R_PPC_REL14_BRTAKEN:
|
||
|
case R_PPC_REL14_BRNTAKEN:
|
||
|
case R_PPC_REL32:
|
||
|
case R_PPC_ADDR30: {
|
||
|
value = (int32_t)symbol_addr + addend - (int32_t)target;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_SECTOFF:
|
||
|
case R_PPC_SECTOFF_LO:
|
||
|
case R_PPC_SECTOFF_HI:
|
||
|
case R_PPC_SECTOFF_HA: {
|
||
|
value = offset + addend;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_EMB_NADDR32:
|
||
|
case R_PPC_EMB_NADDR16:
|
||
|
case R_PPC_EMB_NADDR16_LO:
|
||
|
case R_PPC_EMB_NADDR16_HI:
|
||
|
case R_PPC_EMB_NADDR16_HA: {
|
||
|
value = addend - (int32_t)symbol_addr;
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
DEBUG_FUNCTION_LINE("Unknown relocation type: %02X\n",type);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
switch (type) {
|
||
|
case R_PPC_ADDR32:
|
||
|
case R_PPC_UADDR32:
|
||
|
case R_PPC_REL32:
|
||
|
case R_PPC_SECTOFF:
|
||
|
case R_PPC_EMB_NADDR32: {
|
||
|
*(int32_t *)target = value;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR24:
|
||
|
case R_PPC_PLTREL24:
|
||
|
case R_PPC_LOCAL24PC:
|
||
|
case R_PPC_REL24: {
|
||
|
*(int32_t *)target =
|
||
|
(*(int32_t *)target & 0xfc000003) | (value & 0x03fffffc);
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR16:
|
||
|
case R_PPC_UADDR16:
|
||
|
case R_PPC_EMB_NADDR16: {
|
||
|
*(short *)target = value;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR16_HI:
|
||
|
case R_PPC_SECTOFF_HI:
|
||
|
case R_PPC_EMB_NADDR16_HI: {
|
||
|
*(short *)target = value >> 16;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR16_HA:
|
||
|
case R_PPC_SECTOFF_HA:
|
||
|
case R_PPC_EMB_NADDR16_HA: {
|
||
|
*(short *)target = (((value >> 16) + ((value & 0x8000) ? 1 : 0))) & 0xFFFF;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR16_LO:
|
||
|
case R_PPC_SECTOFF_LO:
|
||
|
case R_PPC_EMB_NADDR16_LO: {
|
||
|
*(short *)target = value & 0xffff;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR14:
|
||
|
case R_PPC_REL14: {
|
||
|
*(int32_t *)target =
|
||
|
(*(int32_t *)target & 0xffff0003) | (value & 0x0000fffc);
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR14_BRTAKEN:
|
||
|
case R_PPC_REL14_BRTAKEN: {
|
||
|
*(int32_t *)target =
|
||
|
(*(int32_t *)target & 0xffdf0003) | (value & 0x0000fffc) |
|
||
|
0x00200000;
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR14_BRNTAKEN:
|
||
|
case R_PPC_REL14_BRNTAKEN: {
|
||
|
*(int32_t *)target =
|
||
|
(*(int32_t *)target & 0xffdf0003) | (value & 0x0000fffc);
|
||
|
break;
|
||
|
}
|
||
|
case R_PPC_ADDR30: {
|
||
|
*(int32_t *)target =
|
||
|
(*(int32_t *)target & 0x00000003) | (value & 0xfffffffc);
|
||
|
break;
|
||
|
}
|
||
|
default:
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
result = true;
|
||
|
exit_error:
|
||
|
if (!result) DEBUG_FUNCTION_LINE("Plugin_ElfLinkOne: exit_error\n");
|
||
|
return result;
|
||
|
}
|
||
|
|