staticunsignedint local_entry_offset(const Elf64_Sym *sym)
{ /* sym->st_other indicates offset to local entry point *(otherwiseitwillassumer12istheaddressofthestart
* of function and try to derive r2 from it). */ return PPC64_LOCAL_ENTRY_OFFSET(sym->st_other);
} #else
/* Like PPC32, we need little trampolines to do > 24-bit jumps (into thekernelitself).ButonPPC64,theseneedtobeusedforevery
jump, actually, to reset r2 (TOC+0x8000). */ struct ppc64_stub_entry { /* *28bytejumpinstructionsequence(7instructions)thatcan *holdppc64_stub_insnsorstub_insns.Mustbe8-bytealigned *withPCRELkernelsthatuseprefixinstructionsinthestub.
*/
u32 jump[7]; /* Used by ftrace to identify stubs */
u32 magic; /* Data for the above code */
func_desc_t funcdata;
} __aligned(8);
struct ppc64_got_entry {
u64 addr;
};
/* *PPC64uses24bitjumps,butweneedtojumpintoothermodulesor *thekernelwhichmaybefurther.Sowejumptoastub. * *TargetaddressandTOCareloadedfromfunctiondescriptorinthe *ppc64_stub_entry. * *r12isusedtogeneratethetargetaddress,whichisrequiredforthe *ELFv2globalentrypointcallingconvention. * *TOChandling: *-PCRELdoesnothaveaTOC. *-ELFv2non-PCRELjusthastosaver2,thecalleeisresponsiblefor *settingitsownTOCpointerattheglobalentryaddress. *-ELFv1mustloadthenewTOCpointerfromthefunctiondescriptor.
*/ static u32 ppc64_stub_insns[] = { #ifdef CONFIG_PPC_KERNEL_PCREL /* pld r12,addr */
PPC_PREFIX_8LS | __PPC_PRFX_R(1),
PPC_INST_PLD | ___PPC_RT(_R12), #else
PPC_RAW_ADDIS(_R11, _R2, 0),
PPC_RAW_ADDI(_R11, _R11, 0), /* Save current r2 value in magic place on the stack. */
PPC_RAW_STD(_R2, _R1, R2_STACK_OFFSET),
PPC_RAW_LD(_R12, _R11, 32), #ifdef CONFIG_PPC64_ELF_ABI_V1 /* Set up new r2 from function descriptor */
PPC_RAW_LD(_R2, _R11, 40), #endif #endif
PPC_RAW_MTCTR(_R12),
PPC_RAW_BCTR(),
};
/* Compare the entire r_info (as opposed to ELF64_R_SYM(r_info) only) to *makethecomparisoncheaper/faster.Itwon'taffectthesortingor *thecountingalgorithms'performance
*/ if (x->r_info < y->r_info) return -1; elseif (x->r_info > y->r_info) return1; elseif (x->r_addend < y->r_addend) return -1; elseif (x->r_addend > y->r_addend) return1; else return0;
}
/* Get size of potential trampolines required. */ staticunsignedlong get_stubs_size(const Elf64_Ehdr *hdr, const Elf64_Shdr *sechdrs, char *secstrings, struct module *me)
{ /* One extra reloc so it's always 0-addr terminated */ unsignedlong relocs = 1; unsigned i;
/* Every relocated section... */ for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type == SHT_RELA) {
pr_debug("Found relocations in section %u\n", i);
pr_debug("Ptr: %p. Number: %Lu\n",
(void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela));
/* Sort the relocation information based on a symbol and *addendkey.ThisisastableO(n*logn)complexity *algorithmbutitwillreducethecomplexityof *count_relocs()tolinearcomplexityO(n)
*/
sort((void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela), sizeof(Elf64_Rela), relacmp, NULL);
pr_debug("Looks like a total of %lu GOT entries, max\n", relocs); return relocs * sizeof(struct ppc64_got_entry);
} #else/* CONFIG_PPC_KERNEL_PCREL */
/* Still needed for ELFv2, for .TOC. */ staticvoid dedotify_versions(struct modversion_info *vers, unsignedlong size)
{ struct modversion_info *end;
for (end = (void *)vers + size; vers < end; vers++) if (vers->name[0] == '.') {
memmove(vers->name, vers->name+1, strlen(vers->name));
}
}
/* Same as normal versions, remove a leading dot if present. */ staticvoid dedotify_ext_version_names(char *str_seq, unsignedlong size)
{ unsignedlong out = 0; unsignedlong in; char last = '\0';
for (in = 0; in < size; in++) { /* Skip one leading dot */ if (last == '\0' && str_seq[in] == '.')
in++;
last = str_seq[in];
str_seq[out++] = last;
} /* Zero the trailing portion of the names table for robustness */
memset(&str_seq[out], 0, size - out);
}
/* Override the got size */
sechdrs[me->arch.got_section].sh_size = get_got_size(hdr, sechdrs, me); #else /* If we don't have a .toc, just use .stubs. We need to set r2 tosomereasonablevalueincasethemodulecallsoutto otherfunctionsviaastub,orifafunctionpointerescapes
the module by some means. */ if (!me->arch.toc_section)
me->arch.toc_section = me->arch.stubs_section; #endif
if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) { /* Stub uses address relative to kernel base (from the paca) */
reladdr = addr - local_paca->kernelbase; if (reladdr > 0x1FFFFFFFFL || reladdr < -0x200000000L) {
pr_err("%s: Address of %ps out of range of 34-bit relative address.\n",
me->name, (void *)addr); return0;
}
entry->jump[2] |= IMM_H18(reladdr);
entry->jump[3] |= IMM_L(reladdr);
} else { /* Stub uses address relative to kernel toc (from the paca) */
reladdr = addr - kernel_toc_addr(); if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
pr_err("%s: Address of %ps out of range of kernel_toc.\n",
me->name, (void *)addr); return0;
}
/* Patch stub to reference function and correct r2 value. */ staticinlineint create_stub(const Elf64_Shdr *sechdrs, struct ppc64_stub_entry *entry, unsignedlong addr, struct module *me, constchar *name)
{ long reladdr;
func_desc_t desc; int i;
if (is_mprofile_ftrace_call(name)) return create_ftrace_stub(entry, addr, me);
if ((unsignedlong)entry->jump % 8 != 0) {
pr_err("%s: Address of stub entry is not 8-byte aligned\n", me->name); return0;
}
BUILD_BUG_ON(sizeof(ppc64_stub_insns) > sizeof(entry->jump)); for (i = 0; i < ARRAY_SIZE(ppc64_stub_insns); i++) { if (patch_instruction(&entry->jump[i],
ppc_inst(ppc64_stub_insns[i]))) return0;
}
if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) { /* Stub uses address relative to itself! */
reladdr = 0 + offsetof(struct ppc64_stub_entry, funcdata);
BUILD_BUG_ON(reladdr != 32); if (reladdr > 0x1FFFFFFFFL || reladdr < -0x200000000L) {
pr_err("%s: Address of %p out of range of 34-bit relative address.\n",
me->name, (void *)reladdr); return0;
}
pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr);
/* May not even need this if we're relative to 0 */ if (patch_instruction(&entry->jump[0],
ppc_inst_prefix(entry->jump[0] | IMM_H18(reladdr),
entry->jump[1] | IMM_L(reladdr)))) return0;
} else { /* Stub uses address relative to r2. */
reladdr = (unsignedlong)entry - my_r2(sechdrs, me); if (reladdr > 0x7FFFFFFF || reladdr < -(0x80000000L)) {
pr_err("%s: Address %p of stub out of range of %p.\n",
me->name, (void *)reladdr, (void *)my_r2); return0;
}
pr_debug("Stub %p get data from reladdr %li\n", entry, reladdr);
if (patch_instruction(&entry->jump[0],
ppc_inst(entry->jump[0] | PPC_HA(reladdr)))) return0;
if (patch_instruction(&entry->jump[1],
ppc_inst(entry->jump[1] | PPC_LO(reladdr)))) return0;
}
// func_desc_t is 8 bytes if ABIv2, else 16 bytes
desc = func_desc(addr); for (i = 0; i < sizeof(func_desc_t) / sizeof(u32); i++) { if (patch_u32(((u32 *)&entry->funcdata) + i, ((u32 *)&desc)[i])) return0;
}
if (patch_u32(&entry->magic, STUB_MAGIC)) return0;
return1;
}
/* Create stub to jump to function described in this OPD/ptr: we need the
stub to set up the TOC ptr (r2) for the function. */ staticunsignedlong stub_for_addr(const Elf64_Shdr *sechdrs, unsignedlong addr, struct module *me, constchar *name)
{ struct ppc64_stub_entry *stubs; unsignedint i, num_stubs;
/* Find this stub, or if that fails, the next avail. entry */
stubs = (void *)sechdrs[me->arch.stubs_section].sh_addr; for (i = 0; stub_func_addr(stubs[i].funcdata); i++) { if (WARN_ON(i >= num_stubs)) return0;
if (stub_func_addr(stubs[i].funcdata) == func_addr(addr)) return (unsignedlong)&stubs[i];
}
if (!create_stub(sechdrs, &stubs[i], addr, me, name)) return0;
return (unsignedlong)&stubs[i];
}
#ifdef CONFIG_PPC_KERNEL_PCREL /* Create GOT to load the location described in this ptr */ staticunsignedlong got_for_addr(const Elf64_Shdr *sechdrs, unsignedlong addr, struct module *me, constchar *name)
{ struct ppc64_got_entry *got; unsignedint i, num_got;
if (!IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) return addr;
/* Find this stub, or if that fails, the next avail. entry */
got = (void *)sechdrs[me->arch.got_section].sh_addr; for (i = 0; got[i].addr; i++) { if (WARN_ON(i >= num_got)) return0;
if (got[i].addr == addr) return (unsignedlong)&got[i];
}
got[i].addr = addr;
return (unsignedlong)&got[i];
} #endif
/* We expect a noop next: if it is, replace it with instruction to
restore r2. */ staticint restore_r2(constchar *name, u32 *instruction, struct module *me)
{
u32 *prev_insn = instruction - 1;
u32 insn_val = *instruction;
if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) return0;
if (is_mprofile_ftrace_call(name)) return0;
/* *Makesurethebranchisn'tasiblingcall.Siblingcallsaren't *"link"branchesandtheydon'treturn,sotheydon'tneedther2 *restoreafterwards.
*/ if (!instr_is_relative_link_branch(ppc_inst(*prev_insn))) return0;
pr_debug("Applying ADD relocate section %u to %u\n", relsec,
sechdrs[relsec].sh_info);
#ifndef CONFIG_PPC_KERNEL_PCREL /* First time we're called, we can fix up .TOC. */ if (!me->arch.toc_fixed) {
sym = find_dot_toc(sechdrs, strtab, symindex); /* It's theoretically possible that a module doesn't want a
* .TOC. so don't fail it just for that. */ if (sym)
sym->st_value = my_r2(sechdrs, me);
me->arch.toc_fixed = true;
} #endif 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 */
sym = (Elf64_Sym *)sechdrs[symindex].sh_addr
+ ELF64_R_SYM(rela[i].r_info);
pr_debug("RELOC at %p: %li-type as %s (0x%lx) + %li\n",
location, (long)ELF64_R_TYPE(rela[i].r_info),
strtab + sym->st_name, (unsignedlong)sym->st_value,
(long)rela[i].r_addend);
/* `Everything is relative'. */
value = sym->st_value + rela[i].r_addend;
switch (ELF64_R_TYPE(rela[i].r_info)) { case R_PPC64_ADDR32: /* Simply set it */
*(u32 *)location = value; break;
case R_PPC64_ADDR64: /* Simply set it */
*(unsignedlong *)location = value; break;
#ifndef CONFIG_PPC_KERNEL_PCREL case R_PPC64_TOC:
*(unsignedlong *)location = my_r2(sechdrs, me); break;
/* Find the next available entry */
stub = (void *)sechdrs[me->arch.stubs_section].sh_addr; for (i = 0; stub_func_addr(stub[i].funcdata); i++) if (WARN_ON(i >= total_stubs)) return -1;
if (WARN_ON(i + num_stubs > total_stubs)) return -1;
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.