kpatch-build: Add PPC64le livepatch support

This patch adds support for livepatch hook based module
creation for PPC64le. It introduces PPC64le architecture
bits:
- Add relocation type of R_PPC64_ADDR64 while parsing powerpc ELF.
- Introduce .toc sections mainpulation.
- Skip kpatch specific details for livepatch hook.

Also remove the definition of rela_insn() for powerpc. The only
call site is been guarded by #ifdef x86.

Cc: Josh Poimboeuf <jpoimboe@redhat.com>
Signed-off-by: Kamalesh Babulal <kamalesh@linux.vnet.ibm.com>
This commit is contained in:
Kamalesh Babulal 2017-07-26 01:46:53 -04:00
parent c9e4230d88
commit c14e6e9118
7 changed files with 213 additions and 22 deletions

View File

@ -3,6 +3,14 @@ KPATCH_BUILD ?= /lib/modules/$(shell uname -r)/build
KPATCH_MAKE = $(MAKE) -C $(KPATCH_BUILD) M=$(PWD)
LDFLAGS += $(KPATCH_LDFLAGS)
# ppc64le kernel modules are expected to compiled with
# -mcmodel=large flag. This enable 64-bit relocations,
# instead of 32-bit offset from TOC pointer.
PROCESSOR = $(shell uname -p)
ifeq ($(PROCESSOR), ppc64le)
KBUILD_CFLAGS_MODULE += -mcmodel=large
endif
obj-m += kpatch-$(KPATCH_NAME).o
kpatch-$(KPATCH_NAME)-objs += patch-hook.o kpatch.lds output.o

View File

