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