From c19c854b307424c745dd6de73eea8db099c79408 Mon Sep 17 00:00:00 2001 From: Wentong Tian Date: Thu, 22 Jan 2026 22:44:04 +0800 Subject: [PATCH 01/19] objtool: Use section/symbol type helpers Commit 25eac74b6bdb ("objtool: Add section/symbol type helpers") introduced several helper macros to improve code readability. Update the remaining open-coded checks in check.c, disas.c, elf.c, and klp-diff.c to use these new helpers. Signed-off-by: Wentong Tian Link: https://patch.msgid.link/20260122144404.40602-1-tianwentong2000@gmail.com Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 4 ++-- tools/objtool/disas.c | 6 +++--- tools/objtool/elf.c | 6 +++--- tools/objtool/klp-diff.c | 8 ++++---- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index a30379e4ff97..efd4b9ce0560 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4293,8 +4293,8 @@ static int validate_retpoline(struct objtool_file *file) list_for_each_entry(insn, &file->retpoline_call_list, call_node) { struct symbol *sym = insn->sym; - if (sym && (sym->type == STT_NOTYPE || - sym->type == STT_FUNC) && !sym->nocfi) { + if (sym && (is_notype_sym(sym) || + is_func_sym(sym)) && !sym->nocfi) { struct instruction *prev = prev_insn_same_sym(file, insn); diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c index 26f08d41f2b1..59090234af19 100644 --- a/tools/objtool/disas.c +++ b/tools/objtool/disas.c @@ -264,7 +264,7 @@ static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo) * If the relocation symbol is a section name (for example ".bss") * then we try to further resolve the name. */ - if (reloc->sym->type == STT_SECTION) { + if (is_sec_sym(reloc->sym)) { str = offstr(reloc->sym->sec, reloc->sym->offset + offset); DINFO_FPRINTF(dinfo, bfd_vma_fmt, addr, str); free(str); @@ -580,7 +580,7 @@ static size_t disas_insn_common(struct disas_context *dctx, */ dinfo->buffer = insn->sec->data->d_buf; dinfo->buffer_vma = 0; - dinfo->buffer_length = insn->sec->sh.sh_size; + dinfo->buffer_length = sec_size(insn->sec); return disasm(insn->offset, &dctx->info); } @@ -1231,7 +1231,7 @@ void disas_funcs(struct disas_context *dctx) for_each_sec(dctx->file->elf, sec) { - if (!(sec->sh.sh_flags & SHF_EXECINSTR)) + if (!is_text_sec(sec)) continue; sec_for_each_sym(sec, sym) { diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 2c02c7b49265..a634b227d627 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -614,7 +614,7 @@ static int read_symbols(struct elf *elf) if (elf_add_symbol(elf, sym)) return -1; - if (sym->type == STT_FILE) + if (is_file_sym(sym)) file = sym; else if (sym->bind == STB_LOCAL) sym->file = file; @@ -1335,7 +1335,7 @@ unsigned int elf_add_string(struct elf *elf, struct section *strtab, const char return -1; } - offset = ALIGN(strtab->sh.sh_size, strtab->sh.sh_addralign); + offset = ALIGN(sec_size(strtab), strtab->sh.sh_addralign); if (!elf_add_data(elf, strtab, str, strlen(str) + 1)) return -1; @@ -1377,7 +1377,7 @@ void *elf_add_data(struct elf *elf, struct section *sec, const void *data, size_ sec->data->d_size = size; sec->data->d_align = 1; - offset = ALIGN(sec->sh.sh_size, sec->sh.sh_addralign); + offset = ALIGN(sec_size(sec), sec->sh.sh_addralign); sec->sh.sh_size = offset + size; mark_sec_changed(elf, sec, true); diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 9f1f4011eb9c..885775f6072e 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -271,7 +271,7 @@ static bool is_uncorrelated_static_local(struct symbol *sym) */ static bool is_clang_tmp_label(struct symbol *sym) { - return sym->type == STT_NOTYPE && + return is_notype_sym(sym) && is_text_sec(sym->sec) && strstarts(sym->name, ".Ltmp") && isdigit(sym->name[5]); @@ -480,7 +480,7 @@ static unsigned long find_sympos(struct elf *elf, struct symbol *sym) if (sym->bind != STB_LOCAL) return 0; - if (vmlinux && sym->type == STT_FUNC) { + if (vmlinux && is_func_sym(sym)) { /* * HACK: Unfortunately, symbol ordering can differ between * vmlinux.o and vmlinux due to the linker script emitting @@ -1046,8 +1046,8 @@ static int clone_reloc_klp(struct elfs *e, struct reloc *patched_reloc, sec->name, offset, patched_sym->name, \ addend >= 0 ? "+" : "-", labs(addend), \ sym_type(patched_sym), \ - patched_sym->type == STT_SECTION ? "" : " ", \ - patched_sym->type == STT_SECTION ? "" : sym_bind(patched_sym), \ + is_sec_sym(patched_sym) ? "" : " ", \ + is_sec_sym(patched_sym) ? "" : sym_bind(patched_sym), \ is_undef_sym(patched_sym) ? " UNDEF" : "", \ export ? " EXPORTED" : "", \ klp ? " KLP" : "") From a1cbaff2ea23645f0a94fdfa721aef202aebdf86 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:25 -0800 Subject: [PATCH 02/19] objtool/klp: Remove redundant strcmp() in correlate_symbols() find_global_symbol_by_name() already compares names of the two symbols, so there is no need to compare them again. Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-2-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/klp-diff.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 885775f6072e..639bdd3ad5eb 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -454,7 +454,7 @@ static int correlate_symbols(struct elfs *e) sym2 = find_global_symbol_by_name(e->patched, sym1->name); - if (sym2 && !sym2->twin && !strcmp(sym1->name, sym2->name)) { + if (sym2 && !sym2->twin) { sym1->twin = sym2; sym2->twin = sym1; } From a3f28d2072452edb377eaad01375445dbace6771 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:26 -0800 Subject: [PATCH 03/19] objtool/klp: Remove trailing '_' in demangle_name() With CONFIG_LTO_CLANG_THIN, it is possible to have nested __UNIQUE_ID_, such as: __UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695 To remove both trailing numbers, also remove trailing '_'. Also add comments to demangle_name(). Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-3-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index a634b227d627..fe6e66dd5422 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -441,6 +441,19 @@ static int read_sections(struct elf *elf) return 0; } +/* + * Remove number suffix of a symbol. + * + * Specifically, remove trailing numbers for "__UNIQUE_ID_" symbols and + * symbols with '.'. + * + * With CONFIG_LTO_CLANG_THIN, it is possible to have nested __UNIQUE_ID_, + * such as + * + * __UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695 + * + * to remove both trailing numbers, also remove trailing '_'. + */ static const char *demangle_name(struct symbol *sym) { char *str; @@ -463,7 +476,7 @@ static const char *demangle_name(struct symbol *sym) for (int i = strlen(str) - 1; i >= 0; i--) { char c = str[i]; - if (!isdigit(c) && c != '.') { + if (!isdigit(c) && c != '.' && c != '_') { str[i + 1] = '\0'; break; } From 0b8fc6adc3d9bdf161fc8ad0a1de191dba293b39 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:27 -0800 Subject: [PATCH 04/19] objtool/klp: Use sym->demangled_name for symbol_name hash For klp-build with LTO, it is necessary to correlate demangled symbols, e.g., correlate foo.llvm. and foo.llvm.. However, these two symbols do not have the same str_hash(name). To be able to correlate the two symbols, calculate hash based on demanged_name, so that these two symbols have the same hash. No functional changes intended. Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-4-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 58 +++++++++++++++++++++++++++++++-------------- 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index fe6e66dd5422..b2d73c4c6cf1 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -26,11 +26,18 @@ #include #include +static ssize_t demangled_name_len(const char *name); + static inline u32 str_hash(const char *str) { return jhash(str, strlen(str), 0); } +static inline u32 str_hash_demangled(const char *str) +{ + return jhash(str, demangled_name_len(str), 0); +} + #define __elf_table(name) (elf->name##_hash) #define __elf_bits(name) (elf->name##_bits) @@ -294,7 +301,7 @@ static struct symbol *find_local_symbol_by_file_and_name(const struct elf *elf, { struct symbol *sym; - elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { + elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) { if (sym->bind == STB_LOCAL && sym->file == file && !strcmp(sym->name, name)) { return sym; @@ -308,7 +315,7 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam { struct symbol *sym; - elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(name)) { + elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash_demangled(name)) { if (!strcmp(sym->name, name) && !is_local_sym(sym)) return sym; } @@ -441,6 +448,28 @@ static int read_sections(struct elf *elf) return 0; } +/* + * Returns desired length of the demangled name. + * If name doesn't need demangling, return strlen(name). + */ +static ssize_t demangled_name_len(const char *name) +{ + ssize_t idx; + + if (!strstarts(name, "__UNIQUE_ID_") && !strchr(name, '.')) + return strlen(name); + + for (idx = strlen(name) - 1; idx >= 0; idx--) { + char c = name[idx]; + + if (!isdigit(c) && c != '.' && c != '_') + break; + } + if (idx <= 0) + return strlen(name); + return idx + 1; +} + /* * Remove number suffix of a symbol. * @@ -457,6 +486,7 @@ static int read_sections(struct elf *elf) static const char *demangle_name(struct symbol *sym) { char *str; + ssize_t len; if (!is_local_sym(sym)) return sym->name; @@ -464,24 +494,16 @@ static const char *demangle_name(struct symbol *sym) if (!is_func_sym(sym) && !is_object_sym(sym)) return sym->name; - if (!strstarts(sym->name, "__UNIQUE_ID_") && !strchr(sym->name, '.')) + len = demangled_name_len(sym->name); + if (len == strlen(sym->name)) return sym->name; - str = strdup(sym->name); + str = strndup(sym->name, len); if (!str) { ERROR_GLIBC("strdup"); return NULL; } - for (int i = strlen(str) - 1; i >= 0; i--) { - char c = str[i]; - - if (!isdigit(c) && c != '.' && c != '_') { - str[i + 1] = '\0'; - break; - } - } - return str; } @@ -517,9 +539,13 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym) entry = &sym->sec->symbol_list; list_add(&sym->list, entry); + sym->demangled_name = demangle_name(sym); + if (!sym->demangled_name) + return -1; + 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)); + elf_hash_add(symbol_name, &sym->name_hash, str_hash(sym->demangled_name)); if (is_func_sym(sym) && (strstarts(sym->name, "__pfx_") || @@ -543,10 +569,6 @@ static int elf_add_symbol(struct elf *elf, struct symbol *sym) sym->pfunc = sym->cfunc = sym; - sym->demangled_name = demangle_name(sym); - if (!sym->demangled_name) - return -1; - return 0; } From 8206277746d5c6ae300e7e062a0d9238ed59cc7f Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:28 -0800 Subject: [PATCH 05/19] objtool/klp: Also demangle global objects With CONFIG_LTO_CLANG_THIN, it is possible to have global __UNIQUE_ID, such as: FUNC GLOBAL HIDDEN 19745 __UNIQUE_ID_quirk_amd_nb_node_458 Also demangle global objects. Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-5-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index b2d73c4c6cf1..7e019f15aac5 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -488,9 +488,6 @@ static const char *demangle_name(struct symbol *sym) char *str; ssize_t len; - if (!is_local_sym(sym)) - return sym->name; - if (!is_func_sym(sym) && !is_object_sym(sym)) return sym->name; From 020b71dcafeeececb78d9ee9e5a2e68e8e05e922 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:29 -0800 Subject: [PATCH 06/19] objtool/klp: Remove .llvm suffix in demangle_name() Remove .llvm suffix, so that we can correlate foo.llvm. and foo.llvm.. Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-6-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 7e019f15aac5..feaec45a14a0 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -455,6 +455,11 @@ static int read_sections(struct elf *elf) static ssize_t demangled_name_len(const char *name) { ssize_t idx; + const char *p; + + p = strstr(name, ".llvm."); + if (p) + return p - name; if (!strstarts(name, "__UNIQUE_ID_") && !strchr(name, '.')) return strlen(name); @@ -482,6 +487,9 @@ static ssize_t demangled_name_len(const char *name) * __UNIQUE_ID_addressable___UNIQUE_ID_pci_invalid_bar_694_695 * * to remove both trailing numbers, also remove trailing '_'. + * + * For symbols with llvm suffix, i.e., foo.llvm., remove the + * .llvm. part. */ static const char *demangle_name(struct symbol *sym) { From cdea5cadb0ca403b1929f8d29929c0eda0f715d6 Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:30 -0800 Subject: [PATCH 07/19] objtool/klp: Match symbols based on demangled_name for global variables correlate_symbols() will always try to match full name first. If there is no match, try match only demangled_name. In very rare cases, it is possible to have multiple foo.llvm. in the same kernel. Whenever there is ambiguity like this, fail the klp diff. Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-7-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 13 +++++++ tools/objtool/include/objtool/elf.h | 3 ++ tools/objtool/klp-diff.c | 57 +++++++++++++++++++++++++++++ 3 files changed, 73 insertions(+) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index feaec45a14a0..8122c5f5141c 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -323,6 +323,19 @@ struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *nam return NULL; } +void iterate_global_symbol_by_demangled_name(const struct elf *elf, + const char *demangled_name, + void (*process)(struct symbol *sym, void *data), + void *data) +{ + struct symbol *sym; + + elf_hash_for_each_possible(symbol_name, sym, name_hash, str_hash(demangled_name)) { + if (!strcmp(sym->demangled_name, demangled_name) && !is_local_sym(sym)) + process(sym, data); + } +} + struct reloc *find_reloc_by_dest_range(const struct elf *elf, struct section *sec, unsigned long offset, unsigned int len) { diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index e12c516bd320..25573e5af76e 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -186,6 +186,9 @@ struct symbol *find_func_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_offset(struct section *sec, unsigned long offset); struct symbol *find_symbol_by_name(const struct elf *elf, const char *name); struct symbol *find_global_symbol_by_name(const struct elf *elf, const char *name); +void iterate_global_symbol_by_demangled_name(const struct elf *elf, const char *demangled_name, + void (*process)(struct symbol *sym, void *data), + void *data); struct symbol *find_symbol_containing(const struct section *sec, unsigned long offset); int find_symbol_hole_containing(const struct section *sec, unsigned long offset); struct reloc *find_reloc_by_dest(const struct elf *elf, struct section *sec, unsigned long offset); diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 639bdd3ad5eb..46afbf4a03b9 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -355,6 +355,46 @@ static bool dont_correlate(struct symbol *sym) strstarts(sym->name, "__initcall__"); } +struct process_demangled_name_data { + struct symbol *ret; + int count; +}; + +static void process_demangled_name(struct symbol *sym, void *d) +{ + struct process_demangled_name_data *data = d; + + if (sym->twin) + return; + + data->count++; + data->ret = sym; +} + +/* + * When there is no full name match, try match demangled_name. This would + * match original foo.llvm.123 to patched foo.llvm.456. + * + * Note that, in very rare cases, it is possible to have multiple + * foo.llvm. in the same kernel. When this happens, report error and + * fail the diff. + */ +static int find_global_symbol_by_demangled_name(struct elf *elf, struct symbol *sym, + struct symbol **out_sym) +{ + struct process_demangled_name_data data = {}; + + iterate_global_symbol_by_demangled_name(elf, sym->demangled_name, + process_demangled_name, + &data); + if (data.count > 1) { + ERROR("Multiple (%d) correlation candidates for %s", data.count, sym->name); + return -1; + } + *out_sym = data.ret; + return 0; +} + /* * For each symbol in the original kernel, find its corresponding "twin" in the * patched kernel. @@ -453,6 +493,23 @@ static int correlate_symbols(struct elfs *e) continue; sym2 = find_global_symbol_by_name(e->patched, sym1->name); + if (sym2 && !sym2->twin) { + sym1->twin = sym2; + sym2->twin = sym1; + } + } + + /* + * Correlate globals with demangled_name. + * A separate loop is needed because we want to finish all the + * full name correlations first. + */ + for_each_sym(e->orig, sym1) { + if (sym1->bind == STB_LOCAL || sym1->twin) + continue; + + if (find_global_symbol_by_demangled_name(e->patched, sym1, &sym2)) + return -1; if (sym2 && !sym2->twin) { sym1->twin = sym2; From 4b57e97be22fb8332d05ee1d0fe3c0dd43c828bf Mon Sep 17 00:00:00 2001 From: Song Liu Date: Thu, 5 Mar 2026 15:15:31 -0800 Subject: [PATCH 08/19] objtool/klp: Correlate locals to globals Allow correlating original locals to patched globals, and vice versa. This is needed when: 1. User adds/removes "static" for a function. 2. CONFIG_LTO_CLANG_THIN promotes local functions and objects to global and add .llvm. suffix. Signed-off-by: Song Liu Link: https://patch.msgid.link/20260305231531.3847295-8-song@kernel.org Signed-off-by: Josh Poimboeuf --- tools/objtool/klp-diff.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/tools/objtool/klp-diff.c b/tools/objtool/klp-diff.c index 46afbf4a03b9..85281b3b021f 100644 --- a/tools/objtool/klp-diff.c +++ b/tools/objtool/klp-diff.c @@ -517,6 +517,36 @@ static int correlate_symbols(struct elfs *e) } } + /* Correlate original locals with patched globals */ + for_each_sym(e->orig, sym1) { + if (sym1->twin || dont_correlate(sym1) || !is_local_sym(sym1)) + continue; + + sym2 = find_global_symbol_by_name(e->patched, sym1->name); + if (!sym2 && find_global_symbol_by_demangled_name(e->patched, sym1, &sym2)) + return -1; + + if (sym2 && !sym2->twin) { + sym1->twin = sym2; + sym2->twin = sym1; + } + } + + /* Correlate original globals with patched locals */ + for_each_sym(e->patched, sym2) { + if (sym2->twin || dont_correlate(sym2) || !is_local_sym(sym2)) + continue; + + sym1 = find_global_symbol_by_name(e->orig, sym2->name); + if (!sym1 && find_global_symbol_by_demangled_name(e->orig, sym2, &sym1)) + return -1; + + if (sym1 && !sym1->twin) { + sym2->twin = sym1; + sym1->twin = sym2; + } + } + for_each_sym(e->orig, sym1) { if (sym1->twin || dont_correlate(sym1)) continue; From 757bd10ff0f015d83481d39a266eb752dbbfce33 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:42 -0400 Subject: [PATCH 09/19] livepatch/klp-build: support patches that add/remove files The klp-build script prepares a clean patch by populating two temporary directories ('a' and 'b') with source files and diffing the result. However, this process fails when a patch introduces a new source file, as the script attempts to copy files that do not yet exist in the original source tree. Likewise, it fails when a patch removes a source file and the script attempts to copy a file that no longer exists. Refactor the file-gathering logic to distinguish between original input files and patched output files: - Split get_patch_files() into get_patch_input_files() and get_patch_output_files() to identify which files exist before and after patch application. - Filter out "/dev/null" from both to handle file creation/deletion. - Update refresh_patch() to only copy existing input files to the 'a' directory and the resulting output files to the 'b' directory. Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-4-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 809e198a561d..94ed3b4a91d8 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -296,12 +296,30 @@ set_kernelversion() { sed -i "2i echo $localversion; exit 0" scripts/setlocalversion } +get_patch_input_files() { + local patch="$1" + + grep0 -E '^--- ' "$patch" \ + | gawk '{print $2}' \ + | grep0 -v '^/dev/null$' \ + | sed 's|^[^/]*/||' \ + | sort -u +} + +get_patch_output_files() { + local patch="$1" + + grep0 -E '^\+\+\+ ' "$patch" \ + | gawk '{print $2}' \ + | grep0 -v '^/dev/null$' \ + | sed 's|^[^/]*/||' \ + | sort -u +} + get_patch_files() { local patch="$1" - grep0 -E '^(--- |\+\+\+ )' "$patch" \ - | gawk '{print $2}' \ - | sed 's|^[^/]*/||' \ + { get_patch_input_files "$patch"; get_patch_output_files "$patch"; } \ | sort -u } @@ -312,7 +330,7 @@ git_refresh() { [[ ! -e "$SRC/.git" ]] && return - get_patch_files "$patch" | mapfile -t files + get_patch_input_files "$patch" | mapfile -t files ( cd "$SRC" @@ -426,21 +444,23 @@ do_init() { refresh_patch() { local patch="$1" local tmpdir="$PATCH_TMP_DIR" - local files=() + local input_files=() + local output_files=() rm -rf "$tmpdir" mkdir -p "$tmpdir/a" mkdir -p "$tmpdir/b" # Get all source files affected by the patch - get_patch_files "$patch" | mapfile -t files + get_patch_input_files "$patch" | mapfile -t input_files + get_patch_output_files "$patch" | mapfile -t output_files # Copy orig source files to 'a' - ( cd "$SRC" && echo "${files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" ) + ( cd "$SRC" && echo "${input_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" ) + ( cd "$SRC" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" ) revert_patch "$patch" --recount # Diff 'a' and 'b' to make a clean patch From d36a7343f4bac518b6ef05e2ccc47acd3a2cdab9 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:43 -0400 Subject: [PATCH 10/19] livepatch/klp-build: switch to GNU patch and recountdiff The klp-build script is currently very strict with input patches, requiring them to apply cleanly via `git apply --recount`. This prevents the use of patches with minor contextual fuzz relative to the target kernel sources. To allow users to reuse a patch across similar kernel streams, switch to using GNU patch and patchutils for intermediate patch manipulation. Update the logic for applying, reverting, and regenerating patches: - Use 'patch -p1' for better handling of context fuzz. - Use 'recountdiff' to update line counts after FIX_PATCH_LINES. - Drop git_refresh() and related git-specific logic. Signed-off-by: Joe Lawrence Acked-by: Song Liu Link: https://patch.msgid.link/20260310203751.1479229-5-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 59 ++++++++----------------------------- 1 file changed, 13 insertions(+), 46 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 94ed3b4a91d8..564985a1588a 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -95,7 +95,7 @@ restore_files() { cleanup() { set +o nounset - revert_patches "--recount" + revert_patches restore_files [[ "$KEEP_TMP" -eq 0 ]] && rm -rf "$TMP_DIR" return 0 @@ -282,7 +282,7 @@ set_module_name() { } # Hardcode the value printed by the localversion script to prevent patch -# application from appending it with '+' due to a dirty git working tree. +# application from appending it with '+' due to a dirty working tree. set_kernelversion() { local file="$SRC/scripts/setlocalversion" local localversion @@ -300,8 +300,8 @@ get_patch_input_files() { local patch="$1" grep0 -E '^--- ' "$patch" \ + | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \ | gawk '{print $2}' \ - | grep0 -v '^/dev/null$' \ | sed 's|^[^/]*/||' \ | sort -u } @@ -310,8 +310,8 @@ get_patch_output_files() { local patch="$1" grep0 -E '^\+\+\+ ' "$patch" \ + | grep0 -v -e '/dev/null' -e '1969-12-31' -e '1970-01-01' \ | gawk '{print $2}' \ - | grep0 -v '^/dev/null$' \ | sed 's|^[^/]*/||' \ | sort -u } @@ -323,21 +323,6 @@ get_patch_files() { | sort -u } -# Make sure git re-stats the changed files -git_refresh() { - local patch="$1" - local files=() - - [[ ! -e "$SRC/.git" ]] && return - - get_patch_input_files "$patch" | mapfile -t files - - ( - cd "$SRC" - git update-index -q --refresh -- "${files[@]}" - ) -} - check_unsupported_patches() { local patch @@ -358,36 +343,19 @@ check_unsupported_patches() { apply_patch() { local patch="$1" - shift - local extra_args=("$@") [[ ! -f "$patch" ]] && die "$patch doesn't exist" - - ( - cd "$SRC" - - # The sed strips the version signature from 'git format-patch', - # otherwise 'git apply --recount' warns. - sed -n '/^-- /q;p' "$patch" | - git apply "${extra_args[@]}" - ) + patch -d "$SRC" -p1 --dry-run --silent --no-backup-if-mismatch -r /dev/null < "$patch" + patch -d "$SRC" -p1 --silent --no-backup-if-mismatch -r /dev/null < "$patch" APPLIED_PATCHES+=("$patch") } revert_patch() { local patch="$1" - shift - local extra_args=("$@") local tmp=() - ( - cd "$SRC" - - sed -n '/^-- /q;p' "$patch" | - git apply --reverse "${extra_args[@]}" - ) - git_refresh "$patch" + patch -d "$SRC" -p1 -R --silent --no-backup-if-mismatch -r /dev/null < "$patch" for p in "${APPLIED_PATCHES[@]}"; do [[ "$p" == "$patch" ]] && continue @@ -406,11 +374,10 @@ apply_patches() { } revert_patches() { - local extra_args=("$@") local patches=("${APPLIED_PATCHES[@]}") for (( i=${#patches[@]}-1 ; i>=0 ; i-- )) ; do - revert_patch "${patches[$i]}" "${extra_args[@]}" + revert_patch "${patches[$i]}" done APPLIED_PATCHES=() @@ -434,6 +401,7 @@ do_init() { APPLIED_PATCHES=() [[ -x "$FIX_PATCH_LINES" ]] || die "can't find fix-patch-lines" + command -v recountdiff &>/dev/null || die "recountdiff not found (install patchutils)" validate_config set_module_name @@ -459,12 +427,12 @@ refresh_patch() { ( cd "$SRC" && echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" ) # Copy patched source files to 'b' - apply_patch "$patch" --recount + apply_patch "$patch" ( cd "$SRC" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" ) - revert_patch "$patch" --recount + revert_patch "$patch" # Diff 'a' and 'b' to make a clean patch - ( cd "$tmpdir" && git diff --no-index --no-prefix a b > "$patch" ) || true + ( cd "$tmpdir" && diff -Nupr a b > "$patch" ) || true } # Copy the patches to a temporary directory, fix their lines so as not to @@ -487,8 +455,7 @@ fix_patches() { cp -f "$old_patch" "$tmp_patch" refresh_patch "$tmp_patch" - "$FIX_PATCH_LINES" "$tmp_patch" > "$new_patch" - refresh_patch "$new_patch" + "$FIX_PATCH_LINES" "$tmp_patch" | recountdiff > "$new_patch" PATCHES[i]="$new_patch" From e4dbf70615e52255de3ff943ac08e0bbd080dcd6 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:44 -0400 Subject: [PATCH 11/19] livepatch/klp-build: add grep-override function Provide a custom grep() function to catch direct usage of the command. Bare grep calls are generally incompatible with pipefail and errexit behavior (where a failed match causes the script to exit). Developers can still call grep via command grep if that behavior is explicitly desired. Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-6-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 564985a1588a..cf6c2bf694aa 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -56,6 +56,13 @@ grep0() { command grep "$@" || true } +# Because pipefail is enabled, the grep0 helper should be used instead of +# grep, otherwise a failed match can propagate to an error. +grep() { + echo "error: $SCRIPT: use grep0 or 'command grep' instead of bare grep" >&2 + exit 1 +} + status() { echo "$*" } From 0573bcc4ffca498a6c644b0e1ccbe1a6d9b96a5c Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:45 -0400 Subject: [PATCH 12/19] livepatch/klp-build: add Makefile with check target Add a standalone Makefile with a 'check' target that runs static code analysis (shellcheck) on the klp-build script(s). This is intended strictly as a development aid. Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-7-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/Makefile | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 scripts/livepatch/Makefile diff --git a/scripts/livepatch/Makefile b/scripts/livepatch/Makefile new file mode 100644 index 000000000000..17b590213740 --- /dev/null +++ b/scripts/livepatch/Makefile @@ -0,0 +1,20 @@ +# SPDX-License-Identifier: GPL-2.0 +# Standalone Makefile for developer tooling (not part of kbuild). + +SHELLCHECK := $(shell which shellcheck 2> /dev/null) + +SRCS := \ + klp-build + +.DEFAULT_GOAL := help +.PHONY: help +help: + @echo " check - Run shellcheck on $(SRCS)" + @echo " help - Show this help message" + +.PHONY: check +check: +ifndef SHELLCHECK + $(error shellcheck is not installed. Please install it to run checks) +endif + @$(SHELLCHECK) $(SHELLCHECK_OPTIONS) $(SRCS) From b4a53519393521c68ec65f43bfebd64f178e6220 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:46 -0400 Subject: [PATCH 13/19] livepatch/klp-build: fix shellcheck complaints Fix or suppress the following shellcheck warnings: In klp-build line 57: command grep "$@" || true ^--^ SC2317 (info): Command appears to be unreachable. Check usage (or ignore if invoked indirectly). Fix the following warning: In klp-build line 565: local file_dir="$(dirname "$file")" ^------^ SC2034 (warning): file_dir appears unused. Verify use (or export if used externally). Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-8-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index cf6c2bf694aa..374e1261fd7a 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -53,6 +53,7 @@ PATCH_TMP_DIR="$TMP_DIR/tmp" KLP_DIFF_LOG="$DIFF_DIR/diff.log" grep0() { + # shellcheck disable=SC2317 command grep "$@" || true } @@ -550,7 +551,6 @@ copy_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")" From e506ad210d6d7aeaff4bca777428c8c8f9850150 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:47 -0400 Subject: [PATCH 14/19] livepatch/klp-build: improve short-circuit validation Update SHORT_CIRCUIT behavior to better handle patch validation and argument processing in later klp-build steps. Perform patch validation for both step 1 (building original kernel) and step 2 (building patched kernel) to ensure patches are verified before any compilation occurs. Additionally, allow the user to omit input patches when skipping past step 2. Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-9-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 374e1261fd7a..60c7635e65c1 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -220,7 +220,7 @@ process_args() { esac done - if [[ $# -eq 0 ]]; then + if [[ $# -eq 0 ]] && (( SHORT_CIRCUIT <= 2 )); then usage exit 1 fi @@ -791,9 +791,12 @@ build_patch_module() { process_args "$@" do_init -if (( SHORT_CIRCUIT <= 1 )); then +if (( SHORT_CIRCUIT <= 2 )); then status "Validating patch(es)" validate_patches +fi + +if (( SHORT_CIRCUIT <= 1 )); then status "Building original kernel" clean_kernel build_kernel From b41d8b7d1752f2f85fc1a87f5e4f4dda45adad15 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:49 -0400 Subject: [PATCH 15/19] livepatch/klp-build: provide friendlier error messages Provide more context for common klp-build failure modes. Clarify which user-provided patch is unsupported or failed to apply, and explicitly identify which kernel build (original or patched) failed. Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-11-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index 60c7635e65c1..dc0a23a8908b 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -342,7 +342,7 @@ check_unsupported_patches() { for file in "${files[@]}"; do case "$file" in lib/*|*.S) - die "unsupported patch to $file" + die "${patch}: unsupported patch to $file" ;; esac done @@ -487,6 +487,7 @@ clean_kernel() { } build_kernel() { + local build="$1" local log="$TMP_DIR/build.log" local objtool_args=() local cmd=() @@ -524,7 +525,7 @@ build_kernel() { "${cmd[@]}" \ 1> >(tee -a "$log") \ 2> >(tee -a "$log" | grep0 -v "modpost.*undefined!" >&2) - ) + ) || die "$build kernel build failed" } find_objects() { @@ -799,7 +800,7 @@ fi if (( SHORT_CIRCUIT <= 1 )); then status "Building original kernel" clean_kernel - build_kernel + build_kernel "original" status "Copying original object files" copy_orig_objects fi @@ -809,7 +810,7 @@ if (( SHORT_CIRCUIT <= 2 )); then fix_patches apply_patches status "Building patched kernel" - build_kernel + build_kernel "patched" revert_patches status "Copying patched object files" copy_patched_objects From 1fbc9b855f08f89ccf933324a5cbd8c53ee94d87 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:50 -0400 Subject: [PATCH 16/19] livepatch/klp-build: add terminal color output Improve the readability of klp-build output by implementing a basic color scheme. When the standard output and error are connected to a terminal, highlight status messages in bold and warning/error prefixes in yellow/red. Acked-by: Song Liu Signed-off-by: Joe Lawrence Link: https://patch.msgid.link/20260310203751.1479229-12-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index dc0a23a8908b..d628e2c86078 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -52,6 +52,15 @@ PATCH_TMP_DIR="$TMP_DIR/tmp" KLP_DIFF_LOG="$DIFF_DIR/diff.log" +# Terminal output colors +read -r COLOR_RESET COLOR_BOLD COLOR_ERROR COLOR_WARN <<< "" +if [[ -t 1 && -t 2 ]]; then + COLOR_RESET="\033[0m" + COLOR_BOLD="\033[1m" + COLOR_ERROR="\033[0;31m" + COLOR_WARN="\033[0;33m" +fi + grep0() { # shellcheck disable=SC2317 command grep "$@" || true @@ -65,15 +74,15 @@ grep() { } status() { - echo "$*" + echo -e "${COLOR_BOLD}$*${COLOR_RESET}" } warn() { - echo "error: $SCRIPT: $*" >&2 + echo -e "${COLOR_WARN}warning${COLOR_RESET}: $SCRIPT: $*" >&2 } die() { - warn "$@" + echo -e "${COLOR_ERROR}error${COLOR_RESET}: $SCRIPT: $*" >&2 exit 1 } @@ -110,7 +119,7 @@ cleanup() { } trap_err() { - warn "line ${BASH_LINENO[0]}: '$BASH_COMMAND'" + die "line ${BASH_LINENO[0]}: '$BASH_COMMAND'" } trap cleanup EXIT INT TERM HUP From 51a0b7c4ede5c775e9d362e5f465ca993e076823 Mon Sep 17 00:00:00 2001 From: Joe Lawrence Date: Tue, 10 Mar 2026 16:37:51 -0400 Subject: [PATCH 17/19] livepatch/klp-build: report patch validation fuzz Capture the output of the patch command to detect when a patch applies with fuzz or line offsets. If such "fuzz" is detected during the validation phase, warn the user and display the details. This helps identify input patches that may need refreshing against the target source tree. Ensure that internal patch operations (such as those in refresh_patch or during the final build phase) can still run quietly. Signed-off-by: Joe Lawrence Acked-by: Song Liu Link: https://patch.msgid.link/20260310203751.1479229-13-joe.lawrence@redhat.com Signed-off-by: Josh Poimboeuf --- scripts/livepatch/klp-build | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/scripts/livepatch/klp-build b/scripts/livepatch/klp-build index d628e2c86078..839f9b6bfe1f 100755 --- a/scripts/livepatch/klp-build +++ b/scripts/livepatch/klp-build @@ -360,11 +360,24 @@ check_unsupported_patches() { apply_patch() { local patch="$1" + shift + local extra_args=("$@") + local drift_regex="with fuzz|offset [0-9]+ line" + local output + local status [[ ! -f "$patch" ]] && die "$patch doesn't exist" - patch -d "$SRC" -p1 --dry-run --silent --no-backup-if-mismatch -r /dev/null < "$patch" - patch -d "$SRC" -p1 --silent --no-backup-if-mismatch -r /dev/null < "$patch" + status=0 + output=$(patch -d "$SRC" -p1 --dry-run --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" < "$patch" 2>&1) || status=$? + if [[ "$status" -ne 0 ]]; then + echo "$output" >&2 + die "$patch did not apply" + elif [[ "$output" =~ $drift_regex ]]; then + echo "$output" >&2 + warn "${patch} applied with fuzz" + fi + patch -d "$SRC" -p1 --no-backup-if-mismatch -r /dev/null "${extra_args[@]}" --silent < "$patch" APPLIED_PATCHES+=("$patch") } @@ -383,10 +396,11 @@ revert_patch() { } apply_patches() { + local extra_args=("$@") local patch for patch in "${PATCHES[@]}"; do - apply_patch "$patch" + apply_patch "$patch" "${extra_args[@]}" done } @@ -444,7 +458,7 @@ refresh_patch() { ( cd "$SRC" && echo "${input_files[@]}" | xargs cp --parents --target-directory="$tmpdir/a" ) # Copy patched source files to 'b' - apply_patch "$patch" + apply_patch "$patch" "--silent" ( cd "$SRC" && echo "${output_files[@]}" | xargs cp --parents --target-directory="$tmpdir/b" ) revert_patch "$patch" @@ -817,7 +831,7 @@ fi if (( SHORT_CIRCUIT <= 2 )); then status "Fixing patch(es)" fix_patches - apply_patches + apply_patches "--silent" status "Building patched kernel" build_kernel "patched" revert_patches From 96f3b16a9de552538b810f773645d43f3b661b50 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Mon, 16 Mar 2026 17:47:56 -0700 Subject: [PATCH 18/19] objtool: Support Clang RAX DRAP sequence Recent Clang can use RAX as a temporary register for the DRAP stack alignment sequence. Add support for that. Fixes the following warning: vmlinux.o: error: objtool: vmw_host_printf+0xd: unknown CFA base reg 0 Closes: https://lore.kernel.org/cefefdd1-7b82-406d-8ff4-e4b167e45ee6@app.fastmail.com Reported-by: Arnd Bergmann Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://patch.msgid.link/3f33dc720b83dc6d3a2b7094f75a5c90a0b1cbc5.1773708458.git.jpoimboe@kernel.org --- arch/x86/include/asm/orc_types.h | 1 + arch/x86/kernel/unwind_orc.c | 8 ++++++++ tools/arch/x86/include/asm/orc_types.h | 1 + tools/objtool/arch/x86/decode.c | 3 +++ tools/objtool/arch/x86/orc.c | 5 +++++ 5 files changed, 18 insertions(+) diff --git a/arch/x86/include/asm/orc_types.h b/arch/x86/include/asm/orc_types.h index e0125afa53fb..b3cc7970fa54 100644 --- a/arch/x86/include/asm/orc_types.h +++ b/arch/x86/include/asm/orc_types.h @@ -37,6 +37,7 @@ #define ORC_REG_R13 7 #define ORC_REG_BP_INDIRECT 8 #define ORC_REG_SP_INDIRECT 9 +#define ORC_REG_AX 10 #define ORC_REG_MAX 15 #define ORC_TYPE_UNDEFINED 0 diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index f610fde2d5c4..32f7e918d3d9 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c @@ -578,6 +578,14 @@ bool unwind_next_frame(struct unwind_state *state) } break; + case ORC_REG_AX: + if (!get_reg(state, offsetof(struct pt_regs, ax), &sp)) { + orc_warn_current("missing AX value at %pB\n", + (void *)state->ip); + goto err; + } + break; + default: orc_warn("unknown SP base reg %d at %pB\n", orc->sp_reg, (void *)state->ip); diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h index e0125afa53fb..b3cc7970fa54 100644 --- a/tools/arch/x86/include/asm/orc_types.h +++ b/tools/arch/x86/include/asm/orc_types.h @@ -37,6 +37,7 @@ #define ORC_REG_R13 7 #define ORC_REG_BP_INDIRECT 8 #define ORC_REG_SP_INDIRECT 9 +#define ORC_REG_AX 10 #define ORC_REG_MAX 15 #define ORC_TYPE_UNDEFINED 0 diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index 73bfea220d1b..c3a10f3c72d9 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -912,6 +912,9 @@ int arch_decode_hint_reg(u8 sp_reg, int *base) case ORC_REG_DX: *base = CFI_DX; break; + case ORC_REG_AX: + *base = CFI_AX; + break; default: return -1; } diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c index 735e150ca6b7..5494bb450ab5 100644 --- a/tools/objtool/arch/x86/orc.c +++ b/tools/objtool/arch/x86/orc.c @@ -70,6 +70,9 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct case CFI_DX: orc->sp_reg = ORC_REG_DX; break; + case CFI_AX: + orc->sp_reg = ORC_REG_AX; + break; default: ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); return -1; @@ -138,6 +141,8 @@ static const char *reg_name(unsigned int reg) return "bp(ind)"; case ORC_REG_SP_INDIRECT: return "sp(ind)"; + case ORC_REG_AX: + return "ax"; default: return "?"; } From 1735858caa4bbb8b923860c0833d463b5d9c5f79 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Wed, 18 Mar 2026 09:38:38 +0100 Subject: [PATCH 19/19] objtool/x86: Reorder ORC register numbering Reorder the ORC register values so their ordering matches the x86 instruction set register encodings. No functional change intended. Suggested-by: Peter Zijlstra Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) --- arch/x86/include/asm/orc_types.h | 10 +++--- arch/x86/kernel/unwind_orc.c | 48 +++++++++++++++----------- tools/arch/x86/include/asm/orc_types.h | 10 +++--- tools/objtool/arch/x86/decode.c | 21 ++++++----- tools/objtool/arch/x86/orc.c | 36 +++++++++---------- 5 files changed, 67 insertions(+), 58 deletions(-) diff --git a/arch/x86/include/asm/orc_types.h b/arch/x86/include/asm/orc_types.h index b3cc7970fa54..5837c2bb277f 100644 --- a/arch/x86/include/asm/orc_types.h +++ b/arch/x86/include/asm/orc_types.h @@ -28,16 +28,16 @@ * and GCC realigned stacks. */ #define ORC_REG_UNDEFINED 0 -#define ORC_REG_PREV_SP 1 +#define ORC_REG_AX 1 #define ORC_REG_DX 2 -#define ORC_REG_DI 3 +#define ORC_REG_SP 3 #define ORC_REG_BP 4 -#define ORC_REG_SP 5 +#define ORC_REG_DI 5 #define ORC_REG_R10 6 #define ORC_REG_R13 7 -#define ORC_REG_BP_INDIRECT 8 +#define ORC_REG_PREV_SP 8 #define ORC_REG_SP_INDIRECT 9 -#define ORC_REG_AX 10 +#define ORC_REG_BP_INDIRECT 10 #define ORC_REG_MAX 15 #define ORC_TYPE_UNDEFINED 0 diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index 32f7e918d3d9..6407bc9256bf 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c @@ -546,25 +546,15 @@ bool unwind_next_frame(struct unwind_state *state) indirect = true; break; - case ORC_REG_R10: - if (!get_reg(state, offsetof(struct pt_regs, r10), &sp)) { - orc_warn_current("missing R10 value at %pB\n", - (void *)state->ip); - goto err; - } - break; + /* + * Any of the below registers may temporarily hold the stack pointer, + * typically during a DRAP stack realignment sequence or some other + * stack swizzle. + */ - case ORC_REG_R13: - if (!get_reg(state, offsetof(struct pt_regs, r13), &sp)) { - orc_warn_current("missing R13 value at %pB\n", - (void *)state->ip); - goto err; - } - break; - - case ORC_REG_DI: - if (!get_reg(state, offsetof(struct pt_regs, di), &sp)) { - orc_warn_current("missing RDI value at %pB\n", + case ORC_REG_AX: + if (!get_reg(state, offsetof(struct pt_regs, ax), &sp)) { + orc_warn_current("missing AX value at %pB\n", (void *)state->ip); goto err; } @@ -578,9 +568,25 @@ bool unwind_next_frame(struct unwind_state *state) } break; - case ORC_REG_AX: - if (!get_reg(state, offsetof(struct pt_regs, ax), &sp)) { - orc_warn_current("missing AX value at %pB\n", + case ORC_REG_DI: + if (!get_reg(state, offsetof(struct pt_regs, di), &sp)) { + orc_warn_current("missing RDI value at %pB\n", + (void *)state->ip); + goto err; + } + break; + + case ORC_REG_R10: + if (!get_reg(state, offsetof(struct pt_regs, r10), &sp)) { + orc_warn_current("missing R10 value at %pB\n", + (void *)state->ip); + goto err; + } + break; + + case ORC_REG_R13: + if (!get_reg(state, offsetof(struct pt_regs, r13), &sp)) { + orc_warn_current("missing R13 value at %pB\n", (void *)state->ip); goto err; } diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h index b3cc7970fa54..5837c2bb277f 100644 --- a/tools/arch/x86/include/asm/orc_types.h +++ b/tools/arch/x86/include/asm/orc_types.h @@ -28,16 +28,16 @@ * and GCC realigned stacks. */ #define ORC_REG_UNDEFINED 0 -#define ORC_REG_PREV_SP 1 +#define ORC_REG_AX 1 #define ORC_REG_DX 2 -#define ORC_REG_DI 3 +#define ORC_REG_SP 3 #define ORC_REG_BP 4 -#define ORC_REG_SP 5 +#define ORC_REG_DI 5 #define ORC_REG_R10 6 #define ORC_REG_R13 7 -#define ORC_REG_BP_INDIRECT 8 +#define ORC_REG_PREV_SP 8 #define ORC_REG_SP_INDIRECT 9 -#define ORC_REG_AX 10 +#define ORC_REG_BP_INDIRECT 10 #define ORC_REG_MAX 15 #define ORC_TYPE_UNDEFINED 0 diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index c3a10f3c72d9..23e48ea9d94e 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -891,14 +891,20 @@ int arch_decode_hint_reg(u8 sp_reg, int *base) case ORC_REG_UNDEFINED: *base = CFI_UNDEFINED; break; + case ORC_REG_AX: + *base = CFI_AX; + break; + case ORC_REG_DX: + *base = CFI_DX; + break; case ORC_REG_SP: *base = CFI_SP; break; case ORC_REG_BP: *base = CFI_BP; break; - case ORC_REG_SP_INDIRECT: - *base = CFI_SP_INDIRECT; + case ORC_REG_DI: + *base = CFI_DI; break; case ORC_REG_R10: *base = CFI_R10; @@ -906,14 +912,11 @@ int arch_decode_hint_reg(u8 sp_reg, int *base) case ORC_REG_R13: *base = CFI_R13; break; - case ORC_REG_DI: - *base = CFI_DI; + case ORC_REG_SP_INDIRECT: + *base = CFI_SP_INDIRECT; break; - case ORC_REG_DX: - *base = CFI_DX; - break; - case ORC_REG_AX: - *base = CFI_AX; + case ORC_REG_BP_INDIRECT: + *base = CFI_BP_INDIRECT; break; default: return -1; diff --git a/tools/objtool/arch/x86/orc.c b/tools/objtool/arch/x86/orc.c index 5494bb450ab5..eff078ecc945 100644 --- a/tools/objtool/arch/x86/orc.c +++ b/tools/objtool/arch/x86/orc.c @@ -46,17 +46,20 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct orc->signal = cfi->signal; switch (cfi->cfa.base) { + case CFI_AX: + orc->sp_reg = ORC_REG_AX; + break; + case CFI_DX: + orc->sp_reg = ORC_REG_DX; + break; case CFI_SP: orc->sp_reg = ORC_REG_SP; break; - case CFI_SP_INDIRECT: - orc->sp_reg = ORC_REG_SP_INDIRECT; - break; case CFI_BP: orc->sp_reg = ORC_REG_BP; break; - case CFI_BP_INDIRECT: - orc->sp_reg = ORC_REG_BP_INDIRECT; + case CFI_DI: + orc->sp_reg = ORC_REG_DI; break; case CFI_R10: orc->sp_reg = ORC_REG_R10; @@ -64,14 +67,11 @@ int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, struct instruct case CFI_R13: orc->sp_reg = ORC_REG_R13; break; - case CFI_DI: - orc->sp_reg = ORC_REG_DI; + case CFI_SP_INDIRECT: + orc->sp_reg = ORC_REG_SP_INDIRECT; break; - case CFI_DX: - orc->sp_reg = ORC_REG_DX; - break; - case CFI_AX: - orc->sp_reg = ORC_REG_AX; + case CFI_BP_INDIRECT: + orc->sp_reg = ORC_REG_BP_INDIRECT; break; default: ERROR_INSN(insn, "unknown CFA base reg %d", cfi->cfa.base); @@ -125,24 +125,24 @@ static const char *reg_name(unsigned int reg) switch (reg) { case ORC_REG_PREV_SP: return "prevsp"; + case ORC_REG_AX: + return "ax"; case ORC_REG_DX: return "dx"; - case ORC_REG_DI: - return "di"; case ORC_REG_BP: return "bp"; case ORC_REG_SP: return "sp"; + case ORC_REG_DI: + return "di"; case ORC_REG_R10: return "r10"; case ORC_REG_R13: return "r13"; - case ORC_REG_BP_INDIRECT: - return "bp(ind)"; case ORC_REG_SP_INDIRECT: return "sp(ind)"; - case ORC_REG_AX: - return "ax"; + case ORC_REG_BP_INDIRECT: + return "bp(ind)"; default: return "?"; }