diff --git a/loader/src/main.cpp b/loader/src/main.cpp index c30dbfa..eada4a8 100644 --- a/loader/src/main.cpp +++ b/loader/src/main.cpp @@ -30,6 +30,7 @@ #include "common/retain_vars.h" #include "common/common.h" +#include "modules/ModuleLoader.h" #include "modules/ModuleData.h" #include @@ -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* modules); -static void loadElf(std::vector* 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 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 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* 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 entry_data_list = cur_module->getEntryDataList(); - std::vector 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* 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){ diff --git a/loader/src/modules/ElfTools.cpp b/loader/src/modules/ElfTools.cpp index ade7efe..f57816b 100644 --- a/loader/src/modules/ElfTools.cpp +++ b/loader/src/modules/ElfTools.cpp @@ -26,13 +26,11 @@ #include "ElfTools.h" #include #include -#include #include #include 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; } diff --git a/loader/src/modules/ModuleData.h b/loader/src/modules/ModuleData.h index 16359c7..6727f04 100644 --- a/loader/src/modules/ModuleData.h +++ b/loader/src/modules/ModuleData.h @@ -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 . + ****************************************************************************/ #ifndef _MODULE_DATA_H_ #define _MODULE_DATA_H_ @@ -30,6 +22,7 @@ #include #include "EntryData.h" #include "HookData.h" +#include "ModuleInformation.h" #include #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 entry_data_list; std::vector hook_data_list; diff --git a/loader/src/modules/ModuleData.cpp b/loader/src/modules/ModuleInformation.cpp similarity index 63% rename from loader/src/modules/ModuleData.cpp rename to loader/src/modules/ModuleInformation.cpp index edb30df..4808f62 100644 --- a/loader/src/modules/ModuleData.cpp +++ b/loader/src/modules/ModuleInformation.cpp @@ -23,12 +23,11 @@ * SOFTWARE. */ -#include "ModuleData.h" +#include "ModuleInformation.h" #include #include #include #include -#include #include #include #include @@ -37,20 +36,23 @@ #include #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 entry_t_list; - std::vector hook_t_list; - - std::vector entry_data_list; - std::vector hook_data_list; - - if (!ElfTools::loadElfSymtab(elf, &symtab, &symtab_count, &symtab_strndx)){ - goto exit_error; - } - - assert(symtab != NULL); - - if (elf_getshdrnum(elf, §ion_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;jtype,(void*) hook->target); - HookData * hook_data = new HookData((void *) hook->target,hook->type); - addHookData(hook_data); - } - - for(size_t j=0;j_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; -} diff --git a/loader/src/modules/ModuleInformation.h b/loader/src/modules/ModuleInformation.h new file mode 100644 index 0000000..e80172e --- /dev/null +++ b/loader/src/modules/ModuleInformation.h @@ -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 +#include +#include "EntryData.h" +#include "HookData.h" +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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 diff --git a/loader/src/modules/ModuleLoader.cpp b/loader/src/modules/ModuleLoader.cpp new file mode 100644 index 0000000..90b2ae6 --- /dev/null +++ b/loader/src/modules/ModuleLoader.cpp @@ -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 +#include +#include +#include +#include +#include "ElfTools.h" +#include "ModuleData.h" +#include "ModuleLoader.h" +#include "utils/StringTools.h" +#include "common/retain_vars.h" + +ModuleLoader * ModuleLoader::instance = NULL; + +std::vector ModuleLoader::getModuleInformation(const char * path){ + std::vector 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){ + std::vector 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){ + 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 entry_t_list; + std::vector hook_t_list; + + std::vector entry_data_list; + std::vector 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, §ion_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;jgetModuleInformation()->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_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 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 entry_data_list = cur_module->getEntryDataList(); + std::vector 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++; + } +} diff --git a/loader/src/modules/ModuleLoader.h b/loader/src/modules/ModuleLoader.h new file mode 100644 index 0000000..4bd9706 --- /dev/null +++ b/loader/src/modules/ModuleLoader.h @@ -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 +#include "ModuleInformation.h" +#include "ModuleData.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#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 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 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); +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); + + /** + \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 modules); + + void * getCurrentStoreAddress(){ + return this->currentStoreAddress; + } + + void setCurrentStoreAddress(void * addr){ + this->currentStoreAddress = addr; + } + + void * startAddress = NULL; + void * endAddress = NULL; + void * currentStoreAddress = NULL; +}; + + +#endif