From bf71940fc16953bed84caa59b7b076ead70d42f6 Mon Sep 17 00:00:00 2001 From: David Engraf Date: Mon, 3 Feb 2025 08:36:10 +0100 Subject: [PATCH 01/22] objtool: Hide unnecessary compiler error message The check for using old libelf prints an error message when libelf.h is not available but does not abort. This may confuse so hide the compiler error message. Signed-off-by: David Engraf Link: https://lore.kernel.org/r/20250203073610.206000-1-david.engraf@sysgo.com Signed-off-by: Josh Poimboeuf --- tools/objtool/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 7a65948892e5..8c20361dd100 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -37,7 +37,7 @@ OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBE OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) # Allow old libelf to be used: -elfshdr := $(shell echo '$(pound)include ' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr) +elfshdr := $(shell echo '$(pound)include ' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr) OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) # Always want host compilation. From ab6ce22b789622ca732e91cbb3a5cb5ba370cbd0 Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 11 Feb 2025 19:50:10 +0800 Subject: [PATCH 02/22] objtool: Handle various symbol types of rodata In the relocation section ".rela.rodata" of each .o file compiled with LoongArch toolchain, there are various symbol types such as STT_NOTYPE, STT_OBJECT, STT_FUNC in addition to the usual STT_SECTION, it needs to use reloc symbol offset instead of reloc addend to find the destination instruction in find_jump_table() and add_jump_table(). For the most part, an absolute relocation type is used for rodata. In the case of STT_SECTION, reloc->sym->offset is always zero, and for the other symbol types, reloc_addend(reloc) is always zero, thus it can use a simple statement "reloc->sym->offset + reloc_addend(reloc)" to obtain the symbol offset for various symbol types. Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/20250211115016.26913-2-yangtiezhu@loongson.cn Acked-by: Huacai Chen Signed-off-by: Josh Poimboeuf --- tools/objtool/check.c | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index ce973d9d8e6d..cfab4a1b1f70 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1954,6 +1954,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, unsigned int prev_offset = 0; struct reloc *reloc = table; struct alternative *alt; + unsigned long sym_offset; /* * Each @reloc is a switch table relocation which points to the target @@ -1971,9 +1972,10 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, if (prev_offset && reloc_offset(reloc) != prev_offset + 8) break; + sym_offset = reloc->sym->offset + reloc_addend(reloc); + /* Detect function pointers from contiguous objects: */ - if (reloc->sym->sec == pfunc->sec && - reloc_addend(reloc) == pfunc->offset) + if (reloc->sym->sec == pfunc->sec && sym_offset == pfunc->offset) break; /* @@ -1981,10 +1983,10 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, * which point to the end of the function. Ignore them. */ if (reloc->sym->sec == pfunc->sec && - reloc_addend(reloc) == pfunc->offset + pfunc->len) + sym_offset == pfunc->offset + pfunc->len) goto next; - dest_insn = find_insn(file, reloc->sym->sec, reloc_addend(reloc)); + dest_insn = find_insn(file, reloc->sym->sec, sym_offset); if (!dest_insn) break; @@ -2023,6 +2025,7 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func, struct reloc *table_reloc; struct instruction *dest_insn, *orig_insn = insn; unsigned long table_size; + unsigned long sym_offset; /* * Backward search using the @first_jump_src links, these help avoid @@ -2046,7 +2049,10 @@ static void find_jump_table(struct objtool_file *file, struct symbol *func, table_reloc = arch_find_switch_table(file, insn, &table_size); if (!table_reloc) continue; - dest_insn = find_insn(file, table_reloc->sym->sec, reloc_addend(table_reloc)); + + sym_offset = table_reloc->sym->offset + reloc_addend(table_reloc); + + dest_insn = find_insn(file, table_reloc->sym->sec, sym_offset); if (!dest_insn || !insn_func(dest_insn) || insn_func(dest_insn)->pfunc != func) continue; From 091bf313f8a852a7f30c3a8dcef569edfd06f5dc Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 11 Feb 2025 19:50:11 +0800 Subject: [PATCH 03/22] objtool: Handle different entry size of rodata In the most cases, the entry size of rodata is 8 bytes because the relocation type is 64 bit. There are also 32 bit relocation types, the entry size of rodata should be 4 bytes in this case. Add an arch-specific function arch_reloc_size() to assign the entry size of rodata for x86, powerpc and LoongArch. Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/20250211115016.26913-3-yangtiezhu@loongson.cn Acked-by: Huacai Chen Signed-off-by: Josh Poimboeuf --- tools/objtool/arch/loongarch/decode.c | 11 +++++++++++ tools/objtool/arch/powerpc/decode.c | 14 ++++++++++++++ tools/objtool/arch/x86/decode.c | 13 +++++++++++++ tools/objtool/check.c | 2 +- tools/objtool/include/objtool/arch.h | 2 ++ 5 files changed, 41 insertions(+), 1 deletion(-) diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index 69b66994f2a1..b64205b89f6b 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -363,3 +363,14 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state) state->cfa.base = CFI_SP; state->cfa.offset = 0; } + +unsigned int arch_reloc_size(struct reloc *reloc) +{ + switch (reloc_type(reloc)) { + case R_LARCH_32: + case R_LARCH_32_PCREL: + return 4; + default: + return 8; + } +} diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c index 53b55690f320..7c0bf2429067 100644 --- a/tools/objtool/arch/powerpc/decode.c +++ b/tools/objtool/arch/powerpc/decode.c @@ -106,3 +106,17 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state) state->regs[CFI_RA].base = CFI_CFA; state->regs[CFI_RA].offset = 0; } + +unsigned int arch_reloc_size(struct reloc *reloc) +{ + switch (reloc_type(reloc)) { + case R_PPC_REL32: + case R_PPC_ADDR32: + case R_PPC_UADDR32: + case R_PPC_PLT32: + case R_PPC_PLTREL32: + return 4; + default: + return 8; + } +} diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index fe1362c34564..fb9691a34d92 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -852,3 +852,16 @@ bool arch_is_embedded_insn(struct symbol *sym) return !strcmp(sym->name, "retbleed_return_thunk") || !strcmp(sym->name, "srso_safe_ret"); } + +unsigned int arch_reloc_size(struct reloc *reloc) +{ + switch (reloc_type(reloc)) { + case R_X86_64_32: + case R_X86_64_32S: + case R_X86_64_PC32: + case R_X86_64_PLT32: + return 4; + default: + return 8; + } +} diff --git a/tools/objtool/check.c b/tools/objtool/check.c index cfab4a1b1f70..f762d231c747 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1969,7 +1969,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, break; /* Make sure the table entries are consecutive: */ - if (prev_offset && reloc_offset(reloc) != prev_offset + 8) + if (prev_offset && reloc_offset(reloc) != prev_offset + arch_reloc_size(reloc)) break; sym_offset = reloc->sym->offset + reloc_addend(reloc); diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index d63b46a19f39..396f7c6c81c0 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -97,4 +97,6 @@ int arch_rewrite_retpolines(struct objtool_file *file); bool arch_pc_relative_reloc(struct reloc *reloc); +unsigned int arch_reloc_size(struct reloc *reloc); + #endif /* _ARCH_H */ From c4b93b06230ae49870187189d9f7342f6ad4f14e Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 11 Feb 2025 19:50:12 +0800 Subject: [PATCH 04/22] objtool: Handle PC relative relocation type For the most part, an absolute relocation type is used for rodata. In the case of STT_SECTION, reloc->sym->offset is always zero, for the other symbol types, reloc_addend(reloc) is always zero, thus it can use a simple statement "reloc->sym->offset + reloc_addend(reloc)" to obtain the symbol offset for various symbol types. When compiling on LoongArch, there exist PC relative relocation types for rodata, it needs to calculate the symbol offset with "S + A - PC" according to the spec of "ELF for the LoongArch Architecture". If there is only one jump table in the rodata, the "PC" is the entry address which is equal with the value of reloc_offset(reloc), at this time, reloc_offset(table) is 0. If there are many jump tables in the rodata, the "PC" is the offset of the jump table's base address which is equal with the value of reloc_offset(reloc) - reloc_offset(table). So for LoongArch, if the relocation type is PC relative, it can use a statement "reloc_offset(reloc) - reloc_offset(table)" to get the "PC" value when calculating the symbol offset with "S + A - PC" for one or many jump tables in the rodata. Add an arch-specific function arch_jump_table_sym_offset() to assign the symbol offset, for the most part that is an absolute relocation, the default value is "reloc->sym->offset + reloc_addend(reloc)" in the weak definition, it can be overridden by each architecture that has different requirements. Link: https://github.com/loongson/la-abi-specs/blob/release/laelf.adoc Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/20250211115016.26913-4-yangtiezhu@loongson.cn Acked-by: Huacai Chen Signed-off-by: Josh Poimboeuf --- tools/objtool/arch/loongarch/decode.c | 17 +++++++++++++---- tools/objtool/arch/loongarch/include/arch/elf.h | 7 +++++++ tools/objtool/check.c | 7 ++++++- tools/objtool/include/objtool/arch.h | 1 + 4 files changed, 27 insertions(+), 5 deletions(-) diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c index b64205b89f6b..02e490555966 100644 --- a/tools/objtool/arch/loongarch/decode.c +++ b/tools/objtool/arch/loongarch/decode.c @@ -5,10 +5,7 @@ #include #include #include - -#ifndef EM_LOONGARCH -#define EM_LOONGARCH 258 -#endif +#include int arch_ftrace_match(char *name) { @@ -374,3 +371,15 @@ unsigned int arch_reloc_size(struct reloc *reloc) return 8; } } + +unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table) +{ + switch (reloc_type(reloc)) { + case R_LARCH_32_PCREL: + case R_LARCH_64_PCREL: + return reloc->sym->offset + reloc_addend(reloc) - + (reloc_offset(reloc) - reloc_offset(table)); + default: + return reloc->sym->offset + reloc_addend(reloc); + } +} diff --git a/tools/objtool/arch/loongarch/include/arch/elf.h b/tools/objtool/arch/loongarch/include/arch/elf.h index 9623d663220e..ec79062c9554 100644 --- a/tools/objtool/arch/loongarch/include/arch/elf.h +++ b/tools/objtool/arch/loongarch/include/arch/elf.h @@ -18,6 +18,13 @@ #ifndef R_LARCH_32_PCREL #define R_LARCH_32_PCREL 99 #endif +#ifndef R_LARCH_64_PCREL +#define R_LARCH_64_PCREL 109 +#endif + +#ifndef EM_LOONGARCH +#define EM_LOONGARCH 258 +#endif #define R_NONE R_LARCH_NONE #define R_ABS32 R_LARCH_32 diff --git a/tools/objtool/check.c b/tools/objtool/check.c index f762d231c747..7dbf22c6da9d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1944,6 +1944,11 @@ static int add_special_section_alts(struct objtool_file *file) return ret; } +__weak unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table) +{ + return reloc->sym->offset + reloc_addend(reloc); +} + static int add_jump_table(struct objtool_file *file, struct instruction *insn, struct reloc *next_table) { @@ -1972,7 +1977,7 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, if (prev_offset && reloc_offset(reloc) != prev_offset + arch_reloc_size(reloc)) break; - sym_offset = reloc->sym->offset + reloc_addend(reloc); + sym_offset = arch_jump_table_sym_offset(reloc, table); /* Detect function pointers from contiguous objects: */ if (reloc->sym->sec == pfunc->sec && sym_offset == pfunc->offset) diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index 396f7c6c81c0..089a1acc48a8 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -98,5 +98,6 @@ int arch_rewrite_retpolines(struct objtool_file *file); bool arch_pc_relative_reloc(struct reloc *reloc); unsigned int arch_reloc_size(struct reloc *reloc); +unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table); #endif /* _ARCH_H */ From b95f852d3af21513e19a54c1e971c79f2911b54a Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 11 Feb 2025 19:50:13 +0800 Subject: [PATCH 05/22] objtool/LoongArch: Add support for switch table The objtool program need to analysis the control flow of each object file generated by compiler toolchain, it needs to know all the locations that a branch instruction may jump into, if a jump table is used, objtool has to correlate the jump instruction with the table. On x86 (which is the only port supported by objtool before LoongArch), there is a relocation type on the jump instruction and directly points to the table. But on LoongArch, the relocation is on another kind of instruction prior to the jump instruction, and also with scheduling it is not very easy to tell the offset of that instruction from the jump instruction. Furthermore, because LoongArch has -fsection-anchors (often enabled at -O1 or above) the relocation may actually points to a section anchor instead of the table itself. The good news is that after continuous analysis and discussion, at last a GCC patch "LoongArch: Add support to annotate tablejump" and a Clang patch "[LoongArch] Add options for annotate tablejump" have been merged into the upstream mainline, the compiler changes make life much easier for switch table support of objtool on LoongArch. By now, there is an additional section ".discard.tablejump_annotate" to store the jump info as pairs of addresses, each pair contains the address of jump instruction and the address of jump table. In order to find switch table, it is easy to parse the relocation section ".rela.discard.tablejump_annotate" to get table_sec and table_offset, the rest process is somehow like x86. Additionally, it needs to get each table size. When compiling on LoongArch, there are unsorted table offsets of rodata if there exist many jump tables, it will get the wrong table end and find the wrong table jump destination instructions in add_jump_table(). Sort the rodata table offset by parsing ".rela.discard.tablejump_annotate" and then get each table size of rodata corresponded with each table jump instruction, it is used to check the table end and will break the process when parsing ".rela.rodata" to avoid getting the wrong jump destination instructions. Link: https://gcc.gnu.org/git/?p=gcc.git;a=commit;h=0ee028f55640 Link: https://github.com/llvm/llvm-project/commit/4c2c17756739 Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/20250211115016.26913-5-yangtiezhu@loongson.cn Acked-by: Huacai Chen Signed-off-by: Josh Poimboeuf --- tools/objtool/arch/loongarch/special.c | 131 ++++++++++++++++++++++++- 1 file changed, 130 insertions(+), 1 deletion(-) diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c index 87230ed570fd..4fa6877d5b2b 100644 --- a/tools/objtool/arch/loongarch/special.c +++ b/tools/objtool/arch/loongarch/special.c @@ -1,5 +1,7 @@ // SPDX-License-Identifier: GPL-2.0-or-later +#include #include +#include bool arch_support_alt_relocation(struct special_alt *special_alt, struct instruction *insn, @@ -8,9 +10,136 @@ bool arch_support_alt_relocation(struct special_alt *special_alt, return false; } +struct table_info { + struct list_head jump_info; + unsigned long insn_offset; + unsigned long rodata_offset; +}; + +static void get_rodata_table_size_by_table_annotate(struct objtool_file *file, + struct instruction *insn, + unsigned long *table_size) +{ + struct section *rsec; + struct reloc *reloc; + struct list_head table_list; + struct table_info *orig_table; + struct table_info *next_table; + unsigned long tmp_insn_offset; + unsigned long tmp_rodata_offset; + + rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); + if (!rsec) + return; + + INIT_LIST_HEAD(&table_list); + + for_each_reloc(rsec, reloc) { + orig_table = malloc(sizeof(struct table_info)); + if (!orig_table) { + WARN("malloc failed"); + return; + } + + orig_table->insn_offset = reloc->sym->offset + reloc_addend(reloc); + reloc++; + orig_table->rodata_offset = reloc->sym->offset + reloc_addend(reloc); + + list_add_tail(&orig_table->jump_info, &table_list); + + if (reloc_idx(reloc) + 1 == sec_num_entries(rsec)) + break; + } + + list_for_each_entry(orig_table, &table_list, jump_info) { + next_table = list_next_entry(orig_table, jump_info); + list_for_each_entry_from(next_table, &table_list, jump_info) { + if (next_table->rodata_offset < orig_table->rodata_offset) { + tmp_insn_offset = next_table->insn_offset; + tmp_rodata_offset = next_table->rodata_offset; + next_table->insn_offset = orig_table->insn_offset; + next_table->rodata_offset = orig_table->rodata_offset; + orig_table->insn_offset = tmp_insn_offset; + orig_table->rodata_offset = tmp_rodata_offset; + } + } + } + + list_for_each_entry(orig_table, &table_list, jump_info) { + if (insn->offset == orig_table->insn_offset) { + next_table = list_next_entry(orig_table, jump_info); + if (&next_table->jump_info == &table_list) { + *table_size = 0; + return; + } + + while (next_table->rodata_offset == orig_table->rodata_offset) { + next_table = list_next_entry(next_table, jump_info); + if (&next_table->jump_info == &table_list) { + *table_size = 0; + return; + } + } + + *table_size = next_table->rodata_offset - orig_table->rodata_offset; + } + } +} + +static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file, + struct instruction *insn, + unsigned long *table_size) +{ + struct section *rsec; + struct reloc *reloc; + unsigned long offset; + + rsec = find_section_by_name(file->elf, ".rela.discard.tablejump_annotate"); + if (!rsec) + return NULL; + + for_each_reloc(rsec, reloc) { + if (reloc->sym->sec->rodata) + continue; + + if (strcmp(insn->sec->name, reloc->sym->sec->name)) + continue; + + offset = reloc->sym->offset + reloc_addend(reloc); + if (insn->offset == offset) { + get_rodata_table_size_by_table_annotate(file, insn, table_size); + reloc++; + return reloc; + } + } + + return NULL; +} + struct reloc *arch_find_switch_table(struct objtool_file *file, struct instruction *insn, unsigned long *table_size) { - return NULL; + struct reloc *annotate_reloc; + struct reloc *rodata_reloc; + struct section *table_sec; + unsigned long table_offset; + + annotate_reloc = find_reloc_by_table_annotate(file, insn, table_size); + if (!annotate_reloc) + return NULL; + + table_sec = annotate_reloc->sym->sec; + table_offset = annotate_reloc->sym->offset + reloc_addend(annotate_reloc); + + /* + * Each table entry has a rela associated with it. The rela + * should reference text in the same function as the original + * instruction. + */ + rodata_reloc = find_reloc_by_dest(file->elf, table_sec, table_offset); + if (!rodata_reloc) + return NULL; + + return rodata_reloc; } From 88cbb468d4545526a33126f8a41352cac6dd0f3c Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 11 Feb 2025 19:50:14 +0800 Subject: [PATCH 06/22] objtool/LoongArch: Add support for goto table The objtool program need to analysis the control flow of each object file generated by compiler toolchain, it needs to know all the locations that a branch instruction may jump into, if a jump table is used, objtool has to correlate the jump instruction with the table. On x86 (which is the only port supported by objtool before LoongArch), there is a relocation type on the jump instruction and directly points to the table. But on LoongArch, the relocation is on another kind of instruction prior to the jump instruction, and also with scheduling it is not very easy to tell the offset of that instruction from the jump instruction. Furthermore, because LoongArch has -fsection-anchors (often enabled at -O1 or above) the relocation may actually points to a section anchor instead of the table itself. For the jump table of switch cases, a GCC patch "LoongArch: Add support to annotate tablejump" and a Clang patch "[LoongArch] Add options for annotate tablejump" have been merged into the upstream mainline, it can parse the additional section ".discard.tablejump_annotate" which stores the jump info as pairs of addresses, each pair contains the address of jump instruction and the address of jump table. For the jump table of computed gotos, it is indeed not easy to implement in the compiler, especially if there is more than one computed goto in a function such as ___bpf_prog_run(). objdump kernel/bpf/core.o shows that there are many table jump instructions in ___bpf_prog_run(), but there are no relocations on the table jump instructions and to the table directly on LoongArch. Without the help of compiler, in order to figure out the address of goto table for the special case of ___bpf_prog_run(), since the instruction sequence is relatively single and stable, it makes sense to add a helper find_reloc_of_rodata_c_jump_table() to find the relocation which points to the section ".rodata..c_jump_table". If find_reloc_by_table_annotate() failed, it means there is no relocation info of switch table address in ".rela.discard.tablejump_annotate", then objtool may find the relocation info of goto table ".rodata..c_jump_table" with find_reloc_of_rodata_c_jump_table(). Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/20250211115016.26913-6-yangtiezhu@loongson.cn Acked-by: Huacai Chen Signed-off-by: Josh Poimboeuf --- tools/objtool/arch/loongarch/special.c | 32 ++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c index 4fa6877d5b2b..e39f86d97002 100644 --- a/tools/objtool/arch/loongarch/special.c +++ b/tools/objtool/arch/loongarch/special.c @@ -116,6 +116,30 @@ static struct reloc *find_reloc_by_table_annotate(struct objtool_file *file, return NULL; } +static struct reloc *find_reloc_of_rodata_c_jump_table(struct section *sec, + unsigned long offset, + unsigned long *table_size) +{ + struct section *rsec; + struct reloc *reloc; + + rsec = sec->rsec; + if (!rsec) + return NULL; + + for_each_reloc(rsec, reloc) { + if (reloc_offset(reloc) > offset) + break; + + if (!strcmp(reloc->sym->sec->name, C_JUMP_TABLE_SECTION)) { + *table_size = 0; + return reloc; + } + } + + return NULL; +} + struct reloc *arch_find_switch_table(struct objtool_file *file, struct instruction *insn, unsigned long *table_size) @@ -126,8 +150,12 @@ struct reloc *arch_find_switch_table(struct objtool_file *file, unsigned long table_offset; annotate_reloc = find_reloc_by_table_annotate(file, insn, table_size); - if (!annotate_reloc) - return NULL; + if (!annotate_reloc) { + annotate_reloc = find_reloc_of_rodata_c_jump_table( + insn->sec, insn->offset, table_size); + if (!annotate_reloc) + return NULL; + } table_sec = annotate_reloc->sym->sec; table_offset = annotate_reloc->sym->offset + reloc_addend(annotate_reloc); From e20ab7d454ee8d1e0e8b9ff73a7c87e84c666b2f Mon Sep 17 00:00:00 2001 From: Tiezhu Yang Date: Tue, 17 Dec 2024 09:09:03 +0800 Subject: [PATCH 07/22] LoongArch: Enable jump table for objtool For now, it is time to remove -fno-jump-tables to enable jump table for objtool if the compiler has -mannotate-tablejump, otherwise it is better to remain -fno-jump-tables to keep compatibility with older compilers. Signed-off-by: Tiezhu Yang Link: https://lore.kernel.org/r/20241217010905.13054-8-yangtiezhu@loongson.cn Acked-by: Huacai Chen Signed-off-by: Josh Poimboeuf --- arch/loongarch/Kconfig | 3 +++ arch/loongarch/Makefile | 6 +++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/loongarch/Kconfig b/arch/loongarch/Kconfig index 2b8bd27a852f..15aaa2e6757e 100644 --- a/arch/loongarch/Kconfig +++ b/arch/loongarch/Kconfig @@ -291,6 +291,9 @@ config AS_HAS_LBT_EXTENSION config AS_HAS_LVZ_EXTENSION def_bool $(as-instr,hvcl 0) +config CC_HAS_ANNOTATE_TABLEJUMP + def_bool $(cc-option,-mannotate-tablejump) + menu "Kernel type and options" source "kernel/Kconfig.hz" diff --git a/arch/loongarch/Makefile b/arch/loongarch/Makefile index 567bd122a9ee..0304eabbe606 100644 --- a/arch/loongarch/Makefile +++ b/arch/loongarch/Makefile @@ -101,7 +101,11 @@ KBUILD_AFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma) KBUILD_CFLAGS += $(call cc-option,-mthin-add-sub) $(call cc-option,-Wa$(comma)-mthin-add-sub) ifdef CONFIG_OBJTOOL -KBUILD_CFLAGS += -fno-jump-tables +ifdef CONFIG_CC_HAS_ANNOTATE_TABLEJUMP +KBUILD_CFLAGS += -mannotate-tablejump +else +KBUILD_CFLAGS += -fno-jump-tables # keep compatibility with older compilers +endif endif KBUILD_RUSTFLAGS += --target=loongarch64-unknown-none-softfloat -Ccode-model=small From 8085fcd78c1a3dbdf2278732579009d41ce0bc4e Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:28:59 -0700 Subject: [PATCH 08/22] x86/traps: Make exc_double_fault() consistently noreturn The CONFIG_X86_ESPFIX64 version of exc_double_fault() can return to its caller, but the !CONFIG_X86_ESPFIX64 version never does. In the latter case the compiler and/or objtool may consider it to be implicitly noreturn. However, due to the currently inflexible way objtool detects noreturns, a function's noreturn status needs to be consistent across configs. The current workaround for this issue is to suppress unreachable warnings for exc_double_fault()'s callers. Unfortunately that can result in ORC coverage gaps and potentially worse issues like inert static calls and silently disabled CPU mitigations. Instead, prevent exc_double_fault() from ever being implicitly marked noreturn by forcing a return behind a never-taken conditional. Until a more integrated noreturn detection method exists, this is likely the least objectionable workaround. Fixes: 55eeab2a8a11 ("objtool: Ignore exc_double_fault() __noreturn warnings") Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Brendan Jackman Link: https://lore.kernel.org/r/d1f4026f8dc35d0de6cc61f2684e0cb6484009d1.1741975349.git.jpoimboe@kernel.org --- arch/x86/kernel/traps.c | 18 +++++++++++++++++- tools/objtool/check.c | 31 +------------------------------ 2 files changed, 18 insertions(+), 31 deletions(-) diff --git a/arch/x86/kernel/traps.c b/arch/x86/kernel/traps.c index 2dbadf347b5f..5e3e036e6e53 100644 --- a/arch/x86/kernel/traps.c +++ b/arch/x86/kernel/traps.c @@ -379,6 +379,21 @@ __visible void __noreturn handle_stack_overflow(struct pt_regs *regs, } #endif +/* + * Prevent the compiler and/or objtool from marking the !CONFIG_X86_ESPFIX64 + * version of exc_double_fault() as noreturn. Otherwise the noreturn mismatch + * between configs triggers objtool warnings. + * + * This is a temporary hack until we have compiler or plugin support for + * annotating noreturns. + */ +#ifdef CONFIG_X86_ESPFIX64 +#define always_true() true +#else +bool always_true(void); +bool __weak always_true(void) { return true; } +#endif + /* * Runs on an IST stack for x86_64 and on a special task stack for x86_32. * @@ -514,7 +529,8 @@ DEFINE_IDTENTRY_DF(exc_double_fault) pr_emerg("PANIC: double fault, error_code: 0x%lx\n", error_code); die("double fault", regs, error_code); - panic("Machine halted."); + if (always_true()) + panic("Machine halted."); instrumentation_end(); } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 7dbf22c6da9d..12bf6c1f5071 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4460,35 +4460,6 @@ static int validate_sls(struct objtool_file *file) return warnings; } -static bool ignore_noreturn_call(struct instruction *insn) -{ - struct symbol *call_dest = insn_call_dest(insn); - - /* - * FIXME: hack, we need a real noreturn solution - * - * Problem is, exc_double_fault() may or may not return, depending on - * whether CONFIG_X86_ESPFIX64 is set. But objtool has no visibility - * to the kernel config. - * - * Other potential ways to fix it: - * - * - have compiler communicate __noreturn functions somehow - * - remove CONFIG_X86_ESPFIX64 - * - read the .config file - * - add a cmdline option - * - create a generic objtool annotation format (vs a bunch of custom - * formats) and annotate it - */ - if (!strcmp(call_dest->name, "exc_double_fault")) { - /* prevent further unreachable warnings for the caller */ - insn->sym->warned = 1; - return true; - } - - return false; -} - static int validate_reachable_instructions(struct objtool_file *file) { struct instruction *insn, *prev_insn; @@ -4505,7 +4476,7 @@ static int validate_reachable_instructions(struct objtool_file *file) prev_insn = prev_insn_same_sec(file, insn); if (prev_insn && prev_insn->dead_end) { call_dest = insn_call_dest(prev_insn); - if (call_dest && !ignore_noreturn_call(prev_insn)) { + if (call_dest) { WARN_INSN(insn, "%s() is missing a __noreturn annotation", call_dest->name); warnings++; From b745962cb97569aad026806bb0740663cf813147 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:00 -0700 Subject: [PATCH 09/22] objtool: Fix error handling inconsistencies in check() Make sure all fatal errors are funneled through the 'out' label with a negative ret. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Brendan Jackman Link: https://lore.kernel.org/r/0f49d6a27a080b4012e84e6df1e23097f44cc082.1741975349.git.jpoimboe@kernel.org --- tools/objtool/check.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 12bf6c1f5071..e68a89efc5fe 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4605,8 +4605,10 @@ int check(struct objtool_file *file) init_cfi_state(&force_undefined_cfi); force_undefined_cfi.force_undefined = true; - if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) + if (!cfi_hash_alloc(1UL << (file->elf->symbol_bits - 3))) { + ret = -1; goto out; + } cfi_hash_add(&init_cfi); cfi_hash_add(&func_cfi); @@ -4623,7 +4625,7 @@ int check(struct objtool_file *file) if (opts.retpoline) { ret = validate_retpoline(file); if (ret < 0) - return ret; + goto out; warnings += ret; } @@ -4659,7 +4661,7 @@ int check(struct objtool_file *file) */ ret = validate_unrets(file); if (ret < 0) - return ret; + goto out; warnings += ret; } @@ -4722,7 +4724,7 @@ int check(struct objtool_file *file) if (opts.prefix) { ret = add_prefix_symbols(file); if (ret < 0) - return ret; + goto out; warnings += ret; } From acae6b5bfffedc0440837c52584696dadb2fa334 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:01 -0700 Subject: [PATCH 10/22] objtool: Improve __noreturn annotation warning Clarify what needs to be done to resolve the missing __noreturn warning. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/ab835a35d00bacf8aff0b56257df93f14fdd8224.1741975349.git.jpoimboe@kernel.org --- tools/objtool/Documentation/objtool.txt | 12 +++++------- tools/objtool/check.c | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 7c3ee959b63c..87950a7aaa17 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -319,14 +319,12 @@ the objtool maintainers. a just a bad person, you can tell objtool to ignore it. See the "Adding exceptions" section below. - If it's not actually in a callable function (e.g. kernel entry code), - change ENDPROC to END. +3. file.o: warning: objtool: foo+0x48c: bar() missing __noreturn in .c/.h or NORETURN() in noreturns.h -3. file.o: warning: objtool: foo+0x48c: bar() is missing a __noreturn annotation - - The call from foo() to bar() doesn't return, but bar() is missing the - __noreturn annotation. NOTE: In addition to annotating the function - with __noreturn, please also add it to tools/objtool/noreturns.h. + The call from foo() to bar() doesn't return, but bar() is incorrectly + annotated. A noreturn function must be marked __noreturn in both its + declaration and its definition, and must have a NORETURN() annotation + in tools/objtool/noreturns.h. 4. file.o: warning: objtool: func(): can't find starting instruction or diff --git a/tools/objtool/check.c b/tools/objtool/check.c index e68a89efc5fe..d6af5385baf6 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4477,7 +4477,7 @@ static int validate_reachable_instructions(struct objtool_file *file) if (prev_insn && prev_insn->dead_end) { call_dest = insn_call_dest(prev_insn); if (call_dest) { - WARN_INSN(insn, "%s() is missing a __noreturn annotation", + WARN_INSN(insn, "%s() missing __noreturn in .c/.h or NORETURN() in noreturns.h", call_dest->name); warnings++; continue; From dd95beba97b61ed53e6c96b5a43fbc9edf07c033 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:02 -0700 Subject: [PATCH 11/22] objtool: Update documentation Fix some outdated information in the objtool doc. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/2552ee8b48631127bf269359647a7389edf5f002.1741975349.git.jpoimboe@kernel.org --- tools/objtool/Documentation/objtool.txt | 95 ++++++++++++++----------- 1 file changed, 53 insertions(+), 42 deletions(-) diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 87950a7aaa17..28ac57b9e102 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -28,6 +28,15 @@ Objtool has the following features: sites, enabling the kernel to patch them inline, to prevent "thunk funneling" for both security and performance reasons +- Return thunk validation -- validates return thunks are used for + certain CPU mitigations including Retbleed and SRSO + +- Return thunk annotation -- annotates all return thunk sites so kernel + can patch them inline, depending on enabled mitigations + +- Return thunk training valiation -- validate that all entry paths + untrain a "safe return" before the first return (or call) + - Non-instrumentation validation -- validates non-instrumentable ("noinstr") code rules, preventing instrumentation in low-level C entry code @@ -53,6 +62,9 @@ Objtool has the following features: - Function entry annotation -- annotates function entries, enabling kernel function tracing +- Function preamble (prefix) annotation and/or symbol generation -- used + for FineIBT and call depth tracking + - Other toolchain hacks which will go unmentioned at this time... Each feature can be enabled individually or in combination using the @@ -197,19 +209,17 @@ To achieve the validation, objtool enforces the following rules: 1. Each callable function must be annotated as such with the ELF function type. In asm code, this is typically done using the - ENTRY/ENDPROC macros. If objtool finds a return instruction + SYM_FUNC_{START,END} macros. If objtool finds a return instruction outside of a function, it flags an error since that usually indicates callable code which should be annotated accordingly. This rule is needed so that objtool can properly identify each callable function in order to analyze its stack metadata. -2. Conversely, each section of code which is *not* callable should *not* - be annotated as an ELF function. The ENDPROC macro shouldn't be used - in this case. - - This rule is needed so that objtool can ignore non-callable code. - Such code doesn't have to follow any of the other rules. +2. Conversely, each section of code which is *not* callable, or is + otherwise doing funny things with the stack or registers, should + *not* be annotated as an ELF function. Rather, SYM_CODE_{START,END} + should be used along with unwind hints. 3. Each callable function which calls another function must have the correct frame pointer logic, if required by CONFIG_FRAME_POINTER or @@ -221,7 +231,7 @@ To achieve the validation, objtool enforces the following rules: function B, the _caller_ of function A will be skipped on the stack trace. -4. Dynamic jumps and jumps to undefined symbols are only allowed if: +4. Indirect jumps and jumps to undefined symbols are only allowed if: a) the jump is part of a switch statement; or @@ -304,20 +314,21 @@ the objtool maintainers. 001e 2823e: 80 ce 02 or $0x2,%dh ... + 2. file.o: warning: objtool: .text+0x53: unreachable instruction Objtool couldn't find a code path to reach the instruction. If the error is for an asm file, and the instruction is inside (or reachable from) a callable function, the function should be annotated - with the ENTRY/ENDPROC macros (ENDPROC is the important one). - Otherwise, the code should probably be annotated with the unwind hint - macros in asm/unwind_hints.h so objtool and the unwinder can know the - stack state associated with the code. + with the SYM_FUNC_START and SYM_FUNC_END macros. + + Otherwise, SYM_CODE_START can be used. In that case the code needs + to be annotated with unwind hint macros. + + If you're sure the code won't affect the reliability of runtime stack + traces and want objtool to ignore it, see "Adding exceptions" below. - If you're 100% sure the code won't affect stack traces, or if you're - a just a bad person, you can tell objtool to ignore it. See the - "Adding exceptions" section below. 3. file.o: warning: objtool: foo+0x48c: bar() missing __noreturn in .c/.h or NORETURN() in noreturns.h @@ -326,6 +337,7 @@ the objtool maintainers. declaration and its definition, and must have a NORETURN() annotation in tools/objtool/noreturns.h. + 4. file.o: warning: objtool: func(): can't find starting instruction or file.o: warning: objtool: func()+0x11dd: can't decode instruction @@ -339,23 +351,21 @@ the objtool maintainers. This is a kernel entry/exit instruction like sysenter or iret. Such instructions aren't allowed in a callable function, and are most - likely part of the kernel entry code. They should usually not have - the callable function annotation (ENDPROC) and should always be - annotated with the unwind hint macros in asm/unwind_hints.h. + likely part of the kernel entry code. Such code should probably be + placed in a SYM_FUNC_CODE block with unwind hints. 6. file.o: warning: objtool: func()+0x26: sibling call from callable instruction with modified stack frame - This is a dynamic jump or a jump to an undefined symbol. Objtool - assumed it's a sibling call and detected that the frame pointer - wasn't first restored to its original state. + This is a branch to an UNDEF symbol. Objtool assumed it's a + sibling call and detected that the stack wasn't first restored to its + original state. - If it's not really a sibling call, you may need to move the - destination code to the local file. + If it's not really a sibling call, you may need to use unwind hints + and/or move the destination code to the local file. If the instruction is not actually in a callable function (e.g. - kernel entry code), change ENDPROC to END and annotate manually with - the unwind hint macros in asm/unwind_hints.h. + kernel entry code), use SYM_CODE_{START,END} and unwind hints. 7. file: warning: objtool: func()+0x5c: stack state mismatch @@ -371,8 +381,8 @@ the objtool maintainers. Another possibility is that the code has some asm or inline asm which does some unusual things to the stack or the frame pointer. In such - cases it's probably appropriate to use the unwind hint macros in - asm/unwind_hints.h. + cases it's probably appropriate to use SYM_FUNC_CODE with unwind + hints. 8. file.o: warning: objtool: funcA() falls through to next function funcB() @@ -382,17 +392,16 @@ the objtool maintainers. can fall through into the next function. There could be different reasons for this: - 1) funcA()'s last instruction is a call to a "noreturn" function like + a) funcA()'s last instruction is a call to a "noreturn" function like panic(). In this case the noreturn function needs to be added to objtool's hard-coded global_noreturns array. Feel free to bug the objtool maintainer, or you can submit a patch. - 2) funcA() uses the unreachable() annotation in a section of code + b) funcA() uses the unreachable() annotation in a section of code that is actually reachable. - 3) If funcA() calls an inline function, the object code for funcA() - might be corrupt due to a gcc bug. For more details, see: - https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70646 + c) Some undefined behavior like divide by zero. + 9. file.o: warning: objtool: funcA() call to funcB() with UACCESS enabled @@ -430,24 +439,26 @@ the objtool maintainers. This limitation can be overcome by massaging the alternatives with NOPs to shift the stack changes around so they no longer conflict. + 11. file.o: warning: unannotated intra-function call - This warning means that a direct call is done to a destination which - is not at the beginning of a function. If this is a legit call, you - can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL - directive right before the call. + This warning means that a direct call is done to a destination which + is not at the beginning of a function. If this is a legit call, you + can remove this warning by putting the ANNOTATE_INTRA_FUNCTION_CALL + directive right before the call. + 12. file.o: warning: func(): not an indirect call target - This means that objtool is running with --ibt and a function expected - to be an indirect call target is not. In particular, this happens for - init_module() or cleanup_module() if a module relies on these special - names and does not use module_init() / module_exit() macros to create - them. + This means that objtool is running with --ibt and a function + expected to be an indirect call target is not. In particular, this + happens for init_module() or cleanup_module() if a module relies on + these special names and does not use module_init() / module_exit() + macros to create them. If the error doesn't seem to make sense, it could be a bug in objtool. -Feel free to ask the objtool maintainer for help. +Feel free to ask objtool maintainers for help. Adding exceptions From 0a7fb6f07e3ad497d31ae9a2082d2cacab43d54a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:03 -0700 Subject: [PATCH 12/22] objtool: Increase per-function WARN_FUNC() rate limit Increase the per-function WARN_FUNC() rate limit from 1 to 2. If the number of warnings for a given function goes beyond 2, print "skipping duplicate warning(s)". This helps root out additional warnings in a function that might be hiding behind the first one. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/aec318d66c037a51c9f376d6fb0e8ff32812a037.1741975349.git.jpoimboe@kernel.org --- tools/objtool/check.c | 2 +- tools/objtool/include/objtool/elf.h | 2 +- tools/objtool/include/objtool/warn.h | 14 +++++++++++--- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index d6af5385baf6..2f64e46fe02d 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4547,7 +4547,7 @@ static int disas_warned_funcs(struct objtool_file *file) char *funcs = NULL, *tmp; for_each_sym(file, sym) { - if (sym->warned) { + if (sym->warnings) { if (!funcs) { funcs = malloc(strlen(sym->name) + 1); strcpy(funcs, sym->name); diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index d7e815c2fd15..223ac1c24b90 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -65,10 +65,10 @@ struct symbol { u8 return_thunk : 1; u8 fentry : 1; u8 profiling_func : 1; - u8 warned : 1; u8 embedded_insn : 1; u8 local_label : 1; u8 frame_pointer : 1; + u8 warnings : 2; struct list_head pv_target; struct reloc *relocs; }; diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index ac04d3fe4dd9..6180288927fd 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -53,14 +53,22 @@ static inline char *offstr(struct section *sec, unsigned long offset) free(_str); \ }) +#define WARN_LIMIT 2 + #define WARN_INSN(insn, format, ...) \ ({ \ struct instruction *_insn = (insn); \ - if (!_insn->sym || !_insn->sym->warned) \ + BUILD_BUG_ON(WARN_LIMIT > 2); \ + if (!_insn->sym || _insn->sym->warnings < WARN_LIMIT) { \ WARN_FUNC(format, _insn->sec, _insn->offset, \ ##__VA_ARGS__); \ - if (_insn->sym) \ - _insn->sym->warned = 1; \ + if (_insn->sym) \ + _insn->sym->warnings++; \ + } else if (_insn->sym && _insn->sym->warnings == WARN_LIMIT) { \ + WARN_FUNC("skipping duplicate warning(s)", \ + _insn->sec, _insn->offset); \ + _insn->sym->warnings++; \ + } \ }) #define BT_INSN(insn, format, ...) \ From 764d956145f21a0297004e9c67d7d60bde14e709 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:04 -0700 Subject: [PATCH 13/22] objtool: Remove --unret dependency on --rethunk With unret validation enabled and IBT/LTO disabled, objtool runs on TUs with --rethunk and on vmlinux.o with --unret. So this dependency isn't valid as they don't always run on the same object. This error never triggered before because --unret is always coupled with --noinstr, so the first conditional in opts_valid() returns early due to opts.noinstr being true. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/c6f5635784a28ed4b10ac4307b1858e015e6eff0.1741975349.git.jpoimboe@kernel.org --- tools/objtool/builtin-check.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 387d56a7f5fb..c7275cf7641b 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -151,11 +151,6 @@ static bool opts_valid(void) return true; } - if (opts.unret && !opts.rethunk) { - ERROR("--unret requires --rethunk"); - return false; - } - if (opts.dump_orc) return true; From acc8c6a798a011a5fe37b455b0286a85a4164b47 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:05 -0700 Subject: [PATCH 14/22] objtool: Consolidate option validation The option validations are a bit scattered, consolidate them. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Brendan Jackman Link: https://lore.kernel.org/r/8f886502fda1d15f39d7351b70d4ebe5903da627.1741975349.git.jpoimboe@kernel.org --- tools/objtool/builtin-check.c | 68 +++++++++++++---------------------- 1 file changed, 24 insertions(+), 44 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index c7275cf7641b..36d81a455b01 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -131,6 +131,26 @@ int cmd_parse_options(int argc, const char **argv, const char * const usage[]) static bool opts_valid(void) { + if (opts.mnop && !opts.mcount) { + ERROR("--mnop requires --mcount"); + return false; + } + + if (opts.noinstr && !opts.link) { + ERROR("--noinstr requires --link"); + return false; + } + + if (opts.ibt && !opts.link) { + ERROR("--ibt requires --link"); + return false; + } + + if (opts.unret && !opts.link) { + ERROR("--unret requires --link"); + return false; + } + if (opts.hack_jump_label || opts.hack_noinstr || opts.ibt || @@ -158,45 +178,6 @@ static bool opts_valid(void) return false; } -static bool mnop_opts_valid(void) -{ - if (opts.mnop && !opts.mcount) { - ERROR("--mnop requires --mcount"); - return false; - } - - return true; -} - -static bool link_opts_valid(struct objtool_file *file) -{ - if (opts.link) - return true; - - if (has_multiple_files(file->elf)) { - ERROR("Linked object detected, forcing --link"); - opts.link = true; - return true; - } - - if (opts.noinstr) { - ERROR("--noinstr requires --link"); - return false; - } - - if (opts.ibt) { - ERROR("--ibt requires --link"); - return false; - } - - if (opts.unret) { - ERROR("--unret requires --link"); - return false; - } - - return true; -} - int objtool_run(int argc, const char **argv) { const char *objname; @@ -216,11 +197,10 @@ int objtool_run(int argc, const char **argv) if (!file) return 1; - if (!mnop_opts_valid()) - return 1; - - if (!link_opts_valid(file)) - return 1; + if (!opts.link && has_multiple_files(file->elf)) { + ERROR("Linked object detected, forcing --link"); + opts.link = true; + } ret = check(file); if (ret) From fdf5ff2934f4c5c6b483c906fea6e0288df36da2 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:06 -0700 Subject: [PATCH 15/22] objtool: Upgrade "Linked object detected" warning to error Force the user to fix their cmdline if they forget the '--link' option. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Brendan Jackman Link: https://lore.kernel.org/r/8380bbf3a0fa86e03fd63f60568ae06a48146bc1.1741975349.git.jpoimboe@kernel.org --- tools/objtool/builtin-check.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 36d81a455b01..79843512a51b 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -198,8 +198,8 @@ int objtool_run(int argc, const char **argv) return 1; if (!opts.link && has_multiple_files(file->elf)) { - ERROR("Linked object detected, forcing --link"); - opts.link = true; + ERROR("Linked object requires --link"); + goto err; } ret = check(file); From 5a406031d0719d146d2033ee4270310b1ca9a1e3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:07 -0700 Subject: [PATCH 16/22] objtool: Add --output option Add option to allow writing the changed binary to a separate file rather than changing it in place. Libelf makes this suprisingly hard, so take the easy way out and just copy the file before editing it. Also steal the -o short option from --orc. Nobody will notice ;-) Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/0da308d42d82b3bbed16a31a72d6bde52afcd6bd.1741975349.git.jpoimboe@kernel.org --- tools/objtool/builtin-check.c | 98 ++++++++++++++++++++----- tools/objtool/elf.c | 3 - tools/objtool/include/objtool/builtin.h | 1 + tools/objtool/objtool.c | 15 ++-- tools/objtool/orc_dump.c | 7 +- 5 files changed, 89 insertions(+), 35 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 79843512a51b..3de3afa0a19c 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -6,6 +6,10 @@ #include #include #include +#include +#include +#include +#include #include #include @@ -14,6 +18,8 @@ "error: objtool: " format "\n", \ ##__VA_ARGS__) +const char *objname; + struct opts opts; static const char * const check_usage[] = { @@ -71,7 +77,7 @@ static const struct option check_options[] = { 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('o', "orc", &opts.orc, "generate ORC metadata"), + 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"), @@ -84,15 +90,16 @@ static const struct option check_options[] = { 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, "backup", &opts.backup, "create .orig files before modification"), - 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_BOOLEAN(0, "sec-address", &opts.sec_address, "print section addresses in warnings"), - OPT_BOOLEAN(0, "stats", &opts.stats, "print statistics"), + OPT_BOOLEAN(0, "backtrace", &opts.backtrace, "unwind on error"), + OPT_BOOLEAN(0, "backup", &opts.backup, "create .orig files before modification"), + 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_END(), @@ -178,24 +185,75 @@ static bool opts_valid(void) return false; } +static int copy_file(const char *src, const char *dst) +{ + size_t to_copy, copied; + int dst_fd, src_fd; + struct stat stat; + off_t offset = 0; + + src_fd = open(src, O_RDONLY); + if (src_fd == -1) { + ERROR("can't open '%s' for reading", src); + return 1; + } + + dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC); + if (dst_fd == -1) { + ERROR("can't open '%s' for writing", dst); + return 1; + } + + if (fstat(src_fd, &stat) == -1) { + perror("fstat"); + return 1; + } + + if (fchmod(dst_fd, stat.st_mode) == -1) { + perror("fchmod"); + return 1; + } + + for (to_copy = stat.st_size; to_copy > 0; to_copy -= copied) { + copied = sendfile(dst_fd, src_fd, &offset, to_copy); + if (copied == -1) { + perror("sendfile"); + return 1; + } + } + + close(dst_fd); + close(src_fd); + return 0; +} + int objtool_run(int argc, const char **argv) { - const char *objname; struct objtool_file *file; int ret; - argc = cmd_parse_options(argc, argv, check_usage); - objname = argv[0]; + cmd_parse_options(argc, argv, check_usage); if (!opts_valid()) return 1; + objname = argv[0]; + if (opts.dump_orc) return orc_dump(objname); + if (!opts.dryrun && opts.output) { + /* copy original .o file to output file */ + if (copy_file(objname, opts.output)) + return 1; + + /* from here on, work directly on the output file */ + objname = opts.output; + } + file = objtool_open_read(objname); if (!file) - return 1; + goto err; if (!opts.link && has_multiple_files(file->elf)) { ERROR("Linked object requires --link"); @@ -204,10 +262,16 @@ int objtool_run(int argc, const char **argv) ret = check(file); if (ret) - return ret; + goto err; - if (file->elf->changed) - return elf_write(file->elf); + if (!opts.dryrun && file->elf->changed && elf_write(file->elf)) + goto err; return 0; + +err: + if (opts.output) + unlink(opts.output); + + return 1; } diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 6f64d611faea..be4f4b62730c 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -1302,9 +1302,6 @@ int elf_write(struct elf *elf) struct section *sec; Elf_Scn *s; - if (opts.dryrun) - return 0; - /* Update changed relocation sections and section headers: */ list_for_each_entry(sec, &elf->sections, list) { if (sec->truncate) diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index fcca6662c8b4..25cfa01758b9 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -35,6 +35,7 @@ struct opts { bool mnop; bool module; bool no_unreachable; + const char *output; bool sec_address; bool stats; bool verbose; diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index f40febdd6e36..53cd881c6b95 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -18,7 +18,6 @@ bool help; -const char *objname; static struct objtool_file file; static bool objtool_create_backup(const char *_objname) @@ -79,18 +78,14 @@ static bool objtool_create_backup(const char *_objname) return true; } -struct objtool_file *objtool_open_read(const char *_objname) +struct objtool_file *objtool_open_read(const char *filename) { - if (objname) { - if (strcmp(objname, _objname)) { - WARN("won't handle more than one file at a time"); - return NULL; - } - return &file; + if (file.elf) { + WARN("won't handle more than one file at a time"); + return NULL; } - objname = _objname; - file.elf = elf_open_read(objname, O_RDWR); + file.elf = elf_open_read(filename, O_RDWR); if (!file.elf) return NULL; diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c index a62247efb64f..05ef0e297837 100644 --- a/tools/objtool/orc_dump.c +++ b/tools/objtool/orc_dump.c @@ -10,7 +10,7 @@ #include #include -int orc_dump(const char *_objname) +int orc_dump(const char *filename) { int fd, nr_entries, i, *orc_ip = NULL, orc_size = 0; struct orc_entry *orc = NULL; @@ -26,12 +26,9 @@ int orc_dump(const char *_objname) Elf_Data *data, *symtab = NULL, *rela_orc_ip = NULL; struct elf dummy_elf = {}; - - objname = _objname; - elf_version(EV_CURRENT); - fd = open(objname, O_RDONLY); + fd = open(filename, O_RDONLY); if (fd == -1) { perror("open"); return -1; From bb62243943dbef4592266e817f4e2e5a96293907 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:08 -0700 Subject: [PATCH 17/22] objtool: Add --Werror option Any objtool warning has the potential of reflecting (or triggering) a major bug in the kernel or compiler which could result in crashing the kernel or breaking the livepatch consistency model. In preparation for failing the build on objtool errors/warnings, add a new --Werror option. [ jpoimboe: commit log, comments, error out on fatal errors too ] Co-developed-by: Brendan Jackman Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/e423ea4ec297f510a108aa6c78b52b9fe30fa8c1.1741975349.git.jpoimboe@kernel.org --- tools/objtool/builtin-check.c | 1 + tools/objtool/check.c | 15 ++++++++++++--- tools/objtool/include/objtool/builtin.h | 1 + 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 3de3afa0a19c..c201650efe49 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -101,6 +101,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_END(), }; diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 2f64e46fe02d..48d7bc5b4736 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -4756,9 +4756,18 @@ int check(struct objtool_file *file) out: /* - * For now, don't fail the kernel build on fatal warnings. These - * errors are still fairly common due to the growing matrix of - * supported toolchains and their recent pace of change. + * CONFIG_OBJTOOL_WERROR upgrades all warnings (and errors) to actual + * errors. + * + * Note that even "fatal" type errors don't actually return an error + * without CONFIG_OBJTOOL_WERROR. That probably needs improved at some + * point. */ + if (opts.werror && (ret || warnings)) { + if (warnings) + WARN("%d warning(s) upgraded to errors", warnings); + return 1; + } + return 0; } diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index 25cfa01758b9..b18f114cdaa4 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -39,6 +39,7 @@ struct opts { bool sec_address; bool stats; bool verbose; + bool werror; }; extern struct opts opts; From a307dd28b1c6655b67b2367663331034ac8da79c Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:09 -0700 Subject: [PATCH 18/22] objtool: Change "warning:" to "error:" for --Werror This is similar to GCC's behavior and makes it more obvious why the build failed. Suggested-by: Nathan Chancellor Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Reviewed-by: Brendan Jackman Link: https://lore.kernel.org/r/56f0565b15b4b4caa9a08953fa9c679dfa973514.1741975349.git.jpoimboe@kernel.org --- tools/objtool/include/objtool/warn.h | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h index 6180288927fd..e72b9d630551 100644 --- a/tools/objtool/include/objtool/warn.h +++ b/tools/objtool/include/objtool/warn.h @@ -43,8 +43,10 @@ static inline char *offstr(struct section *sec, unsigned long offset) #define WARN(format, ...) \ fprintf(stderr, \ - "%s: warning: objtool: " format "\n", \ - objname, ##__VA_ARGS__) + "%s: %s: objtool: " format "\n", \ + objname, \ + opts.werror ? "error" : "warning", \ + ##__VA_ARGS__) #define WARN_FUNC(format, sec, offset, ...) \ ({ \ From aa8b3e64fd397eddd6a627d148a964e4bc2ed9ab Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:10 -0700 Subject: [PATCH 19/22] objtool: Create backup on error and print args Recreating objtool errors can be a manual process. Kbuild removes the object, so it has to be compiled or linked again before running objtool. Then the objtool args need to be reversed engineered. Make that all easier by automatically making a backup of the object file on error, and print a modified version of the args which can be used to recreate. Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/7571e30636359b3e173ce6e122419452bb31882f.1741975349.git.jpoimboe@kernel.org --- tools/objtool/builtin-check.c | 68 +++++++++++++++++++++++-- tools/objtool/include/objtool/builtin.h | 1 - tools/objtool/objtool.c | 63 ----------------------- 3 files changed, 65 insertions(+), 67 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index c201650efe49..39ddca64cb58 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -91,7 +91,6 @@ 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 .orig files before modification"), 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"), @@ -228,10 +227,39 @@ static int copy_file(const char *src, const char *dst) return 0; } +static char **save_argv(int argc, const char **argv) +{ + char **orig_argv; + + orig_argv = calloc(argc, sizeof(char *)); + if (!orig_argv) { + perror("calloc"); + return NULL; + } + + for (int i = 0; i < argc; i++) { + orig_argv[i] = strdup(argv[i]); + if (!orig_argv[i]) { + perror("strdup"); + return NULL; + } + }; + + return orig_argv; +} + +#define ORIG_SUFFIX ".orig" + int objtool_run(int argc, const char **argv) { struct objtool_file *file; - int ret; + char *backup = NULL; + char **orig_argv; + int ret = 0; + + orig_argv = save_argv(argc, argv); + if (!orig_argv) + return 1; cmd_parse_options(argc, argv, check_usage); @@ -271,8 +299,42 @@ int objtool_run(int argc, const char **argv) return 0; err: - if (opts.output) + if (opts.dryrun) + goto err_msg; + + if (opts.output) { unlink(opts.output); + goto err_msg; + } + + /* + * Make a backup before kbuild deletes the file so the error + * can be recreated without recompiling or relinking. + */ + backup = malloc(strlen(objname) + strlen(ORIG_SUFFIX) + 1); + if (!backup) { + perror("malloc"); + return 1; + } + + strcpy(backup, objname); + strcat(backup, ORIG_SUFFIX); + if (copy_file(objname, backup)) + return 1; + +err_msg: + fprintf(stderr, "%s", orig_argv[0]); + + for (int i = 1; i < 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, "\n"); return 1; } diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index b18f114cdaa4..0fafd0f7a209 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -29,7 +29,6 @@ struct opts { /* options: */ bool backtrace; - bool backup; bool dryrun; bool link; bool mnop; diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index 53cd881c6b95..1c73fb62fd57 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -20,64 +20,6 @@ bool help; static struct objtool_file file; -static bool objtool_create_backup(const char *_objname) -{ - int len = strlen(_objname); - char *buf, *base, *name = malloc(len+6); - int s, d, l, t; - - if (!name) { - perror("failed backup name malloc"); - return false; - } - - strcpy(name, _objname); - strcpy(name + len, ".orig"); - - d = open(name, O_CREAT|O_WRONLY|O_TRUNC, 0644); - if (d < 0) { - perror("failed to create backup file"); - return false; - } - - s = open(_objname, O_RDONLY); - if (s < 0) { - perror("failed to open orig file"); - return false; - } - - buf = malloc(4096); - if (!buf) { - perror("failed backup data malloc"); - return false; - } - - while ((l = read(s, buf, 4096)) > 0) { - base = buf; - do { - t = write(d, base, l); - if (t < 0) { - perror("failed backup write"); - return false; - } - base += t; - l -= t; - } while (l); - } - - if (l < 0) { - perror("failed backup read"); - return false; - } - - free(name); - free(buf); - close(d); - close(s); - - return true; -} - struct objtool_file *objtool_open_read(const char *filename) { if (file.elf) { @@ -89,11 +31,6 @@ struct objtool_file *objtool_open_read(const char *filename) if (!file.elf) return NULL; - if (opts.backup && !objtool_create_backup(objname)) { - WARN("can't create backup file"); - return NULL; - } - hash_init(file.insn_hash); INIT_LIST_HEAD(&file.retpoline_call_list); INIT_LIST_HEAD(&file.return_thunk_list); From 36799069b48198e5ce92d99310060c4aecb4b3e3 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 14 Mar 2025 12:29:11 -0700 Subject: [PATCH 20/22] objtool: Add CONFIG_OBJTOOL_WERROR Objtool warnings can be indicative of crashes, broken live patching, or even boot failures. Ignoring them is not recommended. Add CONFIG_OBJTOOL_WERROR to upgrade objtool warnings to errors by enabling the objtool --Werror option. Also set --backtrace to print the branches leading up to the warning, which can help considerably when debugging certain warnings. To avoid breaking bots too badly for now, make it the default for real world builds only (!COMPILE_TEST). Co-developed-by: Brendan Jackman Signed-off-by: Josh Poimboeuf Signed-off-by: Peter Zijlstra (Intel) Link: https://lore.kernel.org/r/3e7c109313ff15da6c80788965cc7450115b0196.1741975349.git.jpoimboe@kernel.org --- lib/Kconfig.debug | 11 +++++++++++ scripts/Makefile.lib | 1 + 2 files changed, 12 insertions(+) diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug index 35796c290ca3..a9709a6db30f 100644 --- a/lib/Kconfig.debug +++ b/lib/Kconfig.debug @@ -545,6 +545,17 @@ config FRAME_POINTER config OBJTOOL bool +config OBJTOOL_WERROR + bool "Upgrade objtool warnings to errors" + depends on OBJTOOL && !COMPILE_TEST + help + Fail the build on objtool warnings. + + Objtool warnings can indicate kernel instability, including boot + failures. This option is highly recommended. + + If unsure, say Y. + config STACK_VALIDATION bool "Compile-time stack metadata validation" depends on HAVE_STACK_VALIDATION && UNWINDER_FRAME_POINTER diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib index cad20f0e66ee..99e281966ba3 100644 --- a/scripts/Makefile.lib +++ b/scripts/Makefile.lib @@ -277,6 +277,7 @@ objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess objtool-args-$(CONFIG_GCOV_KERNEL) += --no-unreachable objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES) +objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror --backtrace objtool-args = $(objtool-args-y) \ $(if $(delay-objtool), --link) \ From 73070466ed3b5e4620e03c159ee12a570b171d08 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Mon, 17 Mar 2025 12:21:57 +0100 Subject: [PATCH 21/22] objtool: Use O_CREAT with explicit mode mask MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Recent Ubuntu enforces 3-argument open() with O_CREAT: CC /home/mingo/tip/tools/objtool/builtin-check.o In file included from /usr/include/fcntl.h:341, from builtin-check.c:9: In function ‘open’, inlined from ‘copy_file’ at builtin-check.c:201:11: /usr/include/x86_64-linux-gnu/bits/fcntl2.h:52:11: error: call to ‘__open_missing_mode’ declared with attribute error: open with O_CREAT or O_TMPFILE in second argument needs 3 arguments 52 | __open_missing_mode (); | ^~~~~~~~~~~~~~~~~~~~~~ Use 0400 as the most restrictive mode for the new file. Signed-off-by: Ingo Molnar Cc: Josh Poimboeuf Cc: Peter Zijlstra Cc: linux-kernel@vger.kernel.org --- tools/objtool/builtin-check.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index 39ddca64cb58..5f761f420b8c 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -198,7 +198,7 @@ static int copy_file(const char *src, const char *dst) return 1; } - dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC); + dst_fd = open(dst, O_WRONLY | O_CREAT | O_TRUNC, 0400); if (dst_fd == -1) { ERROR("can't open '%s' for writing", dst); return 1; From 2cbb20b008dba39893f0e296dc8ca312f40a9a0e Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 21 Mar 2025 12:53:32 -0700 Subject: [PATCH 22/22] tracing: Disable branch profiling in noinstr code CONFIG_TRACE_BRANCH_PROFILING inserts a call to ftrace_likely_update() for each use of likely() or unlikely(). That breaks noinstr rules if the affected function is annotated as noinstr. Disable branch profiling for files with noinstr functions. In addition to some individual files, this also includes the entire arch/x86 subtree, as well as the kernel/entry, drivers/cpuidle, and drivers/idle directories, all of which are noinstr-heavy. Due to the nature of how sched binaries are built by combining multiple .c files into one, branch profiling is disabled more broadly across the sched code than would otherwise be needed. This fixes many warnings like the following: vmlinux.o: warning: objtool: do_syscall_64+0x40: call to ftrace_likely_update() leaves .noinstr.text section vmlinux.o: warning: objtool: __rdgsbase_inactive+0x33: call to ftrace_likely_update() leaves .noinstr.text section vmlinux.o: warning: objtool: handle_bug.isra.0+0x198: call to ftrace_likely_update() leaves .noinstr.text section ... Reported-by: Ingo Molnar Suggested-by: Steven Rostedt Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Acked-by: Thomas Gleixner Cc: Linus Torvalds Link: https://lore.kernel.org/r/fb94fc9303d48a5ed370498f54500cc4c338eb6d.1742586676.git.jpoimboe@kernel.org --- arch/x86/Kbuild | 4 ++++ arch/x86/coco/sev/core.c | 2 -- arch/x86/kernel/head64.c | 2 -- arch/x86/mm/kasan_init_64.c | 1 - arch/x86/mm/mem_encrypt_amd.c | 2 -- arch/x86/mm/mem_encrypt_identity.c | 2 -- drivers/acpi/Makefile | 4 ++++ drivers/cpuidle/Makefile | 3 +++ drivers/idle/Makefile | 5 ++++- kernel/Makefile | 5 +++++ kernel/entry/Makefile | 3 +++ kernel/sched/Makefile | 5 +++++ kernel/time/Makefile | 6 ++++++ lib/Makefile | 5 +++++ 14 files changed, 39 insertions(+), 10 deletions(-) diff --git a/arch/x86/Kbuild b/arch/x86/Kbuild index cf0ad89f5639..f7fb3d88c57b 100644 --- a/arch/x86/Kbuild +++ b/arch/x86/Kbuild @@ -1,4 +1,8 @@ # SPDX-License-Identifier: GPL-2.0 + +# Branch profiling isn't noinstr-safe. Disable it for arch/x86/* +subdir-ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING + obj-$(CONFIG_ARCH_HAS_CC_PLATFORM) += coco/ obj-y += entry/ diff --git a/arch/x86/coco/sev/core.c b/arch/x86/coco/sev/core.c index 96c7bc698e6b..d14bce0f82cc 100644 --- a/arch/x86/coco/sev/core.c +++ b/arch/x86/coco/sev/core.c @@ -9,8 +9,6 @@ #define pr_fmt(fmt) "SEV: " fmt -#define DISABLE_BRANCH_PROFILING - #include /* For show_regs() */ #include #include diff --git a/arch/x86/kernel/head64.c b/arch/x86/kernel/head64.c index 22c9ba305ac1..368157a7f6d2 100644 --- a/arch/x86/kernel/head64.c +++ b/arch/x86/kernel/head64.c @@ -5,8 +5,6 @@ * Copyright (C) 2000 Andrea Arcangeli SuSE */ -#define DISABLE_BRANCH_PROFILING - /* cpu_feature_enabled() cannot be used this early */ #define USE_EARLY_PGTABLE_L5 diff --git a/arch/x86/mm/kasan_init_64.c b/arch/x86/mm/kasan_init_64.c index 9dddf19a5571..0539efd0d216 100644 --- a/arch/x86/mm/kasan_init_64.c +++ b/arch/x86/mm/kasan_init_64.c @@ -1,5 +1,4 @@ // SPDX-License-Identifier: GPL-2.0 -#define DISABLE_BRANCH_PROFILING #define pr_fmt(fmt) "kasan: " fmt /* cpu_feature_enabled() cannot be used this early */ diff --git a/arch/x86/mm/mem_encrypt_amd.c b/arch/x86/mm/mem_encrypt_amd.c index b56c5c073003..7490ff6d83b1 100644 --- a/arch/x86/mm/mem_encrypt_amd.c +++ b/arch/x86/mm/mem_encrypt_amd.c @@ -7,8 +7,6 @@ * Author: Tom Lendacky */ -#define DISABLE_BRANCH_PROFILING - #include #include #include diff --git a/arch/x86/mm/mem_encrypt_identity.c b/arch/x86/mm/mem_encrypt_identity.c index e6c7686f443a..4e991dedc1db 100644 --- a/arch/x86/mm/mem_encrypt_identity.c +++ b/arch/x86/mm/mem_encrypt_identity.c @@ -7,8 +7,6 @@ * Author: Tom Lendacky */ -#define DISABLE_BRANCH_PROFILING - /* * Since we're dealing with identity mappings, physical and virtual * addresses are the same, so override these defines which are ultimately diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 40208a0f5dfb..797070fc9a3f 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -5,6 +5,10 @@ ccflags-$(CONFIG_ACPI_DEBUG) += -DACPI_DEBUG_OUTPUT +ifdef CONFIG_TRACE_BRANCH_PROFILING +CFLAGS_processor_idle.o += -DDISABLE_BRANCH_PROFILING +endif + # # ACPI Boot-Time Table Parsing # diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index d103342b7cfc..1de9e92c5b0f 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -3,6 +3,9 @@ # Makefile for cpuidle. # +# Branch profiling isn't noinstr-safe +ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING + obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o obj-$(CONFIG_DT_IDLE_STATES) += dt_idle_states.o diff --git a/drivers/idle/Makefile b/drivers/idle/Makefile index 0a3c37510079..a34af1ba09bd 100644 --- a/drivers/idle/Makefile +++ b/drivers/idle/Makefile @@ -1,3 +1,6 @@ # SPDX-License-Identifier: GPL-2.0-only -obj-$(CONFIG_INTEL_IDLE) += intel_idle.o +# Branch profiling isn't noinstr-safe +ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING + +obj-$(CONFIG_INTEL_IDLE) += intel_idle.o diff --git a/kernel/Makefile b/kernel/Makefile index 87866b037fbe..434929de17ef 100644 --- a/kernel/Makefile +++ b/kernel/Makefile @@ -21,6 +21,11 @@ ifdef CONFIG_FUNCTION_TRACER CFLAGS_REMOVE_irq_work.o = $(CC_FLAGS_FTRACE) endif +# Branch profiling isn't noinstr-safe +ifdef CONFIG_TRACE_BRANCH_PROFILING +CFLAGS_context_tracking.o += -DDISABLE_BRANCH_PROFILING +endif + # Prevents flicker of uninteresting __do_softirq()/__local_bh_disable_ip() # in coverage traces. KCOV_INSTRUMENT_softirq.o := n diff --git a/kernel/entry/Makefile b/kernel/entry/Makefile index 095c775e001e..d4b8bd0af79b 100644 --- a/kernel/entry/Makefile +++ b/kernel/entry/Makefile @@ -6,6 +6,9 @@ KASAN_SANITIZE := n UBSAN_SANITIZE := n KCOV_INSTRUMENT := n +# Branch profiling isn't noinstr-safe +ccflags-$(CONFIG_TRACE_BRANCH_PROFILING) += -DDISABLE_BRANCH_PROFILING + CFLAGS_REMOVE_common.o = -fstack-protector -fstack-protector-strong CFLAGS_common.o += -fno-stack-protector diff --git a/kernel/sched/Makefile b/kernel/sched/Makefile index 976092b7bd45..8ae86371ddcd 100644 --- a/kernel/sched/Makefile +++ b/kernel/sched/Makefile @@ -22,6 +22,11 @@ ifneq ($(CONFIG_SCHED_OMIT_FRAME_POINTER),y) CFLAGS_core.o := $(PROFILING) -fno-omit-frame-pointer endif +# Branch profiling isn't noinstr-safe +ifdef CONFIG_TRACE_BRANCH_PROFILING +CFLAGS_build_policy.o += -DDISABLE_BRANCH_PROFILING +CFLAGS_build_utility.o += -DDISABLE_BRANCH_PROFILING +endif # # Build efficiency: # diff --git a/kernel/time/Makefile b/kernel/time/Makefile index fe0ae82124fe..e6e9b85d4db5 100644 --- a/kernel/time/Makefile +++ b/kernel/time/Makefile @@ -1,4 +1,10 @@ # SPDX-License-Identifier: GPL-2.0 + +# Branch profiling isn't noinstr-safe +ifdef CONFIG_TRACE_BRANCH_PROFILING +CFLAGS_sched_clock.o += -DDISABLE_BRANCH_PROFILING +endif + obj-y += time.o timer.o hrtimer.o sleep_timeout.o obj-y += timekeeping.o ntp.o clocksource.o jiffies.o timer_list.o obj-y += timeconv.o timecounter.o alarmtimer.o diff --git a/lib/Makefile b/lib/Makefile index d5cfc7afbbb8..4f3d00a2fd65 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -5,6 +5,11 @@ ccflags-remove-$(CONFIG_FUNCTION_TRACER) += $(CC_FLAGS_FTRACE) +# Branch profiling isn't noinstr-safe +ifdef CONFIG_TRACE_BRANCH_PROFILING +CFLAGS_smp_processor_id.o += -DDISABLE_BRANCH_PROFILING +endif + # These files are disabled because they produce lots of non-interesting and/or # flaky coverage that is not a function of syscall inputs. For example, # rbtree can be global and individual rotations don't correlate with inputs.