From 6c8e90e64086d4d1524474ef770e8d1f6d8dcbc3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 20 Sep 2017 18:26:57 -0500 Subject: [PATCH 1/3] create-kpatch-module: support unbundled symbols The create_dynamic_rela_sections() function assumes that all dest symbols are bundled, i.e. each symbol is located at offset 0 in its own section. However that may not always be the case. Unbundled symbols can occur, for example, when combining two .o files which have the same bundled symbol. They will be combined into the same section and will no longer be considered "bundled". Signed-off-by: Josh Poimboeuf --- kpatch-build/create-kpatch-module.c | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/kpatch-build/create-kpatch-module.c b/kpatch-build/create-kpatch-module.c index a2c6adf..183a1e1 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); From 5888f316e63a2cc460180e8ace12d45820cd0d19 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 20 Sep 2017 23:36:44 -0500 Subject: [PATCH 2/3] create-klp-module: support unbundled symbols The create_klp_relasecs_and_syms() function assumes that all dest symbols are bundled, i.e. each symbol is located at offset 0 in its own section. However that may not always be the case. Unbundled symbols can occur, for example, when combining two .o files which have the same bundled symbol. They will be combined into the same section and will no longer be considered "bundled". Signed-off-by: Josh Poimboeuf --- kpatch-build/create-klp-module.c | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/kpatch-build/create-klp-module.c b/kpatch-build/create-klp-module.c index 73ceb0e..b18cbe6 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 { From b72027c44e6865da4a2f1f56210ba110021ece2f Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 20 Sep 2017 11:55:01 -0500 Subject: [PATCH 3/3] kpatch-build: do symbol/section bundling in create-diff-object kpatch-elf.c is used by binaries other than create-diff-object, but create-diff-object is the only one that cares about "bundling". Move the bundling to create-diff-object. Fixes #700. Signed-off-by: Josh Poimboeuf --- kpatch-build/create-diff-object.c | 99 +++++++++++++++++++++++++++++++ kpatch-build/kpatch-elf.c | 83 +------------------------- 2 files changed, 100 insertions(+), 82 deletions(-) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 4921487..cdc9e4c 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/kpatch-elf.c b/kpatch-build/kpatch-elf.c index 72ed62f..54abad3 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;