diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 0bc5cca..56cd166 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -2253,6 +2253,27 @@ static void kpatch_create_intermediate_sections(struct kpatch_elf *kelf, */ sym_objname = objname; + /* + * function prologue generated by gcc6 on ppc64le has + * the sequence: + * + * .globl my_func + * .type my_func, @function + * .quad .TOC.-my_func + * my_func: + * .reloc ., R_PPC64_ENTRY ; optional + * ld r2,-8(r12) + * add r2,r2,r12 + * .localentry my_func, .-my_func + * + * first entry past function global entry point is optional + * and has the relocation type R_PPC64_ENTRY. We should *not* + * skip this entry while building up the rela list, rather + * skip it's lookup. + */ + if (rela->type == R_PPC64_ENTRY) + continue; + if (rela->sym->bind == STB_LOCAL) { /* An unchanged local symbol */ ret = lookup_local_symbol(table, diff --git a/kpatch-build/create-klp-module.c b/kpatch-build/create-klp-module.c index 046f2f4..9c1187e 100644 --- a/kpatch-build/create-klp-module.c +++ b/kpatch-build/create-klp-module.c @@ -243,7 +243,19 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section rela->offset = toc_offset; else rela->offset = krelas[index].offset; - rela->addend = krelas[index].addend; + + /* + * gcc6 adds offset for 0x8 for every local function entry in + * .toc section, for avoiding setup of toc but when the previously + * local function becomes global, toc is anyways setup. So remove + * the wrong addend. + */ + if (!strcmp(base->name, ".toc") && + rela->sym->type == STT_FUNC && rela->sym->bind == STB_LOCAL) { + rela->addend = 0; + } else { + rela->addend = krelas[index].addend; + } } } diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 9b80e14..bd249de 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -76,6 +76,50 @@ int is_debug_section(struct section *sec) !strncmp(name, ".eh_frame", 9); } +#ifdef __powerpc__ + +/* Symbol st_others value for powerpc */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + +/* + * function prologue generated by gcc6 on ppc64le has + * the sequence: + * + * .globl my_func + * .type my_func, @function + * .quad .TOC.-my_func + * my_func: + * .reloc ., R_PPC64_ENTRY ; optional + * ld r2,-8(r12) + * add r2,r2,r12 + * .localentry my_func, .-my_func + * + * my_func is resolved by .TOC.-my_func to a 64-bit offset stored + * above the global entry point. my_func st_value is set 0x8, for + * calling into the .localentry of the function. + * + * Number of instructions between global and local entry point of a + * function is mostly 8 instructions. i.e st_other == 3. The count of + * instruction can be decoded only for local function symbols. + */ +static int is_localentry_sym(struct symbol *sym) +{ + if (sym->type != STT_FUNC || sym->sym.st_shndx == SHN_UNDEF) + return 0; + + if (sym->sym.st_value != 0x8) + return 0; + + if (!PPC64_LOCAL_ENTRY_OFFSET(sym->sym.st_other)) + return 0; + + return 1; +} +#endif + struct section *find_section_by_index(struct list_head *list, unsigned int index) { struct section *sec; @@ -328,9 +372,14 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf) sym->name); if (is_bundleable(sym)) { - if (sym->sym.st_value != 0) + if (sym->sym.st_value != 0) { +#ifdef __powerpc__ + if (!is_localentry_sym(sym)) +#endif ERROR("symbol %s at offset %lu within section %s, expected 0", - sym->name, sym->sym.st_value, sym->sec->name); + sym->name, sym->sym.st_value, sym->sec->name); + } + sym->sec->sym = sym; } else if (sym->type == STT_SECTION) { sym->sec->secsym = sym; diff --git a/kpatch-build/kpatch-elf.h b/kpatch-build/kpatch-elf.h index aa98688..e4b5b52 100644 --- a/kpatch-build/kpatch-elf.h +++ b/kpatch-build/kpatch-elf.h @@ -131,6 +131,10 @@ struct rela *find_rela_by_offset(struct section *relasec, unsigned int offset); int offset_of_string(struct list_head *list, char *name); +#ifndef R_PPC64_ENTRY +#define R_PPC64_ENTRY 118 +#endif + /************* * Functions * **********/