From 3da73f102309fe29150e5c35acd20dd82063ff67 Mon Sep 17 00:00:00 2001 From: Miaoqian Lin Date: Mon, 5 Dec 2022 12:06:42 +0400 Subject: [PATCH 01/22] objtool: Fix memory leak in create_static_call_sections() strdup() allocates memory for key_name. We need to release the memory in the following error paths. Add free() to avoid memory leak. Fixes: 1e7e47883830 ("x86/static_call: Add inline static call implementation for x86-64") Signed-off-by: Miaoqian Lin Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/20221205080642.558583-1-linmq006@gmail.com Cc: Josh Poimboeuf Cc: Peter Zijlstra --- tools/objtool/check.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 4350be739f4f..cab1a162781c 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -679,6 +679,7 @@ static int create_static_call_sections(struct objtool_file *file) if (strncmp(key_name, STATIC_CALL_TRAMP_PREFIX_STR, STATIC_CALL_TRAMP_PREFIX_LEN)) { WARN("static_call: trampoline name malformed: %s", key_name); + free(key_name); return -1; } tmp = key_name + STATIC_CALL_TRAMP_PREFIX_LEN - STATIC_CALL_KEY_PREFIX_LEN; @@ -688,6 +689,7 @@ static int create_static_call_sections(struct objtool_file *file) if (!key_sym) { if (!opts.module) { WARN("static_call: can't find static_call_key symbol: %s", tmp); + free(key_name); return -1; } From 03d7a1053cf72372be22b43faada5bca12ff183d Mon Sep 17 00:00:00 2001 From: Michal Kubecek Date: Wed, 18 Jan 2023 11:52:15 +0100 Subject: [PATCH 02/22] objtool: Check that module init/exit function is an indirect call target Some out-of-tree modules still do not use module_init() / module_exit() macros and simply create functions with magic names init_module() and cleanup_module() instead. As a result, these functions are not recognized as indirect call targets by objtool and such module fails to load into an IBT enabled kernel. This old way is not even documented any more but it is cleaner to issue a warning than to let the module fail on load without obvious reason. Signed-off-by: Michal Kubecek Signed-off-by: Peter Zijlstra (Intel) Link: https://lkml.kernel.org/r/20230118105215.B9DA960514@lion.mk-sys.cz --- tools/objtool/Documentation/objtool.txt | 8 ++++++++ tools/objtool/check.c | 7 +++++++ 2 files changed, 15 insertions(+) diff --git a/tools/objtool/Documentation/objtool.txt b/tools/objtool/Documentation/objtool.txt index 8a671902a187..8e53fc6735ef 100644 --- a/tools/objtool/Documentation/objtool.txt +++ b/tools/objtool/Documentation/objtool.txt @@ -410,6 +410,14 @@ the objtool maintainers. 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. + 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. diff --git a/tools/objtool/check.c b/tools/objtool/check.c index cab1a162781c..7c40bd51c75a 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -847,8 +847,15 @@ static int create_ibt_endbr_seal_sections(struct objtool_file *file) list_for_each_entry(insn, &file->endbr_list, call_node) { int *site = (int *)sec->data->d_buf + idx; + struct symbol *sym = insn->sym; *site = 0; + if (opts.module && sym && sym->type == STT_FUNC && + insn->offset == sym->offset && + (!strcmp(sym->name, "init_module") || + !strcmp(sym->name, "cleanup_module"))) + WARN("%s(): not an indirect call target", sym->name); + if (elf_add_reloc_to_insn(file->elf, sec, idx * sizeof(int), R_X86_64_PC32, From bdb8bf7d56afd1d22c12c61455d732d3baff2bde Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 26 Jan 2023 11:06:04 -0800 Subject: [PATCH 03/22] objtool: Install libsubcmd in build Including from tools/lib can create inadvertent dependencies. Install libsubcmd in the objtool build and then include the headers from there. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20230126190606.40739-2-irogers@google.com Signed-off-by: Josh Poimboeuf --- tools/objtool/.gitignore | 1 + tools/objtool/Build | 2 -- tools/objtool/Makefile | 31 +++++++++++++++++++++++-------- 3 files changed, 24 insertions(+), 10 deletions(-) diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore index 14236db3677f..4faa4dd72f35 100644 --- a/tools/objtool/.gitignore +++ b/tools/objtool/.gitignore @@ -2,3 +2,4 @@ arch/x86/lib/inat-tables.c /objtool fixdep +libsubcmd/ diff --git a/tools/objtool/Build b/tools/objtool/Build index 33f2ee5a46d3..a3cdf8af6635 100644 --- a/tools/objtool/Build +++ b/tools/objtool/Build @@ -16,8 +16,6 @@ objtool-y += libctype.o objtool-y += str_error_r.o objtool-y += librbtree.o -CFLAGS += -I$(srctree)/tools/lib - $(OUTPUT)libstring.o: ../lib/string.c FORCE $(call rule_mkdir) $(call if_changed_dep,cc_o_c) diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index a3a9cc24e0e3..3505ae4b0e36 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -12,9 +12,13 @@ srctree := $(patsubst %/,%,$(dir $(CURDIR))) srctree := $(patsubst %/,%,$(dir $(srctree))) endif -SUBCMD_SRCDIR = $(srctree)/tools/lib/subcmd/ -LIBSUBCMD_OUTPUT = $(or $(OUTPUT),$(CURDIR)/) -LIBSUBCMD = $(LIBSUBCMD_OUTPUT)libsubcmd.a +LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/ +ifneq ($(OUTPUT),) + LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd +else + LIBSUBCMD_OUTPUT = $(CURDIR)/libsubcmd +endif +LIBSUBCMD = $(LIBSUBCMD_OUTPUT)/libsubcmd.a OBJTOOL := $(OUTPUT)objtool OBJTOOL_IN := $(OBJTOOL)-in.o @@ -28,7 +32,8 @@ INCLUDES := -I$(srctree)/tools/include \ -I$(srctree)/tools/arch/$(HOSTARCH)/include/uapi \ -I$(srctree)/tools/arch/$(SRCARCH)/include \ -I$(srctree)/tools/objtool/include \ - -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include + -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \ + -I$(LIBSUBCMD_OUTPUT)/include WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS) LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) @@ -38,6 +43,7 @@ elfshdr := $(shell echo '$(pound)include ' | $(CC) $(CFLAGS) -x c -E - CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) AWK = awk +MKDIR = mkdir BUILD_ORC := n @@ -57,13 +63,22 @@ $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ -$(LIBSUBCMD): fixdep FORCE - $(Q)$(MAKE) -C $(SUBCMD_SRCDIR) OUTPUT=$(LIBSUBCMD_OUTPUT) +$(LIBSUBCMD_OUTPUT): + @$(MKDIR) -p $@ -clean: +$(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) FORCE + @$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \ + DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= \ + $@ install_headers + +$(LIBSUBCMD)-clean: + $(call QUIET_CLEAN, libsubcmd) + $(Q)$(RM) -r -- $(LIBSUBCMD_OUTPUT) + +clean: $(LIBSUBCMD)-clean $(call QUIET_CLEAN, objtool) $(RM) $(OBJTOOL) $(Q)find $(OUTPUT) -name '*.o' -delete -o -name '\.*.cmd' -delete -o -name '\.*.d' -delete - $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep $(LIBSUBCMD) + $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep FORCE: From 8c4526ca6a45e7ff915c2b33b54db6b773291fac Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 26 Jan 2023 11:06:05 -0800 Subject: [PATCH 04/22] objtool: Properly support make V=1 The Q variable was being used but never correctly set up. Add the setting up and use in place of @. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20230126190606.40739-3-irogers@google.com Signed-off-by: Josh Poimboeuf --- tools/objtool/Makefile | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index 3505ae4b0e36..d54b66986627 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -45,6 +45,12 @@ CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) AWK = awk MKDIR = mkdir +ifeq ($(V),1) + Q = +else + Q = @ +endif + BUILD_ORC := n ifeq ($(SRCARCH),x86) @@ -56,18 +62,18 @@ export srctree OUTPUT CFLAGS SRCARCH AWK include $(srctree)/tools/build/Makefile.include $(OBJTOOL_IN): fixdep FORCE - @$(CONFIG_SHELL) ./sync-check.sh - @$(MAKE) $(build)=objtool + $(Q)$(CONFIG_SHELL) ./sync-check.sh + $(Q)$(MAKE) $(build)=objtool $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ $(LIBSUBCMD_OUTPUT): - @$(MKDIR) -p $@ + $(Q)$(MKDIR) -p $@ $(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) FORCE - @$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \ + $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \ DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= \ $@ install_headers From cd955bdd6aa5ec54cdef622a142f8899a64b5446 Mon Sep 17 00:00:00 2001 From: Ian Rogers Date: Thu, 26 Jan 2023 11:06:06 -0800 Subject: [PATCH 05/22] objtool: Fix HOSTCC flag usage HOSTCC is always wanted when building objtool. Setting CC to HOSTCC happens after tools/scripts/Makefile.include is included, meaning flags (like CFLAGS) are set assuming say CC is gcc, but then it can be later set to HOSTCC which may be clang. tools/scripts/Makefile.include is needed for host set up and common macros in objtool's Makefile. Rather than override the CC variable to HOSTCC, just pass CC as HOSTCC to the sub-makes of Makefile.build, the libsubcmd builds and also to the linkage step. Signed-off-by: Ian Rogers Link: https://lore.kernel.org/r/20230126190606.40739-4-irogers@google.com Signed-off-by: Josh Poimboeuf --- tools/objtool/Makefile | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile index d54b66986627..83b100c1e7f6 100644 --- a/tools/objtool/Makefile +++ b/tools/objtool/Makefile @@ -2,11 +2,6 @@ include ../scripts/Makefile.include include ../scripts/Makefile.arch -# always use the host compiler -AR = $(HOSTAR) -CC = $(HOSTCC) -LD = $(HOSTLD) - ifeq ($(srctree),) srctree := $(patsubst %/,%,$(dir $(CURDIR))) srctree := $(patsubst %/,%,$(dir $(srctree))) @@ -34,13 +29,18 @@ INCLUDES := -I$(srctree)/tools/include \ -I$(srctree)/tools/objtool/include \ -I$(srctree)/tools/objtool/arch/$(SRCARCH)/include \ -I$(LIBSUBCMD_OUTPUT)/include +# Note, EXTRA_WARNINGS here was determined for CC and not HOSTCC, it +# is passed here to match a legacy behavior. WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -Wno-nested-externs -CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS) -LDFLAGS += $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) +OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS) +OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) # Allow old libelf to be used: -elfshdr := $(shell echo '$(pound)include ' | $(CC) $(CFLAGS) -x c -E - | grep elf_getshdr) -CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) +elfshdr := $(shell echo '$(pound)include ' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - | grep elf_getshdr) +OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED) + +# Always want host compilation. +HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)" AWK = awk MKDIR = mkdir @@ -61,12 +61,14 @@ export BUILD_ORC export srctree OUTPUT CFLAGS SRCARCH AWK include $(srctree)/tools/build/Makefile.include -$(OBJTOOL_IN): fixdep FORCE +$(OBJTOOL_IN): fixdep $(LIBSUBCMD) FORCE $(Q)$(CONFIG_SHELL) ./sync-check.sh - $(Q)$(MAKE) $(build)=objtool + $(Q)$(MAKE) $(build)=objtool $(HOST_OVERRIDES) CFLAGS="$(OBJTOOL_CFLAGS)" \ + LDFLAGS="$(OBJTOOL_LDFLAGS)" + $(OBJTOOL): $(LIBSUBCMD) $(OBJTOOL_IN) - $(QUIET_LINK)$(CC) $(OBJTOOL_IN) $(LDFLAGS) -o $@ + $(QUIET_LINK)$(HOSTCC) $(OBJTOOL_IN) $(OBJTOOL_LDFLAGS) -o $@ $(LIBSUBCMD_OUTPUT): @@ -75,6 +77,7 @@ $(LIBSUBCMD_OUTPUT): $(LIBSUBCMD): fixdep $(LIBSUBCMD_OUTPUT) FORCE $(Q)$(MAKE) -C $(LIBSUBCMD_DIR) O=$(LIBSUBCMD_OUTPUT) \ DESTDIR=$(LIBSUBCMD_OUTPUT) prefix= subdir= \ + $(HOST_OVERRIDES) EXTRA_CFLAGS="$(OBJTOOL_CFLAGS)" \ $@ install_headers $(LIBSUBCMD)-clean: From d93ee0553cf2e83c1696a18423bcf05b94b85e1d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 27 Dec 2022 16:00:57 +0000 Subject: [PATCH 06/22] objtool: Make struct entries[] static and const MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This data is not modified and not used outside of special.c. Also adapt its users to the constness. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20221216-objtool-memory-v2-1-17968f85a464@weissschuh.net Signed-off-by: Josh Poimboeuf --- tools/objtool/special.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tools/objtool/special.c b/tools/objtool/special.c index 9c8d827f69af..baa85c31526b 100644 --- a/tools/objtool/special.c +++ b/tools/objtool/special.c @@ -26,7 +26,7 @@ struct special_entry { unsigned char key; /* jump_label key */ }; -struct special_entry entries[] = { +static const struct special_entry entries[] = { { .sec = ".altinstructions", .group = true, @@ -65,7 +65,7 @@ static void reloc_to_sec_off(struct reloc *reloc, struct section **sec, *off = reloc->sym->offset + reloc->addend; } -static int get_alt_entry(struct elf *elf, struct special_entry *entry, +static int get_alt_entry(struct elf *elf, const struct special_entry *entry, struct section *sec, int idx, struct special_alt *alt) { @@ -139,7 +139,7 @@ static int get_alt_entry(struct elf *elf, struct special_entry *entry, */ int special_get_alts(struct elf *elf, struct list_head *alts) { - struct special_entry *entry; + const struct special_entry *entry; struct section *sec; unsigned int nr_entries; struct special_alt *alt; From cfd66e81799f4a2fdc6447fa99bdb1871f45ff08 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 27 Dec 2022 16:00:58 +0000 Subject: [PATCH 07/22] objtool: Make struct check_options static MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is not used outside of builtin-check.c. Also remove the unused declaration from builtin.h . Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20221216-objtool-memory-v2-2-17968f85a464@weissschuh.net Signed-off-by: Josh Poimboeuf --- tools/objtool/builtin-check.c | 2 +- tools/objtool/include/objtool/builtin.h | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c index a4f39407bf59..7c175198d09f 100644 --- a/tools/objtool/builtin-check.c +++ b/tools/objtool/builtin-check.c @@ -65,7 +65,7 @@ static int parse_hacks(const struct option *opt, const char *str, int unset) return found ? 0 : -1; } -const struct option check_options[] = { +static const struct option check_options[] = { OPT_GROUP("Actions:"), OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks), OPT_BOOLEAN('i', "ibt", &opts.ibt, "validate and annotate IBT"), diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h index fa45044e3863..2a108e648b7a 100644 --- a/tools/objtool/include/objtool/builtin.h +++ b/tools/objtool/include/objtool/builtin.h @@ -7,8 +7,6 @@ #include -extern const struct option check_options[]; - struct opts { /* actions: */ bool dump_orc; From 8045b8f0b17edf375849f83c80dd05194850b6ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 27 Dec 2022 16:00:59 +0000 Subject: [PATCH 08/22] objtool: Allocate multiple structures with calloc() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit By using calloc() instead of malloc() in a loop, libc does not have to keep around bookkeeping information for each single structure. This reduces maximum memory usage while processing vmlinux.o from 3153325 KB to 3035668 KB (-3.7%) on my notebooks "localmodconfig". Note this introduces memory leaks, because some additional structs get added to the lists later after reading the symbols and sections from the original object. Luckily we don't really care about memory leaks in objtool. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20221216-objtool-memory-v2-3-17968f85a464@weissschuh.net Signed-off-by: Josh Poimboeuf --- tools/objtool/elf.c | 42 ++++++++++++++--------------- tools/objtool/include/objtool/elf.h | 4 +++ 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/tools/objtool/elf.c b/tools/objtool/elf.c index 64443a7f4bbf..6806ce01d933 100644 --- a/tools/objtool/elf.c +++ b/tools/objtool/elf.c @@ -284,13 +284,13 @@ static int read_sections(struct elf *elf) !elf_alloc_hash(section_name, sections_nr)) return -1; + elf->section_data = calloc(sections_nr, sizeof(*sec)); + if (!elf->section_data) { + perror("calloc"); + return -1; + } for (i = 0; i < sections_nr; i++) { - sec = malloc(sizeof(*sec)); - if (!sec) { - perror("malloc"); - return -1; - } - memset(sec, 0, sizeof(*sec)); + sec = &elf->section_data[i]; INIT_LIST_HEAD(&sec->symbol_list); INIT_LIST_HEAD(&sec->reloc_list); @@ -422,13 +422,13 @@ static int read_symbols(struct elf *elf) !elf_alloc_hash(symbol_name, symbols_nr)) return -1; + elf->symbol_data = calloc(symbols_nr, sizeof(*sym)); + if (!elf->symbol_data) { + perror("calloc"); + return -1; + } for (i = 0; i < symbols_nr; i++) { - sym = malloc(sizeof(*sym)); - if (!sym) { - perror("malloc"); - return -1; - } - memset(sym, 0, sizeof(*sym)); + sym = &elf->symbol_data[i]; sym->idx = i; @@ -918,13 +918,13 @@ static int read_relocs(struct elf *elf) sec->base->reloc = sec; nr_reloc = 0; + sec->reloc_data = calloc(sec->sh.sh_size / sec->sh.sh_entsize, sizeof(*reloc)); + if (!sec->reloc_data) { + perror("calloc"); + return -1; + } for (i = 0; i < sec->sh.sh_size / sec->sh.sh_entsize; i++) { - reloc = malloc(sizeof(*reloc)); - if (!reloc) { - perror("malloc"); - return -1; - } - memset(reloc, 0, sizeof(*reloc)); + reloc = &sec->reloc_data[i]; switch (sec->sh.sh_type) { case SHT_REL: if (read_rel_reloc(sec, i, reloc, &symndx)) @@ -1453,16 +1453,16 @@ void elf_close(struct elf *elf) list_for_each_entry_safe(sym, tmpsym, &sec->symbol_list, list) { list_del(&sym->list); hash_del(&sym->hash); - free(sym); } list_for_each_entry_safe(reloc, tmpreloc, &sec->reloc_list, list) { list_del(&reloc->list); hash_del(&reloc->hash); - free(reloc); } list_del(&sec->list); - free(sec); + free(sec->reloc_data); } + free(elf->symbol_data); + free(elf->section_data); free(elf); } diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index bb60fd42b46f..1c90f0ac0d53 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -39,6 +39,7 @@ struct section { char *name; int idx; bool changed, text, rodata, noinstr, init, truncate; + struct reloc *reloc_data; }; struct symbol { @@ -104,6 +105,9 @@ struct elf { struct hlist_head *section_hash; struct hlist_head *section_name_hash; struct hlist_head *reloc_hash; + + struct section *section_data; + struct symbol *symbol_data; }; #define OFFSET_STRIDE_BITS 4 From 21a899f9fc78be6b289ee4627bccadf560930eb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 27 Dec 2022 16:01:02 +0000 Subject: [PATCH 09/22] objtool: Optimize layout of struct symbol MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the size of struct symbol on x86_64 from 208 to 200 bytes. This structure is allocated a lot and never freed. This reduces maximum memory usage while processing vmlinux.o from 2919716 KB to 2917988 KB (-0.5%) on my notebooks "localmodconfig". Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20221216-objtool-memory-v2-6-17968f85a464@weissschuh.net Signed-off-by: Josh Poimboeuf --- tools/objtool/include/objtool/elf.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tools/objtool/include/objtool/elf.h b/tools/objtool/include/objtool/elf.h index 1c90f0ac0d53..ad0024da262b 100644 --- a/tools/objtool/include/objtool/elf.h +++ b/tools/objtool/include/objtool/elf.h @@ -50,12 +50,11 @@ struct symbol { GElf_Sym sym; struct section *sec; char *name; - unsigned int idx; - unsigned char bind, type; + unsigned int idx, len; unsigned long offset; - unsigned int len; unsigned long __subtree_last; struct symbol *pfunc, *cfunc, *alias; + unsigned char bind, type; u8 uaccess_safe : 1; u8 static_call_tramp : 1; u8 retpoline_thunk : 1; From a20717aca33b1ff133f513721050fe6c3d7f97b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= Date: Tue, 27 Dec 2022 16:01:03 +0000 Subject: [PATCH 10/22] objtool: Optimize layout of struct special_alt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reduce the size of struct special_alt from 72 to 64 bytes. Signed-off-by: Thomas Weißschuh Link: https://lore.kernel.org/r/20221216-objtool-memory-v2-7-17968f85a464@weissschuh.net Signed-off-by: Josh Poimboeuf --- tools/objtool/include/objtool/special.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h index dc4721e19002..86d4af9c5aa9 100644 --- a/tools/objtool/include/objtool/special.h +++ b/tools/objtool/include/objtool/special.h @@ -19,6 +19,7 @@ struct special_alt { bool skip_orig; bool skip_alt; bool jump_or_nop; + u8 key_addend; struct section *orig_sec; unsigned long orig_off; @@ -27,7 +28,6 @@ struct special_alt { unsigned long new_off; unsigned int orig_len, new_len; /* group only */ - u8 key_addend; }; int special_get_alts(struct elf *elf, struct list_head *alts); From ffb1b4a41016295e298409c9dbcacd55680bd6d4 Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 10 Feb 2023 14:42:01 -0800 Subject: [PATCH 11/22] x86/unwind/orc: Add 'signal' field to ORC metadata Add a 'signal' field which allows unwind hints to specify whether the instruction pointer should be taken literally (like for most interrupts and exceptions) rather than decremented (like for call stack return addresses) when used to find the next ORC entry. Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/d2c5ec4d83a45b513d8fd72fab59f1a8cfa46871.1676068346.git.jpoimboe@kernel.org --- arch/x86/include/asm/orc_types.h | 4 +++- arch/x86/include/asm/unwind_hints.h | 10 +++++----- arch/x86/kernel/unwind_orc.c | 5 ++--- include/linux/objtool.h | 11 +++++++---- tools/arch/x86/include/asm/orc_types.h | 4 +++- tools/include/linux/objtool.h | 11 +++++++---- tools/objtool/orc_dump.c | 4 ++-- 7 files changed, 29 insertions(+), 20 deletions(-) diff --git a/arch/x86/include/asm/orc_types.h b/arch/x86/include/asm/orc_types.h index 5a2baf28a1dc..1343a62106de 100644 --- a/arch/x86/include/asm/orc_types.h +++ b/arch/x86/include/asm/orc_types.h @@ -57,12 +57,14 @@ struct orc_entry { unsigned sp_reg:4; unsigned bp_reg:4; unsigned type:2; + unsigned signal:1; unsigned end:1; #elif defined(__BIG_ENDIAN_BITFIELD) unsigned bp_reg:4; unsigned sp_reg:4; - unsigned unused:5; + unsigned unused:4; unsigned end:1; + unsigned signal:1; unsigned type:2; #endif } __packed; diff --git a/arch/x86/include/asm/unwind_hints.h b/arch/x86/include/asm/unwind_hints.h index f66fbe6537dd..e7c71750b309 100644 --- a/arch/x86/include/asm/unwind_hints.h +++ b/arch/x86/include/asm/unwind_hints.h @@ -15,7 +15,7 @@ UNWIND_HINT type=UNWIND_HINT_TYPE_ENTRY end=1 .endm -.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0 +.macro UNWIND_HINT_REGS base=%rsp offset=0 indirect=0 extra=1 partial=0 signal=1 .if \base == %rsp .if \indirect .set sp_reg, ORC_REG_SP_INDIRECT @@ -45,11 +45,11 @@ .set type, UNWIND_HINT_TYPE_REGS .endif - UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type + UNWIND_HINT sp_reg=sp_reg sp_offset=sp_offset type=type signal=\signal .endm -.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0 - UNWIND_HINT_REGS base=\base offset=\offset partial=1 +.macro UNWIND_HINT_IRET_REGS base=%rsp offset=0 signal=1 + UNWIND_HINT_REGS base=\base offset=\offset partial=1 signal=\signal .endm .macro UNWIND_HINT_FUNC @@ -67,7 +67,7 @@ #else #define UNWIND_HINT_FUNC \ - UNWIND_HINT(ORC_REG_SP, 8, UNWIND_HINT_TYPE_FUNC, 0) + UNWIND_HINT(ORC_REG_SP, 8, UNWIND_HINT_TYPE_FUNC, 0, 0) #endif /* __ASSEMBLY__ */ diff --git a/arch/x86/kernel/unwind_orc.c b/arch/x86/kernel/unwind_orc.c index cdf6c6060170..37307b40f8da 100644 --- a/arch/x86/kernel/unwind_orc.c +++ b/arch/x86/kernel/unwind_orc.c @@ -484,6 +484,8 @@ bool unwind_next_frame(struct unwind_state *state) goto the_end; } + state->signal = orc->signal; + /* Find the previous frame's stack: */ switch (orc->sp_reg) { case ORC_REG_SP: @@ -563,7 +565,6 @@ bool unwind_next_frame(struct unwind_state *state) state->sp = sp; state->regs = NULL; state->prev_regs = NULL; - state->signal = false; break; case UNWIND_HINT_TYPE_REGS: @@ -587,7 +588,6 @@ bool unwind_next_frame(struct unwind_state *state) state->regs = (struct pt_regs *)sp; state->prev_regs = NULL; state->full_regs = true; - state->signal = true; break; case UNWIND_HINT_TYPE_REGS_PARTIAL: @@ -604,7 +604,6 @@ bool unwind_next_frame(struct unwind_state *state) state->prev_regs = state->regs; state->regs = (void *)sp - IRET_FRAME_OFFSET; state->full_regs = false; - state->signal = true; break; default: diff --git a/include/linux/objtool.h b/include/linux/objtool.h index 62c54ffbeeaa..9ac3df3fccf0 100644 --- a/include/linux/objtool.h +++ b/include/linux/objtool.h @@ -15,6 +15,7 @@ struct unwind_hint { s16 sp_offset; u8 sp_reg; u8 type; + u8 signal; u8 end; }; #endif @@ -49,7 +50,7 @@ struct unwind_hint { #ifndef __ASSEMBLY__ -#define UNWIND_HINT(sp_reg, sp_offset, type, end) \ +#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \ "987: \n\t" \ ".pushsection .discard.unwind_hints\n\t" \ /* struct unwind_hint */ \ @@ -57,6 +58,7 @@ struct unwind_hint { ".short " __stringify(sp_offset) "\n\t" \ ".byte " __stringify(sp_reg) "\n\t" \ ".byte " __stringify(type) "\n\t" \ + ".byte " __stringify(signal) "\n\t" \ ".byte " __stringify(end) "\n\t" \ ".balign 4 \n\t" \ ".popsection\n\t" @@ -129,7 +131,7 @@ struct unwind_hint { * the debuginfo as necessary. It will also warn if it sees any * inconsistencies. */ -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0 +.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0 .Lunwind_hint_ip_\@: .pushsection .discard.unwind_hints /* struct unwind_hint */ @@ -137,6 +139,7 @@ struct unwind_hint { .short \sp_offset .byte \sp_reg .byte \type + .byte \signal .byte \end .balign 4 .popsection @@ -174,7 +177,7 @@ struct unwind_hint { #ifndef __ASSEMBLY__ -#define UNWIND_HINT(sp_reg, sp_offset, type, end) \ +#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \ "\n\t" #define STACK_FRAME_NON_STANDARD(func) #define STACK_FRAME_NON_STANDARD_FP(func) @@ -182,7 +185,7 @@ struct unwind_hint { #define ASM_REACHABLE #else #define ANNOTATE_INTRA_FUNCTION_CALL -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0 +.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0 .endm .macro STACK_FRAME_NON_STANDARD func:req .endm diff --git a/tools/arch/x86/include/asm/orc_types.h b/tools/arch/x86/include/asm/orc_types.h index 5a2baf28a1dc..1343a62106de 100644 --- a/tools/arch/x86/include/asm/orc_types.h +++ b/tools/arch/x86/include/asm/orc_types.h @@ -57,12 +57,14 @@ struct orc_entry { unsigned sp_reg:4; unsigned bp_reg:4; unsigned type:2; + unsigned signal:1; unsigned end:1; #elif defined(__BIG_ENDIAN_BITFIELD) unsigned bp_reg:4; unsigned sp_reg:4; - unsigned unused:5; + unsigned unused:4; unsigned end:1; + unsigned signal:1; unsigned type:2; #endif } __packed; diff --git a/tools/include/linux/objtool.h b/tools/include/linux/objtool.h index 62c54ffbeeaa..9ac3df3fccf0 100644 --- a/tools/include/linux/objtool.h +++ b/tools/include/linux/objtool.h @@ -15,6 +15,7 @@ struct unwind_hint { s16 sp_offset; u8 sp_reg; u8 type; + u8 signal; u8 end; }; #endif @@ -49,7 +50,7 @@ struct unwind_hint { #ifndef __ASSEMBLY__ -#define UNWIND_HINT(sp_reg, sp_offset, type, end) \ +#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \ "987: \n\t" \ ".pushsection .discard.unwind_hints\n\t" \ /* struct unwind_hint */ \ @@ -57,6 +58,7 @@ struct unwind_hint { ".short " __stringify(sp_offset) "\n\t" \ ".byte " __stringify(sp_reg) "\n\t" \ ".byte " __stringify(type) "\n\t" \ + ".byte " __stringify(signal) "\n\t" \ ".byte " __stringify(end) "\n\t" \ ".balign 4 \n\t" \ ".popsection\n\t" @@ -129,7 +131,7 @@ struct unwind_hint { * the debuginfo as necessary. It will also warn if it sees any * inconsistencies. */ -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0 +.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0 .Lunwind_hint_ip_\@: .pushsection .discard.unwind_hints /* struct unwind_hint */ @@ -137,6 +139,7 @@ struct unwind_hint { .short \sp_offset .byte \sp_reg .byte \type + .byte \signal .byte \end .balign 4 .popsection @@ -174,7 +177,7 @@ struct unwind_hint { #ifndef __ASSEMBLY__ -#define UNWIND_HINT(sp_reg, sp_offset, type, end) \ +#define UNWIND_HINT(sp_reg, sp_offset, type, signal, end) \ "\n\t" #define STACK_FRAME_NON_STANDARD(func) #define STACK_FRAME_NON_STANDARD_FP(func) @@ -182,7 +185,7 @@ struct unwind_hint { #define ASM_REACHABLE #else #define ANNOTATE_INTRA_FUNCTION_CALL -.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 end=0 +.macro UNWIND_HINT type:req sp_reg=0 sp_offset=0 signal=0 end=0 .endm .macro STACK_FRAME_NON_STANDARD func:req .endm diff --git a/tools/objtool/orc_dump.c b/tools/objtool/orc_dump.c index 4f1211fec82c..2d8ebdcd1db3 100644 --- a/tools/objtool/orc_dump.c +++ b/tools/objtool/orc_dump.c @@ -211,8 +211,8 @@ int orc_dump(const char *_objname) print_reg(orc[i].bp_reg, bswap_if_needed(&dummy_elf, orc[i].bp_offset)); - printf(" type:%s end:%d\n", - orc_type_name(orc[i].type), orc[i].end); + printf(" type:%s signal:%d end:%d\n", + orc_type_name(orc[i].type), orc[i].signal, orc[i].end); } elf_end(elf); From 37064583f63eca93c98a9cdf2360485ea05f617a Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Fri, 10 Feb 2023 14:42:02 -0800 Subject: [PATCH 12/22] x86/entry: Fix unwinding from kprobe on PUSH/POP instruction If a kprobe (INT3) is set on a stack-modifying single-byte instruction, like a single-byte PUSH/POP or a LEAVE, ORC fails to unwind past it: Call Trace: dump_stack_lvl+0x57/0x90 handler_pre+0x33/0x40 [kprobe_example] aggr_pre_handler+0x49/0x90 kprobe_int3_handler+0xe3/0x180 do_int3+0x3a/0x80 exc_int3+0x7d/0xc0 asm_exc_int3+0x35/0x40 RIP: 0010:kernel_clone+0xe/0x3a0 Code: cc e8 16 b2 bf 00 66 0f 1f 44 00 00 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 90 0f 1f 44 00 00 41 57 41 56 41 55 41 54 cc <53> 48 89 fb 48 83 ec 68 4c 8b 27 65 48 8b 04 25 28 00 00 00 48 89 RSP: 0018:ffffc9000074fda0 EFLAGS: 00000206 RAX: 0000000000808100 RBX: ffff888109de9d80 RCX: 0000000000000000 RDX: 0000000000000011 RSI: ffff888109de9d80 RDI: ffffc9000074fdc8 RBP: ffff8881019543c0 R08: ffffffff81127e30 R09: 00000000e71742a5 R10: ffff888104764a18 R11: 0000000071742a5e R12: ffff888100078800 R13: ffff888100126000 R14: 0000000000000000 R15: ffff888100126005 ? __pfx_call_usermodehelper_exec_async+0x10/0x10 ? kernel_clone+0xe/0x3a0 ? user_mode_thread+0x5b/0x80 ? __pfx_call_usermodehelper_exec_async+0x10/0x10 ? call_usermodehelper_exec_work+0x77/0xb0 ? process_one_work+0x299/0x5f0 ? worker_thread+0x4f/0x3a0 ? __pfx_worker_thread+0x10/0x10 ? kthread+0xf2/0x120 ? __pfx_kthread+0x10/0x10 ? ret_from_fork+0x29/0x50 The problem is that #BP saves the pointer to the instruction immediately *after* the INT3, rather than to the INT3 itself. The instruction replaced by the INT3 hasn't actually run, but ORC assumes otherwise and expects the wrong stack layout. Fix it by annotating the #BP exception as a non-signal stack frame, which tells the ORC unwinder to decrement the instruction pointer before looking up the corresponding ORC entry. Reported-by: Chen Zhongjin Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/baafcd3cc1abb14cb757fe081fa696012a5265ee.1676068346.git.jpoimboe@kernel.org --- arch/x86/entry/entry_64.S | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/arch/x86/entry/entry_64.S b/arch/x86/entry/entry_64.S index 15739a2c0983..8d21881adf86 100644 --- a/arch/x86/entry/entry_64.S +++ b/arch/x86/entry/entry_64.S @@ -385,7 +385,14 @@ SYM_CODE_END(xen_error_entry) */ .macro idtentry vector asmsym cfunc has_error_code:req SYM_CODE_START(\asmsym) - UNWIND_HINT_IRET_REGS offset=\has_error_code*8 + + .if \vector == X86_TRAP_BP + /* #BP advances %rip to the next instruction */ + UNWIND_HINT_IRET_REGS offset=\has_error_code*8 signal=0 + .else + UNWIND_HINT_IRET_REGS offset=\has_error_code*8 + .endif + ENDBR ASM_CLAC cld From 20a554638dd2665a88d3d68a68f7981480a27f36 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:17:57 +0100 Subject: [PATCH 13/22] objtool: Change arch_decode_instruction() signature MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In preparation to changing struct instruction around a bit, avoid passing it's members by pointer and instead pass the whole thing. A cleanup in it's own right too. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.291087549@infradead.org --- tools/objtool/arch/powerpc/decode.c | 22 +++--- tools/objtool/arch/x86/decode.c | 105 +++++++++++++-------------- tools/objtool/check.c | 4 +- tools/objtool/include/objtool/arch.h | 4 +- 4 files changed, 64 insertions(+), 71 deletions(-) diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c index 9c653805a08a..53b55690f320 100644 --- a/tools/objtool/arch/powerpc/decode.c +++ b/tools/objtool/arch/powerpc/decode.c @@ -41,38 +41,36 @@ const char *arch_ret_insn(int len) int arch_decode_instruction(struct objtool_file *file, const struct section *sec, unsigned long offset, unsigned int maxlen, - unsigned int *len, enum insn_type *type, - unsigned long *immediate, - struct list_head *ops_list) + struct instruction *insn) { unsigned int opcode; enum insn_type typ; unsigned long imm; - u32 insn; + u32 ins; - insn = bswap_if_needed(file->elf, *(u32 *)(sec->data->d_buf + offset)); - opcode = insn >> 26; + ins = bswap_if_needed(file->elf, *(u32 *)(sec->data->d_buf + offset)); + opcode = ins >> 26; typ = INSN_OTHER; imm = 0; switch (opcode) { case 18: /* b[l][a] */ - if ((insn & 3) == 1) /* bl */ + if ((ins & 3) == 1) /* bl */ typ = INSN_CALL; - imm = insn & 0x3fffffc; + imm = ins & 0x3fffffc; if (imm & 0x2000000) imm -= 0x4000000; break; } if (opcode == 1) - *len = 8; + insn->len = 8; else - *len = 4; + insn->len = 4; - *type = typ; - *immediate = imm; + insn->type = typ; + insn->immediate = imm; return 0; } diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index e7b030f7e2a5..c5c49277cf1a 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -146,12 +146,11 @@ static bool has_notrack_prefix(struct insn *insn) int arch_decode_instruction(struct objtool_file *file, const struct section *sec, unsigned long offset, unsigned int maxlen, - unsigned int *len, enum insn_type *type, - unsigned long *immediate, - struct list_head *ops_list) + struct instruction *insn) { + struct list_head *ops_list = &insn->stack_ops; const struct elf *elf = file->elf; - struct insn insn; + struct insn ins; int x86_64, ret; unsigned char op1, op2, op3, prefix, rex = 0, rex_b = 0, rex_r = 0, rex_w = 0, rex_x = 0, @@ -165,42 +164,42 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (x86_64 == -1) return -1; - ret = insn_decode(&insn, sec->data->d_buf + offset, maxlen, + ret = insn_decode(&ins, sec->data->d_buf + offset, maxlen, x86_64 ? INSN_MODE_64 : INSN_MODE_32); if (ret < 0) { WARN("can't decode instruction at %s:0x%lx", sec->name, offset); return -1; } - *len = insn.length; - *type = INSN_OTHER; + insn->len = ins.length; + insn->type = INSN_OTHER; - if (insn.vex_prefix.nbytes) + if (ins.vex_prefix.nbytes) return 0; - prefix = insn.prefixes.bytes[0]; + prefix = ins.prefixes.bytes[0]; - op1 = insn.opcode.bytes[0]; - op2 = insn.opcode.bytes[1]; - op3 = insn.opcode.bytes[2]; + op1 = ins.opcode.bytes[0]; + op2 = ins.opcode.bytes[1]; + op3 = ins.opcode.bytes[2]; - if (insn.rex_prefix.nbytes) { - rex = insn.rex_prefix.bytes[0]; + if (ins.rex_prefix.nbytes) { + rex = ins.rex_prefix.bytes[0]; rex_w = X86_REX_W(rex) >> 3; rex_r = X86_REX_R(rex) >> 2; rex_x = X86_REX_X(rex) >> 1; rex_b = X86_REX_B(rex); } - if (insn.modrm.nbytes) { - modrm = insn.modrm.bytes[0]; + if (ins.modrm.nbytes) { + modrm = ins.modrm.bytes[0]; modrm_mod = X86_MODRM_MOD(modrm); modrm_reg = X86_MODRM_REG(modrm) + 8*rex_r; modrm_rm = X86_MODRM_RM(modrm) + 8*rex_b; } - if (insn.sib.nbytes) { - sib = insn.sib.bytes[0]; + if (ins.sib.nbytes) { + sib = ins.sib.bytes[0]; /* sib_scale = X86_SIB_SCALE(sib); */ sib_index = X86_SIB_INDEX(sib) + 8*rex_x; sib_base = X86_SIB_BASE(sib) + 8*rex_b; @@ -254,7 +253,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; case 0x70 ... 0x7f: - *type = INSN_JUMP_CONDITIONAL; + insn->type = INSN_JUMP_CONDITIONAL; break; case 0x80 ... 0x83: @@ -278,7 +277,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (!rm_is_reg(CFI_SP)) break; - imm = insn.immediate.value; + imm = ins.immediate.value; if (op1 & 2) { /* sign extend */ if (op1 & 1) { /* imm32 */ imm <<= 32; @@ -309,7 +308,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec ADD_OP(op) { op->src.type = OP_SRC_AND; op->src.reg = CFI_SP; - op->src.offset = insn.immediate.value; + op->src.offset = ins.immediate.value; op->dest.type = OP_DEST_REG; op->dest.reg = CFI_SP; } @@ -356,7 +355,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec op->src.reg = CFI_SP; op->dest.type = OP_DEST_REG_INDIRECT; op->dest.reg = modrm_rm; - op->dest.offset = insn.displacement.value; + op->dest.offset = ins.displacement.value; } break; } @@ -389,7 +388,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec op->src.reg = modrm_reg; op->dest.type = OP_DEST_REG_INDIRECT; op->dest.reg = CFI_BP; - op->dest.offset = insn.displacement.value; + op->dest.offset = ins.displacement.value; } break; } @@ -402,7 +401,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec op->src.reg = modrm_reg; op->dest.type = OP_DEST_REG_INDIRECT; op->dest.reg = CFI_SP; - op->dest.offset = insn.displacement.value; + op->dest.offset = ins.displacement.value; } break; } @@ -419,7 +418,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec ADD_OP(op) { op->src.type = OP_SRC_REG_INDIRECT; op->src.reg = CFI_BP; - op->src.offset = insn.displacement.value; + op->src.offset = ins.displacement.value; op->dest.type = OP_DEST_REG; op->dest.reg = modrm_reg; } @@ -432,7 +431,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec ADD_OP(op) { op->src.type = OP_SRC_REG_INDIRECT; op->src.reg = CFI_SP; - op->src.offset = insn.displacement.value; + op->src.offset = ins.displacement.value; op->dest.type = OP_DEST_REG; op->dest.reg = modrm_reg; } @@ -464,7 +463,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec /* lea disp(%src), %dst */ ADD_OP(op) { - op->src.offset = insn.displacement.value; + op->src.offset = ins.displacement.value; if (!op->src.offset) { /* lea (%src), %dst */ op->src.type = OP_SRC_REG; @@ -487,7 +486,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; case 0x90: - *type = INSN_NOP; + insn->type = INSN_NOP; break; case 0x9c: @@ -511,39 +510,39 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec if (op2 == 0x01) { if (modrm == 0xca) - *type = INSN_CLAC; + insn->type = INSN_CLAC; else if (modrm == 0xcb) - *type = INSN_STAC; + insn->type = INSN_STAC; } else if (op2 >= 0x80 && op2 <= 0x8f) { - *type = INSN_JUMP_CONDITIONAL; + insn->type = INSN_JUMP_CONDITIONAL; } else if (op2 == 0x05 || op2 == 0x07 || op2 == 0x34 || op2 == 0x35) { /* sysenter, sysret */ - *type = INSN_CONTEXT_SWITCH; + insn->type = INSN_CONTEXT_SWITCH; } else if (op2 == 0x0b || op2 == 0xb9) { /* ud2 */ - *type = INSN_BUG; + insn->type = INSN_BUG; } else if (op2 == 0x0d || op2 == 0x1f) { /* nopl/nopw */ - *type = INSN_NOP; + insn->type = INSN_NOP; } else if (op2 == 0x1e) { if (prefix == 0xf3 && (modrm == 0xfa || modrm == 0xfb)) - *type = INSN_ENDBR; + insn->type = INSN_ENDBR; } else if (op2 == 0x38 && op3 == 0xf8) { - if (insn.prefixes.nbytes == 1 && - insn.prefixes.bytes[0] == 0xf2) { + if (ins.prefixes.nbytes == 1 && + ins.prefixes.bytes[0] == 0xf2) { /* ENQCMD cannot be used in the kernel. */ WARN("ENQCMD instruction at %s:%lx", sec->name, offset); @@ -591,29 +590,29 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec case 0xcc: /* int3 */ - *type = INSN_TRAP; + insn->type = INSN_TRAP; break; case 0xe3: /* jecxz/jrcxz */ - *type = INSN_JUMP_CONDITIONAL; + insn->type = INSN_JUMP_CONDITIONAL; break; case 0xe9: case 0xeb: - *type = INSN_JUMP_UNCONDITIONAL; + insn->type = INSN_JUMP_UNCONDITIONAL; break; case 0xc2: case 0xc3: - *type = INSN_RETURN; + insn->type = INSN_RETURN; break; case 0xc7: /* mov imm, r/m */ if (!opts.noinstr) break; - if (insn.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) { + if (ins.length == 3+4+4 && !strncmp(sec->name, ".init.text", 10)) { struct reloc *immr, *disp; struct symbol *func; int idx; @@ -661,17 +660,17 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec case 0xca: /* retf */ case 0xcb: /* retf */ - *type = INSN_CONTEXT_SWITCH; + insn->type = INSN_CONTEXT_SWITCH; break; case 0xe0: /* loopne */ case 0xe1: /* loope */ case 0xe2: /* loop */ - *type = INSN_JUMP_CONDITIONAL; + insn->type = INSN_JUMP_CONDITIONAL; break; case 0xe8: - *type = INSN_CALL; + insn->type = INSN_CALL; /* * For the impact on the stack, a CALL behaves like * a PUSH of an immediate value (the return address). @@ -683,30 +682,30 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; case 0xfc: - *type = INSN_CLD; + insn->type = INSN_CLD; break; case 0xfd: - *type = INSN_STD; + insn->type = INSN_STD; break; case 0xff: if (modrm_reg == 2 || modrm_reg == 3) { - *type = INSN_CALL_DYNAMIC; - if (has_notrack_prefix(&insn)) + insn->type = INSN_CALL_DYNAMIC; + if (has_notrack_prefix(&ins)) WARN("notrack prefix found at %s:0x%lx", sec->name, offset); } else if (modrm_reg == 4) { - *type = INSN_JUMP_DYNAMIC; - if (has_notrack_prefix(&insn)) + insn->type = INSN_JUMP_DYNAMIC; + if (has_notrack_prefix(&ins)) WARN("notrack prefix found at %s:0x%lx", sec->name, offset); } else if (modrm_reg == 5) { /* jmpf */ - *type = INSN_CONTEXT_SWITCH; + insn->type = INSN_CONTEXT_SWITCH; } else if (modrm_reg == 6) { @@ -723,7 +722,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec break; } - *immediate = insn.immediate.nbytes ? insn.immediate.value : 0; + insn->immediate = ins.immediate.nbytes ? ins.immediate.value : 0; return 0; } diff --git a/tools/objtool/check.c b/tools/objtool/check.c index ba07a8ebaf73..b3b423d33cc2 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -406,9 +406,7 @@ static int decode_instructions(struct objtool_file *file) ret = arch_decode_instruction(file, sec, offset, sec->sh.sh_size - offset, - &insn->len, &insn->type, - &insn->immediate, - &insn->stack_ops); + insn); if (ret) goto err; diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index 4ecb480131c7..73149f8090fa 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -75,9 +75,7 @@ void arch_initial_func_cfi_state(struct cfi_init_state *state); int arch_decode_instruction(struct objtool_file *file, const struct section *sec, unsigned long offset, unsigned int maxlen, - unsigned int *len, enum insn_type *type, - unsigned long *immediate, - struct list_head *ops_list); + struct instruction *insn); bool arch_callee_saved_reg(unsigned char reg); From 3ee88df1b063962e39d7798ccc3b18fd10cea813 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:17:58 +0100 Subject: [PATCH 14/22] objtool: Make instruction::stack_ops a single-linked list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct instruction { struct list_head list; /* 0 16 */ struct hlist_node hash; /* 16 16 */ struct list_head call_node; /* 32 16 */ struct section * sec; /* 48 8 */ long unsigned int offset; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ unsigned int len; /* 64 4 */ enum insn_type type; /* 68 4 */ long unsigned int immediate; /* 72 8 */ u16 dead_end:1; /* 80: 0 2 */ u16 ignore:1; /* 80: 1 2 */ u16 ignore_alts:1; /* 80: 2 2 */ u16 hint:1; /* 80: 3 2 */ u16 save:1; /* 80: 4 2 */ u16 restore:1; /* 80: 5 2 */ u16 retpoline_safe:1; /* 80: 6 2 */ u16 noendbr:1; /* 80: 7 2 */ u16 entry:1; /* 80: 8 2 */ /* XXX 7 bits hole, try to pack */ s8 instr; /* 82 1 */ u8 visited; /* 83 1 */ /* XXX 4 bytes hole, try to pack */ struct alt_group * alt_group; /* 88 8 */ struct symbol * call_dest; /* 96 8 */ struct instruction * jump_dest; /* 104 8 */ struct instruction * first_jump_src; /* 112 8 */ struct reloc * jump_table; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ struct reloc * reloc; /* 128 8 */ struct list_head alts; /* 136 16 */ struct symbol * sym; /* 152 8 */ - struct list_head stack_ops; /* 160 16 */ - struct cfi_state * cfi; /* 176 8 */ + struct stack_op * stack_ops; /* 160 8 */ + struct cfi_state * cfi; /* 168 8 */ - /* size: 184, cachelines: 3, members: 29 */ - /* sum members: 178, holes: 1, sum holes: 4 */ + /* size: 176, cachelines: 3, members: 29 */ + /* sum members: 170, holes: 1, sum holes: 4 */ /* sum bitfield members: 9 bits, bit holes: 1, sum bit holes: 7 bits */ - /* last cacheline: 56 bytes */ + /* last cacheline: 48 bytes */ }; pre: 5:58.22 real, 226.69 user, 131.22 sys, 26221520 mem post: 5:58.50 real, 229.64 user, 128.65 sys, 26221520 mem Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.362196959@infradead.org --- tools/objtool/arch/x86/decode.c | 4 ++-- tools/objtool/check.c | 11 +++++------ tools/objtool/include/objtool/arch.h | 2 +- tools/objtool/include/objtool/check.h | 2 +- 4 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c index c5c49277cf1a..9ef024fd648c 100644 --- a/tools/objtool/arch/x86/decode.c +++ b/tools/objtool/arch/x86/decode.c @@ -105,7 +105,7 @@ bool arch_pc_relative_reloc(struct reloc *reloc) #define ADD_OP(op) \ if (!(op = calloc(1, sizeof(*op)))) \ return -1; \ - else for (list_add_tail(&op->list, ops_list); op; op = NULL) + else for (*ops_list = op, ops_list = &op->next; op; op = NULL) /* * Helpers to decode ModRM/SIB: @@ -148,7 +148,7 @@ int arch_decode_instruction(struct objtool_file *file, const struct section *sec unsigned long offset, unsigned int maxlen, struct instruction *insn) { - struct list_head *ops_list = &insn->stack_ops; + struct stack_op **ops_list = &insn->stack_ops; const struct elf *elf = file->elf; struct insn ins; int x86_64, ret; diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b3b423d33cc2..8109d7405297 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -398,7 +398,6 @@ static int decode_instructions(struct objtool_file *file) } memset(insn, 0, sizeof(*insn)); INIT_LIST_HEAD(&insn->alts); - INIT_LIST_HEAD(&insn->stack_ops); INIT_LIST_HEAD(&insn->call_node); insn->sec = sec; @@ -1331,12 +1330,13 @@ static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *i static void remove_insn_ops(struct instruction *insn) { - struct stack_op *op, *tmp; + struct stack_op *op, *next; - list_for_each_entry_safe(op, tmp, &insn->stack_ops, list) { - list_del(&op->list); + for (op = insn->stack_ops; op; op = next) { + next = op->next; free(op); } + insn->stack_ops = NULL; } static void annotate_call_site(struct objtool_file *file, @@ -1781,7 +1781,6 @@ static int handle_group_alt(struct objtool_file *file, } memset(nop, 0, sizeof(*nop)); INIT_LIST_HEAD(&nop->alts); - INIT_LIST_HEAD(&nop->stack_ops); nop->sec = special_alt->new_sec; nop->offset = special_alt->new_off + special_alt->new_len; @@ -3226,7 +3225,7 @@ static int handle_insn_ops(struct instruction *insn, { struct stack_op *op; - list_for_each_entry(op, &insn->stack_ops, list) { + for (op = insn->stack_ops; op; op = op->next) { if (update_cfi_state(insn, next_insn, &state->cfi, op)) return 1; diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h index 73149f8090fa..2b6d2ce4f9a5 100644 --- a/tools/objtool/include/objtool/arch.h +++ b/tools/objtool/include/objtool/arch.h @@ -62,9 +62,9 @@ struct op_src { }; struct stack_op { + struct stack_op *next; struct op_dest dest; struct op_src src; - struct list_head list; }; struct instruction; diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index acd7fae59348..23e981999365 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -68,7 +68,7 @@ struct instruction { struct reloc *reloc; struct list_head alts; struct symbol *sym; - struct list_head stack_ops; + struct stack_op *stack_ops; struct cfi_state *cfi; }; From d54066546121426ecd7ad01a53ae429c4e37a9d5 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:17:59 +0100 Subject: [PATCH 15/22] objtool: Make instruction::alts a single-linked list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit struct instruction { struct list_head list; /* 0 16 */ struct hlist_node hash; /* 16 16 */ struct list_head call_node; /* 32 16 */ struct section * sec; /* 48 8 */ long unsigned int offset; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ unsigned int len; /* 64 4 */ enum insn_type type; /* 68 4 */ long unsigned int immediate; /* 72 8 */ u16 dead_end:1; /* 80: 0 2 */ u16 ignore:1; /* 80: 1 2 */ u16 ignore_alts:1; /* 80: 2 2 */ u16 hint:1; /* 80: 3 2 */ u16 save:1; /* 80: 4 2 */ u16 restore:1; /* 80: 5 2 */ u16 retpoline_safe:1; /* 80: 6 2 */ u16 noendbr:1; /* 80: 7 2 */ u16 entry:1; /* 80: 8 2 */ /* XXX 7 bits hole, try to pack */ s8 instr; /* 82 1 */ u8 visited; /* 83 1 */ /* XXX 4 bytes hole, try to pack */ struct alt_group * alt_group; /* 88 8 */ struct symbol * call_dest; /* 96 8 */ struct instruction * jump_dest; /* 104 8 */ struct instruction * first_jump_src; /* 112 8 */ struct reloc * jump_table; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ struct reloc * reloc; /* 128 8 */ - struct list_head alts; /* 136 16 */ - struct symbol * sym; /* 152 8 */ - struct stack_op * stack_ops; /* 160 8 */ - struct cfi_state * cfi; /* 168 8 */ + struct alternative * alts; /* 136 8 */ + struct symbol * sym; /* 144 8 */ + struct stack_op * stack_ops; /* 152 8 */ + struct cfi_state * cfi; /* 160 8 */ - /* size: 176, cachelines: 3, members: 29 */ - /* sum members: 170, holes: 1, sum holes: 4 */ + /* size: 168, cachelines: 3, members: 29 */ + /* sum members: 162, holes: 1, sum holes: 4 */ /* sum bitfield members: 9 bits, bit holes: 1, sum bit holes: 7 bits */ - /* last cacheline: 48 bytes */ + /* last cacheline: 40 bytes */ }; pre: 5:58.50 real, 229.64 user, 128.65 sys, 26221520 mem post: 5:48.86 real, 220.30 user, 128.34 sys, 24834672 mem Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.430556498@infradead.org --- tools/objtool/check.c | 18 +++++++++--------- tools/objtool/include/objtool/check.h | 2 +- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 8109d7405297..9f83e85e2093 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -23,7 +23,7 @@ #include struct alternative { - struct list_head list; + struct alternative *next; struct instruction *insn; bool skip_orig; }; @@ -397,7 +397,6 @@ static int decode_instructions(struct objtool_file *file) return -1; } memset(insn, 0, sizeof(*insn)); - INIT_LIST_HEAD(&insn->alts); INIT_LIST_HEAD(&insn->call_node); insn->sec = sec; @@ -1780,7 +1779,6 @@ static int handle_group_alt(struct objtool_file *file, return -1; } memset(nop, 0, sizeof(*nop)); - INIT_LIST_HEAD(&nop->alts); nop->sec = special_alt->new_sec; nop->offset = special_alt->new_off + special_alt->new_len; @@ -1978,7 +1976,8 @@ static int add_special_section_alts(struct objtool_file *file) alt->insn = new_insn; alt->skip_orig = special_alt->skip_orig; orig_insn->ignore_alts |= special_alt->skip_alt; - list_add_tail(&alt->list, &orig_insn->alts); + alt->next = orig_insn->alts; + orig_insn->alts = alt; list_del(&special_alt->list); free(special_alt); @@ -2037,7 +2036,8 @@ static int add_jump_table(struct objtool_file *file, struct instruction *insn, } alt->insn = dest_insn; - list_add_tail(&alt->list, &insn->alts); + alt->next = insn->alts; + insn->alts = alt; prev_offset = reloc->offset; } @@ -3594,10 +3594,10 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, if (propagate_alt_cfi(file, insn)) return 1; - if (!insn->ignore_alts && !list_empty(&insn->alts)) { + if (!insn->ignore_alts && insn->alts) { bool skip_orig = false; - list_for_each_entry(alt, &insn->alts, list) { + for (alt = insn->alts; alt; alt = alt->next) { if (alt->skip_orig) skip_orig = true; @@ -3796,11 +3796,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn) insn->visited |= VISITED_ENTRY; - if (!insn->ignore_alts && !list_empty(&insn->alts)) { + if (!insn->ignore_alts && insn->alts) { struct alternative *alt; bool skip_orig = false; - list_for_each_entry(alt, &insn->alts, list) { + for (alt = insn->alts; alt; alt = alt->next) { if (alt->skip_orig) skip_orig = true; diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index 23e981999365..7966f60f858b 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -66,7 +66,7 @@ struct instruction { struct instruction *first_jump_src; struct reloc *jump_table; struct reloc *reloc; - struct list_head alts; + struct alternative *alts; struct symbol *sym; struct stack_op *stack_ops; struct cfi_state *cfi; From 8b2de412158ecdb312c707918432e6650df808cc Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:18:00 +0100 Subject: [PATCH 16/22] objtool: Shrink instruction::{type,visited} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since we don't have that many types in enum insn_type, force it into a u8 and re-arrange member to get rid of the holes, saves another 8 bytes. struct instruction { struct list_head list; /* 0 16 */ struct hlist_node hash; /* 16 16 */ struct list_head call_node; /* 32 16 */ struct section * sec; /* 48 8 */ long unsigned int offset; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ - unsigned int len; /* 64 4 */ - enum insn_type type; /* 68 4 */ - long unsigned int immediate; /* 72 8 */ - u16 dead_end:1; /* 80: 0 2 */ - u16 ignore:1; /* 80: 1 2 */ - u16 ignore_alts:1; /* 80: 2 2 */ - u16 hint:1; /* 80: 3 2 */ - u16 save:1; /* 80: 4 2 */ - u16 restore:1; /* 80: 5 2 */ - u16 retpoline_safe:1; /* 80: 6 2 */ - u16 noendbr:1; /* 80: 7 2 */ - u16 entry:1; /* 80: 8 2 */ + long unsigned int immediate; /* 64 8 */ + unsigned int len; /* 72 4 */ + u8 type; /* 76 1 */ - /* XXX 7 bits hole, try to pack */ + /* Bitfield combined with previous fields */ - s8 instr; /* 82 1 */ - u8 visited; /* 83 1 */ + u16 dead_end:1; /* 76: 8 2 */ + u16 ignore:1; /* 76: 9 2 */ + u16 ignore_alts:1; /* 76:10 2 */ + u16 hint:1; /* 76:11 2 */ + u16 save:1; /* 76:12 2 */ + u16 restore:1; /* 76:13 2 */ + u16 retpoline_safe:1; /* 76:14 2 */ + u16 noendbr:1; /* 76:15 2 */ + u16 entry:1; /* 78: 0 2 */ + u16 visited:4; /* 78: 1 2 */ - /* XXX 4 bytes hole, try to pack */ + /* XXX 3 bits hole, try to pack */ + /* Bitfield combined with next fields */ - struct alt_group * alt_group; /* 88 8 */ - struct symbol * call_dest; /* 96 8 */ - struct instruction * jump_dest; /* 104 8 */ - struct instruction * first_jump_src; /* 112 8 */ - struct reloc * jump_table; /* 120 8 */ + s8 instr; /* 79 1 */ + struct alt_group * alt_group; /* 80 8 */ + struct symbol * call_dest; /* 88 8 */ + struct instruction * jump_dest; /* 96 8 */ + struct instruction * first_jump_src; /* 104 8 */ + struct reloc * jump_table; /* 112 8 */ + struct reloc * reloc; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ - struct reloc * reloc; /* 128 8 */ - struct alternative * alts; /* 136 8 */ - struct symbol * sym; /* 144 8 */ - struct stack_op * stack_ops; /* 152 8 */ - struct cfi_state * cfi; /* 160 8 */ + struct alternative * alts; /* 128 8 */ + struct symbol * sym; /* 136 8 */ + struct stack_op * stack_ops; /* 144 8 */ + struct cfi_state * cfi; /* 152 8 */ - /* size: 168, cachelines: 3, members: 29 */ - /* sum members: 162, holes: 1, sum holes: 4 */ - /* sum bitfield members: 9 bits, bit holes: 1, sum bit holes: 7 bits */ - /* last cacheline: 40 bytes */ + /* size: 160, cachelines: 3, members: 29 */ + /* sum members: 158 */ + /* sum bitfield members: 13 bits, bit holes: 1, sum bit holes: 3 bits */ + /* last cacheline: 32 bytes */ }; pre: 5:48.86 real, 220.30 user, 128.34 sys, 24834672 mem post: 5:48.89 real, 220.96 user, 127.55 sys, 24834672 mem Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.501847188@infradead.org --- tools/objtool/include/objtool/check.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index 7966f60f858b..a497ee7672fb 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -42,9 +42,9 @@ struct instruction { struct list_head call_node; struct section *sec; unsigned long offset; - unsigned int len; - enum insn_type type; unsigned long immediate; + unsigned int len; + u8 type; u16 dead_end : 1, ignore : 1, @@ -54,11 +54,11 @@ struct instruction { restore : 1, retpoline_safe : 1, noendbr : 1, - entry : 1; - /* 7 bit hole */ + entry : 1, + visited : 4; + /* 3 bit hole */ s8 instr; - u8 visited; struct alt_group *alt_group; struct symbol *call_dest; From 0932dbe1f5680481e612cafe0c7d0f1796f68612 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:18:01 +0100 Subject: [PATCH 17/22] objtool: Remove instruction::reloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Instead of caching the reloc for each instruction, only keep a negative cache of not having a reloc (by far the most common case). struct instruction { struct list_head list; /* 0 16 */ struct hlist_node hash; /* 16 16 */ struct list_head call_node; /* 32 16 */ struct section * sec; /* 48 8 */ long unsigned int offset; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int immediate; /* 64 8 */ unsigned int len; /* 72 4 */ u8 type; /* 76 1 */ /* Bitfield combined with previous fields */ u16 dead_end:1; /* 76: 8 2 */ u16 ignore:1; /* 76: 9 2 */ u16 ignore_alts:1; /* 76:10 2 */ u16 hint:1; /* 76:11 2 */ u16 save:1; /* 76:12 2 */ u16 restore:1; /* 76:13 2 */ u16 retpoline_safe:1; /* 76:14 2 */ u16 noendbr:1; /* 76:15 2 */ u16 entry:1; /* 78: 0 2 */ u16 visited:4; /* 78: 1 2 */ + u16 no_reloc:1; /* 78: 5 2 */ - /* XXX 3 bits hole, try to pack */ + /* XXX 2 bits hole, try to pack */ /* Bitfield combined with next fields */ s8 instr; /* 79 1 */ struct alt_group * alt_group; /* 80 8 */ struct symbol * call_dest; /* 88 8 */ struct instruction * jump_dest; /* 96 8 */ struct instruction * first_jump_src; /* 104 8 */ struct reloc * jump_table; /* 112 8 */ - struct reloc * reloc; /* 120 8 */ + struct alternative * alts; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ - struct alternative * alts; /* 128 8 */ - struct symbol * sym; /* 136 8 */ - struct stack_op * stack_ops; /* 144 8 */ - struct cfi_state * cfi; /* 152 8 */ + struct symbol * sym; /* 128 8 */ + struct stack_op * stack_ops; /* 136 8 */ + struct cfi_state * cfi; /* 144 8 */ - /* size: 160, cachelines: 3, members: 29 */ - /* sum members: 158 */ - /* sum bitfield members: 13 bits, bit holes: 1, sum bit holes: 3 bits */ - /* last cacheline: 32 bytes */ + /* size: 152, cachelines: 3, members: 29 */ + /* sum members: 150 */ + /* sum bitfield members: 14 bits, bit holes: 1, sum bit holes: 2 bits */ + /* last cacheline: 24 bytes */ }; pre: 5:48.89 real, 220.96 user, 127.55 sys, 24834672 mem post: 5:39.35 real, 215.58 user, 123.69 sys, 23448736 mem Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.572145269@infradead.org --- tools/objtool/check.c | 24 +++++++++++------------- tools/objtool/include/objtool/check.h | 6 +++--- 2 files changed, 14 insertions(+), 16 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 9f83e85e2093..6d0ce2395554 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1305,26 +1305,24 @@ __weak bool arch_is_rethunk(struct symbol *sym) return false; } -#define NEGATIVE_RELOC ((void *)-1L) - static struct reloc *insn_reloc(struct objtool_file *file, struct instruction *insn) { - if (insn->reloc == NEGATIVE_RELOC) + struct reloc *reloc; + + if (insn->no_reloc) return NULL; - if (!insn->reloc) { - if (!file) - return NULL; + if (!file) + return NULL; - insn->reloc = find_reloc_by_dest_range(file->elf, insn->sec, - insn->offset, insn->len); - if (!insn->reloc) { - insn->reloc = NEGATIVE_RELOC; - return NULL; - } + reloc = find_reloc_by_dest_range(file->elf, insn->sec, + insn->offset, insn->len); + if (!reloc) { + insn->no_reloc = 1; + return NULL; } - return insn->reloc; + return reloc; } static void remove_insn_ops(struct instruction *insn) diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index a497ee7672fb..fffc8b86f9f3 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -55,8 +55,9 @@ struct instruction { retpoline_safe : 1, noendbr : 1, entry : 1, - visited : 4; - /* 3 bit hole */ + visited : 4, + no_reloc : 1; + /* 2 bit hole */ s8 instr; @@ -65,7 +66,6 @@ struct instruction { struct instruction *jump_dest; struct instruction *first_jump_src; struct reloc *jump_table; - struct reloc *reloc; struct alternative *alts; struct symbol *sym; struct stack_op *stack_ops; From c6f5dc28fb3d736fa8d7f7d31e0664a9c772c299 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:18:02 +0100 Subject: [PATCH 18/22] objtool: Union instruction::{call_dest,jump_table} MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The instruction call_dest and jump_table members can never be used at the same time, their usage depends on type. struct instruction { struct list_head list; /* 0 16 */ struct hlist_node hash; /* 16 16 */ struct list_head call_node; /* 32 16 */ struct section * sec; /* 48 8 */ long unsigned int offset; /* 56 8 */ /* --- cacheline 1 boundary (64 bytes) --- */ long unsigned int immediate; /* 64 8 */ unsigned int len; /* 72 4 */ u8 type; /* 76 1 */ /* Bitfield combined with previous fields */ u16 dead_end:1; /* 76: 8 2 */ u16 ignore:1; /* 76: 9 2 */ u16 ignore_alts:1; /* 76:10 2 */ u16 hint:1; /* 76:11 2 */ u16 save:1; /* 76:12 2 */ u16 restore:1; /* 76:13 2 */ u16 retpoline_safe:1; /* 76:14 2 */ u16 noendbr:1; /* 76:15 2 */ u16 entry:1; /* 78: 0 2 */ u16 visited:4; /* 78: 1 2 */ u16 no_reloc:1; /* 78: 5 2 */ /* XXX 2 bits hole, try to pack */ /* Bitfield combined with next fields */ s8 instr; /* 79 1 */ struct alt_group * alt_group; /* 80 8 */ - struct symbol * call_dest; /* 88 8 */ - struct instruction * jump_dest; /* 96 8 */ - struct instruction * first_jump_src; /* 104 8 */ - struct reloc * jump_table; /* 112 8 */ - struct alternative * alts; /* 120 8 */ + struct instruction * jump_dest; /* 88 8 */ + struct instruction * first_jump_src; /* 96 8 */ + union { + struct symbol * _call_dest; /* 104 8 */ + struct reloc * _jump_table; /* 104 8 */ + }; /* 104 8 */ + struct alternative * alts; /* 112 8 */ + struct symbol * sym; /* 120 8 */ /* --- cacheline 2 boundary (128 bytes) --- */ - struct symbol * sym; /* 128 8 */ - struct stack_op * stack_ops; /* 136 8 */ - struct cfi_state * cfi; /* 144 8 */ + struct stack_op * stack_ops; /* 128 8 */ + struct cfi_state * cfi; /* 136 8 */ - /* size: 152, cachelines: 3, members: 29 */ - /* sum members: 150 */ + /* size: 144, cachelines: 3, members: 28 */ + /* sum members: 142 */ /* sum bitfield members: 14 bits, bit holes: 1, sum bit holes: 2 bits */ - /* last cacheline: 24 bytes */ + /* last cacheline: 16 bytes */ }; pre: 5:39.35 real, 215.58 user, 123.69 sys, 23448736 mem post: 5:38.18 real, 213.25 user, 124.90 sys, 23449040 mem Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.640914454@infradead.org --- tools/objtool/check.c | 73 +++++++++++++++++---------- tools/objtool/include/objtool/check.h | 6 ++- 2 files changed, 50 insertions(+), 29 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 6d0ce2395554..6f0adb2f76c6 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -114,16 +114,34 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file, for (insn = next_insn_same_sec(file, insn); insn; \ insn = next_insn_same_sec(file, insn)) +static inline struct symbol *insn_call_dest(struct instruction *insn) +{ + if (insn->type == INSN_JUMP_DYNAMIC || + insn->type == INSN_CALL_DYNAMIC) + return NULL; + + return insn->_call_dest; +} + +static inline struct reloc *insn_jump_table(struct instruction *insn) +{ + if (insn->type == INSN_JUMP_DYNAMIC || + insn->type == INSN_CALL_DYNAMIC) + return insn->_jump_table; + + return NULL; +} + static bool is_jump_table_jump(struct instruction *insn) { struct alt_group *alt_group = insn->alt_group; - if (insn->jump_table) + if (insn_jump_table(insn)) return true; /* Retpoline alternative for a jump table? */ return alt_group && alt_group->orig_group && - alt_group->orig_group->first_insn->jump_table; + insn_jump_table(alt_group->orig_group->first_insn); } static bool is_sibling_call(struct instruction *insn) @@ -137,8 +155,8 @@ static bool is_sibling_call(struct instruction *insn) return !is_jump_table_jump(insn); } - /* add_jump_destinations() sets insn->call_dest for sibling calls. */ - return (is_static_jump(insn) && insn->call_dest); + /* add_jump_destinations() sets insn_call_dest(insn) for sibling calls. */ + return (is_static_jump(insn) && insn_call_dest(insn)); } /* @@ -274,8 +292,8 @@ static void init_insn_state(struct objtool_file *file, struct insn_state *state, /* * We need the full vmlinux for noinstr validation, otherwise we can - * not correctly determine insn->call_dest->sec (external symbols do - * not have a section). + * not correctly determine insn_call_dest(insn)->sec (external symbols + * do not have a section). */ if (opts.link && opts.noinstr && sec) state->noinstr = sec->noinstr; @@ -678,7 +696,7 @@ static int create_static_call_sections(struct objtool_file *file) return -1; /* find key symbol */ - key_name = strdup(insn->call_dest->name); + key_name = strdup(insn_call_dest(insn)->name); if (!key_name) { perror("strdup"); return -1; @@ -709,7 +727,7 @@ static int create_static_call_sections(struct objtool_file *file) * trampoline address. This is fixed up in * static_call_add_module(). */ - key_sym = insn->call_dest; + key_sym = insn_call_dest(insn); } free(key_name); @@ -1340,7 +1358,7 @@ static void annotate_call_site(struct objtool_file *file, struct instruction *insn, bool sibling) { struct reloc *reloc = insn_reloc(file, insn); - struct symbol *sym = insn->call_dest; + struct symbol *sym = insn_call_dest(insn); if (!sym) sym = reloc->sym; @@ -1425,7 +1443,7 @@ static void annotate_call_site(struct objtool_file *file, static void add_call_dest(struct objtool_file *file, struct instruction *insn, struct symbol *dest, bool sibling) { - insn->call_dest = dest; + insn->_call_dest = dest; if (!dest) return; @@ -1683,12 +1701,12 @@ static int add_call_destinations(struct objtool_file *file) if (insn->ignore) continue; - if (!insn->call_dest) { + if (!insn_call_dest(insn)) { WARN_FUNC("unannotated intra-function call", insn->sec, insn->offset); return -1; } - if (insn_func(insn) && insn->call_dest->type != STT_FUNC) { + if (insn_func(insn) && insn_call_dest(insn)->type != STT_FUNC) { WARN_FUNC("unsupported call to non-function", insn->sec, insn->offset); return -1; @@ -2125,7 +2143,7 @@ static void mark_func_jump_tables(struct objtool_file *file, reloc = find_jump_table(file, func, insn); if (reloc) { reloc->jump_table_start = true; - insn->jump_table = reloc; + insn->_jump_table = reloc; } } } @@ -2137,10 +2155,10 @@ static int add_func_jump_tables(struct objtool_file *file, int ret; func_for_each_insn(file, func, insn) { - if (!insn->jump_table) + if (!insn_jump_table(insn)) continue; - ret = add_jump_table(file, insn, insn->jump_table); + ret = add_jump_table(file, insn, insn_jump_table(insn)); if (ret) return ret; } @@ -2612,8 +2630,8 @@ static int decode_sections(struct objtool_file *file) static bool is_fentry_call(struct instruction *insn) { if (insn->type == INSN_CALL && - insn->call_dest && - insn->call_dest->fentry) + insn_call_dest(insn) && + insn_call_dest(insn)->fentry) return true; return false; @@ -3320,8 +3338,8 @@ static inline const char *call_dest_name(struct instruction *insn) struct reloc *rel; int idx; - if (insn->call_dest) - return insn->call_dest->name; + if (insn_call_dest(insn)) + return insn_call_dest(insn)->name; rel = insn_reloc(NULL, insn); if (rel && !strcmp(rel->sym->name, "pv_ops")) { @@ -3403,13 +3421,13 @@ static int validate_call(struct objtool_file *file, struct insn_state *state) { if (state->noinstr && state->instr <= 0 && - !noinstr_call_dest(file, insn, insn->call_dest)) { + !noinstr_call_dest(file, insn, insn_call_dest(insn))) { WARN_FUNC("call to %s() leaves .noinstr.text section", insn->sec, insn->offset, call_dest_name(insn)); return 1; } - if (state->uaccess && !func_uaccess_safe(insn->call_dest)) { + if (state->uaccess && !func_uaccess_safe(insn_call_dest(insn))) { WARN_FUNC("call to %s() with UACCESS enabled", insn->sec, insn->offset, call_dest_name(insn)); return 1; @@ -3847,11 +3865,11 @@ static int validate_entry(struct objtool_file *file, struct instruction *insn) /* fallthrough */ case INSN_CALL: - dest = find_insn(file, insn->call_dest->sec, - insn->call_dest->offset); + dest = find_insn(file, insn_call_dest(insn)->sec, + insn_call_dest(insn)->offset); if (!dest) { WARN("Unresolved function after linking!?: %s", - insn->call_dest->name); + insn_call_dest(insn)->name); return -1; } @@ -3952,13 +3970,13 @@ static int validate_retpoline(struct objtool_file *file) static bool is_kasan_insn(struct instruction *insn) { return (insn->type == INSN_CALL && - !strcmp(insn->call_dest->name, "__asan_handle_no_return")); + !strcmp(insn_call_dest(insn)->name, "__asan_handle_no_return")); } static bool is_ubsan_insn(struct instruction *insn) { return (insn->type == INSN_CALL && - !strcmp(insn->call_dest->name, + !strcmp(insn_call_dest(insn)->name, "__ubsan_handle_builtin_unreachable")); } @@ -4036,7 +4054,8 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio * It may also insert a UD2 after calling a __noreturn function. */ prev_insn = list_prev_entry(insn, list); - if ((prev_insn->dead_end || dead_end_function(file, prev_insn->call_dest)) && + if ((prev_insn->dead_end || + dead_end_function(file, insn_call_dest(prev_insn))) && (insn->type == INSN_BUG || (insn->type == INSN_JUMP_UNCONDITIONAL && insn->jump_dest && insn->jump_dest->type == INSN_BUG))) diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index fffc8b86f9f3..ab6deaed9777 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -62,10 +62,12 @@ struct instruction { s8 instr; struct alt_group *alt_group; - struct symbol *call_dest; struct instruction *jump_dest; struct instruction *first_jump_src; - struct reloc *jump_table; + union { + struct symbol *_call_dest; + struct reloc *_jump_table; + }; struct alternative *alts; struct symbol *sym; struct stack_op *stack_ops; From a706bb08c81ac878982e41d4b6abcc42258bd39e Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:18:03 +0100 Subject: [PATCH 19/22] objtool: Fix overlapping alternatives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Things like ALTERNATIVE_{2,3}() generate multiple alternatives on the same place, objtool would override the first orig_alt_group with the second (or third), failing to check the CFI among all the different variants. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.711471461@infradead.org --- tools/objtool/check.c | 69 +++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 6f0adb2f76c6..7e9d3d3eed65 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -1744,36 +1744,49 @@ static int handle_group_alt(struct objtool_file *file, struct instruction *orig_insn, struct instruction **new_insn) { - struct instruction *last_orig_insn, *last_new_insn = NULL, *insn, *nop = NULL; + struct instruction *last_new_insn = NULL, *insn, *nop = NULL; struct alt_group *orig_alt_group, *new_alt_group; unsigned long dest_off; - - orig_alt_group = malloc(sizeof(*orig_alt_group)); + orig_alt_group = orig_insn->alt_group; if (!orig_alt_group) { - WARN("malloc failed"); - return -1; - } - orig_alt_group->cfi = calloc(special_alt->orig_len, - sizeof(struct cfi_state *)); - if (!orig_alt_group->cfi) { - WARN("calloc failed"); - return -1; - } + struct instruction *last_orig_insn = NULL; - last_orig_insn = NULL; - insn = orig_insn; - sec_for_each_insn_from(file, insn) { - if (insn->offset >= special_alt->orig_off + special_alt->orig_len) - break; + orig_alt_group = malloc(sizeof(*orig_alt_group)); + if (!orig_alt_group) { + WARN("malloc failed"); + return -1; + } + orig_alt_group->cfi = calloc(special_alt->orig_len, + sizeof(struct cfi_state *)); + if (!orig_alt_group->cfi) { + WARN("calloc failed"); + return -1; + } - insn->alt_group = orig_alt_group; - last_orig_insn = insn; + insn = orig_insn; + sec_for_each_insn_from(file, insn) { + if (insn->offset >= special_alt->orig_off + special_alt->orig_len) + break; + + insn->alt_group = orig_alt_group; + last_orig_insn = insn; + } + orig_alt_group->orig_group = NULL; + orig_alt_group->first_insn = orig_insn; + orig_alt_group->last_insn = last_orig_insn; + } else { + if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len - + orig_alt_group->first_insn->offset != special_alt->orig_len) { + WARN_FUNC("weirdly overlapping alternative! %ld != %d", + orig_insn->sec, orig_insn->offset, + orig_alt_group->last_insn->offset + + orig_alt_group->last_insn->len - + orig_alt_group->first_insn->offset, + special_alt->orig_len); + return -1; + } } - orig_alt_group->orig_group = NULL; - orig_alt_group->first_insn = orig_insn; - orig_alt_group->last_insn = last_orig_insn; - new_alt_group = malloc(sizeof(*new_alt_group)); if (!new_alt_group) { @@ -1848,7 +1861,7 @@ static int handle_group_alt(struct objtool_file *file, dest_off = arch_jump_destination(insn); if (dest_off == special_alt->new_off + special_alt->new_len) { - insn->jump_dest = next_insn_same_sec(file, last_orig_insn); + insn->jump_dest = next_insn_same_sec(file, orig_alt_group->last_insn); if (!insn->jump_dest) { WARN_FUNC("can't find alternative jump destination", insn->sec, insn->offset); @@ -3226,8 +3239,12 @@ static int propagate_alt_cfi(struct objtool_file *file, struct instruction *insn alt_cfi[group_off] = insn->cfi; } else { if (cficmp(alt_cfi[group_off], insn->cfi)) { - WARN_FUNC("stack layout conflict in alternatives", - insn->sec, insn->offset); + struct alt_group *orig_group = insn->alt_group->orig_group ?: insn->alt_group; + struct instruction *orig = orig_group->first_insn; + char *where = offstr(insn->sec, insn->offset); + WARN_FUNC("stack layout conflict in alternatives: %s", + orig->sec, orig->offset, where); + free(where); return -1; } } From 6ea17e848a8ba5138b30e936c4b71877bc972c13 Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:18:04 +0100 Subject: [PATCH 20/22] x86: Fix FILL_RETURN_BUFFER MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit With overlapping alternative validation fixed, objtool promptly complains: vmlinux.o: warning: objtool: __switch_to_asm+0x2c: stack layout conflict in alternatives: .altinstr_replacement+0x47 .rela.altinstructions: 000000000000009c 0000000200000002 R_X86_64_PC32 0000000000000000 .text + 16dc 00000000000000a0 0000000600000002 R_X86_64_PC32 0000000000000000 .altinstr_replacement + 3a 00000000000000a8 0000000200000002 R_X86_64_PC32 0000000000000000 .text + 16dc 00000000000000ac 0000000600000002 R_X86_64_PC32 0000000000000000 .altinstr_replacement + 66 .text: 00000000000016b0 <__switch_to_asm>: 16b0: f3 0f 1e fa endbr64 16b4: 55 push %rbp 16b5: 53 push %rbx 16b6: 41 54 push %r12 16b8: 41 55 push %r13 16ba: 41 56 push %r14 16bc: 41 57 push %r15 16be: 48 89 a7 18 0b 00 00 mov %rsp,0xb18(%rdi) 16c5: 48 8b a6 18 0b 00 00 mov 0xb18(%rsi),%rsp 16cc: 48 8b 9e 28 05 00 00 mov 0x528(%rsi),%rbx 16d3: 65 48 89 1c 25 00 00 00 00 mov %rbx,%gs:0x0 16d8: R_X86_64_32S fixed_percpu_data+0x28 16dc: eb 2a jmp 1708 <__switch_to_asm+0x58> 16de: 90 nop 16df: 90 nop 16e0: 90 nop 16e1: 90 nop 16e2: 90 nop 16e3: 90 nop 16e4: 90 nop 16e5: 90 nop 16e6: 90 nop 16e7: 90 nop 16e8: 90 nop 16e9: 90 nop 16ea: 90 nop 16eb: 90 nop 16ec: 90 nop 16ed: 90 nop 16ee: 90 nop 16ef: 90 nop 16f0: 90 nop 16f1: 90 nop 16f2: 90 nop 16f3: 90 nop 16f4: 90 nop 16f5: 90 nop 16f6: 90 nop 16f7: 90 nop 16f8: 90 nop 16f9: 90 nop 16fa: 90 nop 16fb: 90 nop 16fc: 90 nop 16fd: 90 nop 16fe: 90 nop 16ff: 90 nop 1700: 90 nop 1701: 90 nop 1702: 90 nop 1703: 90 nop 1704: 90 nop 1705: 90 nop 1706: 90 nop 1707: 90 nop 1708: 41 5f pop %r15 170a: 41 5e pop %r14 170c: 41 5d pop %r13 170e: 41 5c pop %r12 1710: 5b pop %rbx 1711: 5d pop %rbp 1712: e9 00 00 00 00 jmp 1717 <__switch_to_asm+0x67> 1713: R_X86_64_PLT32 __switch_to-0x4 .altinstr_replacement: 3a: 49 c7 c4 10 00 00 00 mov $0x10,%r12 41: e8 01 00 00 00 call 47 <.altinstr_replacement+0x47> 46: cc int3 47: e8 01 00 00 00 call 4d <.altinstr_replacement+0x4d> 4c: cc int3 4d: 48 83 c4 10 add $0x10,%rsp 51: 49 ff cc dec %r12 54: 75 eb jne 41 <.altinstr_replacement+0x41> 56: 0f ae e8 lfence 59: 65 48 c7 04 25 00 00 00 00 ff ff ff ff movq $0xffffffffffffffff,%gs:0x0 5e: R_X86_64_32S pcpu_hot+0x10 66: e8 01 00 00 00 call 6c <.altinstr_replacement+0x6c> 6b: cc int3 6c: 48 83 c4 08 add $0x8,%rsp 70: 0f ae e8 lfence As can be seen from the two alternatives, when overlaid, the NOP after the shorter (starting at 66) coinsides with the call at 47, leading to conflicting CFI state for that instruction. By offsetting the shorter alternative by 2 bytes, this alignment is undone. Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.783099843@infradead.org --- arch/x86/include/asm/nospec-branch.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/x86/include/asm/nospec-branch.h b/arch/x86/include/asm/nospec-branch.h index e04313e89f4f..3ef70e54a858 100644 --- a/arch/x86/include/asm/nospec-branch.h +++ b/arch/x86/include/asm/nospec-branch.h @@ -261,7 +261,7 @@ .macro FILL_RETURN_BUFFER reg:req nr:req ftr:req ftr2=ALT_NOT(X86_FEATURE_ALWAYS) ALTERNATIVE_2 "jmp .Lskip_rsb_\@", \ __stringify(__FILL_RETURN_BUFFER(\reg,\nr)), \ftr, \ - __stringify(__FILL_ONE_RETURN), \ftr2 + __stringify(nop;nop;__FILL_ONE_RETURN), \ftr2 .Lskip_rsb_\@: .endm From 1c34496e5856886d565665fb64029ecdeb080ffb Mon Sep 17 00:00:00 2001 From: Peter Zijlstra Date: Wed, 8 Feb 2023 18:18:05 +0100 Subject: [PATCH 21/22] objtool: Remove instruction::list MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the instruction::list by allocating instructions in arrays of 256 entries and stringing them together by (amortized) find_insn(). This shrinks instruction by 16 bytes and brings it down to 128. struct instruction { - struct list_head list; /* 0 16 */ - struct hlist_node hash; /* 16 16 */ - struct list_head call_node; /* 32 16 */ - struct section * sec; /* 48 8 */ - long unsigned int offset; /* 56 8 */ - /* --- cacheline 1 boundary (64 bytes) --- */ - long unsigned int immediate; /* 64 8 */ - unsigned int len; /* 72 4 */ - u8 type; /* 76 1 */ - - /* Bitfield combined with previous fields */ + struct hlist_node hash; /* 0 16 */ + struct list_head call_node; /* 16 16 */ + struct section * sec; /* 32 8 */ + long unsigned int offset; /* 40 8 */ + long unsigned int immediate; /* 48 8 */ + u8 len; /* 56 1 */ + u8 prev_len; /* 57 1 */ + u8 type; /* 58 1 */ + s8 instr; /* 59 1 */ + u32 idx:8; /* 60: 0 4 */ + u32 dead_end:1; /* 60: 8 4 */ + u32 ignore:1; /* 60: 9 4 */ + u32 ignore_alts:1; /* 60:10 4 */ + u32 hint:1; /* 60:11 4 */ + u32 save:1; /* 60:12 4 */ + u32 restore:1; /* 60:13 4 */ + u32 retpoline_safe:1; /* 60:14 4 */ + u32 noendbr:1; /* 60:15 4 */ + u32 entry:1; /* 60:16 4 */ + u32 visited:4; /* 60:17 4 */ + u32 no_reloc:1; /* 60:21 4 */ - u16 dead_end:1; /* 76: 8 2 */ - u16 ignore:1; /* 76: 9 2 */ - u16 ignore_alts:1; /* 76:10 2 */ - u16 hint:1; /* 76:11 2 */ - u16 save:1; /* 76:12 2 */ - u16 restore:1; /* 76:13 2 */ - u16 retpoline_safe:1; /* 76:14 2 */ - u16 noendbr:1; /* 76:15 2 */ - u16 entry:1; /* 78: 0 2 */ - u16 visited:4; /* 78: 1 2 */ - u16 no_reloc:1; /* 78: 5 2 */ + /* XXX 10 bits hole, try to pack */ - /* XXX 2 bits hole, try to pack */ - /* Bitfield combined with next fields */ - - s8 instr; /* 79 1 */ - struct alt_group * alt_group; /* 80 8 */ - struct instruction * jump_dest; /* 88 8 */ - struct instruction * first_jump_src; /* 96 8 */ + /* --- cacheline 1 boundary (64 bytes) --- */ + struct alt_group * alt_group; /* 64 8 */ + struct instruction * jump_dest; /* 72 8 */ + struct instruction * first_jump_src; /* 80 8 */ union { - struct symbol * _call_dest; /* 104 8 */ - struct reloc * _jump_table; /* 104 8 */ - }; /* 104 8 */ - struct alternative * alts; /* 112 8 */ - struct symbol * sym; /* 120 8 */ - /* --- cacheline 2 boundary (128 bytes) --- */ - struct stack_op * stack_ops; /* 128 8 */ - struct cfi_state * cfi; /* 136 8 */ + struct symbol * _call_dest; /* 88 8 */ + struct reloc * _jump_table; /* 88 8 */ + }; /* 88 8 */ + struct alternative * alts; /* 96 8 */ + struct symbol * sym; /* 104 8 */ + struct stack_op * stack_ops; /* 112 8 */ + struct cfi_state * cfi; /* 120 8 */ - /* size: 144, cachelines: 3, members: 28 */ - /* sum members: 142 */ - /* sum bitfield members: 14 bits, bit holes: 1, sum bit holes: 2 bits */ - /* last cacheline: 16 bytes */ + /* size: 128, cachelines: 2, members: 29 */ + /* sum members: 124 */ + /* sum bitfield members: 22 bits, bit holes: 1, sum bit holes: 10 bits */ }; pre: 5:38.18 real, 213.25 user, 124.90 sys, 23449040 mem post: 5:03.34 real, 210.75 user, 88.80 sys, 20241232 mem Signed-off-by: Peter Zijlstra (Intel) Signed-off-by: Ingo Molnar Acked-by: Josh Poimboeuf Tested-by: Nathan Chancellor # build only Tested-by: Thomas Weißschuh # compile and run Link: https://lore.kernel.org/r/20230208172245.851307606@infradead.org --- tools/objtool/check.c | 168 +++++++++++++++--------- tools/objtool/include/objtool/check.h | 51 +++---- tools/objtool/include/objtool/objtool.h | 1 - tools/objtool/objtool.c | 1 - 4 files changed, 134 insertions(+), 87 deletions(-) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index 7e9d3d3eed65..b0b467d9608a 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -47,27 +47,29 @@ struct instruction *find_insn(struct objtool_file *file, return NULL; } -static struct instruction *next_insn_same_sec(struct objtool_file *file, - struct instruction *insn) +struct instruction *next_insn_same_sec(struct objtool_file *file, + struct instruction *insn) { - struct instruction *next = list_next_entry(insn, list); + if (insn->idx == INSN_CHUNK_MAX) + return find_insn(file, insn->sec, insn->offset + insn->len); - if (!next || &next->list == &file->insn_list || next->sec != insn->sec) + insn++; + if (!insn->len) return NULL; - return next; + return insn; } static struct instruction *next_insn_same_func(struct objtool_file *file, struct instruction *insn) { - struct instruction *next = list_next_entry(insn, list); + struct instruction *next = next_insn_same_sec(file, insn); struct symbol *func = insn_func(insn); if (!func) return NULL; - if (&next->list != &file->insn_list && insn_func(next) == func) + if (next && insn_func(next) == func) return next; /* Check if we're already in the subfunction: */ @@ -78,17 +80,35 @@ static struct instruction *next_insn_same_func(struct objtool_file *file, return find_insn(file, func->cfunc->sec, func->cfunc->offset); } -static struct instruction *prev_insn_same_sym(struct objtool_file *file, - struct instruction *insn) +static struct instruction *prev_insn_same_sec(struct objtool_file *file, + struct instruction *insn) { - struct instruction *prev = list_prev_entry(insn, list); + if (insn->idx == 0) { + if (insn->prev_len) + return find_insn(file, insn->sec, insn->offset - insn->prev_len); + return NULL; + } - if (&prev->list != &file->insn_list && insn_func(prev) == insn_func(insn)) + return insn - 1; +} + +static struct instruction *prev_insn_same_sym(struct objtool_file *file, + struct instruction *insn) +{ + struct instruction *prev = prev_insn_same_sec(file, insn); + + if (prev && insn_func(prev) == insn_func(insn)) return prev; return NULL; } +#define for_each_insn(file, insn) \ + for (struct section *__sec, *__fake = (struct section *)1; \ + __fake; __fake = NULL) \ + for_each_sec(file, __sec) \ + sec_for_each_insn(file, __sec, insn) + #define func_for_each_insn(file, func, insn) \ for (insn = find_insn(file, func->sec, func->offset); \ insn; \ @@ -96,16 +116,13 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file, #define sym_for_each_insn(file, sym, insn) \ for (insn = find_insn(file, sym->sec, sym->offset); \ - insn && &insn->list != &file->insn_list && \ - insn->sec == sym->sec && \ - insn->offset < sym->offset + sym->len; \ - insn = list_next_entry(insn, list)) + insn && insn->offset < sym->offset + sym->len; \ + insn = next_insn_same_sec(file, insn)) #define sym_for_each_insn_continue_reverse(file, sym, insn) \ - for (insn = list_prev_entry(insn, list); \ - &insn->list != &file->insn_list && \ - insn->sec == sym->sec && insn->offset >= sym->offset; \ - insn = list_prev_entry(insn, list)) + for (insn = prev_insn_same_sec(file, insn); \ + insn && insn->offset >= sym->offset; \ + insn = prev_insn_same_sec(file, insn)) #define sec_for_each_insn_from(file, insn) \ for (; insn; insn = next_insn_same_sec(file, insn)) @@ -384,6 +401,9 @@ static int decode_instructions(struct objtool_file *file) int ret; for_each_sec(file, sec) { + struct instruction *insns = NULL; + u8 prev_len = 0; + u8 idx = 0; if (!(sec->sh.sh_flags & SHF_EXECINSTR)) continue; @@ -409,22 +429,31 @@ static int decode_instructions(struct objtool_file *file) sec->init = true; for (offset = 0; offset < sec->sh.sh_size; offset += insn->len) { - insn = malloc(sizeof(*insn)); - if (!insn) { - WARN("malloc failed"); - return -1; + if (!insns || idx == INSN_CHUNK_MAX) { + insns = calloc(sizeof(*insn), INSN_CHUNK_SIZE); + if (!insns) { + WARN("malloc failed"); + return -1; + } + idx = 0; + } else { + idx++; } - memset(insn, 0, sizeof(*insn)); - INIT_LIST_HEAD(&insn->call_node); + insn = &insns[idx]; + insn->idx = idx; + INIT_LIST_HEAD(&insn->call_node); insn->sec = sec; insn->offset = offset; + insn->prev_len = prev_len; ret = arch_decode_instruction(file, sec, offset, sec->sh.sh_size - offset, insn); if (ret) - goto err; + return ret; + + prev_len = insn->len; /* * By default, "ud2" is a dead end unless otherwise @@ -435,10 +464,11 @@ static int decode_instructions(struct objtool_file *file) insn->dead_end = true; hash_add(file->insn_hash, &insn->hash, sec_offset_hash(sec, insn->offset)); - list_add_tail(&insn->list, &file->insn_list); nr_insns++; } +// printf("%s: last chunk used: %d\n", sec->name, (int)idx); + list_for_each_entry(func, &sec->symbol_list, list) { if (func->type != STT_NOTYPE && func->type != STT_FUNC) continue; @@ -481,10 +511,6 @@ static int decode_instructions(struct objtool_file *file) printf("nr_insns: %lu\n", nr_insns); return 0; - -err: - free(insn); - return ret; } /* @@ -599,7 +625,7 @@ static int add_dead_ends(struct objtool_file *file) } insn = find_insn(file, reloc->sym->sec, reloc->addend); if (insn) - insn = list_prev_entry(insn, list); + insn = prev_insn_same_sec(file, insn); else if (reloc->addend == reloc->sym->sec->sh.sh_size) { insn = find_last_insn(file, reloc->sym->sec); if (!insn) { @@ -634,7 +660,7 @@ static int add_dead_ends(struct objtool_file *file) } insn = find_insn(file, reloc->sym->sec, reloc->addend); if (insn) - insn = list_prev_entry(insn, list); + insn = prev_insn_same_sec(file, insn); else if (reloc->addend == reloc->sym->sec->sh.sh_size) { insn = find_last_insn(file, reloc->sym->sec); if (!insn) { @@ -1775,6 +1801,7 @@ static int handle_group_alt(struct objtool_file *file, orig_alt_group->orig_group = NULL; orig_alt_group->first_insn = orig_insn; orig_alt_group->last_insn = last_orig_insn; + orig_alt_group->nop = NULL; } else { if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len - orig_alt_group->first_insn->offset != special_alt->orig_len) { @@ -1876,12 +1903,11 @@ static int handle_group_alt(struct objtool_file *file, return -1; } - if (nop) - list_add(&nop->list, &last_new_insn->list); end: new_alt_group->orig_group = orig_alt_group; new_alt_group->first_insn = *new_insn; - new_alt_group->last_insn = nop ? : last_new_insn; + new_alt_group->last_insn = last_new_insn; + new_alt_group->nop = nop; new_alt_group->cfi = orig_alt_group->cfi; return 0; } @@ -1931,7 +1957,7 @@ static int handle_jump_alt(struct objtool_file *file, else file->jl_long++; - *new_insn = list_next_entry(orig_insn, list); + *new_insn = next_insn_same_sec(file, orig_insn); return 0; } @@ -3522,11 +3548,28 @@ static struct instruction *next_insn_to_validate(struct objtool_file *file, * Simulate the fact that alternatives are patched in-place. When the * end of a replacement alt_group is reached, redirect objtool flow to * the end of the original alt_group. + * + * insn->alts->insn -> alt_group->first_insn + * ... + * alt_group->last_insn + * [alt_group->nop] -> next(orig_group->last_insn) */ - if (alt_group && insn == alt_group->last_insn && alt_group->orig_group) - return next_insn_same_sec(file, alt_group->orig_group->last_insn); + if (alt_group) { + if (alt_group->nop) { + /* ->nop implies ->orig_group */ + if (insn == alt_group->last_insn) + return alt_group->nop; + if (insn == alt_group->nop) + goto next_orig; + } + if (insn == alt_group->last_insn && alt_group->orig_group) + goto next_orig; + } return next_insn_same_sec(file, insn); + +next_orig: + return next_insn_same_sec(file, alt_group->orig_group->last_insn); } /* @@ -3777,11 +3820,25 @@ static int validate_branch(struct objtool_file *file, struct symbol *func, return 0; } +static int validate_unwind_hint(struct objtool_file *file, + struct instruction *insn, + struct insn_state *state) +{ + if (insn->hint && !insn->visited && !insn->ignore) { + int ret = validate_branch(file, insn_func(insn), insn, *state); + if (ret && opts.backtrace) + BT_FUNC("<=== (hint)", insn); + return ret; + } + + return 0; +} + static int validate_unwind_hints(struct objtool_file *file, struct section *sec) { struct instruction *insn; struct insn_state state; - int ret, warnings = 0; + int warnings = 0; if (!file->hints) return 0; @@ -3789,22 +3846,11 @@ static int validate_unwind_hints(struct objtool_file *file, struct section *sec) init_insn_state(file, &state, sec); if (sec) { - insn = find_insn(file, sec, 0); - if (!insn) - return 0; + sec_for_each_insn(file, sec, insn) + warnings += validate_unwind_hint(file, insn, &state); } else { - insn = list_first_entry(&file->insn_list, typeof(*insn), list); - } - - while (&insn->list != &file->insn_list && (!sec || insn->sec == sec)) { - if (insn->hint && !insn->visited && !insn->ignore) { - ret = validate_branch(file, insn_func(insn), insn, state); - if (ret && opts.backtrace) - BT_FUNC("<=== (hint)", insn); - warnings += ret; - } - - insn = list_next_entry(insn, list); + for_each_insn(file, insn) + warnings += validate_unwind_hint(file, insn, &state); } return warnings; @@ -4070,7 +4116,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio * * It may also insert a UD2 after calling a __noreturn function. */ - prev_insn = list_prev_entry(insn, list); + prev_insn = prev_insn_same_sec(file, insn); if ((prev_insn->dead_end || dead_end_function(file, insn_call_dest(prev_insn))) && (insn->type == INSN_BUG || @@ -4102,7 +4148,7 @@ static bool ignore_unreachable_insn(struct objtool_file *file, struct instructio if (insn->offset + insn->len >= insn_func(insn)->offset + insn_func(insn)->len) break; - insn = list_next_entry(insn, list); + insn = next_insn_same_sec(file, insn); } return false; @@ -4115,10 +4161,10 @@ static int add_prefix_symbol(struct objtool_file *file, struct symbol *func, return 0; for (;;) { - struct instruction *prev = list_prev_entry(insn, list); + struct instruction *prev = prev_insn_same_sec(file, insn); u64 offset; - if (&prev->list == &file->insn_list) + if (!prev) break; if (prev->type != INSN_NOP) @@ -4517,7 +4563,7 @@ int check(struct objtool_file *file) warnings += ret; - if (list_empty(&file->insn_list)) + if (!nr_insns) goto out; if (opts.retpoline) { @@ -4626,7 +4672,7 @@ int check(struct objtool_file *file) warnings += ret; } - if (opts.orc && !list_empty(&file->insn_list)) { + if (opts.orc && nr_insns) { ret = orc_create(file); if (ret < 0) goto out; diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h index ab6deaed9777..3e7c7004f7df 100644 --- a/tools/objtool/include/objtool/check.h +++ b/tools/objtool/include/objtool/check.h @@ -27,7 +27,7 @@ struct alt_group { struct alt_group *orig_group; /* First and last instructions in the group */ - struct instruction *first_insn, *last_insn; + struct instruction *first_insn, *last_insn, *nop; /* * Byte-offset-addressed len-sized array of pointers to CFI structs. @@ -36,31 +36,36 @@ struct alt_group { struct cfi_state **cfi; }; +#define INSN_CHUNK_BITS 8 +#define INSN_CHUNK_SIZE (1 << INSN_CHUNK_BITS) +#define INSN_CHUNK_MAX (INSN_CHUNK_SIZE - 1) + struct instruction { - struct list_head list; struct hlist_node hash; struct list_head call_node; struct section *sec; unsigned long offset; unsigned long immediate; - unsigned int len; + + u8 len; + u8 prev_len; u8 type; - - u16 dead_end : 1, - ignore : 1, - ignore_alts : 1, - hint : 1, - save : 1, - restore : 1, - retpoline_safe : 1, - noendbr : 1, - entry : 1, - visited : 4, - no_reloc : 1; - /* 2 bit hole */ - s8 instr; + u32 idx : INSN_CHUNK_BITS, + dead_end : 1, + ignore : 1, + ignore_alts : 1, + hint : 1, + save : 1, + restore : 1, + retpoline_safe : 1, + noendbr : 1, + entry : 1, + visited : 4, + no_reloc : 1; + /* 10 bit hole */ + struct alt_group *alt_group; struct instruction *jump_dest; struct instruction *first_jump_src; @@ -109,13 +114,11 @@ static inline bool is_jump(struct instruction *insn) struct instruction *find_insn(struct objtool_file *file, struct section *sec, unsigned long offset); -#define for_each_insn(file, insn) \ - list_for_each_entry(insn, &file->insn_list, list) +struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruction *insn); -#define sec_for_each_insn(file, sec, insn) \ - for (insn = find_insn(file, sec, 0); \ - insn && &insn->list != &file->insn_list && \ - insn->sec == sec; \ - insn = list_next_entry(insn, list)) +#define sec_for_each_insn(file, _sec, insn) \ + for (insn = find_insn(file, _sec, 0); \ + insn && insn->sec == _sec; \ + insn = next_insn_same_sec(file, insn)) #endif /* _CHECK_H */ diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h index 6b40977bcdb1..94a33ee7b363 100644 --- a/tools/objtool/include/objtool/objtool.h +++ b/tools/objtool/include/objtool/objtool.h @@ -21,7 +21,6 @@ struct pv_state { struct objtool_file { struct elf *elf; - struct list_head insn_list; DECLARE_HASHTABLE(insn_hash, 20); struct list_head retpoline_call_list; struct list_head return_thunk_list; diff --git a/tools/objtool/objtool.c b/tools/objtool/objtool.c index 6affd8067f83..c54f7235c5d9 100644 --- a/tools/objtool/objtool.c +++ b/tools/objtool/objtool.c @@ -99,7 +99,6 @@ struct objtool_file *objtool_open_read(const char *_objname) return NULL; } - INIT_LIST_HEAD(&file.insn_list); hash_init(file.insn_hash); INIT_LIST_HEAD(&file.retpoline_call_list); INIT_LIST_HEAD(&file.return_thunk_list); From 00c8f01c4e84637c3db76f368b8687cb61f4dd9d Mon Sep 17 00:00:00 2001 From: Josh Poimboeuf Date: Thu, 16 Feb 2023 12:34:41 -0800 Subject: [PATCH 22/22] objtool: Fix ORC 'signal' propagation There have been some recently reported ORC unwinder warnings like: WARNING: can't access registers at entry_SYSCALL_64_after_hwframe+0x63/0xcd WARNING: stack going in the wrong direction? at __sys_setsockopt+0x2c6/0x5b0 net/socket.c:2271 And a KASAN warning: BUG: KASAN: stack-out-of-bounds in unwind_next_frame (arch/x86/include/asm/ptrace.h:136 arch/x86/kernel/unwind_orc.c:455) It turns out the 'signal' bit isn't getting propagated from the unwind hints to the ORC entries, making the unwinder confused at times. Fixes: ffb1b4a41016 ("x86/unwind/orc: Add 'signal' field to ORC metadata") Reported-by: kernel test robot Reported-by: Dmitry Vyukov Signed-off-by: Josh Poimboeuf Signed-off-by: Ingo Molnar Link: https://lore.kernel.org/r/97eef9db60cd86d376a9a40d49d77bb67a8f6526.1676579666.git.jpoimboe@kernel.org --- tools/objtool/check.c | 1 + tools/objtool/include/objtool/cfi.h | 1 + tools/objtool/orc_gen.c | 1 + 3 files changed, 3 insertions(+) diff --git a/tools/objtool/check.c b/tools/objtool/check.c index b0b467d9608a..5822de376d9a 100644 --- a/tools/objtool/check.c +++ b/tools/objtool/check.c @@ -2330,6 +2330,7 @@ static int read_unwind_hints(struct objtool_file *file) cfi.cfa.offset = bswap_if_needed(file->elf, hint->sp_offset); cfi.type = hint->type; + cfi.signal = hint->signal; cfi.end = hint->end; insn->cfi = cfi_hash_find_or_add(&cfi); diff --git a/tools/objtool/include/objtool/cfi.h b/tools/objtool/include/objtool/cfi.h index f11d1ac1dadf..b1258e79a1b7 100644 --- a/tools/objtool/include/objtool/cfi.h +++ b/tools/objtool/include/objtool/cfi.h @@ -34,6 +34,7 @@ struct cfi_state { unsigned char type; bool bp_scratch; bool drap; + bool signal; bool end; }; diff --git a/tools/objtool/orc_gen.c b/tools/objtool/orc_gen.c index 1f22b7ebae58..57a4527d5988 100644 --- a/tools/objtool/orc_gen.c +++ b/tools/objtool/orc_gen.c @@ -27,6 +27,7 @@ static int init_orc_entry(struct orc_entry *orc, struct cfi_state *cfi, } orc->end = cfi->end; + orc->signal = cfi->signal; if (cfi->cfa.base == CFI_UNDEFINED) { orc->sp_reg = ORC_REG_UNDEFINED;