mirror of
https://github.com/Maschell/WiiUPluginLoader.git
synced 2024-09-29 16:08:35 +02:00
424 lines
13 KiB
C++
424 lines
13 KiB
C++
|
/* based on plugin.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 "PluginInformation.h"
|
||
|
#include <utils/logger.h>
|
||
|
#include <dynamic_libs/os_types.h>
|
||
|
#include <libelf.h>
|
||
|
#include <string.h>
|
||
|
#include <sys/types.h>
|
||
|
#include <wups.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <fcntl.h>
|
||
|
#include <unistd.h>
|
||
|
#include <utils/utils.h>
|
||
|
#include "ElfTools.h"
|
||
|
|
||
|
bool PluginInformation::checkFileExtenstion(const char * path) {
|
||
|
if(path == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
const char *extension;
|
||
|
|
||
|
/* find the file extension */
|
||
|
extension = strrchr(path, '.');
|
||
|
if (extension == NULL) {
|
||
|
extension = strchr(path, '\0');
|
||
|
} else {
|
||
|
extension++;
|
||
|
}
|
||
|
|
||
|
if(extension == NULL) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
if (strcmp(extension, "mod") == 0 ||
|
||
|
strcmp(extension, "o") == 0 ||
|
||
|
strcmp(extension, "a") == 0 ||
|
||
|
strcmp(extension, "elf") == 0) {
|
||
|
return true;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
bool PluginInformation::openAndParseElf() {
|
||
|
bool result = false;
|
||
|
int32_t fd = -1;
|
||
|
Elf *elf = NULL;
|
||
|
|
||
|
/* check for compile errors */
|
||
|
if (elf_version(EV_CURRENT) == EV_NONE) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
fd = open(getPath().c_str(), O_RDONLY, 0);
|
||
|
|
||
|
if (fd == -1) {
|
||
|
DEBUG_FUNCTION_LINE("failed to open '%s' \n", getPath().c_str());
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
elf = elf_begin(fd, ELF_C_READ, NULL);
|
||
|
|
||
|
if (elf == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("elf was NULL\n");
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
switch (elf_kind(elf)) {
|
||
|
case ELF_K_AR:
|
||
|
/* TODO */
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Archives not yet supported.\n", getPath().c_str());
|
||
|
goto exit_error;
|
||
|
case ELF_K_ELF:
|
||
|
result = this->parseElf(elf);
|
||
|
break;
|
||
|
default:
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Invalid ELF file.\n", getPath().c_str());
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
exit_error:
|
||
|
if (elf != NULL) {
|
||
|
elf_end(elf);
|
||
|
}
|
||
|
if (fd != -1) {
|
||
|
close(fd);
|
||
|
}
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
bool PluginInformation::parseElf( Elf *elf) {
|
||
|
bool res = false;
|
||
|
Elf_Scn *scn;
|
||
|
Elf32_Ehdr *ehdr;
|
||
|
char *ident;
|
||
|
size_t shstrndx, sz, symtab_count, symtab_strndx;
|
||
|
Elf32_Sym *symtab = NULL;
|
||
|
|
||
|
size_t cur_size = 0;
|
||
|
|
||
|
const char * path_c = getPath().c_str();
|
||
|
|
||
|
if(elf == NULL) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if(elf_kind(elf) != ELF_K_ELF) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
ident = elf_getident(elf, &sz);
|
||
|
|
||
|
if (ident == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Invalid ELF header.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (sz < 7) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Invalid ELF header.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (ident[4] != ELFCLASS32) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Not 32 bit ELF.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (ident[5] != ELFDATA2MSB) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Not Big Endian.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (ident[6] != EV_CURRENT) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Unknown ELF version.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
ehdr = elf32_getehdr(elf);
|
||
|
|
||
|
if (ehdr == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Invalid ELF header\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
/*if (ehdr->e_type != ET_REL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Not relocatable ELF.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}*/
|
||
|
if (ehdr->e_machine != EM_PPC) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Architecture not EM_PPC.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (ehdr->e_version != EV_CURRENT) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Unknown ELF version.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
if (!ElfTools::loadElfSymtab(elf, &symtab, &symtab_count, &symtab_strndx)) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Couldn't parse symtab.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
if(symtab == NULL) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
if(!metadataRead(elf, symtab, symtab_count, symtab_strndx)) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Couldn't find shdrstndx.\n", path_c);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
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_PROGBITS || shdr->sh_type == SHT_NOBITS) &&
|
||
|
(shdr->sh_flags & SHF_ALLOC)) {
|
||
|
|
||
|
const char *name;
|
||
|
|
||
|
name = elf_strptr(elf, shstrndx, shdr->sh_name);
|
||
|
if (name == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (strcmp(name, ".wups.meta") == 0) {
|
||
|
continue;
|
||
|
} else if (strcmp(name, ".wups.load") == 0) {
|
||
|
cur_size +=
|
||
|
shdr->sh_size / sizeof(wups_loader_entry_t) * 6*4;
|
||
|
} else if (strcmp(name, ".wups.hooks") == 0) {
|
||
|
cur_size +=
|
||
|
shdr->sh_size / sizeof(wups_loader_hook_t) * 2*4;
|
||
|
} else {
|
||
|
cur_size += shdr->sh_size;
|
||
|
/* add alignment padding to size */
|
||
|
if (shdr->sh_addralign > 3) {
|
||
|
/* roundup to multiple of sh_addralign */
|
||
|
cur_size += (-cur_size & (shdr->sh_addralign - 1));
|
||
|
} else {
|
||
|
/* roundup to multiple of 4 */
|
||
|
cur_size += (-cur_size & 3);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/* roundup to multiple of 4 */
|
||
|
cur_size += (-cur_size & 3);
|
||
|
|
||
|
this->setSize(cur_size);
|
||
|
|
||
|
res = true;
|
||
|
exit_error:
|
||
|
if (symtab != NULL) {
|
||
|
free(symtab);
|
||
|
}
|
||
|
|
||
|
return res;
|
||
|
}
|
||
|
|
||
|
bool PluginInformation::metadataRead(Elf *elf, Elf32_Sym *symtab, size_t symtab_count, size_t symtab_strndx) {
|
||
|
char *metadata = NULL, *metadata_cur, *metadata_end;
|
||
|
const char *name, *author, *version, *license, *wups, *buildtimestamp, *description;
|
||
|
|
||
|
Elf_Scn *scn;
|
||
|
size_t shstrndx;
|
||
|
|
||
|
if (elf_getshdrstrndx(elf, &shstrndx) != 0) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Couldn't find shstrndx\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
for (scn = elf_nextscn(elf, NULL); scn != NULL; scn = elf_nextscn(elf, scn)) {
|
||
|
Elf32_Shdr *shdr;
|
||
|
const char *name;
|
||
|
|
||
|
shdr = elf32_getshdr(scn);
|
||
|
if (shdr == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
name = elf_strptr(elf, shstrndx, shdr->sh_name);
|
||
|
if (name == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (strcmp(name, ".wups.meta") == 0) {
|
||
|
if (shdr->sh_size == 0) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (metadata != NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
metadata = (char*) malloc(shdr->sh_size);
|
||
|
|
||
|
if (metadata == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (!ElfTools::elfLoadSection(elf, scn, shdr, metadata)) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Couldn't load .wups.meta.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
ElfTools::elfLoadSymbols(elf_ndxscn(scn), metadata, symtab, symtab_count);
|
||
|
|
||
|
if (!ElfTools::elfLink(elf, elf_ndxscn(scn), metadata, symtab, symtab_count, symtab_strndx, false)) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - .wups.meta contains invalid relocations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
metadata_end = metadata + shdr->sh_size;
|
||
|
metadata_end[-1] = '\0';
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (metadata == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Not a WUPS plugin file.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
name = NULL;
|
||
|
author = NULL;
|
||
|
version = NULL;
|
||
|
license = NULL;
|
||
|
wups = NULL;
|
||
|
buildtimestamp = NULL;
|
||
|
description = NULL;
|
||
|
|
||
|
for (metadata_cur = metadata; metadata_cur < metadata_end; metadata_cur = strchr(metadata_cur, '\0') + 1) {
|
||
|
|
||
|
char *eq;
|
||
|
|
||
|
if(metadata_cur < metadata || metadata_cur >= metadata_end) {
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
if (*metadata_cur == '\0') {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
eq = strchr(metadata_cur, '=');
|
||
|
if (eq == NULL) {
|
||
|
continue;
|
||
|
}
|
||
|
|
||
|
if (strncmp(metadata_cur, "name", eq - metadata_cur) == 0) {
|
||
|
if (name != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_NAME declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
name = eq + 1;
|
||
|
} else if (strncmp(metadata_cur, "author", eq - metadata_cur) == 0) {
|
||
|
if (author != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_AUTHOR declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
author = eq + 1;
|
||
|
} else if (strncmp(metadata_cur, "version", eq - metadata_cur) == 0) {
|
||
|
if (version != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_VERSION declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
version = eq + 1;
|
||
|
} else if (strncmp(metadata_cur, "license", eq - metadata_cur) == 0) {
|
||
|
if (license != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_LICENSE declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
license = eq + 1;
|
||
|
} else if (strncmp(metadata_cur, "buildtimestamp", eq - metadata_cur) == 0) {
|
||
|
if (buildtimestamp != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_TIMESTAMP declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
buildtimestamp = eq + 1;
|
||
|
} else if (strncmp(metadata_cur, "description", eq - metadata_cur) == 0) {
|
||
|
if (description != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_LICENSE declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
description = eq + 1;
|
||
|
} else if (strncmp(metadata_cur, "wups", eq - metadata_cur) == 0) {
|
||
|
if (wups != NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Multiple WUPS_PLUGIN_NAME declarations.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
wups = eq + 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (description == NULL) {
|
||
|
description = "";
|
||
|
}
|
||
|
|
||
|
if (wups == NULL || strcmp(wups, "0.1") != 0) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Unrecognised WUPS version.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (buildtimestamp == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Couldn't find buildtimestamp.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (name == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Missing WUPS_PLUGIN_NAME declaration.\n",path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (author == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Missing WUPS_PLUGIN_AUTHOR declaration.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (version == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Missing WUPS_PLUGIN_VERSION declaration.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
if (license == NULL) {
|
||
|
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Missing WUPS_PLUGIN_LICENSE declaration.\n", path);
|
||
|
goto exit_error;
|
||
|
}
|
||
|
|
||
|
this->setName(name);
|
||
|
this->setAuthor(author);
|
||
|
this->setVersion(version);
|
||
|
this->setLicense(license);
|
||
|
this->setBuildTimestamp(buildtimestamp);
|
||
|
this->setDescription(description);
|
||
|
|
||
|
return true;
|
||
|
|
||
|
exit_error:
|
||
|
|
||
|
if (metadata != NULL) {
|
||
|
free(metadata);
|
||
|
}
|
||
|
return false;
|
||
|
}
|