@ -248,6 +248,11 @@ static int __init patch_init(void)
goto out;
lpatch->mod = THIS_MODULE;
lpatch->objs = lobjects;
#if __powerpc__
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0))
lpatch->immediate = true;
#endif
#endif
i = 0;
list_for_each_entry(object, &patch_objects, list) {

View File

@ -59,6 +59,12 @@
error(2, 0, "unreconcilable difference"); \
})
#ifdef __powerpc__
#define ABSOLUTE_RELA_TYPE R_PPC64_ADDR64
#else
#define ABSOLUTE_RELA_TYPE R_X86_64_64
#endif
char *childobj;
enum loglevel loglevel = NORMAL;
@ -146,6 +152,11 @@ static int kpatch_mangled_strcmp(char *s1, char *s2)
static int rela_equal(struct rela *rela1, struct rela *rela2)
{
#ifdef __powerpc__
struct section *toc_relasec1, *toc_relasec2;
struct rela *r_toc_relasec1, *r_toc_relasec2;
#endif
if (rela1->type != rela2->type ||
rela1->offset != rela2->offset)
return 0;
@ -153,6 +164,64 @@ static int rela_equal(struct rela *rela1, struct rela *rela2)
if (rela1->string)
return rela2->string && !strcmp(rela1->string, rela2->string);
#ifdef __powerpc__
/*
* Relocation section '.rela.toc' at offset 0x91f0 contains 122 entries:
* Offset Info Type Symbol's Value Symbol's Name + Addend
* [...]
* 0000000000000090 0000001c00000026 R_PPC64_ADDR64 0000000000000000 .rodata.__dev_remove_pack.str1.8 + 0
* 0000000000000098 0000001a00000026 R_PPC64_ADDR64 0000000000000000 .rodata.__dev_getfirstbyhwtype.str1.8 + 0
* 00000000000000a0 0000001a00000026 R_PPC64_ADDR64 0000000000000000 .rodata.__dev_getfirstbyhwtype.str1.8 + 10
* 00000000000000a8 000000cb00000026 R_PPC64_ADDR64 0000000000000000 dev_base_lock + 0
*
* Relocation section '.rela.text.netdev_master_upper_dev_get' at offset 0xe38 contains 10 entries:
* Offset Info Type Symbol's Value Symbol's Name + Addend
* [...]
* 00000000000000a0 0000004e00000032 R_PPC64_TOC16_HA 0000000000000000 .toc + 98
* 00000000000000a8 0000004e00000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + 98
* 00000000000000ac 0000004e00000032 R_PPC64_TOC16_HA 0000000000000000 .toc + a0
* 00000000000000b0 0000004e00000040 R_PPC64_TOC16_LO_DS 0000000000000000 .toc + a0
* 00000000000000b4 000000b90000000a R_PPC64_REL24 0000000000000000 printk + 0
* 00000000000000bc 000000a10000000a R_PPC64_REL24 0000000000000000 dump_stack + 0
*
* with -mcmodel=large on ppc64le, gcc might generate entries in .toc section for symbol
* reference in relocation sections. There are chances of symbol referenced by toc + offset
* may point to different symbols in original and patched .o. Compare the symbols by
* reading its corresponding toc + offset rela from .toc section.
*/
if (!strcmp(rela1->sym->name, ".toc") &&
!strcmp(rela2->sym->name, ".toc")) {
toc_relasec1 = rela1->sym->sec->rela;
if (!toc_relasec1)
ERROR("cannot find .rela.toc");
r_toc_relasec1 = find_rela_by_offset(toc_relasec1, rela1->addend);
if (!r_toc_relasec1)
ERROR(".toc entry not found %s + %x", rela1->sym->name, rela1->addend);
toc_relasec2 = rela2->sym->sec->rela;
if (!toc_relasec2)
ERROR("cannot find .rela.toc");
r_toc_relasec2 = find_rela_by_offset(toc_relasec2, rela2->addend);
if (!r_toc_relasec2)
ERROR(".toc entry not found %s + %x", rela2->sym->name, rela2->addend);
if (r_toc_relasec1->string)
return r_toc_relasec2->string &&
!strcmp(r_toc_relasec1->string, r_toc_relasec2->string);
if ((r_toc_relasec1->addend != r_toc_relasec2->addend))
return 0;
if (is_special_static(r_toc_relasec1->sym))
return !kpatch_mangled_strcmp(r_toc_relasec1->sym->name,
r_toc_relasec2->sym->name);
return !strcmp(r_toc_relasec1->sym->name, r_toc_relasec2->sym->name);
}
#endif
if (rela1->addend != rela2->addend)
return 0;
@ -167,6 +236,16 @@ static void kpatch_compare_correlated_rela_section(struct section *sec)
{
struct rela *rela1, *rela2 = NULL;
/*
* Don't compare relocation entries for .toc section.
* .toc and .rela.toc sections are included as standard
* elements.
*/
if (!strcmp(sec->name, ".rela.toc")) {
sec->status = SAME;
return;
}
rela2 = list_entry(sec->twin->relas.next, struct rela, list);
list_for_each_entry(rela1, &sec->relas, list) {
if (rela_equal(rela1, rela2)) {
@ -198,7 +277,6 @@ static void kpatch_compare_correlated_section(struct section *sec)
/* Compare section headers (must match or fatal) */
if (sec1->sh.sh_type != sec2->sh.sh_type ||
sec1->sh.sh_flags != sec2->sh.sh_flags ||
sec1->sh.sh_addr != sec2->sh.sh_addr ||
sec1->sh.sh_addralign != sec2->sh.sh_addralign ||
sec1->sh.sh_entsize != sec2->sh.sh_entsize)
DIFF_FATAL("%s section header details differ", sec1->name);
@ -894,8 +972,6 @@ static void rela_insn(struct section *sec, struct rela *rela, struct insn *insn)
return;
}
}
#else
static void rela_insn(struct section *sec, struct rela *rela, struct insn *insn) { }
#endif
/*
@ -933,6 +1009,18 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
continue;
}
#ifdef __powerpc__
/*
* With -mcmodel=large, the relative relocation or
* R_PPC64_REL24 type is limited to functions only.
* rela->sym->sec should have been replaced with it's
* section symbol already. If not bail out.
*/
if (rela->type == R_PPC64_REL24)
ERROR("Unexpected relocation type R_PPC64_REL24 for %s\n", rela->sym->name);
add_off = 0;
#else
if (rela->type == R_X86_64_PC32) {
struct insn insn;
rela_insn(sec, rela, &insn);
@ -944,6 +1032,7 @@ static void kpatch_replace_sections_syms(struct kpatch_elf *kelf)
add_off = 0;
else
continue;
#endif
/*
* Attempt to replace references to unbundled sections
@ -1114,12 +1203,14 @@ out:
static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
{
struct section *sec;
struct rela *rela;
list_for_each_entry(sec, &kelf->sections, list) {
/* include these sections even if they haven't changed */
if (!strcmp(sec->name, ".shstrtab") ||
!strcmp(sec->name, ".strtab") ||
!strcmp(sec->name, ".symtab") ||
!strcmp(sec->name, ".toc") ||
!strcmp(sec->name, ".rodata") ||
(!strncmp(sec->name, ".rodata.", 8) &&
strstr(sec->name, ".str1."))) {
@ -1127,6 +1218,18 @@ static void kpatch_include_standard_elements(struct kpatch_elf *kelf)
if (sec->secsym)
sec->secsym->include = 1;
}
/*
* rela.toc section holds reference to symbols,
* referred by function section relocation
* entries. Include all of the symbols.
*/
if (!strcmp(sec->name, ".rela.toc"))
{
sec->include = 1;
list_for_each_entry(rela, &sec->relas, list)
kpatch_include_symbol(rela->sym, 0);
}
}
/* include the NULL symbol */
@ -1776,7 +1879,7 @@ static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *obj
/* entries[index].sec */
ALLOC_LINK(rela, &karch_sec->rela->relas);
rela->sym = sec->secsym;
rela->type = R_X86_64_64;
rela->type = ABSOLUTE_RELA_TYPE;
rela->addend = 0;
rela->offset = index * sizeof(*entries) + \
offsetof(struct kpatch_arch, sec);
@ -1784,7 +1887,7 @@ static void kpatch_create_kpatch_arch_section(struct kpatch_elf *kelf, char *obj
/* entries[index].objname */
ALLOC_LINK(rela, &karch_sec->rela->relas);
rela->sym = strsym;
rela->type = R_X86_64_64;
rela->type = ABSOLUTE_RELA_TYPE;
rela->addend = offset_of_string(&kelf->strings, objname);
rela->offset = index * sizeof(*entries) + \
offsetof(struct kpatch_arch, objname);
@ -1980,7 +2083,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
*/
ALLOC_LINK(rela, &relasec->relas);
rela->sym = sym;
rela->type = R_X86_64_64;
rela->type = ABSOLUTE_RELA_TYPE;
rela->addend = 0;
rela->offset = index * sizeof(*funcs);
@ -1990,7 +2093,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
*/
ALLOC_LINK(rela, &relasec->relas);
rela->sym = strsym;
rela->type = R_X86_64_64;
rela->type = ABSOLUTE_RELA_TYPE;
rela->addend = offset_of_string(&kelf->strings, sym->name);
rela->offset = index * sizeof(*funcs) +
offsetof(struct kpatch_patch_func, name);
@ -2001,7 +2104,7 @@ static void kpatch_create_patches_sections(struct kpatch_elf *kelf,
*/
ALLOC_LINK(rela, &relasec->relas);
rela->sym = strsym;
rela->type = R_X86_64_64;
rela->type = ABSOLUTE_RELA_TYPE;
rela->addend = objname_offset;
rela->offset = index * sizeof(*funcs) +
offsetof(struct kpatch_patch_func,objname);
@ -2120,9 +2223,28 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
* a global symbol. Use a normal rela for
* exported symbols and a dynrela otherwise.
*/
#ifdef __powerpc__
/*
* An exported symbol, might be local to an object file
* and any access to the function might be through localentry
* (toc+offset) instead of global offset.
*
* fs/proc/proc_sysctl::sysctl_head_grab:
* 166: 0000000000000000 256 FUNC GLOBAL DEFAULT [<localentry>: 8] 42 unregister_sysctl_table
* 167: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND .TOC.
*
* These type of function have the sym->type == STT_FUNC, treat them as local
* symbol. This creates an entry under klp.relocations and relocation
* is handled by livepatch.
*/
if (lookup_is_exported_symbol(table, rela->sym->name)) {
if (rela->sym->type != STT_FUNC)
continue;
}
#else
if (lookup_is_exported_symbol(table, rela->sym->name))
continue;
#endif
/*
* If lookup_global_symbol() fails, assume the
* symbol is defined in another object in the
@ -2181,7 +2303,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
/* add rela to fill in ksyms[index].name field */
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
rela2->sym = strsym;
rela2->type = R_X86_64_64;
rela2->type = ABSOLUTE_RELA_TYPE;
rela2->addend = offset_of_string(&kelf->strings, rela->sym->name);
rela2->offset = index * sizeof(*ksyms) + \
offsetof(struct kpatch_symbol, name);
@ -2189,7 +2311,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
/* add rela to fill in ksyms[index].objname field */
ALLOC_LINK(rela2, &ksym_sec->rela->relas);
rela2->sym = strsym;
rela2->type = R_X86_64_64;
rela2->type = ABSOLUTE_RELA_TYPE;
rela2->addend = offset_of_string(&kelf->strings, sym_objname);
rela2->offset = index * sizeof(*ksyms) + \
offsetof(struct kpatch_symbol, objname);
@ -2210,7 +2332,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
ERROR("can't create dynrela for section %s (symbol %s): no bundled section or section symbol",
sec->name, rela->sym->name);
rela2->type = R_X86_64_64;
rela2->type = ABSOLUTE_RELA_TYPE;
rela2->addend = rela->offset;
rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, dest);
@ -2218,7 +2340,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
/* add rela to fill in krelas[index].objname field */
ALLOC_LINK(rela2, &krela_sec->rela->relas);
rela2->sym = strsym;
rela2->type = R_X86_64_64;
rela2->type = ABSOLUTE_RELA_TYPE;
rela2->addend = offset_of_string(&kelf->strings, objname);
rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, objname);
@ -2226,7 +2348,7 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf,
/* add rela to fill in krelas[index].ksym field */
ALLOC_LINK(rela2, &krela_sec->rela->relas);
rela2->sym = ksym_sec_sym;
rela2->type = R_X86_64_64;
rela2->type = ABSOLUTE_RELA_TYPE;
rela2->addend = index * sizeof(*ksyms);
rela2->offset = index * sizeof(*krelas) + \
offsetof(struct kpatch_relocation, ksym);
@ -2269,12 +2391,15 @@ static void kpatch_create_hooks_objname_rela(struct kpatch_elf *kelf, char *objn
ALLOC_LINK(rela, &sec->relas);
rela->sym = strsym;
rela->type = R_X86_64_64;
rela->type = ABSOLUTE_RELA_TYPE;
rela->addend = objname_offset;
rela->offset = offsetof(struct kpatch_patch_hook, objname);
}
}
#ifdef __powerpc__
void kpatch_create_mcount_sections(struct kpatch_elf *kelf) { }
#else
/*
* This function basically reimplements the functionality of the Linux
* recordmcount script, so that patched functions can be recognized by ftrace.
@ -2349,6 +2474,7 @@ static void kpatch_create_mcount_sections(struct kpatch_elf *kelf)
if (index != nr)
ERROR("size mismatch in funcs sections");
}
#endif
/*
* This function strips out symbols that were referenced by changed rela

View File

@ -160,7 +160,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
struct symbol *sym;
struct rela *rela;
char *objname;
int nr, index, offset;
int nr, index, offset, toc_offset;
krelas = krelasec->data->d_buf;
nr = krelasec->data->d_size / sizeof(*krelas);
@ -174,6 +174,37 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
if (!rela)
ERROR("find_rela_by_offset");
/*
* Patched file:
* Relocation section '.rela.toc' at offset 0x46358 contains 60 entries:
* Offset Info Type Symbol's Value Symbol's Name + Addend
* 0000000000000000 000001ee00000026 R_PPC64_ADDR64 0000000000000000 jiffies + 0
* 0000000000000008 0000009400000026 R_PPC64_ADDR64 0000000000000000 __tracepoints + 0
* 0000000000000010 000001db00000026 R_PPC64_ADDR64 0000000000000000 __cpu_online_mask + 0
* 0000000000000018 0000009c00000026 R_PPC64_ADDR64 0000000000000000 .data..percpu + 0
* 0000000000000020 000001ac00000026 R_PPC64_ADDR64 0000000000000000 __per_cpu_offset + 0
* 0000000000000028 0000006900000026 R_PPC64_ADDR64 0000000000000000 .rodata.str1.8 + 0
* [...]
*
* Output file:
* Relocation section '.rela.toc' at offset 0x1270 contains 58 entries:
* Offset Info Type Symbol's Value Symbol's Name + Addend
* 0000000000000000 0000000700000026 R_PPC64_ADDR64 0000000000000000 .data + 0
* 0000000000000008 0000003c00000026 R_PPC64_ADDR64 0000000000000000 __kpatch_funcs + 0
* 0000000000000010 0000005300000026 R_PPC64_ADDR64 0000000000000000 kmalloc_caches + 0
* 0000000000000018 0000001100000026 R_PPC64_ADDR64 0000000000000000 .rodata.str1.8 + 0
* 0000000000000020 0000001600000026 R_PPC64_ADDR64 0000000000000000 .bss + 0
* 0000000000000028 0000004200000026 R_PPC64_ADDR64 0000000000000038 __kpatch_funcs_end + 0
* 0000000000000030 0000003400000026 R_PPC64_ADDR64 0000000000000000 __this_module + 0
* 0000000000000038 0000004d00000026 R_PPC64_ADDR64 0000000000000000 jiffies + 0
* 0000000000000048 0000004500000026 R_PPC64_ADDR64 0000000000000000 __cpu_online_mask + 0
* 0000000000000058 0000003900000026 R_PPC64_ADDR64 0000000000000000 __per_cpu_offset + 0
* [...]
*
* when more .o files are linked together, the .toc entries might get re-arranged. Capture new .toc rela
* offset value, which will be used below to setting the rela->addend. */
toc_offset = rela->addend;
base = rela->sym->sec;
if (!base)
ERROR("base sec of krela not found");
@ -208,7 +239,10 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section
ALLOC_LINK(rela, &klp_relasec->relas);
rela->sym = sym;
rela->type = krelas[index].type;
rela->offset = krelas[index].offset;
if (!strcmp(base->name, ".toc"))
rela->offset = toc_offset;
else
rela->offset = krelas[index].offset;
rela->addend = krelas[index].addend;
}
}

