/* Count how many different relocations (different symbol, different
addend) */ staticunsignedint count_relocs(const Elf32_Rela *rela, unsignedint num)
{ unsignedint i, r_info, r_addend, _count_relocs;
_count_relocs = 0;
r_info = 0;
r_addend = 0; for (i = 0; i < num; i++) /* Only count 24-bit relocs, others don't need stubs */ if (ELF32_R_TYPE(rela[i].r_info) == R_PPC_REL24 &&
(r_info != ELF32_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) {
_count_relocs++;
r_info = ELF32_R_SYM(rela[i].r_info);
r_addend = rela[i].r_addend;
}
#ifdef CONFIG_DYNAMIC_FTRACE
_count_relocs++; /* add one for ftrace_caller */ #endif return _count_relocs;
}
/* Compare the entire r_info (as opposed to ELF32_R_SYM(r_info) only) to * make the comparison cheaper/faster. It won't affect the sorting or * the counting algorithms' performance
*/ if (x->r_info < y->r_info) return -1; elseif (x->r_info > y->r_info) return 1; elseif (x->r_addend < y->r_addend) return -1; elseif (x->r_addend > y->r_addend) return 1; else return 0;
}
/* Get the potential trampolines size required of the init and
non-init sections */ staticunsignedlong get_plt_size(const Elf32_Ehdr *hdr, const Elf32_Shdr *sechdrs, constchar *secstrings, int is_init)
{ unsignedlong ret = 0; unsigned i;
/* Everything marked ALLOC (this includes the exported
symbols) */ for (i = 1; i < hdr->e_shnum; i++) { /* If it's called *.init*, and we're not init, we're
not interested */ if ((strstr(secstrings + sechdrs[i].sh_name, ".init") != NULL)
!= is_init) continue;
/* We don't want to look at debug sections. */ if (strstr(secstrings + sechdrs[i].sh_name, ".debug")) continue;
/* Sort the relocation information based on a symbol and * addend key. This is a stable O(n*log n) complexity * algorithm but it will reduce the complexity of * count_relocs() to linear complexity O(n)
*/
sort((void *)hdr + sechdrs[i].sh_offset,
sechdrs[i].sh_size / sizeof(Elf32_Rela), sizeof(Elf32_Rela), relacmp, NULL);
/* Set up a trampoline in the PLT to bounce us to the distant function */ static uint32_t do_plt_call(void *location,
Elf32_Addr val, const Elf32_Shdr *sechdrs, struct module *mod)
{ struct ppc_plt_entry *entry;
pr_debug("Doing plt for call to 0x%x at 0x%x\n", val, (unsignedint)location); /* Init, or core PLT? */ if (within_module_core((unsignedlong)location, mod))
entry = (void *)sechdrs[mod->arch.core_plt_section].sh_addr; else
entry = (void *)sechdrs[mod->arch.init_plt_section].sh_addr;
/* Find this entry, or if that fails, the next avail. entry */ while (entry->jump[0]) { if (entry_matches(entry, val)) return (uint32_t)entry;
entry++;
}
if (patch_instruction(&entry->jump[0], ppc_inst(PPC_RAW_LIS(_R12, PPC_HA(val))))) return 0; if (patch_instruction(&entry->jump[1], ppc_inst(PPC_RAW_ADDI(_R12, _R12, PPC_LO(val))))) return 0; if (patch_instruction(&entry->jump[2], ppc_inst(PPC_RAW_MTCTR(_R12)))) return 0; if (patch_instruction(&entry->jump[3], ppc_inst(PPC_RAW_BCTR()))) return 0;
pr_debug("Initialized plt for 0x%x at %p\n", val, entry); return (uint32_t)entry;
}
pr_debug("Applying ADD relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info); for (i = 0; i < sechdrs[relsec].sh_size / sizeof(*rela); i++) { /* This is where to make the change */
location = (void *)sechdrs[sechdrs[relsec].sh_info].sh_addr
+ rela[i].r_offset; /* This is the symbol it is referring to. Note that all
undefined symbols have been resolved. */
sym = (Elf32_Sym *)sechdrs[symindex].sh_addr
+ ELF32_R_SYM(rela[i].r_info); /* `Everything is relative'. */
value = sym->st_value + rela[i].r_addend;
switch (ELF32_R_TYPE(rela[i].r_info)) { case R_PPC_ADDR32: /* Simply set it */
*(uint32_t *)location = value; break;
case R_PPC_ADDR16_LO: /* Low half of the symbol */ if (patch_location_16(location, PPC_LO(value))) return -EFAULT; break;
case R_PPC_ADDR16_HI: /* Higher half of the symbol */ if (patch_location_16(location, PPC_HI(value))) return -EFAULT; break;
case R_PPC_ADDR16_HA: if (patch_location_16(location, PPC_HA(value))) return -EFAULT; break;
case R_PPC_REL24: if ((int)(value - (uint32_t)location) < -0x02000000
|| (int)(value - (uint32_t)location) >= 0x02000000) {
value = do_plt_call(location, value,
sechdrs, module); if (!value) return -EFAULT;
}
/* Only replace bits 2 through 26 */
pr_debug("REL24 value = %08X. location = %08X\n",
value, (uint32_t)location);
pr_debug("Location before: %08X.\n",
*(uint32_t *)location);
value = (*(uint32_t *)location & ~PPC_LI_MASK) |
PPC_LI(value - (uint32_t)location);
if (patch_instruction(location, ppc_inst(value))) return -EFAULT;
/* Find where the trampoline jumps to */ if (copy_inst_from_kernel_nofault(jmp, (void *)addr)) return -EFAULT; if (__copy_inst_from_kernel_nofault(jmp + 1, (void *)addr + 4)) return -EFAULT; if (__copy_inst_from_kernel_nofault(jmp + 2, (void *)addr + 8)) return -EFAULT; if (__copy_inst_from_kernel_nofault(jmp + 3, (void *)addr + 12)) return -EFAULT;
/* verify that this is what we expect it to be */ if ((ppc_inst_val(jmp[0]) & 0xffff0000) != PPC_RAW_LIS(_R12, 0)) return -EINVAL; if ((ppc_inst_val(jmp[1]) & 0xffff0000) != PPC_RAW_ADDI(_R12, _R12, 0)) return -EINVAL; if (ppc_inst_val(jmp[2]) != PPC_RAW_MTCTR(_R12)) return -EINVAL; if (ppc_inst_val(jmp[3]) != PPC_RAW_BCTR()) return -EINVAL;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.