objtool,livepatch: klp-build livepatch module generation
Subsystems (11)
Josh Poimboeuf
maintainerv2 cover objtool,livepatch: klp-build livepatch module generation
This series introduces new objtool features and a klp-build script to generate livepatch modules using a source .patch as input.
This builds on concepts from the longstanding out-of-tree kpatch [1] project which began in 2012 and has been used for many years to generate livepatch modules for production kernels. However, this is a complete rewrite which incorporates hard-earned lessons from 12+ years of maintaining kpatch.
Key improvements compared to kpatch-build:
- Integrated with objtool: Leverages objtool's existing control-flow graph analysis to help detect changed functions.
- Works on vmlinux.o: Supports late-linked objects, making it compatible with LTO, IBT, and similar.
- Simplified code base: ~3k fewer lines of code.
- Upstream: No more out-of-tree #ifdef hacks, far less cruft.
- Cleaner internals: Vastly simplified logic for symbol/section/reloc inclusion and special section extraction.
- Robust __LINE__ macro handling: Avoids false positive binary diffs caused by the __LINE__ macro by introducing a fix-patch-lines script which injects #line directives into the source .patch to preserve the original line numbers at compile time.
The primary user interface is the klp-build script which does the following:
- Builds an original kernel with -function-sections and -fdata-sections, plus objtool function checksumming.
- Applies the .patch file and rebuilds the kernel using the same options.
- Runs 'objtool klp diff' to detect changed functions and generate intermediate binary diff objects.
- Builds a kernel module which links the diff objects with some livepatch module init code (scripts/livepatch/init.c).
- Finalizes the livepatch module (aka work around linker wreckage) using 'objtool klp post-link'.
I've tested with a variety of patches on defconfig and Fedora-config kernels with both GCC and Clang.
These patches can also be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git klp-build-v2
Please test!
[1] https://github.com/dynup/kpatch
Changes since the RFC: https://lore.kernel.org/[email protected]
- Too many to list. Doubled the patch count exactly ;-)
Josh Poimboeuf (62):
s390/vmlinux.lds.S: Prevent thunk functions from getting placed with
normal text
vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and related macros x86/module: Improve relocation error messages
x86/kprobes: Remove STACK_FRAME_NON_STANDARD annotation
compiler: Tweak __UNIQUE_ID() naming
compiler.h: Make addressable symbols less of an eyesore
elfnote: Change ELFNOTE() to use __UNIQUE_ID()
kbuild: Remove 'kmod_' prefix from __KBUILD_MODNAME
modpost: Ignore unresolved section bounds symbols x86/alternative: Refactor INT3 call emulation selftest
objtool: Make find_symbol_containing() less arbitrary
objtool: Speed up SHT_GROUP reindexing
objtool: Fix broken error handling in read_symbols()
objtool: Propagate elf_truncate_section() error in elf_write()
objtool: Add empty symbols to the symbol tree again
objtool: Fix interval tree insertion for zero-length symbols
objtool: Fix weak symbol detection
objtool: Fix x86 addend calculation
objtool: Fix __pa_symbol() relocation handling
objtool: Fix "unexpected end of section" warning for alternatives
objtool: Check for missing annotation entries in read_annotate()
objtool: Const string cleanup
objtool: Clean up compiler flag usage
objtool: Remove .parainstructions reference
objtool: Convert elf iterator macros to use 'struct elf'
objtool: Add section/symbol type helpers
objtool: Mark .cold subfunctions
objtool: Fix weak symbol hole detection for .cold functions
objtool: Mark prefix functions
objtool: Simplify reloc offset calculation in unwind_read_hints()
objtool: Avoid emptying lists for duplicate sections
objtool: Suppress section skipping warnings with --dryrun
objtool: Rename --Werror to --werror
objtool: Reindent check_options[]
objtool: Refactor add_jump_destinations()
objtool: Simplify special symbol handling in elf_update_symbol()
objtool: Generalize elf_create_symbol()
objtool: Generalize elf_create_section()
objtool: Add elf_create_data()
objtool: Introduce elf_create_reloc() and elf_init_reloc()
objtool: Add elf_create_file()
kbuild,x86: Fix module permissions for __jump_table and __bug_table x86/alternative: Define ELF section entry size for alternatives x86/jump_label: Define ELF section entry size for jump table x86/extable: Define ELF section entry size for exception tables x86/bug: Define ELF section entry size for the bug table x86/orc: Define ELF section entry size for unwind hints
objtool: Make STACK_FRAME_NON_STANDARD consistent kbuild,objtool: Defer objtool validation step for CONFIG_LIVEPATCH objtool/klp: Add --checksum option to generate per-function checksums objtool/klp: Add --debug-checksum=<funcs> to show per-instruction
checksums
objtool/klp: Introduce klp diff subcommand for diffing object files objtool/klp: Add --debug option to show cloning decisions objtool/klp: Add post-link subcommand to finalize livepatch modules
objtool: Disallow duplicate prefix symbols
objtool: Add base objtool support for livepatch modules livepatch/klp-build: Introduce fix-patch-lines script to avoid
__LINE__ diff noise
livepatch/klp-build: Add stub init code for livepatch modules livepatch/klp-build: Introduce klp-build script for generating
livepatch modules
livepatch/klp-build: Add --debug option to show cloning decisions livepatch/klp-build: Add --show-first-changed option to show function
divergence
livepatch: Introduce source code helpers for livepatch modules
MAINTAINERS | 3 +- arch/Kconfig | 3 + arch/s390/include/asm/nospec-insn.h | 2 +- arch/s390/kernel/vmlinux.lds.S | 2 +- arch/x86/Kconfig | 1 + arch/x86/include/asm/alternative.h | 7 +- arch/x86/include/asm/asm.h | 20 +- arch/x86/include/asm/bug.h | 44 +- arch/x86/include/asm/jump_label.h | 32 +- arch/x86/kernel/alternative.c | 53 +- arch/x86/kernel/kprobes/opt.c | 4 - arch/x86/kernel/module.c | 15 +- arch/x86/kernel/unwind_orc.c | 2 + include/asm-generic/vmlinux.lds.h | 40 +- include/linux/compiler.h | 8 +- include/linux/elfnote.h | 13 +- include/linux/init.h | 3 +- include/linux/jump_label.h | 20 +- include/linux/livepatch.h | 25 +- include/linux/livepatch_external.h | 76 + include/linux/livepatch_helpers.h | 68 + include/linux/objtool.h | 16 +- kernel/extable.c | 2 + kernel/livepatch/core.c | 8 +- scripts/Makefile.lib | 6 +- scripts/Makefile.modfinal | 18 +- scripts/Makefile.vmlinux_o | 2 +- scripts/link-vmlinux.sh | 3 +- scripts/livepatch/fix-patch-lines | 79 + scripts/livepatch/init.c | 108 ++ scripts/livepatch/klp-build | 781 +++++++++ scripts/mod/modpost.c | 5 + scripts/module.lds.S | 26 +- tools/include/linux/interval_tree_generic.h | 2 +- tools/include/linux/livepatch_external.h | 76 + tools/objtool/Build | 4 +- tools/objtool/Makefile | 48 +- tools/objtool/arch/loongarch/decode.c | 6 +- tools/objtool/arch/powerpc/decode.c | 6 +- tools/objtool/arch/x86/decode.c | 68 +- tools/objtool/arch/x86/special.c | 2 +- tools/objtool/builtin-check.c | 70 +- tools/objtool/builtin-klp.c | 53 + tools/objtool/check.c | 690 +++++--- tools/objtool/elf.c | 812 ++++++--- tools/objtool/include/objtool/arch.h | 5 +- tools/objtool/include/objtool/builtin.h | 6 +- tools/objtool/include/objtool/check.h | 6 +- tools/objtool/include/objtool/checksum.h | 43 + .../objtool/include/objtool/checksum_types.h | 25 + tools/objtool/include/objtool/elf.h | 186 +- tools/objtool/include/objtool/klp.h | 35 + tools/objtool/include/objtool/objtool.h | 6 +- tools/objtool/include/objtool/warn.h | 40 + tools/objtool/klp-diff.c | 1504 +++++++++++++++++ tools/objtool/klp-post-link.c | 165 ++ tools/objtool/objtool.c | 42 +- tools/objtool/orc_gen.c | 8 +- tools/objtool/special.c | 4 +- tools/objtool/sync-check.sh | 1 + tools/objtool/weak.c | 7 + 61 files changed, 4687 insertions(+), 728 deletions(-) create mode 100644 include/linux/livepatch_external.h create mode 100644 include/linux/livepatch_helpers.h create mode 100755 scripts/livepatch/fix-patch-lines create mode 100644 scripts/livepatch/init.c create mode 100755 scripts/livepatch/klp-build create mode 100644 tools/include/linux/livepatch_external.h create mode 100644 tools/objtool/builtin-klp.c create mode 100644 tools/objtool/include/objtool/checksum.h create mode 100644 tools/objtool/include/objtool/checksum_types.h create mode 100644 tools/objtool/include/objtool/klp.h create mode 100644 tools/objtool/klp-diff.c create mode 100644 tools/objtool/klp-post-link.c
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 1/62 s390/vmlinux.lds.S: Prevent thunk functions from getting placed with normal text
The s390 indirect thunks are placed in the .text.__s390_indirect_jump_* sections.
Certain config options which enable -ffunction-sections have a custom version of the TEXT_TEXT macro:
.text.[0-9a-zA-Z_]*
That unintentionally matches the thunk sections, causing them to get grouped with normal text rather than being handled by their intended rule later in the script:
*(.text.*_indirect_*)
Fix that by adding another period to the thunk section names, following the kernel's general convention for distinguishing code-generated text sections from compiler-generated ones.
--- arch/s390/include/asm/nospec-insn.h | 2 +- arch/s390/kernel/vmlinux.lds.S | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-)
arch/s390/include/asm/nospec-insn.h +1 -1
@@ -18,7 +18,7 @@#ifdef CONFIG_EXPOLINE_EXTERN SYM_CODE_START(\name) #else - .pushsection .text.\name,"axG",@progbits,\name,comdat + .pushsection .text..\name,"axG",@progbits,\name,comdat .globl \name .hidden \name .type \name,@function
arch/s390/kernel/vmlinux.lds.S +1 -1
@@ -51,7 +51,7 @@ SECTIONS IRQENTRY_TEXT SOFTIRQENTRY_TEXT FTRACE_HOTPATCH_TRAMPOLINES_TEXT - *(.text.*_indirect_*) + *(.text..*_indirect_*) *(.gnu.warning) . = ALIGN(PAGE_SIZE); _etext = .; /* End of text section */
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 2/62 vmlinux.lds: Unify TEXT_MAIN, DATA_MAIN, and related macros
In reply to Josh PoimboeufTEXT_MAIN, DATA_MAIN and friends are defined differently depending on whether certain config options enable -ffunction-sections and/or -fdata-sections.
There's no technical reason for that beyond voodoo coding. Keeping the separate implementations adds unnecessary complexity, fragments the logic, and increases the risk of subtle bugs.
Unify the macros by using the same input section patterns across all configs.
This is a prerequisite for the upcoming livepatch klp-build tooling which will manually enable -ffunction-sections and -fdata-sections via KCFLAGS.
--- include/asm-generic/vmlinux.lds.h | 40 ++++++++++--------------------- scripts/module.lds.S | 16 ++++--------- 2 files changed, 17 insertions(+), 39 deletions(-)
include/asm-generic/vmlinux.lds.h +12 -28
@@ -87,39 +87,24 @@#define ALIGN_FUNCTION () . = ALIGN(CONFIG_FUNCTION_ALIGNMENT)/* -* LD_DEAD_CODE_DATA_ELIMINATION option enables -fdata-sections, which -* generates .data.identifier sections, which need to be pulled in with -* .data. We don't want to pull in .data..other sections, which Linux -* has defined. Same for text and bss. +* Support -ffunction-sections by matching .text and .text.*, +* but exclude '.text..*'. * -* With LTO_CLANG, the linker also splits sections by default, so we need -* these macros to combine the sections during the final link. -* -* With AUTOFDO_CLANG and PROPELLER_CLANG, by default, the linker splits -* text sections and regroups functions into subsections. -* -* RODATA_MAIN is not used because existing code already defines .rodata.x -* sections to be brought in with rodata. +* Special .text.* sections that are typically grouped separately, such as +* .text.unlikely or .text.hot, must be matched explicitly before invoking +* TEXT_MAIN. */ -#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION ) || defined(CONFIG_LTO_CLANG ) || \ -defined(CONFIG_AUTOFDO_CLANG ) || defined(CONFIG_PROPELLER_CLANG )#define TEXT_MAIN .text .text.[0-9a-zA-Z_]* -#else -#define TEXT_MAIN .text -#endif -#if defined(CONFIG_LD_DEAD_CODE_DATA_ELIMINATION ) || defined(CONFIG_LTO_CLANG ) + +/* +* Support -fdata-sections by matching .data, .data.*, and others, +* but exclude '.data..*'. +*/ #define DATA_MAIN .data .data.[0-9a-zA-Z_]* .data.rel.* .data..L* .data..compoundliteral* .data.$__unnamed_* .data.$L*#define SDATA_MAIN .sdata .sdata.[0-9a-zA-Z_]*#define RODATA_MAIN .rodata .rodata.[0-9a-zA-Z_]* .rodata..L*#define BSS_MAIN .bss .bss.[0-9a-zA-Z_]* .bss..L* .bss..compoundliteral*#define SBSS_MAIN .sbss .sbss.[0-9a-zA-Z_]* -#else -#define DATA_MAIN .data .data.rel .data.rel.local -#define SDATA_MAIN .sdata -#define RODATA_MAIN .rodata -#define BSS_MAIN .bss -#define SBSS_MAIN .sbss -#endif /* * GCC 4.5 and later have a 32 bytes section alignment for structures.
@@ -580,9 +565,8 @@ defined(CONFIG_AUTOFDO_CLANG) || defined(CONFIG_PROPELLER_CLANG)* during second ld run in second ld pass when generating System.map * * TEXT_MAIN here will match symbols with a fixed pattern (for example, -* .text.hot or .text.unlikely) if dead code elimination or -* function-section is enabled. Match these symbols first before -* TEXT_MAIN to ensure they are grouped together. +* .text.hot or .text.unlikely). Match those before TEXT_MAIN to ensure +* they get grouped together. * * Also placing .text.hot section at the beginning of a page, this * would help the TLB performance.
scripts/module.lds.S +5 -11
@@ -38,12 +38,10 @@ SECTIONS { __kcfi_traps : { KEEP(*(.kcfi_traps)) } #endif -#ifdef CONFIG_LTO_CLANG - /* - * With CONFIG_LTO_CLANG, LLD always enables -fdata-sections and - * -ffunction-sections, which increases the size of the final module. - * Merge the split sections in the final binary. - */ + .text : { + *(.text .text.[0-9a-zA-Z_]*) + } + .bss : { *(.bss .bss.[0-9a-zA-Z_]*) *(.bss..L*)
@@ -59,11 +57,7 @@ SECTIONS { *(.rodata .rodata.[0-9a-zA-Z_]*) *(.rodata..L*) } -#else - .data : { - MOD_CODETAG_SECTIONS() - } -#endif + MOD_SEPARATE_CODETAG_SECTIONS() }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 3/62 x86/module: Improve relocation error messages
In reply to Josh PoimboeufAdd the section number and reloc index to relocation error messages to help find the faulty relocation.
--- arch/x86/kernel/module.c | 15 +++++++++------ kernel/livepatch/core.c | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-)
arch/x86/kernel/module.c +9 -6
@@ -97,6 +97,7 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,DEBUGP ("%s relocate section %u to %u\n" , apply ?"Applying" :"Clearing" , relsec, sechdrs[relsec].sh_info); +for (i =0 ; i < sechdrs[relsec].sh_size /sizeof (*rel); i++) {size_t size;
@@ -162,15 +163,17 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,if (apply) {if (memcmp (loc, &zero, size)) { -pr_err ("x86/modules: Invalid relocation target, existing value is nonzero for type %d, loc %p, val %Lx\n" , - (int )ELF64_R_TYPE (rel[i].r_info), loc, val); +pr_err ("x86/modules: Invalid relocation target, existing value is nonzero for sec %u, idx %u, type %d, loc %lx, val %llx\n" , + relsec, i, (int )ELF64_R_TYPE (rel[i].r_info), + (unsigned long )loc, val);return -ENOEXEC ; }write (loc, &val, size); }else {if (memcmp (loc, &val, size)) { -pr_warn ("x86/modules: Invalid relocation target, existing value does not match expected value for type %d, loc %p, val %Lx\n" , - (int )ELF64_R_TYPE (rel[i].r_info), loc, val); +pr_warn ("x86/modules: Invalid relocation target, existing value does not match expected value for sec %u, idx %u, type %d, loc %lx, val %llx\n" , + relsec, i, (int )ELF64_R_TYPE (rel[i].r_info), + (unsigned long )loc, val);return -ENOEXEC ; }write (loc, &zero, size);
@@ -179,8 +182,8 @@ static int __write_relocate_add(Elf64_Shdr *sechdrs,return 0 ; overflow: -pr_err ("overflow in relocation type %d val %Lx\n" , - (int )ELF64_R_TYPE (rel[i].r_info), val); +pr_err ("overflow in relocation type %d val %llx sec %u idx %d\n" , + (int )ELF64_R_TYPE (rel[i].r_info), val, relsec, i);pr_err ("`%s' likely not compiled with -mcmodel=kernel\n" , me->name);return -ENOEXEC ;
kernel/livepatch/core.c +2 -2
@@ -217,8 +217,8 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,for (i =0 ; i < relasec->sh_size /sizeof (Elf_Rela); i++) { sym = (Elf_Sym *)sechdrs[symndx].sh_addr +ELF_R_SYM (relas[i].r_info);if (sym->st_shndx !=SHN_LIVEPATCH ) { -pr_err ("symbol %s is not marked as a livepatch symbol\n" , - strtab + sym->st_name); +pr_err ("symbol %s at rela sec %u idx %d is not marked as a livepatch symbol\n" , + strtab + sym->st_name, symndx, i);return -EINVAL ; }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 4/62 x86/kprobes: Remove STACK_FRAME_NON_STANDARD annotation
In reply to Josh PoimboeufSince commit 877b145f0f47 ("x86/kprobes: Move trampoline code into RODATA"), the optprobe template code is no longer analyzed by objtool so it doesn't need to be ignored.
--- arch/x86/kernel/kprobes/opt.c | 4 ---- 1 file changed, 4 deletions(-)
arch/x86/kernel/kprobes/opt.c +0 -4
@@ -103,7 +103,6 @@ static void synthesize_set_arg1(kprobe_opcode_t *addr, unsigned long val) asm ( ".pushsection .rodata\n" - "optprobe_template_func:\n" ".global optprobe_template_entry\n" "optprobe_template_entry:\n"#ifdef CONFIG_X86_64
@@ -160,9 +159,6 @@ asm ("optprobe_template_end:\n" ".popsection\n"); -void optprobe_template_func (void ); -STACK_FRAME_NON_STANDARD (optprobe_template_func); -#define TMPL_CLAC_IDX \ ((long)optprobe_template_clac - (long)optprobe_template_entry)#define TMPL_MOVE_IDX \
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 5/62 compiler: Tweak __UNIQUE_ID() naming
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, add an underscore between the name and the counter. This will make it possible for objtool to distinguish between the non-unique and unique parts of the symbol name so it can properly correlate the symbols.
--- include/linux/compiler.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-)
include/linux/compiler.h +5 -1
@@ -163,7 +163,11 @@ void ftrace_likely_update(struct ftrace_likely_data *f, int val, __asm__ ("" : "=r" (var) : "0" (var))#endif -#define __UNIQUE_ID (prefix) __PASTE(__PASTE(__UNIQUE_ID_, prefix), __COUNTER__) +/* Format: __UNIQUE_ID_<name>_<__COUNTER__> */ +#define __UNIQUE_ID (name) \ + __PASTE(__UNIQUE_ID_, \ + __PASTE(name, \ + __PASTE(_, __COUNTER__)))/** * data_race - mark an expression as containing intentional data races
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 6/62 compiler.h: Make addressable symbols less of an eyesore
In reply to Josh PoimboeufAvoid underscore overload by changing:
__UNIQUE_ID___addressable_loops_per_jiffy_868
to the following:
__UNIQUE_ID_addressable_loops_per_jiffy_868
This matches the format used by other __UNIQUE_ID()-generated symbols and improves readability for those who stare at ELF symbol table dumps.
--- include/linux/compiler.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
include/linux/compiler.h +1 -1
@@ -287,7 +287,7 @@ static inline void *offset_to_ptr(const int *off)*/ #define ___ADDRESSABLE (sym, __attrs) \ static void * __used __attrs \ - __UNIQUE_ID(__PASTE(__addressable_,sym)) = (void *)(uintptr_t)&sym; + __UNIQUE_ID(__PASTE(addressable_,sym)) = (void *)(uintptr_t)&sym;#define __ADDRESSABLE (sym) \ ___ADDRESSABLE(sym, __section(".discard.addressable"))
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 7/62 elfnote: Change ELFNOTE() to use __UNIQUE_ID()
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, replace the custom unique symbol name generation in ELFNOTE() with __UNIQUE_ID().
This standardizes the naming format for all "unique" symbols, which will allow objtool to properly correlate them. Note this also removes the "one ELF note per line" limitation.
--- include/linux/elfnote.h | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-)
include/linux/elfnote.h +5 -8
@@ -60,23 +60,21 @@#else /* !__ASSEMBLER__ */ #include <uapi/linux/elf.h> +#include <linux/compiler.h> /* * Use an anonymous structure which matches the shape of * Elf{32,64}_Nhdr, but includes the name and desc data. The size and * type of name and desc depend on the macro arguments. "name" must -* be a literal string, and "desc" must be passed by value. You may -* only define one note per line, since __LINE__ is used to generate -* unique symbols. +* be a literal string, and "desc" must be passed by value. */ -#define _ELFNOTE_PASTE (a,b) a##b -#define _ELFNOTE (size, name, unique, type, desc) \ +#define ELFNOTE (size, name, type, desc) \ static const struct { \ struct elf##size##_note _nhdr; \ unsigned char _name[sizeof(name)] \ __attribute__((aligned(sizeof(Elf##size##_Word)))); \ typeof(desc) _desc \ __attribute__((aligned(sizeof(Elf##size##_Word)))); \ - } _ELFNOTE_PASTE(_note_, unique) \ + } __UNIQUE_ID(note) \ __used \ __attribute__((section(".note." name), \ aligned(sizeof(Elf##size##_Word)), \
@@ -89,11 +87,10 @@ name, \ desc \ } -#define ELFNOTE (size, name, type, desc) \ - _ELFNOTE(size, name, __LINE__, type, desc)#define ELFNOTE32 (name, type, desc) ELFNOTE(32, name, type, desc)#define ELFNOTE64 (name, type, desc) ELFNOTE(64, name, type, desc) +#endif /* __ASSEMBLER__ */ #endif /* _LINUX_ELFNOTE_H */
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 8/62 kbuild: Remove 'kmod_' prefix from __KBUILD_MODNAME
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, remove the arbitrary 'kmod_' prefix from __KBUILD_MODNAME and instead add it explicitly in the __initcall_id() macro.
This change supports the standardization of "unique" symbol naming by ensuring the non-unique portion of the name comes before the unique part. That will enable objtool to properly correlate symbols across builds.
--- include/linux/init.h | 3 ++- scripts/Makefile.lib | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
include/linux/init.h +2 -1
@@ -206,12 +206,13 @@ extern struct module __this_module;/* Format: <modname>__<counter>_<line>_<fn> */ #define __initcall_id (fn) \ + __PASTE(kmod_, \ __PASTE(__KBUILD_MODNAME, \ __PASTE(__, \ __PASTE(__COUNTER__, \ __PASTE(_, \ __PASTE(__LINE__, \ - __PASTE(_, fn)))))) + __PASTE(_, fn)))))))/* Format: __<prefix>__<iid><id> */ #define __initcall_name (prefix, __iid, id) \
scripts/Makefile.lib +1 -1
@@ -104,7 +104,7 @@ name-fix-token = $(subst $(comma),_,$(subst -,_,$1)) name-fix = $(call stringify,$(call name-fix-token,$1)) basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget)) modname_flags = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \ - -D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname)) + -D__KBUILD_MODNAME=$(call name-fix-token,$(modname)) modfile_flags = -DKBUILD_MODFILE=$(call stringify,$(modfile)) _c_flags = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 9/62 modpost: Ignore unresolved section bounds symbols
In reply to Josh PoimboeufIn preparation for klp-build livepatch module creation tooling, suppress warnings for unresolved references to linker-generated __start_* and __stop_* section bounds symbols.
These symbols are expected to be undefined when modpost runs, as they're created later by the linker.
--- scripts/mod/modpost.c | 5 +++++ 1 file changed, 5 insertions(+)
scripts/mod/modpost.c +5 -0
@@ -604,6 +604,11 @@ static int ignore_undef_symbol(struct elf_info *info, const char *symname) strstarts(symname, "_savevr_") || strcmp(symname, ".TOC.") == 0)return 1 ; + +/* ignore linker-created section bounds variables */ +if (strstarts (symname,"__start_" ) ||strstarts (symname,"__stop_" )) +return 1 ; +/* Do not ignore this symbol */ return 0 ; }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 10/62 x86/alternative: Refactor INT3 call emulation selftest
In reply to Josh PoimboeufThe INT3 call emulation selftest is a bit fragile as it relies on the compiler not inserting any extra instructions before the int3_selftest_ip() definition.
Also, the int3_selftest_ip() symbol overlaps with the int3_selftest symbol(), which can confuse objtool.
Fix those issues by slightly reworking the functionality and moving int3_selftest_ip() to a separate asm function. While at it, improve the naming.
--- arch/x86/kernel/alternative.c | 51 +++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 23 deletions(-)
arch/x86/kernel/alternative.c +28 -23
@@ -1899,21 +1899,34 @@ int alternatives_text_reserved(void *start, void *end)* See entry_{32,64}.S for more details. */ -/* -* We define the int3_magic() function in assembly to control the calling -* convention such that we can 'call' it from assembly. -*/ - -extern void int3_magic (unsigned int *ptr);/* defined in asm */ +extern void int3_selftest_asm (unsigned int *ptr); asm (" .pushsection .init.text, \"ax\", @progbits\n" -" .type int3_magic, @function\n" -"int3_magic:\n" +" .type int3_selftest_asm, @function\n" +"int3_selftest_asm:\n" ANNOTATE_NOENDBR -" movl $1, (%" _ASM_ARG1")\n" +/* +* INT3 padded with NOP to CALL_INSN_SIZE. The INT3 triggers an +* exception, then the int3_exception_nb notifier emulates a call to +* int3_selftest_callee(). +*/ +" int3; nop; nop; nop; nop\n" ASM_RET -" .size int3_magic, .-int3_magic\n" +" .size int3_selftest_asm, . - int3_selftest_asm\n" +" .popsection\n" +); + +extern void int3_selftest_callee (unsigned int *ptr); + +asm ( +" .pushsection .init.text, \"ax\", @progbits\n" +" .type int3_selftest_callee, @function\n" +"int3_selftest_callee:\n" +ANNOTATE_NOENDBR +" movl $0x1234, (%" _ASM_ARG1")\n" +ASM_RET +" .size int3_selftest_callee, . - int3_selftest_callee\n" " .popsection\n" );
@@ -1922,7 +1935,7 @@ extern void int3_selftest_ip(void); /* defined in asm below */static int __init int3_exception_notify (struct notifier_block *self,unsigned long val,void *data) { -unsigned long selftest = (unsigned long )&int3_selftest_ip; +unsigned long selftest = (unsigned long )&int3_selftest_asm;struct die_args *args = data;struct pt_regs *regs = args->regs;
@@ -1937,7 +1950,7 @@ int3_exception_notify(struct notifier_block *self, unsigned long val, void *dataif (regs->ip -INT3_INSN_SIZE != selftest)return NOTIFY_DONE ; -int3_emulate_call (regs, (unsigned long )&int3_magic); +int3_emulate_call (regs, (unsigned long )&int3_selftest_callee);return NOTIFY_STOP ; }
@@ -1953,19 +1966,11 @@ static noinline void __init int3_selftest(void)BUG_ON (register_die_notifier (&int3_exception_nb));/* -* Basically: int3_magic(&val); but really complicated :-) -* -* INT3 padded with NOP to CALL_INSN_SIZE. The int3_exception_nb -* notifier above will emulate CALL for us. +* Basically: int3_selftest_callee(&val); but really complicated :-) */ - asmvolatile ("int3_selftest_ip:\n\t" -ANNOTATE_NOENDBR -" int3; nop; nop; nop; nop\n\t" - : ASM_CALL_CONSTRAINT - : __ASM_SEL_RAW(a, D) (&val) - : "memory"); +int3_selftest_asm (&val); -BUG_ON (val !=1 ); +BUG_ON (val !=0x1234 );unregister_die_notifier (&int3_exception_nb); }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 11/62 objtool: Make find_symbol_containing() less arbitrary
In reply to Josh PoimboeufIn the rare case of overlapping symbols, find_symbol_containing() just returns the first one it finds. Make it slightly less arbitrary by returning the smallest symbol with size > 0.
--- tools/objtool/elf.c | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-)
tools/objtool/elf.c +20 -5
@@ -193,14 +193,29 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)struct symbol *find_symbol_containing (const struct section *sec,unsigned long offset) {struct rb_root_cached *tree = (struct rb_root_cached *)&sec->symbol_tree; -struct symbol *iter; +struct symbol *sym =NULL , *tmp; -__sym_for_each (iter, tree, offset, offset) { -if (iter->type !=STT_SECTION ) -return iter; +__sym_for_each (tmp, tree, offset, offset) { +if (tmp->len) { +if (!sym) { + sym = tmp; +continue ; + } + +if (sym->offset != tmp->offset || sym->len != tmp->len) { +/* +* In the rare case of overlapping symbols, +* pick the smaller one. +* +* TODO: outlaw overlapping symbols +*/ +if (tmp->len < sym->len) + sym = tmp; + } + } } -return NULL ; +return sym; }/*
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 12/62 objtool: Speed up SHT_GROUP reindexing
In reply to Josh PoimboeufAfter elf_update_group_sh_info() was introduced, a prototype version of "objtool klp diff" went from taking ~1s to several minutes, due to looping almost endlessly in elf_update_group_sh_info() while creating thousands of local symbols in a file with thousands of sections.
Dramatically improve the performance by marking all symbols' correlated SHT_GROUP sections while reading the object. That way there's no need to search for it every time a symbol gets reindexed.
--- tools/objtool/elf.c | 47 ++++++++++++++++++----------- tools/objtool/include/objtool/elf.h | 1 + 2 files changed, 30 insertions(+), 18 deletions(-)
tools/objtool/elf.c +29 -18
@@ -587,28 +587,32 @@ static int read_symbols(struct elf *elf)return -1 ; } -/* -* @sym's idx has changed. Update the sh_info in group sections. -*/ -static void elf_update_group_sh_info (struct elf *elf,Elf32_Word symtab_idx, -Elf32_Word new_idx,Elf32_Word old_idx) +static int mark_group_syms (struct elf *elf) { -struct section *sec; +struct section *symtab, *sec; +struct symbol *sym; + + symtab =find_section_by_name (elf,".symtab" ); +if (!symtab) { +ERROR ("no .symtab" ); +return -1 ; + }list_for_each_entry (sec, &elf->sections, list) { -if (sec->sh.sh_type !=SHT_GROUP ) -continue ; -if (sec->sh.sh_link == symtab_idx && - sec->sh.sh_info == old_idx) { - sec->sh.sh_info = new_idx; -mark_sec_changed (elf, sec, true); -/* -* Each ELF group should have a unique symbol key. -* Return early on match. -*/ -return ; +if (sec->sh.sh_type ==SHT_GROUP && + sec->sh.sh_link == symtab->idx) { + sym =find_symbol_by_index (elf, sec->sh.sh_info); +if (!sym) { +ERROR ("%s: can't find SHT_GROUP signature symbol" , + sec->name); +return -1 ; + } + + sym->group_sec = sec; } } + +return 0 ; }/*
@@ -802,7 +806,11 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)if (elf_update_sym_relocs (elf, old))return NULL ; -elf_update_group_sh_info (elf, symtab->idx, new_idx, first_non_local); +if (old->group_sec) { + old->group_sec->sh.sh_info = new_idx; +mark_sec_changed (elf, old->group_sec, true); + } + new_idx = first_non_local; }
@@ -1075,6 +1083,9 @@ struct elf *elf_open_read(const char *name, int flags)if (read_symbols (elf)) goto err; +if (mark_group_syms (elf)) + goto err; +if (read_relocs (elf)) goto err;
tools/objtool/include/objtool/elf.h +1 -0
@@ -72,6 +72,7 @@ struct symbol { u8 ignore : 1;struct list_head pv_target;struct reloc *relocs; +struct section *group_sec; };struct reloc {
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 13/62 objtool: Fix broken error handling in read_symbols()
In reply to Josh PoimboeufThe free(sym) call in the read_symbols() error path is fundamentally
broken: 'sym' doesn't point to any allocated block. If triggered, things would go from bad to worse.
Remove the free() and simplify the error paths. Freeing memory isn't necessary here anyway, these are fatal errors which lead to an immediate exit().
--- tools/objtool/elf.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-)
tools/objtool/elf.c +3 -7
@@ -492,14 +492,14 @@ static int read_symbols(struct elf *elf)if (!gelf_getsymshndx (symtab->data, shndx_data, i, &sym->sym, &shndx)) {ERROR_ELF ("gelf_getsymshndx" ); - goto err; +return -1 ; } sym->name =elf_strptr (elf->elf, symtab->sh.sh_link, sym->sym.st_name);if (!sym->name) {ERROR_ELF ("elf_strptr" ); - goto err; +return -1 ; } if ((sym->sym.st_shndx > SHN_UNDEF &&
@@ -511,7 +511,7 @@ static int read_symbols(struct elf *elf) sym->sec =find_section_by_index (elf, shndx);if (!sym->sec) {ERROR ("couldn't find section for symbol %s" , sym->name); - goto err; +return -1 ; }if (GELF_ST_TYPE (sym->sym.st_info) ==STT_SECTION ) { sym->name = sym->sec->name;
@@ -581,10 +581,6 @@ static int read_symbols(struct elf *elf) }return 0 ; - -err: -free (sym); -return -1 ; }static int mark_group_syms (struct elf *elf)
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 14/62 objtool: Propagate elf_truncate_section() error in elf_write()
In reply to Josh PoimboeufProperly check and propagate the return value of elf_truncate_section() to avoid silent failures.
--- tools/objtool/elf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
tools/objtool/elf.c +2 -3
@@ -1307,7 +1307,6 @@ static int elf_truncate_section(struct elf *elf, struct section *sec)for (;;) {/* get next data descriptor for the relevant section */ data =elf_getdata (s, data); -if (!data) {if (size) {ERROR ("end of section data but non-zero size left\n" );
@@ -1343,8 +1342,8 @@ int elf_write(struct elf *elf)/* Update changed relocation sections and section headers: */ list_for_each_entry (sec, &elf->sections, list) { -if (sec->truncate) -elf_truncate_section (elf, sec); +if (sec->truncate &&elf_truncate_section (elf, sec)) +return -1 ;if (sec_changed (sec)) { s =elf_getscn (elf->elf, sec->idx);
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 15/62 objtool: Add empty symbols to the symbol tree again
In reply to Josh PoimboeufThe following commit
5da6aea375cd ("objtool: Fix find_{symbol,func}_containing()")
fixed the issue where overlapping symbols weren't getting sorted properly in the symbol tree. Therefore the workaround to skip adding empty symbols from the following commit
a2e38dffcd93 ("objtool: Don't add empty symbols to the rbtree")
is no longer needed.
--- tools/objtool/elf.c | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-)
tools/objtool/elf.c +2 -8
@@ -96,7 +96,8 @@ static inline unsigned long __sym_last(struct symbol *s) } INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last, - __sym_start, __sym_last, static, __sym) + __sym_start, __sym_last, static inline __maybe_unused, + __sym)#define __sym_for_each (_iter, _tree, _start, _end) \ for (_iter = __sym_iter_first((_tree), (_start), (_end)); \
@@ -440,13 +441,6 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)list_add (&sym->list, entry);elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name)); - -/* -* Don't store empty STT_NOTYPE symbols in the rbtree. They -* can exist within a function, confusing the sorting. -*/ -if (!sym->len) -__sym_remove (sym, &sym->sec->symbol_tree); }static int read_symbols (struct elf *elf)
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 16/62 objtool: Fix interval tree insertion for zero-length symbols
In reply to Josh PoimboeufZero-length symbols get inserted in the wrong spot. Fix that.
--- tools/objtool/elf.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
tools/objtool/elf.c +1 -1
@@ -92,7 +92,7 @@ static inline unsigned long __sym_start(struct symbol *s)static inline unsigned long __sym_last (struct symbol *s) { -return s->offset + s->len -1 ; +return s->offset + (s->len ? s->len -1 :0 ); } INTERVAL_TREE_DEFINE(struct symbol, node, unsigned long, __subtree_last,
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 17/62 objtool: Fix weak symbol detection
In reply to Josh Poimboeuffind_symbol_hole_containing() fails to find a symbol hole (aka stripped weak symbol) if its section has no symbols before the hole. This breaks weak symbol detection if -ffunction-sections is enabled.
Fix that by allowing the interval tree to contain section symbols, which are always at offset zero for a given section.
Fixes a bunch of (-ffunction-sections) warnings like:
vmlinux.o: warning: objtool: .text.__x64_sys_io_setup+0x10: unreachable instruction
--- tools/include/linux/interval_tree_generic.h | 2 +- tools/objtool/elf.c | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-)
tools/include/linux/interval_tree_generic.h +1 -1
@@ -77,7 +77,7 @@ ITSTATIC void ITPREFIX ## _remove(ITSTRUCT *node, \* Cond2: start <= ITLAST(node) \ */ \ \ -static ITSTRUCT * \ +ITSTATIC ITSTRUCT * \ ITPREFIX ## _subtree_search(ITSTRUCT *node, ITTYPE start, ITTYPE last) \ { \while (true) { \
tools/objtool/elf.c +4 -4
@@ -109,7 +109,7 @@ struct symbol_hole { };/* -* Find !section symbol where @offset is after it. +* Find the last symbol before @offset. */ static int symbol_hole_by_offset (const void *key,const struct rb_node *node) {
@@ -120,8 +120,7 @@ static int symbol_hole_by_offset(const void *key, const struct rb_node *node)return -1 ;if (sh->key >= s->offset + s->len) { -if (s->type !=STT_SECTION ) - sh->sym = s; + sh->sym = s;return 1 ; }
@@ -428,7 +427,8 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym) sym->len = sym->sym.st_size;__sym_for_each (iter, &sym->sec->symbol_tree, sym->offset, sym->offset) { -if (iter->offset == sym->offset && iter->type == sym->type) +if (iter->offset == sym->offset && iter->type == sym->type && + iter->len == sym->len) iter->alias = sym; }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 18/62 objtool: Fix x86 addend calculation
In reply to Josh PoimboeufOn x86, arch_dest_reloc_offset() hardcodes the addend adjustment to four, but the actual adjustment depends on the relocation type. Fix that.
--- tools/objtool/arch/loongarch/decode.c | 4 ++-- tools/objtool/arch/powerpc/decode.c | 4 ++-- tools/objtool/arch/x86/decode.c | 15 +++++++++++++-- tools/objtool/check.c | 13 ++++--------- tools/objtool/include/objtool/arch.h | 2 +- 5 files changed, 22 insertions(+), 16 deletions(-)
tools/objtool/arch/loongarch/decode.c +2 -2
@@ -17,9 +17,9 @@ unsigned long arch_jump_destination(struct instruction *insn)return insn->offset + (insn->immediate <<2 ); } -unsigned long arch_dest_reloc_offset (int addend) +s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc) { -return addend; +return reloc_addend (reloc); }bool arch_pc_relative_reloc (struct reloc *reloc)
tools/objtool/arch/powerpc/decode.c +2 -2
@@ -14,9 +14,9 @@ int arch_ftrace_match(char *name)return !strcmp (name,"_mcount" ); } -unsigned long arch_dest_reloc_offset (int addend) +s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc) { -return addend; +return reloc_addend (reloc); }bool arch_callee_saved_reg (unsigned char reg)
tools/objtool/arch/x86/decode.c +13 -2
@@ -68,9 +68,20 @@ bool arch_callee_saved_reg(unsigned char reg) } } -unsigned long arch_dest_reloc_offset (int addend) +s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc) { -return addend +4 ; +s64 addend =reloc_addend (reloc); + +switch (reloc_type (reloc)) { +case R_X86_64_PC32 : +case R_X86_64_PLT32 : + addend += insn->offset + insn->len -reloc_offset (reloc); +break ; +default : +break ; + } + +return addend; }unsigned long arch_jump_destination (struct instruction *insn)
tools/objtool/check.c +4 -9
@@ -1502,7 +1502,7 @@ static int add_jump_destinations(struct objtool_file *file) dest_off =arch_jump_destination (insn); }else if (reloc->sym->type ==STT_SECTION ) { dest_sec = reloc->sym->sec; - dest_off =arch_dest_reloc_offset (reloc_addend (reloc)); + dest_off =arch_insn_adjusted_addend (insn, reloc); }else if (reloc->sym->retpoline_thunk) { ret =add_retpoline_call (file, insn);if (ret)
@@ -1672,7 +1672,7 @@ static int add_call_destinations(struct objtool_file *file) } }else if (reloc->sym->type ==STT_SECTION ) { - dest_off =arch_dest_reloc_offset (reloc_addend (reloc)); + dest_off =arch_insn_adjusted_addend (insn, reloc); dest =find_call_destination (reloc->sym->sec, dest_off);if (!dest) { ERROR_INSN(insn, "can't find call dest symbol at %s+0x%lx",
@@ -3348,7 +3348,7 @@ static bool pv_call_dest(struct objtool_file *file, struct instruction *insn)if (!reloc ||strcmp (reloc->sym->name,"pv_ops" ))return false; - idx = (arch_dest_reloc_offset (reloc_addend (reloc)) /sizeof (void *)); + idx = (arch_insn_adjusted_addend (insn, reloc) /sizeof (void *));if (file->pv_ops[idx].clean)return true;
@@ -4396,12 +4396,7 @@ static int validate_ibt_insn(struct objtool_file *file, struct instruction *insn reloc_offset(reloc) + 1, (insn->offset + insn->len) - (reloc_offset(reloc) + 1))) { - off = reloc->sym->offset; -if (reloc_type (reloc) ==R_X86_64_PC32 || -reloc_type (reloc) ==R_X86_64_PLT32 ) - off +=arch_dest_reloc_offset (reloc_addend (reloc)); -else - off +=reloc_addend (reloc); + off = reloc->sym->offset +arch_insn_adjusted_addend (insn, reloc); dest =find_insn (file, reloc->sym->sec, off);if (!dest)
tools/objtool/include/objtool/arch.h +1 -1
@@ -83,7 +83,7 @@ bool arch_callee_saved_reg(unsigned char reg);unsigned long arch_jump_destination (struct instruction *insn); -unsigned long arch_dest_reloc_offset (int addend); +s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc);const char *arch_nop_insn (int len);const char *arch_ret_insn (int len);
Signature · 1 line
2.49.0
Peter Zijlstra
maintainerRe: [PATCH v2 18/62] objtool: Fix x86 addend calculation
Quoted 10 lines · Josh Poimboeuf
On x86,
arch_dest_reloc_offset()hardcodes the addend adjustment to four, but the actual adjustment depends on the relocation type. Fix that.tools/objtool/arch/x86/decode.c +4
-1+s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc) { -return addend +4 ; +s64 addend =reloc_addend (reloc); + +switch (reloc_type (reloc)) {
tools/objtool/arch/x86/decode.c bool arch_callee_saved_reg(unsigned char reg) · line 85
Quoted 10 lines
tools/objtool/arch/x86/decode.c +9
-0+case R_X86_64_PC32 : +case R_X86_64_PLT32 : + addend += insn->offset + insn->len -reloc_offset (reloc); +break ; +default : +break ; + } + +return addend; }
Should this not be something like:
s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)
{
s64 addend = reloc_addend(reloc);
if (arch_pc_relative_reloc(reloc))
addend += insn->offset + insn->len - reloc_offset(reloc);
return addend;
}instead?
AFAIU arch_pc_relative_reloc() is the exact same set of relocations.
Josh Poimboeuf
maintainerRe: [PATCH v2 18/62] objtool: Fix x86 addend calculation
Quoted 34 lines · Peter Zijlstra
Quoted 10 lines · Josh Poimboeuf
On x86,
arch_dest_reloc_offset()hardcodes the addend adjustment to four, but the actual adjustment depends on the relocation type. Fix that.tools/objtool/arch/x86/decode.c +4
-1+s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc) { -return addend +4 ; +s64 addend =reloc_addend (reloc); + +switch (reloc_type (reloc)) {tools/objtool/arch/x86/decode.c bool arch_callee_saved_reg(unsigned char reg) · line 85
Quoted 10 lines
tools/objtool/arch/x86/decode.c +9
-0+case R_X86_64_PC32 : +case R_X86_64_PLT32 : + addend += insn->offset + insn->len -reloc_offset (reloc); +break ; +default : +break ; + } + +return addend; }Should this not be something like:
s64
arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc){ s64 addend = reloc_addend(reloc); if (arch_pc_relative_reloc(reloc)) addend += insn->offset + insn->len - reloc_offset(reloc); return addend; }
Quoted 3 lines
instead?
AFAIU
arch_pc_relative_reloc()is the exact same set of relocations.
Yeah that's better, thanks.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 19/62 objtool: Fix __pa_symbol() relocation handling
In reply to Josh Poimboeuf__pa_symbol() generates a relocation which refers to a physical address. Convert it to back its virtual form before calculating the addend.
--- tools/objtool/arch/x86/decode.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-)
tools/objtool/arch/x86/decode.c +12 -1
@@ -68,6 +68,17 @@ bool arch_callee_saved_reg(unsigned char reg) } } +/* Undo the effects of __pa_symbol() if necessary */ +static unsigned long phys_to_virt (unsigned long pa) +{ +s64 va = pa; + +if (va >0 ) + va &= ~(0x80000000 ); + +return va; +} +s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc) {s64 addend =reloc_addend (reloc);
@@ -81,7 +92,7 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)break ; } -return addend; +return phys_to_virt (addend); }unsigned long arch_jump_destination (struct instruction *insn)
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 20/62 objtool: Fix "unexpected end of section" warning for alternatives
In reply to Josh PoimboeufDue to the short circuiting logic in next_insn_to_validate(), control flow may silently transition from .altinstr_replacement to .text without a corresponding nested call to validate_branch().
As a result the validate_branch() 'sec' variable doesn't get reinitialized, which can trigger a confusing "unexpected end of section" warning which blames .altinstr_replacement rather than the offending fallthrough function.
Fix that by not caching the section. There's no point in doing that anyway.
--- tools/objtool/check.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-)
tools/objtool/check.c +1 -4
@@ -3545,15 +3545,12 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, {struct alternative *alt;struct instruction *next_insn, *prev_insn =NULL ; -struct section *sec;u8 visited;int ret;if (func && func->ignore)return 0 ; - sec = insn->sec; -while (1 ) { next_insn =next_insn_to_validate (file, insn);
@@ -3791,7 +3788,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,WARN ("%s%sunexpected end of section %s" , func ? func->name :"" , func ?"(): " :"" , - sec->name); + insn->sec->name);return 1 ; }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 21/62 objtool: Check for missing annotation entries in read_annotate()
In reply to Josh PoimboeufAdd a sanity check to make sure none of the relocations for the .discard.annotate_insn section have gone missing.
--- tools/objtool/check.c | 5 +++++ 1 file changed, 5 insertions(+)
tools/objtool/check.c +5 -0
@@ -2314,6 +2314,11 @@ static int read_annotate(struct objtool_file *file, sec->sh.sh_entsize =8 ; } +if (sec_num_entries (sec) !=sec_num_entries (sec->rsec)) { +ERROR ("bad .discard.annotate_insn section: missing relocs" ); +return -1 ; + } +for_each_reloc (sec->rsec, reloc) { type = *(u32 *)(sec->data->d_buf + (reloc_idx (reloc) * sec->sh.sh_entsize) +4 );
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 22/62 objtool: Const string cleanup
In reply to Josh PoimboeufUse 'const char *' where applicable.
--- tools/objtool/arch/loongarch/decode.c | 2 +- tools/objtool/arch/powerpc/decode.c | 2 +- tools/objtool/arch/x86/decode.c | 2 +- tools/objtool/elf.c | 6 +++--- tools/objtool/include/objtool/arch.h | 2 +- tools/objtool/include/objtool/elf.h | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-)
tools/objtool/arch/loongarch/decode.c +1 -1
@@ -7,7 +7,7 @@#include <linux/objtool_types.h> #include <arch/elf.h> -int arch_ftrace_match (char *name) +int arch_ftrace_match (const char *name) {return !strcmp (name,"_mcount" ); }
tools/objtool/arch/powerpc/decode.c +1 -1
@@ -9,7 +9,7 @@#include <objtool/builtin.h> #include <objtool/endianness.h> -int arch_ftrace_match (char *name) +int arch_ftrace_match (const char *name) {return !strcmp (name,"_mcount" ); }
tools/objtool/arch/x86/decode.c +1 -1
@@ -23,7 +23,7 @@#include <objtool/builtin.h> #include <arch/elf.h> -int arch_ftrace_match (char *name) +int arch_ftrace_match (const char *name) {return !strcmp (name,"__fentry__" ); }
tools/objtool/elf.c +3 -3
@@ -853,7 +853,7 @@ elf_create_section_symbol(struct elf *elf, struct section *sec)return sym; } -static int elf_add_string (struct elf *elf,struct section *strtab,char *str); +static int elf_add_string (struct elf *elf,struct section *strtab,const char *str);struct symbol *elf_create_prefix_symbol (struct elf *elf,struct symbol *orig,long size)
@@ -1086,7 +1086,7 @@ struct elf *elf_open_read(const char *name, int flags)return NULL ; } -static int elf_add_string (struct elf *elf,struct section *strtab,char *str) +static int elf_add_string (struct elf *elf,struct section *strtab,const char *str) {Elf_Data *data;Elf_Scn *s;
@@ -1111,7 +1111,7 @@ static int elf_add_string(struct elf *elf, struct section *strtab, char *str)return -1 ; } - data->d_buf = str; + data->d_buf =strdup (str); data->d_size =strlen (str) +1 ; data->d_align =1 ;
tools/objtool/include/objtool/arch.h +1 -1
@@ -71,7 +71,7 @@ struct stack_op {struct instruction ; -int arch_ftrace_match (char *name); +int arch_ftrace_match (const char *name);void arch_initial_func_cfi_state (struct cfi_init_state *state);
tools/objtool/include/objtool/elf.h +3 -3
@@ -40,7 +40,7 @@ struct section {struct section *base, *rsec;struct symbol *sym;Elf_Data *data; -char *name; +const char *name;int idx;bool _changed, text, rodata, noinstr, init, truncate;struct reloc *relocs;
@@ -53,7 +53,7 @@ struct symbol {struct elf_hash_node name_hash;GElf_Sym sym;struct section *sec; -char *name; +const char *name;unsigned int idx, len;unsigned long offset;unsigned long __subtree_last;
@@ -87,7 +87,7 @@ struct elf {GElf_Ehdr ehdr;int fd;bool changed; -char *name; +const char *name;unsigned int num_files;struct list_head sections;unsigned long num_relocs;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 23/62 objtool: Clean up compiler flag usage
In reply to Josh PoimboeufKBUILD_HOSTCFLAGS and KBUILD_HOSTLDFLAGS aren't defined when objtool is built standalone. Also, the EXTRA_WARNINGS flags are rather arbitrary.
Make things simpler and more consistent by specifying compiler flags explicitly and tweaking the warnings. Also make a few code tweaks to make the new warnings happy.
--- tools/objtool/Makefile | 15 ++++++++++----- tools/objtool/check.c | 4 ++-- tools/objtool/elf.c | 2 +- 3 files changed, 13 insertions(+), 8 deletions(-)
tools/objtool/Makefile +10 -5
@@ -23,6 +23,11 @@ LIBELF_LIBS := $(shell $(HOSTPKG_CONFIG) libelf --libs 2>/dev/null || echo -lel all: $(OBJTOOL) +WARNINGS := -Werror -Wall -Wextra -Wmissing-prototypes \ + -Wmissing-declarations -Wwrite-strings \ + -Wno-implicit-fallthrough -Wno-sign-compare \ + -Wno-unused-parameter + INCLUDES := -I$(srctree)/tools/include \ -I$(srctree)/tools/include/uapi \ -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \
@@ -30,11 +35,11 @@ INCLUDES := -I$(srctree)/tools/include \ -I$(srctree)/tools/objtool/include \ -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \ -I$(LIBSUBCMD_OUTPUT)/include -# Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it -# is passed here to match a legacy behavior. -WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs -OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS) -OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) + +OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g \ + $(WARNINGS) $(INCLUDES) $(LIBELF_FLAGS) $(HOSTCFLAGS) + +OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(HOSTLDFLAGS) # Allow old libelf to be used: elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
tools/objtool/check.c +2 -2
@@ -459,7 +459,7 @@ static int decode_instructions(struct objtool_file *file)for (offset =0 ; offset < sec->sh.sh_size; offset += insn->len) {if (!insns || idx ==INSN_CHUNK_MAX ) { - insns =calloc (sizeof (*insn),INSN_CHUNK_SIZE ); + insns =calloc (INSN_CHUNK_SIZE ,sizeof (*insn));if (!insns) {ERROR_GLIBC ("calloc" );return -1 ;
@@ -608,7 +608,7 @@ static int init_pv_ops(struct objtool_file *file)return 0 ; nr = sym->len /sizeof (unsigned long ); - file->pv_ops =calloc (sizeof (struct pv_state ), nr); + file->pv_ops =calloc (nr,sizeof (struct pv_state ));if (!file->pv_ops) {ERROR_GLIBC ("calloc" );return -1 ;
tools/objtool/elf.c +1 -1
@@ -736,7 +736,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab, }/* setup extended section index magic and write the symbol */ -if ((shndx >=SHN_UNDEF && shndx <SHN_LORESERVE ) || is_special_shndx) { +if (shndx <SHN_LORESERVE || is_special_shndx) { sym->sym.st_shndx = shndx;if (!shndx_data) shndx =0 ;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 24/62 objtool: Remove .parainstructions reference
In reply to Josh PoimboeufThe .parainstructions section no longer exists since the following commit:
60bc276b129e ("x86/paravirt: Switch mixed paravirt/alternative calls to alternatives").
Remove the reference to it.
--- tools/objtool/check.c | 1 - 1 file changed, 1 deletion(-)
tools/objtool/check.c +0 -1
@@ -4468,7 +4468,6 @@ static int validate_ibt(struct objtool_file *file) !strcmp (sec->name,".altinstructions" ) || !strcmp (sec->name,".ibt_endbr_seal" ) || !strcmp (sec->name,".orc_unwind_ip" ) || - !strcmp (sec->name,".parainstructions" ) || !strcmp (sec->name,".retpoline_sites" ) || !strcmp (sec->name,".smp_locks" ) || !strcmp(sec->name, ".static_call_sites") ||
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 25/62 objtool: Convert elf iterator macros to use 'struct elf'
In reply to Josh Poimboeuf'struct objtool_file' is specific to the check code and doesn't belong in the elf code which is supposed to be objtool_file-agnostic. Convert the elf iterator macros to use 'struct elf' instead.
--- tools/objtool/check.c | 22 +++++++++++----------- tools/objtool/include/objtool/elf.h | 8 ++++---- tools/objtool/orc_gen.c | 2 +- 3 files changed, 16 insertions(+), 16 deletions(-)
tools/objtool/check.c +11 -11
@@ -106,7 +106,7 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,#define for_each_insn (file, insn) \ for (struct section *__sec, *__fake = (struct section *)1; \ __fake; __fake = NULL) \ - for_each_sec(file, __sec) \ + for_each_sec(file->elf, __sec) \ sec_for_each_insn(file, __sec, insn)#define func_for_each_insn (file, func, insn) \
@@ -429,7 +429,7 @@ static int decode_instructions(struct objtool_file *file)struct instruction *insn;int ret; -for_each_sec (file, sec) { +for_each_sec (file->elf, sec) {struct instruction *insns =NULL ;u8 prev_len =0 ;u8 idx =0 ;
@@ -859,7 +859,7 @@ static int create_cfi_sections(struct objtool_file *file) } idx =0 ; -for_each_sym (file, sym) { +for_each_sym (file->elf, sym) {if (sym->type !=STT_FUNC )continue ;
@@ -875,7 +875,7 @@ static int create_cfi_sections(struct objtool_file *file)return -1 ; idx =0 ; -for_each_sym (file, sym) { +for_each_sym (file->elf, sym) {if (sym->type !=STT_FUNC )continue ;
@@ -2165,7 +2165,7 @@ static int add_jump_table_alts(struct objtool_file *file)if (!file->rodata)return 0 ; -for_each_sym (file, func) { +for_each_sym (file->elf, func) {if (func->type !=STT_FUNC )continue ;
@@ -2471,7 +2471,7 @@ static int classify_symbols(struct objtool_file *file) {struct symbol *func; -for_each_sym (file, func) { +for_each_sym (file->elf, func) {if (func->type ==STT_NOTYPE &&strstarts (func->name,".L" )) func->local_label = true;
@@ -2516,7 +2516,7 @@ static void mark_rodata(struct objtool_file *file)* * .rodata.str1.* sections are ignored; they don't contain jump tables. */ -for_each_sec (file, sec) { +for_each_sec (file->elf, sec) {if ((!strncmp (sec->name,".rodata" ,7 ) && !strstr (sec->name,".str1." )) || !strncmp (sec->name,".data.rel.ro" ,12 )) {
@@ -4178,7 +4178,7 @@ static int add_prefix_symbols(struct objtool_file *file)struct section *sec;struct symbol *func; -for_each_sec (file, sec) { +for_each_sec (file->elf, sec) {if (!(sec->sh.sh_flags &SHF_EXECINSTR ))continue ;
@@ -4270,7 +4270,7 @@ static int validate_functions(struct objtool_file *file)struct section *sec;int warnings =0 ; -for_each_sec (file, sec) { +for_each_sec (file->elf, sec) {if (!(sec->sh.sh_flags &SHF_EXECINSTR ))continue ;
@@ -4449,7 +4449,7 @@ static int validate_ibt(struct objtool_file *file)for_each_insn (file, insn) warnings +=validate_ibt_insn (file, insn); -for_each_sec (file, sec) { +for_each_sec (file->elf, sec) {/* Already done by validate_ibt_insn() */ if (sec->sh.sh_flags &SHF_EXECINSTR )
@@ -4610,7 +4610,7 @@ static void disas_warned_funcs(struct objtool_file *file)struct symbol *sym;char *funcs =NULL , *tmp; -for_each_sym (file, sym) { +for_each_sym (file->elf, sym) {if (sym->warned) {if (!funcs) { funcs =malloc (strlen (sym->name) +1 );
tools/objtool/include/objtool/elf.h +4 -4
@@ -324,16 +324,16 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) reloc->_sym_next_reloc = (unsigned long )next | bit; } -#define for_each_sec (file, sec) \ - list_for_each_entry(sec, &file->elf->sections, list) +#define for_each_sec (elf, sec) \ + list_for_each_entry(sec, &elf->sections, list)#define sec_for_each_sym (sec, sym) \ list_for_each_entry(sym, &sec->symbol_list, list) -#define for_each_sym (file, sym) \ +#define for_each_sym (elf, sym) \ for (struct section *__sec, *__fake = (struct section *)1; \ __fake; __fake = NULL) \ - for_each_sec(file, __sec) \ + for_each_sec(elf, __sec) \ sec_for_each_sym(__sec, sym)#define for_each_reloc (rsec, reloc) \
tools/objtool/orc_gen.c +1 -1
@@ -57,7 +57,7 @@ int orc_create(struct objtool_file *file)/* Build a deduplicated list of ORC entries: */ INIT_LIST_HEAD (&orc_list); -for_each_sec (file, sec) { +for_each_sec (file->elf, sec) {struct orc_entry orc, prev_orc = {0 };struct instruction *insn;bool empty = true;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 26/62 objtool: Add section/symbol type helpers
In reply to Josh PoimboeufAdd some helper macros to improve readability.
--- tools/objtool/arch/x86/special.c | 2 +- tools/objtool/check.c | 58 ++++++++++++------------- tools/objtool/elf.c | 20 ++++----- tools/objtool/include/objtool/elf.h | 66 +++++++++++++++++++++++++++++ tools/objtool/special.c | 4 +- 5 files changed, 108 insertions(+), 42 deletions(-)
tools/objtool/arch/x86/special.c +1 -1
@@ -89,7 +89,7 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,/* look for a relocation which references .rodata */ text_reloc =find_reloc_by_dest_range (file->elf, insn->sec, insn->offset, insn->len); -if (!text_reloc || text_reloc->sym->type !=STT_SECTION || +if (!text_reloc || !is_sec_sym (text_reloc->sym) || !text_reloc->sym->sec->rodata)return NULL ;
tools/objtool/check.c +29 -29
214 lines not on this page · raw message
tools/objtool/elf.c +10 -10
@@ -170,7 +170,7 @@ struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset)struct symbol *iter;__sym_for_each (iter, tree, offset, offset) { -if (iter->offset == offset && iter->type !=STT_SECTION ) +if (iter->offset == offset && !is_sec_sym (iter))return iter; }
@@ -183,7 +183,7 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset)struct symbol *iter;__sym_for_each (iter, tree, offset, offset) { -if (iter->offset == offset && iter->type ==STT_FUNC ) +if (iter->offset == offset &&is_func_sym (iter))return iter; }
@@ -264,7 +264,7 @@ struct symbol *find_func_containing(struct section *sec, unsigned long offset)struct symbol *iter;__sym_for_each (iter, tree, offset, offset) { -if (iter->type ==STT_FUNC ) +if (is_func_sym (iter))return iter; }
@@ -373,14 +373,14 @@ static int read_sections(struct elf *elf)return -1 ; } -if (sec->sh.sh_size !=0 && !is_dwarf_section (sec)) { +if (sec_size (sec) !=0 && !is_dwarf_section (sec)) { sec->data =elf_getdata (s,NULL );if (!sec->data) {ERROR_ELF ("elf_getdata" );return -1 ; }if (sec->data->d_off !=0 || - sec->data->d_size != sec->sh.sh_size) { + sec->data->d_size !=sec_size (sec)) {ERROR ("unexpected data attributes for %s" , sec->name);return -1 ; }
@@ -420,7 +420,7 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym) sym->type =GELF_ST_TYPE (sym->sym.st_info); sym->bind =GELF_ST_BIND (sym->sym.st_info); -if (sym->type ==STT_FILE ) +if (is_file_sym (sym)) elf->num_files++; sym->offset = sym->sym.st_value;
@@ -527,7 +527,7 @@ static int read_symbols(struct elf *elf)sec_for_each_sym (sec, sym) {char *pname;size_t pnamelen; -if (sym->type !=STT_FUNC ) +if (!is_func_sym (sym))continue ;if (sym->pfunc ==NULL )
@@ -929,7 +929,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec,struct symbol *sym = insn_sec->sym;int addend = insn_off; -if (!(insn_sec->sh.sh_flags &SHF_EXECINSTR )) { +if (!is_text_sec (insn_sec)) {ERROR ("bad call to %s() for data symbol %s" , __func__, sym->name);return NULL ; }
@@ -958,7 +958,7 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec, struct symbol *sym, s64 addend) { -if (sym->sec && (sec->sh.sh_flags &SHF_EXECINSTR )) { + if (is_text_sec(sec)) {ERROR ("bad call to %s() for text symbol %s" , __func__, sym->name);return NULL ; }
@@ -1287,7 +1287,7 @@ int elf_write_insn(struct elf *elf, struct section *sec,*/ static int elf_truncate_section (struct elf *elf,struct section *sec) { -u64 size = sec->sh.sh_size; +u64 size =sec_size (sec);bool truncated = false;Elf_Data *data =NULL ;Elf_Scn *s;
tools/objtool/include/objtool/elf.h +66 -0
89 lines not on this page · raw message
tools/objtool/special.c +2 -2
@@ -142,12 +142,12 @@ int special_get_alts(struct elf *elf, struct list_head *alts)if (!sec)continue ; -if (sec->sh.sh_size % entry->size !=0 ) { +if (sec_size (sec) % entry->size !=0 ) {ERROR ("%s size not a multiple of %d" , sec->name, entry->size);return -1 ; } - nr_entries = sec->sh.sh_size / entry->size; + nr_entries =sec_size (sec) / entry->size;for (idx =0 ; idx < nr_entries; idx++) { alt =malloc (sizeof (*alt));
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 27/62 objtool: Mark .cold subfunctions
In reply to Josh PoimboeufIntroduce a flag to identify .cold subfunctions so they can be detected easier and faster.
--- tools/objtool/check.c | 14 ++++++-------- tools/objtool/elf.c | 19 ++++++++++--------- tools/objtool/include/objtool/elf.h | 1 + 3 files changed, 17 insertions(+), 17 deletions(-)
tools/objtool/check.c +6 -8
@@ -1581,7 +1581,9 @@ static int add_jump_destinations(struct objtool_file *file)/* * Cross-function jump. */ -if (func &&insn_func (jump_dest) && func !=insn_func (jump_dest)) { + +if (func &&insn_func (jump_dest) && !func->cold && +insn_func (jump_dest)->cold) {/* * For GCC 8+, create parent/child links for any cold
@@ -1598,11 +1600,8 @@ static int add_jump_destinations(struct objtool_file *file)* case where the parent function's only reference to a * subfunction is through a jump table. */ -if (!strstr (func->name,".cold" ) && -strstr (insn_func (jump_dest)->name,".cold" )) { - func->cfunc =insn_func (jump_dest); -insn_func (jump_dest)->pfunc = func; - } + func->cfunc =insn_func (jump_dest); +insn_func (jump_dest)->pfunc = func; }if (jump_is_sibling_call (file, insn, jump_dest)) {
@@ -4066,9 +4065,8 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio* If this hole jumps to a .cold function, mark it ignore too. */ if (insn->jump_dest &&insn_func (insn->jump_dest) && -strstr (insn_func (insn->jump_dest)->name,".cold" )) { +insn_func (insn->jump_dest)->cold)insn_func (insn->jump_dest)->ignore = true; - } }return false;
tools/objtool/elf.c +10 -9
@@ -441,6 +441,10 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)list_add (&sym->list, entry);elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name)); + +if (is_func_sym (sym) &&strstr (sym->name,".cold" )) + sym->cold =1 ; + sym->pfunc = sym->cfunc = sym; }static int read_symbols (struct elf *elf)
@@ -527,18 +531,15 @@ static int read_symbols(struct elf *elf)sec_for_each_sym (sec, sym) {char *pname;size_t pnamelen; -if (!is_func_sym (sym)) + +if (!sym->cold)continue ; -if (sym->pfunc ==NULL ) - sym->pfunc = sym; - -if (sym->cfunc ==NULL ) - sym->cfunc = sym; - coldstr =strstr (sym->name,".cold" ); -if (!coldstr) -continue ; +if (!coldstr) { +ERROR ("%s(): cold subfunction without \".cold\"?" , sym->name); +return -1 ; + } pnamelen = coldstr - sym->name; pname =strndup (sym->name, pnamelen);
tools/objtool/include/objtool/elf.h +1 -0
@@ -71,6 +71,7 @@ struct symbol { u8 local_label : 1; u8 frame_pointer : 1; u8 ignore : 1; + u8 cold : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 28/62 objtool: Fix weak symbol hole detection for .cold functions
In reply to Josh PoimboeufWhen ignore_unreachable_insn() looks for weak function holes which jump to their .cold functions, it assumes the parent function comes before the corresponding .cold function in the symbol table. That's not necessarily the case with -ffunction-sections.
Mark all the holes beforehand (including .cold functions) so the ordering of the discovery doesn't matter.
--- tools/objtool/check.c | 84 ++++++++++++++------------- tools/objtool/include/objtool/check.h | 3 +- 2 files changed, 45 insertions(+), 42 deletions(-)
tools/objtool/check.c +43 -41
@@ -2527,6 +2527,44 @@ static void mark_rodata(struct objtool_file *file) file->rodata = found; } +static void mark_holes (struct objtool_file *file) +{ +struct instruction *insn; +bool in_hole = false; + +if (!opts.link) +return ; + +/* +* Whole archive runs might encounter dead code from weak symbols. +* This is where the linker will have dropped the weak symbol in +* favour of a regular symbol, but leaves the code in place. +*/ +for_each_insn (file, insn) { +if (insn->sym || !find_symbol_hole_containing (insn->sec, insn->offset)) { + in_hole = false; +continue ; + } + +/* Skip function padding and pfx code */ +if (!in_hole && insn->type ==INSN_NOP ) +continue ; + + in_hole = true; + insn->hole =1 ; + +/* +* If this hole jumps to a .cold function, mark it ignore. +*/ +if (insn->jump_dest) { +struct symbol *dest_func =insn_func (insn->jump_dest); + +if (dest_func && dest_func->cold) + dest_func->ignore = true; + } + } +} +static int decode_sections (struct objtool_file *file) {int ret;
@@ -2592,6 +2630,9 @@ static int decode_sections(struct objtool_file *file)if (ret)return ret; +/* Must be after add_jump_destinations() */ +mark_holes (file); +/* * Must be after add_call_destinations() such that it can override * dead_end_function() marks.
@@ -4021,7 +4062,8 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructiostruct instruction *prev_insn;int i; -if (insn->type ==INSN_NOP || insn->type ==INSN_TRAP || (func && func->ignore)) +if (insn->type ==INSN_NOP || insn->type ==INSN_TRAP || + insn->hole || (func && func->ignore))return true;/*
@@ -4032,46 +4074,6 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio !strcmp(insn->sec->name, ".altinstr_aux"))return true; -/* -* Whole archive runs might encounter dead code from weak symbols. -* This is where the linker will have dropped the weak symbol in -* favour of a regular symbol, but leaves the code in place. -* -* In this case we'll find a piece of code (whole function) that is not -* covered by a !section symbol. Ignore them. -*/ -if (opts.link && !func) { -int size =find_symbol_hole_containing (insn->sec, insn->offset); -unsigned long end = insn->offset + size; - -if (!size)/* not a hole */ -return false; - -if (size <0 )/* hole until the end */ -return true; - -sec_for_each_insn_continue (file, insn) { -/* -* If we reach a visited instruction at or before the -* end of the hole, ignore the unreachable. -*/ -if (insn->visited) -return true; - -if (insn->offset >= end) -break ; - -/* -* If this hole jumps to a .cold function, mark it ignore too. -*/ -if (insn->jump_dest &&insn_func (insn->jump_dest) && -insn_func (insn->jump_dest)->cold) -insn_func (insn->jump_dest)->ignore = true; - } - -return false; - } -if (!func)return false;
tools/objtool/include/objtool/check.h +2 -1
@@ -64,7 +64,8 @@ struct instruction { noendbr : 1, unret : 1, visited : 4, - no_reloc : 1; + no_reloc : 1, + hole : 1;/* 10 bit hole */ struct alt_group *alt_group;
Signature · 1 line
2.49.0
Peter Zijlstra
maintainerRe: [PATCH v2 28/62] objtool: Fix weak symbol hole detection for .cold functions
Quoted 7 lines · Josh Poimboeuf
When
ignore_unreachable_insn()looks for weak function holes which jump to their .cold functions, it assumes the parent function comes before the corresponding .cold function in the symbol table. That's not necessarily the case with -ffunction-sections.Mark all the holes beforehand (including .cold functions) so the ordering of the discovery doesn't matter.
One of the things I have a 'todo' entry on, is rewriting all sections that reference any one of these instructions.
That is, things like fentry, alternatives, retpoline, static_call, jump_label. Everything that can cause runtime code patching.
Once we are sure none of those sections will contain references to this dead code, we can go and wipe the actual code. Perhaps fill it with a UD1 instruction with some identifying immediate.
Josh Poimboeuf
maintainerRe: [PATCH v2 28/62] objtool: Fix weak symbol hole detection for .cold functions
Quoted 12 lines · Peter Zijlstra
Quoted 7 lines · Josh Poimboeuf
When
ignore_unreachable_insn()looks for weak function holes which jump to their .cold functions, it assumes the parent function comes before the corresponding .cold function in the symbol table. That's not necessarily the case with -ffunction-sections.Mark all the holes beforehand (including .cold functions) so the ordering of the discovery doesn't matter.
One of the things I have a 'todo' entry on, is rewriting all sections that reference any one of these instructions.
Quoted 6 lines
That is, things like fentry, alternatives, retpoline,
static_call,jump_label. Everything that can cause runtime code patching.Once we are sure none of those sections will contain references to this dead code, we can go and wipe the actual code. Perhaps fill it with a UD1 instruction with some identifying immediate.
Yeah, that would be nice.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 29/62 objtool: Mark prefix functions
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, introduce a flag to identify __pfx_*() and __cfi_*() functions in advance so they don't need to be manually identified every time a check is needed.
--- tools/objtool/check.c | 3 +-- tools/objtool/elf.c | 5 +++++ tools/objtool/include/objtool/elf.h | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-)
tools/objtool/check.c +1 -2
@@ -3601,8 +3601,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,if (func &&insn_func (insn) && func !=insn_func (insn)->pfunc) {/* Ignore KCFI type preambles, which always fall through */ -if (!strncmp (func->name,"__cfi_" ,6 ) || - !strncmp (func->name,"__pfx_" ,6 )) +if (is_prefix_func (func))return 0 ;if (file->ignore_unreachables)
tools/objtool/elf.c +5 -0
@@ -442,6 +442,11 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name)); +if (is_func_sym (sym) && sym->len ==16 && + (strstarts (sym->name,"__pfx" ) ||strstarts (sym->name,"__cfi_" ))) + sym->prefix =1 ; + +if (is_func_sym (sym) &&strstr (sym->name,".cold" )) sym->cold =1 ; sym->pfunc = sym->cfunc = sym;
tools/objtool/include/objtool/elf.h +6 -0
@@ -72,6 +72,7 @@ struct symbol { u8 frame_pointer : 1; u8 ignore : 1; u8 cold : 1; + u8 prefix : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
@@ -229,6 +230,11 @@ static inline bool is_local_sym(struct symbol *sym)return sym->bind ==STB_LOCAL ; } +static inline bool is_prefix_func (struct symbol *sym) +{ +return is_func_sym (sym) && sym->prefix; +} +static inline bool is_reloc_sec (struct section *sec) {return sec->sh.sh_type ==SHT_RELA || sec->sh.sh_type ==SHT_REL ;
Signature · 1 line
2.49.0
Peter Zijlstra
maintainerRe: [PATCH v2 29/62] objtool: Mark prefix functions
Quoted 30 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, introduce a flag to identify __pfx_*() and __cfi_*() functions in advance so they don't need to be manually identified every time a check is needed.
Signed-off-by Josh Poimboeuf <[email protected]>--- tools/objtool/check.c | 3 +-- tools/objtool/elf.c | 5 +++++ tools/objtool/include/objtool/elf.h | 6 ++++++ 3 files changed, 12 insertions(+), 2 deletions(-)tools/objtool/check.c +1
-2@@ -3601,8 +3601,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,if (func &&insn_func (insn) && func !=insn_func (insn)->pfunc) {/* Ignore KCFI type preambles, which always fall through */ -if (!strncmp (func->name,"__cfi_" ,6 ) || - !strncmp (func->name,"__pfx_" ,6 )) +if (is_prefix_func (func))return 0 ;if (file->ignore_unreachables)tools/objtool/elf.c +0
-0
tools/objtool/elf.c static void elf_add_symbol(struct elf *elf, struct symbol *sym) · line 445
Quoted 4 lines
tools/objtool/elf.c +1
-0@@ -442,6 +442,11 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name)); + if (is_func_sym(sym) && sym->len == 16 &&
Where did that 'sym->len == 16' thing come from? I mean, they are, but the old code didn't assert that.
I would rather objtool issue a warn if not 16, but still consider these as prefix.
Quoted 7 lines
tools/objtool/elf.c +4
-0+ (strstarts(sym->name, "__pfx") || strstarts(sym->name, "__cfi_"))) + sym->prefix = 1; + +if (is_func_sym (sym) &&strstr (sym->name,".cold" )) sym->cold =1 ; sym->pfunc = sym->cfunc = sym;
Josh Poimboeuf
maintainerRe: [PATCH v2 29/62] objtool: Mark prefix functions
Quoted 5 lines · Peter Zijlstra
Quoted 5 lines
+++
b/tools/objtool/elf.c(quoted diff) +0
-0@@ -442,6 +442,11 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym) elf_hash_add(symbol, &sym->hash, sym->idx); elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->name));
Quoted 7 lines
Review static void elf_add_symbol(struct elf *elf, struct symbol *sym) · line 445
Quoted 1 line
(quoted diff) +1
-0+ if (is_func_sym(sym) && sym->len == 16 &&Where did that '
sym->len== 16' thing come from? I mean, they are, but the old code didn't assert that.I would rather objtool issue a warn if not 16, but still consider these as prefix.
Ok:
tools/objtool/elf.c +5 -3
@@ -442,10 +442,12 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name)); -if (is_func_sym (sym) && sym->len ==16 && - (strstarts (sym->name,"__pfx" ) ||strstarts (sym->name,"__cfi_" ))) +if (is_func_sym (sym) && + (strstarts (sym->name,"__pfx_" ) ||strstarts (sym->name,"__cfi_" ))) { +if (sym->len !=16 ) +WARN ("%s size %d != 16" , sym->name, sym->len); sym->prefix =1 ; - + }if (is_func_sym (sym) &&strstr (sym->name,".cold" )) sym->cold =1 ;
Josh Poimboeuf
maintainerRe: [PATCH v2 29/62] objtool: Mark prefix functions
Quoted 11 lines · Josh Poimboeuf
Quoted 10 lines · Peter Zijlstra
tools/objtool/elf.c static void elf_add_symbol(struct elf *elf, struct symbol *sym) · line 445
Quoted 6 lines
+++
b/tools/objtool/elf.ctools/objtool/elf.c +1
-0@@ -442,6 +442,11 @@ static void elf_add_symbol(struct elf *elf, struct symbol *sym)elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name)); + if (is_func_sym(sym) && sym->len == 16 &&Where did that '
sym->len== 16' thing come from? I mean, they are, but the old code didn't assert that.
Quoted 4 lines
Quoted 2 lines
I would rather objtool issue a warn if not 16, but still consider these as prefix.
Ok:
Turns out there are kCFI cases with prefixes != 16, so I omitted the size check altogether.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 30/62 objtool: Simplify reloc offset calculation in unwind_read_hints()
In reply to Josh PoimboeufSimplify the relocation offset calculation in unwind_read_hints(), similar to other conversions which have already been done.
--- tools/objtool/check.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-)
tools/objtool/check.c +1 -8
@@ -2221,14 +2221,7 @@ static int read_unwind_hints(struct objtool_file *file)return -1 ; } -if (is_sec_sym (reloc->sym)) { - offset =reloc_addend (reloc); - }else if (reloc->sym->local_label) { - offset = reloc->sym->offset; - }else { -ERROR ("unexpected relocation symbol type in %s" , sec->rsec->name); -return -1 ; - } + offset = reloc->sym->offset +reloc_addend (reloc); insn =find_insn (file, reloc->sym->sec, offset);if (!insn) {
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 31/62 objtool: Avoid emptying lists for duplicate sections
In reply to Josh PoimboeufWhen a to-be-created section already exists, there's no point in emptying the various lists if their respective sections already exist. In fact it's better to leave them intact as they might get used later.
--- tools/objtool/check.c | 4 ---- 1 file changed, 4 deletions(-)
tools/objtool/check.c +0 -4
@@ -637,7 +637,6 @@ static int create_static_call_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".static_call_sites" );if (sec) { -INIT_LIST_HEAD (&file->static_call_list);WARN ("file already has .static_call_sites section, skipping" );return 0 ; }
@@ -853,7 +852,6 @@ static int create_cfi_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".cfi_sites" );if (sec) { -INIT_LIST_HEAD (&file->call_list);WARN ("file already has .cfi_sites section, skipping" );return 0 ; }
@@ -902,7 +900,6 @@ static int create_mcount_loc_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,"__mcount_loc" );if (sec) { -INIT_LIST_HEAD (&file->mcount_loc_list);WARN ("file already has __mcount_loc section, skipping" );return 0 ; }
@@ -947,7 +944,6 @@ static int create_direct_call_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".call_sites" );if (sec) { -INIT_LIST_HEAD (&file->call_list);WARN ("file already has .call_sites section, skipping" );return 0 ; }
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 32/62 objtool: Suppress section skipping warnings with --dryrun
In reply to Josh PoimboeufIt's common to use --dryrun on binaries that have already been processed. Don't print the section skipping warnings in that case.
--- tools/objtool/check.c | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-)
tools/objtool/check.c +21 -7
@@ -637,7 +637,9 @@ static int create_static_call_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".static_call_sites" );if (sec) { -WARN ("file already has .static_call_sites section, skipping" ); +if (!opts.dryrun) +WARN ("file already has .static_call_sites section, skipping" ); +return 0 ; }
@@ -719,7 +721,9 @@ static int create_retpoline_sites_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".retpoline_sites" );if (sec) { -WARN ("file already has .retpoline_sites, skipping" ); +if (!opts.dryrun) +WARN ("file already has .retpoline_sites, skipping" ); +return 0 ; }
@@ -757,7 +761,9 @@ static int create_return_sites_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".return_sites" );if (sec) { -WARN ("file already has .return_sites, skipping" ); +if (!opts.dryrun) +WARN ("file already has .return_sites, skipping" ); +return 0 ; }
@@ -795,7 +801,9 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".ibt_endbr_seal" );if (sec) { -WARN ("file already has .ibt_endbr_seal, skipping" ); +if (!opts.dryrun) +WARN ("file already has .ibt_endbr_seal, skipping" ); +return 0 ; }
@@ -852,7 +860,9 @@ static int create_cfi_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".cfi_sites" );if (sec) { -WARN ("file already has .cfi_sites section, skipping" ); +if (!opts.dryrun) +WARN ("file already has .cfi_sites section, skipping" ); +return 0 ; }
@@ -900,7 +910,9 @@ static int create_mcount_loc_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,"__mcount_loc" );if (sec) { -WARN ("file already has __mcount_loc section, skipping" ); +if (!opts.dryrun) +WARN ("file already has __mcount_loc section, skipping" ); +return 0 ; }
@@ -944,7 +956,9 @@ static int create_direct_call_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".call_sites" );if (sec) { -WARN ("file already has .call_sites section, skipping" ); +if (!opts.dryrun) +WARN ("file already has .call_sites section, skipping" ); +return 0 ; }
Signature · 1 line
2.49.0
Peter Zijlstra
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
Quoted 2 lines · Josh Poimboeuf
It's common to use --dryrun on binaries that have already been processed. Don't print the section skipping warnings in that case.
Ah, I rather like this warning, it gives me an easy check to see if the file has already been processed.
I typically do a OBJTOOL_ARGS="--backup" build and run dryrun debug sessions against those .orig files.
Peter Zijlstra
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
Quoted 9 lines · Peter Zijlstra
Quoted 2 lines · Josh Poimboeuf
It's common to use --dryrun on binaries that have already been processed. Don't print the section skipping warnings in that case.
Ah, I rather like this warning, it gives me an easy check to see if the file has already been processed.
I typically do a OBJTOOL_ARGS="--backup" build and run dryrun debug sessions against those .orig files.
Turns out, you already broke this.. :-(
I'm now having a case where objtool fails on vmlinux.o and make happily deletes vmlinux.o and I'm left empty handed.
Let me go resurrect --backup
Josh Poimboeuf
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
Quoted 11 lines · Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 2 lines · Josh Poimboeuf
It's common to use --dryrun on binaries that have already been processed. Don't print the section skipping warnings in that case.
Ah, I rather like this warning, it gives me an easy check to see if the file has already been processed.
I typically do a OBJTOOL_ARGS="--backup" build and run dryrun debug sessions against those .orig files.
Quoted 6 lines
Turns out, you already broke this.. :-(
I'm now having a case where objtool fails on vmlinux.o and make happily deletes vmlinux.o and I'm left empty handed.
Let me go resurrect --backup
Yeah, as I just mentioned in that other email, --verbose should give you what you need. It also prints the cmdline args, which is nice.
But also, feel free to resurrect --backup, or you can yell at me to do it as the backup code changed a bit.
Signature · 1 line
Josh
Peter Zijlstra
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
Quoted 17 lines · Josh Poimboeuf
Quoted 16 lines · Peter Zijlstra
Quoted 9 lines · Peter Zijlstra
Quoted 2 lines · Josh Poimboeuf
It's common to use --dryrun on binaries that have already been processed. Don't print the section skipping warnings in that case.
Ah, I rather like this warning, it gives me an easy check to see if the file has already been processed.
I typically do a OBJTOOL_ARGS="--backup" build and run dryrun debug sessions against those .orig files.
Turns out, you already broke this.. :-(
I'm now having a case where objtool fails on vmlinux.o and make happily deletes vmlinux.o and I'm left empty handed.
Quoted 7 lines
Quoted 1 line
Let me go resurrect --backup
Yeah, as I just mentioned in that other email, --verbose should give you what you need. It also prints the cmdline args, which is nice.
But also, feel free to resurrect --backup, or you can yell at me to do it as the backup code changed a bit.
I have the patch somewhere, failed to send it out. I'll try and dig it out later today.
Peter Zijlstra
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
Quoted 5 lines · Peter Zijlstra
Quoted 2 lines
But also, feel free to resurrect --backup, or you can yell at me to do it as the backup code changed a bit.
I have the patch somewhere, failed to send it out. I'll try and dig it out later today.
This is what I had. Wasn't sure we wanted to make -v imply --backup ?
I'm used to stealing the objtool arguments from V=1 builds. I suppose the print_args thing is easier, might get used to it eventually.
tools/objtool/builtin-check.c +12 -12
@@ -101,6 +101,7 @@ static const struct option check_options[] = {OPT_BOOLEAN (0 ,"stats" , &opts.stats,"print statistics" ),OPT_BOOLEAN ('v' ,"verbose" , &opts.verbose,"verbose warnings" ),OPT_BOOLEAN (0 ,"Werror" , &opts.werror,"return error on warnings" ), +OPT_BOOLEAN (0 ,"backup" , &opts.backup,"create a backup (.orig) file on error" ), OPT_END(), };
@@ -244,13 +245,10 @@ static void save_argv(int argc, const char **argv) }; } -void print_args (void ) +static void make_backup (void ) {char *backup =NULL ; -if (opts.output || opts.dryrun) - goto print; -/* * Make a backup before kbuild deletes the file so the error * can be recreated without recompiling or relinking.
@@ -258,17 +256,19 @@ void print_args(void) backup =malloc (strlen (objname) +strlen (ORIG_SUFFIX ) +1 );if (!backup) {ERROR_GLIBC ("malloc" ); - goto print; +return ; }strcpy (backup, objname);strcat (backup,ORIG_SUFFIX );if (copy_file (objname, backup)) { backup =NULL ; - goto print; +return ; } +} -print: +void print_args (void ) +{/* * Print the cmdline args to make it easier to recreate. If '--output' * wasn't used, add it to the printed args with the backup as input.
@@ -278,10 +278,7 @@ void print_args(void)for (int i =1 ; i < orig_argc; i++) {char *arg = orig_argv[i]; -if (backup && !strcmp (arg, objname)) -fprintf (stderr," %s -o %s" , backup, objname); -else -fprintf (stderr," %s" , arg); +fprintf (stderr," %s" , arg); }fprintf (stderr,"\n" );
@@ -324,8 +321,11 @@ int objtool_run(int argc, const char **argv) } ret =check (file); -if (ret) +if (ret) { +if (opts.backup) +make_backup ();return ret; + }if (!opts.dryrun && file->elf->changed &&elf_write (file->elf))return 1 ;
tools/objtool/include/objtool/builtin.h +1 -0
@@ -39,6 +39,7 @@ struct opts {bool stats;bool verbose;bool werror; +bool backup; };extern struct opts opts;
Josh Poimboeuf
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Peter Zijlstra
Quoted 2 lines
But also, feel free to resurrect --backup, or you can yell at me to do it as the backup code changed a bit.
I have the patch somewhere, failed to send it out. I'll try and dig it out later today.
This is what I had. Wasn't sure we wanted to make -v imply --backup ?
Yeah, I suppose --verbose shouldn't be doing unrequested changes.
Regardless I want to keep the feature where print_args() modifies the args to use the backup as input as that's very convenient. We can just tie that (and the printing of the args itself) to --backup.
tools/objtool/builtin-check.c static const struct option check_options[] = { · line 104
Quoted 13 lines
I'm used to stealing the objtool arguments from V=1 builds. I suppose the
print_argsthing is easier, might get used to it eventually.tools/objtool/builtin-check.c +1
-0@@ -101,6 +101,7 @@ static const struct option check_options[] = {OPT_BOOLEAN (0 ,"stats" , &opts.stats,"print statistics" ),OPT_BOOLEAN ('v' ,"verbose" , &opts.verbose,"verbose warnings" ),OPT_BOOLEAN (0 ,"Werror" , &opts.werror,"return error on warnings" ), + OPT_BOOLEAN(0, "backup", &opts.backup, "create a backup (.orig) file on error"),
It should also work on warnings (non-werror) as well.
Something like so?
tools/objtool/builtin-check.c +11 -14
@@ -91,6 +91,7 @@ static const struct option check_options[] = {OPT_GROUP ("Options:" ),OPT_BOOLEAN (0 ,"backtrace" , &opts.backtrace,"unwind on error" ), +OPT_BOOLEAN (0 ,"backup" , &opts.backup,"create a backup (.orig) file on warning" ),OPT_BOOLEAN (0 ,"dry-run" , &opts.dryrun,"don't write modifications" ),OPT_BOOLEAN (0 ,"link" , &opts.link,"object is a linked object" ), OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
@@ -244,12 +245,9 @@ static void save_argv(int argc, const char **argv) }; } -void print_args (void ) +int make_backup (void ) { -char *backup =NULL ; - -if (opts.output || opts.dryrun) - goto print; +char *backup;/* * Make a backup before kbuild deletes the file so the error
@@ -258,33 +256,32 @@ void print_args(void) backup =malloc (strlen (objname) +strlen (ORIG_SUFFIX ) +1 );if (!backup) {ERROR_GLIBC ("malloc" ); - goto print; +return 1 ; }strcpy (backup, objname);strcat (backup,ORIG_SUFFIX ); -if (copy_file (objname, backup)) { - backup =NULL ; - goto print; - } +if (copy_file (objname, backup)) +return 1 ; -print:/* -* Print the cmdline args to make it easier to recreate. If '--output' -* wasn't used, add it to the printed args with the backup as input. +* Print the cmdline args to make it easier to recreate. */ +fprintf (stderr,"%s" , orig_argv[0 ]);for (int i =1 ; i < orig_argc; i++) {char *arg = orig_argv[i]; -if (backup && !strcmp (arg, objname)) +/* Modify the printed args to use the backup */ +if (!opts.output && !strcmp (arg, objname))fprintf (stderr," %s -o %s" , backup, objname);else fprintf (stderr," %s" , arg); }fprintf (stderr,"\n" ); +return 0 ; }int objtool_run (int argc,const char **argv)
tools/objtool/check.c +3 -1
@@ -4798,9 +4798,11 @@ int check(struct objtool_file *file)if (opts.verbose) {if (opts.werror && warnings)WARN ("%d warning(s) upgraded to errors" , warnings); -print_args ();disas_warned_funcs (file); } +if (opts.backup &&make_backup ()) +return 1 ; +return ret; }
tools/objtool/include/objtool/builtin.h +2 -1
@@ -29,6 +29,7 @@ struct opts {/* options: */ bool backtrace; +bool backup;bool dryrun;bool link;bool mnop;
@@ -47,6 +48,6 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]);int objtool_run (int argc,const char **argv); -void print_args (void ); +int make_backup (void );#endif /* _BUILTIN_H */
Josh Poimboeuf
maintainerRe: [PATCH v2 32/62] objtool: Suppress section skipping warnings with --dryrun
In reply to Peter ZijlstraQuoted 9 lines · Peter Zijlstra
Quoted 2 lines · Josh Poimboeuf
It's common to use --dryrun on binaries that have already been processed. Don't print the section skipping warnings in that case.
Ah, I rather like this warning, it gives me an easy check to see if the file has already been processed.
I typically do a OBJTOOL_ARGS="--backup" build and run dryrun debug sessions against those .orig files.
Ok.
Though, note that as of a few months ago, --backup no longer exists. A backup is now automatically created with --verbose. But we can revive it if you want.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 33/62 objtool: Rename --Werror to --werror
In reply to Josh PoimboeufThe objtool --Werror option name is stylistically inconsistent: halfway between GCC's single-dash capitalized -Werror and objtool's double-dash --lowercase convention, making it unnecessarily hard to remember.
Make the 'W' lower case (--werror) for consistency with objtool's other options.
--- scripts/Makefile.lib | 2 +- scripts/Makefile.vmlinux_o | 2 +- tools/objtool/builtin-check.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-)
scripts/Makefile.lib +1 -1
@@ -272,7 +272,7 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES) -objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror +objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \
scripts/Makefile.vmlinux_o +1 -1
@@ -41,7 +41,7 @@ objtool-enabled := $(or $(delay-objtool),$(CONFIG_NOINSTR_VALIDATION)) ifeq ($(delay-objtool),y) vmlinux-objtool-args-y += $(objtool-args-y) else -vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror +vmlinux-objtool-args-$(CONFIG_OBJTOOL_WERROR) += --werror endif vmlinux-objtool-args-$(CONFIG_NOINSTR_VALIDATION) += --noinstr \
tools/objtool/builtin-check.c +1 -1
@@ -100,7 +100,7 @@ static const struct option check_options[] = {OPT_BOOLEAN (0 ,"sec-address" , &opts.sec_address,"print section addresses in warnings" ),OPT_BOOLEAN (0 ,"stats" , &opts.stats,"print statistics" ),OPT_BOOLEAN ('v' ,"verbose" , &opts.verbose,"verbose warnings" ), -OPT_BOOLEAN (0 ,"Werror" , &opts.werror,"return error on warnings" ), +OPT_BOOLEAN (0 ,"werror" , &opts.werror,"return error on warnings" ), OPT_END(), };
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 34/62 objtool: Reindent check_options[]
In reply to Josh PoimboeufBring the cmdline check_options[] array back into vertical alignment for better readability.
--- tools/objtool/builtin-check.c | 50 +++++++++++++++++------------------ 1 file changed, 25 insertions(+), 25 deletions(-)
tools/objtool/builtin-check.c +25 -25
@@ -73,34 +73,34 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)static const struct option check_options[] = {OPT_GROUP ("Actions:" ), +OPT_BOOLEAN (0 ,"cfi" , &opts.cfi,"annotate kernel control flow integrity (kCFI) function preambles" ),OPT_CALLBACK_OPTARG ('h' ,"hacks" ,NULL ,NULL ,"jump_label,noinstr,skylake" ,"patch toolchain bugs/limitations" , parse_hacks), -OPT_BOOLEAN ('i' ,"ibt" , &opts.ibt,"validate and annotate IBT" ), -OPT_BOOLEAN ('m' ,"mcount" , &opts.mcount,"annotate mcount/fentry calls for ftrace" ), -OPT_BOOLEAN ('n' ,"noinstr" , &opts.noinstr,"validate noinstr rules" ), -OPT_BOOLEAN (0 ,"orc" , &opts.orc,"generate ORC metadata" ), -OPT_BOOLEAN ('r' ,"retpoline" , &opts.retpoline,"validate and annotate retpoline usage" ), -OPT_BOOLEAN (0 ,"rethunk" , &opts.rethunk,"validate and annotate rethunk usage" ), -OPT_BOOLEAN (0 ,"unret" , &opts.unret,"validate entry unret placement" ), -OPT_INTEGER (0 ,"prefix" , &opts.prefix,"generate prefix symbols" ), -OPT_BOOLEAN ('l' ,"sls" , &opts.sls,"validate straight-line-speculation mitigations" ), -OPT_BOOLEAN ('s' ,"stackval" , &opts.stackval,"validate frame pointer rules" ), -OPT_BOOLEAN ('t' ,"static-call" , &opts.static_call,"annotate static calls" ), -OPT_BOOLEAN ('u' ,"uaccess" , &opts.uaccess,"validate uaccess rules for SMAP" ), -OPT_BOOLEAN (0 ,"cfi" , &opts.cfi,"annotate kernel control flow integrity (kCFI) function preambles" ), -OPT_CALLBACK_OPTARG (0 ,"dump" ,NULL ,NULL ,"orc" ,"dump metadata" , parse_dump), +OPT_BOOLEAN ('i' ,"ibt" , &opts.ibt,"validate and annotate IBT" ), +OPT_BOOLEAN ('m' ,"mcount" , &opts.mcount,"annotate mcount/fentry calls for ftrace" ), +OPT_BOOLEAN ('n' ,"noinstr" , &opts.noinstr,"validate noinstr rules" ), +OPT_BOOLEAN (0 ,"orc" , &opts.orc,"generate ORC metadata" ), +OPT_BOOLEAN ('r' ,"retpoline" , &opts.retpoline,"validate and annotate retpoline usage" ), +OPT_BOOLEAN (0 ,"rethunk" , &opts.rethunk,"validate and annotate rethunk usage" ), +OPT_BOOLEAN (0 ,"unret" , &opts.unret,"validate entry unret placement" ), +OPT_INTEGER (0 ,"prefix" , &opts.prefix,"generate prefix symbols" ), +OPT_BOOLEAN ('l' ,"sls" , &opts.sls,"validate straight-line-speculation mitigations" ), +OPT_BOOLEAN ('s' ,"stackval" , &opts.stackval,"validate frame pointer rules" ), +OPT_BOOLEAN ('t' ,"static-call" , &opts.static_call,"annotate static calls" ), +OPT_BOOLEAN ('u' ,"uaccess" , &opts.uaccess,"validate uaccess rules for SMAP" ), +OPT_CALLBACK_OPTARG (0 ,"dump" ,NULL ,NULL ,"orc" ,"dump metadata" , parse_dump),OPT_GROUP ("Options:" ), -OPT_BOOLEAN (0 ,"backtrace" , &opts.backtrace,"unwind on error" ), -OPT_BOOLEAN (0 ,"dry-run" , &opts.dryrun,"don't write modifications" ), -OPT_BOOLEAN (0 ,"link" , &opts.link,"object is a linked object" ), -OPT_BOOLEAN (0 ,"module" , &opts.module,"object is part of a kernel module" ), -OPT_BOOLEAN (0 ,"mnop" , &opts.mnop,"nop out mcount call sites" ), -OPT_BOOLEAN (0 ,"no-unreachable" , &opts.no_unreachable,"skip 'unreachable instruction' warnings" ), -OPT_STRING ('o' ,"output" , &opts.output,"file" ,"output file name" ), -OPT_BOOLEAN (0 ,"sec-address" , &opts.sec_address,"print section addresses in warnings" ), -OPT_BOOLEAN (0 ,"stats" , &opts.stats,"print statistics" ), -OPT_BOOLEAN ('v' ,"verbose" , &opts.verbose,"verbose warnings" ), -OPT_BOOLEAN (0 ,"werror" , &opts.werror,"return error on warnings" ), +OPT_BOOLEAN (0 ,"backtrace" , &opts.backtrace,"unwind on error" ), +OPT_BOOLEAN (0 ,"dry-run" , &opts.dryrun,"don't write modifications" ), +OPT_BOOLEAN (0 ,"link" , &opts.link,"object is a linked object" ), +OPT_BOOLEAN (0 ,"module" , &opts.module,"object is part of a kernel module" ), +OPT_BOOLEAN (0 ,"mnop" , &opts.mnop,"nop out mcount call sites" ), +OPT_BOOLEAN (0 ,"no-unreachable" , &opts.no_unreachable,"skip 'unreachable instruction' warnings" ), +OPT_STRING ('o' ,"output" , &opts.output,"file" ,"output file name" ), +OPT_BOOLEAN (0 ,"sec-address" , &opts.sec_address,"print section addresses in warnings" ), +OPT_BOOLEAN (0 ,"stats" , &opts.stats,"print statistics" ), +OPT_BOOLEAN ('v' ,"verbose" , &opts.verbose,"verbose warnings" ), +OPT_BOOLEAN (0 ,"werror" , &opts.werror,"return error on warnings" ),OPT_END (), };
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 35/62 objtool: Refactor add_jump_destinations()
In reply to Josh PoimboeufThe add_jump_destinations() logic is a bit weird and convoluted after being incrementally tweaked over the years. Refactor it to hopefully be more logical and straightforward.
--- tools/objtool/check.c | 227 +++++++++++++--------------- tools/objtool/include/objtool/elf.h | 4 +- 2 files changed, 104 insertions(+), 127 deletions(-)
tools/objtool/check.c +102 -125
@@ -1439,9 +1439,14 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn, }static bool is_first_func_insn (struct objtool_file *file, -struct instruction *insn,struct symbol *sym) +struct instruction *insn) { -if (insn->offset == sym->offset) +struct symbol *func =insn_func (insn); + +if (!func) +return false; + +if (insn->offset == func->offset)return true;/* Allow direct CALL/JMP past ENDBR */
@@ -1449,52 +1454,32 @@ static bool is_first_func_insn(struct objtool_file *file,struct instruction *prev =prev_insn_same_sym (file, insn);if (prev && prev->type ==INSN_ENDBR && - insn->offset == sym->offset + prev->len) + insn->offset == func->offset + prev->len)return true; }return false; } -/* -* A sibling call is a tail-call to another symbol -- to differentiate from a -* recursive tail-call which is to the same symbol. -*/ -static bool jump_is_sibling_call (struct objtool_file *file, -struct instruction *from,struct instruction *to) -{ -struct symbol *fs = from->sym; -struct symbol *ts = to->sym; - -/* Not a sibling call if from/to a symbol hole */ -if (!fs || !ts) -return false; - -/* Not a sibling call if not targeting the start of a symbol. */ -if (!is_first_func_insn (file, to, ts)) -return false; - -/* Disallow sibling calls into STT_NOTYPE */ -if (is_notype_sym (ts)) -return false; - -/* Must not be self to be a sibling */ -return fs->pfunc != ts->pfunc; -} -/* * Find the destination instructions for all jumps. */ static int add_jump_destinations (struct objtool_file *file) { -struct instruction *insn, *jump_dest; +struct instruction *insn;struct reloc *reloc; -struct section *dest_sec; -unsigned long dest_off;int ret;for_each_insn (file, insn) {struct symbol *func =insn_func (insn); +struct instruction *dest_insn; +struct section *dest_sec; +struct symbol *dest_sym; +unsigned long dest_off; +bool dest_undef = false; + +if (!is_static_jump (insn)) +continue ;if (insn->jump_dest) {/*
@@ -1503,129 +1488,121 @@ static int add_jump_destinations(struct objtool_file *file)*/ continue ; } -if (!is_static_jump (insn)) -continue ; reloc =insn_reloc (file, insn);if (!reloc) { dest_sec = insn->sec; dest_off =arch_jump_destination (insn); - }else if (is_sec_sym (reloc->sym)) { + }else if (is_undef_sym (reloc->sym)) { + dest_sym = reloc->sym; + dest_undef = true; + }else { dest_sec = reloc->sym->sec; - dest_off =arch_insn_adjusted_addend (insn, reloc); - }else if (reloc->sym->retpoline_thunk) { + dest_off = reloc->sym->sym.st_value + +arch_insn_adjusted_addend (insn, reloc); + } + +if (!dest_undef) { + dest_insn =find_insn (file, dest_sec, dest_off); +if (!dest_insn) { +struct symbol *sym =find_symbol_by_offset (dest_sec, dest_off); + +/* +* retbleed_untrain_ret() jumps to +* __x86_return_thunk(), but objtool can't find +* the thunk's starting RET instruction, +* because the RET is also in the middle of +* another instruction. Objtool only knows +* about the outer instruction. +*/ +if (sym && sym->embedded_insn) { +add_return_call (file, insn, false); +continue ; + } + +/* +* GCOV/KCOV dead code can jump to the end of +* the function/section. +*/ +if (file->ignore_unreachables && func && + dest_sec == insn->sec && + dest_off == func->offset + func->len) +continue ; + +ERROR_INSN (insn,"can't find jump dest instruction at %s+0x%lx" , + dest_sec->name, dest_off); +return -1 ; + } + + dest_sym = dest_insn->sym; +if (!dest_sym) + goto set_jump_dest; + } + +if (dest_sym->retpoline_thunk) { ret =add_retpoline_call (file, insn);if (ret)return ret;continue ; - }else if (reloc->sym->return_thunk) { + } + +if (dest_sym->return_thunk) {add_return_call (file, insn, true);continue ; - }else if (func) { -/* -* External sibling call or internal sibling call with -* STT_FUNC reloc. -*/ - ret =add_call_dest (file, insn, reloc->sym, true); -if (ret) -return ret; -continue ; - }else if (reloc->sym->sec->idx) { - dest_sec = reloc->sym->sec; - dest_off = reloc->sym->sym.st_value + -arch_dest_reloc_offset (reloc_addend (reloc)); - }else { -/* non-func asm code jumping to another file */ -continue ; } - jump_dest =find_insn (file, dest_sec, dest_off); -if (!jump_dest) { -struct symbol *sym =find_symbol_by_offset (dest_sec, dest_off); - -/* -* This is a special case for retbleed_untrain_ret(). -* It jumps to __x86_return_thunk(), but objtool -* can't find the thunk's starting RET -* instruction, because the RET is also in the -* middle of another instruction. Objtool only -* knows about the outer instruction. -*/ -if (sym && sym->embedded_insn) { -add_return_call (file, insn, false); -continue ; - } - -/* -* GCOV/KCOV dead code can jump to the end of the -* function/section. -*/ -if (file->ignore_unreachables && func && - dest_sec == insn->sec && - dest_off == func->offset + func->len) -continue ; - -ERROR_INSN (insn,"can't find jump dest instruction at %s+0x%lx" , - dest_sec->name, dest_off); -return -1 ; - } - -/* -* An intra-TU jump in retpoline.o might not have a relocation -* for its jump dest, in which case the above -* add_{retpoline,return}_call() didn't happen. -*/ -if (jump_dest->sym && jump_dest->offset == jump_dest->sym->offset) { -if (jump_dest->sym->retpoline_thunk) { - ret =add_retpoline_call (file, insn); +if (dest_undef) { +/* External symbol */ +if (func) { +/* External sibling call */ + ret =add_call_dest (file, insn, dest_sym, true);if (ret)return ret;continue ; } -if (jump_dest->sym->return_thunk) { -add_return_call (file, insn, true); -continue ; - } + +/* Non-func asm code jumping to external symbol */ +continue ; } +if (!insn->sym || insn->sym == dest_insn->sym) + goto set_jump_dest; +/* -* Cross-function jump. +* Internal cross-function jump. */ -if (func &&insn_func (jump_dest) && !func->cold && -insn_func (jump_dest)->cold) { - -/* -* For GCC 8+, create parent/child links for any cold -* subfunctions. This is _mostly_ redundant with a -* similar initialization in read_symbols(). -* -* If a function has aliases, we want the *first* such -* function in the symbol table to be the subfunction's -* parent. In that case we overwrite the -* initialization done in read_symbols(). -* -* However this code can't completely replace the -* read_symbols() code because this doesn't detect the -* case where the parent function's only reference to a -* subfunction is through a jump table. -*/ - func->cfunc =insn_func (jump_dest); -insn_func (jump_dest)->pfunc = func; +/* +* For GCC 8+, create parent/child links for any cold +* subfunctions. This is _mostly_ redundant with a +* similar initialization in read_symbols(). +* +* If a function has aliases, we want the *first* such +* function in the symbol table to be the subfunction's +* parent. In that case we overwrite the +* initialization done in read_symbols(). +* +* However this code can't completely replace the +* read_symbols() code because this doesn't detect the +* case where the parent function's only reference to a +* subfunction is through a jump table. +*/ +if (func && dest_sym->cold) { + func->cfunc = dest_sym; + dest_sym->pfunc = func; + goto set_jump_dest; } -if (jump_is_sibling_call (file, insn, jump_dest)) { -/* -* Internal sibling call without reloc or with -* STT_SECTION reloc. -*/ - ret =add_call_dest (file, insn,insn_func (jump_dest), true); +if (is_first_func_insn (file, dest_insn)) { +/* Internal sibling call */ + ret =add_call_dest (file, insn, dest_sym, true);if (ret)return ret;continue ; } - insn->jump_dest = jump_dest; +set_jump_dest: + insn->jump_dest = dest_insn; }return 0 ;
tools/objtool/include/objtool/elf.h +2 -2
@@ -180,9 +180,9 @@ static inline unsigned int elf_text_rela_type(struct elf *elf)return elf_addr_size (elf) ==4 ?R_TEXT32 :R_TEXT64 ; } -static inline bool sym_has_sec (struct symbol *sym) +static inline bool is_undef_sym (struct symbol *sym) { -return sym->sec->idx; +return !sym->sec->idx; }static inline bool is_null_sym (struct symbol *sym)
Signature · 1 line
2.49.0
Joe Lawrence
maintainerRe: [PATCH v2 35/62] objtool: Refactor add_jump_destinations()
Quoted 168 lines · Josh Poimboeuf
The
add_jump_destinations()logic is a bit weird and convoluted after being incrementally tweaked over the years. Refactor it to hopefully be more logical and straightforward.Signed-off-by Josh Poimboeuf <[email protected]>--- tools/objtool/check.c | 227 +++++++++++++--------------- tools/objtool/include/objtool/elf.h | 4 +- 2 files changed, 104 insertions(+), 127 deletions(-)tools/objtool/check.c +66
-41@@ -1439,9 +1439,14 @@ static void add_return_call(struct objtool_file *file, struct instruction *insn, }static bool is_first_func_insn (struct objtool_file *file, -struct instruction *insn,struct symbol *sym) +struct instruction *insn) { -if (insn->offset == sym->offset) +struct symbol *func =insn_func (insn); + +if (!func) +return false; + +if (insn->offset == func->offset)return true;/* Allow direct CALL/JMP past ENDBR */ @@ -1449,52 +1454,32 @@ static bool is_first_func_insn(struct objtool_file *file,struct instruction *prev =prev_insn_same_sym (file, insn);if (prev && prev->type ==INSN_ENDBR && - insn->offset == sym->offset + prev->len) + insn->offset == func->offset + prev->len)return true; }return false; } -/* -* A sibling call is a tail-call to another symbol -- to differentiate from a -* recursive tail-call which is to the same symbol. -*/ -static bool jump_is_sibling_call (struct objtool_file *file, -struct instruction *from,struct instruction *to) -{ -struct symbol *fs = from->sym; -struct symbol *ts = to->sym; - -/* Not a sibling call if from/to a symbol hole */ -if (!fs || !ts) -return false; - -/* Not a sibling call if not targeting the start of a symbol. */ -if (!is_first_func_insn (file, to, ts)) -return false; - -/* Disallow sibling calls into STT_NOTYPE */ -if (is_notype_sym (ts)) -return false; - -/* Must not be self to be a sibling */ -return fs->pfunc != ts->pfunc; -} -/* * Find the destination instructions for all jumps. */ static int add_jump_destinations (struct objtool_file *file) { -struct instruction *insn, *jump_dest; +struct instruction *insn;struct reloc *reloc; -struct section *dest_sec; -unsigned long dest_off;int ret;for_each_insn (file, insn) {struct symbol *func =insn_func (insn); +struct instruction *dest_insn; +struct section *dest_sec; +struct symbol *dest_sym; +unsigned long dest_off; +bool dest_undef = false; + +if (!is_static_jump (insn)) +continue ;if (insn->jump_dest) {/* @@ -1503,129 +1488,121 @@ static int add_jump_destinations(struct objtool_file *file)*/ continue ; } -if (!is_static_jump (insn)) -continue ; reloc =insn_reloc (file, insn);if (!reloc) { dest_sec = insn->sec; dest_off =arch_jump_destination (insn); - }else if (is_sec_sym (reloc->sym)) { + }else if (is_undef_sym (reloc->sym)) { + dest_sym = reloc->sym; + dest_undef = true; + }else { dest_sec = reloc->sym->sec; - dest_off =arch_insn_adjusted_addend (insn, reloc); - }else if (reloc->sym->retpoline_thunk) { + dest_off = reloc->sym->sym.st_value + +arch_insn_adjusted_addend (insn, reloc); + } + +if (!dest_undef) { + dest_insn =find_insn (file, dest_sec, dest_off); +if (!dest_insn) { +struct symbol *sym =find_symbol_by_offset (dest_sec, dest_off); + +/* +* retbleed_untrain_ret() jumps to +* __x86_return_thunk(), but objtool can't find +* the thunk's starting RET instruction, +* because the RET is also in the middle of +* another instruction. Objtool only knows +* about the outer instruction. +*/ +if (sym && sym->embedded_insn) { +add_return_call (file, insn, false); +continue ; + } + +/* +* GCOV/KCOV dead code can jump to the end of +* the function/section. +*/ +if (file->ignore_unreachables && func && + dest_sec == insn->sec && + dest_off == func->offset + func->len) +continue ; + +ERROR_INSN (insn,"can't find jump dest instruction at %s+0x%lx" , + dest_sec->name, dest_off); +return -1 ; + } + + dest_sym = dest_insn->sym; +if (!dest_sym) + goto set_jump_dest; + } + +if (dest_sym->retpoline_thunk) { ret =add_retpoline_call (file, insn);if (ret)return ret;continue ; - }else if (reloc->sym->return_thunk) { + } + +if (dest_sym->return_thunk) {add_return_call (file, insn, true);continue ; - }else if (func) { -/* -* External sibling call or internal sibling call with
tools/objtool/check.c static int add_jump_destinations(struct objtool_file *file) · line 1536
Quoted 10 lines
tools/objtool/check.c +0
-10-* STT_FUNC reloc. -*/ - ret =add_call_dest (file, insn, reloc->sym, true); -if (ret) -return ret; -continue ; - }else if (reloc->sym->sec->idx) { - dest_sec = reloc->sym->sec; - dest_off = reloc->sym->sym.st_value + -arch_dest_reloc_offset (reloc_addend (reloc));
Way back in ("[PATCH v2 18/62] objtool: Fix x86 addend calculation"), arch_dest_reloc_offset() was replaced with arch_insn_adjusted_addend(), so I think that patch missed this callsite and breaks bisectability.
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 35/62] objtool: Refactor add_jump_destinations()
Quoted 8 lines · Joe Lawrence
tools/objtool/check.c static int add_jump_destinations(struct objtool_file *file) · line 1536
Quoted 4 lines
tools/objtool/check.c +0
-4- }else if (reloc->sym->sec->idx) { - dest_sec = reloc->sym->sec; - dest_off = reloc->sym->sym.st_value + -arch_dest_reloc_offset (reloc_addend (reloc));Way back in ("[PATCH v2 18/62] objtool: Fix x86 addend calculation"),
arch_dest_reloc_offset()was replaced witharch_insn_adjusted_addend(), so I think that patch missed this callsite and breaks bisectability.
Fixed, thanks.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 36/62 objtool: Simplify special symbol handling in elf_update_symbol()
In reply to Josh Poimboeuf!sym->sec isn't actually a thing: even STT_UNDEF and other special symbol types belong to NULL section 0.
Simplify the initialization of 'shndx' accordingly.
--- tools/objtool/elf.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-)
tools/objtool/elf.c +2 -3
@@ -635,7 +635,7 @@ static int elf_update_sym_relocs(struct elf *elf, struct symbol *sym)static int elf_update_symbol (struct elf *elf,struct section *symtab,struct section *symtab_shndx,struct symbol *sym) { -Elf32_Word shndx = sym->sec ? sym->sec->idx :SHN_UNDEF ; +Elf32_Word shndx;Elf_Data *symtab_data =NULL , *shndx_data =NULL ;Elf64_Xword entsize = symtab->sh.sh_entsize;int max_idx, idx = sym->idx;
@@ -643,8 +643,7 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,bool is_special_shndx = sym->sym.st_shndx >=SHN_LORESERVE && sym->sym.st_shndx !=SHN_XINDEX ; -if (is_special_shndx) - shndx = sym->sym.st_shndx; + shndx = is_special_shndx ? sym->sym.st_shndx : sym->sec->idx; s =elf_getscn (elf->elf, symtab->idx);if (!s) {
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 37/62 objtool: Generalize elf_create_symbol()
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, broaden the elf_create_symbol() interface to give callers more control and reduce duplication of some subtle setup logic.
While at it, make elf_create_symbol() and elf_create_section_symbol() public so sections can be created by the upcoming klp diff code.
--- tools/objtool/elf.c | 111 +++++++++++++++------------- tools/objtool/include/objtool/elf.h | 11 ++- 2 files changed, 69 insertions(+), 53 deletions(-)
tools/objtool/elf.c +59 -52
@@ -761,24 +761,60 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,return 0 ; } -static struct symbol * -__elf_create_symbol (struct elf *elf,struct symbol *sym) +static int elf_add_string (struct elf *elf,struct section *strtab,const char *str); + +struct symbol *elf_create_symbol (struct elf *elf,const char *name, +struct section *sec,unsigned int bind, +unsigned int type,unsigned long offset, +size_t size) {struct section *symtab, *symtab_shndx;Elf32_Word first_non_local, new_idx; -struct symbol *old; +struct symbol *old, *sym; + + sym =calloc (1 ,sizeof (*sym)); +if (!sym) { +ERROR_GLIBC ("calloc" ); +return NULL ; + } + + sym->name =strdup (name); +if (!sym->name) { +ERROR_GLIBC ("strdup" ); +return NULL ; + } + +if (type !=STT_SECTION ) { + sym->sym.st_name =elf_add_string (elf,NULL , sym->name); +if (sym->sym.st_name ==-1 ) +return NULL ; + } + +if (sec) { + sym->sec = sec; + }else { + sym->sec =find_section_by_index (elf,0 ); +if (!sym->sec) { +ERROR ("no NULL section" ); +return NULL ; + } + } + + sym->sym.st_info =GELF_ST_INFO (bind, type); + sym->sym.st_value = offset; + sym->sym.st_size = size; symtab =find_section_by_name (elf,".symtab" ); -if (symtab) { - symtab_shndx =find_section_by_name (elf,".symtab_shndx" ); - }else { +if (!symtab) {ERROR ("no .symtab" );return NULL ; } + symtab_shndx =find_section_by_name (elf,".symtab_shndx" ); + new_idx =sec_num_entries (symtab); -if (GELF_ST_BIND (sym->sym.st_info) !=STB_LOCAL ) +if (bind !=STB_LOCAL ) goto non_local;/*
@@ -816,10 +852,8 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym) non_local: sym->idx = new_idx; -if (elf_update_symbol (elf, symtab, symtab_shndx, sym)) { -ERROR ("elf_update_symbol" ); +if (sym->idx &&elf_update_symbol (elf, symtab, symtab_shndx, sym))return NULL ; - } symtab->sh.sh_size += symtab->sh.sh_entsize;mark_sec_changed (elf, symtab, true);
@@ -829,64 +863,39 @@ __elf_create_symbol(struct elf *elf, struct symbol *sym)mark_sec_changed (elf, symtab_shndx, true); } +elf_add_symbol (elf, sym); +return sym; } -static struct symbol * -elf_create_section_symbol (struct elf *elf,struct section *sec) +struct symbol *elf_create_section_symbol (struct elf *elf,struct section *sec) {struct symbol *sym =calloc (1 ,sizeof (*sym)); -if (!sym) { -ERROR_GLIBC ("malloc" ); + sym =elf_create_symbol (elf, sec->name, sec,STB_LOCAL ,STT_SECTION ,0 ,0 ); +if (!sym)return NULL ; - } - sym->name = sec->name; - sym->sec = sec; - -// st_name 0 - sym->sym.st_info =GELF_ST_INFO (STB_LOCAL ,STT_SECTION ); -// st_other 0 -// st_value 0 -// st_size 0 - - sym =__elf_create_symbol (elf, sym); -if (sym) -elf_add_symbol (elf, sym); + sec->sym = sym;return sym; } -static int elf_add_string (struct elf *elf,struct section *strtab,const char *str); -struct symbol * -elf_create_prefix_symbol (struct elf *elf,struct symbol *orig,long size) +elf_create_prefix_symbol (struct elf *elf,struct symbol *orig,size_t size) { -struct symbol *sym =calloc (1 ,sizeof (*sym));size_t namelen =strlen (orig->name) +sizeof ("__pfx_" ); -char *name =malloc (namelen); - -if (!sym || !name) { -ERROR_GLIBC ("malloc" ); -return NULL ; - } +char name[SYM_NAME_LEN ]; +unsigned long offset;snprintf (name, namelen,"__pfx_%s" , orig->name); - sym->name = name; - sym->sec = orig->sec; + offset = orig->sym.st_value - size; - sym->sym.st_name =elf_add_string (elf,NULL , name); - sym->sym.st_info = orig->sym.st_info; - sym->sym.st_value = orig->sym.st_value - size; - sym->sym.st_size = size; - - sym =__elf_create_symbol (elf, sym); -if (sym) -elf_add_symbol (elf, sym); - -return sym; +return elf_create_symbol (elf, name, orig->sec, +GELF_ST_BIND (orig->sym.st_info), +GELF_ST_TYPE (orig->sym.st_info), + offset, size); } static struct reloc *elf_init_reloc(struct elf *elf, struct section *rsec,
@@ -932,7 +941,7 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, unsigned long insn_off) {struct symbol *sym = insn_sec->sym; -int addend = insn_off; +s64 addend = insn_off;if (!is_text_sec (insn_sec)) {ERROR ("bad call to %s() for data symbol %s" , __func__, sym->name);
@@ -949,8 +958,6 @@ struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, sym =elf_create_section_symbol (elf, insn_sec);if (!sym)return NULL ; - - insn_sec->sym = sym; } return elf_init_reloc(elf, sec->rsec, reloc_idx, offset, sym, addend,
tools/objtool/include/objtool/elf.h +10 -1
@@ -15,6 +15,8 @@#include <linux/jhash.h> #include <arch/elf.h> +#define SYM_NAME_LEN 512 +#ifdef LIBELF_USE_DEPRECATED # define elf_getshdrnum elf_getshnum# define elf_getshdrstrndx elf_getshstrndx
@@ -119,7 +121,14 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name, size_t entsize, unsigned int nr, unsigned int reloc_nr); -struct symbol *elf_create_prefix_symbol (struct elf *elf,struct symbol *orig,long size); +struct symbol *elf_create_symbol (struct elf *elf,const char *name, +struct section *sec,unsigned int bind, +unsigned int type,unsigned long offset, +size_t size); +struct symbol *elf_create_section_symbol (struct elf *elf,struct section *sec); +struct symbol *elf_create_prefix_symbol (struct elf *elf,struct symbol *orig, +size_t size); + struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, unsigned long offset,
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 38/62 objtool: Generalize elf_create_section()
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, broaden the elf_create_section() interface to give callers more control and reduce duplication of some subtle setup logic.
While at it, make elf_create_rela_section() public so sections can be created by the upcoming klp diff code.
--- tools/objtool/elf.c | 118 ++++++++++++++++------------ tools/objtool/include/objtool/elf.h | 7 +- tools/objtool/orc_gen.c | 6 +- 3 files changed, 77 insertions(+), 54 deletions(-)
tools/objtool/elf.c +66 -52
@@ -1136,51 +1136,53 @@ static int elf_add_string(struct elf *elf, struct section *strtab, const char *s }struct section *elf_create_section (struct elf *elf,const char *name, -size_t entsize,unsigned int nr) +size_t size,size_t entsize, +unsigned int type,unsigned int align, +unsigned int flags) {struct section *sec, *shstrtab; -size_t size = entsize * nr;Elf_Scn *s; - sec =malloc (sizeof (*sec)); -if (!sec) { -ERROR_GLIBC ("malloc" ); +if (name &&find_section_by_name (elf, name)) { +ERROR ("section '%s' already exists" , name); +return NULL ; + } + + sec =calloc (1 ,sizeof (*sec)); +if (!sec) { +ERROR_GLIBC ("calloc" );return NULL ; } -memset (sec,0 ,sizeof (*sec));INIT_LIST_HEAD (&sec->symbol_list); +/* don't actually create the section, just the data structures */ +if (type ==SHT_NULL ) + goto add; + s =elf_newscn (elf->elf);if (!s) {ERROR_ELF ("elf_newscn" );return NULL ; } - sec->name =strdup (name); -if (!sec->name) { -ERROR_GLIBC ("strdup" ); -return NULL ; - } - sec->idx =elf_ndxscn (s); - sec->data =elf_newdata (s); -if (!sec->data) { -ERROR_ELF ("elf_newdata" ); -return NULL ; - } - - sec->data->d_size = size; - sec->data->d_align =1 ; -if (size) { - sec->data->d_buf =malloc (size); -if (!sec->data->d_buf) { -ERROR_GLIBC ("malloc" ); + sec->data =elf_newdata (s); +if (!sec->data) { +ERROR_ELF ("elf_newdata" ); +return NULL ; + } + + sec->data->d_size = size; + sec->data->d_align =1 ; + + sec->data->d_buf =calloc (1 , size); +if (!sec->data->d_buf) { +ERROR_GLIBC ("calloc" );return NULL ; } -memset (sec->data->d_buf,0 , size); }if (!gelf_getshdr (s, &sec->sh)) {
@@ -1190,34 +1192,44 @@ struct section *elf_create_section(struct elf *elf, const char *name, sec->sh.sh_size = size; sec->sh.sh_entsize = entsize; - sec->sh.sh_type =SHT_PROGBITS ; - sec->sh.sh_addralign =1 ; - sec->sh.sh_flags =SHF_ALLOC ; + sec->sh.sh_type = type; + sec->sh.sh_addralign = align; + sec->sh.sh_flags = flags; -/* Add section name to .shstrtab (or .strtab for Clang) */ - shstrtab =find_section_by_name (elf,".shstrtab" ); -if (!shstrtab) - shstrtab =find_section_by_name (elf,".strtab" ); -if (!shstrtab) { -ERROR ("can't find .shstrtab or .strtab section" ); -return NULL ; +if (name) { + sec->name =strdup (name); +if (!sec->name) { +ERROR ("strdup" ); +return NULL ; + } + +/* Add section name to .shstrtab (or .strtab for Clang) */ + shstrtab =find_section_by_name (elf,".shstrtab" ); +if (!shstrtab) { + shstrtab =find_section_by_name (elf,".strtab" ); +if (!shstrtab) { +ERROR ("can't find .shstrtab or .strtab" ); +return NULL ; + } + } + sec->sh.sh_name =elf_add_string (elf, shstrtab, sec->name); +if (sec->sh.sh_name ==-1 ) +return NULL ; + +elf_hash_add (section_name, &sec->name_hash,str_hash (sec->name)); } - sec->sh.sh_name =elf_add_string (elf, shstrtab, sec->name); -if (sec->sh.sh_name ==-1 ) -return NULL ; +add:list_add_tail (&sec->list, &elf->sections);elf_hash_add (section, &sec->hash, sec->idx); -elf_hash_add (section_name, &sec->name_hash,str_hash (sec->name));mark_sec_changed (elf, sec, true);return sec; } -static struct section *elf_create_rela_section (struct elf *elf, -struct section *sec, -unsigned int reloc_nr) +struct section *elf_create_rela_section (struct elf *elf,struct section *sec, +unsigned int reloc_nr) {struct section *rsec;char *rsec_name;
@@ -1230,22 +1242,23 @@ static struct section *elf_create_rela_section(struct elf *elf,strcpy (rsec_name,".rela" );strcat (rsec_name, sec->name); - rsec =elf_create_section (elf, rsec_name,elf_rela_size (elf), reloc_nr); + rsec =elf_create_section (elf, rsec_name, reloc_nr *elf_rela_size (elf), +elf_rela_size (elf),SHT_RELA ,elf_addr_size (elf), +SHF_INFO_LINK );free (rsec_name);if (!rsec)return NULL ; - rsec->data->d_type =ELF_T_RELA ; - rsec->sh.sh_type =SHT_RELA ; - rsec->sh.sh_addralign =elf_addr_size (elf); rsec->sh.sh_link =find_section_by_name (elf,".symtab" )->idx; rsec->sh.sh_info = sec->idx; - rsec->sh.sh_flags =SHF_INFO_LINK ; - rsec->relocs =calloc (sec_num_entries (rsec),sizeof (struct reloc )); -if (!rsec->relocs) { -ERROR_GLIBC ("calloc" ); -return NULL ; +if (reloc_nr) { + rsec->data->d_type =ELF_T_RELA ; + rsec->relocs =calloc (sec_num_entries (rsec),sizeof (struct reloc )); +if (!rsec->relocs) { +ERROR_GLIBC ("calloc" ); +return NULL ; + } } sec->rsec = rsec;
@@ -1260,7 +1273,8 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name, {struct section *sec; - sec =elf_create_section (elf, name, entsize, nr); + sec =elf_create_section (elf, name, nr * entsize, entsize, +SHT_PROGBITS ,1 ,SHF_ALLOC );if (!sec)return NULL ;
tools/objtool/include/objtool/elf.h +6 -1
@@ -116,11 +116,16 @@ struct elf {struct elf *elf_open_read (const char *name,int flags);struct section *elf_create_section (struct elf *elf,const char *name, -size_t entsize,unsigned int nr); +size_t size,size_t entsize, +unsigned int type,unsigned int align, +unsigned int flags);struct section *elf_create_section_pair (struct elf *elf,const char *name,size_t entsize,unsigned int nr,unsigned int reloc_nr); +struct section *elf_create_rela_section (struct elf *elf,struct section *sec, +unsigned int reloc_nr); + struct symbol *elf_create_symbol(struct elf *elf, const char *name, struct section *sec, unsigned int bind, unsigned int type, unsigned long offset,
tools/objtool/orc_gen.c +5 -1
@@ -127,7 +127,11 @@ int orc_create(struct objtool_file *file)return -1 ; } orc_sec =elf_create_section (file->elf,".orc_unwind" , -sizeof (struct orc_entry ), nr); + nr *sizeof (struct orc_entry ), +sizeof (struct orc_entry ), +SHT_PROGBITS , +1 , +SHF_ALLOC );if (!orc_sec)return -1 ;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 39/62 objtool: Add elf_create_data()
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, refactor elf_add_string() by adding a new elf_add_data() helper which allows the adding of arbitrary data to a section.
Make both interfaces public so they can be used by the upcoming klp diff code.
--- tools/objtool/elf.c | 66 ++++++++++++++++++++--------- tools/objtool/include/objtool/elf.h | 10 +++-- 2 files changed, 54 insertions(+), 22 deletions(-)
tools/objtool/elf.c +47 -19
@@ -18,10 +18,11 @@#include <errno.h> #include <linux/interval_tree_generic.h> #include <objtool/builtin.h> -#include <objtool/elf.h> #include <objtool/warn.h> +#define ALIGN_UP (x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1)) +static inline u32 str_hash (const char *str) {return jhash (str,strlen (str),0 );
@@ -761,8 +762,6 @@ static int elf_update_symbol(struct elf *elf, struct section *symtab,return 0 ; } -static int elf_add_string (struct elf *elf,struct section *strtab,const char *str); - struct symbol *elf_create_symbol(struct elf *elf, const char *name, struct section *sec, unsigned int bind, unsigned int type, unsigned long offset,
@@ -1098,11 +1097,9 @@ struct elf *elf_open_read(const char *name, int flags)return NULL ; } -static int elf_add_string (struct elf *elf,struct section *strtab,const char *str) +unsigned int elf_add_string (struct elf *elf,struct section *strtab,const char *str) { -Elf_Data *data; -Elf_Scn *s; -int len; +unsigned int offset;if (!strtab) strtab =find_section_by_name (elf,".strtab" );
@@ -1111,28 +1108,59 @@ static int elf_add_string(struct elf *elf, struct section *strtab, const char *sreturn -1 ; } - s =elf_getscn (elf->elf, strtab->idx); +if (!strtab->sh.sh_addralign) { +ERROR ("'%s': invalid sh_addralign" , strtab->name); +return -1 ; + } + + offset =ALIGN_UP (strtab->sh.sh_size, strtab->sh.sh_addralign); + +if (!elf_add_data (elf, strtab, str,strlen (str) +1 )) +return -1 ; + +return offset; +} + +void *elf_add_data (struct elf *elf,struct section *sec,const void *data,size_t size) +{ +unsigned long offset; +Elf_Scn *s; + +if (!sec->sh.sh_addralign) { +ERROR ("'%s': invalid sh_addralign" , sec->name); +return NULL ; + } + + s =elf_getscn (elf->elf, sec->idx);if (!s) {ERROR_ELF ("elf_getscn" ); -return -1 ; +return NULL ; } - data =elf_newdata (s); -if (!data) { + sec->data =elf_newdata (s); +if (!sec->data) {ERROR_ELF ("elf_newdata" ); -return -1 ; +return NULL ; } - data->d_buf =strdup (str); - data->d_size =strlen (str) +1 ; - data->d_align =1 ; + sec->data->d_buf =calloc (1 , size); +if (!sec->data->d_buf) { +ERROR_GLIBC ("calloc" ); +return NULL ; + } - len = strtab->sh.sh_size; - strtab->sh.sh_size += data->d_size; +if (data) +memcpy (sec->data->d_buf, data, size); -mark_sec_changed (elf, strtab, true); + sec->data->d_size = size; + sec->data->d_align =1 ; -return len; + offset =ALIGN_UP (sec->sh.sh_size, sec->sh.sh_addralign); + sec->sh.sh_size = offset + size; + +mark_sec_changed (elf, sec, true); + +return sec->data->d_buf; } struct section *elf_create_section(struct elf *elf, const char *name,
tools/objtool/include/objtool/elf.h +7 -3
@@ -134,6 +134,10 @@ struct symbol *elf_create_section_symbol(struct elf *elf, struct section *sec);struct symbol *elf_create_prefix_symbol (struct elf *elf,struct symbol *orig,size_t size); +void *elf_add_data (struct elf *elf,struct section *sec,const void *data, +size_t size); + +unsigned int elf_add_string (struct elf *elf,struct section *strtab,const char *str); struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, unsigned long offset,
@@ -147,9 +151,9 @@ struct reloc *elf_init_reloc_data_sym(struct elf *elf, struct section *sec,struct symbol *sym, s64 addend); -int elf_write_insn (struct elf *elf,struct section *sec, -unsigned long offset,unsigned int len, -const char *insn); +int elf_write_insn (struct elf *elf,struct section *sec,unsigned long offset, +unsigned int len,const char *insn); +int elf_write (struct elf *elf);void elf_close (struct elf *elf);
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 40/62 objtool: Introduce elf_create_reloc() and elf_init_reloc()
In reply to Josh Poimboeufelf_create_rela_section() is quite limited in that it requires the caller to know how many relocations need to be allocated up front.
In preparation for the objtool klp diff subcommand, allow an arbitrary number of relocations to be created and initialized on demand after section creation.
--- tools/objtool/elf.c | 151 +++++++++++++++++++++++++--- tools/objtool/include/objtool/elf.h | 9 ++ 2 files changed, 145 insertions(+), 15 deletions(-)
tools/objtool/elf.c +136 -15
@@ -22,6 +22,8 @@#include <objtool/warn.h> #define ALIGN_UP (x, align_to) (((x) + ((align_to)-1)) & ~((align_to)-1)) +#define ALIGN_UP_POW2 (x) (1U << ((8 * sizeof(x)) - __builtin_clz((x) - 1U))) +#define MAX (a, b) ((a) > (b) ? (a) : (b))static inline u32 str_hash (const char *str) {
@@ -897,10 +899,9 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, size_t size) offset, size); } -static struct reloc *elf_init_reloc (struct elf *elf,struct section *rsec, -unsigned int reloc_idx, -unsigned long offset,struct symbol *sym, -s64 addend,unsigned int type) +struct reloc *elf_init_reloc (struct elf *elf,struct section *rsec, +unsigned int reloc_idx,unsigned long offset, +struct symbol *sym,s64 addend,unsigned int type) {struct reloc *reloc, empty = {0 };
@@ -1002,12 +1003,14 @@ static int read_relocs(struct elf *elf) rsec->base->rsec = rsec; - nr_reloc =0 ; - rsec->relocs =calloc (sec_num_entries (rsec),sizeof (*reloc)); + rsec->nr_alloc_relocs =sec_num_entries (rsec); + rsec->relocs =calloc (rsec->nr_alloc_relocs,sizeof (*reloc));if (!rsec->relocs) {ERROR_GLIBC ("calloc" );return -1 ; } + + nr_reloc =0 ;for (i =0 ; i <sec_num_entries (rsec); i++) { reloc = &rsec->relocs[i];
@@ -1256,8 +1259,99 @@ struct section *elf_create_section(struct elf *elf, const char *name,return sec; } +static int elf_alloc_reloc (struct elf *elf,struct section *rsec) +{ +struct reloc *old_relocs, *old_relocs_end, *new_relocs; +unsigned int nr_relocs_old =sec_num_entries (rsec); +unsigned int nr_relocs_new = nr_relocs_old +1 ; +unsigned long nr_alloc; +struct symbol *sym; + +if (!rsec->data) { + rsec->data =elf_newdata (elf_getscn (elf->elf, rsec->idx)); +if (!rsec->data) { +ERROR_ELF ("elf_newdata" ); +return -1 ; + } + + rsec->data->d_align =1 ; + rsec->data->d_type =ELF_T_RELA ; + rsec->data->d_buf =NULL ; + } + + rsec->data->d_size = nr_relocs_new *elf_rela_size (elf); + rsec->sh.sh_size = rsec->data->d_size; + + nr_alloc =MAX (64 ,ALIGN_UP_POW2 (nr_relocs_new)); +if (nr_alloc <= rsec->nr_alloc_relocs) +return 0 ; + rsec->nr_alloc_relocs = nr_alloc; + + rsec->data->d_buf =realloc (rsec->data->d_buf, + nr_alloc *elf_rela_size (elf)); +if (!rsec->data->d_buf) { +ERROR_GLIBC ("realloc" ); +return -1 ; + } + + old_relocs = rsec->relocs; + new_relocs =calloc (nr_alloc,sizeof (struct reloc )); +if (!new_relocs) { +ERROR_GLIBC ("calloc" ); +return -1 ; + } + +if (!old_relocs) + goto done; + +/* +* The struct reloc's address has changed. Update all the symbols and +* relocs which reference it. +*/ + + old_relocs_end = &old_relocs[nr_relocs_old]; +for_each_sym (elf, sym) { +struct reloc *reloc; + + reloc = sym->relocs; +if (!reloc) +continue ; + +if (reloc >= old_relocs && reloc < old_relocs_end) + sym->relocs = &new_relocs[reloc - old_relocs]; + +while (1 ) { +struct reloc *next_reloc =sym_next_reloc (reloc); + +if (!next_reloc) +break ; + +if (next_reloc >= old_relocs && next_reloc < old_relocs_end) +set_sym_next_reloc (reloc, &new_relocs[next_reloc - old_relocs]); + + reloc = next_reloc; + } + } + +memcpy (new_relocs, old_relocs, nr_relocs_old *sizeof (struct reloc )); + +for (int i =0 ; i < nr_relocs_old; i++) { +struct reloc *old = &old_relocs[i]; +struct reloc *new = &new_relocs[i]; +u32 key =reloc_hash (old); + +elf_hash_del (reloc, &old->hash, key); +elf_hash_add (reloc, &new->hash, key); + } + +free (old_relocs); +done: + rsec->relocs = new_relocs; +return 0 ; +} +struct section *elf_create_rela_section (struct elf *elf,struct section *sec, -unsigned int reloc_nr) +unsigned int nr_relocs) {struct section *rsec;char *rsec_name;
@@ -1270,34 +1364,61 @@ struct section *elf_create_rela_section(struct elf *elf, struct section *sec,strcpy (rsec_name,".rela" );strcat (rsec_name, sec->name); - rsec =elf_create_section (elf, rsec_name, reloc_nr *elf_rela_size (elf), + rsec =elf_create_section (elf, rsec_name, nr_relocs *elf_rela_size (elf),elf_rela_size (elf),SHT_RELA ,elf_addr_size (elf),SHF_INFO_LINK );free (rsec_name);if (!rsec)return NULL ; - rsec->sh.sh_link =find_section_by_name (elf,".symtab" )->idx; - rsec->sh.sh_info = sec->idx; - -if (reloc_nr) { +if (nr_relocs) { rsec->data->d_type =ELF_T_RELA ; - rsec->relocs =calloc (sec_num_entries (rsec),sizeof (struct reloc )); + + rsec->nr_alloc_relocs = nr_relocs; + rsec->relocs =calloc (nr_relocs,sizeof (struct reloc ));if (!rsec->relocs) {ERROR_GLIBC ("calloc" );return NULL ; } } + rsec->sh.sh_link =find_section_by_name (elf,".symtab" )->idx; + rsec->sh.sh_info = sec->idx; + sec->rsec = rsec; rsec->base = sec;return rsec; } +struct reloc *elf_create_reloc (struct elf *elf,struct section *sec, +unsigned long offset, +struct symbol *sym,s64 addend, +unsigned int type) +{ +struct section *rsec = sec->rsec; + +if (!rsec) { + rsec =elf_create_rela_section (elf, sec,0 ); +if (!rsec) +return NULL ; + } + +if (find_reloc_by_dest (elf, sec, offset)) { +ERROR_FUNC (sec, offset,"duplicate reloc" ); +return NULL ; + } + +if (elf_alloc_reloc (elf, rsec)) +return NULL ; + +return elf_init_reloc (elf, rsec,sec_num_entries (rsec) -1 , offset, sym, + addend, type); +} +struct section *elf_create_section_pair (struct elf *elf,const char *name,size_t entsize,unsigned int nr, -unsigned int reloc_nr) +unsigned int nr_relocs) {struct section *sec;
@@ -1306,7 +1427,7 @@ struct section *elf_create_section_pair(struct elf *elf, const char *name,if (!sec)return NULL ; -if (!elf_create_rela_section (elf, sec, reloc_nr)) +if (!elf_create_rela_section (elf, sec, nr_relocs))return NULL ;return sec;
tools/objtool/include/objtool/elf.h +9 -0
@@ -47,6 +47,7 @@ struct section {int idx;bool _changed, text, rodata, noinstr, init, truncate;struct reloc *relocs; +unsigned long nr_alloc_relocs; };struct symbol {
@@ -139,6 +140,14 @@ void *elf_add_data(struct elf *elf, struct section *sec, const void *data,unsigned int elf_add_string (struct elf *elf,struct section *strtab,const char *str); +struct reloc *elf_create_reloc (struct elf *elf,struct section *sec, +unsigned long offset,struct symbol *sym, +s64 addend,unsigned int type); + +struct reloc *elf_init_reloc (struct elf *elf,struct section *rsec, +unsigned int reloc_idx,unsigned long offset, +struct symbol *sym,s64 addend,unsigned int type); + struct reloc *elf_init_reloc_text_sym(struct elf *elf, struct section *sec, unsigned long offset, unsigned int reloc_idx,
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 41/62 objtool: Add elf_create_file()
In reply to Josh PoimboeufAdd interface to enable the creation of a new ELF file.
--- tools/objtool/builtin-check.c | 2 +- tools/objtool/elf.c | 144 +++++++++++++++++++++++++++- tools/objtool/include/objtool/elf.h | 5 +- 3 files changed, 147 insertions(+), 4 deletions(-)
tools/objtool/builtin-check.c +1 -1
@@ -330,5 +330,5 @@ int objtool_run(int argc, const char **argv)if (!opts.dryrun && file->elf->changed &&elf_write (file->elf))return 1 ; -return 0 ; +return elf_close (file->elf); }
tools/objtool/elf.c +143 -1
176 lines not on this page · raw message
tools/objtool/include/objtool/elf.h +3 -2
@@ -93,7 +93,7 @@ struct elf {GElf_Ehdr ehdr;int fd;bool changed; -const char *name; +const char *name, *tmp_name;unsigned int num_files;struct list_head sections;unsigned long num_relocs;
@@ -115,6 +115,7 @@ struct elf { };struct elf *elf_open_read (const char *name,int flags); +struct elf *elf_create_file (GElf_Ehdr *ehdr,const char *name); struct section *elf_create_section(struct elf *elf, const char *name, size_t size, size_t entsize,
@@ -164,7 +165,7 @@ int elf_write_insn(struct elf *elf, struct section *sec, unsigned long offset, unsigned int len, const char *insn);int elf_write (struct elf *elf); -void elf_close (struct elf *elf); +int elf_close (struct elf *elf);struct section *find_section_by_name (const struct elf *elf,const char *name);struct symbol *find_func_by_offset (struct section *sec,unsigned long offset);
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 42/62 kbuild,x86: Fix module permissions for __jump_table and __bug_table
In reply to Josh PoimboeufAn upcoming patch will add the SHF_MERGE flag to x86 __jump_table and __bug_table so their entry sizes can be defined in inline asm.
However, those sections have SHF_WRITE, which the Clang linker (lld) explicitly forbids combining with SHF_MERGE.
Those sections are modified at runtime and must remain writable. While SHF_WRITE is ignored by vmlinux, it's still needed for modules.
To work around the linker interference, remove SHF_WRITE during compilation and restore it after linking the module.
--- arch/Kconfig | 3 +++ arch/x86/Kconfig | 1 + arch/x86/include/asm/bug.h | 4 ++-- arch/x86/include/asm/jump_label.h | 2 +- scripts/Makefile.modfinal | 18 +++++++++++++----- 5 files changed, 20 insertions(+), 8 deletions(-)
arch/Kconfig +3 -0
@@ -1314,6 +1314,9 @@ config HAVE_NOINSTR_HACK config HAVE_NOINSTR_VALIDATION bool +config NEED_MODULE_PERMISSIONS_FIX + bool + config HAVE_UACCESS_VALIDATION bool select OBJTOOL
arch/x86/Kconfig +1 -0
@@ -309,6 +309,7 @@ config X86 select HOTPLUG_SPLIT_STARTUP if SMP && X86_32 select IRQ_FORCED_THREADING select LOCK_MM_AND_FIND_VMA + select NEED_MODULE_PERMISSIONS_FIX select NEED_PER_CPU_EMBED_FIRST_CHUNK select NEED_PER_CPU_PAGE_FIRST_CHUNK select NEED_SG_DMA_LENGTH
arch/x86/include/asm/bug.h +2 -2
@@ -42,7 +42,7 @@#define _BUG_FLAGS (ins, flags, extra) \ do { \ asm_inline volatile("1:\t" ins "\n" \ - ".pushsection __bug_table,\"aw\"\n" \ + ".pushsection __bug_table,\"a\"\n" \ "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ "\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \ "\t.word %c1" "\t# bug_entry::line\n" \
@@ -60,7 +60,7 @@ do { \#define _BUG_FLAGS (ins, flags, extra) \ do { \ asm_inline volatile("1:\t" ins "\n" \ - ".pushsection __bug_table,\"aw\"\n" \ + ".pushsection __bug_table,\"a\"\n" \ "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ "\t.word %c0" "\t# bug_entry::flags\n" \ "\t.org 2b+%c1\n" \
arch/x86/include/asm/jump_label.h +1 -1
@@ -13,7 +13,7 @@#include <linux/types.h> #define JUMP_TABLE_ENTRY (key, label) \ - ".pushsection __jump_table, \"aw\" \n\t" \ + ".pushsection __jump_table, \"a\"\n\t" \ _ASM_ALIGN "\n\t" \ ".long 1b - . \n\t" \ ".long " label " - . \n\t" \
scripts/Makefile.modfinal +13 -5
@@ -28,12 +28,23 @@ ccflags-remove-y := $(CC_FLAGS_CFI) .module-common.o: $(srctree)/scripts/module-common.c FORCE $(call if_changed_rule,cc_o_c) +ifdef CONFIG_NEED_MODULE_PERMISSIONS_FIX +cmd_fix_mod_permissions = \ + $(OBJCOPY) --set-section-flags __jump_table=alloc,data \ + --set-section-flags __bug_table=alloc,data $@ +endif + quiet_cmd_ld_ko_o = LD [M] $@ cmd_ld_ko_o = \ $(LD) -r $(KBUILD_LDFLAGS) \ $(KBUILD_LDFLAGS_MODULE) $(LDFLAGS_MODULE) \ -T $(objtree)/scripts/module.lds -o $@ $(filter %.o, $^) +define rule_ld_ko_o + $(call cmd_and_savecmd,ld_ko_o) + $(call cmd,fix_mod_permissions) +endef + quiet_cmd_btf_ko = BTF [M] $@ cmd_btf_ko = \ if [ ! -f $(objtree)/vmlinux ]; then \
@@ -46,14 +57,11 @@ quiet_cmd_btf_ko = BTF [M] $@ # Same as newer-prereqs, but allows to exclude specified extra dependencies newer_prereqs_except = $(filter-out $(PHONY) $(1),$?) -# Same as if_changed, but allows to exclude specified extra dependencies -if_changed_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check), \ - $(cmd); \ - printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:) +if_changed_rule_except = $(if $(call newer_prereqs_except,$(2))$(cmd-check),$(rule_$(1)),@:) # Re-generate module BTFs if either module's .ko or vmlinux changed %.ko: %.o %.mod.o .module-common.o $(objtree)/scripts/module.lds $(and $(CONFIG_DEBUG_INFO_BTF_MODULES),$(KBUILD_BUILTIN),$(objtree)/vmlinux) FORCE - +$(call if_changed_except,ld_ko_o,$(objtree)/vmlinux) + +$(call if_changed_rule_except,ld_ko_o,$(objtree)/vmlinux) ifdef CONFIG_DEBUG_INFO_BTF_MODULES +$(if $(newer-prereqs),$(call cmd,btf_ko)) endif
Signature · 1 line
2.49.0
Peter Zijlstra
maintainerRe: [PATCH v2 42/62] kbuild,x86: Fix module permissions for __jump_table and __bug_table
Quoted 11 lines · Josh Poimboeuf
An upcoming patch will add the
SHF_MERGEflag to x86__jump_tableand__bug_tableso their entry sizes can be defined in inline asm.However, those sections have
SHF_WRITE, which the Clang linker (lld) explicitly forbids combining withSHF_MERGE.Those sections are modified at runtime and must remain writable. While
SHF_WRITEis ignored by vmlinux, it's still needed for modules.To work around the linker interference, remove
SHF_WRITEduring compilation and restore it after linking the module.
*groan*
This and the following patches marking a whole bunch of sections M, seems to suggest you're going to rely on sh_entsize actually working.
There was an ld.lld bug, and IIRC you need to enforce llvm-20 or later if you want this to be so.
Josh Poimboeuf
maintainerRe: [PATCH v2 42/62] kbuild,x86: Fix module permissions for __jump_table and __bug_table
Quoted 13 lines · Peter Zijlstra
Quoted 11 lines · Josh Poimboeuf
An upcoming patch will add the
SHF_MERGEflag to x86__jump_tableand__bug_tableso their entry sizes can be defined in inline asm.However, those sections have
SHF_WRITE, which the Clang linker (lld) explicitly forbids combining withSHF_MERGE.Those sections are modified at runtime and must remain writable. While
SHF_WRITEis ignored by vmlinux, it's still needed for modules.To work around the linker interference, remove
SHF_WRITEduring compilation and restore it after linking the module.
Quoted 7 lines
*groan*
This and the following patches marking a whole bunch of sections M, seems to suggest you're going to rely on
sh_entsizeactually working.There was an ld.lld bug, and IIRC you need to enforce llvm-20 or later if you want this to be so.
Hm, ISTR this working with clang 18, I'll go test that again.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerRe: [PATCH v2 42/62] kbuild,x86: Fix module permissions for __jump_table and __bug_table
Quoted 16 lines · Josh Poimboeuf
Quoted 15 lines · Peter Zijlstra
Quoted 11 lines · Josh Poimboeuf
An upcoming patch will add the
SHF_MERGEflag to x86__jump_tableand__bug_tableso their entry sizes can be defined in inline asm.However, those sections have
SHF_WRITE, which the Clang linker (lld) explicitly forbids combining withSHF_MERGE.Those sections are modified at runtime and must remain writable. While
SHF_WRITEis ignored by vmlinux, it's still needed for modules.To work around the linker interference, remove
SHF_WRITEduring compilation and restore it after linking the module.*groan*
Quoted 7 lines
Quoted 5 lines
This and the following patches marking a whole bunch of sections M, seems to suggest you're going to rely on
sh_entsizeactually working.There was an ld.lld bug, and IIRC you need to enforce llvm-20 or later if you want this to be so.
Hm, ISTR this working with clang 18, I'll go test that again.
You're right, looks like sh_entsize is getting cleared by the linker with my Clang 18. I guess I tested with newer Clang.
"objtool klp diff" fails with:
vmlinux.o: error: objtool: .discard.annotate_insn: unknown entry size
So yeah, non-buggy linker is already being enforced, though I should probably make the error more human friendly.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 43/62 x86/alternative: Define ELF section entry size for alternatives
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, define the entry size for the .altinstructions section in its ELF header. This will allow tooling to extract individual entries.
--- arch/x86/include/asm/alternative.h | 7 +++++-- arch/x86/kernel/alternative.c | 2 ++ 2 files changed, 7 insertions(+), 2 deletions(-)
arch/x86/include/asm/alternative.h +5 -2
@@ -15,6 +15,8 @@#define ALT_DIRECT_CALL (feature) ((ALT_FLAG_DIRECT_CALL << ALT_FLAGS_SHIFT) | (feature))#define ALT_CALL_ALWAYS ALT_DIRECT_CALL(X86_FEATURE_ALWAYS) +#define ALTINSTR_SIZE 14 +#ifndef __ASSEMBLER__#include <linux/stddef.h>
@@ -165,7 +167,8 @@ static inline int alternatives_text_reserved(void *start, void *end) "773:\n"#define ALTINSTR_ENTRY (ft_flags) \ - ".pushsection .altinstructions,\"a\"\n" \ + ".pushsection .altinstructions, \"aM\", @progbits, " \ + __stringify(ALTINSTR_SIZE) "\n" \ " .long 771b - .\n"/* label */ \" .long 774f - .\n" /* new instruction */ \ " .4byte " __stringify(ft_flags) "\n" /* feature + flags */ \
@@ -328,7 +331,7 @@ void nop_func(void); 741: \ .skip -(((744f-743f)-(741b-740b)) > 0) * ((744f-743f)-(741b-740b)),0x90 ;\ 742: \ - .pushsection .altinstructions,"a" ; \ + .pushsection .altinstructions, "aM", @progbits, ALTINSTR_SIZE ; \altinstr_entry 740b,743f,flag,742b-740b,744f-743f ; \ .popsection ; \ .pushsection .altinstr_replacement," ax" ; \
arch/x86/kernel/alternative.c +2 -0
@@ -425,6 +425,8 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,u8 *instr, *replacement;struct alt_instr *a, *b; +BUILD_BUG_ON (ALTINSTR_SIZE !=sizeof (struct alt_instr )); +DPRINTK (ALT ,"alt table %px, -> %px" , start, end);/*
Signature · 1 line
2.49.0
Borislav Petkov
maintainerRe: [PATCH v2 43/62] x86/alternative: Define ELF section entry size for alternatives
Quoted 1 line · Josh Poimboeuf
+#define
ALTINSTR_SIZE14
We have sizeof(struct alt_instr) to offer...
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteJosh Poimboeuf
maintainerRe: [PATCH v2 43/62] x86/alternative: Define ELF section entry size for alternatives
Quoted 4 lines · Borislav Petkov
Quoted 1 line · Josh Poimboeuf
+#define
ALTINSTR_SIZE14We have
sizeof(struct alt_instr)to offer...
Right, but IIRC, sizeof(struct alt_instr) isn't available at macro expansion time so it would have to be provided as an input constraint.
That doesn't really work for the ALTERNATIVE macro, where the asm constraints are out of our control because they're set by the caller.
Signature · 1 line
Josh
Borislav Petkov
maintainerRe: [PATCH v2 43/62] x86/alternative: Define ELF section entry size for alternatives
Quoted 11 lines · Josh Poimboeuf
Quoted 4 lines · Borislav Petkov
Quoted 1 line · Josh Poimboeuf
+#define
ALTINSTR_SIZE14We have
sizeof(struct alt_instr)to offer...Right, but IIRC,
sizeof(struct alt_instr)isn't available at macro expansion time so it would have to be provided as an input constraint.That doesn't really work for the ALTERNATIVE macro, where the asm constraints are out of our control because they're set by the caller.
Bah, that doesn't work. And you're enforcing it with BUILD_BUG_ON(). Oh well, ignore the noise.
:-)
Signature · 4 lines
Regards/Gruss,
Boris.
https://people.kernel.org/tglx/notes-about-netiquetteJosh Poimboeuf
maintainerPATCH v2 44/62 x86/jump_label: Define ELF section entry size for jump table
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, define the entry size for the __jump_table section in its ELF header. This will allow tooling to extract individual entries.
--- arch/x86/include/asm/jump_label.h | 32 +++++++++++++++++-------------- include/linux/jump_label.h | 20 +++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-)
arch/x86/include/asm/jump_label.h +18 -14
@@ -12,29 +12,31 @@#include <linux/stringify.h> #include <linux/types.h> -#define JUMP_TABLE_ENTRY (key, label) \ - ".pushsection __jump_table, \"a\"\n\t" \ - _ASM_ALIGN "\n\t" \ - ".long 1b - . \n\t" \ - ".long " label " - . \n\t" \ - _ASM_PTR " " key " - . \n\t" \ +#define JUMP_TABLE_ENTRY (key, label, size) \ + ".pushsection __jump_table, \"aM\", @progbits, " size "\n\t" \ + _ASM_ALIGN "\n\t" \ + ".long 1b - . \n\t" \ + ".long " label " - . \n\t" \ + _ASM_PTR " " key " - . \n\t" \ ".popsection \n\t"/* This macro is also expanded on the Rust side. */ #ifdef CONFIG_HAVE_JUMP_LABEL_HACK -#define ARCH_STATIC_BRANCH_ASM (key, label) \ +#define ARCH_STATIC_BRANCH_ASM (key, label, size) \ "1: jmp " label " # objtool NOPs this \n\t" \ - JUMP_TABLE_ENTRY(key " + 2", label) + JUMP_TABLE_ENTRY(key " + 2", label, size)#else /* !CONFIG_HAVE_JUMP_LABEL_HACK */ -#define ARCH_STATIC_BRANCH_ASM (key, label) \ +#define ARCH_STATIC_BRANCH_ASM (key, label, size) \ "1: .byte " __stringify(BYTES_NOP5) "\n\t" \ - JUMP_TABLE_ENTRY(key, label) + JUMP_TABLE_ENTRY(key, label, size)#endif /* CONFIG_HAVE_JUMP_LABEL_HACK */ static __always_inline bool arch_static_branch(struct static_key * const key, const bool branch) { - asm goto(ARCH_STATIC_BRANCH_ASM("%c0 + %c1", "%l[l_yes]") - : : "i" (key), "i" (branch) : : l_yes); + asm goto(ARCH_STATIC_BRANCH_ASM("%c[key] + %c[branch]", "%l[l_yes]", "%c[size]") + : : [key] "i" (key), [branch] "i" (branch), + [size] "i" (sizeof(struct jump_entry)) + : : l_yes);return false; l_yes:
@@ -45,8 +47,10 @@ static __always_inline bool arch_static_branch_jump(struct static_key * const ke { asm goto("1:" "jmp %l[l_yes]\n\t" - JUMP_TABLE_ENTRY("%c0 + %c1", "%l[l_yes]") - : : "i" (key), "i" (branch) : : l_yes); + JUMP_TABLE_ENTRY("%c[key] + %c[branch]", "%l[l_yes]", "%c[size]") + : : [key] "i" (key), [branch] "i" (branch), + [size] "i" (sizeof(struct jump_entry)) + : : l_yes);return false; l_yes:
include/linux/jump_label.h +12 -8
@@ -110,16 +110,20 @@ struct static_key {#endif /* __ASSEMBLY__ */ #ifdef CONFIG_JUMP_LABEL -#include <asm/jump_label.h> - -#ifndef __ASSEMBLY__ -#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE +#if defined(CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE) && !defined(__ASSEMBLY__) +/* Must be defined before including <asm/jump_label.h> */ struct jump_entry { s32 code; s32 target; long key; // key may be far away from the core kernel under KASLR }; +#endif + +#include <asm/jump_label.h> + +#ifndef __ASSEMBLY__ +#ifdef CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE static inline unsigned long jump_entry_code(const struct jump_entry *entry) {
@@ -138,7 +142,7 @@ static inline struct static_key *jump_entry_key(const struct jump_entry *entry) return (struct static_key *)((unsigned long)&entry->key + offset); } -#else +#else /* !CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE */ static inline unsigned long jump_entry_code (const struct jump_entry *entry) {
@@ -155,7 +159,7 @@ static inline struct static_key *jump_entry_key(const struct jump_entry *entry) return (struct static_key *)((unsigned long)entry->key & ~3UL); } -#endif +#endif /* !CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE */ static inline bool jump_entry_is_branch (const struct jump_entry *entry) {
@@ -184,8 +188,8 @@ static inline int jump_entry_size(struct jump_entry *entry)#endif } -#endif -#endif +#endif /* !__ASSEMBLY__ */ +#endif /* CONFIG_JUMP_LABEL */ #ifndef __ASSEMBLY__
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 45/62 x86/extable: Define ELF section entry size for exception tables
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, define the entry size for the __ex_table section in its ELF header. This will allow tooling to extract individual entries.
--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)
arch/x86/include/asm/asm.h +12 -8
@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)# include <asm/extable_fixup_types.h> +#define EXTABLE_SIZE 12 +/* Exception table entry */ #ifdef __ASSEMBLER__ -# define _ASM_EXTABLE_TYPE (from, to, type) \ - .pushsection "__ex_table","a" ; \ - .balign 4 ; \ - .long (from) - . ; \ - .long (to) - . ; \ - .long type ; \ +# define _ASM_EXTABLE_TYPE (from, to, type) \ + .pushsection "__ex_table", "aM", @progbits, EXTABLE_SIZE; \ + .balign 4 ; \ + .long (from) - . ; \ + .long (to) - . ; \ + .long type ; \ .popsection# ifdef CONFIG_KPROBES
@@ -189,7 +191,8 @@ static __always_inline __pure void *rip_rel_ptr(void *p) ".purgem extable_type_reg\n"# define _ASM_EXTABLE_TYPE (from, to, type) \ - " .pushsection \"__ex_table\",\"a\"\n" \ + " .pushsection __ex_table, \"aM\", @progbits, " \ + __stringify(EXTABLE_SIZE) "\n" \ " .balign 4\n" \ " .long (" #from ") - .\n" \ " .long (" #to ") - .\n" \
@@ -197,7 +200,8 @@ static __always_inline __pure void *rip_rel_ptr(void *p) " .popsection\n"# define _ASM_EXTABLE_TYPE_REG (from, to, type, reg) \ - " .pushsection \"__ex_table\",\"a\"\n" \ + " .pushsection __ex_table, \"aM\", @progbits, " \ + __stringify(EXTABLE_SIZE) "\n" \ " .balign 4\n" \ " .long (" #from ") - .\n" \ " .long (" #to ") - .\n" \
kernel/extable.c +2 -0
@@ -55,6 +55,8 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) {const struct exception_table_entry *e; +BUILD_BUG_ON (EXTABLE_SIZE !=sizeof (struct exception_table_entry )); + e =search_kernel_exception_table (addr);if (!e) e =search_module_extables (addr);
Signature · 1 line
2.49.0
Joe Lawrence
maintainerRe: [PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables
Quoted 15 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, define the entry size for the
__ex_tablesection in its ELF header. This will allow tooling to extract individual entries.Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)arch/x86/include/asm/asm.h +0
-0
arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 144
Quoted 7 lines
arch/x86/include/asm/asm.h +2
-0@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)# include <asm/extable_fixup_types.h> +#define EXTABLE_SIZE 12 + > [ ... snip ... ]
EXTABLE_SIZE defined in arch/x86/ ...
kernel/extable.c const struct exception_table_entry *search_exception_tables(unsigned long addr) · line 59
Quoted 10 lines
kernel/extable.c +2
-0@@ -55,6 +55,8 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) {const struct exception_table_entry *e; +BUILD_BUG_ON (EXTABLE_SIZE !=sizeof (struct exception_table_entry )); +
but referenced in kernel/ where a non-x86 build like ppc64le build won't know what EXTABLE_SIZE is :(
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables
Quoted 34 lines · Joe Lawrence
Quoted 15 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, define the entry size for the
__ex_tablesection in its ELF header. This will allow tooling to extract individual entries.Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)arch/x86/include/asm/asm.h +0
-0arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 144
Quoted 7 lines
arch/x86/include/asm/asm.h +2
-0@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)# include <asm/extable_fixup_types.h> +#define EXTABLE_SIZE 12 + > [ ... snip ... ]
EXTABLE_SIZEdefined inarch/x86/...Quoted 8 lines
kernel/extable.c +0
-0@@ -55,6 +55,8 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) {const struct exception_table_entry *e;
Quoted 5 lines
kernel/extable.c const struct exception_table_entry *search_exception_tables(unsigned long addr) · line 59
Quoted 2 lines
kernel/extable.c +2
-0+BUILD_BUG_ON (EXTABLE_SIZE !=sizeof (struct exception_table_entry )); +but referenced in
kernel/where a non-x86 build like ppc64le build won't know whatEXTABLE_SIZEis :(
Thanks, I'll move the BUILD_BUG_ON() to the x86 extable code:
arch/x86/mm/extable.c +2 -0
@@ -303,6 +303,8 @@ int fixup_exception(struct pt_regs *regs, int trapnr, unsigned long error_code,const struct exception_table_entry *e;int type, reg, imm; +BUILD_BUG_ON (EXTABLE_SIZE !=sizeof (struct exception_table_entry )); +#ifdef CONFIG_PNPBIOS if (unlikely (SEGMENT_IS_PNP_CODE (regs->cs))) {extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
kernel/extable.c +0 -2
@@ -55,8 +55,6 @@ const struct exception_table_entry *search_exception_tables(unsigned long addr) {const struct exception_table_entry *e; -BUILD_BUG_ON (EXTABLE_SIZE !=sizeof (struct exception_table_entry )); - e =search_kernel_exception_table (addr);if (!e) e =search_module_extables (addr);
Re: [PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables
In reply to Josh PoimboeufQuoted 16 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, define the entry size for the
__ex_tablesection in its ELF header. This will allow tooling to extract individual entries.Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)arch/x86/include/asm/asm.h +0
-0
arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 141
Quoted 4 lines
arch/x86/include/asm/asm.h +1
-0@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)# include <asm/extable_fixup_types.h> +#define EXTABLE_SIZE 12
Put this in asm-offsets.c instead. That removes the need for the BUILD_BUG_ON().
Brian Gerst
Josh Poimboeuf
maintainerRe: [PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables
Quoted 18 lines · Brian Gerst
Quoted 17 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, define the entry size for the
__ex_tablesection in its ELF header. This will allow tooling to extract individual entries.Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)arch/x86/include/asm/asm.h +0
-0@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
Quoted 5 lines
arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 141
Quoted 3 lines
arch/x86/include/asm/asm.h +1
-0# include <asm/extable_fixup_types.h> +#define EXTABLE_SIZE 12Put this in
asm-offsets.cinstead.
But that's only for .S code right? This is also needed for inline asm.
Signature · 1 line
Josh
Re: [PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables
Quoted 20 lines · Josh Poimboeuf
Quoted 18 lines · Brian Gerst
Quoted 17 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, define the entry size for the
__ex_tablesection in its ELF header. This will allow tooling to extract individual entries.Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)arch/x86/include/asm/asm.h +0
-0@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)
Quoted 7 lines
Quoted 5 lines
arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 141
Quoted 3 lines
arch/x86/include/asm/asm.h +1
-0# include <asm/extable_fixup_types.h> +#define EXTABLE_SIZE 12Put this in
asm-offsets.cinstead.But that's only for .S code right? This is also needed for inline asm.
<asm/asm-offsets.h> can be used in C code too. Normally it wouldn't be needed but the inline asm case is a valid use.
Brian Gerst
Josh Poimboeuf
maintainerRe: [PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables
Quoted 23 lines · Brian Gerst
Quoted 22 lines · Josh Poimboeuf
Quoted 20 lines · Brian Gerst
Quoted 19 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, define the entry size for the
__ex_tablesection in its ELF header. This will allow tooling to extract individual entries.Signed-off-by Josh Poimboeuf <[email protected]>--- arch/x86/include/asm/asm.h | 20 ++++++++++++-------- kernel/extable.c | 2 ++ 2 files changed, 14 insertions(+), 8 deletions(-)arch/x86/include/asm/asm.h +0
-0@@ -138,15 +138,17 @@ static __always_inline __pure void *rip_rel_ptr(void *p)# include <asm/extable_fixup_types.h>
Quoted 8 lines
Quoted 5 lines
Quoted 3 lines
arch/x86/include/asm/asm.h static __always_inline __pure void *rip_rel_ptr(void *p) · line 141
Quoted 1 line
arch/x86/include/asm/asm.h +1
-0+#define EXTABLE_SIZE 12Put this in
asm-offsets.cinstead.But that's only for .S code right? This is also needed for inline asm.
<
asm/asm-offsets.h> can be used in C code too. Normally it wouldn't be needed but the inline asm case is a valid use.
Ah, nice. This is much better. Thanks!
arch/x86/include/asm/alternative.h +2 -4
@@ -16,8 +16,6 @@#define ALT_DIRECT_CALL (feature) ((ALT_FLAG_DIRECT_CALL << ALT_FLAGS_SHIFT) | (feature))#define ALT_CALL_ALWAYS ALT_DIRECT_CALL(X86_FEATURE_ALWAYS) -#define ALTINSTR_SIZE 14 -#ifndef __ASSEMBLER__#include <linux/stddef.h>
@@ -200,7 +198,7 @@ static inline int alternatives_text_reserved(void *start, void *end)#define ALTINSTR_ENTRY (ft_flags) \ ".pushsection .altinstructions, \"aM\", @progbits, " \ - __stringify(ALTINSTR_SIZE) "\n" \ + __stringify(ALT_INSTR_SIZE) "\n" \ " .long 771b - .\n"/* label */ \" .long 774f - .\n" /* new instruction */ \ " .4byte " __stringify(ft_flags) "\n" /* feature + flags */ \
@@ -363,7 +361,7 @@ void nop_func(void); 741: \ .skip -(((744f-743f)-(741b-740b)) > 0) * ((744f-743f)-(741b-740b)),0x90 ;\ 742: \ - .pushsection .altinstructions, "aM", @progbits, ALTINSTR_SIZE ; \ + .pushsection .altinstructions, "aM", @progbits, ALT_INSTR_SIZE ;\altinstr_entry 740b,743f,flag,742b-740b,744f-743f ; \ .popsection ; \ .pushsection .altinstr_replacement," ax" ; \
arch/x86/include/asm/asm.h +3 -1
@@ -138,7 +138,9 @@ static __always_inline __pure void *rip_rel_ptr(void *p)# include <asm/extable_fixup_types.h> -#define EXTABLE_SIZE 12 +#ifndef COMPILE_OFFSETS +#include <asm/asm-offsets.h> +#endif /* Exception table entry */ #ifdef __ASSEMBLER__
arch/x86/kernel/alternative.c +0 -2
@@ -593,8 +593,6 @@ void __init_or_module noinline apply_alternatives(struct alt_instr *start,u8 *instr, *replacement;struct alt_instr *a, *b; -BUILD_BUG_ON (ALTINSTR_SIZE !=sizeof (struct alt_instr )); -DPRINTK (ALT ,"alt table %px, -> %px" , start, end);/*
arch/x86/kernel/asm-offsets.c +4 -0
@@ -123,4 +123,8 @@ static void __used common(void)OFFSET (ARIA_CTX_rounds, aria_ctx, rounds);#endif +BLANK (); +DEFINE (EXTABLE_SIZE ,sizeof (struct exception_table_entry )); +DEFINE (UNWIND_HINT_SIZE ,sizeof (struct unwind_hint )); +DEFINE (ALT_INSTR_SIZE ,sizeof (struct alt_instr )); }
arch/x86/kernel/unwind_orc.c +0 -2
@@ -199,8 +199,6 @@ static struct orc_entry *orc_find(unsigned long ip) {static struct orc_entry *orc; -BUILD_BUG_ON (UNWIND_HINT_SIZE !=sizeof (struct unwind_hint )); -if (ip ==0 )return &null_orc_entry;
arch/x86/mm/extable.c +0 -2
@@ -303,8 +303,6 @@ int fixup_exception(struct pt_regs *regs, int trapnr, unsigned long error_code,const struct exception_table_entry *e;int type, reg, imm; -BUILD_BUG_ON (EXTABLE_SIZE !=sizeof (struct exception_table_entry )); -#ifdef CONFIG_PNPBIOS if (unlikely (SEGMENT_IS_PNP_CODE (regs->cs))) {extern u32 pnp_bios_fault_eip, pnp_bios_fault_esp;
include/linux/objtool.h +0 -2
@@ -8,8 +8,6 @@#include <asm/asm.h> -#define UNWIND_HINT_SIZE 12 -#ifndef __ASSEMBLY__#define UNWIND_HINT (type, sp_reg, sp_offset, signal) \
kernel/bounds.c +1 -0
@@ -6,6 +6,7 @@*/ #define __GENERATING_BOUNDS_H +#define COMPILE_OFFSETS /* Include headers that define the enum constants of interest */ #include <linux/page-flags.h> #include <linux/mmzone.h>
scripts/mod/devicetable-offsets.c +1 -0
@@ -1,4 +1,5 @@// SPDX-License-Identifier: GPL-2.0 +#define COMPILE_OFFSETS #include <linux/kbuild.h> #include <linux/mod_devicetable.h>
Josh Poimboeuf
maintainerPATCH v2 46/62 x86/bug: Define ELF section entry size for the bug table
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, define the entry size for the __bug_table section in its ELF header. This will allow tooling to extract individual entries.
--- arch/x86/include/asm/bug.h | 44 ++++++++++++++++++++------------------ 1 file changed, 23 insertions(+), 21 deletions(-)
arch/x86/include/asm/bug.h +23 -21
@@ -41,33 +41,35 @@#define _BUG_FLAGS (ins, flags, extra) \ do { \ - asm_inline volatile("1:\t" ins "\n" \ - ".pushsection __bug_table,\"a\"\n" \ - "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ - "\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \ - "\t.word %c1" "\t# bug_entry::line\n" \ - "\t.word %c2" "\t# bug_entry::flags\n" \ - "\t.org 2b+%c3\n" \ - ".popsection\n" \ - extra \ - : : "i" (__FILE__), "i" (__LINE__), \ - "i" (flags), \ - "i" (sizeof(struct bug_entry))); \ + asm_inline volatile( \ + "1:\t" ins "\n" \ + ".pushsection __bug_table, \"aM\", @progbits, %c3\n" \ + "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ + "\t" __BUG_REL(%c0) "\t# bug_entry::file\n" \ + "\t.word %c1" "\t# bug_entry::line\n" \ + "\t.word %c2" "\t# bug_entry::flags\n" \ + "\t.org 2b+%c3\n" \ + ".popsection\n" \ + extra \ + : : "i" (__FILE__), "i" (__LINE__), \ + "i" (flags), \ + "i" (sizeof(struct bug_entry))); \ } while (0)#else /* !CONFIG_DEBUG_BUGVERBOSE */ #define _BUG_FLAGS (ins, flags, extra) \ do { \ - asm_inline volatile("1:\t" ins "\n" \ - ".pushsection __bug_table,\"a\"\n" \ - "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ - "\t.word %c0" "\t# bug_entry::flags\n" \ - "\t.org 2b+%c1\n" \ - ".popsection\n" \ - extra \ - : : "i" (flags), \ - "i" (sizeof(struct bug_entry))); \ + asm_inline volatile( \ + "1:\t" ins "\n" \ + ".pushsection __bug_table, \"aM\", @progbits, %c1\n" \ + "2:\t" __BUG_REL(1b) "\t# bug_entry::bug_addr\n" \ + "\t.word %c0" "\t# bug_entry::flags\n" \ + "\t.org 2b+%c1\n" \ + ".popsection\n" \ + extra \ + : : "i" (flags), \ + "i" (sizeof(struct bug_entry))); \ } while (0)#endif /* CONFIG_DEBUG_BUGVERBOSE */
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 47/62 x86/orc: Define ELF section entry size for unwind hints
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, define the entry size for the discard.unwind_hints section in its ELF header. This will allow tooling to extract individual entries.
--- arch/x86/kernel/unwind_orc.c | 2 ++ include/linux/objtool.h | 9 ++++++--- 2 files changed, 8 insertions(+), 3 deletions(-)
arch/x86/kernel/unwind_orc.c +2 -0
@@ -199,6 +199,8 @@ static struct orc_entry *orc_find(unsigned long ip) {static struct orc_entry *orc; +BUILD_BUG_ON (UNWIND_HINT_SIZE !=sizeof (struct unwind_hint )); +if (ip ==0 )return &null_orc_entry;
include/linux/objtool.h +6 -3
@@ -8,11 +8,14 @@#include <asm/asm.h> +#define UNWIND_HINT_SIZE 12 +#ifndef __ASSEMBLY__ -#define UNWIND_HINT (type, sp_reg, sp_offset, signal) \ +#define UNWIND_HINT (type, sp_reg, sp_offset, signal) \ "987: \n\t" \ - ".pushsection .discard.unwind_hints\n\t" \ + ".pushsection .discard.unwind_hints, \"M\", @progbits, "\ + __stringify(UNWIND_HINT_SIZE) "\n\t" \/* struct unwind_hint */ \".long 987b - .\n\t" \ ".short " __stringify(sp_offset) "\n\t" \
@@ -88,7 +91,7 @@*/ .macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 .Lhere_\@: - .pushsection .discard.unwind_hints + .pushsection .discard.unwind_hints, "M", @progbits, UNWIND_HINT_SIZE/* struct unwind_hint */ .long .Lhere_\@ - . .short \sp_offset
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 48/62 objtool: Make STACK_FRAME_NON_STANDARD consistent
In reply to Josh PoimboeufThe C version of STACK_FRAME_NON_STANDARD differs from its asm counterpart in that it creates eight-byte entries (vs four) and creates a superfluous temporary variable.
Make the entry sizes consistent by converting the C version to four byte entries.
--- include/linux/objtool.h | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-)
include/linux/objtool.h +4 -3
@@ -33,9 +33,10 @@* * For more information, see tools/objtool/Documentation/objtool.txt. */ -#define STACK_FRAME_NON_STANDARD (func) \ - static void __used __section(".discard.func_stack_frame_non_standard") \ - *__func_stack_frame_non_standard_##func = func +#define STACK_FRAME_NON_STANDARD (func) \ + asm(".pushsection .discard.func_stack_frame_non_standard, \"aw\"\n\t" \ + ".long " __stringify(func) " - .\n\t" \ + ".popsection")/* * STACK_FRAME_NON_STANDARD_FP() is a frame-pointer-specific function ignore
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 49/62 kbuild,objtool: Defer objtool validation step for CONFIG_LIVEPATCH
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, defer objtool validation for CONFIG_LIVEPATCH until the final pre-link archive (e.g., vmlinux.o, module-foo.o) is built. This will simplify the process of generating livepatch modules.
Delayed objtool is generally preferred anyway, and is already standard for IBT and LTO. Eventually the per-translation-unit mode will be phased out.
--- scripts/Makefile.lib | 2 +- scripts/link-vmlinux.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)
scripts/Makefile.lib +1 -1
@@ -278,7 +278,7 @@ objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ $(if $(part-of-module), --module) -delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT)) +delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_LIVEPATCH)) cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
scripts/link-vmlinux.sh +2 -1
@@ -60,7 +60,8 @@ vmlinux_link() # skip output file argument shift - if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then + if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT || + is_enabled CONFIG_LIVEPATCH; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs=
Signature · 1 line
2.49.0
Joe Lawrence
maintainerRe: [PATCH v2 49/62] kbuild,objtool: Defer objtool validation step for CONFIG_LIVEPATCH
Quoted 33 lines · Josh Poimboeuf
In preparation for the objtool klp diff subcommand, defer objtool validation for
CONFIG_LIVEPATCHuntil the final pre-link archive (e.g., vmlinux.o, module-foo.o) is built. This will simplify the process of generating livepatch modules.Delayed objtool is generally preferred anyway, and is already standard for IBT and LTO. Eventually the per-translation-unit mode will be phased out.
Signed-off-by Josh Poimboeuf <[email protected]>--- scripts/Makefile.lib | 2 +- scripts/link-vmlinux.sh | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-)scripts/Makefile.lib +1
-1@@ -278,7 +278,7 @@ objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ $(if $(part-of-module), --module) -delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT)) +delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_LIVEPATCH)) cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)scripts/link-vmlinux.sh +0
-0
scripts/link-vmlinux.sh vmlinux_link() · line 67
Quoted 9 lines
scripts/link-vmlinux.sh +2
-1@@ -60,7 +60,8 @@ vmlinux_link() # skip output file argument shift - if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then + if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT || + is_enabled CONFIG_LIVEPATCH; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs=
At this commit, I'm getting the following linker error on ppc64le:
ld -EL -m elf64lppc -z noexecstack --no-warn-rwx-segments -pie -z notext --build-id=sha1 -X --orphan-handling=error --script=./arch/powerpc/kernel/vmlinux.lds -o .tmp_vmlinux1 --whole-archive vmlinux.o .vmlinux.export.o init/version-timestamp.o --no-whole-archive --start-group --end-group .tmp_vmlinux0.kallsyms.o arch/powerpc/tools/vmlinux.arch.o
vmlinux.o:(__ftr_alt_97+0x20): relocation truncated to fit:R_PPC64_REL14 against `.text'+4b54 vmlinux.o:(__ftr_alt_97+0x270): relocation truncated to fit:R_PPC64_REL14 against `.text'+173ecc
* Note: I dropped ("[PATCH v2 45/62] x86/extable: Define ELF section entry size for exception tables") since it doesn't build as per the comment I left on that patch.
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 49/62] kbuild,objtool: Defer objtool validation step for CONFIG_LIVEPATCH
Quoted 16 lines · Joe Lawrence
Review
Quoted 6 lines
(quoted diff) +2
-1- if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT; then + if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT || + is_enabled CONFIG_LIVEPATCH; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs=At this commit, I'm getting the following linker error on ppc64le:
ld -EL -m elf64lppc -z noexecstack --no-warn-rwx-segments -pie -z notext --build-id=sha1 -X --orphan-handling=error --script=./arch/powerpc/kernel/vmlinux.lds -o .tmp_vmlinux1 --whole-archive vmlinux.o .vmlinux.export.o
init/version-timestamp.o--no-whole-archive --start-group --end-group .tmp_vmlinux0.kallsyms.oarch/powerpc/tools/vmlinux.arch.o
Quoted 4 lines
vmlinux.o:(__ftr_alt_97+0x20): relocation truncated to fit:
R_PPC64_REL14against `.text'+4b54 vmlinux.o:(__ftr_alt_97+0x270): relocation truncated to fit:R_PPC64_REL14against `.text'+173ecc
Looks like objtool is causing the the __ftr_alt_* sections to get placed far away from .text somehow.
I guess objtool-on-vmlinux isn't quite ready for primetime on powerpc. Though my next TODO is to get all this working there.
Until then, here's the fix:
arch/x86/Kconfig +1 -0
@@ -265,6 +265,7 @@ config X86 select HAVE_FUNCTION_ERROR_INJECTION select HAVE_KRETPROBES select HAVE_RETHOOK + select HAVE_KLP_BUILD if X86_64 select HAVE_LIVEPATCH if X86_64 select HAVE_MIXED_BREAKPOINTS_REGS select HAVE_MOD_ARCH_SPECIFIC
kernel/livepatch/Kconfig +12 -0
@@ -18,3 +18,15 @@ config LIVEPATCH module uses the interface provided by this option to register a patch, causing calls to patched functions to be redirected to new function code contained in the patch module. + +config HAVE_KLP_BUILD + bool + help + Arch supports klp-build + +config KLP_BUILD + def_bool y + depends on LIVEPATCH && HAVE_KLP_BUILD + select OBJTOOL + help + Enable klp-build support
scripts/Makefile.lib +1 -1
@@ -278,7 +278,7 @@ objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ $(if $(part-of-module), --module) -delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_LIVEPATCH)) +delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT),$(CONFIG_KLP_BUILD)) cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@) cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
scripts/link-vmlinux.sh +1 -1
@@ -61,7 +61,7 @@ vmlinux_link() shift if is_enabled CONFIG_LTO_CLANG || is_enabled CONFIG_X86_KERNEL_IBT || - is_enabled CONFIG_LIVEPATCH; then + is_enabled CONFIG_KLP_BUILD; then # Use vmlinux.o instead of performing the slow LTO link again. objs=vmlinux.o libs=
Josh Poimboeuf
maintainerPATCH v2 50/62 objtool/klp: Add --checksum option to generate per-function checksums
In reply to Josh PoimboeufIn preparation for the objtool klp diff subcommand, add a command-line option to generate a unique checksum for each function. This will enable detection of functions which have changed between two versions of an object file.
--- tools/objtool/Makefile | 38 +++-- tools/objtool/builtin-check.c | 12 +- tools/objtool/check.c | 141 +++++++++++++++++- tools/objtool/elf.c | 46 +++++- tools/objtool/include/objtool/builtin.h | 3 +- tools/objtool/include/objtool/check.h | 5 +- tools/objtool/include/objtool/checksum.h | 42 ++++++ .../objtool/include/objtool/checksum_types.h | 25 ++++ tools/objtool/include/objtool/elf.h | 5 +- tools/objtool/include/objtool/objtool.h | 2 + 10 files changed, 293 insertions(+), 26 deletions(-) create mode 100644 tools/objtool/include/objtool/checksum.h create mode 100644 tools/objtool/include/objtool/checksum_types.h
tools/objtool/Makefile +24 -14
@@ -2,6 +2,27 @@ include ../scripts/Makefile.include include ../scripts/Makefile.arch +ifeq ($(SRCARCH),x86) + BUILD_ORC := y + ARCH_HAS_KLP := y +endif + +ifeq ($(SRCARCH),loongarch) + BUILD_ORC := y +endif + +ifeq ($(ARCH_HAS_KLP),y) + HAVE_XXHASH = $(shell echo "int main() {}" | \ + $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n) + ifeq ($(HAVE_XXHASH),y) + LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \ + -DBUILD_KLP + LIBXXHASH_LIBS := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash) + endif +endif + +export BUILD_ORC + ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(CURDIR))) srctree := $(patsubst %/,%,$(dir $(srctree)))
@@ -36,10 +57,10 @@ INCLUDES := -I$(srctree)/tools/include \ -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \ -I$(LIBSUBCMD_OUTPUT)/include -OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g \ - $(WARNINGS) $(INCLUDES) $(LIBELF_FLAGS) $(HOSTCFLAGS) +OBJTOOL_CFLAGS := -std=gnu11 -fomit-frame-pointer -O2 -g $(WARNINGS) \ + $(INCLUDES) $(LIBELF_FLAGS) $(LIBXXHASH_CFLAGS) $(HOSTCFLAGS) -OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(HOSTLDFLAGS) +OBJTOOL_LDFLAGS := $(LIBSUBCMD) $(LIBELF_LIBS) $(LIBXXHASH_LIBS) $(HOSTLDFLAGS) # Allow old libelf to be used: elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
@@ -51,17 +72,6 @@ HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" AWK = awk MKDIR = mkdir -BUILD_ORC := n - -ifeq ($(SRCARCH),x86) - BUILD_ORC := y -endif - -ifeq ($(SRCARCH),loongarch) - BUILD_ORC := y -endif - -export BUILD_ORC export srctree OUTPUT CFLAGS SRCARCH AWK include $(srctree)/tools/build/Makefile.include
tools/objtool/builtin-check.c +11 -1
@@ -73,6 +73,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset)static const struct option check_options[] = {OPT_GROUP ("Actions:" ), +OPT_BOOLEAN (0 ,"checksum" , &opts.checksum,"generate per-function checksums" ),OPT_BOOLEAN (0 ,"cfi" , &opts.cfi,"annotate kernel control flow integrity (kCFI) function preambles" ),OPT_CALLBACK_OPTARG ('h' ,"hacks" ,NULL ,NULL ,"jump_label,noinstr,skylake" ,"patch toolchain bugs/limitations" , parse_hacks),OPT_BOOLEAN ('i' ,"ibt" , &opts.ibt,"validate and annotate IBT" ),
@@ -158,7 +159,16 @@ static bool opts_valid(void)return false; } - if (opts.hack_jump_label || + +#ifndef BUILD_KLP +if (opts.checksum) { +ERROR ("--checksum not supported; install xxhash-devel and recompile" ); +return false; + } +#endif + + if (opts.checksum || + opts.hack_jump_label || opts.hack_noinstr || opts.ibt || opts.mcount ||
tools/objtool/check.c +137 -4
215 lines not on this page · raw message
tools/objtool/elf.c +43 -3
76 lines not on this page · raw message
tools/objtool/include/objtool/builtin.h +2 -1
@@ -9,6 +9,7 @@struct opts {/* actions: */ +bool cfi;bool dump_orc;bool hack_jump_label;bool hack_noinstr;
@@ -23,9 +24,9 @@ struct opts {bool sls;bool stackval;bool static_call; +bool checksum;bool uaccess;int prefix; -bool cfi;/* options: */ bool backtrace;
tools/objtool/include/objtool/check.h +3 -2
@@ -65,8 +65,9 @@ struct instruction { unret : 1, visited : 4, no_reloc : 1, - hole : 1; -/* 10 bit hole */ + hole : 1, + fake : 1; +/* 9 bit hole */ struct alt_group *alt_group;struct instruction *jump_dest;
tools/objtool/include/objtool/checksum.h new file +42 -0
42 lines not on this page · raw message
tools/objtool/include/objtool/checksum_types.h new file +25 -0
@@ -0,0 +1,25 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _OBJTOOL_CHECKSUM_TYPES_H +#define _OBJTOOL_CHECKSUM_TYPES_H + +struct sym_checksum { +u64 addr; +u64 checksum; +}; + +#ifdef BUILD_KLP + +#include <xxhash.h> + +struct checksum { +XXH3_state_t *state; +XXH64_hash_t checksum; +}; + +#else /* !BUILD_KLP */ + +struct checksum {}; + +#endif /* !BUILD_KLP */ + +#endif /* _OBJTOOL_CHECKSUM_TYPES_H */
tools/objtool/include/objtool/elf.h +4 -1
@@ -13,6 +13,8 @@#include <linux/hashtable.h> #include <linux/rbtree.h> #include <linux/jhash.h> + +#include <objtool/checksum_types.h> #include <arch/elf.h> #define SYM_NAME_LEN 512
@@ -57,7 +59,7 @@ struct symbol {struct elf_hash_node name_hash;GElf_Sym sym;struct section *sec; -const char *name; +const char *name, *demangled_name;unsigned int idx, len;unsigned long offset;unsigned long __subtree_last;
@@ -79,6 +81,7 @@ struct symbol {struct list_head pv_target;struct reloc *relocs;struct section *group_sec; +struct checksum csum; };struct reloc {
tools/objtool/include/objtool/objtool.h +2 -0
@@ -14,6 +14,8 @@#define __weak __attribute__((weak)) +#define SYM_CHECKSUM_SEC ".discard.sym_checksum" +struct pv_state {bool clean;struct list_head targets;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 51/62 objtool/klp: Add --debug-checksum=<funcs> to show per-instruction checksums
In reply to Josh PoimboeufAdd a --debug-checksum=<funcs> option to the check subcommand to print the calculated checksum of each instruction in the given functions.
This is useful for determining where two versions of a function begin to diverge.
--- tools/objtool/builtin-check.c | 6 ++++ tools/objtool/check.c | 42 ++++++++++++++++++++++++ tools/objtool/include/objtool/builtin.h | 1 + tools/objtool/include/objtool/checksum.h | 1 + tools/objtool/include/objtool/elf.h | 1 + tools/objtool/include/objtool/warn.h | 19 +++++++++++ 6 files changed, 70 insertions(+)
tools/objtool/builtin-check.c +6 -0
@@ -92,6 +92,7 @@ static const struct option check_options[] = {OPT_GROUP ("Options:" ),OPT_BOOLEAN (0 ,"backtrace" , &opts.backtrace,"unwind on error" ), +OPT_STRING (0 ,"debug-checksum" , &opts.debug_checksum,"funcs" ,"enable checksum debug output" ),OPT_BOOLEAN (0 ,"dry-run" , &opts.dryrun,"don't write modifications" ),OPT_BOOLEAN (0 ,"link" , &opts.link,"object is a linked object" ), OPT_BOOLEAN(0, "module", &opts.module, "object is part of a kernel module"),
@@ -167,6 +168,11 @@ static bool opts_valid(void) }#endif +if (opts.debug_checksum && !opts.checksum) { +ERROR ("--debug-checksum requires --checksum" ); +return false; + } + if (opts.checksum || opts.hack_jump_label || opts.hack_noinstr ||
tools/objtool/check.c +42 -0
54 lines not on this page · raw message
tools/objtool/include/objtool/builtin.h +1 -0
@@ -30,6 +30,7 @@ struct opts {/* options: */ bool backtrace; +const char *debug_checksum;bool dryrun;bool link;bool mnop;
tools/objtool/include/objtool/checksum.h +1 -0
@@ -19,6 +19,7 @@ static inline void checksum_update(struct symbol *func, const void *data, size_t size) { XXH3_64bits_update(func->csum.state, data, size); +dbg_checksum (func, insn,XXH3_64bits_digest (func->csum.state)); }static inline void checksum_finish (struct symbol *func)
tools/objtool/include/objtool/elf.h +1 -0
@@ -78,6 +78,7 @@ struct symbol { u8 ignore : 1; u8 cold : 1; u8 prefix : 1; + u8 debug_checksum : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
tools/objtool/include/objtool/warn.h +19 -0
@@ -102,4 +102,23 @@ static inline char *offstr(struct section *sec, unsigned long offset)#define ERROR_FUNC (sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)#define ERROR_INSN (insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__) + +#define __dbg (format, ...) \ + fprintf(stderr, \ + "DEBUG: %s%s" format "\n", \ + objname ?: "", \ + objname ? ": " : "", \ + ##__VA_ARGS__) + +#define dbg_checksum (func, insn, checksum) \ +({ \ + if (unlikely(insn->sym && insn->sym->pfunc && \ + insn->sym->pfunc->debug_checksum)) { \ + char *insn_off = offstr(insn->sec, insn->offset); \ + __dbg("checksum: %s %s %016lx", \ + func->name, insn_off, checksum); \ + free(insn_off); \ + } \ +}) +#endif /* _WARN_H */
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 52/62 objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufAdd a new klp diff subcommand which performs a binary diff between two object files and extracts changed functions into a new object which can then be linked into a livepatch module.
This builds on concepts from the longstanding out-of-tree kpatch [1] project which began in 2012 and has been used for many years to generate livepatch modules for production kernels. However, this is a complete rewrite which incorporates hard-earned lessons from 12+ years of maintaining kpatch.
Key improvements compared to kpatch-build:
- Integrated with objtool: Leverages objtool's existing control-flow graph analysis to help detect changed functions.
- Works on vmlinux.o: Supports late-linked objects, making it compatible with LTO, IBT, and similar.
- Simplified code base: ~3k fewer lines of code.
- Upstream: No more out-of-tree #ifdef hacks, far less cruft.
- Cleaner internals: Vastly simplified logic for symbol/section/reloc inclusion and special section extraction.
- Robust __LINE__ macro handling: Avoids false positive binary diffs caused by the __LINE__ macro by introducing a fix-patch-lines script (coming in a later patch) which injects #line directives into the source .patch to preserve the original line numbers at compile time.
Note the end result of this subcommand is not yet functionally complete. Livepatch needs some ELF magic which linkers don't like:
- Two relocation sections (.rela*, .klp.rela*) for the same text section.
- Use of SHN_LIVEPATCH to mark livepatch symbols.
Unfortunately linkers tend to mangle such things. To work around that, klp diff generates a linker-compliant intermediate binary which encodes the relevant KLP section/reloc/symbol metadata.
After module linking, a klp post-link step (coming soon) will clean up the mess and convert the linked .ko into a fully compliant livepatch module.
Note this subcommand requires the diffed binaries to have been compiled with -ffunction-sections and -fdata-sections, and processed with 'objtool --checksum'. Those constraints will be handled by a klp-build script introduced in a later patch.
Without '-ffunction-sections -fdata-sections', reliable object diffing would be infeasible due to toolchain limitations:
- For intra-file+intra-section references, the compiler might occasionally generated hard-coded instruction offsets instead of relocations.
- Section-symbol-based references can be ambiguous:
- Overlapping or zero-length symbols create ambiguity as to which symbol is being referenced.
- A reference to the end of a symbol (e.g., checking array bounds) can be misinterpreted as a reference to the next symbol, or vice versa.
A potential future alternative to '-ffunction-sections -fdata-sections' would be to introduce a toolchain option that forces symbol-based (non-section) relocations.
--- MAINTAINERS | 2 +- include/linux/livepatch.h | 25 +- include/linux/livepatch_external.h | 76 ++ kernel/livepatch/core.c | 4 +- scripts/module.lds.S | 10 +- tools/include/linux/livepatch_external.h | 76 ++ tools/objtool/Build | 4 +- tools/objtool/Makefile | 3 +- tools/objtool/arch/x86/decode.c | 40 + tools/objtool/builtin-klp.c | 52 + tools/objtool/elf.c | 21 +- tools/objtool/include/objtool/arch.h | 1 + tools/objtool/include/objtool/builtin.h | 2 + tools/objtool/include/objtool/elf.h | 52 +- tools/objtool/include/objtool/klp.h | 31 + tools/objtool/include/objtool/objtool.h | 2 + tools/objtool/klp-diff.c | 1429 ++++++++++++++++++++++ tools/objtool/objtool.c | 41 +- tools/objtool/sync-check.sh | 1 + tools/objtool/weak.c | 7 + 20 files changed, 1834 insertions(+), 45 deletions(-) create mode 100644 include/linux/livepatch_external.h create mode 100644 tools/include/linux/livepatch_external.h create mode 100644 tools/objtool/builtin-klp.c create mode 100644 tools/objtool/include/objtool/klp.h create mode 100644 tools/objtool/klp-diff.c
MAINTAINERS +1 -1
@@ -13788,7 +13788,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/livepatching/livepatching.g F: Documentation/ABI/testing/sysfs-kernel-livepatch F: Documentation/livepatch/ F: arch/powerpc/include/asm/livepatch.h -F: include/linux/livepatch.h +F: include/linux/livepatch*.h F: kernel/livepatch/ F: kernel/module/livepatch.c F: samples/livepatch/
include/linux/livepatch.h +1 -24
@@ -13,6 +13,7 @@#include <linux/ftrace.h> #include <linux/completion.h> #include <linux/list.h> +#include <linux/livepatch_external.h> #include <linux/livepatch_sched.h> #if IS_ENABLED (CONFIG_LIVEPATCH )
@@ -77,30 +78,6 @@ struct klp_func {bool transition; }; -struct klp_object ; - -/** -* struct klp_callbacks - pre/post live-(un)patch callback structure -* @pre_patch: executed before code patching -* @post_patch: executed after code patching -* @pre_unpatch: executed before code unpatching -* @post_unpatch: executed after code unpatching -* @post_unpatch_enabled: flag indicating if post-unpatch callback -* should run -* -* All callbacks are optional. Only the pre-patch callback, if provided, -* will be unconditionally executed. If the parent klp_object fails to -* patch for any reason, including a non-zero error status returned from -* the pre-patch callback, no further callbacks will be executed. -*/ -struct klp_callbacks { -int (*pre_patch)(struct klp_object *obj); -void (*post_patch)(struct klp_object *obj); -void (*pre_unpatch)(struct klp_object *obj); -void (*post_unpatch)(struct klp_object *obj); -bool post_unpatch_enabled; -}; -/** * struct klp_object - kernel object structure for live patching * @name: module name (or NULL for vmlinux)
include/linux/livepatch_external.h new file +76 -0
76 lines not on this page · raw message
kernel/livepatch/core.c +2 -2
@@ -224,7 +224,7 @@ static int klp_resolve_symbols(Elf_Shdr *sechdrs, const char *strtab,/* Format: .klp.sym.sym_objname.sym_name,sympos */ cnt =sscanf (strtab + sym->st_name, -".klp.sym.%55[^.].%511[^,],%lu" , +KLP_SYM_PREFIX "%55[^.].%511[^,],%lu" , sym_objname, sym_name, &sympos);if (cnt !=3 ) { pr_err("symbol %s has an incorrectly formatted name\n",
@@ -303,7 +303,7 @@ static int klp_write_section_relocs(struct module *pmod, Elf_Shdr *sechdrs,* See comment in klp_resolve_symbols() for an explanation * of the selected field width value. */ - cnt =sscanf (shstrtab + sec->sh_name,".klp.rela.%55[^.]" , + cnt =sscanf (shstrtab + sec->sh_name,KLP_RELOC_SEC_PREFIX "%55[^.]" , sec_objname);if (cnt !=1 ) { pr_err("section %s has an incorrectly formatted name\n",
scripts/module.lds.S +9 -1
@@ -34,8 +34,16 @@ SECTIONS { __patchable_function_entries : { *(__patchable_function_entries) } + __klp_funcs 0: ALIGN(8) { KEEP(*(__klp_funcs)) } + + __klp_objects 0: ALIGN(8) { + __start_klp_objects = .; + KEEP(*(__klp_objects)) + __stop_klp_objects = .; + } + #ifdef CONFIG_ARCH_USES_CFI_TRAPS - __kcfi_traps : { KEEP(*(.kcfi_traps)) } + __kcfi_traps : { KEEP(*(.kcfi_traps)) } #endif .text : {
tools/include/linux/livepatch_external.h new file +76 -0
76 lines not on this page · raw message
tools/objtool/Build +2 -2
@@ -8,8 +8,8 @@ objtool-y += builtin-check.o objtool-y += elf.o objtool-y += objtool.o -objtool-$(BUILD_ORC) += orc_gen.o -objtool-$(BUILD_ORC) += orc_dump.o +objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o +objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o objtool-y += libstring.o objtool-y += libctype.o
tools/objtool/Makefile +2 -1
@@ -15,13 +15,14 @@ ifeq ($(ARCH_HAS_KLP),y) HAVE_XXHASH = $(shell echo "int main() {}" | \ $(HOSTCC) -xc - -o /dev/null -lxxhash 2> /dev/null && echo y || echo n) ifeq ($(HAVE_XXHASH),y) + BUILD_KLP := y LIBXXHASH_CFLAGS := $(shell $(HOSTPKG_CONFIG) libxxhash --cflags 2>/dev/null) \ -DBUILD_KLP LIBXXHASH_LIBS := $(shell $(HOSTPKG_CONFIG) libxxhash --libs 2>/dev/null || echo -lxxhash) endif endif -export BUILD_ORC +export BUILD_ORC BUILD_KLP ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(CURDIR)))
tools/objtool/arch/x86/decode.c +40 -0
@@ -95,6 +95,46 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)return phys_to_virt (addend); } +static void scan_for_insn (struct section *sec,unsigned long offset, +unsigned long *insn_off,unsigned int *insn_len) +{ +unsigned long o =0 ; +struct insn insn; + +while (1 ) { + +insn_decode (&insn, sec->data->d_buf + o,sec_size (sec) - o, +INSN_MODE_64 ); + +if (o + insn.length > offset) { + *insn_off = o; + *insn_len = insn.length; +return ; + } + + o += insn.length; + } +} + +u64 arch_adjusted_addend (struct reloc *reloc) +{ +unsigned int type =reloc_type (reloc); +s64 addend =reloc_addend (reloc); +unsigned long insn_off; +unsigned int insn_len; + +if (type ==R_X86_64_PLT32 ) +return addend +4 ; + +if (type !=R_X86_64_PC32 || !is_text_sec (reloc->sec->base)) +return addend; + +scan_for_insn (reloc->sec->base,reloc_offset (reloc), + &insn_off, &insn_len); + +return addend + insn_off + insn_len -reloc_offset (reloc); +} +unsigned long arch_jump_destination (struct instruction *insn) {return insn->offset + insn->len + insn->immediate;
tools/objtool/builtin-klp.c new file +52 -0
52 lines not on this page · raw message
tools/objtool/elf.c +20 -1
@@ -288,6 +288,18 @@ struct symbol *find_symbol_by_name(const struct elf *elf, const char *name)return NULL ; } +struct symbol *find_global_symbol_by_name (const struct elf *elf,const char *name) +{ +struct symbol *sym; + +elf_hash_for_each_possible (symbol_name, sym, name_hash,str_hash (name)) { +if (!strcmp (sym->name, name) && !is_local_sym (sym)) +return sym; + } + +return NULL ; +} +struct reloc *find_reloc_by_dest_range (const struct elf *elf,struct section *sec,unsigned long offset,unsigned int len) {
@@ -475,6 +487,8 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym)else entry = &sym->sec->symbol_list;list_add (&sym->list, entry); + +list_add_tail (&sym->global_list, &elf->symbols);elf_hash_add (symbol, &sym->hash, sym->idx);elf_hash_add (symbol_name, &sym->name_hash,str_hash (sym->name));
@@ -529,6 +543,9 @@ static int read_symbols(struct elf *elf)ERROR_GLIBC ("calloc" );return -1 ; } + +INIT_LIST_HEAD (&elf->symbols); +for (i =0 ; i < symbols_nr; i++) { sym = &elf->symbol_data[i];
@@ -637,7 +654,7 @@ static int mark_group_syms(struct elf *elf)return -1 ; } -list_for_each_entry (sec, &elf->sections, list) { +for_each_sec (elf, sec) {if (sec->sh.sh_type ==SHT_GROUP && sec->sh.sh_link == symtab->idx) { sym =find_symbol_by_index (elf, sec->sh.sh_info);
@@ -1220,6 +1237,8 @@ struct elf *elf_create_file(GElf_Ehdr *ehdr, const char *name)return NULL ; } +INIT_LIST_HEAD (&elf->symbols); + if (!elf_alloc_hash(section, 1000) || !elf_alloc_hash(section_name, 1000) || !elf_alloc_hash(symbol, 10000) ||
tools/objtool/include/objtool/arch.h +1 -0
@@ -84,6 +84,7 @@ bool arch_callee_saved_reg(unsigned char reg);unsigned long arch_jump_destination (struct instruction *insn);s64 arch_insn_adjusted_addend (struct instruction *insn,struct reloc *reloc); +u64 arch_adjusted_addend (struct reloc *reloc);const char *arch_nop_insn (int len);const char *arch_ret_insn (int len);
tools/objtool/include/objtool/builtin.h +2 -0
@@ -51,4 +51,6 @@ int objtool_run(int argc, const char **argv);void print_args (void ); +int cmd_klp (int argc,const char **argv); +#endif /* _BUILTIN_H */
tools/objtool/include/objtool/elf.h +41 -11
101 lines not on this page · raw message
tools/objtool/include/objtool/klp.h new file +31 -0
@@ -0,0 +1,31 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +#ifndef _OBJTOOL_KLP_H +#define _OBJTOOL_KLP_H + +/* +* __klp_objects and __klp_funcs are created by klp diff and used by the patch +* module init code to build the klp_patch, klp_object and klp_func structs +* needed by the livepatch API. +*/ +#define KLP_OBJECTS_SEC "__klp_objects" +#define KLP_FUNCS_SEC "__klp_funcs" + +/* +* __klp_relocs is an intermediate section which are created by klp diff and +* converted into KLP symbols/relas by "objtool klp post-link". This is needed +* to work around the linker, which doesn't preserve SHN_LIVEPATCH or +* SHF_RELA_LIVEPATCH, nor does it support having two RELA sections for a +* single PROGBITS section. +*/ +#define KLP_RELOCS_SEC "__klp_relocs" +#define KLP_STRINGS_SEC ".rodata.klp.str1.1" + +struct klp_reloc { +void *offset; +void *sym; +u32 type; +}; + +int cmd_klp_diff (int argc,const char **argv); + +#endif /* _OBJTOOL_KLP_H */
tools/objtool/include/objtool/objtool.h +2 -0
@@ -41,6 +41,8 @@ struct objtool_file {struct pv_state *pv_ops; }; +char *top_level_dir (const char *file); +struct objtool_file *objtool_open_read (const char *_objname);int objtool_pv_add (struct objtool_file *file,int idx,struct symbol *func);
tools/objtool/klp-diff.c new file +1429 -0
1,429 lines not on this page · raw message
tools/objtool/objtool.c +39 -2
58 lines not on this page · raw message
tools/objtool/sync-check.sh +1 -0
@@ -16,6 +16,7 @@ arch/x86/include/asm/orc_types.h arch/x86/include/asm/emulate_prefix.h arch/x86/lib/x86-opcode-map.txt arch/x86/tools/gen-insn-attr-x86.awk +include/linux/livepatch_external.h include/linux/static_call_types.h "
tools/objtool/weak.c +7 -0
@@ -8,6 +8,8 @@#include <stdbool.h> #include <errno.h> #include <objtool/objtool.h> +#include <objtool/arch.h> +#include <objtool/builtin.h> #define UNSUPPORTED (name) \ ({ \
@@ -24,3 +26,8 @@ int __weak orc_create(struct objtool_file *file) {UNSUPPORTED ("ORC" ); } + +int __weak cmd_klp (int argc,const char **argv) +{ +UNSUPPORTED ("klp" ); +}
Signature · 1 line
2.49.0
Re: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
tools/objtool/include/objtool/elf.h static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) · line 469
Quoted 6 lines · Josh Poimboeuf
tools/objtool/include/objtool/elf.h +6
-0+ +#define sym_for_each_reloc (elf, sym, reloc) \ + for (reloc = find_reloc_by_dest_range(elf, sym->sec, \ + sym->offset, sym->len); \ + reloc && reloc_offset(reloc) < sym->offset + sym->len; \ + reloc = rsec_next_reloc(sym->sec->rsec, reloc))
This macro intents to walk through ALL relocations for the 'sym'. It seems we have the assumption that, there is at most one single relocation for the same offset and find_reloc_by_dest_range only needs to do 'less than' offset comparison:
elf_hash_for_each_possible(reloc, reloc, hash,
sec_offset_hash(rsec, o)) {
if (reloc->sec != rsec)
continue;
if (reloc_offset(reloc) >= offset &&
reloc_offset(reloc) < offset + len) {
less than ==> if (!r || reloc_offset(reloc) < reloc_offset(r))
r = reloc;Because if there were multiple relocations for the same offset, the returned one would be the last one in section entry order(hash list has reverse order against section order), then broken the intention.
Right?
Thanks,
laokz
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 22 lines · laokz
tools/objtool/include/objtool/elf.h static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) · line 469
Quoted 6 lines · Josh Poimboeuf
tools/objtool/include/objtool/elf.h +6
-0+ +#define sym_for_each_reloc (elf, sym, reloc) \ + for (reloc = find_reloc_by_dest_range(elf, sym->sec, \ + sym->offset, sym->len); \ + reloc && reloc_offset(reloc) < sym->offset + sym->len; \ + reloc = rsec_next_reloc(sym->sec->rsec, reloc))This macro intents to walk through ALL relocations for the 'sym'. It seems we have the assumption that, there is at most one single relocation for the same offset and
find_reloc_by_dest_rangeonly needs to do 'less than' offset comparison:elf_hash_for_each_possible(reloc, reloc, hash, sec_offset_hash(rsec, o)) { if (reloc->sec != rsec) continue; if (reloc_offset(reloc) >= offset && reloc_offset(reloc) < offset + len) { less than ==> if (!r || reloc_offset(reloc) < reloc_offset(r)) r = reloc;
Quoted 3 lines
Because if there were multiple relocations for the same offset, the returned one would be the last one in section entry order(hash list has reverse order against section order), then broken the intention.
Right. Is that a problem? I don't believe I've ever seen two relocations for the same offset.
Signature · 1 line
Josh
Re: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 23 lines · Josh Poimboeuf
Quoted 22 lines · laokz
tools/objtool/include/objtool/elf.h static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next) · line 469
Quoted 6 lines · Josh Poimboeuf
tools/objtool/include/objtool/elf.h +6
-0+ +#define sym_for_each_reloc (elf, sym, reloc) \ + for (reloc = find_reloc_by_dest_range(elf, sym->sec, \ + sym->offset, sym->len); \ + reloc && reloc_offset(reloc) < sym->offset + sym->len; \ + reloc = rsec_next_reloc(sym->sec->rsec, reloc))This macro intents to walk through ALL relocations for the 'sym'. It seems we have the assumption that, there is at most one single relocation for the same offset and
find_reloc_by_dest_rangeonly needs to do 'less than' offset comparison:elf_hash_for_each_possible(reloc, reloc, hash, sec_offset_hash(rsec, o)) { if (reloc->sec != rsec) continue; if (reloc_offset(reloc) >= offset && reloc_offset(reloc) < offset + len) { less than ==> if (!r || reloc_offset(reloc) < reloc_offset(r)) r = reloc;
Quoted 7 lines
Quoted 3 lines
Because if there were multiple relocations for the same offset, the returned one would be the last one in section entry order(hash list has reverse order against section order), then broken the intention.
Right. Is that a problem? I don't believe I've ever seen two relocations for the same offset.
Thanks for the clarification. I asked this because I noticed the patchset have done some code refactoring, so guess if we could make it more general to other architectures which not support objtool yet. Such as RISC-V, it is not unusual having multiple relocs for same offset, like vmlinux.o might have:
000c 0000010a00000017 R_RISCV_PCREL_HI20 0000000000000000 .LANCHOR0 + 48
000c 0000000000000033 R_RISCV_RELAX 48
0044 0000061700000023 R_RISCV_ADD32 0000000000000048 pe_head_start + 0
0044 000dd5b900000027 R_RISCV_SUB32 0000000000000002 _start + 0
But it is a bit off-topic:/
Regards,
laokz
Peter Zijlstra
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufQuoted 12 lines · Josh Poimboeuf
Without '-ffunction-sections -fdata-sections', reliable object diffing would be infeasible due to toolchain limitations:
- For intra-file+intra-section references, the compiler might occasionally generated hard-coded instruction offsets instead of relocations.
- Section-symbol-based references can be ambiguous:
- Overlapping or zero-length symbols create ambiguity as to which symbol is being referenced.
Quoted 7 lines
- A reference to the end of a symbol (e.g., checking array bounds) can be misinterpreted as a reference to the next symbol, or vice versa.
A potential future alternative to '-ffunction-sections -fdata-sections' would be to introduce a toolchain option that forces symbol-based (non-section) relocations.
Urgh.. So the first issue we can fix with objtool, but the ambiguous cases are indeed very hard to fix up in post.
Did you already talk to toolchain people about this?
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 18 lines · Peter Zijlstra
Quoted 16 lines · Josh Poimboeuf
Without '-ffunction-sections -fdata-sections', reliable object diffing would be infeasible due to toolchain limitations:
- For intra-file+intra-section references, the compiler might occasionally generated hard-coded instruction offsets instead of relocations.
- Section-symbol-based references can be ambiguous:
- Overlapping or zero-length symbols create ambiguity as to which symbol is being referenced.
- A reference to the end of a symbol (e.g., checking array bounds) can be misinterpreted as a reference to the next symbol, or vice versa.
Quoted 8 lines
Quoted 3 lines
A potential future alternative to '-ffunction-sections -fdata-sections' would be to introduce a toolchain option that forces symbol-based (non-section) relocations.
Urgh.. So the first issue we can fix with objtool, but the ambiguous cases are indeed very hard to fix up in post.
Did you already talk to toolchain people about this?
For now, I want to stick with -ffunction-sections -fdata-sections, as that's what kpatch has done for 10+ years and it works well. That's the only option we have for current compilers anyway.
The above mentioned possibility of diffing without -ffunction-sections -fdata-sections is theoretical, and needs more exploration. If it indeed works then we can try to get toolchain support for that.
Signature · 1 line
Josh
Peter Zijlstra
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufReview
Quoted 2 lines · Josh Poimboeuf
(quoted diff) +2
-0+#define KLP_RELOC_SEC_PREFIX ".klp.rela." +#define KLP_SYM_PREFIX ".klp.sym."
This max symbol length test is getting more and more broken every day :-)
Peter Zijlstra
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufQuoted 37 lines · Josh Poimboeuf
tools/objtool/arch/x86/decode.c +29
-0@@ -95,6 +95,46 @@ s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc)return phys_to_virt (addend); } +static void scan_for_insn (struct section *sec,unsigned long offset, +unsigned long *insn_off,unsigned int *insn_len) +{ +unsigned long o =0 ; +struct insn insn; + +while (1 ) { + +insn_decode (&insn, sec->data->d_buf + o,sec_size (sec) - o, +INSN_MODE_64 ); + +if (o + insn.length > offset) { + *insn_off = o; + *insn_len = insn.length; +return ; + } + + o += insn.length; + } +} + +u64 arch_adjusted_addend (struct reloc *reloc) +{ +unsigned int type =reloc_type (reloc); +s64 addend =reloc_addend (reloc); +unsigned long insn_off; +unsigned int insn_len; + +if (type ==R_X86_64_PLT32 )
tools/objtool/arch/x86/decode.c s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc) · line 136
Quoted 10 lines
tools/objtool/arch/x86/decode.c +10
-0+return addend +4 ; + +if (type !=R_X86_64_PC32 || !is_text_sec (reloc->sec->base)) +return addend; + +scan_for_insn (reloc->sec->base,reloc_offset (reloc), + &insn_off, &insn_len); + +return addend + insn_off + insn_len -reloc_offset (reloc); +}
This looks like a rather expensive proposition; it will have to decode the section nr_reloc times.
Does it not make more sense to fully decode the section like 'normal' ?
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 44 lines · Peter Zijlstra
Quoted 29 lines · Josh Poimboeuf
tools/objtool/arch/x86/decode.c +29
-0+static void scan_for_insn (struct section *sec,unsigned long offset, +unsigned long *insn_off,unsigned int *insn_len) +{ +unsigned long o =0 ; +struct insn insn; + +while (1 ) { + +insn_decode (&insn, sec->data->d_buf + o,sec_size (sec) - o, +INSN_MODE_64 ); + +if (o + insn.length > offset) { + *insn_off = o; + *insn_len = insn.length; +return ; + } + + o += insn.length; + } +} + +u64 arch_adjusted_addend (struct reloc *reloc) +{ +unsigned int type =reloc_type (reloc); +s64 addend =reloc_addend (reloc); +unsigned long insn_off; +unsigned int insn_len; + +if (type ==R_X86_64_PLT32 )tools/objtool/arch/x86/decode.c s64 arch_insn_adjusted_addend(struct instruction *insn, struct reloc *reloc) · line 136
Quoted 10 lines
tools/objtool/arch/x86/decode.c +10
-0+return addend +4 ; + +if (type !=R_X86_64_PC32 || !is_text_sec (reloc->sec->base)) +return addend; + +scan_for_insn (reloc->sec->base,reloc_offset (reloc), + &insn_off, &insn_len); + +return addend + insn_off + insn_len -reloc_offset (reloc); +}This looks like a rather expensive proposition; it will have to decode the section
nr_reloctimes.
Quoted 1 line
Does it not make more sense to fully decode the section like 'normal' ?
Yeah, I'm not crazy about it either, but it at least keeps the pain nicely localized to x86, and avoids pulling in struct instruction, struct objtool_file, etc.
Also this typically doesn't need to be all that fast as this is only done for changed functions, and only for a subset of relocations (those which might be references to non-bundled data in a text section).
To give a general idea, in one of my tests, for a patch with 22 functions, it only calls scan_for_insn() 41 times.
Signature · 1 line
Josh
Peter Zijlstra
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufQuoted 4 lines · Josh Poimboeuf
tools/objtool/klp-diff.c new file +1
-0+#define SEC_NAME_LEN 512#define SYM_NAME_LEN 512
tools/objtool/klp-diff.c line 62
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+static int validate_ffunction_fdata_sections (struct elf *elf) +{ +struct symbol *sym; +bool found_text = false, found_data = false; + +for_each_sym (elf, sym) { +char sec_name[SEC_NAME_LEN ]; + +if (!found_text &&is_func_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".text.%s" , sym->name);
So given SYM_NAME_LEN is 512, this SEC_NAME_LEN should be at least 6 more, no?
Quoted 17 lines
tools/objtool/klp-diff.c new file +17
-0+if (!strcmp (sym->sec->name, sec_name)) + found_text = true; + } + +if (!found_data &&is_object_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".data.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_data = true; + } + +if (found_text && found_data) +return 0 ; + } + +ERROR ("changed object '%s' not built with -ffunction-sections and -fdata-sections" , elf->name); +return -1 ; +}
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 5 lines · Peter Zijlstra
Quoted 3 lines · Josh Poimboeuf
+#define
SEC_NAME_LEN512
#defineSYM_NAME_LEN512
Quoted 13 lines
tools/objtool/klp-diff.c line 62
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+static int validate_ffunction_fdata_sections (struct elf *elf) +{ +struct symbol *sym; +bool found_text = false, found_data = false; + +for_each_sym (elf, sym) { +char sec_name[SEC_NAME_LEN ]; + +if (!found_text &&is_func_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".text.%s" , sym->name);So given
SYM_NAME_LENis 512, thisSEC_NAME_LENshould be at least 6 more, no?
I suppose so. There's also the .rela.text.* and .klp.rela.sec_objname.* prefixes. I'll just bump SEC_NAME_LEN to 1024.
I should also double check the snprintf() return codes.
Signature · 1 line
Josh
Peter Zijlstra
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufLet me hand you a fresh bucket of curlies, you must've run out :-)
tools/objtool/klp-diff.c line 213
Quoted 5 lines · Josh Poimboeuf
tools/objtool/klp-diff.c new file +5
-0+static struct symbol *first_file_symbol (struct elf *elf) +{ +struct symbol *sym; + +for_each_sym (elf, sym)
{
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (is_file_sym(sym)) + return sym;
}
tools/objtool/klp-diff.c line 222
Quoted 7 lines
tools/objtool/klp-diff.c new file +7
-0+ +return NULL ; +} + +static struct symbol *next_file_symbol (struct elf *elf,struct symbol *sym) +{ +for_each_sym_continue (elf, sym)
{
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (is_file_sym(sym)) + return sym;
}
Quoted 24 lines
tools/objtool/klp-diff.c new file +23
-0+ +return NULL ; +} +static bool is_special_section (struct section *sec) +{ +static const char *const specials[] = { +".altinstructions" , +".smp_locks" , +"__bug_table" , +"__ex_table" , +"__jump_table" , +"__mcount_loc" , + +/* +* Extract .static_call_sites here to inherit non-module +* preferential treatment. The later static call processing +* during klp module build will be skipped when it sees this +* section already exists. +*/ +".static_call_sites" , + }; + +static const char *const non_special_discards[] = {
tools/objtool/klp-diff.c line 298
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+".discard.addressable" , + SYM_CHECKSUM_SEC, + }; + +for (int i =0 ; i <ARRAY_SIZE (specials); i++) +if (!strcmp (sec->name, specials[i])) +return true; + +/* Most .discard sections are special */ +for (int i =0 ; i <ARRAY_SIZE (non_special_discards); i++)
{
Review
Quoted 2 lines
(quoted diff) +2
-0+ if (!strcmp(sec->name, non_special_discards[i])) + return false;
}
Quoted 6 lines
tools/objtool/klp-diff.c new file +6
-0+ +return strstarts (sec->name,".discard." ); +} + +/* +* These sections are referenced by special sections but aren't considered
tools/objtool/klp-diff.c line 316
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+* special sections themselves. +*/ +static bool is_special_section_aux (struct section *sec) +{ +static const char *const specials_aux[] = { +".altinstr_replacement" , +".altinstr_aux" , + }; + +for (int i =0 ; i <ARRAY_SIZE (specials_aux); i++)
{
tools/objtool/klp-diff.c line 318
Quoted 2 lines
tools/objtool/klp-diff.c new file +2
-0+if (!strcmp (sec->name, specials_aux[i])) +return true;
}
tools/objtool/klp-diff.c line 254
Quoted 4 lines
tools/objtool/klp-diff.c new file +4
-0+ +return false; +} +
And possibly more..
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 2 lines · Peter Zijlstra
Let me hand you a fresh bucket of curlies, you must've run out :-)
Thanks for the freebies, imported curlies aren't so cheap these days!
Signature · 1 line
Josh
Peter Zijlstra
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufQuoted 40 lines · Josh Poimboeuf
tools/objtool/include/objtool/elf.h +6
-0@@ -17,6 +17,7 @@#include <objtool/checksum_types.h> #include <arch/elf.h> +#define SEC_NAME_LEN 512#define SYM_NAME_LEN 512#ifdef LIBELF_USE_DEPRECATED @@ -50,10 +51,12 @@ struct section {bool _changed, text, rodata, noinstr, init, truncate;struct reloc *relocs;unsigned long nr_alloc_relocs; +struct section *twin; };struct symbol {struct list_head list; +struct list_head global_list;struct rb_node node;struct elf_hash_node hash;struct elf_hash_node name_hash;@@ -79,10 +82,13 @@ struct symbol { u8 cold : 1; u8 prefix : 1; u8 debug_checksum : 1; + u8 changed : 1; + u8 included : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;struct checksum csum; +struct symbol *twin, *clone; };struct reloc {
tools/objtool/include/objtool/elf.h struct elf { · line 112
Quoted 7 lines
tools/objtool/include/objtool/elf.h +1
-0@@ -100,6 +106,7 @@ struct elf {const char *name, *tmp_name;unsigned int num_files;struct list_head sections; +struct list_head symbols;unsigned long num_relocs;int symbol_bits;
ISTR us spending significant effort shrinking all this stuff. How does this affect vmlinux.o memory footprint etc?
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 34 lines · Peter Zijlstra
Quoted 34 lines
tools/objtool/include/objtool/elf.h +6
-0@@ -50,10 +51,12 @@ struct section {bool _changed, text, rodata, noinstr, init, truncate;struct reloc *relocs;unsigned long nr_alloc_relocs; +struct section *twin; };struct symbol {struct list_head list; +struct list_head global_list;struct rb_node node;struct elf_hash_node hash;struct elf_hash_node name_hash;@@ -79,10 +82,13 @@ struct symbol { u8 cold : 1; u8 prefix : 1; u8 debug_checksum : 1; + u8 changed : 1; + u8 included : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;struct checksum csum; +struct symbol *twin, *clone; };struct reloc {@@ -100,6 +106,7 @@ struct elf {const char *name, *tmp_name;unsigned int num_files;struct list_head sections; +struct list_head symbols;unsigned long num_relocs;
Quoted 4 lines
tools/objtool/include/objtool/elf.h struct elf { · line 112
Quoted 1 line
tools/objtool/include/objtool/elf.h +0
-0int symbol_bits;ISTR us spending significant effort shrinking all this stuff. How does this affect vmlinux.o memory footprint etc?
IIRC, most of our shrinking efforts were related to instructions and relocs, which use up the bulk of the memory. This set doesn't touch those.
Before and after the set, with a Fedora config:
Maximum resident set size (kbytes): 2934116
Maximum resident set size (kbytes): 2953708
So about ~0.67% more memory.
Signature · 1 line
Josh
Joe Lawrence
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
In reply to Josh PoimboeufQuoted 8 lines · Josh Poimboeuf
tools/objtool/klp-diff.c new file +8
-0+static int validate_ffunction_fdata_sections (struct elf *elf) +{ +struct symbol *sym; +bool found_text = false, found_data = false; + +for_each_sym (elf, sym) { +char sec_name[SEC_NAME_LEN ]; +
tools/objtool/klp-diff.c line 70
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+if (!found_text &&is_func_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".text.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_text = true; + } + +if (!found_data &&is_object_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".data.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_data = true;
Hi Josh,
Should we check for other data section prefixes here, like:
else {
snprintf(sec_name, SEC_NAME_LEN, ".rodata.%s", sym->name);
if (!strcmp(sym->sec->name, sec_name))
found_data = true;
}because on my system, I tried patching net/netfilter/nft_tunnel.c, but if I look at its OBJECTS and their corresponding sections, I see:
24: 0000000000000000 24 OBJECT LOCAL DEFAULT 71 __msg.92
[71] .rodata.__msg.92 PROGBITS
43: 000000000000003f 22 OBJECT LOCAL DEFAULT 72 __UNIQUE_ID_alias_1010
42: 000000000000002f 16 OBJECT LOCAL DEFAULT 72 __UNIQUE_ID_alias_1011
44: 0000000000000055 47 OBJECT LOCAL DEFAULT 72 __UNIQUE_ID_author_1009
41: 0000000000000000 47 OBJECT LOCAL DEFAULT 72 __UNIQUE_ID_description_1012
45: 0000000000000084 12 OBJECT LOCAL DEFAULT 72 __UNIQUE_ID_license_1008[72] .modinfo
47: 0000000000000000 8 OBJECT LOCAL DEFAULT 73 __UNIQUE_ID_addressable_cleanup_module_1007
[73] .exit.data
49: 0000000000000000 8 OBJECT LOCAL DEFAULT 75 __UNIQUE_ID_addressable_init_module_1006
[72] .modinfo
51: 0000000000000000 56 OBJECT LOCAL DEFAULT 77 nft_tunnel_obj_ops
[77] .rodata.nft_tunnel_obj_ops
5: 0000000000000000 64 OBJECT LOCAL DEFAULT 79 nft_tunnel_obj_type
4: 0000000000000040 80 OBJECT LOCAL DEFAULT 79 nft_tunnel_type[79] .data..read_mostly
53: 0000000000000000 160 OBJECT LOCAL DEFAULT 81 nft_tunnel_key_policy
[81] .rodata.nft_tunnel_key_policy
30: 0000000000000000 64 OBJECT LOCAL DEFAULT 82 nft_tunnel_opts_policy
[82] .rodata.nft_tunnel_opts_policy
23: 0000000000000000 64 OBJECT LOCAL DEFAULT 83 nft_tunnel_opts_geneve_policy
[83] .rodata.nft_tunnel_opts_geneve_policy
32: 0000000000000000 80 OBJECT LOCAL DEFAULT 84 nft_tunnel_opts_erspan_policy
[84] .rodata.nft_tunnel_opts_erspan_policy
31: 0000000000000000 32 OBJECT LOCAL DEFAULT 85 nft_tunnel_opts_vxlan_policy
[85] .rodata.nft_tunnel_opts_vxlan_policy
20: 0000000000000000 64 OBJECT LOCAL DEFAULT 86 nft_tunnel_ip6_policy
[86] .rodata.nft_tunnel_ip6_policy
29: 0000000000000000 48 OBJECT LOCAL DEFAULT 87 nft_tunnel_ip_policy
[87] .rodata.nft_tunnel_ip_policy
62: 0000000000000000 136 OBJECT LOCAL DEFAULT 88 nft_tunnel_get_ops
[88] .rodata.nft_tunnel_get_ops
64: 0000000000000000 64 OBJECT LOCAL DEFAULT 91 nft_tunnel_policy
[91] .rodata.nft_tunnel_policy
I believe there are others like this, drivers/firmware/iscsi_ibft.o for one, so even though validate_ffunction_fdata_sections() only needs to find one .text.<section> and one .data.<section>, not all objects may be able to provide that.
At the same time, while we're here, what about other .text.* section prefixes?
--
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 22 lines · Joe Lawrence
Quoted 8 lines · Josh Poimboeuf
tools/objtool/klp-diff.c new file +8
-0+static int validate_ffunction_fdata_sections (struct elf *elf) +{ +struct symbol *sym; +bool found_text = false, found_data = false; + +for_each_sym (elf, sym) { +char sec_name[SEC_NAME_LEN ]; +tools/objtool/klp-diff.c line 70
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+if (!found_text &&is_func_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".text.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_text = true; + } + +if (!found_data &&is_object_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".data.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_data = true;Hi Josh,
Quoted 7 lines
Should we check for other data section prefixes here, like:
else { snprintf(sec_name, SEC_NAME_LEN, ".rodata.%s", sym->name); if (!strcmp(sym->sec->name, sec_name)) found_data = true; }
Indeed. And also .bss.*.
Quoted 2 lines
At the same time, while we're here, what about other .text.* section prefixes?
AFAIK, .text.* is the only one.
Signature · 1 line
Josh
Joe Lawrence
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 31 lines · Josh Poimboeuf
Quoted 22 lines · Joe Lawrence
Quoted 8 lines · Josh Poimboeuf
tools/objtool/klp-diff.c new file +8
-0+static int validate_ffunction_fdata_sections (struct elf *elf) +{ +struct symbol *sym; +bool found_text = false, found_data = false; + +for_each_sym (elf, sym) { +char sec_name[SEC_NAME_LEN ]; +tools/objtool/klp-diff.c line 70
Quoted 10 lines
tools/objtool/klp-diff.c new file +10
-0+if (!found_text &&is_func_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".text.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_text = true; + } + +if (!found_data &&is_object_sym (sym)) { +snprintf (sec_name,SEC_NAME_LEN ,".data.%s" , sym->name); +if (!strcmp (sym->sec->name, sec_name)) + found_data = true;Hi Josh,
Quoted 7 lines
Should we check for other data section prefixes here, like:
else { snprintf(sec_name, SEC_NAME_LEN, ".rodata.%s", sym->name); if (!strcmp(sym->sec->name, sec_name)) found_data = true; }
Quoted 7 lines
Indeed. And also .bss.*.
Quoted 2 lines
At the same time, while we're here, what about other .text.* section prefixes?
AFAIK, .text.* is the only one.
What about .text.unlikely, .text.hot (not sure if these can come alone or are only optimization copies) ?
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 52/62] objtool/klp: Introduce klp diff subcommand for diffing object files
Quoted 10 lines · Joe Lawrence
Quoted 10 lines
Quoted 7 lines
Should we check for other data section prefixes here, like:
else { snprintf(sec_name, SEC_NAME_LEN, ".rodata.%s", sym->name); if (!strcmp(sym->sec->name, sec_name)) found_data = true; }Indeed. And also .bss.*.
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
At the same time, while we're here, what about other .text.* section prefixes?
AFAIK, .text.* is the only one.
What about .text.unlikely, .text.hot (not sure if these can come alone or are only optimization copies) ?
Hm, I think .text.unlikely.foo is at least theoretically possible without .text.foo. Seems "unlikely" though.
IIRC, .text.hot is used for profile-guided optimization, probably not a concern here.
There are actually several edge cases that would cause this validation to fail. If a module only had init/exit then it wouldn't have any .text.foo. Or if it didn't have global data then there'd be no .[ro]data.foo.
This function could get pretty fiddly, and honestly I'm not sure this validation buys us much anyway. I'm thinking about just removing it altogether...
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 53/62 objtool/klp: Add --debug option to show cloning decisions
In reply to Josh PoimboeufAdd a --debug option to klp diff which prints cloning decisions and an indented dependency tree for all cloned symbols and relocations. This helps visualize which symbols and relocations were included and why.
--- tools/objtool/include/objtool/warn.h | 21 ++++++++ tools/objtool/klp-diff.c | 75 ++++++++++++++++++++++++++++ tools/objtool/objtool.c | 3 ++ 3 files changed, 99 insertions(+)
tools/objtool/include/objtool/warn.h +21 -0
@@ -102,6 +102,10 @@ static inline char *offstr(struct section *sec, unsigned long offset)#define ERROR_FUNC (sec, offset, format, ...) __WARN_FUNC(ERROR_STR, sec, offset, format, ##__VA_ARGS__)#define ERROR_INSN (insn, format, ...) WARN_FUNC(insn->sec, insn->offset, format, ##__VA_ARGS__) +extern bool debug; +extern int indent; + +static inline void unindent (int *unused) { indent--; }#define __dbg (format, ...) \ fprintf(stderr, \
@@ -110,6 +114,23 @@ static inline char *offstr(struct section *sec, unsigned long offset) objname ? ": " : "", \ ##__VA_ARGS__) +#define dbg(args...) \ +({ \ + if (unlikely(debug)) \ + __dbg(args); \ +}) + +#define __dbg_indent (format, ...) \ +({ \ + if (unlikely(debug)) \ + __dbg("%*s" format, indent * 8, "", ##__VA_ARGS__); \ +}) + +#define dbg_indent(args...) \ + int __attribute__((cleanup(unindent))) __dummy_##__COUNTER__; \ + __dbg_indent(args); \ + indent++ +#define dbg_checksum (func, insn, checksum) \ ({ \ if (unlikely(insn->sym && insn->sym->pfunc && \
tools/objtool/klp-diff.c +75 -0
123 lines not on this page · raw message
tools/objtool/objtool.c +3 -0
@@ -16,6 +16,9 @@#include <objtool/objtool.h> #include <objtool/warn.h> +bool debug; +int indent; +static struct objtool_file file;struct objtool_file *objtool_open_read (const char *filename)
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 54/62 objtool/klp: Add post-link subcommand to finalize livepatch modules
In reply to Josh PoimboeufLivepatch needs some ELF magic which linkers don't like:
- Two relocation sections (.rela*, .klp.rela*) for the same text section.
- Use of SHN_LIVEPATCH to mark livepatch symbols.
Unfortunately linkers tend to mangle such things. To work around that, klp diff generates a linker-compliant intermediate binary which encodes the relevant KLP section/reloc/symbol metadata.
After module linking, the .ko then needs to be converted to an actual livepatch module. Introduce a new klp post-link subcommand to do so.
--- tools/objtool/Build | 2 +- tools/objtool/builtin-klp.c | 1 + tools/objtool/include/objtool/klp.h | 4 + tools/objtool/klp-post-link.c | 165 ++++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) create mode 100644 tools/objtool/klp-post-link.c
tools/objtool/Build +1 -1
@@ -9,7 +9,7 @@ objtool-y += elf.o objtool-y += objtool.o objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o -objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o +objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o objtool-y += libstring.o objtool-y += libctype.o
tools/objtool/builtin-klp.c +1 -0
@@ -14,6 +14,7 @@ struct subcmd {static struct subcmd subcmds[] = { {"diff" ,"Generate binary diff of two object files" , cmd_klp_diff, }, + {"post-link" ,"Finalize klp symbols/relocs after module linking" , cmd_klp_post_link, }, };static void cmd_klp_usage (void )
tools/objtool/include/objtool/klp.h +4 -0
@@ -2,6 +2,9 @@#ifndef _OBJTOOL_KLP_H#define _OBJTOOL_KLP_H +#define SHF_RELA_LIVEPATCH 0x00100000 +#define SHN_LIVEPATCH 0xff20 +/* * __klp_objects and __klp_funcs are created by klp diff and used by the patch * module init code to build the klp_patch, klp_object and klp_func structs
@@ -27,5 +30,6 @@ struct klp_reloc { };int cmd_klp_diff (int argc,const char **argv); +int cmd_klp_post_link (int argc,const char **argv);#endif /* _OBJTOOL_KLP_H */
tools/objtool/klp-post-link.c new file +165 -0
165 lines not on this page · raw message
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerRe: [PATCH v2 54/62] objtool/klp: Add post-link subcommand to finalize livepatch modules
Quoted 7 lines · Josh Poimboeuf
Livepatch needs some ELF magic which linkers don't like:
- Two relocation sections (.rela*, .klp.rela*) for the same text section.
- Use of
SHN_LIVEPATCHto mark livepatch symbols.
Quoted 8 lines
Unfortunately linkers tend to mangle such things. To work around that, klp diff generates a linker-compliant intermediate binary which encodes the relevant KLP section/reloc/symbol metadata.
After module linking, the .ko then needs to be converted to an actual livepatch module. Introduce a new klp post-link subcommand to do so.
Signed-off-by Josh Poimboeuf <[email protected]>
FWIW, I have plans to get rid of this post-link step by saying goodbye to "klp relocs" altogether.
I have a working PoC which implements livepatch "submodules" which are specific to their target object (vmlinux or module). The top-level livepatch module keeps its submodule .ko binaries in memory (embedded in its private data) and loads/unloads them on demand.
The end result looks a lot cleaner. It also removes the restrictions we have today which don't allow references to static call/branch keys which live in modules.
That will have to be another patch set though, 63 patches is plenty long enough already.
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 55/62 objtool: Disallow duplicate prefix symbols
In reply to Josh PoimboeufIn preparation for adding objtool 'check' support for analyzing livepatch modules, error out if a duplicate prefix symbol is attempted.
--- tools/objtool/check.c | 12 +++++++++++- tools/objtool/elf.c | 9 +++++++++ tools/objtool/include/objtool/elf.h | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-)
tools/objtool/check.c +11 -1
@@ -4271,6 +4271,7 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)for (prev =prev_insn_same_sec (file, insn); prev; prev =prev_insn_same_sec (file, prev)) { +struct symbol *sym_pfx;u64 offset;if (prev->type !=INSN_NOP )
@@ -4284,7 +4285,12 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)if (offset < opts.prefix)continue ; -elf_create_prefix_symbol (file->elf, func, opts.prefix); + sym_pfx =elf_create_prefix_symbol (file->elf, func, opts.prefix); +if (!sym_pfx) { +WARN ("duplicate prefix symbol for %s\n" , func->name); +return -1 ; + } +break ; }
@@ -4320,6 +4326,10 @@ static int add_prefix_symbols(struct objtool_file *file)if (!is_func_sym (func))continue ; +/* +* Ignore this error on purpose, there are valid +* reasons for this to fail. +*/ add_prefix_symbol (file, func); } }
tools/objtool/elf.c +9 -0
@@ -946,11 +946,20 @@ elf_create_prefix_symbol(struct elf *elf, struct symbol *orig, size_t size)size_t namelen =strlen (orig->name) +sizeof ("__pfx_" );char name[SYM_NAME_LEN ];unsigned long offset; +struct symbol *sym;snprintf (name, namelen,"__pfx_%s" , orig->name); + sym = orig; offset = orig->sym.st_value - size; +sec_for_each_sym_continue_reverse (orig->sec, sym) { +if (sym->offset < offset) +break ; +if (sym->offset == offset && !strcmp (sym->name, name)) +return NULL ; + } + return elf_create_symbol(elf, name, orig->sec, GELF_ST_BIND(orig->sym.st_info), GELF_ST_TYPE(orig->sym.st_info),
tools/objtool/include/objtool/elf.h +3 -0
@@ -443,6 +443,9 @@ static inline void set_sym_next_reloc(struct reloc *reloc, struct reloc *next)#define sec_for_each_sym (sec, sym) \ list_for_each_entry(sym, &sec->symbol_list, list) +#define sec_for_each_sym_continue_reverse (sec, sym) \ + list_for_each_entry_continue_reverse(sym, &sec->symbol_list, list) +#define sec_prev_sym (sym) \ sym->sec && sym->list.prev != &sym->sec->symbol_list ? \ list_prev_entry(sym, list) : NULL
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 56/62 objtool: Add base objtool support for livepatch modules
In reply to Josh PoimboeufIn preparation for klp-build, enable "classic" objtool to work on livepatch modules:
- Avoid duplicate symbol/section warnings for prefix symbols and the .static_call_sites and __mcount_loc sections which may have already been extracted by klp diff.
- Add __klp_funcs to the IBT function pointer section whitelist.
- Prevent KLP symbols from getting incorrectly classified as cold subfunctions.
--- tools/objtool/check.c | 44 +++++++++++++++++++++---- tools/objtool/elf.c | 4 ++- tools/objtool/include/objtool/elf.h | 1 + tools/objtool/include/objtool/objtool.h | 2 +- 4 files changed, 43 insertions(+), 8 deletions(-)
tools/objtool/check.c +38 -6
@@ -3,6 +3,7 @@* Copyright (C) 2015-2017 Josh Poimboeuf <[email protected]> */ +#define _GNU_SOURCE/* memmem() */ #include <string.h> #include <stdlib.h> #include <inttypes.h>
@@ -627,6 +628,20 @@ static int init_pv_ops(struct objtool_file *file)return 0 ; } +static bool is_livepatch_module (struct objtool_file *file) +{ +struct section *sec; + +if (!opts.module) +return false; + + sec =find_section_by_name (file->elf,".modinfo" ); +if (!sec) +return false; + +return memmem (sec->data->d_buf,sec_size (sec),"\0livepatch=Y" ,12 ); +} +static int create_static_call_sections (struct objtool_file *file) {struct static_call_site *site;
@@ -638,7 +653,12 @@ static int create_static_call_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,".static_call_sites" );if (sec) { -if (!opts.dryrun) +/* +* Livepatch modules may have already extracted the static call +* site entries to take advantage of vmlinux static call +* privileges. +*/ +if (!!opts.dryrun || !file->klp)WARN ("file already has .static_call_sites section, skipping" );return 0 ;
@@ -684,7 +704,7 @@ static int create_static_call_sections(struct objtool_file *file) key_sym =find_symbol_by_name (file->elf, tmp);if (!key_sym) { -if (!opts.module) { +if (!opts.module || file->klp) {ERROR ("static_call: can't find static_call_key symbol: %s" , tmp);return -1 ; }
@@ -911,7 +931,11 @@ static int create_mcount_loc_sections(struct objtool_file *file) sec =find_section_by_name (file->elf,"__mcount_loc" );if (sec) { -if (!opts.dryrun) +/* +* Livepatch modules have already extracted their __mcount_loc +* entries to cover the !CONFIG_FTRACE_MCOUNT_USE_OBJTOOL case. +*/ +if (!opts.dryrun && !file->klp)WARN ("file already has __mcount_loc section, skipping" );return 0 ;
@@ -2612,6 +2636,8 @@ static int decode_sections(struct objtool_file *file) {int ret; + file->klp =is_livepatch_module (file); +mark_rodata (file); ret =init_pv_ops (file);
@@ -4285,8 +4311,13 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func)if (offset < opts.prefix)continue ; +/* +* Ignore attempts to make duplicate symbols in livepatch +* modules. They've already extracted the prefix symbols +* except for the newly compiled init.c. +*/ sym_pfx =elf_create_prefix_symbol (file->elf, func, opts.prefix); -if (!sym_pfx) { +if (!sym_pfx && !file->klp) {WARN ("duplicate prefix symbol for %s\n" , func->name);return -1 ; }
@@ -4621,6 +4652,7 @@ static int validate_ibt(struct objtool_file *file) !strncmp (sec->name,".debug" ,6 ) || !strcmp (sec->name,".altinstructions" ) || !strcmp (sec->name,".ibt_endbr_seal" ) || + !strcmp (sec->name,".kcfi_traps" ) || !strcmp (sec->name,".orc_unwind_ip" ) || !strcmp (sec->name,".retpoline_sites" ) || !strcmp(sec->name, ".smp_locks") ||
@@ -4630,12 +4662,12 @@ static int validate_ibt(struct objtool_file *file) !strcmp (sec->name,"__bug_table" ) || !strcmp (sec->name,"__ex_table" ) || !strcmp (sec->name,"__jump_table" ) || + !strcmp (sec->name,"__klp_funcs" ) || !strcmp (sec->name,"__mcount_loc" ) || - !strcmp (sec->name,".kcfi_traps" ) || !strcmp (sec->name,".llvm.call-graph-profile" ) || !strcmp (sec->name,".llvm_bb_addr_map" ) || !strcmp (sec->name,"__tracepoints" ) || - strstr(sec->name, "__patchable_function_entries")) + !strcmp(sec->name, "__patchable_function_entries")) continue;for_each_reloc (sec->rsec, reloc)
tools/objtool/elf.c +3 -1
@@ -496,8 +496,10 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym) (strstarts(sym->name, "__pfx") || strstarts(sym->name, "__cfi_"))) sym->prefix = 1; +if (strstarts (sym->name,".klp.sym" )) + sym->klp =1 ; -if (is_func_sym (sym) &&strstr (sym->name,".cold" )) +if (!sym->klp &&is_func_sym (sym) &&strstr (sym->name,".cold" )) sym->cold =1 ; sym->pfunc = sym->cfunc = sym;
tools/objtool/include/objtool/elf.h +1 -0
@@ -84,6 +84,7 @@ struct symbol { u8 debug_checksum : 1; u8 changed : 1; u8 included : 1; + u8 klp : 1;struct list_head pv_target;struct reloc *relocs;struct section *group_sec;
tools/objtool/include/objtool/objtool.h +1 -1
@@ -30,7 +30,7 @@ struct objtool_file {struct list_head mcount_loc_list;struct list_head endbr_list;struct list_head call_list; -bool ignore_unreachables, hints, rodata; +bool ignore_unreachables, hints, rodata, klp;unsigned int nr_endbr;unsigned int nr_endbr_int;
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 57/62 livepatch/klp-build: Introduce fix-patch-lines script to avoid __LINE__ diff noise
In reply to Josh PoimboeufThe __LINE__ macro creates challenges for binary diffing. When a .patch file adds or removes lines, it shifts the line numbers for all code below it.
This can cause the code generation of functions using __LINE__ to change due to the line number constant being embedded in a MOV instruction, despite there being no semantic difference.
Avoid such false positives by adding a fix-patch-lines script which can be used to insert a #line directive in each patch hunk affecting the line numbering. This script will be used by klp-build, which will be introduced in a subsequent patch.
--- MAINTAINERS | 1 + scripts/livepatch/fix-patch-lines | 79 +++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100755 scripts/livepatch/fix-patch-lines
MAINTAINERS +1 -0
@@ -13792,6 +13792,7 @@ F: include/linux/livepatch*.h F: kernel/livepatch/ F: kernel/module/livepatch.c F: samples/livepatch/ +F: scripts/livepatch/ F: tools/testing/selftests/livepatch/ LLC (802.2)
scripts/livepatch/fix-patch-lines new file +79 -0
79 lines not on this page · raw message
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 58/62 livepatch/klp-build: Add stub init code for livepatch modules
In reply to Josh PoimboeufAdd a module initialization stub which can be linked with binary diff objects to produce a livepatch module.
--- scripts/livepatch/init.c | 108 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 108 insertions(+) create mode 100644 scripts/livepatch/init.c
scripts/livepatch/init.c new file +108 -0
@@ -0,0 +1,108 @@ +// SPDX-License-Identifier: GPL-2.0 +/* +* Init code for a livepatch kernel module +*/ + +#define pr_fmt (fmt) KBUILD_MODNAME ": " fmt + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/livepatch.h> + +extern struct klp_object_ext __start_klp_objects[]; +extern struct klp_object_ext __stop_klp_objects[]; + +static struct klp_patch *patch; + +static int __init livepatch_mod_init (void ) +{ +struct klp_object *objs; +unsigned int nr_objs; +int ret; + + nr_objs = __stop_klp_objects - __start_klp_objects; + +if (!nr_objs) { +pr_err ("nothing to patch!\n" ); + ret = -EINVAL ; + goto err; + } + + patch =kzalloc (sizeof (*patch),GFP_KERNEL ); +if (!patch) { + ret = -ENOMEM ; + goto err; + } + + objs =kzalloc (sizeof (struct klp_object ) * (nr_objs +1 ),GFP_KERNEL ); +if (!objs) { + ret = -ENOMEM ; + goto err_free_patch; + } + +for (int i =0 ; i < nr_objs; i++) { +struct klp_object_ext *obj_ext = __start_klp_objects + i; +struct klp_func_ext *funcs_ext = obj_ext->funcs; +unsigned int nr_funcs = obj_ext->nr_funcs; +struct klp_func *funcs = objs[i].funcs; +struct klp_object *obj = objs + i; + + funcs =kzalloc (sizeof (struct klp_func ) * (nr_funcs +1 ),GFP_KERNEL ); +if (!funcs) { + ret = -ENOMEM ; +for (int j =0 ; j < i; j++) +kfree (objs[i].funcs); + goto err_free_objs; + } + +for (int j =0 ; j < nr_funcs; j++) { + funcs[j].old_name = funcs_ext[j].old_name; + funcs[j].new_func = funcs_ext[j].new_func; + funcs[j].old_sympos = funcs_ext[j].sympos; + } + + obj->name = obj_ext->name; + obj->funcs = funcs; + +memcpy (&obj->callbacks, &obj_ext->callbacks,sizeof (struct klp_callbacks )); + } + + patch->mod =THIS_MODULE ; + patch->objs = objs; + +/* TODO patch->states */ + +#ifdef KLP_NO_REPLACE + patch->replace = false; +#else + patch->replace = true; +#endif + +return klp_enable_patch (patch); + +err_free_objs: +kfree (objs); +err_free_patch: +kfree (patch); +err: +return ret; +} + +static void __exit livepatch_mod_exit(void) +{ +unsigned int nr_objs; + + nr_objs = __stop_klp_objects - __start_klp_objects; + +for (int i =0 ; i < nr_objs; i++) +kfree (patch->objs[i].funcs); + +kfree (patch->objs); +kfree (patch); +} + +module_init (livepatch_mod_init); +module_exit (livepatch_mod_exit); +MODULE_LICENSE ("GPL" ); +MODULE_INFO (livepatch,"Y" ); +MODULE_DESCRIPTION ("Livepatch module" );
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 59/62 livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh PoimboeufAdd a klp-build script which automates the generation of a livepatch module from a source .patch file by performing the following steps:
- Builds an original kernel with -function-sections and -fdata-sections, plus objtool function checksumming.
- Applies the .patch file and rebuilds the kernel using the same options.
- Runs 'objtool klp diff' to detect changed functions and generate intermediate binary diff objects.
- Builds a kernel module which links the diff objects with some livepatch module init code (scripts/livepatch/init.c).
- Finalizes the livepatch module (aka work around linker wreckage) using 'objtool klp post-link'.
--- scripts/livepatch/klp-build | 697 ++++++++++++++++++++++++++++++++++++ 1 file changed, 697 insertions(+) create mode 100755 scripts/livepatch/klp-build
scripts/livepatch/klp-build new file +697 -0
@@ -0,0 +1,697 @@ +#!/bin/bash +# SPDX-License-Identifier: GPL-2.0 +# +# Build a livepatch module +# + +# shellcheck disable=SC1090 + +if (( BASH_VERSINFO[0] < 4 \ + || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] < 4) )); then + echo "error: this script requires bash 4.4+" >&2 + exit 1 +fi + +set -o errexit +set -o errtrace +set -o pipefail +set -o nounset + +# Allow doing 'cmd | mapfile -t array' instead of 'mapfile -t array < <(cmd)'. +# This helps keep execution in pipes so pipefail+errexit can catch errors. +shopt -s lastpipe + +unset SKIP_CLEANUP XTRACE +REPLACE=1 +SHORT_CIRCUIT=0 +shopt -o xtrace | grep -q 'on' && XTRACE=1 +# Avoid removing the previous $TMP_DIR until args have been fully processed. +KEEP_TMP=1 + +CPUS="$(getconf _NPROCESSORS_ONLN)" +VERBOSE="-s" + + +SCRIPT="$(basename "$0")" +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +FIX_PATCH_LINES="$SCRIPT_DIR/fix-patch-lines" + +SRC="$(pwd)" +OBJ="$(pwd)" + +CONFIG="$OBJ/.config" +TMP_DIR="$OBJ/klp-tmp" + +ORIG_DIR="$TMP_DIR/orig" +PATCHED_DIR="$TMP_DIR/patched" +DIFF_DIR="$TMP_DIR/diff" +KMOD_DIR="$TMP_DIR/kmod" + +STASH_DIR="$TMP_DIR/stash" +TIMESTAMP="$TMP_DIR/timestamp" +PATCH_TMP_DIR="$TMP_DIR/tmp" + +KLP_DIFF_LOG="$DIFF_DIR/diff.log" + +grep0() { + command grep "$@" || true +} + +status() { + echo "$*" +} + +warn() { + echo "error: $(basename "$SCRIPT"): $*" >&2 +} + +die() { + warn "$@" + exit 1 +} + +declare -a STASHED_FILES + +stash_file() { + local file="$1" + local rel_file="${file#"$SRC"/}" + + [[ ! -e "$file" ]] && die "no file to stash: $file" + + mkdir -p "$STASH_DIR/$(dirname "$rel_file")" + cp -f "$file" "$STASH_DIR/$rel_file" + + STASHED_FILES+=("$rel_file") +} + +restore_files() { + local file + + for file in "${STASHED_FILES[@]}"; do + mv -f "$STASH_DIR/$file" "$SRC/$file" || warn "can't restore file: $file" + done + + STASHED_FILES=() +} + +cleanup() { + set +o nounset + revert_patches "--recount" + restore_files + [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR" + true +} + +trap_err() { + warn "line ${BASH_LINENO[0]}: '$BASH_COMMAND'" +} + +trap cleanup EXIT INT TERM HUP +trap trap_err ERR + +__usage() { + cat <<EOF +Usage: $SCRIPT [OPTIONS] PATCH_FILE(s) +Generate a livepatch module. + +Options: + -o, --output <file.ko> Output file [default: livepatch-<patch-name>.ko] + --no-replace Disable livepatch atomic replace + -v, --verbose Pass V=1 to kernel/module builds + +Advanced Options: + -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp) + 1|orig Build original kernel (default) + 2|patched Build patched kernel + 3|diff Diff objects + 4|kmod Build patch module + -T, --keep-tmp Preserve tmp dir on exit + +EOF +} + +usage() { + __usage >&2 +} + +process_args() { + local keep_tmp=0 + local short + local long + local args + + short="ho:vS:T" + long="help,output:,no-replace,verbose,short-circuit:,keep-tmp" + + args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { + echo; usage; exit + } + eval set -- "$args" + + while true; do + case "$1" in + -h | --help) + usage + exit 0 + ;; + -o | --output) + [[ "$2" != *.ko ]] && die "output filename should end with .ko" + OUTFILE="$2" + NAME="$(basename "$OUTFILE")" + NAME="${NAME%.ko}" + NAME="$(module_name_string "$NAME")" + shift 2 + ;; + --no-replace) + REPLACE=0 + shift + ;; + -v | --verbose) + VERBOSE="V=1" + shift + ;; + -S | --short-circuit) + [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir" + keep_tmp=1 + case "$2" in + 1 | orig) SHORT_CIRCUIT=1; ;; + 2 | patched) SHORT_CIRCUIT=2; ;; + 3 | diff) SHORT_CIRCUIT=3; ;; + 4 | mod) SHORT_CIRCUIT=4; ;; + *) die "invalid short-circuit step '$2'" ;; + esac + shift 2 + ;; + -T | --keep-tmp) + keep_tmp=1 + shift + ;; + --) + shift + break + ;; + *) + usage + exit 1 + ;; + esac + done + + if [[ $# -eq 0 ]]; then + usage + exit 1 + fi + + KEEP_TMP="$keep_tmp" + PATCHES=("$@") +} + +# temporarily disable xtrace for especially verbose code +xtrace_save() { + [[ -v XTRACE ]] && set +x + return 0 +} + +xtrace_restore() { + [[ -v XTRACE ]] && set -x + return 0 +} + +validate_config() { + xtrace_save "reading .config" + source "$CONFIG" || die "no .config file in $(dirname "$CONFIG")" + xtrace_restore + + [[ -v CONFIG_LIVEPATCH ]] \ + || die "CONFIG_LIVEPATCH not enabled" + + [[ -v CONFIG_GCC_PLUGIN_LATENT_ENTROPY ]] \ + && die "kernel option 'CONFIG_GCC_PLUGIN_LATENT_ENTROPY' not supported" + + [[ -v CONFIG_GCC_PLUGIN_RANDSTRUCT ]] \ + && die "kernel option 'CONFIG_GCC_PLUGIN_RANDSTRUCT' not supported" + + return 0 +} + +# Only allow alphanumerics and '_' and '-' in the module name. Everything else +# is replaced with '-'. Also truncate to 55 chars so the full name + NUL +# terminator fits in the kernel's 56-byte module name array. +module_name_string() { + echo "${1//[^a-zA-Z0-9_-]/-}" | cut -c 1-55 +} + +# If the module name wasn't specified on the cmdline with --output, give it a +# name based on the patch name. +set_module_name() { + [[ -v NAME ]] && return 0 + + if [[ "${#PATCHES[@]}" -eq 1 ]]; then + NAME="$(basename "${PATCHES[0]}")" + NAME="${NAME%.*}" + else + NAME="patch" + fi + + NAME="livepatch-$NAME" + NAME="$(module_name_string "$NAME")" + + OUTFILE="$NAME.ko" +} + +# Hardcode the value printed by the localversion script to prevent patch +# application from appending it with '+' due to a dirty git working tree. +set_kernelversion() { + local file="$SRC/scripts/setlocalversion" + local localversion + + stash_file "$file" + + localversion="$(cd "$SRC" && make --no-print-directory kernelversion)" + localversion="$(cd "$SRC" && KERNELVERSION="$localversion" ./scripts/setlocalversion)" + [[ -z "$localversion" ]] && die "setlocalversion failed" + + echo "echo $localversion" > "$file" +} + +get_patch_files() { + local patch="$1" + + grep0 -E '^(--- |\+\+\+ )' "$patch" \ + | gawk '{print $2}' \ + | sed 's|^[^/]*/||' \ + | sort -u +} + +# Make sure git re-stats the changed files +git_refresh() { + local patch="$1" + local files=() + + [[ ! -d "$SRC/.git" ]] && return + + get_patch_files "$patch" | mapfile -t files + + ( + cd "$SRC" + git update-index -q --refresh -- "${files[@]}" + ) +} + +check_unsupported_patches() { + local patch + + for patch in "${PATCHES[@]}"; do + local files=() + + get_patch_files "$patch" | mapfile -t files + + for file in "${files[@]}"; do + case "$file" in + lib/*|*.S) + die "unsupported patch to $file" + ;; + esac + done + done +} + +apply_patch() { + local patch="$1" + shift + local extra_args=("$@") + + [[ ! -f "$patch" ]] && die "$patch doesn't exist" + + ( cd "$SRC" && git apply "${extra_args[@]}" "$patch" ) + + APPLIED_PATCHES+=("$patch") +} + +revert_patch() { + local patch="$1" + shift + local extra_args=("$@") + local tmp=() + + ( cd "$SRC" && git apply --reverse "${extra_args[@]}" "$patch" ) + git_refresh "$patch" + + for p in "${APPLIED_PATCHES[@]}"; do + [[ "$p" == "$patch" ]] && continue + tmp+=("$p") + done + + APPLIED_PATCHES=("${tmp[@]}") +} + +apply_patches() { + local patch + + for patch in "${PATCHES[@]}"; do + apply_patch "$patch" + done +} + +revert_patches() { + local extra_args=("$@") + local patches=("${APPLIED_PATCHES[@]}") + + for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do + revert_patch "${patches[$i]}" "${extra_args[@]}" + done + + APPLIED_PATCHES=() + + # Make sure git actually sees the patches have been reverted. + [[ -d "$SRC/.git" ]] && (cd "$SRC" && git update-index -q --refresh) +} + +validate_patches() { + check_unsupported_patches + apply_patches + revert_patches +} + +do_init() { + # We're not yet smart enough to handle anything other than in-tree + # builds in pwd. + [[ ! "$SRC" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory" + [[ ! "$OBJ" -ef "$SCRIPT_DIR/../.." ]] && die "please run from the kernel root directory" + + (( SHORT_CIRCUIT <= 1 )) && rm -rf "$TMP_DIR" + mkdir -p "$TMP_DIR" + + APPLIED_PATCHES=() + + [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines" + + validate_config + set_module_name + set_kernelversion +} + +# Refresh the patch hunk headers, specifically the line numbers and counts. +refresh_patch() { + local patch="$1" + local tmpdir="$PATCH_TMP_DIR" + local files=() + + rm -rf "$tmpdir" + mkdir -p "$tmpdir/a" + mkdir -p "$tmpdir/b" + + # Find all source files affected by the patch + grep0 -E '^(--- |\+\+\+ )[^ /]+' "$patch" | + sed -E 's/(--- |\+\+\+ )[^ /]+\///' | + sort | uniq | mapfile -t files + + # Copy orig source files to 'a' + ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" ) + + # Copy patched source files to 'b' + apply_patch "$patch" --recount + ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" ) + revert_patch "$patch" --recount + + # Diff 'a' and 'b' to make a clean patch + ( cd "$tmpdir" && git diff --no-index --no-prefix a b > "$patch" ) || true +} + +# Copy the patches to a temporary directory, fix their lines so as not to +# affect the __LINE__ macro for otherwise unchanged functions further down the +# file, and update $PATCHES to point to the fixed patches. +fix_patches() { + local idx + local i + + rm -f "$TMP_DIR"/*.patch + + idx=0001 + for i in "${!PATCHES[@]}"; do + local old_patch="${PATCHES[$i]}" + local tmp_patch="$TMP_DIR/tmp.patch" + local patch="${PATCHES[$i]}" + local new_patch + + new_patch="$TMP_DIR/$idx-fixed-$(basename "$patch")" + + cp -f "$old_patch" "$tmp_patch" + refresh_patch "$tmp_patch" + "$FIX_PATCH_LINES" "$tmp_patch" > "$new_patch" + refresh_patch "$new_patch" + + PATCHES[i]="$new_patch" + + rm -f "$tmp_patch" + idx=$(printf "%04d" $(( 10#$idx + 1 ))) + done +} + +build_kernel() { + local log="$TMP_DIR/build.log" + local objtool_args=() + local cmd=() + + objtool_args=("--checksum") + [[ -v OBJTOOL_ARGS ]] && objtool_args+=("${OBJTOOL_ARGS}") + + cmd=("make") + + # When a patch to a kernel module references a newly created unexported + # symbol which lives in vmlinux or another kernel module, the patched + # kernel build fails with the following error: + # + # ERROR: modpost: "klp_string" [fs/xfs/xfs.ko] undefined! + # + # The undefined symbols are working as designed in that case. They get + # resolved later when the livepatch module build link pulls all the + # disparate objects together into the same kernel module. + # + # It would be good to have a way to tell modpost to skip checking for + # undefined symbols altogether. For now, just convert the error to a + # warning with KBUILD_MODPOST_WARN, and grep out the warning to avoid + # confusing the user. + # + cmd+=("KBUILD_MODPOST_WARN=1") + + cmd+=("$VERBOSE") + cmd+=("-j$CPUS") + cmd+=("KCFLAGS=-ffunction-sections -fdata-sections") + cmd+=("OBJTOOL_ARGS=${objtool_args[*]}") + cmd+=("vmlinux") + cmd+=("modules") + + ( + cd "$SRC" + "${cmd[@]}" \ + > >(tee -a "$log") \ + 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) + ) +} + +find_objects() { + local opts=("$@") + + # Find root-level vmlinux.o and non-root-level .ko files, + # excluding klp-tmp/ and .git/ + find "$OBJ" \( -path "$TMP_DIR" -o -path "$OBJ/.git" -o -regex "$OBJ/[^/][^/]*\.ko" \) -prune -o \ + -type f "${opts[@]}" \ + \( -name "*.ko" -o -path "$OBJ/vmlinux.o" \) \ + -printf '%P\n' +} + +# Copy all objects (.o archives) to $ORIG_DIR +copy_orig_objects() { + + rm -rf "$ORIG_DIR" + mkdir -p "$ORIG_DIR" + + ( + cd "$OBJ" + find_objects \ + | sed 's/\.ko$/.o/' \ + | xargs cp --parents --target-directory="$ORIG_DIR" + ) + + mv -f "$TMP_DIR/build.log" "$ORIG_DIR" + touch "$TIMESTAMP" +} + +# Copy all changed objects to $PATCHED_DIR +copy_patched_objects() { + local found + local files=() + local opts=() + + rm -rf "$PATCHED_DIR" + mkdir -p "$PATCHED_DIR" + + # Note this doesn't work with some configs, thus the 'cmp' below. + opts=("-newer") + opts+=("$TIMESTAMP") + + find_objects "${opts[@]}" | mapfile -t files + + xtrace_save "processing all objects" + for _file in "${files[@]}"; do + local rel_file="${_file/.ko/.o}" + local file="$OBJ/$rel_file" + local orig_file="$ORIG_DIR/$rel_file" + local patched_file="$PATCHED_DIR/$rel_file" + + [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" + + cmp -s "$orig_file" "$file" && continue + + mkdir -p "$(dirname "$patched_file")" + cp -f "$file" "$patched_file" + found=1 + done + xtrace_restore + + [[ -n "$found" ]] || die "no changes detected" + + mv -f "$TMP_DIR/build.log" "$PATCHED_DIR" +} + +# Diff changed objects, writing output object to $DIFF_DIR +diff_objects() { + local log="$KLP_DIFF_LOG" + local files=() + + rm -rf "$DIFF_DIR" + mkdir -p "$DIFF_DIR" + + find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files + [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + + # Diff all changed objects + for file in "${files[@]}"; do + local rel_file="${file#"$PATCHED_DIR"/}" + local orig_file="$rel_file" + local patched_file="$PATCHED_DIR/$rel_file" + local out_file="$DIFF_DIR/$rel_file" + local cmd=() + + mkdir -p "$(dirname "$out_file")" + + cmd=("$SRC/tools/objtool/objtool") + cmd+=("klp") + cmd+=("diff") + cmd+=("$orig_file") + cmd+=("$patched_file") + cmd+=("$out_file") + + ( + cd "$ORIG_DIR" + "${cmd[@]}" \ + > >(tee -a "$log") \ + 2> >(tee -a "$log" >&2) \ + || die "objtool klp diff failed" + ) + done +} + +# Build and post-process livepatch module in $KMOD_DIR +build_patch_module() { + local makefile="$KMOD_DIR/Kbuild" + local log="$KMOD_DIR/build.log" + local cflags=() + local files=() + local cmd=() + + rm -rf "$KMOD_DIR" + mkdir -p "$KMOD_DIR" + + cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR" + + echo "obj-m := $NAME.o" > "$makefile" + echo -n "$NAME-y := init.o" >> "$makefile" + + find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files + [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + + for file in "${files[@]}"; do + local rel_file="${file#"$DIFF_DIR"/}" + local kmod_file="$KMOD_DIR/$rel_file" + local cmd_file + + mkdir -p "$(dirname "$kmod_file")" + cp -f "$file" "$kmod_file" + + # Tell kbuild this is a prebuilt object + cp -f "$file" "${kmod_file}_shipped" + + echo -n " $rel_file" >> "$makefile" + + cmd_file="$ORIG_DIR/$(dirname "$rel_file")/.$(basename "$rel_file").cmd" + [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$(dirname "$kmod_file")" + done + + echo >> "$makefile" + + cflags=("-ffunction-sections") + cflags+=("-fdata-sections") + [[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE") + + cmd=("make") + cmd+=("$VERBOSE") + cmd+=("-j$CPUS") + cmd+=("--directory=.") + cmd+=("M=$KMOD_DIR") + cmd+=("KCFLAGS=${cflags[*]}") + + # Build a "normal" kernel module with init.c and the diffed objects + ( + cd "$SRC" + "${cmd[@]}" \ + > >(tee -a "$log") \ + 2> >(tee -a "$log" >&2) + ) + + # Save off the intermediate binary for debugging + cp -f "$KMOD_DIR/$NAME.ko" "$KMOD_DIR/$NAME.ko.orig" + + # Fix (and work around) linker wreckage for klp syms / relocs + "$SRC/tools/objtool/objtool" klp post-link "$KMOD_DIR/$NAME.ko" || die "objtool klp post-link failed" + + cp -f "$KMOD_DIR/$NAME.ko" "$OUTFILE" +} + + +################################################################################ + +process_args "$@" +do_init + +if (( SHORT_CIRCUIT <= 1 )); then + status "Building original kernel" + validate_patches + build_kernel + status "Copying original object files" + copy_orig_objects +fi + +if (( SHORT_CIRCUIT <= 2 )); then + status "Fixing patches" + fix_patches + apply_patches + status "Building patched kernel" + build_kernel + revert_patches + status "Copying patched object files" + copy_patched_objects +fi + +if (( SHORT_CIRCUIT <= 3 )); then + status "Diffing objects" + diff_objects +fi + +if (( SHORT_CIRCUIT <= 4 )); then + status "Building patch module: $OUTFILE" + build_patch_module +fi + +status "SUCCESS"
Signature · 1 line
2.49.0
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 24 lines
scripts/livepatch/klp-build new file +24
-0+# Build and post-process livepatch module in $KMOD_DIR +build_patch_module() { + local makefile="$KMOD_DIR/Kbuild" + local log="$KMOD_DIR/build.log" + local cflags=() + local files=() + local cmd=() + + rm -rf "$KMOD_DIR" + mkdir -p "$KMOD_DIR" + + cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR" + + echo "obj-m := $NAME.o" > "$makefile" + echo -n "$NAME-y := init.o" >> "$makefile" + + find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files + [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + + for file in "${files[@]}"; do + local rel_file="${file#"$DIFF_DIR"/}" + local kmod_file="$KMOD_DIR/$rel_file" + local cmd_file +
scripts/livepatch/klp-build line 629
Quoted 10 lines
scripts/livepatch/klp-build new file +10
-0+ mkdir -p "$(dirname "$kmod_file")" + cp -f "$file" "$kmod_file" + + # Tell kbuild this is a prebuilt object + cp -f "$file" "${kmod_file}_shipped" + + echo -n " $rel_file" >> "$makefile" + + cmd_file="$ORIG_DIR/$(dirname "$rel_file")/.$(basename "$rel_file").cmd" + [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$(dirname "$kmod_file")"
Hi Josh,
Should the .cmd file copy come from the reference SRC and not original ORIG directory?
cmd_file="$SRC/$(dirname "$rel_file")/.$(basename "$rel_file").cmd"
because I don't see any .cmd files in klp-tmp/orig/
FWIW, I only noticed this after backporting the series to centos-stream-10. There, I got this build error:
Building original kernel
Copying original object files
Fixing patches
Building patched kernel
Copying patched object files
Diffing objects
vmlinux.o: changed function: cmdline_proc_show Building patch module: livepatch-test.ko <...>/klp-tmp/kmod/.vmlinux.o.cmd: No such file or directory make[2]: *** [scripts/Makefile.modpost:145:
<...>/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [<...>/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2
The above edit worked for both your upstream branch and my downstream backport.
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 24 lines · Joe Lawrence
Should the .cmd file copy come from the reference SRC and not original ORIG directory?
cmd_file="$SRC/$(dirname "$rel_file")/.$(basename "$rel_file").cmd"because I don't see any .cmd files in klp-tmp/orig/
FWIW, I only noticed this after backporting the series to centos-stream-10. There, I got this build error:
Building original kernel
Copying original object files
Fixing patches
Building patched kernel
Copying patched object files
Diffing objects
vmlinux.o: changed function:cmdline_proc_showBuilding patch module: livepatch-test.ko <...>/klp-tmp/kmod/.vmlinux.o.cmd: No such file or directory make[2]: *** [scripts/Makefile.modpost:145:
<...>/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [<...>/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2
Quoted 2 lines
The above edit worked for both your upstream branch and my downstream backport.
Hm, I broke this in one of my refactorings before posting.
Is this with CONFIG_MODVERSIONS?
If you get a chance to test, here's a fix (currently untested):
scripts/livepatch/klp-build +19 -6
@@ -517,16 +517,29 @@ find_objects() { # Copy all objects (.o archives) to $ORIG_DIR copy_orig_objects() { + local files=() rm -rf "$ORIG_DIR" mkdir -p "$ORIG_DIR" - ( - cd "$OBJ" - find_objects \ - | sed 's/\.ko$/.o/' \ - | xargs cp --parents --target-directory="$ORIG_DIR" - ) + find_objects | mapfile -t files + + xtrace_save "copying orig objects" + for _file in "${files[@]}"; do + local rel_file="${_file/.ko/.o}" + local file="$OBJ/$rel_file" + local file_dir="$(dirname "$file")" + local orig_file="$ORIG_DIR/$rel_file" + local orig_dir="$(dirname "$orig_file")" + local cmd_file="$file_dir/.$(basename "$file").cmd" + + [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" + + mkdir -p "$orig_dir" + cp -f "$file" "$orig_dir" + [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$orig_dir" + done + xtrace_restore mv -f "$TMP_DIR/build.log" "$ORIG_DIR" touch "$TIMESTAMP"
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 25 lines · Josh Poimboeuf
Quoted 24 lines · Joe Lawrence
Should the .cmd file copy come from the reference SRC and not original ORIG directory?
cmd_file="$SRC/$(dirname "$rel_file")/.$(basename "$rel_file").cmd"because I don't see any .cmd files in klp-tmp/orig/
FWIW, I only noticed this after backporting the series to centos-stream-10. There, I got this build error:
Building original kernel
Copying original object files
Fixing patches
Building patched kernel
Copying patched object files
Diffing objects
vmlinux.o: changed function:cmdline_proc_showBuilding patch module: livepatch-test.ko <...>/klp-tmp/kmod/.vmlinux.o.cmd: No such file or directory make[2]: *** [scripts/Makefile.modpost:145:
<...>/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [<...>/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2
Quoted 8 lines
Quoted 2 lines
The above edit worked for both your upstream branch and my downstream backport.
Hm, I broke this in one of my refactorings before posting.
Is this with
CONFIG_MODVERSIONS?If you get a chance to test, here's a fix (currently untested):
It was indeed CONFIG_MODVERSIONS. I verified the fix works.
All the latest fixes are in my klp-build branch:
git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git klp-build
I hope to post v3 next week and then start looking at merging patches -- if not all of them, then at least the first ~40 dependency patches which are mostly standalone improvements.
Signature · 1 line
Josh
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 37 lines · Josh Poimboeuf
Quoted 25 lines · Josh Poimboeuf
Quoted 24 lines · Joe Lawrence
Should the .cmd file copy come from the reference SRC and not original ORIG directory?
cmd_file="$SRC/$(dirname "$rel_file")/.$(basename "$rel_file").cmd"because I don't see any .cmd files in klp-tmp/orig/
FWIW, I only noticed this after backporting the series to centos-stream-10. There, I got this build error:
Building original kernel
Copying original object files
Fixing patches
Building patched kernel
Copying patched object files
Diffing objects
vmlinux.o: changed function:cmdline_proc_showBuilding patch module: livepatch-test.ko <...>/klp-tmp/kmod/.vmlinux.o.cmd: No such file or directory make[2]: *** [scripts/Makefile.modpost:145:
<...>/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [<...>/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2Quoted 8 lines
Quoted 2 lines
The above edit worked for both your upstream branch and my downstream backport.
Hm, I broke this in one of my refactorings before posting.
Is this with
CONFIG_MODVERSIONS?If you get a chance to test, here's a fix (currently untested):
It was indeed
CONFIG_MODVERSIONS. I verified the fix works.
Quoted 8 lines
All the latest fixes are in my klp-build branch:
git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.gitklp-buildI hope to post v3 next week and then start looking at merging patches -- if not all of them, then at least the first ~40 dependency patches which are mostly standalone improvements.
Ack, I tested this update with CONFIG_MODVERSIONS for both vmlinux and module cases with success :)
Signature · 1 line
Joe
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh PoimboeufQuoted 23 lines · Josh Poimboeuf
scripts/livepatch/klp-build new file +23
-0+copy_patched_objects() { + local found + local files=() + local opts=() + + rm -rf "$PATCHED_DIR" + mkdir -p "$PATCHED_DIR" + + # Note this doesn't work with some configs, thus the 'cmp' below. + opts=("-newer") + opts+=("$TIMESTAMP") + + find_objects "${opts[@]}" | mapfile -t files + + xtrace_save "processing all objects" + for _file in "${files[@]}"; do + local rel_file="${_file/.ko/.o}" + local file="$OBJ/$rel_file" + local orig_file="$ORIG_DIR/$rel_file" + local patched_file="$PATCHED_DIR/$rel_file" + + [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" +
scripts/livepatch/klp-build line 554
Quoted 10 lines
scripts/livepatch/klp-build new file +10
-0+ cmp -s "$orig_file" "$file" && continue + + mkdir -p "$(dirname "$patched_file")" + cp -f "$file" "$patched_file" + found=1 + done + xtrace_restore + + [[ -n "$found" ]] || die "no changes detected" +
Minor nit here, I gave it a patch for files that didn't compile and because because files() was presumably empty:
./scripts/livepatch/klp-build: line 564: found: unbound variable
since found was only declared local, but never set inside the loop.
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 38 lines · Joe Lawrence
Quoted 23 lines · Josh Poimboeuf
(quoted diff) +23
-0+copy_patched_objects() { + local found + local files=() + local opts=() + + rm -rf "$PATCHED_DIR" + mkdir -p "$PATCHED_DIR" + + # Note this doesn't work with some configs, thus the 'cmp' below. + opts=("-newer") + opts+=("$TIMESTAMP") + + find_objects "${opts[@]}" | mapfile -t files + + xtrace_save "processing all objects" + for _file in "${files[@]}"; do + local rel_file="${_file/.ko/.o}" + local file="$OBJ/$rel_file" + local orig_file="$ORIG_DIR/$rel_file" + local patched_file="$PATCHED_DIR/$rel_file" + + [[ ! -f "$file" ]] && die "missing $(basename "$file") for $_file" +Review
Quoted 10 lines
(quoted diff) +10
-0+ cmp -s "$orig_file" "$file" && continue + + mkdir -p "$(dirname "$patched_file")" + cp -f "$file" "$patched_file" + found=1 + done + xtrace_restore + + [[ -n "$found" ]] || die "no changes detected" +Minor nit here, I gave it a patch for files that didn't compile and because because
files()was presumably empty:
Quoted 3 lines
./scripts/livepatch/klp-build: line 564: found: unbound variablesince found was only declared local, but never set inside the loop.
Thanks, I'm adding the following:
scripts/livepatch/klp-build +2 -2
@@ -563,7 +563,7 @@ copy_orig_objects() { copy_patched_objects() { local files=() local opts=() - local found + local found=0 rm -rf "$PATCHED_DIR" mkdir -p "$PATCHED_DIR"
@@ -592,7 +592,7 @@ copy_patched_objects() { done xtrace_restore - [[ -n "$found" ]] || die "no changes detected" + (( found == 0 )) && die "no changes detected" mv -f "$TMP_DIR/build.log" "$PATCHED_DIR" }
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh PoimboeufQuoted 55 lines · Josh Poimboeuf
scripts/livepatch/klp-build new file +55
-0+# Build and post-process livepatch module in $KMOD_DIR +build_patch_module() { + local makefile="$KMOD_DIR/Kbuild" + local log="$KMOD_DIR/build.log" + local cflags=() + local files=() + local cmd=() + + rm -rf "$KMOD_DIR" + mkdir -p "$KMOD_DIR" + + cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR" + + echo "obj-m := $NAME.o" > "$makefile" + echo -n "$NAME-y := init.o" >> "$makefile" + + find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files + [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + + for file in "${files[@]}"; do + local rel_file="${file#"$DIFF_DIR"/}" + local kmod_file="$KMOD_DIR/$rel_file" + local cmd_file + + mkdir -p "$(dirname "$kmod_file")" + cp -f "$file" "$kmod_file" + + # Tell kbuild this is a prebuilt object + cp -f "$file" "${kmod_file}_shipped" + + echo -n " $rel_file" >> "$makefile" + + cmd_file="$ORIG_DIR/$(dirname "$rel_file")/.$(basename "$rel_file").cmd" + [[ -e "$cmd_file" ]] && cp -f "$cmd_file" "$(dirname "$kmod_file")" + done + + echo >> "$makefile" + + cflags=("-ffunction-sections") + cflags+=("-fdata-sections") + [[ $REPLACE -eq 0 ]] && cflags+=("-DKLP_NO_REPLACE") + + cmd=("make") + cmd+=("$VERBOSE") + cmd+=("-j$CPUS") + cmd+=("--directory=.") + cmd+=("M=$KMOD_DIR") + cmd+=("KCFLAGS=${cflags[*]}") + + # Build a "normal" kernel module with init.c and the diffed objects + ( + cd "$SRC" + "${cmd[@]}" \ + > >(tee -a "$log") \ + 2> >(tee -a "$log" >&2)
scripts/livepatch/klp-build line 660
Quoted 10 lines
scripts/livepatch/klp-build new file +10
-0+ ) + + # Save off the intermediate binary for debugging + cp -f "$KMOD_DIR/$NAME.ko" "$KMOD_DIR/$NAME.ko.orig" + + # Fix (and work around) linker wreckage for klp syms / relocs + "$SRC/tools/objtool/objtool" klp post-link "$KMOD_DIR/$NAME.ko" || die "objtool klp post-link failed" + + cp -f "$KMOD_DIR/$NAME.ko" "$OUTFILE" +}
Hi Josh,
Another small bug feature? report: module symbol namespaces.
If you touch sound/soc/sof/intel/, klp-build will error out with:
Building patch module: livepatch-unCVE-2024-58012.ko
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbol hda_dai_config from namespace SND_SOC_SOF_INTEL_HDA_COMMON, but does not import it.
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbol hdac_bus_eml_sdw_map_stream_ch from namespace SND_SOC_SOF_HDA_MLINK, but does not import it. make[2]: *** [scripts/Makefile.modpost:145: /home/jolawren/src/centos-stream-10/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [/home/jolawren/src/centos-stream-10/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2
since the diff objects do not necessarily carry forward the namespace import.
There's several options to how to handle it (cross-reference with Modules.symvers, copy out the .modinfo sections, include the section in the diff .o, etc.) ... my late afternoon hack just snarfed it from the original objects with a modinfo hack. Anyway, you get the idea.
-- Joe
-->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8-- -->8--
@@ -687,7 +700,9 @@ build_patch_module() {
cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR"
echo "obj-m := $NAME.o" > "$makefile"
- echo -n "$NAME-y := init.o" >> "$makefile"
+
+ echo "#include <linux/module.h>" >> "$KMOD_DIR/namespaces.c"
+ echo -n "$NAME-y := init.o namespaces.o" >> "$makefile"
find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files [[ ${#files[@]} -eq 0 ]] && die "no changes detected"
@@ -697,6 +712,13 @@ build_patch_module() {
local kmod_file="$KMOD_DIR/$rel_file" local cmd_file
+ # Symbol namespace hack
+ echo ln -s -f "$file" ns-temp.ko
+ ln -s -f "$ORIG_DIR/$rel_file" ns-temp.ko
+ for ns in $(modinfo ns-temp.ko -F import_ns); do
+ echo "MODULE_IMPORT_NS(\"$ns\");" >> "$KMOD_DIR/namespaces.c"
+ done
+
mkdir -p "$(dirname "$kmod_file")" cp -f "$file" "$kmod_file"
--
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 11 lines · Joe Lawrence
If you touch
sound/soc/sof/intel/, klp-build will error out with:Building patch module: livepatch-unCVE-2024-58012.ko
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbolhda_dai_configfrom namespaceSND_SOC_SOF_INTEL_HDA_COMMON, but does not import it.
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbolhdac_bus_eml_sdw_map_stream_chfrom namespaceSND_SOC_SOF_HDA_MLINK, but does not import it. make[2]: *** [scripts/Makefile.modpost:145:/home/jolawren/src/centos-stream-10/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [/home/jolawren/src/centos-stream-10/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2since the diff objects do not necessarily carry forward the namespace import.
Nice, thanks for finding that. I completely forgot about export namespaces.
Can you send me the patch for testing? Is this the default centos10 config?
Signature · 1 line
Josh
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 10 lines · Josh Poimboeuf
Quoted 9 lines · Joe Lawrence
If you touch
sound/soc/sof/intel/, klp-build will error out with:Building patch module: livepatch-unCVE-2024-58012.ko
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbolhda_dai_configfrom namespaceSND_SOC_SOF_INTEL_HDA_COMMON, but does not import it.
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbolhdac_bus_eml_sdw_map_stream_chfrom namespaceSND_SOC_SOF_HDA_MLINK, but does not import it. make[2]: *** [scripts/Makefile.modpost:145:/home/jolawren/src/centos-stream-10/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [/home/jolawren/src/centos-stream-10/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2
Quoted 9 lines
Quoted 2 lines
since the diff objects do not necessarily carry forward the namespace import.
Nice, thanks for finding that. I completely forgot about export namespaces.
Can you send me the patch for testing? Is this the default centos10 config?
Yeah, cs-10 sets CONFIG_NAMESPACES=y.
The hack I posted earlier abused modinfo to get the namespaces. You could just dump the import_ns= strings in the .modinfo section with readelf like (lightly tested):
scripts/livepatch/klp-build +11 -1
@@ -687,7 +687,9 @@ build_patch_module() { cp -f "$SRC/scripts/livepatch/init.c" "$KMOD_DIR" echo "obj-m := $NAME.o" > "$makefile" - echo -n "$NAME-y := init.o" >> "$makefile" + echo -n "$NAME-y := init.o namespaces.o" >> "$makefile" + + echo "#include <linux/module.h>" >> "$KMOD_DIR/namespaces.c" find "$DIFF_DIR" -type f -name "*.o" | mapfile -t files [[ ${#files[@]} -eq 0 ]] && die "no changes detected"
@@ -695,8 +697,16 @@ build_patch_module() { for file in "${files[@]}"; do local rel_file="${file#"$DIFF_DIR"/}" local kmod_file="$KMOD_DIR/$rel_file" + local namespaces=() local cmd_file + # Copy symbol namespace + readelf -p .modinfo "$ORIG_DIR/$rel_file" | \ + gawk -F= '/\<import_ns=/ {print $2}' | mapfile -t namespaces + for ns in "${namespaces[@]}"; do + echo "MODULE_IMPORT_NS(\"$ns\");" >> "$KMOD_DIR/namespaces.c" + done + mkdir -p "$(dirname "$kmod_file")" cp -f "$file" "$kmod_file"
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
scripts/livepatch/klp-build build_patch_module() { · line 705
Quoted 3 lines · Joe Lawrence
scripts/livepatch/klp-build +3
-0+ # Copy symbol namespace + readelf -p .modinfo "$ORIG_DIR/$rel_file" | \ + gawk -F= '/\<import_ns=/ {print $2}' | mapfile -t namespaces
Errr, that is $PATCHED_DIR/$rel_file if we want to pick up the updated list of namespaces in case the .patch had amended it.
--
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Joe LawrenceQuoted 21 lines · Joe Lawrence
Quoted 10 lines · Josh Poimboeuf
Quoted 9 lines · Joe Lawrence
If you touch
sound/soc/sof/intel/, klp-build will error out with:Building patch module: livepatch-unCVE-2024-58012.ko
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbolhda_dai_configfrom namespaceSND_SOC_SOF_INTEL_HDA_COMMON, but does not import it.
ERROR: modpost: module livepatch-unCVE-2024-58012 uses symbolhdac_bus_eml_sdw_map_stream_chfrom namespaceSND_SOC_SOF_HDA_MLINK, but does not import it. make[2]: *** [scripts/Makefile.modpost:145:/home/jolawren/src/centos-stream-10/klp-tmp/kmod/Module.symvers] Error 1
make[1]: *** [/home/jolawren/src/centos-stream-10/Makefile:1936: modpost] Error 2
make: *** [Makefile:236: __sub-make] Error 2Quoted 9 lines
Quoted 2 lines
since the diff objects do not necessarily carry forward the namespace import.
Nice, thanks for finding that. I completely forgot about export namespaces.
Can you send me the patch for testing? Is this the default centos10 config?
Quoted 5 lines
Yeah, cs-10 sets
CONFIG_NAMESPACES=y.The hack I posted earlier abused modinfo to get the namespaces. You could just dump the import_ns= strings in the .modinfo section with readelf like (lightly tested):
Sorry, I wasn't clear, I meant the original .patch for recreating the issue. But that's ok, I think the below fix should work.
This is basically the same approach as yours, but in klp-diff. It copies from the patched object instead of the orig object in case the patch needs to add an IMPORT_NS().
I also experimented with reading the namespaces from Module.symvers, and then adding them on demand for exported symbols in clone_symbol(). But this is simpler.
tools/objtool/klp-diff.c +48 -0
@@ -1490,6 +1490,51 @@ static int create_klp_sections(struct elfs *e)return 0 ; } +/* +* Copy all .modinfo import_ns= tags to ensure all namespaced exported symbols +* can be accessed via normal relocs. +*/ +static int copy_import_ns (struct elfs *e) +{ +struct section *patched_sec, *out_sec =NULL ; +char *import_ns, *data_end; + + patched_sec =find_section_by_name (e->patched,".modinfo" ); +if (!patched_sec) +return 0 ; + + import_ns = patched_sec->data->d_buf; +if (!import_ns) +return 0 ; + +for (data_end = import_ns +sec_size (patched_sec); + import_ns < data_end; + import_ns +=strlen (import_ns) +1 ) { + + import_ns =memmem (import_ns, data_end - import_ns,"import_ns=" ,10 ); +if (!import_ns) +return 0 ; + +if (!out_sec) { + out_sec =find_section_by_name (e->out,".modinfo" ); +if (!out_sec) { + out_sec =elf_create_section (e->out,".modinfo" ,0 , + patched_sec->sh.sh_entsize, + patched_sec->sh.sh_type, + patched_sec->sh.sh_addralign, + patched_sec->sh.sh_flags); +if (!out_sec) +return -1 ; + } + } + +if (!elf_add_data (e->out, out_sec, import_ns,strlen (import_ns) +1 )) +return -1 ; + } + +return 0 ; +} +int cmd_klp_diff (int argc,const char **argv) {struct elfs e = {0 };
@@ -1539,6 +1584,9 @@ int cmd_klp_diff(int argc, const char **argv)if (create_klp_sections (&e))return -1 ; +if (copy_import_ns (&e)) +return -1 ; +if (elf_write (e.out))return -1 ;
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh PoimboeufQuoted 6 lines · Josh Poimboeuf
(quoted diff) +6
-0+revert_patch() { + local patch="$1" + shift + local extra_args=("$@") + local tmp=() +
Review
Quoted 10 lines
(quoted diff) +10
-0+ ( cd "$SRC" && git apply --reverse "${extra_args[@]}" "$patch" ) + git_refresh "$patch" + + for p in "${APPLIED_PATCHES[@]}"; do + [[ "$p" == "$patch" ]] && continue + tmp+=("$p") + done + + APPLIED_PATCHES=("${tmp[@]}") +}
You may consider a slight adjustment to revert_patch() to handle git format-patch generated .patches? The reversal trips up on the git version trailer:
warning: recount: unexpected line: 2.47.1
scripts/livepatch/klp-build +2 -1
@@ -345,7 +345,8 @@ revert_patch() { local extra_args=("$@") local tmp=() - ( cd "$SRC" && git apply --reverse "${extra_args[@]}" "$patch" ) + ( cd "$SRC" && \ + git apply --reverse "${extra_args[@]}" <(sed -n '/^-- /q;p' "$patch") ) git_refresh "$patch" for p in "${APPLIED_PATCHES[@]}"; do
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 22 lines · Joe Lawrence
Quoted 6 lines · Josh Poimboeuf
(quoted diff) +6
-0+revert_patch() { + local patch="$1" + shift + local extra_args=("$@") + local tmp=() +Review
Quoted 10 lines
(quoted diff) +10
-0+ ( cd "$SRC" && git apply --reverse "${extra_args[@]}" "$patch" ) + git_refresh "$patch" + + for p in "${APPLIED_PATCHES[@]}"; do + [[ "$p" == "$patch" ]] && continue + tmp+=("$p") + done + + APPLIED_PATCHES=("${tmp[@]}") +}You may consider a slight adjustment to
revert_patch()to handle git format-patch generated .patches? The reversal trips up on the git version trailer:
Quoted 1 line
warning: recount: unexpected line: 2.47.1
Thanks. Looks like the normal apply with --recount also trips it up. I have the below:
scripts/livepatch/klp-build +14 -2
@@ -337,7 +337,14 @@ apply_patch() { [[ ! -f "$patch" ]] && die "$patch doesn't exist" - ( cd "$SRC" && git apply "${extra_args[@]}" "$patch" ) + ( + cd "$SRC" + + # The sed removes the version signature from 'git format-patch', + # otherwise 'git apply --recount' warns. + sed -n '/^-- /q;p' "$patch" | + git apply "${extra_args[@]}" + ) APPLIED_PATCHES+=("$patch") }
@@ -348,7 +355,12 @@ revert_patch() { local extra_args=("$@") local tmp=() - ( cd "$SRC" && git apply --reverse "${extra_args[@]}" "$patch" ) + ( + cd "$SRC" + + sed -n '/^-- /q;p' "$patch" | + git apply --reverse "${extra_args[@]}" + ) git_refresh "$patch" for p in "${APPLIED_PATCHES[@]}"; do
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh PoimboeufQuoted 22 lines · Josh Poimboeuf
Add a klp-build script which automates the generation of a livepatch module from a source .patch file by performing the following steps:
- Builds an original kernel with -function-sections and -fdata-sections, plus objtool function checksumming.
- Applies the .patch file and rebuilds the kernel using the same options.
- Runs 'objtool klp diff' to detect changed functions and generate intermediate binary diff objects.
- Builds a kernel module which links the diff objects with some livepatch module init code (
scripts/livepatch/init.c).- Finalizes the livepatch module (aka work around linker wreckage) using 'objtool klp post-link'.
Signed-off-by Josh Poimboeuf <[email protected]>--- scripts/livepatch/klp-build | 697 ++++++++++++++++++++++++++++++++++++...
scripts/livepatch/klp-build line 281
Quoted 5 lines
scripts/livepatch/klp-build new file +5
-0+get_patch_files() { + local patch="$1" + + grep0 -E '^(--- |\+\+\+ )' "$patch" \ + | gawk '{print $2}' \
If we split the rest of this line on the tab character and print the first part of $2:
gawk '{ split($2, a, "\t"); print a[1] }'
then it can additionally handle patches generated by diff -Nupr with a timepstamp ("--- <filepath>\t<timestamp>").
Quoted 5 lines
scripts/livepatch/klp-build new file +5
-0+# Refresh the patch hunk headers, specifically the line numbers and counts. +refresh_patch() { + local patch="$1" + local tmpdir="$PATCH_TMP_DIR" + local files=()
scripts/livepatch/klp-build line 408
Quoted 10 lines
scripts/livepatch/klp-build new file +10
-0+ + rm -rf "$tmpdir" + mkdir -p "$tmpdir/a" + mkdir -p "$tmpdir/b" + + # Find all source files affected by the patch + grep0 -E '^(--- |\+\+\+ )[^ /]+' "$patch" | + sed -E 's/(--- |\+\+\+ )[^ /]+\///' | + sort | uniq | mapfile -t files +
Should just call `get_patch_files() here?
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 9 lines · Joe Lawrence
scripts/livepatch/klp-build line 281
Quoted 5 lines
scripts/livepatch/klp-build new file +5
-0+get_patch_files() { + local patch="$1" + + grep0 -E '^(--- |\+\+\+ )' "$patch" \ + | gawk '{print $2}' \If we split the rest of this line on the tab character and print the first part of $2:
Quoted 4 lines
gawk '{
split($2, a, "\t"); print a[1] }'then it can additionally handle patches generated by
diff -Nuprwith a timepstamp ("--- <filepath>\t<timestamp>").
Hm? The default gawk behavior is to treat both tabs and groups of spaces as field separators:
https://www.gnu.org/software/gawk/manual/html_node/Default-Field-Splitting.html
And it works for me:
$ diff -Nupr /tmp/meminfo.c fs/proc/meminfo.c > /tmp/a.patch
$ grep -E '^(--- |\+\+\+ )' /tmp/a.patch | gawk '{print $2}'
/tmp/meminfo.c
fs/proc/meminfo.cOr did I miss something?
Quoted 9 lines
Quoted 9 lines
scripts/livepatch/klp-build new file +9
-0+# Refresh the patch hunk headers, specifically the line numbers and counts. +refresh_patch() { + local patch="$1" + local tmpdir="$PATCH_TMP_DIR" + local files=() + + rm -rf "$tmpdir" + mkdir -p "$tmpdir/a" + mkdir -p "$tmpdir/b"
Quoted 8 lines
scripts/livepatch/klp-build line 408
Quoted 6 lines
scripts/livepatch/klp-build new file +6
-0+ + # Find all source files affected by the patch + grep0 -E '^(--- |\+\+\+ )[^ /]+' "$patch" | + sed -E 's/(--- |\+\+\+ )[^ /]+\///' | + sort | uniq | mapfile -t files +Should just call `get_patch_files() here?
Indeed.
Signature · 1 line
Josh
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 19 lines · Josh Poimboeuf
Quoted 8 lines · Joe Lawrence
scripts/livepatch/klp-build line 281
Quoted 5 lines
scripts/livepatch/klp-build new file +5
-0+get_patch_files() { + local patch="$1" + + grep0 -E '^(--- |\+\+\+ )' "$patch" \ + | gawk '{print $2}' \If we split the rest of this line on the tab character and print the first part of $2:
Quoted 4 lines
gawk '{
split($2, a, "\t"); print a[1] }'then it can additionally handle patches generated by
diff -Nuprwith a timepstamp ("--- <filepath>\t<timestamp>").Hm? The default gawk behavior is to treat both tabs and groups of spaces as field separators:
https://www.gnu.org/software/gawk/manual/html_node/Default-Field-
Splitting.html
Quoted 8 lines
And it works for me:
$ diff -Nupr /tmp/meminfo.c fs/proc/meminfo.c > /tmp/a.patch $ grep -E '^(--- |\+\+\+ )' /tmp/a.patch | gawk '{print $2}' /tmp/meminfo.c fs/proc/meminfo.cOr did I miss something?
Ah hah, I fixed up the code in refresh_patch() with that double gawk, and then noticed it could have just called get_patch_files() instead.
So yeah, you're right, the simple gawk '{print $2}' handles both cases already :)
Signature · 1 line
Joe
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh Poimboeufscripts/livepatch/klp-build line 368
Quoted 13 lines · Josh Poimboeuf
scripts/livepatch/klp-build new file +13
-0+revert_patches() { + local extra_args=("$@") + local patches=("${APPLIED_PATCHES[@]}") + + for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do + revert_patch "${patches[$i]}" "${extra_args[@]}" + done + + APPLIED_PATCHES=() + + # Make sure git actually sees the patches have been reverted. + [[ -d "$SRC/.git" ]] && (cd "$SRC" && git update-index -q --refresh) +}
< warning: testing entropy field fully engaged :D >
Minor usability nit: I had run klp-build --help while inside a VM that didn't seem to have r/w access to .git/. Since the cleanup code is called unconditionally, it gave me a strange error when running this git update-index when I never supplied any patches to operate on. I just wanted to see the usage.
Could this git update-index be made conditional?
if (( ${#APPLIED_PATCHES[@]} > 0 )); then
([[ -d "$SRC/.git" ]] && cd "$SRC" && git update-index -q --refresh)
APPLIED_PATCHES=()
fiAnother way to find yourself in this function is to move .git/ out of the way. In that case, since it's the last line of revert_patches(), I think the failure of [[ -d "$SRC/.git" ]] causes the script to immediately exit:
- for foo.patch, at the validate_patches() -> revert_patches() call
- for --help, at the cleanup() -> revert_patches() call
So if you don't like the conditional change above, should revert_patches() end with true to eat the [[ -d "$SRC/.git" ]] status?
Or does that interfere with other calls to that function throughout the
script?
FWIW, with either adjustment, the script seems happy to operate on a plain ol' kernel source tree without git.
Signature · 1 line
Joe
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 38 lines · Joe Lawrence
Review
Quoted 13 lines · Josh Poimboeuf
(quoted diff) +13
-0+revert_patches() { + local extra_args=("$@") + local patches=("${APPLIED_PATCHES[@]}") + + for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do + revert_patch "${patches[$i]}" "${extra_args[@]}" + done + + APPLIED_PATCHES=() + + # Make sure git actually sees the patches have been reverted. + [[ -d "$SRC/.git" ]] && (cd "$SRC" && git update-index -q --refresh) +}< warning: testing entropy field fully engaged :D >
Minor usability nit: I had run
klp-build --helpwhile inside a VM that didn't seem to have r/w access to .git/. Since the cleanup code is called unconditionally, it gave me a strange error when running thisgit update-indexwhen I never supplied any patches to operate on. I just wanted to see the usage.Could this git update-index be made conditional?
if (( ${#APPLIED_PATCHES[@]} > 0 )); then ([[ -d "$SRC/.git" ]] && cd "$SRC" && git update-index -q --refresh) APPLIED_PATCHES=() fiAnother way to find yourself in this function is to move .git/ out of the way. In that case, since it's the last line of
revert_patches(), I think the failure of [[ -d "$SRC/.git" ]] causes the script to immediately exit:- for foo.patch, at the
validate_patches()->revert_patches()call
- for --help, at thecleanup()->revert_patches()call
Quoted 7 lines
So if you don't like the conditional change above, should
revert_patches()end withtrueto eat the [[ -d "$SRC/.git" ]] status?
Or does that interfere with other calls to that function throughout the
script?FWIW, with either adjustment, the script seems happy to operate on a plain ol' kernel source tree without git.
Hm, revert_patch() already has a call to git_refresh(), so there doesn't appear to be any point to that extra refresh in revert_patches().
Does this fix?
scripts/livepatch/klp-build +0 -3
@@ -388,9 +388,6 @@ revert_patches() { done APPLIED_PATCHES=() - - # Make sure git actually sees the patches have been reverted. - [[ -d "$SRC/.git" ]] && (cd "$SRC" && git update-index -q --refresh) } validate_patches() {
Joe Lawrence
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 12 lines · Josh Poimboeuf
Hm,
revert_patch()already has a call togit_refresh(), so there doesn't appear to be any point to that extra refresh inrevert_patches().Does this fix?
scripts/livepatch/klp-build +0
-0
scripts/livepatch/klp-build revert_patches() { · line 393
Quoted 9 lines
scripts/livepatch/klp-build +0
-3@@ -388,9 +388,6 @@ revert_patches() { done APPLIED_PATCHES=() - - # Make sure git actually sees the patches have been reverted. - [[ -d "$SRC/.git" ]] && (cd "$SRC" && git update-index -q --refresh) } validate_patches() {
Ah yes, even better.
Signature · 1 line
Joe
Re: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
In reply to Josh Poimboeufscripts/livepatch/klp-build line 291
Quoted 8 lines · Josh Poimboeuf
scripts/livepatch/klp-build new file +7
-0+ +# Make sure git re-stats the changed files +git_refresh() { + local patch="$1" + local files=() + + [[ ! -d "$SRC/.git" ]] && return
As a user of git worktrees, my $SRC/.git is a file containing a key:
value pair "gitdir: <path>", causing this script to fail on a [[ ! -d "$SRC/.git" ]] check. Can this be handled, perhaps with a check if .git is a file?
It seems like the check is just to confirm the $SRC directory is still a git tree, in which case maybe adding a -f check would fix this:
[[ ! -d "$SRC/.git" ]] && [[ ! -f "$SRC/.git" ]] && return
Or if the actual git directory is needed for something, maybe it can be located ahead of time:
GITDIR="$SRC/.git"
[[ -f $GITDIR ]] && GITDIR=$(sed -n
's/^gitdir[[:space:]]*:[[:space:]]*//p' $GITDIR)
Thanks,
Dylan
Josh Poimboeuf
maintainerRe: [PATCH v2 59/62] livepatch/klp-build: Introduce klp-build script for generating livepatch modules
Quoted 18 lines · Dylan Hatch
scripts/livepatch/klp-build line 291
Quoted 8 lines · Josh Poimboeuf
scripts/livepatch/klp-build new file +7
-0+ +# Make sure git re-stats the changed files +git_refresh() { + local patch="$1" + local files=() + + [[ ! -d "$SRC/.git" ]] && returnAs a user of git worktrees, my $SRC/.git is a file containing a key:
value pair "gitdir: <path>", causing this script to fail on a [[ ! -d "$SRC/.git" ]] check. Can this be handled, perhaps with a check if .git is a file?It seems like the check is just to confirm the $SRC directory is still a git tree, in which case maybe adding a -f check would fix this:
Quoted 8 lines
[[ ! -d "$SRC/.git" ]] && [[ ! -f "$SRC/.git" ]] && return
Or if the actual git directory is needed for something, maybe it can be located ahead of time:
GITDIR="$SRC/.git"
[[ -f $GITDIR ]] && GITDIR=$(sed -n
's/^gitdir[[:space:]]*:[[:space:]]*//p' $GITDIR)
I believe the subsequent "git update-index" operation should work on git worktrees as well, so I changed that to use '-e':
[[ ! -e "$SRC/.git" ]] && return
Signature · 1 line
Josh
Josh Poimboeuf
maintainerPATCH v2 60/62 livepatch/klp-build: Add --debug option to show cloning decisions
In reply to Josh PoimboeufAdd a --debug option which gets passed to "objtool klp diff" to enable debug output related to cloning decisions.
--- scripts/livepatch/klp-build | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-)
scripts/livepatch/klp-build +13 -3
@@ -21,7 +21,7 @@ set -o nounset # This helps keep execution in pipes so pipefail+errexit can catch errors. shopt -s lastpipe -unset SKIP_CLEANUP XTRACE +unset DEBUG_CLONE SKIP_CLEANUP XTRACE REPLACE=1 SHORT_CIRCUIT=0 shopt -o xtrace | grep -q 'on' && XTRACE=1
@@ -120,6 +120,7 @@ Options: -v, --verbose Pass V=1 to kernel/module builds Advanced Options: + -D, --debug Show symbol/reloc cloning decisions -S, --short-circuit=STEP Start at build step (requires prior --keep-tmp) 1|orig Build original kernel (default) 2|patched Build patched kernel
@@ -140,8 +141,8 @@ process_args() { local long local args - short="ho:vS:T" - long="help,output:,no-replace,verbose,short-circuit:,keep-tmp" + short="ho:vDS:T" + long="help,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { echo; usage; exit
@@ -170,6 +171,11 @@ process_args() { VERBOSE="V=1" shift ;; + -D | --debug) + DEBUG_CLONE=1 + keep_tmp=1 + shift + ;; -S | --short-circuit) [[ ! -d "$TMP_DIR" ]] && die "--short-circuit requires preserved klp-tmp dir" keep_tmp=1
@@ -559,6 +565,7 @@ copy_patched_objects() { diff_objects() { local log="$KLP_DIFF_LOG" local files=() + local opts=() rm -rf "$DIFF_DIR" mkdir -p "$DIFF_DIR"
@@ -566,6 +573,8 @@ diff_objects() { find "$PATCHED_DIR" -type f -name "*.o" | mapfile -t files [[ ${#files[@]} -eq 0 ]] && die "no changes detected" + [[ -v DEBUG_CLONE ]] && opts=("--debug") + # Diff all changed objects for file in "${files[@]}"; do local rel_file="${file#"$PATCHED_DIR"/}"
@@ -579,6 +588,7 @@ diff_objects() { cmd=("$SRC/tools/objtool/objtool") cmd+=("klp") cmd+=("diff") + (( ${#opts[@]} > 0 )) && cmd+=("${opts[@]}") cmd+=("$orig_file") cmd+=("$patched_file") cmd+=("$out_file")
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 61/62 livepatch/klp-build: Add --show-first-changed option to show function divergence
In reply to Josh PoimboeufAdd a --show-first-changed option to identify where changed functions begin to diverge:
- Parse 'objtool klp diff' output to find changed functions.
- Run objtool again on each object with --debug-checksum=<funcs>.
- Diff the per-instruction checksum debug output to locate the first differing instruction.
This can be useful for quickly determining where and why a function changed.
--- scripts/livepatch/klp-build | 82 +++++++++++++++++++++++++++++++++++-- 1 file changed, 78 insertions(+), 4 deletions(-)
scripts/livepatch/klp-build +78 -4
@@ -21,7 +21,7 @@ set -o nounset # This helps keep execution in pipes so pipefail+errexit can catch errors. shopt -s lastpipe -unset DEBUG_CLONE SKIP_CLEANUP XTRACE +unset DEBUG_CLONE DIFF_CHECKSUM SKIP_CLEANUP XTRACE REPLACE=1 SHORT_CIRCUIT=0 shopt -o xtrace | grep -q 'on' && XTRACE=1
@@ -115,6 +115,7 @@ Usage: $SCRIPT [OPTIONS] PATCH_FILE(s) Generate a livepatch module. Options: + -f, --show-first-changed Show address of first changed instruction -o, --output <file.ko> Output file [default: livepatch-<patch-name>.ko] --no-replace Disable livepatch atomic replace -v, --verbose Pass V=1 to kernel/module builds
@@ -141,8 +142,8 @@ process_args() { local long local args - short="ho:vDS:T" - long="help,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" + short="hfo:vDS:T" + long="help,show-first-changed,output:,no-replace,verbose,debug,short-circuit:,keep-tmp" args=$(getopt --options "$short" --longoptions "$long" -- "$@") || { echo; usage; exit
@@ -155,6 +156,10 @@ process_args() { usage exit 0 ;; + -f | --show-first-changed) + DIFF_CHECKSUM=1 + shift + ;; -o | --output) [[ "$2" != *.ko ]] && die "output filename should end with .ko" OUTFILE="$2"
@@ -581,6 +586,7 @@ diff_objects() { local orig_file="$rel_file" local patched_file="$PATCHED_DIR/$rel_file" local out_file="$DIFF_DIR/$rel_file" + local filter=() local cmd=() mkdir -p "$(dirname "$out_file")"
@@ -593,16 +599,80 @@ diff_objects() { cmd+=("$patched_file") cmd+=("$out_file") + if [[ -v DIFF_CHECKSUM ]]; then + filter=("grep0") + filter+=("-Ev") + filter+=("DEBUG: .*checksum: ") + else + filter=("cat") + fi + ( cd "$ORIG_DIR" "${cmd[@]}" \ > >(tee -a "$log") \ - 2> >(tee -a "$log" >&2) \ + 2> >(tee -a "$log" | "${filter[@]}" >&2) \ || die "objtool klp diff failed" ) done } +# For each changed object, run objtool with --debug-checksum to get the +# per-instruction checksums, and then diff those to find the first changed +# instruction for each function. +diff_checksums() { + local orig_log="$ORIG_DIR/checksum.log" + local patched_log="$PATCHED_DIR/checksum.log" + local -A funcs + local cmd=() + local line + local file + local func + + gawk '/\.o: changed function: / { + sub(/:$/, "", $1) + print $1, $NF + }' "$KLP_DIFF_LOG" | mapfile -t lines + + for line in "${lines[@]}"; do + read -r file func <<< "$line" + if [[ ! -v funcs["$file"] ]]; then + funcs["$file"]="$func" + else + funcs["$file"]+=" $func" + fi + done + + cmd=("$SRC/tools/objtool/objtool") + cmd+=("--checksum") + cmd+=("--link") + cmd+=("--dry-run") + + for file in "${!funcs[@]}"; do + local opt="--debug-checksum=${funcs[$file]// /,}" + + ( + cd "$ORIG_DIR" + "${cmd[@]}" "$opt" "$file" &> "$orig_log" \ + || ( cat "$orig_log" >&2; die "objtool --debug-checksum failed" ) + + cd "$PATCHED_DIR" + "${cmd[@]}" "$opt" "$file" &> "$patched_log" \ + || ( cat "$patched_log" >&2; die "objtool --debug-checksum failed" ) + ) + + for func in ${funcs[$file]}; do + diff <( grep0 -E "^DEBUG: .*checksum: $func " "$orig_log" | sed "s|$ORIG_DIR/||") \ + <( grep0 -E "^DEBUG: .*checksum: $func " "$patched_log" | sed "s|$PATCHED_DIR/||") \ + | gawk '/^< DEBUG: / { + gsub(/:/, "") + printf "%s: %s: %s\n", $3, $5, $6 + exit + }' || true + done + done +} + # Build and post-process livepatch module in $KMOD_DIR build_patch_module() { local makefile="$KMOD_DIR/Kbuild"
@@ -697,6 +767,10 @@ fi if (( SHORT_CIRCUIT <= 3 )); then status "Diffing objects" diff_objects + if [[ -v DIFF_CHECKSUM ]]; then + status "Finding first changed instructions" + diff_checksums + fi fi if (( SHORT_CIRCUIT <= 4 )); then
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerPATCH v2 62/62 livepatch: Introduce source code helpers for livepatch modules
In reply to Josh PoimboeufAdd some helper macros which can be used by livepatch source .patch files to register callbacks or patch syscalls.
--- include/linux/livepatch_helpers.h | 68 +++++++++++++++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 include/linux/livepatch_helpers.h
include/linux/livepatch_helpers.h new file +68 -0
@@ -0,0 +1,68 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _LINUX_LIVEPATCH_HELPERS_H +#define _LINUX_LIVEPATCH_HELPERS_H + +/* +* Interfaces for use by livepatch patches +*/ + +#include <linux/syscalls.h> +#include <linux/livepatch.h> + +#ifdef MODULE +#define KLP_OBJNAME __KBUILD_MODNAME +#else +#define KLP_OBJNAME vmlinux +#endif + +/* Livepatch callback registration */ + +#define KLP_CALLBACK_PTRS ".discard.klp_callback_ptrs" + +#define KLP_PRE_PATCH_CALLBACK (func) \ + klp_pre_patch_t __used __section(KLP_CALLBACK_PTRS) \ + __PASTE(__KLP_PRE_PATCH_PREFIX, KLP_OBJNAME) = func + +#define KLP_POST_PATCH_CALLBACK (func) \ + klp_post_patch_t __used __section(KLP_CALLBACK_PTRS) \ + __PASTE(__KLP_POST_PATCH_PREFIX, KLP_OBJNAME) = func + +#define KLP_PRE_UNPATCH_CALLBACK (func) \ + klp_pre_unpatch_t __used __section(KLP_CALLBACK_PTRS) \ + __PASTE(__KLP_PRE_UNPATCH_PREFIX, KLP_OBJNAME) = func + +#define KLP_POST_UNPATCH_CALLBACK (func) \ + klp_post_unpatch_t __used __section(KLP_CALLBACK_PTRS) \ + __PASTE(__KLP_POST_UNPATCH_PREFIX, KLP_OBJNAME) = func + +/* Syscall patching */ + +#define KLP_SYSCALL_DEFINE1 (name, ...) KLP_SYSCALL_DEFINEx(1, _##name, __VA_ARGS__) +#define KLP_SYSCALL_DEFINE2 (name, ...) KLP_SYSCALL_DEFINEx(2, _##name, __VA_ARGS__) +#define KLP_SYSCALL_DEFINE3 (name, ...) KLP_SYSCALL_DEFINEx(3, _##name, __VA_ARGS__) +#define KLP_SYSCALL_DEFINE4 (name, ...) KLP_SYSCALL_DEFINEx(4, _##name, __VA_ARGS__) +#define KLP_SYSCALL_DEFINE5 (name, ...) KLP_SYSCALL_DEFINEx(5, _##name, __VA_ARGS__) +#define KLP_SYSCALL_DEFINE6 (name, ...) KLP_SYSCALL_DEFINEx(6, _##name, __VA_ARGS__) + +#define KLP_SYSCALL_DEFINEx (x, sname, ...) \ + __KLP_SYSCALL_DEFINEx(x, sname, __VA_ARGS__) + +#ifdef CONFIG_X86_64 +// TODO move this to arch/x86/include/asm/syscall_wrapper.h and share code +#define __KLP_SYSCALL_DEFINEx (x, name, ...) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)); \ + static inline long __klp_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__));\ + __X64_SYS_STUBx(x, name, __VA_ARGS__) \ + __IA32_SYS_STUBx(x, name, __VA_ARGS__) \ + static long __se_sys##name(__MAP(x,__SC_LONG,__VA_ARGS__)) \ + { \ + long ret = __klp_do_sys##name(__MAP(x,__SC_CAST,__VA_ARGS__));\ + __MAP(x,__SC_TEST,__VA_ARGS__); \ + __PROTECT(x, ret,__MAP(x,__SC_ARGS,__VA_ARGS__)); \ + return ret; \ + } \ + static inline long __klp_do_sys##name(__MAP(x,__SC_DECL,__VA_ARGS__)) + +#endif + +#endif /* _LINUX_LIVEPATCH_HELPERS_H */
Signature · 1 line
2.49.0
Josh Poimboeuf
maintainerQuoted 8 lines · Josh Poimboeuf
I've tested with a variety of patches on defconfig and Fedora-config kernels with both GCC and Clang.
These patches can also be found at:
git://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.gitklp-build-v2Please test!
I found a nasty bug while trying to patch copy_process(). The wrong version of __refcount_add.constprop.0() was being called due to a bad klp rela sympos value, causing refcount overflow/UAF warnings and hangs.
The problem was a mismatch between the sympos order of the vmlinux.o archive (which klp-diff uses) and the final vmlinux.
The linker script manually emits .text.unlikely before .text instead of emitting in them in section table order. So if a function name exists in both sections, the calculated sympos (based on vmlinux.o) is wrong.
In my test kernel with GCC 14, only 25 out of 136,931 functions (0.018%) have this problem. It was my lucky day.
The below hack fixes it by starting the sympos counting with .text.unlikely*. It's a bit fragile, but it works fine for now.
I'm thinking the final fix would involve adding a checksum field to kallsyms. Then sympos would no longer be needed. But that will need to come later.
tools/include/linux/string.h +14 -0
@@ -44,6 +44,20 @@ static inline bool strstarts(const char *str, const char *prefix)return strncmp (str, prefix,strlen (prefix)) ==0 ; } +/* +* Checks if a string ends with another. +*/ +static inline bool str_ends_with (const char *str,const char *substr) +{ +size_t len =strlen (str); +size_t sublen =strlen (substr); + +if (sublen > len) +return false; + +return !strcmp (str + len - sublen, substr); +} +extern char *__must_check skip_spaces (const char *);extern char *strim (char *);
tools/objtool/check.c +0 -14
@@ -187,20 +187,6 @@ static bool is_sibling_call(struct instruction *insn)return (is_static_jump (insn) &&insn_call_dest (insn)); } -/* -* Checks if a string ends with another. -*/ -static bool str_ends_with (const char *s,const char *sub) -{ -const int slen =strlen (s); -const int sublen =strlen (sub); - -if (sublen > slen) -return 0 ; - -return !memcmp (s + slen - sublen, sub, sublen); -} -/* * Checks if a function is a Rust "noreturn" one. */
tools/objtool/klp-diff.c +38 -7
57 lines not on this page · raw message