/* 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 #include #include #include #include #include #include #include #include #include #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; }