From e0ab9f5d1796f824fc1788868443d2199f04381a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 21 Feb 2024 21:17:27 -0800 Subject: [PATCH] create-diff-object: Add support for CONFIG_X86_KERNEL_IBT With IBT enabled, objtool runs on the final linked vmlinux.o object instead of the individual translation units, creating the __pfx symbols at the end. But create-diff-object still runs on the individual .o objects, in which case the __pfx symbols may be missing. Manually detect function padding for that case. With this change, it should be fine [*] to patch a kernel with CONFIG_X86_KERNEL_IBT enabled. [*] Unless your patch adds an indirect call to an existing function which doesn't have any other indirect callers, in which case the callee might have been sealed, which will trigger a "Missing ENDBR" warning/panic. Signed-off-by: Josh Poimboeuf --- kpatch-build/create-diff-object.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/kpatch-build/create-diff-object.c b/kpatch-build/create-diff-object.c index 25710e921..0ce37a1a0 100644 --- a/kpatch-build/create-diff-object.c +++ b/kpatch-build/create-diff-object.c @@ -228,6 +228,28 @@ static struct rela *toc_rela(const struct rela *rela) (unsigned int)rela->addend); } +static unsigned int function_padding_size(struct kpatch_elf *kelf, struct symbol *sym) +{ + unsigned int size = 0; + + switch (kelf->arch) { + case X86_64: + { + unsigned char *insn = sym->sec->data->d_buf; + unsigned int i; + + for (i = 0; i < sym->sym.st_value && *insn == 0x90; i++, insn++) + size++; + + break; + } + default: + break; + } + + return size; +} + /* * When compiling with -ffunction-sections and -fdata-sections, almost every * symbol gets its own dedicated section. We call such symbols "bundled" @@ -244,6 +266,8 @@ static void kpatch_bundle_symbols(struct kpatch_elf *kelf) expected_offset = 16; else if (is_gcc6_localentry_bundled_sym(kelf, sym)) expected_offset = 8; + else if (sym->type == STT_FUNC) + expected_offset = function_padding_size(kelf, sym); else expected_offset = 0;