diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 49214876d..cdc9e4ca8 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -81,6 +81,102 @@ struct special_section { * Functions * **********/ +static int is_bundleable(struct symbol *sym) +{ + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.",6) && + !strcmp(sym->sec->name + 6, sym->name)) + return 1; + + if (sym->type == STT_FUNC && + !strncmp(sym->sec->name, ".text.unlikely.",15) && + !strcmp(sym->sec->name + 15, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".data.",6) && + !strcmp(sym->sec->name + 6, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".rodata.",8) && + !strcmp(sym->sec->name + 8, sym->name)) + return 1; + + if (sym->type == STT_OBJECT && + !strncmp(sym->sec->name, ".bss.",5) && + !strcmp(sym->sec->name + 5, sym->name)) + return 1; + + return 0; +} + +#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) + +/* + * On ppc64le, the function prologue generated by GCC 6+ 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 the global entry point, which, when called, sets up the TOC. + * .localentry is the local entry point, for calls to the function from within + * the object file. The local entry point is 8 bytes after the global entry + * point. + */ +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; +} +#else +static int is_localentry_sym(struct symbol *sym) +{ + return 0; +} +#endif + +/* + * When compiling with -ffunction-sections and -fdata-sections, almost every + * symbol gets its own dedicated section. We call such symbols "bundled" + * symbols. They're indicated by "sym->sec->sym == sym". + */ +static void kpatch_bundle_symbols(struct kpatch_elf *kelf) +{ + struct symbol *sym; + + list_for_each_entry(sym, &kelf->symbols, list) { + if (is_bundleable(sym)) { + if (sym->sym.st_value != 0 && !is_localentry_sym(sym)) { + ERROR("symbol %s at offset %lu within section %s, expected 0", + sym->name, sym->sym.st_value, + sym->sec->name); + } + + sym->sec->sym = sym; + } + } +} + /* * This function detects whether the given symbol is a "special" static local * variable (for lack of a better term). @@ -2713,6 +2809,9 @@ int main(int argc, char *argv[]) kelf_base = kpatch_elf_open(arguments.args[0]); kelf_patched = kpatch_elf_open(arguments.args[1]); + kpatch_bundle_symbols(kelf_base); + kpatch_bundle_symbols(kelf_patched); + kpatch_compare_elf_headers(kelf_base->elf, kelf_patched->elf); kpatch_check_program_headers(kelf_base->elf); kpatch_check_program_headers(kelf_patched->elf); diff --git a/kpatch-build/create-klp-module.c b/kpatch-build/create-klp-module.c index 73ceb0e4c..b18cbe6fc 100644 --- a/kpatch-build/create-klp-module.c +++ b/kpatch-build/create-klp-module.c @@ -155,9 +155,9 @@ static struct section *find_or_add_klp_relasec(struct kpatch_elf *kelf, static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section *krelasec, struct section *ksymsec, char *strings) { - struct section *base, *klp_relasec; + struct section *klp_relasec; struct kpatch_relocation *krelas; - struct symbol *sym; + struct symbol *sym, *dest; struct rela *rela; char *objname; int nr, index, offset, toc_offset; @@ -207,9 +207,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section */ toc_offset = rela->addend; - base = rela->sym->sec; - if (!base) - ERROR("base sec of krela not found"); + dest = rela->sym; /* Get the name of the object the rela belongs to */ rela = find_rela_by_offset(krelasec->rela, @@ -232,8 +230,8 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section if (!sym) ERROR("error finding or adding ksym to symtab"); - /* Create (or find) the .klp.rela. section for this base sec and object */ - klp_relasec = find_or_add_klp_relasec(kelf, base, objname); + /* Create (or find) the .klp.rela. section for this dest sec and object */ + klp_relasec = find_or_add_klp_relasec(kelf, dest->sec, objname); if (!klp_relasec) ERROR("error finding or adding klp relasec"); @@ -241,10 +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; - if (!strcmp(base->name, ".toc")) + if (!strcmp(dest->sec->name, ".toc")) rela->offset = toc_offset; else - rela->offset = krelas[index].offset; + rela->offset = krelas[index].offset + dest->sym.st_value; /* * GCC 6+ adds 0x8 to the offset of every local function entry @@ -253,7 +251,7 @@ static void create_klp_relasecs_and_syms(struct kpatch_elf *kelf, struct section * local function becomes global, we don't want to skip the * .toc setup anymore. */ - if (!strcmp(base->name, ".toc") && + if (!strcmp(dest->sec->name, ".toc") && rela->sym->type == STT_FUNC && rela->sym->bind == STB_LOCAL) { rela->addend = 0; } else { diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c index a2c6adfef..183a1e185 100644 --- a/kpatch-build/create-kpatch-module.c +++ b/kpatch-build/create-kpatch-module.c @@ -43,7 +43,8 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section struct kpatch_patch_dynrela *dynrelas; struct kpatch_relocation *krelas; struct kpatch_symbol *ksym, *ksyms; - struct section *base, *dynsec; + struct section *dynsec; + struct symbol *sym; struct rela *rela; int index, nr, offset, dest_offset, objname_offset, name_offset; @@ -58,19 +59,16 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section offset = index * sizeof(*krelas); /* - * To fill in each dynrela entry, find base section (dest), + * To fill in each dynrela entry, find dest location, * objname offset, ksym, and symbol name offset */ - /* Get base section */ + /* Get dest location */ rela = find_rela_by_offset(krelasec->rela, offset + offsetof(struct kpatch_relocation, dest)); if (!rela) ERROR("find_rela_by_offset"); - - base = rela->sym->sec; - if (!base) - ERROR("base sec of krela not found"); + sym = rela->sym; dest_offset = rela->addend; /* Get objname offset */ @@ -103,7 +101,7 @@ static void create_dynamic_rela_sections(struct kpatch_elf *kelf, struct section /* dest */ ALLOC_LINK(rela, &dynsec->rela->relas); - rela->sym = base->sym ? base->sym : base->secsym; + rela->sym = sym; rela->type = R_X86_64_64; rela->addend = dest_offset; rela->offset = index * sizeof(*dynrelas); diff --git a/kpatch-build/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 72ed62f16..54abad38f 100644 --- a/kpatch-build/kpatch-elf.c +++ b/kpatch-build/kpatch-elf.c @@ -76,46 +76,6 @@ 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) - -/* - * On ppc64le, the function prologue generated by GCC 6+ 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 the global entry point, which, when called, sets up the TOC. - * .localentry is the local entry point, for calls to the function from within - * the object file. The local entry point is 8 bytes after the global entry - * point. - */ -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; @@ -299,36 +259,6 @@ void kpatch_create_section_list(struct kpatch_elf *kelf) ERROR("expected NULL"); } -static int is_bundleable(struct symbol *sym) -{ - if (sym->type == STT_FUNC && - !strncmp(sym->sec->name, ".text.",6) && - !strcmp(sym->sec->name + 6, sym->name)) - return 1; - - if (sym->type == STT_FUNC && - !strncmp(sym->sec->name, ".text.unlikely.",15) && - !strcmp(sym->sec->name + 15, sym->name)) - return 1; - - if (sym->type == STT_OBJECT && - !strncmp(sym->sec->name, ".data.",6) && - !strcmp(sym->sec->name + 6, sym->name)) - return 1; - - if (sym->type == STT_OBJECT && - !strncmp(sym->sec->name, ".rodata.",8) && - !strcmp(sym->sec->name + 8, sym->name)) - return 1; - - if (sym->type == STT_OBJECT && - !strncmp(sym->sec->name, ".bss.",5) && - !strcmp(sym->sec->name + 5, sym->name)) - return 1; - - return 0; -} - void kpatch_create_symbol_list(struct kpatch_elf *kelf) { struct section *symtab; @@ -367,18 +297,7 @@ void kpatch_create_symbol_list(struct kpatch_elf *kelf) ERROR("couldn't find section for symbol %s\n", sym->name); - if (is_bundleable(sym)) { - 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->sec->sym = sym; - - } else if (sym->type == STT_SECTION) { + if (sym->type == STT_SECTION) { sym->sec->secsym = sym; /* use the section name as the symbol name */ sym->name = sym->sec->name;