staticunsignedint local_entry_offset(const Elf64_Sym *sym)
{ /* sym->st_other indicates offset to local entry point * (otherwise it will assume r12 is the address of the start
* 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 the kernel itself). But on PPC64, these need to be used for every
jump, actually, to reset r2 (TOC+0x8000). */ struct ppc64_stub_entry { /* * 28 byte jump instruction sequence (7 instructions) that can * hold ppc64_stub_insns or stub_insns. Must be 8-byte aligned * with PCREL kernels that use prefix instructions in the stub.
*/
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;
};
/* * PPC64 uses 24 bit jumps, but we need to jump into other modules or * the kernel which may be further. So we jump to a stub. * * Target address and TOC are loaded from function descriptor in the * ppc64_stub_entry. * * r12 is used to generate the target address, which is required for the * ELFv2 global entry point calling convention. * * TOC handling: * - PCREL does not have a TOC. * - ELFv2 non-PCREL just has to save r2, the callee is responsible for * setting its own TOC pointer at the global entry address. * - ELFv1 must load the new TOC pointer from the function descriptor.
*/ 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(),
};
/* * Count how many different r_type relocations (different symbol, * different addend).
*/ staticunsignedint count_relocs(const Elf64_Rela *rela, unsignedint num, unsignedlong r_type)
{ unsignedint i, r_info, r_addend, _count_relocs;
/* FIXME: Only count external ones --RR */
_count_relocs = 0;
r_info = 0;
r_addend = 0; for (i = 0; i < num; i++) /* Only count r_type relocs, others don't need stubs */ if (ELF64_R_TYPE(rela[i].r_info) == r_type &&
(r_info != ELF64_R_SYM(rela[i].r_info) ||
r_addend != rela[i].r_addend)) {
_count_relocs++;
r_info = ELF64_R_SYM(rela[i].r_info);
r_addend = rela[i].r_addend;
}
/* Compare the entire r_info (as opposed to ELF64_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 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 * 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 *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela), sizeof(Elf64_Rela), relacmp, NULL);
/* Get size of potential GOT required. */ staticunsignedlong get_got_size(const Elf64_Ehdr *hdr, const Elf64_Shdr *sechdrs, struct module *me)
{ /* One extra reloc so it's always 0-addr terminated */ unsignedlong relocs = 1; unsignedint i, symindex = 0;
for (i = 1; i < hdr->e_shnum; i++) { if (sechdrs[i].sh_type == SHT_SYMTAB) {
symindex = i; break;
}
}
WARN_ON_ONCE(!symindex);
/* 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: %llu\n", (void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela));
/* * 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 *)sechdrs[i].sh_addr,
sechdrs[i].sh_size / sizeof(Elf64_Rela), sizeof(Elf64_Rela), relacmp, NULL);
/* * Percpu data access typically gets linked with * REL34 relocations, but the percpu section gets * moved at load time and requires that to be * converted to GOT linkage.
*/ if (IS_ENABLED(CONFIG_SMP) && symindex)
relocs += count_pcpu_relocs(sechdrs,
(void *)sechdrs[i].sh_addr,
sechdrs[i].sh_size
/ sizeof(Elf64_Rela),
symindex, me->arch.pcpu_section);
}
}
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);
}
/* * Undefined symbols which refer to .funcname, hack to funcname. Make .TOC. * seem to be defined (value set later).
*/ staticvoid dedotify(Elf64_Sym *syms, unsignedint numsyms, char *strtab)
{ unsignedint i;
for (i = 1; i < numsyms; i++) { if (syms[i].st_shndx == SHN_UNDEF) { char *name = strtab + syms[i].st_name; if (name[0] == '.') { if (strcmp(name+1, "TOC.") == 0)
syms[i].st_shndx = SHN_ABS;
syms[i].st_name++;
}
}
}
}
/* 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 to some reasonable value in case the module calls out to other functions via a stub, or if a function pointer escapes
the module by some means. */ if (!me->arch.toc_section)
me->arch.toc_section = me->arch.stubs_section; #endif
/* * For mprofile-kernel we use a special stub for ftrace_caller() because we * can't rely on r2 containing this module's TOC when we enter the stub. * * That can happen if the function calling us didn't need to use the toc. In * that case it won't have setup r2, and the r2 value will be either the * kernel's toc, or possibly another modules toc. * * To deal with that this stub uses the kernel toc, which is always accessible * via the paca (in r13). The target (ftrace_caller()) is responsible for * saving and restoring the toc before returning.
*/ staticinlineint create_ftrace_stub(struct ppc64_stub_entry *entry, unsignedlong addr, struct module *me)
{ long reladdr;
if ((unsignedlong)entry->jump % 8 != 0) {
pr_err("%s: Address of stub entry is not 8-byte aligned\n", me->name); return 0;
}
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); return 0;
}
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); return 0;
}
/* * r2 is the TOC pointer: it actually points 0x8000 into the TOC (this gives the * value maximum span in an instruction which uses a signed offset). Round down * to a 256 byte boundary for the odd case where we are setting up r2 without a * .toc section.
*/ staticinlineunsignedlong my_r2(const Elf64_Shdr *sechdrs, struct module *me)
{ #ifndef CONFIG_PPC_KERNEL_PCREL return (sechdrs[me->arch.toc_section].sh_addr & ~0xfful) + 0x8000; #else return -1; #endif
}
/* 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); return 0;
}
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]))) return 0;
}
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); return 0;
}
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)))) return 0;
} 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); return 0;
}
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)))) return 0;
if (patch_instruction(&entry->jump[1],
ppc_inst(entry->jump[1] | PPC_LO(reladdr)))) return 0;
}
// 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])) return 0;
}
if (patch_u32(&entry->magic, STUB_MAGIC)) return 0;
return 1;
}
/* 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)) return 0;
if (stub_func_addr(stubs[i].funcdata) == func_addr(addr)) return (unsignedlong)&stubs[i];
}
if (!create_stub(sechdrs, &stubs[i], addr, me, name)) return 0;
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)) return 0;
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)) return 0;
if (is_mprofile_ftrace_call(name)) return 0;
/* * Make sure the branch isn't a sibling call. Sibling calls aren't * "link" branches and they don't return, so they don't need the r2 * restore afterwards.
*/ if (!instr_is_relative_link_branch(ppc_inst(*prev_insn))) return 0;
/* * For livepatch, the restore r2 instruction might have already been * written previously, if the referenced symbol is in a previously * unloaded module which is now being loaded again. In that case, skip * the warning and the instruction write.
*/ if (insn_val == PPC_INST_LD_TOC) return 0;
if (insn_val != PPC_RAW_NOP()) {
pr_err("%s: Expected nop after call, got %08x at %pS\n",
me->name, insn_val, instruction); return -ENOEXEC;
}
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;
case R_PPC_REL24: #ifdef CONFIG_PPC_KERNEL_PCREL /* PCREL still generates REL24 for mcount */ case R_PPC64_REL24_NOTOC: #endif /* FIXME: Handle weak symbols here --RR */ if (sym->st_shndx == SHN_UNDEF ||
sym->st_shndx == SHN_LIVEPATCH) { /* External: go via stub */
value = stub_for_addr(sechdrs, value, me,
strtab + sym->st_name); if (!value) return -ENOENT; if (restore_r2(strtab + sym->st_name,
(u32 *)location + 1, me)) return -ENOEXEC;
} else
value += local_entry_offset(sym);
/* Convert value to relative */
value -= (unsignedlong)location; if (value + 0x2000000 > 0x3ffffff || (value & 3) != 0){
pr_err("%s: REL24 %li out of range!\n",
me->name, (longint)value); return -ENOEXEC;
}
/* Only replace bits 2 through 26 */
value = (*(uint32_t *)location & ~PPC_LI_MASK) | PPC_LI(value);
if (patch_instruction((u32 *)location, ppc_inst(value))) return -EFAULT;
break;
case R_PPC64_REL64: /* 64 bits relative (used by features fixups) */
*location = value - (unsignedlong)location; break;
case R_PPC64_REL32: /* 32 bits relative (used by relative exception tables) */ /* Convert value to relative */
value -= (unsignedlong)location; if (value + 0x80000000 > 0xffffffff) {
pr_err("%s: REL32 %li out of range!\n",
me->name, (longint)value); return -ENOEXEC;
}
*(u32 *)location = value; break;
#ifdef CONFIG_PPC_KERNEL_PCREL case R_PPC64_PCREL34: { unsignedlong absvalue = value;
/* Convert value to relative */
value -= (unsignedlong)location;
if (value + 0x200000000 > 0x3ffffffff) { if (sym->st_shndx != me->arch.pcpu_section) {
pr_err("%s: REL34 %li out of range!\n",
me->name, (long)value); return -ENOEXEC;
}
/* * per-cpu section is special cased because * it is moved during loading, so has to be * converted to use GOT.
*/
value = got_for_addr(sechdrs, absvalue, me,
strtab + sym->st_name); if (!value) return -ENOENT;
value -= (unsignedlong)location;
#else case R_PPC64_TOCSAVE: /* * Marker reloc indicates we don't have to save r2. * That would only save us one instruction, so ignore * it.
*/ break; #endif
case R_PPC64_ENTRY: if (IS_ENABLED(CONFIG_PPC_KERNEL_PCREL)) break;
/* * Optimize ELFv2 large code model entry point if * the TOC is within 2GB range of current location.
*/
value = my_r2(sechdrs, me) - (unsignedlong)location; if (value + 0x80008000 > 0xffffffff) break; /* * Check for the large code model prolog sequence: * ld r2, ...(r12) * add r2, r2, r12
*/ if ((((uint32_t *)location)[0] & ~0xfffc) != PPC_RAW_LD(_R2, _R12, 0)) break; if (((uint32_t *)location)[1] != PPC_RAW_ADD(_R2, _R2, _R12)) break; /* * If found, replace it with: * addis r2, r12, (.TOC.-func)@ha * addi r2, r2, (.TOC.-func)@l
*/
((uint32_t *)location)[0] = PPC_RAW_ADDIS(_R2, _R12, PPC_HA(value));
((uint32_t *)location)[1] = PPC_RAW_ADDI(_R2, _R2, PPC_LO(value)); 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.