[Loader] Refactored the plugin loading.

- Now all plugins in the "sd:/wiiu/plugins" folder will be loaded.
- Minor code cleanup
This commit is contained in:
Maschell 2018-02-18 15:55:43 +01:00
parent f6ec4bcc56
commit d0908e7eb6
7 changed files with 760 additions and 427 deletions

View File

@ -30,6 +30,7 @@
#include "common/retain_vars.h"
#include "common/common.h"
#include "modules/ModuleLoader.h"
#include "modules/ModuleData.h"
#include <utils/function_patcher.h>
@ -50,19 +51,13 @@
#include "version.h"
#include "settings/CSettings.h"
static bool loadSamplePlugins();
static void ApplyPatches();
void CallHook(wups_loader_hook_type_t hook_type);
static void RestorePatches();
s32 isInMiiMakerHBL();
static void copyDataIntoGlobalStruct(std::vector<ModuleData *>* modules);
static void loadElf(std::vector<ModuleData *>* modules, const char * elfPath, uint8_t ** space);
u8 isFirstBoot __attribute__((section(".data"))) = 1;
#define PLUGIN_LOCATION_END_ADDRESS 0x01000000
/* Entry point */
extern "C" int Menu_Main(int argc, char **argv){
if(gAppStatus == 2){
@ -93,9 +88,10 @@ extern "C" int Menu_Main(int argc, char **argv){
if(isFirstBoot){
memset((void*)&gbl_replacement_data,0,sizeof(gbl_replacement_data));
if(!loadSamplePlugins()){
return EXIT_SUCCESS;
}
ModuleLoader * moduleLoader = ModuleLoader::getInstance();
std::vector<ModuleInformation *> moduleList = moduleLoader->getModuleInformation("sd:/wiiu/plugins/");
moduleLoader->loadAndLinkModules(moduleList);
//!*******************************************************************
//! Initialize heap memory *
@ -221,120 +217,6 @@ s32 isInMiiMakerHBL(){
return 0;
}
bool loadSamplePlugins(){
if((gSDInitDone & WUPS_SD_MOUNTED) > 0){
DEBUG_FUNCTION_LINE("Mounting successful. Loading modules\n");
std::vector<ModuleData *> modules;
// Set a pointer to the END of our plugin location.
// So our plugins will be stored from PLUGIN_LOCATION_END_ADDRESS backward. Maximum is getApplicationEndAddr() (behind this loader).
// Copied the "reserved" direction from brainslug, not sure why we're doing this.
unsigned char * space = (unsigned char*)PLUGIN_LOCATION_END_ADDRESS;
// TODO: Load all files from a certain directory, don't use hardcoded paths. (common.h)
loadElf(&modules, "sd:/wiiu/plugins/hid_to_vpad.mod",&space);
loadElf(&modules, "sd:/wiiu/plugins/sdcafiine.mod",&space);
loadElf(&modules, "sd:/wiiu/plugins/padcon.mod",&space);
loadElf(&modules, "sd:/wiiu/plugins/swipswapme.mod",&space);
// Copy the data into the global struct locating in the .data section.
copyDataIntoGlobalStruct(&modules);
// Free memory.
for(size_t i = 0; i< modules.size();i++){
ModuleData * cur_module = modules[i];
if(cur_module != NULL){
free(cur_module);
}
}
DEBUG_FUNCTION_LINE("Flush memory\n");
DCFlushRange ((void*)getApplicationEndAddr(),PLUGIN_LOCATION_END_ADDRESS-getApplicationEndAddr());
DCInvalidateRange((void*)getApplicationEndAddr(),PLUGIN_LOCATION_END_ADDRESS-getApplicationEndAddr());
// TODO: keep it mounted for the plugins. But this would require sharing the read/write/open etc. functions from this loader.
// Idea: Giving the init hook the pointers. Hiding the __wrap function of the plugin behind the INITIALIZE macro.
// Needs to be tested if this is working. This would have the advantage of adopting all right/accesses from the loader (libfat, libntfs, iosuhax etc.)
//unmount_sd_fat("sd");
}
return true;
}
static void copyDataIntoGlobalStruct(std::vector<ModuleData *>* modules){
int module_index = 0;
// Copy data to global struct.
for(size_t i = 0; i< modules->size();i++){
ModuleData * cur_module = modules->at(i);
std::vector<EntryData *> entry_data_list = cur_module->getEntryDataList();
std::vector<HookData *> hook_data_list = cur_module->getHookDataList();
if(module_index >= MAXIMUM_MODULES ){
DEBUG_FUNCTION_LINE("Maximum of %d modules reached. %s won't be loaded!\n",MAXIMUM_MODULES,cur_module->getName().c_str());
continue;
}
if(entry_data_list.size() > MAXIMUM_FUNCTION_PER_MODULE){
DEBUG_FUNCTION_LINE("Module %s would replace to many function (%d, maximum is %d). It won't be loaded.\n",cur_module->getName().c_str(),entry_data_list.size(),MAXIMUM_FUNCTION_PER_MODULE);
continue;
}
if(hook_data_list.size() > MAXIMUM_HOOKS_PER_MODULE){
DEBUG_FUNCTION_LINE("Module %s would set too many hooks (%d, maximum is %d). It won't be loaded.\n",cur_module->getName().c_str(),hook_data_list.size(),MAXIMUM_HOOKS_PER_MODULE);
continue;
}
replacement_data_module_t * module_data = &gbl_replacement_data.module_data[module_index];
strncpy(module_data->module_name,cur_module->getName().c_str(),MAXIMUM_MODULE_NAME_LENGTH-1);
for(size_t j = 0; j < entry_data_list.size();j++){
replacement_data_function_t * function_data = &module_data->functions[j];
EntryData * cur_entry = entry_data_list[j];
DEBUG_FUNCTION_LINE("Adding entry \"%s\" for module \"%s\"\n",cur_entry->getName().c_str(),module_data->module_name);
//TODO: Warning/Error if string is too long.
strncpy(function_data->function_name,cur_entry->getName().c_str(),MAXIMUM_FUNCTION_NAME_LENGTH-1);
function_data->library = cur_entry->getLibrary();
function_data->replaceAddr = (u32) cur_entry->getReplaceAddress();
function_data->replaceCall = (u32) cur_entry->getReplaceCall();
module_data->number_used_functions++;
}
DEBUG_FUNCTION_LINE("Entries for module \"%s\": %d\n",module_data->module_name,module_data->number_used_functions);
for(size_t j = 0; j < hook_data_list.size();j++){
replacement_data_hook_t * hook_data = &module_data->hooks[j];
HookData * hook_entry = hook_data_list[j];
DEBUG_FUNCTION_LINE("Set hook for module \"%s\" of type %08X to target %08X\n",module_data->module_name,hook_entry->getType(),(void*) hook_entry->getFunctionPointer());
hook_data->func_pointer = (void*) hook_entry->getFunctionPointer();
hook_data->type = hook_entry->getType();
module_data->number_used_hooks++;
}
DEBUG_FUNCTION_LINE("Hooks for module \"%s\": %d\n",module_data->module_name,module_data->number_used_hooks);
module_index++;
gbl_replacement_data.number_used_modules++;
}
}
static void loadElf(std::vector<ModuleData *>* modules, const char * elfPath, uint8_t ** space){
DEBUG_FUNCTION_LINE("Try to load %s\n",elfPath);
ModuleData * module = new ModuleData(elfPath,space);
if(module->isLoadedSuccessfully()){
DEBUG_FUNCTION_LINE("%s loading was successful!. \n", elfPath);
modules->push_back(module);
} else {
DEBUG_FUNCTION_LINE("%s loading failed. \n", elfPath);
}
}
void Init_SD_USB() {
int res = IOSUHAX_Open(NULL);
if(res < 0){

View File

@ -26,13 +26,11 @@
#include "ElfTools.h"
#include <string.h>
#include <malloc.h>
#include <assert.h>
#include <libelf.h>
#include <utils/logger.h>
bool ElfTools::elfLoadSection(const Elf *elf, Elf_Scn *scn, const Elf32_Shdr *shdr,void *destination) {
assert(destination != NULL);
if (destination == NULL) { return false; }
switch (shdr->sh_type) {
case SHT_SYMTAB:
@ -58,29 +56,33 @@ bool ElfTools::loadElfSymtab(Elf *elf, Elf32_Sym **symtab, size_t *symtab_count,
Elf_Scn *scn;
bool result = false;
for (scn = elf_nextscn(elf, NULL);
scn != NULL;
scn = elf_nextscn(elf, scn)) {
for (scn = elf_nextscn(elf, NULL); scn != NULL; scn = elf_nextscn(elf, scn)) {
Elf32_Shdr *shdr;
shdr = elf32_getshdr(scn);
if (shdr == NULL)
if (shdr == NULL){
continue;
}
if (shdr->sh_type == SHT_SYMTAB) {
size_t sym;
assert (*symtab == NULL);
*symtab = (Elf32_Sym *)malloc(shdr->sh_size);
if (*symtab == NULL)
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))
if (!elfLoadSection(elf, scn, shdr, *symtab)){
goto exit_error;
}
for (sym = 0; sym < *symtab_count; sym++){
(*symtab)[sym].st_other = 0;
@ -90,8 +92,9 @@ bool ElfTools::loadElfSymtab(Elf *elf, Elf32_Sym **symtab, size_t *symtab_count,
}
}
if (*symtab == NULL)
if (*symtab == NULL){
goto exit_error;
}
result = true;
exit_error:
@ -354,7 +357,7 @@ bool ElfTools::elfLinkOne(char type, size_t offset, int addend, void *destinatio
value = addend - (int)symbol_addr;
break;
} default:
DEBUG_FUNCTION_LINE("Module_ElfLinkOne01: %02X\n",type);
DEBUG_FUNCTION_LINE("Unknown relocation type: %02X\n",type);
goto exit_error;
}
@ -414,7 +417,6 @@ bool ElfTools::elfLinkOne(char type, size_t offset, int addend, void *destinatio
(*(int *)target & 0x00000003) | (value & 0xfffffffc);
break;
}default:
DEBUG_FUNCTION_LINE("Module_ElfLinkOne01: %02X\n",type);
goto exit_error;
}

View File

@ -1,27 +1,19 @@
/* based on module.c
* by Alex Chadwick
/****************************************************************************
* Copyright (C) 2018 Maschell
*
* Copyright (C) 2014, Alex Chadwick
* Modified 2018, Maschell
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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:
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* 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.
*/
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef _MODULE_DATA_H_
#define _MODULE_DATA_H_
@ -30,6 +22,7 @@
#include <vector>
#include "EntryData.h"
#include "HookData.h"
#include "ModuleInformation.h"
#include <utils/logger.h>
#ifdef __cplusplus
@ -44,14 +37,8 @@ extern "C" {
class ModuleData{
public:
ModuleData(std::string path, uint8_t ** space){
this->path = path;
if(checkFile()){
DEBUG_FUNCTION_LINE("Checkfile successfully, loading now \n");
this->loadedSuccessfully = load(space);
} else {
this->loadedSuccessfully = false;
}
ModuleData(ModuleInformation * moduleInformation){
this->moduleInformation = moduleInformation;
}
~ModuleData(){
@ -68,50 +55,6 @@ class ModuleData{
}
}
void setName(const char * name){
this->name = name;
}
void setAuthor(const char * author){
this->author = author;
}
void setVersion(const char * version){
this->version = version;
}
void setLicense(const char * license){
this->license = license;
}
void setSize(size_t size){
this->size = size;
}
std::string getName(){
return this->name;
}
std::string getAuthor(){
return this->author;
}
std::string getVersion(){
return this->version;
}
std::string getLicense(){
return this->license;
}
std::string getPath(){
return path;
}
bool isLoadedSuccessfully(){
return loadedSuccessfully;
}
void addEntryData(EntryData * entry_data){
entry_data_list.push_back(entry_data);
}
@ -128,26 +71,13 @@ class ModuleData{
return hook_data_list;
}
ModuleInformation * getModuleInformation(){
return moduleInformation;
}
private:
bool checkFile();
bool load(uint8_t ** space);
bool loadElf(Elf *elf);
bool metadataRead(Elf *elf, Elf32_Sym *symtab, size_t symtab_count, size_t symtab_strndx);
bool linkModuleElf(Elf *elf, uint8_t **space);
bool loadedSuccessfully = false;
std::string path;
std::string name;
std::string author;
std::string version;
std::string license;
size_t size;
ModuleInformation * moduleInformation;
std::vector<EntryData *> entry_data_list;
std::vector<HookData *> hook_data_list;

View File

@ -23,12 +23,11 @@
* SOFTWARE.
*/
#include "ModuleData.h"
#include "ModuleInformation.h"
#include <utils/logger.h>
#include <dynamic_libs/os_types.h>
#include <libelf.h>
#include <string.h>
#include <assert.h>
#include <sys/types.h>
#include <wups.h>
#include <sys/stat.h>
@ -37,20 +36,23 @@
#include <utils/utils.h>
#include "ElfTools.h"
bool ModuleData::checkFile() {
bool ModuleInformation::checkFileExtenstion(const char * path) {
if(path == NULL){
return false;
}
const char *extension;
const char * path_c = getPath().c_str();
/* find the file extension */
extension = strrchr(path_c, '.');
extension = strrchr(path, '.');
if (extension == NULL){
extension = strchr(path_c, '\0');
extension = strchr(path, '\0');
}else{
extension++;
}
assert(extension != NULL);
if(extension == NULL){
return false;
}
if (strcmp(extension, "mod") == 0 ||
strcmp(extension, "o") == 0 ||
@ -61,14 +63,13 @@ bool ModuleData::checkFile() {
return false;
}
bool ModuleData::load(uint8_t ** space) {
bool ModuleInformation::openAndParseElf() {
bool result = false;
int fd = -1;
Elf *elf = NULL;
/* check for compile errors */
if (elf_version(EV_CURRENT) == EV_NONE){
DEBUG_FUNCTION_LINE("Compiler errors in '%s' \n", getPath().c_str());
goto exit_error;
}
@ -92,10 +93,7 @@ bool ModuleData::load(uint8_t ** space) {
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Archives not yet supported.\n", getPath().c_str());
goto exit_error;
case ELF_K_ELF:
if(!this->loadElf(elf)){
goto exit_error;
}
result = linkModuleElf(elf,space);
result = this->parseElf(elf);
break;
default:
DEBUG_FUNCTION_LINE("Warning: Ignoring '%s' - Invalid ELF file.\n", getPath().c_str());
@ -112,7 +110,7 @@ exit_error:
return result;
}
bool ModuleData::loadElf( Elf *elf) {
bool ModuleInformation::parseElf( Elf *elf) {
bool res = false;
Elf_Scn *scn;
Elf32_Ehdr *ehdr;
@ -124,8 +122,8 @@ bool ModuleData::loadElf( Elf *elf) {
const char * path_c = getPath().c_str();
assert(elf != NULL);
assert(elf_kind(elf) == ELF_K_ELF);
if(elf == NULL){ goto exit_error; }
if(elf_kind(elf) != ELF_K_ELF){ goto exit_error; }
ident = elf_getident(elf, &sz);
@ -174,9 +172,7 @@ bool ModuleData::loadElf( Elf *elf) {
goto exit_error;
}
assert(symtab != NULL);
DEBUG_FUNCTION_LINE("Reading metadata from path %s.\n", path_c);
if(symtab == NULL){ goto exit_error; }
if(!metadataRead(elf, symtab, symtab_count, symtab_strndx)){
goto exit_error;
@ -241,7 +237,7 @@ exit_error:
return res;
}
bool ModuleData::metadataRead(Elf *elf, Elf32_Sym *symtab, size_t symtab_count, size_t symtab_strndx) {
bool ModuleInformation::metadataRead(Elf *elf, Elf32_Sym *symtab, size_t symtab_count, size_t symtab_strndx) {
char *metadata = NULL, *metadata_cur, *metadata_end;
const char *game, *name, *author, *version, *license, *wups;
@ -315,7 +311,9 @@ bool ModuleData::metadataRead(Elf *elf, Elf32_Sym *symtab, size_t symtab_count,
char *eq;
assert(metadata_cur >= metadata && metadata_cur < metadata_end);
if(metadata_cur < metadata || metadata_cur >= metadata_end){
goto exit_error;
}
if (*metadata_cur == '\0'){
continue;
@ -404,180 +402,3 @@ exit_error:
}
return false;
}
bool ModuleData::linkModuleElf(Elf *elf, uint8_t **space) {
Elf_Scn *scn;
size_t symtab_count, section_count, shstrndx, symtab_strndx, entries_count, hooks_count;
Elf32_Sym *symtab = NULL;
uint8_t **destinations = NULL;
wups_loader_entry_t *entries = NULL;
wups_loader_hook_t *hooks = NULL;
bool result = false;
std::vector<wups_loader_entry_t *> entry_t_list;
std::vector<wups_loader_hook_t *> hook_t_list;
std::vector<EntryData *> entry_data_list;
std::vector<HookData *> hook_data_list;
if (!ElfTools::loadElfSymtab(elf, &symtab, &symtab_count, &symtab_strndx)){
goto exit_error;
}
assert(symtab != NULL);
if (elf_getshdrnum(elf, &section_count) != 0){
goto exit_error;
}
if (elf_getshdrstrndx(elf, &shstrndx) != 0){
goto exit_error;
}
destinations = (uint8_t **) malloc(sizeof(uint8_t *) * section_count);
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;
destinations[elf_ndxscn(scn)] = NULL;
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) {
if (entries != NULL){
goto exit_error;
}
entries_count = shdr->sh_size / sizeof(wups_loader_entry_t);
entries = (wups_loader_entry_t *) malloc(sizeof(wups_loader_entry_t) * entries_count);
if (entries == NULL){
goto exit_error;
}
destinations[elf_ndxscn(scn)] = (uint8_t *)entries;
if (!ElfTools::elfLoadSection(elf, scn, shdr, entries)){
goto exit_error;
}
ElfTools::elfLoadSymbols(elf_ndxscn(scn), entries, symtab, symtab_count);
for(size_t i = 0;i< entries_count;i++){
entry_t_list.push_back(&entries[i]);
}
}else if (strcmp(name, ".wups.hooks") == 0) {
if (hooks != NULL){
goto exit_error;
}
hooks_count = shdr->sh_size / sizeof(wups_loader_hook_t);
hooks = (wups_loader_hook_t *) malloc(sizeof(wups_loader_hook_t) * hooks_count);
if (hooks == NULL){
goto exit_error;
}
destinations[elf_ndxscn(scn)] = (uint8_t *)hooks;
if (!ElfTools::elfLoadSection(elf, scn, shdr, hooks)){
goto exit_error;
}
ElfTools::elfLoadSymbols(elf_ndxscn(scn), hooks, symtab, symtab_count);
for(size_t i = 0;i< hooks_count;i++){
hook_t_list.push_back(&hooks[i]);
}
} else {
*space -= shdr->sh_size;
if (shdr->sh_addralign > 3)
*space = (uint8_t *)((int)*space &
~(shdr->sh_addralign - 1));
else
*space = (uint8_t *)((int)*space & ~3);
destinations[elf_ndxscn(scn)] = *space;
assert(*space != NULL);
if((u32) *space < getApplicationEndAddr()){
DEBUG_FUNCTION_LINE("Not enough space to load function %s into memory at %08X.\n",name,*space);
goto exit_error;
}
DEBUG_FUNCTION_LINE("Copy section %s to %08X\n",name,*space);
if (!ElfTools::elfLoadSection(elf, scn, shdr, *space)){
goto exit_error;
}
ElfTools::elfLoadSymbols(elf_ndxscn(scn), *space, symtab, symtab_count);
}
}
}
if (entries == NULL){
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) &&
destinations[elf_ndxscn(scn)] != NULL) {
if (!ElfTools::elfLink(elf, elf_ndxscn(scn), destinations[elf_ndxscn(scn)], symtab, symtab_count, symtab_strndx, true)){
goto exit_error;
}
}
}
for(size_t j=0;j<hook_t_list.size();j++){
wups_loader_hook_t * hook = hook_t_list[j];
DEBUG_FUNCTION_LINE("Saving hook of module \"%s\". Type: %08X, target: %08X\n",getName().c_str(),hook->type,(void*) hook->target);
HookData * hook_data = new HookData((void *) hook->target,hook->type);
addHookData(hook_data);
}
for(size_t j=0;j<entry_t_list.size();j++){
wups_loader_entry_t * entry = entry_t_list[j];
DEBUG_FUNCTION_LINE("Saving entry \"%s\" of module \"%s\". Library: %08X, target: %08X, call_addr: %08X\n",entry->_function.name,getName().c_str(),entry->_function.library,entry->_function.target, (void *) entry->_function.call_addr);
EntryData * entry_data = new EntryData(entry->_function.name,entry->_function.library, (void *) entry->_function.target, (void *) entry->_function.call_addr);
addEntryData(entry_data);
}
result = true;
exit_error:
if (!result) DEBUG_FUNCTION_LINE("exit_error\n");
if (destinations != NULL){
free(destinations);
}
if (symtab != NULL){
free(symtab);
}
if (hooks != NULL){
free(hooks);
}
if (entries != NULL){
free(entries);
}
return result;
}

View File

@ -0,0 +1,133 @@
/* 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.
*/
#ifndef _MODULE_INFORMATION_H_
#define _MODULE_INFORMATION_H_
#include <string>
#include <vector>
#include "EntryData.h"
#include "HookData.h"
#include <utils/logger.h>
#ifdef __cplusplus
extern "C" {
#endif
#include <libelf.h>
#ifdef __cplusplus
}
#endif
class ModuleInformation{
public:
/**
returns ModuleInformation* if a valid plugin was found at the given path. Otherwise returns NULL
**/
static ModuleInformation * loadModuleInformation(std::string path){
if(ModuleInformation::checkFileExtenstion(path.c_str())){
DEBUG_FUNCTION_LINE("Checkfile successfully, loading now Module Information\n");
ModuleInformation * moduleInformation = new ModuleInformation(path);
if(moduleInformation->openAndParseElf()){
return moduleInformation;
}else{
delete moduleInformation;
return NULL;
}
} else {
return NULL;
}
}
std::string getName(){
return this->name;
}
std::string getAuthor(){
return this->author;
}
std::string getVersion(){
return this->version;
}
std::string getLicense(){
return this->license;
}
std::string getPath(){
return path;
}
size_t getSize(){
return this->size;
}
private:
ModuleInformation(std::string path){
this->path = path;
}
void setName(const char * name){
this->name = name;
}
void setAuthor(const char * author){
this->author = author;
}
void setVersion(const char * version){
this->version = version;
}
void setLicense(const char * license){
this->license = license;
}
void setSize(size_t size){
this->size = size;
}
static bool checkFileExtenstion(const char * path);
bool openAndParseElf();
bool parseElf(Elf *elf);
bool metadataRead(Elf *elf, Elf32_Sym *symtab, size_t symtab_count, size_t symtab_strndx);
bool loadedSuccessfully = false;
std::string path;
std::string name;
std::string author;
std::string version;
std::string license;
size_t size;
};
#endif

View File

@ -0,0 +1,414 @@
/* 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 <string>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include "ElfTools.h"
#include "ModuleData.h"
#include "ModuleLoader.h"
#include "utils/StringTools.h"
#include "common/retain_vars.h"
ModuleLoader * ModuleLoader::instance = NULL;
std::vector<ModuleInformation *> ModuleLoader::getModuleInformation(const char * path){
std::vector<ModuleInformation *> result;
struct dirent *dp;
DIR *dfd = NULL;
if(path == NULL){
DEBUG_FUNCTION_LINE("Path was NULL");
return result;
}
if ((dfd = opendir(path)) == NULL){
DEBUG_FUNCTION_LINE("Couldn't open dir %s",path);
return result;
}
while ((dp = readdir(dfd)) != NULL){
struct stat stbuf ;
std::string full_file_path = StringTools::strfmt("%s/%s",path,dp->d_name);
StringTools::RemoveDoubleSlashs(full_file_path);
if( stat(full_file_path.c_str(),&stbuf ) == -1 ){
DEBUG_FUNCTION_LINE("Unable to stat file: %s\n",full_file_path.c_str()) ;
continue;
}
if ( ( stbuf.st_mode & S_IFMT ) == S_IFDIR ){ // Skip directories
continue;
}else{
DEBUG_FUNCTION_LINE("Found file: %s\n",full_file_path.c_str()) ;
ModuleInformation * module = ModuleInformation::loadModuleInformation(full_file_path);
if(module != NULL){
DEBUG_FUNCTION_LINE("Found plugin %s by %s. Size: %d kb \n",module->getName().c_str(),module->getAuthor().c_str(),module->getSize()/1024) ;
result.push_back(module);
} else {
DEBUG_FUNCTION_LINE("%s is not a valid plugin\n",full_file_path.c_str()) ;
}
}
}
return result;
}
void ModuleLoader::loadAndLinkModules(std::vector<ModuleInformation *> moduleInformation){
std::vector<ModuleData *> loadedModules;
for(size_t i = 0;i < moduleInformation.size(); i++){
DEBUG_FUNCTION_LINE("loadAndLinkModules for %d\n",i) ;
ModuleInformation * cur_info = moduleInformation[i];
ModuleData * moduleData = loadAndLinkModule(cur_info);
if(moduleData == NULL){
DEBUG_FUNCTION_LINE("loadAndLinkModules failed for %d\n",i) ;
continue;
} else {
loadedModules.push_back(moduleData);
}
}
copyModuleDataIntoGlobalStruct(loadedModules);
clearModuleData(loadedModules);
}
void ModuleLoader::clearModuleData(std::vector<ModuleData *> moduleData){
for(size_t i = 0;i < moduleData.size(); i++){
ModuleData * curModuleData = moduleData[i];
if(curModuleData != NULL){
delete curModuleData;
}
}
}
ModuleData * ModuleLoader::loadAndLinkModule(ModuleInformation * moduleInformation){
DEBUG_FUNCTION_LINE("\n");
ModuleData * result = NULL;
int fd = -1;
Elf *elf = NULL;
if(moduleInformation == NULL){
DEBUG_FUNCTION_LINE("moduleInformation was NULL\n");
goto exit_error;
}
if(moduleInformation->getSize() > ((u32) this->getCurrentStoreAddress() - (u32) this->startAddress)){
DEBUG_FUNCTION_LINE("Not enough space left to loader the plugin into memory\n");
goto exit_error;
}
/* check for compile errors */
if (elf_version(EV_CURRENT) == EV_NONE){
goto exit_error;
}
fd = open(moduleInformation->getPath().c_str(), O_RDONLY, 0);
if (fd == -1){
DEBUG_FUNCTION_LINE("failed to open '%s' \n", moduleInformation->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;
}
DEBUG_FUNCTION_LINE("\n");
result = new ModuleData(moduleInformation);
if(result == NULL){
DEBUG_FUNCTION_LINE("Failed to create object\n");
goto exit_error;
}
if(!this->loadAndLinkElf(result, elf, this->getCurrentStoreAddress())){
delete result;
result = NULL;
}
exit_error:
if (elf != NULL){
elf_end(elf);
}
if (fd != -1){
close(fd);
}
return result;
}
bool ModuleLoader::loadAndLinkElf(ModuleData * moduleData, Elf *elf, void * endAddress) {
if(moduleData == NULL || elf == NULL || endAddress == NULL){
return false;
}
DEBUG_FUNCTION_LINE("\n");
u32 curAddress = (u32) endAddress;
Elf_Scn *scn;
size_t symtab_count, section_count, shstrndx, symtab_strndx, entries_count, hooks_count;
Elf32_Sym *symtab = NULL;
uint8_t **destinations = NULL;
wups_loader_entry_t *entries = NULL;
wups_loader_hook_t *hooks = NULL;
bool result = false;
std::vector<wups_loader_entry_t *> entry_t_list;
std::vector<wups_loader_hook_t *> hook_t_list;
std::vector<EntryData *> entry_data_list;
std::vector<HookData *> hook_data_list;
if (!ElfTools::loadElfSymtab(elf, &symtab, &symtab_count, &symtab_strndx)){
goto exit_error;
}
if(symtab == NULL){
goto exit_error;
}
if (elf_getshdrnum(elf, &section_count) != 0){
goto exit_error;
}
if (elf_getshdrstrndx(elf, &shstrndx) != 0){
goto exit_error;
}
destinations = (uint8_t **) malloc(sizeof(uint8_t *) * section_count);
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;
destinations[elf_ndxscn(scn)] = NULL;
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) {
if (entries != NULL){
goto exit_error;
}
entries_count = shdr->sh_size / sizeof(wups_loader_entry_t);
entries = (wups_loader_entry_t *) malloc(sizeof(wups_loader_entry_t) * entries_count);
if (entries == NULL){
goto exit_error;
}
destinations[elf_ndxscn(scn)] = (uint8_t *)entries;
if (!ElfTools::elfLoadSection(elf, scn, shdr, entries)){
goto exit_error;
}
ElfTools::elfLoadSymbols(elf_ndxscn(scn), entries, symtab, symtab_count);
for(size_t i = 0;i< entries_count;i++){
entry_t_list.push_back(&entries[i]);
}
}else if (strcmp(name, ".wups.hooks") == 0) {
if (hooks != NULL){
goto exit_error;
}
hooks_count = shdr->sh_size / sizeof(wups_loader_hook_t);
hooks = (wups_loader_hook_t *) malloc(sizeof(wups_loader_hook_t) * hooks_count);
if (hooks == NULL){
goto exit_error;
}
destinations[elf_ndxscn(scn)] = (uint8_t *)hooks;
if (!ElfTools::elfLoadSection(elf, scn, shdr, hooks)){
goto exit_error;
}
ElfTools::elfLoadSymbols(elf_ndxscn(scn), hooks, symtab, symtab_count);
for(size_t i = 0;i< hooks_count;i++){
hook_t_list.push_back(&hooks[i]);
}
} else {
curAddress -= shdr->sh_size;
if (shdr->sh_addralign > 3){
curAddress = (u32)((int)curAddress & ~(shdr->sh_addralign - 1));
} else {
curAddress = (u32)((int)curAddress & ~3);
}
destinations[elf_ndxscn(scn)] = (uint8_t *) curAddress;
if((u32) curAddress < (u32) this->startAddress){
DEBUG_FUNCTION_LINE("Not enough space to load function %s into memory at %08X.\n",name,curAddress);
goto exit_error;
}
DEBUG_FUNCTION_LINE("Copy section %s to %08X\n",name,curAddress);
if (!ElfTools::elfLoadSection(elf, scn, shdr, (void*) curAddress)){
goto exit_error;
}
ElfTools::elfLoadSymbols(elf_ndxscn(scn), (void*) curAddress, symtab, symtab_count);
}
}
}
if (entries == NULL){
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) &&
destinations[elf_ndxscn(scn)] != NULL) {
if (!ElfTools::elfLink(elf, elf_ndxscn(scn), destinations[elf_ndxscn(scn)], symtab, symtab_count, symtab_strndx, true)){
goto exit_error;
}
}
}
for(size_t j=0;j<hook_t_list.size();j++){
wups_loader_hook_t * hook = hook_t_list[j];
DEBUG_FUNCTION_LINE("Saving hook of module \"%s\". Type: %08X, target: %08X\n",moduleData->getModuleInformation()->getName().c_str(),hook->type,(void*) hook->target);
HookData * hook_data = new HookData((void *) hook->target,hook->type);
moduleData->addHookData(hook_data);
}
for(size_t j=0;j<entry_t_list.size();j++){
wups_loader_entry_t * entry = entry_t_list[j];
DEBUG_FUNCTION_LINE("Saving entry \"%s\" of module \"%s\". Library: %08X, target: %08X, call_addr: %08X\n",entry->_function.name,moduleData->getModuleInformation()->getName().c_str(),entry->_function.library,entry->_function.target, (void *) entry->_function.call_addr);
EntryData * entry_data = new EntryData(entry->_function.name,entry->_function.library, (void *) entry->_function.target, (void *) entry->_function.call_addr);
moduleData->addEntryData(entry_data);
}
this->setCurrentStoreAddress((void *) curAddress);
result = true;
exit_error:
if (!result) DEBUG_FUNCTION_LINE("exit_error\n");
if (destinations != NULL){
free(destinations);
}
if (symtab != NULL){
free(symtab);
}
if (hooks != NULL){
free(hooks);
}
if (entries != NULL){
free(entries);
}
return result;
}
void ModuleLoader::copyModuleDataIntoGlobalStruct(std::vector<ModuleData *> modules){
// Reset data
memset((void*)&gbl_replacement_data,0,sizeof(gbl_replacement_data));
int module_index = 0;
// Copy data to global struct.
for(size_t i = 0; i< modules.size();i++){
ModuleData * cur_module = modules.at(i);
ModuleInformation * cur_moduleInformation = cur_module->getModuleInformation();
std::vector<EntryData *> entry_data_list = cur_module->getEntryDataList();
std::vector<HookData *> hook_data_list = cur_module->getHookDataList();
if(module_index >= MAXIMUM_MODULES ){
DEBUG_FUNCTION_LINE("Maximum of %d modules reached. %s won't be loaded!\n",MAXIMUM_MODULES,cur_moduleInformation->getName().c_str());
continue;
}
if(entry_data_list.size() > MAXIMUM_FUNCTION_PER_MODULE){
DEBUG_FUNCTION_LINE("Module %s would replace to many function (%d, maximum is %d). It won't be loaded.\n",cur_moduleInformation->getName().c_str(),entry_data_list.size(),MAXIMUM_FUNCTION_PER_MODULE);
continue;
}
if(hook_data_list.size() > MAXIMUM_HOOKS_PER_MODULE){
DEBUG_FUNCTION_LINE("Module %s would set too many hooks (%d, maximum is %d). It won't be loaded.\n",cur_moduleInformation->getName().c_str(),hook_data_list.size(),MAXIMUM_HOOKS_PER_MODULE);
continue;
}
replacement_data_module_t * module_data = &gbl_replacement_data.module_data[module_index];
strncpy(module_data->module_name,cur_moduleInformation->getName().c_str(),MAXIMUM_MODULE_NAME_LENGTH-1);
for(size_t j = 0; j < entry_data_list.size();j++){
replacement_data_function_t * function_data = &module_data->functions[j];
EntryData * cur_entry = entry_data_list[j];
DEBUG_FUNCTION_LINE("Adding entry \"%s\" for module \"%s\"\n",cur_entry->getName().c_str(),module_data->module_name);
//TODO: Warning/Error if string is too long.
strncpy(function_data->function_name,cur_entry->getName().c_str(),MAXIMUM_FUNCTION_NAME_LENGTH-1);
function_data->library = cur_entry->getLibrary();
function_data->replaceAddr = (u32) cur_entry->getReplaceAddress();
function_data->replaceCall = (u32) cur_entry->getReplaceCall();
module_data->number_used_functions++;
}
DEBUG_FUNCTION_LINE("Entries for module \"%s\": %d\n",module_data->module_name,module_data->number_used_functions);
for(size_t j = 0; j < hook_data_list.size();j++){
replacement_data_hook_t * hook_data = &module_data->hooks[j];
HookData * hook_entry = hook_data_list[j];
DEBUG_FUNCTION_LINE("Set hook for module \"%s\" of type %08X to target %08X\n",module_data->module_name,hook_entry->getType(),(void*) hook_entry->getFunctionPointer());
hook_data->func_pointer = (void*) hook_entry->getFunctionPointer();
hook_data->type = hook_entry->getType();
module_data->number_used_hooks++;
}
DEBUG_FUNCTION_LINE("Hooks for module \"%s\": %d\n",module_data->module_name,module_data->number_used_hooks);
module_index++;
gbl_replacement_data.number_used_modules++;
}
}

View File

@ -0,0 +1,151 @@
/* 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.
*/
#ifndef _MODULE_LOADER_H_
#define _MODULE_LOADER_H_
#include <vector>
#include "ModuleInformation.h"
#include "ModuleData.h"
#ifdef __cplusplus
extern "C" {
#endif
#include <utils/utils.h>
#ifdef __cplusplus
}
#endif
#define PLUGIN_LOCATION_END_ADDRESS 0x01000000
class ModuleLoader{
public:
static ModuleLoader *getInstance() {
if(!instance){
instance = new ModuleLoader((void*)getApplicationEndAddr(),(void *)PLUGIN_LOCATION_END_ADDRESS);
}
return instance;
}
static void destroyInstance() {
if(instance){
delete instance;
instance = NULL;
}
}
/**
\brief Parses the meta data of all plugins in the given directory.
\param path the path of the directory which should be scanned.
\return a list of ModuleInformation objects, one for each valid plugin.
**/
std::vector<ModuleInformation *> getModuleInformation(const char * path);
/**
\brief Gets plugin information from the global struct.
\return a list of MetaInformation objects for all plugins currently loaded and linked (relocated). Will only contain
plugin which are still on the sd card.
**/
//std::vector<ModuleInformation *> getModulesLoadedInMemory();
/**
\brief Takes a list of modules that should be linked (relocated) loaded into the memory.
The function that should be replaced will be replaced in the order of the given plugin list.
So two plugin will override the same function, the plugin first in this list will override the function first.
Also the hooks of the plugins will be called in the order their plugin where passed to this method.
\param A list of plugin that should be linked (relocated) an loaded into memory
**/
void loadAndLinkModules(std::vector<ModuleInformation *> moduleInformation);
private:
ModuleLoader(void * startAddress, void * endAddress){
// TODO: Check if endAddress > startAddress.
this->startAddress = startAddress;
this->endAddress = endAddress;
this->currentStoreAddress = endAddress;
}
~ModuleLoader(){
}
static ModuleLoader *instance;
/**
\brief Iterates through the vector and delete all it's elements
\param A list of ModuleData* that should be deleted.
**/
void clearModuleData(std::vector<ModuleData *> moduleData);
/**
\brief Load
\param moduleInformation a ModuleInformation object of the plugin that should be linked (relocated) and loaded.
\return NULL on error. On success it will return a ModuleData object.
**/
ModuleData * loadAndLinkModule(ModuleInformation * moduleInformation);
/**
\brief Loads a plugin into memory (in the startAddress/endAddress range defined in this loader) and relocates it.
Modifies the moduleData param. Adds loaded functions and hooks.
\param moduleData object where the result should be stored
\param elf source elf from where the sections will be loaded
\param storeAddressEnd the address where the plugin data will be stored in memory. Saving BACKWARD.
**/
bool loadAndLinkElf(ModuleData * moduleData, Elf *elf, void * storeAddressEnd);
/**
\brief Copies the needed information into a global, persistent struct. This struct holds information on which
function should be override in which order and which hook should be called.
\param modules list of modules that should be used.
**/
void copyModuleDataIntoGlobalStruct(std::vector<ModuleData *> modules);
void * getCurrentStoreAddress(){
return this->currentStoreAddress;
}
void setCurrentStoreAddress(void * addr){
this->currentStoreAddress = addr;
}
void * startAddress = NULL;
void * endAddress = NULL;
void * currentStoreAddress = NULL;
};
#endif