View File

@ -49,6 +49,7 @@ APPLIEDPATCHFILE="kpatch.patch"
DEBUG=0
SKIPCLEANUP=0
SKIPGCCCHECK=0
ARCH_KCFLAGS=""
warn() {
echo "ERROR: $1" >&2
@ -556,7 +557,12 @@ cd "$SRCDIR" || die
patch -N -p1 --dry-run < "$PATCHFILE" || die "source patch file failed to apply"
cp "$PATCHFILE" "$APPLIEDPATCHFILE" || die
cp -LR "$DATADIR/patch" "$TEMPDIR" || die
export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections"
if [[ $ARCH = "ppc64le" ]]; then
ARCH_KCFLAGS="-mcmodel=large"
fi
export KCFLAGS="-I$DATADIR/patch -ffunction-sections -fdata-sections $ARCH_KCFLAGS"
echo "Reading special section data"
find_special_section_data
@ -648,7 +654,7 @@ do
done
echo
export KCFLAGS="-I$DATADIR/patch"
export KCFLAGS="-I$DATADIR/patch $ARCH_KCFLAGS"
if $KPATCH_MODULE; then
export KCPPFLAGS="-D__KPATCH_MODULE__"
fi

View File

@ -349,7 +349,7 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf)
}
/* Check which functions have fentry calls; save this info for later use. */
/* Check which functions have fentry/mcount calls; save this info for later use. */
static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf)
{
struct symbol *sym;
@ -357,7 +357,14 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf)
list_for_each_entry(sym, &kelf->symbols, list) {
if (sym->type != STT_FUNC || !sym->sec || !sym->sec->rela)
continue;
#ifdef __powerpc__
list_for_each_entry(rela, &sym->sec->rela->relas, list) {
if (!strcmp(rela->sym->name, "_mcount")) {
sym->has_func_profiling = 1;
break;
}
}
#else
rela = list_first_entry(&sym->sec->rela->relas, struct rela,
list);
if (rela->type != R_X86_64_NONE ||
@ -365,6 +372,7 @@ static void kpatch_find_func_profiling_calls(struct kpatch_elf *kelf)
continue;
sym->has_func_profiling = 1;
#endif
}
}

View File

@ -15,7 +15,10 @@ if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then
while [ "$#" -gt 0 ]; do
if [ "$1" = "-o" ]; then
obj=$2
[[ $2 = */.tmp_*.o ]] && obj=${2/.tmp_/}
# skip copying the original .o files, when .tmp_mc_*.o
# is passed from recordmcount.pl.
[[ $obj = */.tmp_mc_*.o ]] && break;
[[ $obj = */.tmp_*.o ]] && obj=${obj/.tmp_/}
case "$obj" in
*.mod.o|\
*built-in.o|\
@ -32,6 +35,7 @@ if [[ "$TOOLCHAINCMD" = "gcc" ]] ; then
arch/x86/vdso/*|\
arch/x86/entry/vdso/*|\
drivers/firmware/efi/libstub/*|\
arch/powerpc/kernel/prom_init.o|\
*.*.o)
break
;;