[PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump

Alexandre Chartre posted 30 patches 2 months, 2 weeks ago
[PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months, 2 weeks ago
objtool executes the objdump command to disassemble code. Use libopcodes
instead to have more control about the disassembly scope and output.
If libopcodes is not present then objtool is built without disassembly
support.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/.gitignore              |   2 +
 tools/objtool/Build                   |   3 +-
 tools/objtool/Makefile                |  25 ++++
 tools/objtool/arch/loongarch/decode.c |  12 ++
 tools/objtool/arch/powerpc/decode.c   |  12 ++
 tools/objtool/arch/x86/decode.c       |  12 ++
 tools/objtool/check.c                 |  14 +-
 tools/objtool/disas.c                 | 187 +++++++++++++++++---------
 tools/objtool/include/objtool/arch.h  |   9 ++
 tools/objtool/include/objtool/check.h |   5 +
 tools/objtool/include/objtool/disas.h |  29 ++++
 11 files changed, 238 insertions(+), 72 deletions(-)

diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 4faa4dd72f350..759303657bd7c 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 arch/x86/lib/inat-tables.c
 /objtool
+feature
+FEATURE-DUMP.objtool
 fixdep
 libsubcmd/
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 17e50a1766d0e..9d1e8f28ef953 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -7,7 +7,8 @@ objtool-y += special.o
 objtool-y += builtin-check.o
 objtool-y += elf.o
 objtool-y += objtool.o
-objtool-y += disas.o
+
+objtool-$(BUILD_DISAS) += disas.o
 
 objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
 objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 021f55b7bd872..df793ca6fc1a1 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -70,6 +70,29 @@ OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
 # Always want host compilation.
 HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
 
+#
+# To support disassembly, objtool needs libopcodes which is provided
+# with libbdf (binutils-dev or binutils-devel package).
+#
+FEATURE_USER = .objtool
+FEATURE_TESTS = libbfd disassembler-init-styled
+FEATURE_DISPLAY =
+include $(srctree)/tools/build/Makefile.feature
+
+ifeq ($(feature-disassembler-init-styled), 1)
+	OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED
+endif
+
+BUILD_DISAS := n
+
+ifeq ($(feature-libbfd),1)
+	BUILD_DISAS := y
+	OBJTOOL_CFLAGS += -DDISAS
+	OBJTOOL_LDFLAGS += -lopcodes
+endif
+
+export BUILD_DISAS
+
 AWK = awk
 MKDIR = mkdir
 
@@ -103,6 +126,8 @@ 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
+	$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
+	$(Q)$(RM) -r -- $(OUTPUT)feature
 
 FORCE:
 
diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 0115b97c526b8..1de86ebb637d5 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include <string.h>
 #include <objtool/check.h>
+#include <objtool/disas.h>
 #include <objtool/warn.h>
 #include <asm/inst.h>
 #include <asm/orc_types.h>
@@ -414,3 +415,14 @@ unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *tabl
 		return reloc->sym->offset + reloc_addend(reloc);
 	}
 }
+
+#ifdef DISAS
+
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_loongarch,
+			       bfd_mach_loongarch32, bfd_mach_loongarch64,
+			       NULL);
+}
+
+#endif /* DISAS */
diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c
index 3a9b748216edc..4f68b402e7855 100644
--- a/tools/objtool/arch/powerpc/decode.c
+++ b/tools/objtool/arch/powerpc/decode.c
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <objtool/check.h>
+#include <objtool/disas.h>
 #include <objtool/elf.h>
 #include <objtool/arch.h>
 #include <objtool/warn.h>
@@ -127,3 +128,14 @@ unsigned int arch_reloc_size(struct reloc *reloc)
 		return 8;
 	}
 }
+
+#ifdef DISAS
+
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_powerpc,
+			       bfd_mach_ppc, bfd_mach_ppc64,
+			       NULL);
+}
+
+#endif /* DISAS */
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index cc85db7b65a41..83e9c604ce105 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -16,6 +16,7 @@
 
 #include <asm/orc_types.h>
 #include <objtool/check.h>
+#include <objtool/disas.h>
 #include <objtool/elf.h>
 #include <objtool/arch.h>
 #include <objtool/warn.h>
@@ -949,3 +950,14 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc)
 		return false;
 	}
 }
+
+#ifdef DISAS
+
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_i386,
+			       bfd_mach_i386_i386, bfd_mach_x86_64,
+			       "att");
+}
+
+#endif /* DISAS */
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 5083e47eef08d..de156e91ee8b7 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4980,8 +4980,6 @@ int check(struct objtool_file *file)
 			goto out;
 	}
 
-	free_insns(file);
-
 	if (opts.stats) {
 		printf("nr_insns_visited: %ld\n", nr_insns_visited);
 		printf("nr_cfi: %ld\n", nr_cfi);
@@ -4990,8 +4988,10 @@ int check(struct objtool_file *file)
 	}
 
 out:
-	if (!ret && !warnings)
+	if (!ret && !warnings) {
+		free_insns(file);
 		return 0;
+	}
 
 	if (opts.werror && warnings)
 		ret = 1;
@@ -5000,10 +5000,14 @@ int check(struct objtool_file *file)
 		if (opts.werror && warnings)
 			WARN("%d warning(s) upgraded to errors", warnings);
 		disas_ctx = disas_context_create(file);
-		disas_warned_funcs(disas_ctx);
-		disas_context_destroy(disas_ctx);
+		if (disas_ctx) {
+			disas_warned_funcs(disas_ctx);
+			disas_context_destroy(disas_ctx);
+		}
 	}
 
+	free_insns(file);
+
 	if (opts.backup && make_backup())
 		return 1;
 
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 7a18e51d43e62..11ac2ec04afc1 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -4,18 +4,56 @@
  */
 
 #include <objtool/arch.h>
+#include <objtool/check.h>
 #include <objtool/disas.h>
 #include <objtool/warn.h>
 
+#include <bfd.h>
 #include <linux/string.h>
+#include <tools/dis-asm-compat.h>
 
 struct disas_context {
 	struct objtool_file *file;
+	disassembler_ftype disassembler;
+	struct disassemble_info info;
 };
 
+#define DINFO_FPRINTF(dinfo, ...)	\
+	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
+
+/*
+ * Initialize disassemble info arch, mach (32 or 64-bit) and options.
+ */
+int disas_info_init(struct disassemble_info *dinfo,
+		    int arch, int mach32, int mach64,
+		    const char *options)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct objtool_file *file = dctx->file;
+
+	dinfo->arch = arch;
+
+	switch (file->elf->ehdr.e_ident[EI_CLASS]) {
+	case ELFCLASS32:
+		dinfo->mach = mach32;
+		break;
+	case ELFCLASS64:
+		dinfo->mach = mach64;
+		break;
+	default:
+		return -1;
+	}
+
+	dinfo->disassembler_options = options;
+
+	return 0;
+}
+
 struct disas_context *disas_context_create(struct objtool_file *file)
 {
 	struct disas_context *dctx;
+	struct disassemble_info *dinfo;
+	int err;
 
 	dctx = malloc(sizeof(*dctx));
 	if (!dctx) {
@@ -24,8 +62,49 @@ struct disas_context *disas_context_create(struct objtool_file *file)
 	}
 
 	dctx->file = file;
+	dinfo = &dctx->info;
+
+	init_disassemble_info_compat(dinfo, stdout,
+				     (fprintf_ftype)fprintf,
+				     fprintf_styled);
+
+	dinfo->read_memory_func = buffer_read_memory;
+	dinfo->application_data = dctx;
+
+	/*
+	 * bfd_openr() is not used to avoid doing ELF data processing
+	 * and caching that has already being done. Here, we just need
+	 * to identify the target file so we call an arch specific
+	 * function to fill some disassemble info (arch, mach).
+	 */
+
+	dinfo->arch = bfd_arch_unknown;
+	dinfo->mach = 0;
+
+	err = arch_disas_info_init(dinfo);
+	if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) {
+		WARN("failed to init disassembly arch");
+		goto error;
+	}
+
+	dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ?
+		BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
+
+	disassemble_init_for_target(dinfo);
+
+	dctx->disassembler = disassembler(dinfo->arch,
+					  dinfo->endian == BFD_ENDIAN_BIG,
+					  dinfo->mach, NULL);
+	if (!dctx->disassembler) {
+		WARN("failed to create disassembler function");
+		goto error;
+	}
 
 	return dctx;
+
+error:
+	free(dctx);
+	return NULL;
 }
 
 void disas_context_destroy(struct disas_context *dctx)
@@ -33,86 +112,62 @@ void disas_context_destroy(struct disas_context *dctx)
 	free(dctx);
 }
 
-/* 'funcs' is a space-separated list of function names */
-static void disas_funcs(const char *funcs)
+/*
+ * Disassemble a single instruction. Return the size of the instruction.
+ */
+static size_t disas_insn(struct disas_context *dctx,
+			 struct instruction *insn)
 {
-	const char *objdump_str, *cross_compile;
-	int size, ret;
-	char *cmd;
-
-	cross_compile = getenv("CROSS_COMPILE");
-	if (!cross_compile)
-		cross_compile = "";
-
-	objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '"
-			"BEGIN { split(_funcs, funcs); }"
-			"/^$/ { func_match = 0; }"
-			"/<.*>:/ { "
-				"f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);"
-				"for (i in funcs) {"
-					"if (funcs[i] == f) {"
-						"func_match = 1;"
-						"base = strtonum(\"0x\" $1);"
-						"break;"
-					"}"
-				"}"
-			"}"
-			"{"
-				"if (func_match) {"
-					"addr = strtonum(\"0x\" $1);"
-					"printf(\"%%04x \", addr - base);"
-					"print;"
-				"}"
-			"}' 1>&2";
-
-	/* fake snprintf() to calculate the size */
-	size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1;
-	if (size <= 0) {
-		WARN("objdump string size calculation failed");
-		return;
+	disassembler_ftype disasm = dctx->disassembler;
+	struct disassemble_info *dinfo = &dctx->info;
+
+	if (insn->type == INSN_NOP) {
+		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
+		return insn->len;
 	}
 
-	cmd = malloc(size);
+	/*
+	 * Set the disassembler buffer to read data from the section
+	 * containing the instruction to disassemble.
+	 */
+	dinfo->buffer = insn->sec->data->d_buf;
+	dinfo->buffer_vma = 0;
+	dinfo->buffer_length = insn->sec->sh.sh_size;
 
-	/* real snprintf() */
-	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
-	ret = system(cmd);
-	if (ret) {
-		WARN("disassembly failed: %d", ret);
-		return;
+	return disasm(insn->offset, &dctx->info);
+}
+
+/*
+ * Disassemble a function.
+ */
+static void disas_func(struct disas_context *dctx, struct symbol *func)
+{
+	struct instruction *insn;
+	size_t addr;
+
+	printf("%s:\n", func->name);
+	sym_for_each_insn(dctx->file, func, insn) {
+		addr = insn->offset;
+		printf(" %6lx:  %s+0x%-6lx      ",
+		       addr, func->name, addr - func->offset);
+		disas_insn(dctx, insn);
+		printf("\n");
 	}
+	printf("\n");
 }
 
+/*
+ * Disassemble all warned functions.
+ */
 void disas_warned_funcs(struct disas_context *dctx)
 {
 	struct symbol *sym;
-	char *funcs = NULL, *tmp;
 
 	if (!dctx)
 		return;
 
 	for_each_sym(dctx->file->elf, sym) {
-		if (sym->warned) {
-			if (!funcs) {
-				funcs = malloc(strlen(sym->name) + 1);
-				if (!funcs) {
-					ERROR_GLIBC("malloc");
-					return;
-				}
-				strcpy(funcs, sym->name);
-			} else {
-				tmp = malloc(strlen(funcs) + strlen(sym->name) + 2);
-				if (!tmp) {
-					ERROR_GLIBC("malloc");
-					return;
-				}
-				sprintf(tmp, "%s %s", funcs, sym->name);
-				free(funcs);
-				funcs = tmp;
-			}
-		}
+		if (sym->warned)
+			disas_func(dctx, sym);
 	}
-
-	if (funcs)
-		disas_funcs(funcs);
 }
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index d89f8b5ec14e3..18c0e69ee6170 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc);
 unsigned int arch_reloc_size(struct reloc *reloc);
 unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
 
+#ifdef DISAS
+
+#include <bfd.h>
+#include <dis-asm.h>
+
+int arch_disas_info_init(struct disassemble_info *dinfo);
+
+#endif /* DISAS */
+
 #endif /* _ARCH_H */
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index d73b0c3ae1ee3..674f57466d125 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -127,4 +127,9 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc
 	     insn && insn->sec == _sec;					\
 	     insn = next_insn_same_sec(file, insn))
 
+#define sym_for_each_insn(file, sym, insn)				\
+	for (insn = find_insn(file, sym->sec, sym->offset);		\
+	     insn && insn->offset < sym->offset + sym->len;		\
+	     insn = next_insn_same_sec(file, insn))
+
 #endif /* _CHECK_H */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 5c543b69fc612..3ec3ce2e4e6f0 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -7,8 +7,37 @@
 #define _DISAS_H
 
 struct disas_context;
+struct disassemble_info;
+
+#ifdef DISAS
+
 struct disas_context *disas_context_create(struct objtool_file *file);
 void disas_context_destroy(struct disas_context *dctx);
 void disas_warned_funcs(struct disas_context *dctx);
+int disas_info_init(struct disassemble_info *dinfo,
+		    int arch, int mach32, int mach64,
+		    const char *options);
+
+#else /* DISAS */
+
+#include <objtool/warn.h>
+
+static inline struct disas_context *disas_context_create(struct objtool_file *file)
+{
+	WARN("Rebuild with libopcodes for disassembly support");
+	return NULL;
+}
+
+static inline void disas_context_destroy(struct disas_context *dctx) {}
+static inline void disas_warned_funcs(struct disas_context *dctx) {}
+
+static inline int disas_info_init(struct disassemble_info *dinfo,
+				  int arch, int mach32, int mach64,
+				  const char *options)
+{
+	return -1;
+}
+
+#endif /* DISAS */
 
 #endif /* _DISAS_H */
-- 
2.43.5
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 2 months ago
Hi,

On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:
> objtool executes the objdump command to disassemble code. Use libopcodes
> instead to have more control about the disassembly scope and output.
> If libopcodes is not present then objtool is built without disassembly
> support.
> 
> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>

With this patch in mainline, I get the following build error when trying
to build x86_64:defconfig (and other configurations).

Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
14.3.0. I tried with both Ubuntu 22.04 and 24.04.

Guenter

---

Building x86_64:defconfig ... failed
--------------
Error log:
In file included from disas.c:17:
tools/include/tools/dis-asm-compat.h:19:39: error: ‘enum disassembler_style’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror]
   19 |                                  enum disassembler_style style,
      |                                       ^~~~~~~~~~~~~~~~~~
tools/include/tools/dis-asm-compat.h:19:58: error: parameter 2 (‘style’) has incomplete type
   19 |                                  enum disassembler_style style,
      |                                  ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
tools/include/tools/dis-asm-compat.h:42:49: error: unknown type name ‘fprintf_styled_ftype’; did you mean ‘fprintf_ftype’?
   42 |                                                 fprintf_styled_ftype styled_func)
      |                                                 ^~~~~~~~~~~~~~~~~~~~
      |                                                 fprintf_ftype
disas.c:152:38: error: ‘enum disassembler_style’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror]
  152 |                                 enum disassembler_style style,
      |                                      ^~~~~~~~~~~~~~~~~~
disas.c:152:57: error: parameter 2 (‘style’) has incomplete type
  152 |                                 enum disassembler_style style,
      |                                 ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
disas.c: In function ‘disas_context_create’:
disas.c:367:9: error: implicit declaration of function ‘init_disassemble_info_compat’; did you mean ‘init_disassemble_info’? [-Werror=implicit-function-declaration]
  367 |         init_disassemble_info_compat(dinfo, dctx,
      |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
      |         init_disassemble_info
In file included from tools/include/linux/kernel.h:5,
                 from tools/include/linux/list.h:7,
                 from tools/objtool/include/objtool/arch.h:10,
                 from disas.c:9:
disas.c: In function ‘disas_fprintf_styled’:
disas.c:158:9: error: ‘va_start’ used in function with fixed arguments
  158 |         va_start(arg, fmt);
      |         ^~~~~~~~
cc1: all warnings being treated as errors

---
Bisect:

# bad: [cb015814f8b6eebcbb8e46e111d108892c5e6821] Merge tag 'f2fs-for-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs
# good: [7d0a66e4bb9081d75c82ec4957c50034cb0ea449] Linux 6.18
git bisect start 'HEAD' 'v6.18'
# bad: [6dfafbd0299a60bfb5d5e277fdf100037c7ded07] Merge tag 'drm-next-2025-12-03' of https://gitlab.freedesktop.org/drm/kernel
git bisect bad 6dfafbd0299a60bfb5d5e277fdf100037c7ded07
# bad: [8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88] Merge tag 'net-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
git bisect bad 8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88
# bad: [4a4e0199378f309fa7259132e1443efe56c1e276] Merge tag 'lkmm.2025.12.01a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu
git bisect bad 4a4e0199378f309fa7259132e1443efe56c1e276
# bad: [a61288200e8b6f42bff116508dc72ebcc206f10a] Merge tag 'ras_core_for_v6.19_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
git bisect bad a61288200e8b6f42bff116508dc72ebcc206f10a
# good: [1b5dd29869b1e63f7e5c37d7552e2dcf22de3c26] Merge tag 'vfs-6.19-rc1.fd_prepare.fs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
git bisect good 1b5dd29869b1e63f7e5c37d7552e2dcf22de3c26
# bad: [dcd8637edb873bd940e6aa82417dfb33ae980778] Merge tag 'x86-core-2025-12-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
git bisect bad dcd8637edb873bd940e6aa82417dfb33ae980778
# bad: [7e017720aae87dc2ca2471ac295e34e2b240e5f5] objtool: Disassemble jump table alternatives
git bisect bad 7e017720aae87dc2ca2471ac295e34e2b240e5f5
# good: [164c9201e1dad8d5c0c38f583dba81e4b6da9cc7] objtool: Add base objtool support for livepatch modules
git bisect good 164c9201e1dad8d5c0c38f583dba81e4b6da9cc7
# good: [9205a322cf96f16a49e412dfa3f09431f3e02fc5] objtool: Return canonical symbol when aliases exist in symbol finding helpers
git bisect good 9205a322cf96f16a49e412dfa3f09431f3e02fc5
# bad: [0bb080ba6469a573bc85122153d931334d10a173] objtool: Disassemble instruction on warning or backtrace
git bisect bad 0bb080ba6469a573bc85122153d931334d10a173
# good: [93863f3f859a626347ce2ec18947b11357b4ca14] kbuild: Check for functions with ambiguous -ffunction-sections section names
git bisect good 93863f3f859a626347ce2ec18947b11357b4ca14
# bad: [59953303827eceb06d486ba66cc0d71f55ded8ec] objtool: Disassemble code with libopcodes instead of running objdump
git bisect bad 59953303827eceb06d486ba66cc0d71f55ded8ec
# good: [55d2a473f317ab028d78a5c5ca69473643657c3d] objtool: Move disassembly functions to a separated file
git bisect good 55d2a473f317ab028d78a5c5ca69473643657c3d
# good: [1013f2e37bec39b1df5679e1c1e2572ece87c088] objtool: Create disassembly context
git bisect good 1013f2e37bec39b1df5679e1c1e2572ece87c088
# first bad commit: [59953303827eceb06d486ba66cc0d71f55ded8ec] objtool: Disassemble code with libopcodes instead of running objdump
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months ago
On 12/9/25 07:58, Guenter Roeck wrote:
> Hi,
> 
> On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:
>> objtool executes the objdump command to disassemble code. Use libopcodes
>> instead to have more control about the disassembly scope and output.
>> If libopcodes is not present then objtool is built without disassembly
>> support.
>>
>> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
> 
> With this patch in mainline, I get the following build error when trying
> to build x86_64:defconfig (and other configurations).
> 
> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
> 

This sounds like a configuration issue depending on the binutils version; in
particular the setting of DISASM_INIT_STYLED (although that's supposed to be
automatically configured by tools/objtool/Makefile).

Which binutils-devel version are you using?

Thanks,

alex.

> 
> Building x86_64:defconfig ... failed
> --------------
> Error log:
> In file included from disas.c:17:
> tools/include/tools/dis-asm-compat.h:19:39: error: ‘enum disassembler_style’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror]
>     19 |                                  enum disassembler_style style,
>        |                                       ^~~~~~~~~~~~~~~~~~
> tools/include/tools/dis-asm-compat.h:19:58: error: parameter 2 (‘style’) has incomplete type
>     19 |                                  enum disassembler_style style,
>        |                                  ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
> tools/include/tools/dis-asm-compat.h:42:49: error: unknown type name ‘fprintf_styled_ftype’; did you mean ‘fprintf_ftype’?
>     42 |                                                 fprintf_styled_ftype styled_func)
>        |                                                 ^~~~~~~~~~~~~~~~~~~~
>        |                                                 fprintf_ftype
> disas.c:152:38: error: ‘enum disassembler_style’ declared inside parameter list will not be visible outside of this definition or declaration [-Werror]
>    152 |                                 enum disassembler_style style,
>        |                                      ^~~~~~~~~~~~~~~~~~
> disas.c:152:57: error: parameter 2 (‘style’) has incomplete type
>    152 |                                 enum disassembler_style style,
>        |                                 ~~~~~~~~~~~~~~~~~~~~~~~~^~~~~
> disas.c: In function ‘disas_context_create’:
> disas.c:367:9: error: implicit declaration of function ‘init_disassemble_info_compat’; did you mean ‘init_disassemble_info’? [-Werror=implicit-function-declaration]
>    367 |         init_disassemble_info_compat(dinfo, dctx,
>        |         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
>        |         init_disassemble_info
> In file included from tools/include/linux/kernel.h:5,
>                   from tools/include/linux/list.h:7,
>                   from tools/objtool/include/objtool/arch.h:10,
>                   from disas.c:9:
> disas.c: In function ‘disas_fprintf_styled’:
> disas.c:158:9: error: ‘va_start’ used in function with fixed arguments
>    158 |         va_start(arg, fmt);
>        |         ^~~~~~~~
> cc1: all warnings being treated as errors
> 
> ---
> Bisect:
> 
> # bad: [cb015814f8b6eebcbb8e46e111d108892c5e6821] Merge tag 'f2fs-for-6.19-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs
> # good: [7d0a66e4bb9081d75c82ec4957c50034cb0ea449] Linux 6.18
> git bisect start 'HEAD' 'v6.18'
> # bad: [6dfafbd0299a60bfb5d5e277fdf100037c7ded07] Merge tag 'drm-next-2025-12-03' of https://gitlab.freedesktop.org/drm/kernel
> git bisect bad 6dfafbd0299a60bfb5d5e277fdf100037c7ded07
> # bad: [8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88] Merge tag 'net-next-6.19' of git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net-next
> git bisect bad 8f7aa3d3c7323f4ca2768a9e74ebbe359c4f8f88
> # bad: [4a4e0199378f309fa7259132e1443efe56c1e276] Merge tag 'lkmm.2025.12.01a' of git://git.kernel.org/pub/scm/linux/kernel/git/paulmck/linux-rcu
> git bisect bad 4a4e0199378f309fa7259132e1443efe56c1e276
> # bad: [a61288200e8b6f42bff116508dc72ebcc206f10a] Merge tag 'ras_core_for_v6.19_rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
> git bisect bad a61288200e8b6f42bff116508dc72ebcc206f10a
> # good: [1b5dd29869b1e63f7e5c37d7552e2dcf22de3c26] Merge tag 'vfs-6.19-rc1.fd_prepare.fs' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
> git bisect good 1b5dd29869b1e63f7e5c37d7552e2dcf22de3c26
> # bad: [dcd8637edb873bd940e6aa82417dfb33ae980778] Merge tag 'x86-core-2025-12-01' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
> git bisect bad dcd8637edb873bd940e6aa82417dfb33ae980778
> # bad: [7e017720aae87dc2ca2471ac295e34e2b240e5f5] objtool: Disassemble jump table alternatives
> git bisect bad 7e017720aae87dc2ca2471ac295e34e2b240e5f5
> # good: [164c9201e1dad8d5c0c38f583dba81e4b6da9cc7] objtool: Add base objtool support for livepatch modules
> git bisect good 164c9201e1dad8d5c0c38f583dba81e4b6da9cc7
> # good: [9205a322cf96f16a49e412dfa3f09431f3e02fc5] objtool: Return canonical symbol when aliases exist in symbol finding helpers
> git bisect good 9205a322cf96f16a49e412dfa3f09431f3e02fc5
> # bad: [0bb080ba6469a573bc85122153d931334d10a173] objtool: Disassemble instruction on warning or backtrace
> git bisect bad 0bb080ba6469a573bc85122153d931334d10a173
> # good: [93863f3f859a626347ce2ec18947b11357b4ca14] kbuild: Check for functions with ambiguous -ffunction-sections section names
> git bisect good 93863f3f859a626347ce2ec18947b11357b4ca14
> # bad: [59953303827eceb06d486ba66cc0d71f55ded8ec] objtool: Disassemble code with libopcodes instead of running objdump
> git bisect bad 59953303827eceb06d486ba66cc0d71f55ded8ec
> # good: [55d2a473f317ab028d78a5c5ca69473643657c3d] objtool: Move disassembly functions to a separated file
> git bisect good 55d2a473f317ab028d78a5c5ca69473643657c3d
> # good: [1013f2e37bec39b1df5679e1c1e2572ece87c088] objtool: Create disassembly context
> git bisect good 1013f2e37bec39b1df5679e1c1e2572ece87c088
> # first bad commit: [59953303827eceb06d486ba66cc0d71f55ded8ec] objtool: Disassemble code with libopcodes instead of running objdump

Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Maciej W. Rozycki 2 months ago
On Tue, 9 Dec 2025, Alexandre Chartre wrote:

> > Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
> > 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
> 
> This sounds like a configuration issue depending on the binutils version; in
> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
> automatically configured by tools/objtool/Makefile).

 I only came across these patches now.

 As attractive as it may seem how is this stuff supposed to fly given that 
binutils internal libraries promise no stable API to out-of-tree software.  
The interfaces can change anytime, just as it is with our internals.

 Wouldn't it make sense to improve objdump instead so as to provide the 
features required?

 Also is it actually legal to link objtool and libopcodes together, given 
that they are GPLv2 and GPLv3 respectively?

 FWIW asking as one of the binutils contributors and port maintainers.

  Maciej
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Peter Zijlstra 2 months ago
On Tue, Dec 09, 2025 at 10:25:03PM +0000, Maciej W. Rozycki wrote:

>  Wouldn't it make sense to improve objdump instead so as to provide the 
> features required?

I don't think objdump wants to know about our very Linux kernel specific
sections - which are somewhat subject to change as well.
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Maciej W. Rozycki 2 months ago
On Wed, 10 Dec 2025, Peter Zijlstra wrote:

> >  Wouldn't it make sense to improve objdump instead so as to provide the 
> > features required?
> 
> I don't think objdump wants to know about our very Linux kernel specific
> sections - which are somewhat subject to change as well.

 Is it something that needs to be embedded in the code itself rather than 
being supplied via the command line?

  Maciej
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months ago
On 12/9/25 23:25, Maciej W. Rozycki wrote:
> On Tue, 9 Dec 2025, Alexandre Chartre wrote:
> 
>>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>
>> This sounds like a configuration issue depending on the binutils version; in
>> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
>> automatically configured by tools/objtool/Makefile).
> 
>   I only came across these patches now.
> 
>   As attractive as it may seem how is this stuff supposed to fly given that
> binutils internal libraries promise no stable API to out-of-tree software.
> The interfaces can change anytime, just as it is with our internals.

Note that bpf (tools/bpf) is also using libbfd and libopcodes.

>   Wouldn't it make sense to improve objdump instead so as to provide the
> features required?

Some disassembly features of objtool can certainly make sense in objdump
(like the support for disassembling alternatives).

But the primary goal was to provide a disassembly tracing option (--trace)
to help troubleshoot objtool validation failures. But this is specific to the
kernel build workflow, so this part would probably not make sense in objdump.

alex.

>   Also is it actually legal to link objtool and libopcodes together, given
> that they are GPLv2 and GPLv3 respectively?
> 
>   FWIW asking as one of the binutils contributors and port maintainers.
> 
>    Maciej
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Peter Zijlstra 2 months ago
On Wed, Dec 10, 2025 at 08:06:51AM +0100, Alexandre Chartre wrote:

> >   As attractive as it may seem how is this stuff supposed to fly given that
> > binutils internal libraries promise no stable API to out-of-tree software.
> > The interfaces can change anytime, just as it is with our internals.
> 
> Note that bpf (tools/bpf) is also using libbfd and libopcodes.

OTOH tools/perf opted to use libcapstone instead.
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 2 months ago
On 12/9/25 14:25, Maciej W. Rozycki wrote:
> On Tue, 9 Dec 2025, Alexandre Chartre wrote:
> 
>>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>
>> This sounds like a configuration issue depending on the binutils version; in
>> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
>> automatically configured by tools/objtool/Makefile).
> 
>   I only came across these patches now.
> 
>   As attractive as it may seem how is this stuff supposed to fly given that
> binutils internal libraries promise no stable API to out-of-tree software.
> The interfaces can change anytime, just as it is with our internals.
> 
>   Wouldn't it make sense to improve objdump instead so as to provide the
> features required?
> 
>   Also is it actually legal to link objtool and libopcodes together, given
> that they are GPLv2 and GPLv3 respectively?
> 
>   FWIW asking as one of the binutils contributors and port maintainers.
> 

After some more digging I found that the version of binutils installed in
the system determines if the build error is seen or not. So I can have
a toolchain with the latest binutils version, but the build still fails if
the binutils version installed in the system is too old. 2.38 (as in
Ubuntu 22.04) is too old since it does not define 'enum disassembler_style'
in dis-asm.h. As far as I can see that enum was only introduced with
binutils 2.39.

Now the problem is that DISASM_INIT_STYLED is evaluated with
tools/build/feature/test-disassembler-init-styled.c, which checks for
the existence of struct disassemble_info (not the enum). AFAICS that
structure was introduced with binutils-2_18.

Unless my analysis is wrong that means that Linux will now fail to build
on systems with binutils 2.18 ... 2.38 installed.

Guenter
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months ago

On 12/10/25 06:08, Guenter Roeck wrote:
> On 12/9/25 14:25, Maciej W. Rozycki wrote:
>> On Tue, 9 Dec 2025, Alexandre Chartre wrote:
>>
>>>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>>>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>>
>>> This sounds like a configuration issue depending on the binutils version; in
>>> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
>>> automatically configured by tools/objtool/Makefile).
>>
>>   I only came across these patches now.
>>
>>   As attractive as it may seem how is this stuff supposed to fly given that
>> binutils internal libraries promise no stable API to out-of-tree software.
>> The interfaces can change anytime, just as it is with our internals.
>>
>>   Wouldn't it make sense to improve objdump instead so as to provide the
>> features required?
>>
>>   Also is it actually legal to link objtool and libopcodes together, given
>> that they are GPLv2 and GPLv3 respectively?
>>
>>   FWIW asking as one of the binutils contributors and port maintainers.
>>
> 
> After some more digging I found that the version of binutils installed in
> the system determines if the build error is seen or not. So I can have
> a toolchain with the latest binutils version, but the build still fails if
> the binutils version installed in the system is too old. 2.38 (as in
> Ubuntu 22.04) is too old since it does not define 'enum disassembler_style'
> in dis-asm.h. As far as I can see that enum was only introduced with
> binutils 2.39.
> 
> Now the problem is that DISASM_INIT_STYLED is evaluated with
> tools/build/feature/test-disassembler-init-styled.c, which checks for
> the existence of struct disassemble_info (not the enum). AFAICS that
> structure was introduced with binutils-2_18.
> 
> Unless my analysis is wrong that means that Linux will now fail to build
> on systems with binutils 2.18 ... 2.38 installed.


test-disassembler-init-styled.c tests that init_disassemble_info() has four
arguments. If this fails then this means that it has only three args and in
that case enum disassembler_style is not defined, and the build should be done
without defining DISASM_INIT_STYLED.

init_disassemble_info() was changed from 3 args to 4 args by the following
binutils change, and enum disassembler_style was introduced at the same time:

   https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07

This change has caused the Linux build to fail, and this was fixed by commit
a45b3d6926231 ("tools include: add dis-asm-compat.h to handle version differences")
which introduced dis-asm-compat.h

Can you check that init_disassemble_info() is declared with only 3 arguments
in your dis-asm.h file? (/usr/include/dis-asm.h).

dis-asm.h should either:

- declare init_disassemble_info() with 3 args and not define enum disassembler_style

or

- declare init_disassemble_info() with 4 args and define enum disassembler_style


alex.

Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 2 months ago
On 12/9/25 23:53, Alexandre Chartre wrote:
> 
> 
> On 12/10/25 06:08, Guenter Roeck wrote:
>> On 12/9/25 14:25, Maciej W. Rozycki wrote:
>>> On Tue, 9 Dec 2025, Alexandre Chartre wrote:
>>>
>>>>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>>>>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>>>
>>>> This sounds like a configuration issue depending on the binutils version; in
>>>> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
>>>> automatically configured by tools/objtool/Makefile).
>>>
>>>   I only came across these patches now.
>>>
>>>   As attractive as it may seem how is this stuff supposed to fly given that
>>> binutils internal libraries promise no stable API to out-of-tree software.
>>> The interfaces can change anytime, just as it is with our internals.
>>>
>>>   Wouldn't it make sense to improve objdump instead so as to provide the
>>> features required?
>>>
>>>   Also is it actually legal to link objtool and libopcodes together, given
>>> that they are GPLv2 and GPLv3 respectively?
>>>
>>>   FWIW asking as one of the binutils contributors and port maintainers.
>>>
>>
>> After some more digging I found that the version of binutils installed in
>> the system determines if the build error is seen or not. So I can have
>> a toolchain with the latest binutils version, but the build still fails if
>> the binutils version installed in the system is too old. 2.38 (as in
>> Ubuntu 22.04) is too old since it does not define 'enum disassembler_style'
>> in dis-asm.h. As far as I can see that enum was only introduced with
>> binutils 2.39.
>>
>> Now the problem is that DISASM_INIT_STYLED is evaluated with
>> tools/build/feature/test-disassembler-init-styled.c, which checks for
>> the existence of struct disassemble_info (not the enum). AFAICS that
>> structure was introduced with binutils-2_18.
>>
>> Unless my analysis is wrong that means that Linux will now fail to build
>> on systems with binutils 2.18 ... 2.38 installed.
> 
> 
> test-disassembler-init-styled.c tests that init_disassemble_info() has four
> arguments. If this fails then this means that it has only three args and in
> that case enum disassembler_style is not defined, and the build should be done
> without defining DISASM_INIT_STYLED.
> 
> init_disassemble_info() was changed from 3 args to 4 args by the following
> binutils change, and enum disassembler_style was introduced at the same time:
> 
>    https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07
> 
> This change has caused the Linux build to fail, and this was fixed by commit
> a45b3d6926231 ("tools include: add dis-asm-compat.h to handle version differences")
> which introduced dis-asm-compat.h
> 
> Can you check that init_disassemble_info() is declared with only 3 arguments
> in your dis-asm.h file? (/usr/include/dis-asm.h).
> 
> dis-asm.h should either:
> 
> - declare init_disassemble_info() with 3 args and not define enum disassembler_style
> 
> or
> 
> - declare init_disassemble_info() with 4 args and define enum disassembler_style
> 

Thanks for the clarification.

Here is the declaration:

/* Method to initialize a disassemble_info struct.  This should be
    called by all applications creating such a struct.  */
extern void init_disassemble_info (struct disassemble_info *dinfo, void *stream,
                                    fprintf_ftype fprintf_func);

So it has three arguments, but DISASM_INIT_STYLED is defined anyway.

I think I found the problem: not even "make mrproper" or "make distclean" deletes
./tools/objtool/feature/test-disassembler-init-styled.bin, and the file existed
in my tree. So DISASM_INIT_STYLED just ended up being defined until I deleted
the file manually.

This condition also happens in my build system. I have not been able to re-create
the situation where the .bin file exists but shouldn't, but it somehow does happen.

Guenter

Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months ago

On 12/10/25 16:26, Guenter Roeck wrote:
> On 12/9/25 23:53, Alexandre Chartre wrote:
>>
>>
>> On 12/10/25 06:08, Guenter Roeck wrote:
>>> On 12/9/25 14:25, Maciej W. Rozycki wrote:
>>>> On Tue, 9 Dec 2025, Alexandre Chartre wrote:
>>>>
>>>>>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>>>>>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>>>>
>>>>> This sounds like a configuration issue depending on the binutils version; in
>>>>> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
>>>>> automatically configured by tools/objtool/Makefile).
>>>>
>>>>   I only came across these patches now.
>>>>
>>>>   As attractive as it may seem how is this stuff supposed to fly given that
>>>> binutils internal libraries promise no stable API to out-of-tree software.
>>>> The interfaces can change anytime, just as it is with our internals.
>>>>
>>>>   Wouldn't it make sense to improve objdump instead so as to provide the
>>>> features required?
>>>>
>>>>   Also is it actually legal to link objtool and libopcodes together, given
>>>> that they are GPLv2 and GPLv3 respectively?
>>>>
>>>>   FWIW asking as one of the binutils contributors and port maintainers.
>>>>
>>>
>>> After some more digging I found that the version of binutils installed in
>>> the system determines if the build error is seen or not. So I can have
>>> a toolchain with the latest binutils version, but the build still fails if
>>> the binutils version installed in the system is too old. 2.38 (as in
>>> Ubuntu 22.04) is too old since it does not define 'enum disassembler_style'
>>> in dis-asm.h. As far as I can see that enum was only introduced with
>>> binutils 2.39.
>>>
>>> Now the problem is that DISASM_INIT_STYLED is evaluated with
>>> tools/build/feature/test-disassembler-init-styled.c, which checks for
>>> the existence of struct disassemble_info (not the enum). AFAICS that
>>> structure was introduced with binutils-2_18.
>>>
>>> Unless my analysis is wrong that means that Linux will now fail to build
>>> on systems with binutils 2.18 ... 2.38 installed.
>>
>>
>> test-disassembler-init-styled.c tests that init_disassemble_info() has four
>> arguments. If this fails then this means that it has only three args and in
>> that case enum disassembler_style is not defined, and the build should be done
>> without defining DISASM_INIT_STYLED.
>>
>> init_disassemble_info() was changed from 3 args to 4 args by the following
>> binutils change, and enum disassembler_style was introduced at the same time:
>>
>>    https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07
>>
>> This change has caused the Linux build to fail, and this was fixed by commit
>> a45b3d6926231 ("tools include: add dis-asm-compat.h to handle version differences")
>> which introduced dis-asm-compat.h
>>
>> Can you check that init_disassemble_info() is declared with only 3 arguments
>> in your dis-asm.h file? (/usr/include/dis-asm.h).
>>
>> dis-asm.h should either:
>>
>> - declare init_disassemble_info() with 3 args and not define enum disassembler_style
>>
>> or
>>
>> - declare init_disassemble_info() with 4 args and define enum disassembler_style
>>
> 
> Thanks for the clarification.
> 
> Here is the declaration:
> 
> /* Method to initialize a disassemble_info struct.  This should be
>     called by all applications creating such a struct.  */
> extern void init_disassemble_info (struct disassemble_info *dinfo, void *stream,
>                                     fprintf_ftype fprintf_func);
> 
> So it has three arguments, but DISASM_INIT_STYLED is defined anyway.
> 
> I think I found the problem: not even "make mrproper" or "make distclean" deletes
> ./tools/objtool/feature/test-disassembler-init-styled.bin, and the file existed
> in my tree. So DISASM_INIT_STYLED just ended up being defined until I deleted
> the file manually.
> 
> This condition also happens in my build system. I have not been able to re-create
> the situation where the .bin file exists but shouldn't, but it somehow does happen.
> 

That's weird because the "clean" rule in tools/objtool/Makefile should remove the
entire tools/objtool/feature directory:

$ cat tools/objtool/Makefile
...
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/cpu-feature-names.c $(OUTPUT)fixdep
	$(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
	$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
	$(Q)$(RM) -r -- $(OUTPUT)feature

see $(Q)$(RM) -r -- $(OUTPUT)feature

alex.





Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 2 months ago
On 12/10/25 08:36, Alexandre Chartre wrote:
> 
> 
> On 12/10/25 16:26, Guenter Roeck wrote:
>> On 12/9/25 23:53, Alexandre Chartre wrote:
>>>
>>>
>>> On 12/10/25 06:08, Guenter Roeck wrote:
>>>> On 12/9/25 14:25, Maciej W. Rozycki wrote:
>>>>> On Tue, 9 Dec 2025, Alexandre Chartre wrote:
>>>>>
>>>>>>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>>>>>>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>>>>>
>>>>>> This sounds like a configuration issue depending on the binutils version; in
>>>>>> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
>>>>>> automatically configured by tools/objtool/Makefile).
>>>>>
>>>>>   I only came across these patches now.
>>>>>
>>>>>   As attractive as it may seem how is this stuff supposed to fly given that
>>>>> binutils internal libraries promise no stable API to out-of-tree software.
>>>>> The interfaces can change anytime, just as it is with our internals.
>>>>>
>>>>>   Wouldn't it make sense to improve objdump instead so as to provide the
>>>>> features required?
>>>>>
>>>>>   Also is it actually legal to link objtool and libopcodes together, given
>>>>> that they are GPLv2 and GPLv3 respectively?
>>>>>
>>>>>   FWIW asking as one of the binutils contributors and port maintainers.
>>>>>
>>>>
>>>> After some more digging I found that the version of binutils installed in
>>>> the system determines if the build error is seen or not. So I can have
>>>> a toolchain with the latest binutils version, but the build still fails if
>>>> the binutils version installed in the system is too old. 2.38 (as in
>>>> Ubuntu 22.04) is too old since it does not define 'enum disassembler_style'
>>>> in dis-asm.h. As far as I can see that enum was only introduced with
>>>> binutils 2.39.
>>>>
>>>> Now the problem is that DISASM_INIT_STYLED is evaluated with
>>>> tools/build/feature/test-disassembler-init-styled.c, which checks for
>>>> the existence of struct disassemble_info (not the enum). AFAICS that
>>>> structure was introduced with binutils-2_18.
>>>>
>>>> Unless my analysis is wrong that means that Linux will now fail to build
>>>> on systems with binutils 2.18 ... 2.38 installed.
>>>
>>>
>>> test-disassembler-init-styled.c tests that init_disassemble_info() has four
>>> arguments. If this fails then this means that it has only three args and in
>>> that case enum disassembler_style is not defined, and the build should be done
>>> without defining DISASM_INIT_STYLED.
>>>
>>> init_disassemble_info() was changed from 3 args to 4 args by the following
>>> binutils change, and enum disassembler_style was introduced at the same time:
>>>
>>>    https://sourceware.org/git/?p=binutils-gdb.git;a=commit;h=60a3da00bd5407f07
>>>
>>> This change has caused the Linux build to fail, and this was fixed by commit
>>> a45b3d6926231 ("tools include: add dis-asm-compat.h to handle version differences")
>>> which introduced dis-asm-compat.h
>>>
>>> Can you check that init_disassemble_info() is declared with only 3 arguments
>>> in your dis-asm.h file? (/usr/include/dis-asm.h).
>>>
>>> dis-asm.h should either:
>>>
>>> - declare init_disassemble_info() with 3 args and not define enum disassembler_style
>>>
>>> or
>>>
>>> - declare init_disassemble_info() with 4 args and define enum disassembler_style
>>>
>>
>> Thanks for the clarification.
>>
>> Here is the declaration:
>>
>> /* Method to initialize a disassemble_info struct.  This should be
>>     called by all applications creating such a struct.  */
>> extern void init_disassemble_info (struct disassemble_info *dinfo, void *stream,
>>                                     fprintf_ftype fprintf_func);
>>
>> So it has three arguments, but DISASM_INIT_STYLED is defined anyway.
>>
>> I think I found the problem: not even "make mrproper" or "make distclean" deletes
>> ./tools/objtool/feature/test-disassembler-init-styled.bin, and the file existed
>> in my tree. So DISASM_INIT_STYLED just ended up being defined until I deleted
>> the file manually.
>>
>> This condition also happens in my build system. I have not been able to re-create
>> the situation where the .bin file exists but shouldn't, but it somehow does happen.
>>
> 
> That's weird because the "clean" rule in tools/objtool/Makefile should remove the
> entire tools/objtool/feature directory:
> 
> $ cat tools/objtool/Makefile
> ...
> 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/cpu-feature-names.c $(OUTPUT)fixdep
>      $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
>      $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
>      $(Q)$(RM) -r -- $(OUTPUT)feature
> 
> see $(Q)$(RM) -r -- $(OUTPUT)feature
>

$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.c
./tools/objtool/feature/test-disassembler-init-styled.make.output
./tools/objtool/feature/test-disassembler-init-styled.d
$ make clean
   CLEAN   arch/x86/lib
   CLEAN   certs
   CLEAN   arch/x86/entry/vdso
   CLEAN   init
   CLEAN   lib/crc
   CLEAN   arch/x86/tools
   CLEAN   arch/x86/realmode/rm
   CLEAN   security/selinux
   CLEAN   usr
   CLEAN   .
$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.c
./tools/objtool/feature/test-disassembler-init-styled.make.output
./tools/objtool/feature/test-disassembler-init-styled.d
$

"make clean" does not execute the cleanup in tools/objtool. I have to run
"make -C tools/objtool clean". "make mrproper" doesn't clean tools either.

I guess that is on purpose ?

Anyway, even "make -C tools/objtool clean" doesn't solve my problem if I build with
an out-of-tree toolchain. Turns out that, in this case, test-disassembler-init-styled
is build with binutils from the toolchain (which succeeds because its binutils
version is more recent). Later on, the actual build of disas.c fails because it uses
the system toolchain.

Guenter

Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 2 months ago
On 12/10/25 10:42, Guenter Roeck wrote:
...
>> That's weird because the "clean" rule in tools/objtool/Makefile should remove the
>> entire tools/objtool/feature directory:
>>
>> $ cat tools/objtool/Makefile
>> ...
>> 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/cpu-feature-names.c $(OUTPUT)fixdep
>>      $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
>>      $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
>>      $(Q)$(RM) -r -- $(OUTPUT)feature
>>
>> see $(Q)$(RM) -r -- $(OUTPUT)feature
>>
> 
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/objtool/feature/test-disassembler-init-styled.make.output
> ./tools/objtool/feature/test-disassembler-init-styled.d
> $ make clean
>    CLEAN   arch/x86/lib
>    CLEAN   certs
>    CLEAN   arch/x86/entry/vdso
>    CLEAN   init
>    CLEAN   lib/crc
>    CLEAN   arch/x86/tools
>    CLEAN   arch/x86/realmode/rm
>    CLEAN   security/selinux
>    CLEAN   usr
>    CLEAN   .
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/objtool/feature/test-disassembler-init-styled.make.output
> ./tools/objtool/feature/test-disassembler-init-styled.d
> $
> 
> "make clean" does not execute the cleanup in tools/objtool. I have to run
> "make -C tools/objtool clean". "make mrproper" doesn't clean tools either.
> 
> I guess that is on purpose ?
> 
> Anyway, even "make -C tools/objtool clean" doesn't solve my problem if I build with
> an out-of-tree toolchain. Turns out that, in this case, test-disassembler-init-styled
> is build with binutils from the toolchain (which succeeds because its binutils
> version is more recent). Later on, the actual build of disas.c fails because it uses
> the system toolchain.
> 

This is actually even worse than I thought.

$ git clean -d -x -f -q
$ make defconfig
$ make -j40
$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.c
./tools/objtool/feature/test-disassembler-init-styled.make.output
./tools/objtool/feature/test-disassembler-init-styled.d
$ make -C tools/objtool/ clean
$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.make.output
./tools/build/feature/test-disassembler-init-styled.c
./tools/build/feature/test-disassembler-init-styled.d
$ rm -f ./tools/build/feature/test-disassembler-init-styled.make.output ./tools/build/feature/test-disassembler-init-styled.d
$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.c
$ make -C tools/objtool/ clean
$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.make.output
./tools/build/feature/test-disassembler-init-styled.c
./tools/build/feature/test-disassembler-init-styled.d

Repeated with local toolchain:
$ git clean -d -x -f -q
$ make-x86 defconfig
$ make-x86 -j40
   (fails)
$ make-x86 -C tools/objtool clean
$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.c
./tools/objtool/feature/test-disassembler-init-styled.make.output
./tools/objtool/feature/test-disassembler-init-styled.bin
./tools/objtool/feature/test-disassembler-init-styled.d

So the "make clean" command actually (re-)creates the .bin file. Worse,
if I now run

$ make -C tools/objtool clean

with the system toolchain, the .bin file is not removed.

$ find . -name test-disassembler-init-styled*
./tools/build/feature/test-disassembler-init-styled.make.output
./tools/build/feature/test-disassembler-init-styled.bin
./tools/build/feature/test-disassembler-init-styled.c
./tools/build/feature/test-disassembler-init-styled.d

The local toolchain used in this example is gcc 13.3.0 with binutils 2.42.

Guenter

Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 1 month, 3 weeks ago
On 12/10/25 22:54, Guenter Roeck wrote:
> On 12/10/25 10:42, Guenter Roeck wrote:
> ...
>>> That's weird because the "clean" rule in tools/objtool/Makefile should remove the
>>> entire tools/objtool/feature directory:
>>>
>>> $ cat tools/objtool/Makefile
>>> ...
>>> 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/cpu-feature-names.c $(OUTPUT)fixdep
>>>      $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
>>>      $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
>>>      $(Q)$(RM) -r -- $(OUTPUT)feature
>>>
>>> see $(Q)$(RM) -r -- $(OUTPUT)feature
>>>
>>
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/objtool/feature/test-disassembler-init-styled.make.output
>> ./tools/objtool/feature/test-disassembler-init-styled.d
>> $ make clean
>>    CLEAN   arch/x86/lib
>>    CLEAN   certs
>>    CLEAN   arch/x86/entry/vdso
>>    CLEAN   init
>>    CLEAN   lib/crc
>>    CLEAN   arch/x86/tools
>>    CLEAN   arch/x86/realmode/rm
>>    CLEAN   security/selinux
>>    CLEAN   usr
>>    CLEAN   .
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/objtool/feature/test-disassembler-init-styled.make.output
>> ./tools/objtool/feature/test-disassembler-init-styled.d
>> $
>>
>> "make clean" does not execute the cleanup in tools/objtool. I have to run
>> "make -C tools/objtool clean". "make mrproper" doesn't clean tools either.
>>
>> I guess that is on purpose ?
>>
>> Anyway, even "make -C tools/objtool clean" doesn't solve my problem if I build with
>> an out-of-tree toolchain. Turns out that, in this case, test-disassembler-init-styled
>> is build with binutils from the toolchain (which succeeds because its binutils
>> version is more recent). Later on, the actual build of disas.c fails because it uses
>> the system toolchain.
>>
> 
> This is actually even worse than I thought.
> 
> $ git clean -d -x -f -q
> $ make defconfig
> $ make -j40
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/objtool/feature/test-disassembler-init-styled.make.output
> ./tools/objtool/feature/test-disassembler-init-styled.d
> $ make -C tools/objtool/ clean
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.make.output
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/build/feature/test-disassembler-init-styled.d
> $ rm -f ./tools/build/feature/test-disassembler-init-styled.make.output ./tools/build/feature/test-disassembler-init-styled.d
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.c
> $ make -C tools/objtool/ clean
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.make.output
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/build/feature/test-disassembler-init-styled.d
> 
> Repeated with local toolchain:
> $ git clean -d -x -f -q
> $ make-x86 defconfig
> $ make-x86 -j40
>    (fails)
> $ make-x86 -C tools/objtool clean
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/objtool/feature/test-disassembler-init-styled.make.output
> ./tools/objtool/feature/test-disassembler-init-styled.bin
> ./tools/objtool/feature/test-disassembler-init-styled.d
> 
> So the "make clean" command actually (re-)creates the .bin file. Worse,
> if I now run
> 
> $ make -C tools/objtool clean
> 
> with the system toolchain, the .bin file is not removed.
> 
> $ find . -name test-disassembler-init-styled*
> ./tools/build/feature/test-disassembler-init-styled.make.output
> ./tools/build/feature/test-disassembler-init-styled.bin
> ./tools/build/feature/test-disassembler-init-styled.c
> ./tools/build/feature/test-disassembler-init-styled.d
> 
> The local toolchain used in this example is gcc 13.3.0 with binutils 2.42.
> 

I think I've found the issue. The problem is that the feature check for objtool
is also run for the "clean" target, and this creates the
tools/build/feature/test-disassembler-init-styled.* files.

The change below should fix this problem. I will prepare a patch. Note that
you will still need to run "make -C tools/objtool clean" (I don't know the
reason for  "make clean" not to cleanup tools/objtool).

Thanks,

alex.

-----

diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index ad6e1ec706ce0..0c9238cacdcea 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -77,7 +77,22 @@ HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
  FEATURE_USER = .objtool
  FEATURE_TESTS = libbfd disassembler-init-styled
  FEATURE_DISPLAY =
+
+check_feat := 1
+NON_CHECK_FEAT_TARGETS := clean uninstall doc doc-clean doc-install doc-uninstall
+ifdef MAKECMDGOALS
+ifeq ($(filter-out $(NON_CHECK_FEAT_TARGETS),$(MAKECMDGOALS)),)
+  check_feat := 0
+endif
+endif
+
+ifeq ($(check_feat),1)
+ifeq ($(FEATURES_DUMP),)
  include $(srctree)/tools/build/Makefile.feature
+else
+include $(FEATURES_DUMP)
+endif
+endif
  
  ifeq ($(feature-disassembler-init-styled), 1)
         OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED


Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 1 month, 3 weeks ago
On 12/15/25 08:00, Alexandre Chartre wrote:
> 
> On 12/10/25 22:54, Guenter Roeck wrote:
>> On 12/10/25 10:42, Guenter Roeck wrote:
>> ...
>>>> That's weird because the "clean" rule in tools/objtool/Makefile should remove the
>>>> entire tools/objtool/feature directory:
>>>>
>>>> $ cat tools/objtool/Makefile
>>>> ...
>>>> 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/cpu-feature-names.c $(OUTPUT)fixdep
>>>>      $(Q)$(RM) $(OUTPUT)arch/x86/lib/inat-tables.c $(OUTPUT)fixdep
>>>>      $(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
>>>>      $(Q)$(RM) -r -- $(OUTPUT)feature
>>>>
>>>> see $(Q)$(RM) -r -- $(OUTPUT)feature
>>>>
>>>
>>> $ find . -name test-disassembler-init-styled*
>>> ./tools/build/feature/test-disassembler-init-styled.c
>>> ./tools/objtool/feature/test-disassembler-init-styled.make.output
>>> ./tools/objtool/feature/test-disassembler-init-styled.d
>>> $ make clean
>>>    CLEAN   arch/x86/lib
>>>    CLEAN   certs
>>>    CLEAN   arch/x86/entry/vdso
>>>    CLEAN   init
>>>    CLEAN   lib/crc
>>>    CLEAN   arch/x86/tools
>>>    CLEAN   arch/x86/realmode/rm
>>>    CLEAN   security/selinux
>>>    CLEAN   usr
>>>    CLEAN   .
>>> $ find . -name test-disassembler-init-styled*
>>> ./tools/build/feature/test-disassembler-init-styled.c
>>> ./tools/objtool/feature/test-disassembler-init-styled.make.output
>>> ./tools/objtool/feature/test-disassembler-init-styled.d
>>> $
>>>
>>> "make clean" does not execute the cleanup in tools/objtool. I have to run
>>> "make -C tools/objtool clean". "make mrproper" doesn't clean tools either.
>>>
>>> I guess that is on purpose ?
>>>
>>> Anyway, even "make -C tools/objtool clean" doesn't solve my problem if I build with
>>> an out-of-tree toolchain. Turns out that, in this case, test-disassembler-init-styled
>>> is build with binutils from the toolchain (which succeeds because its binutils
>>> version is more recent). Later on, the actual build of disas.c fails because it uses
>>> the system toolchain.
>>>
>>
>> This is actually even worse than I thought.
>>
>> $ git clean -d -x -f -q
>> $ make defconfig
>> $ make -j40
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/objtool/feature/test-disassembler-init-styled.make.output
>> ./tools/objtool/feature/test-disassembler-init-styled.d
>> $ make -C tools/objtool/ clean
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.make.output
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/build/feature/test-disassembler-init-styled.d
>> $ rm -f ./tools/build/feature/test-disassembler-init-styled.make.output ./tools/build/feature/test-disassembler-init-styled.d
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.c
>> $ make -C tools/objtool/ clean
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.make.output
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/build/feature/test-disassembler-init-styled.d
>>
>> Repeated with local toolchain:
>> $ git clean -d -x -f -q
>> $ make-x86 defconfig
>> $ make-x86 -j40
>>    (fails)
>> $ make-x86 -C tools/objtool clean
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/objtool/feature/test-disassembler-init-styled.make.output
>> ./tools/objtool/feature/test-disassembler-init-styled.bin
>> ./tools/objtool/feature/test-disassembler-init-styled.d
>>
>> So the "make clean" command actually (re-)creates the .bin file. Worse,
>> if I now run
>>
>> $ make -C tools/objtool clean
>>
>> with the system toolchain, the .bin file is not removed.
>>
>> $ find . -name test-disassembler-init-styled*
>> ./tools/build/feature/test-disassembler-init-styled.make.output
>> ./tools/build/feature/test-disassembler-init-styled.bin
>> ./tools/build/feature/test-disassembler-init-styled.c
>> ./tools/build/feature/test-disassembler-init-styled.d
>>
>> The local toolchain used in this example is gcc 13.3.0 with binutils 2.42.
>>
> 
> I think I've found the issue. The problem is that the feature check for objtool
> is also run for the "clean" target, and this creates the
> tools/build/feature/test-disassembler-init-styled.* files.
> 
> The change below should fix this problem. I will prepare a patch. Note that
> you will still need to run "make -C tools/objtool clean" (I don't know the
> reason for  "make clean" not to cleanup tools/objtool).
> 

My workaround was to install Ubuntu 24.04 instead of 22.04 on all affected
systems, so I won't see the problem anymore (and obviously that also means
that I am unable to test potential fixes).

Guenter

Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Guenter Roeck 2 months ago
On 12/8/25 23:51, Alexandre Chartre wrote:
> 
> On 12/9/25 07:58, Guenter Roeck wrote:
>> Hi,
>>
>> On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:
>>> objtool executes the objdump command to disassemble code. Use libopcodes
>>> instead to have more control about the disassembly scope and output.
>>> If libopcodes is not present then objtool is built without disassembly
>>> support.
>>>
>>> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
>>
>> With this patch in mainline, I get the following build error when trying
>> to build x86_64:defconfig (and other configurations).
>>
>> Bisect log is attached. I see the problem with gcc 11.4.0, 13.3.0, and
>> 14.3.0. I tried with both Ubuntu 22.04 and 24.04.
>>
> 
> This sounds like a configuration issue depending on the binutils version; in
> particular the setting of DISASM_INIT_STYLED (although that's supposed to be
> automatically configured by tools/objtool/Makefile).
> 
> Which binutils-devel version are you using?
> 

I tried 2.38 (with gcc 11.4), 2.42 (with gcc 13.3), and 2.44 (with gcc 14.3)
in three different environments (Ubuntu 12.4, 14.4, and self-built tools).

Guenter
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Nathan Chancellor 2 months, 2 weeks ago
Hi Alexandre,

On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:
> objtool executes the objdump command to disassemble code. Use libopcodes
> instead to have more control about the disassembly scope and output.
> If libopcodes is not present then objtool is built without disassembly
> support.
> 
> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
...
> diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
> index d89f8b5ec14e3..18c0e69ee6170 100644
> --- a/tools/objtool/include/objtool/arch.h
> +++ b/tools/objtool/include/objtool/arch.h
> @@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc);
>  unsigned int arch_reloc_size(struct reloc *reloc);
>  unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
>  
> +#ifdef DISAS
> +
> +#include <bfd.h>

This include of bfd.h breaks the build for me:

  $ make -skj"$(nproc)" ARCH=x86_64 CROSS_COMPILE=x86_64-linux- clean defconfig bzImage
  In file included from tools/objtool/include/objtool/arch.h:108,
                   from check.c:14:
  /usr/include/bfd.h:35:2: error: #error config.h must be included before this header
     35 | #error config.h must be included before this header
        |  ^~~~~
  ...

where my bfd.h has:

  #ifndef __BFD_H_SEEN__
  #define __BFD_H_SEEN__

  /* PR 14072: Ensure that config.h is included first.  */
  #if !defined PACKAGE && !defined PACKAGE_VERSION
  #error config.h must be included before this header
  #endif

Something like this cures it for me but I am not sure if that is a
proper fix or not since I see config.h in my binutils build folder has
many other defines.

Cheers,
Nathan

diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index df793ca6fc1a..96df4a73da23 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -87,7 +87,7 @@ BUILD_DISAS := n
 
 ifeq ($(feature-libbfd),1)
 	BUILD_DISAS := y
-	OBJTOOL_CFLAGS += -DDISAS
+	OBJTOOL_CFLAGS += -DDISAS -DPACKAGE="objtool-disas"
 	OBJTOOL_LDFLAGS += -lopcodes
 endif
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months, 2 weeks ago
On 11/25/25 19:16, Nathan Chancellor wrote:
> Hi Alexandre,
> 
> On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:
>> objtool executes the objdump command to disassemble code. Use libopcodes
>> instead to have more control about the disassembly scope and output.
>> If libopcodes is not present then objtool is built without disassembly
>> support.
>>
>> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
> ...
>> diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
>> index d89f8b5ec14e3..18c0e69ee6170 100644
>> --- a/tools/objtool/include/objtool/arch.h
>> +++ b/tools/objtool/include/objtool/arch.h
>> @@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc);
>>   unsigned int arch_reloc_size(struct reloc *reloc);
>>   unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
>>   
>> +#ifdef DISAS
>> +
>> +#include <bfd.h>
> 
> This include of bfd.h breaks the build for me:
> 
>    $ make -skj"$(nproc)" ARCH=x86_64 CROSS_COMPILE=x86_64-linux- clean defconfig bzImage
>    In file included from tools/objtool/include/objtool/arch.h:108,
>                     from check.c:14:
>    /usr/include/bfd.h:35:2: error: #error config.h must be included before this header
>       35 | #error config.h must be included before this header
>          |  ^~~~~
>    ...
> 
> where my bfd.h has:
> 
>    #ifndef __BFD_H_SEEN__
>    #define __BFD_H_SEEN__
> 
>    /* PR 14072: Ensure that config.h is included first.  */
>    #if !defined PACKAGE && !defined PACKAGE_VERSION
>    #error config.h must be included before this header
>    #endif

This check is effectively present in the bfd.h file generated from the
binutils source code. However it is not present in the bfd.h file provided
by the binutils RPM. I think this explained why we haven't seen this issue
so far.

For history, this was introduced in 2012 for bug 14072. Then there was
complaints reported in bug 14243 and 15920. But it was decided not to
remove this change, and the suggested fix was to define PACKAGE when
including bfd.h.

https://sourceware.org/bugzilla/show_bug.cgi?id=14072
https://sourceware.org/bugzilla/show_bug.cgi?id=14243
https://sourceware.org/bugzilla/show_bug.cgi?id=15920

And Redhat has fixed the issue for the binutils RPM by removing this test:

https://sourceware.org/bugzilla/show_bug.cgi?id=14243


> Something like this cures it for me but I am not sure if that is a
> proper fix or not since I see config.h in my binutils build folder has
> many other defines.
> 
> Cheers,
> Nathan
> 
> diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
> index df793ca6fc1a..96df4a73da23 100644
> --- a/tools/objtool/Makefile
> +++ b/tools/objtool/Makefile
> @@ -87,7 +87,7 @@ BUILD_DISAS := n
>   
>   ifeq ($(feature-libbfd),1)
>   	BUILD_DISAS := y
> -	OBJTOOL_CFLAGS += -DDISAS
> +	OBJTOOL_CFLAGS += -DDISAS -DPACKAGE="objtool-disas"
>   	OBJTOOL_LDFLAGS += -lopcodes
>   endif
>   

This is the proper fix (as indicated in the binutils bugs), and this is
what the other kernel tools using bfd.h (bpf and perf) do. I will create
a patch with your suggestion.

Thanks,

alex.
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by David Laight 2 months, 2 weeks ago
On Wed, 26 Nov 2025 10:00:36 +0100
Alexandre Chartre <alexandre.chartre@oracle.com> wrote:

> On 11/25/25 19:16, Nathan Chancellor wrote:
> > Hi Alexandre,
> > 
> > On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:  
> >> objtool executes the objdump command to disassemble code. Use libopcodes
> >> instead to have more control about the disassembly scope and output.
> >> If libopcodes is not present then objtool is built without disassembly
> >> support.
> >>
> >> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>  
> > ...  
> >> diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
> >> index d89f8b5ec14e3..18c0e69ee6170 100644
> >> --- a/tools/objtool/include/objtool/arch.h
> >> +++ b/tools/objtool/include/objtool/arch.h
> >> @@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc);
> >>   unsigned int arch_reloc_size(struct reloc *reloc);
> >>   unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
> >>   
> >> +#ifdef DISAS
> >> +
> >> +#include <bfd.h>  
> > 
> > This include of bfd.h breaks the build for me:
> > 
> >    $ make -skj"$(nproc)" ARCH=x86_64 CROSS_COMPILE=x86_64-linux- clean defconfig bzImage
> >    In file included from tools/objtool/include/objtool/arch.h:108,
> >                     from check.c:14:
> >    /usr/include/bfd.h:35:2: error: #error config.h must be included before this header
> >       35 | #error config.h must be included before this header
> >          |  ^~~~~
> >    ...
> > 
> > where my bfd.h has:
> > 
> >    #ifndef __BFD_H_SEEN__
> >    #define __BFD_H_SEEN__
> > 
> >    /* PR 14072: Ensure that config.h is included first.  */
> >    #if !defined PACKAGE && !defined PACKAGE_VERSION
> >    #error config.h must be included before this header
> >    #endif  
> 
> This check is effectively present in the bfd.h file generated from the
> binutils source code. However it is not present in the bfd.h file provided
> by the binutils RPM. I think this explained why we haven't seen this issue
> so far.
> 
> For history, this was introduced in 2012 for bug 14072. Then there was
> complaints reported in bug 14243 and 15920. But it was decided not to
> remove this change, and the suggested fix was to define PACKAGE when
> including bfd.h.
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=14072
> https://sourceware.org/bugzilla/show_bug.cgi?id=14243
> https://sourceware.org/bugzilla/show_bug.cgi?id=15920
> 
> And Redhat has fixed the issue for the binutils RPM by removing this test:
> 
> https://sourceware.org/bugzilla/show_bug.cgi?id=14243
> 
> 
> > Something like this cures it for me but I am not sure if that is a
> > proper fix or not since I see config.h in my binutils build folder has
> > many other defines.
> > 
> > Cheers,
> > Nathan
> > 
> > diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
> > index df793ca6fc1a..96df4a73da23 100644
> > --- a/tools/objtool/Makefile
> > +++ b/tools/objtool/Makefile
> > @@ -87,7 +87,7 @@ BUILD_DISAS := n
> >   
> >   ifeq ($(feature-libbfd),1)
> >   	BUILD_DISAS := y
> > -	OBJTOOL_CFLAGS += -DDISAS
> > +	OBJTOOL_CFLAGS += -DDISAS -DPACKAGE="objtool-disas"
> >   	OBJTOOL_LDFLAGS += -lopcodes
> >   endif
> >     
> 
> This is the proper fix (as indicated in the binutils bugs), and this is
> what the other kernel tools using bfd.h (bpf and perf) do. I will create
> a patch with your suggestion.

ISTM that defining it just before including bfd.h is cleaner and more obvious.

	David

> 
> Thanks,
> 
> alex.
> 
>
Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
Posted by Alexandre Chartre 2 months, 2 weeks ago
On 11/26/25 11:07, David Laight wrote:
> On Wed, 26 Nov 2025 10:00:36 +0100
> Alexandre Chartre <alexandre.chartre@oracle.com> wrote:
> 
>> On 11/25/25 19:16, Nathan Chancellor wrote:
>>> Hi Alexandre,
>>>
>>> On Fri, Nov 21, 2025 at 10:53:13AM +0100, Alexandre Chartre wrote:
>>>> objtool executes the objdump command to disassemble code. Use libopcodes
>>>> instead to have more control about the disassembly scope and output.
>>>> If libopcodes is not present then objtool is built without disassembly
>>>> support.
>>>>
>>>> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
>>> ...
>>>> diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
>>>> index d89f8b5ec14e3..18c0e69ee6170 100644
>>>> --- a/tools/objtool/include/objtool/arch.h
>>>> +++ b/tools/objtool/include/objtool/arch.h
>>>> @@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc);
>>>>    unsigned int arch_reloc_size(struct reloc *reloc);
>>>>    unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
>>>>    
>>>> +#ifdef DISAS
>>>> +
>>>> +#include <bfd.h>
>>>
>>> This include of bfd.h breaks the build for me:
>>>
>>>     $ make -skj"$(nproc)" ARCH=x86_64 CROSS_COMPILE=x86_64-linux- clean defconfig bzImage
>>>     In file included from tools/objtool/include/objtool/arch.h:108,
>>>                      from check.c:14:
>>>     /usr/include/bfd.h:35:2: error: #error config.h must be included before this header
>>>        35 | #error config.h must be included before this header
>>>           |  ^~~~~
>>>     ...
>>>
>>> where my bfd.h has:
>>>
>>>     #ifndef __BFD_H_SEEN__
>>>     #define __BFD_H_SEEN__
>>>
>>>     /* PR 14072: Ensure that config.h is included first.  */
>>>     #if !defined PACKAGE && !defined PACKAGE_VERSION
>>>     #error config.h must be included before this header
>>>     #endif
>>
>> This check is effectively present in the bfd.h file generated from the
>> binutils source code. However it is not present in the bfd.h file provided
>> by the binutils RPM. I think this explained why we haven't seen this issue
>> so far.
>>
>> For history, this was introduced in 2012 for bug 14072. Then there was
>> complaints reported in bug 14243 and 15920. But it was decided not to
>> remove this change, and the suggested fix was to define PACKAGE when
>> including bfd.h.
>>
>> https://sourceware.org/bugzilla/show_bug.cgi?id=14072
>> https://sourceware.org/bugzilla/show_bug.cgi?id=14243
>> https://sourceware.org/bugzilla/show_bug.cgi?id=15920
>>
>> And Redhat has fixed the issue for the binutils RPM by removing this test:
>>
>> https://sourceware.org/bugzilla/show_bug.cgi?id=14243
>>
>>
>>> Something like this cures it for me but I am not sure if that is a
>>> proper fix or not since I see config.h in my binutils build folder has
>>> many other defines.
>>>
>>> Cheers,
>>> Nathan
>>>
>>> diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
>>> index df793ca6fc1a..96df4a73da23 100644
>>> --- a/tools/objtool/Makefile
>>> +++ b/tools/objtool/Makefile
>>> @@ -87,7 +87,7 @@ BUILD_DISAS := n
>>>    
>>>    ifeq ($(feature-libbfd),1)
>>>    	BUILD_DISAS := y
>>> -	OBJTOOL_CFLAGS += -DDISAS
>>> +	OBJTOOL_CFLAGS += -DDISAS -DPACKAGE="objtool-disas"
>>>    	OBJTOOL_LDFLAGS += -lopcodes
>>>    endif
>>>      
>>
>> This is the proper fix (as indicated in the binutils bugs), and this is
>> what the other kernel tools using bfd.h (bpf and perf) do. I will create
>> a patch with your suggestion.
> 
> ISTM that defining it just before including bfd.h is cleaner and more obvious.
> 

bfd.h is included at two different places: in arch.h and disas.c (and disas.c
includes arch.h). So we would need to ensure that PACKAGE always has the same
definition otherwise we will have a redefine error.

So I think it is simpler to have a global definition with -DPACKAGE.
Similarly, bpf has multiple includes and uses -DPACKAGE=bpf, while perf has
a single include and use a single define before the include.

alex.
[tip: objtool/core] objtool: Disassemble code with libopcodes instead of running objdump
Posted by tip-bot2 for Alexandre Chartre 2 months, 2 weeks ago
The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     59953303827eceb06d486ba66cc0d71f55ded8ec
Gitweb:        https://git.kernel.org/tip/59953303827eceb06d486ba66cc0d71f55ded8ec
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:13 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Fri, 21 Nov 2025 15:30:07 +01:00

objtool: Disassemble code with libopcodes instead of running objdump

objtool executes the objdump command to disassemble code. Use libopcodes
instead to have more control about the disassembly scope and output.
If libopcodes is not present then objtool is built without disassembly
support.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Acked-by: Josh Poimboeuf <jpoimboe@kernel.org>
Link: https://patch.msgid.link/20251121095340.464045-4-alexandre.chartre@oracle.com
---
 tools/objtool/.gitignore              |   2 +-
 tools/objtool/Build                   |   3 +-
 tools/objtool/Makefile                |  25 +++-
 tools/objtool/arch/loongarch/decode.c |  12 ++-
 tools/objtool/arch/powerpc/decode.c   |  12 ++-
 tools/objtool/arch/x86/decode.c       |  12 ++-
 tools/objtool/check.c                 |  14 +-
 tools/objtool/disas.c                 | 187 ++++++++++++++++---------
 tools/objtool/include/objtool/arch.h  |   9 +-
 tools/objtool/include/objtool/check.h |   5 +-
 tools/objtool/include/objtool/disas.h |  29 ++++-
 11 files changed, 238 insertions(+), 72 deletions(-)

diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 4faa4dd..7593036 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -1,5 +1,7 @@
 # SPDX-License-Identifier: GPL-2.0-only
 arch/x86/lib/inat-tables.c
 /objtool
+feature
+FEATURE-DUMP.objtool
 fixdep
 libsubcmd/
diff --git a/tools/objtool/Build b/tools/objtool/Build
index 17e50a1..9d1e8f2 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -7,7 +7,8 @@ objtool-y += special.o
 objtool-y += builtin-check.o
 objtool-y += elf.o
 objtool-y += objtool.o
-objtool-y += disas.o
+
+objtool-$(BUILD_DISAS) += disas.o
 
 objtool-$(BUILD_ORC) += orc_gen.o orc_dump.o
 objtool-$(BUILD_KLP) += builtin-klp.o klp-diff.o klp-post-link.o
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 021f55b..df793ca 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -70,6 +70,29 @@ OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
 # Always want host compilation.
 HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
 
+#
+# To support disassembly, objtool needs libopcodes which is provided
+# with libbdf (binutils-dev or binutils-devel package).
+#
+FEATURE_USER = .objtool
+FEATURE_TESTS = libbfd disassembler-init-styled
+FEATURE_DISPLAY =
+include $(srctree)/tools/build/Makefile.feature
+
+ifeq ($(feature-disassembler-init-styled), 1)
+	OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED
+endif
+
+BUILD_DISAS := n
+
+ifeq ($(feature-libbfd),1)
+	BUILD_DISAS := y
+	OBJTOOL_CFLAGS += -DDISAS
+	OBJTOOL_LDFLAGS += -lopcodes
+endif
+
+export BUILD_DISAS
+
 AWK = awk
 MKDIR = mkdir
 
@@ -103,6 +126,8 @@ 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
+	$(Q)$(RM) -- $(OUTPUT)FEATURE-DUMP.objtool
+	$(Q)$(RM) -r -- $(OUTPUT)feature
 
 FORCE:
 
diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 0115b97..1de86eb 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0-or-later
 #include <string.h>
 #include <objtool/check.h>
+#include <objtool/disas.h>
 #include <objtool/warn.h>
 #include <asm/inst.h>
 #include <asm/orc_types.h>
@@ -414,3 +415,14 @@ unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *tabl
 		return reloc->sym->offset + reloc_addend(reloc);
 	}
 }
+
+#ifdef DISAS
+
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_loongarch,
+			       bfd_mach_loongarch32, bfd_mach_loongarch64,
+			       NULL);
+}
+
+#endif /* DISAS */
diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c
index 3a9b748..4f68b40 100644
--- a/tools/objtool/arch/powerpc/decode.c
+++ b/tools/objtool/arch/powerpc/decode.c
@@ -3,6 +3,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <objtool/check.h>
+#include <objtool/disas.h>
 #include <objtool/elf.h>
 #include <objtool/arch.h>
 #include <objtool/warn.h>
@@ -127,3 +128,14 @@ unsigned int arch_reloc_size(struct reloc *reloc)
 		return 8;
 	}
 }
+
+#ifdef DISAS
+
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_powerpc,
+			       bfd_mach_ppc, bfd_mach_ppc64,
+			       NULL);
+}
+
+#endif /* DISAS */
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index cc85db7..83e9c60 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -16,6 +16,7 @@
 
 #include <asm/orc_types.h>
 #include <objtool/check.h>
+#include <objtool/disas.h>
 #include <objtool/elf.h>
 #include <objtool/arch.h>
 #include <objtool/warn.h>
@@ -949,3 +950,14 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc)
 		return false;
 	}
 }
+
+#ifdef DISAS
+
+int arch_disas_info_init(struct disassemble_info *dinfo)
+{
+	return disas_info_init(dinfo, bfd_arch_i386,
+			       bfd_mach_i386_i386, bfd_mach_x86_64,
+			       "att");
+}
+
+#endif /* DISAS */
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 8b1a6a5..21d45a3 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4926,8 +4926,6 @@ int check(struct objtool_file *file)
 			goto out;
 	}
 
-	free_insns(file);
-
 	if (opts.stats) {
 		printf("nr_insns_visited: %ld\n", nr_insns_visited);
 		printf("nr_cfi: %ld\n", nr_cfi);
@@ -4936,8 +4934,10 @@ int check(struct objtool_file *file)
 	}
 
 out:
-	if (!ret && !warnings)
+	if (!ret && !warnings) {
+		free_insns(file);
 		return 0;
+	}
 
 	if (opts.werror && warnings)
 		ret = 1;
@@ -4946,10 +4946,14 @@ out:
 		if (opts.werror && warnings)
 			WARN("%d warning(s) upgraded to errors", warnings);
 		disas_ctx = disas_context_create(file);
-		disas_warned_funcs(disas_ctx);
-		disas_context_destroy(disas_ctx);
+		if (disas_ctx) {
+			disas_warned_funcs(disas_ctx);
+			disas_context_destroy(disas_ctx);
+		}
 	}
 
+	free_insns(file);
+
 	if (opts.backup && make_backup())
 		return 1;
 
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 7a18e51..11ac2ec 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -4,18 +4,56 @@
  */
 
 #include <objtool/arch.h>
+#include <objtool/check.h>
 #include <objtool/disas.h>
 #include <objtool/warn.h>
 
+#include <bfd.h>
 #include <linux/string.h>
+#include <tools/dis-asm-compat.h>
 
 struct disas_context {
 	struct objtool_file *file;
+	disassembler_ftype disassembler;
+	struct disassemble_info info;
 };
 
+#define DINFO_FPRINTF(dinfo, ...)	\
+	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
+
+/*
+ * Initialize disassemble info arch, mach (32 or 64-bit) and options.
+ */
+int disas_info_init(struct disassemble_info *dinfo,
+		    int arch, int mach32, int mach64,
+		    const char *options)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct objtool_file *file = dctx->file;
+
+	dinfo->arch = arch;
+
+	switch (file->elf->ehdr.e_ident[EI_CLASS]) {
+	case ELFCLASS32:
+		dinfo->mach = mach32;
+		break;
+	case ELFCLASS64:
+		dinfo->mach = mach64;
+		break;
+	default:
+		return -1;
+	}
+
+	dinfo->disassembler_options = options;
+
+	return 0;
+}
+
 struct disas_context *disas_context_create(struct objtool_file *file)
 {
 	struct disas_context *dctx;
+	struct disassemble_info *dinfo;
+	int err;
 
 	dctx = malloc(sizeof(*dctx));
 	if (!dctx) {
@@ -24,8 +62,49 @@ struct disas_context *disas_context_create(struct objtool_file *file)
 	}
 
 	dctx->file = file;
+	dinfo = &dctx->info;
+
+	init_disassemble_info_compat(dinfo, stdout,
+				     (fprintf_ftype)fprintf,
+				     fprintf_styled);
+
+	dinfo->read_memory_func = buffer_read_memory;
+	dinfo->application_data = dctx;
+
+	/*
+	 * bfd_openr() is not used to avoid doing ELF data processing
+	 * and caching that has already being done. Here, we just need
+	 * to identify the target file so we call an arch specific
+	 * function to fill some disassemble info (arch, mach).
+	 */
+
+	dinfo->arch = bfd_arch_unknown;
+	dinfo->mach = 0;
+
+	err = arch_disas_info_init(dinfo);
+	if (err || dinfo->arch == bfd_arch_unknown || dinfo->mach == 0) {
+		WARN("failed to init disassembly arch");
+		goto error;
+	}
+
+	dinfo->endian = (file->elf->ehdr.e_ident[EI_DATA] == ELFDATA2MSB) ?
+		BFD_ENDIAN_BIG : BFD_ENDIAN_LITTLE;
+
+	disassemble_init_for_target(dinfo);
+
+	dctx->disassembler = disassembler(dinfo->arch,
+					  dinfo->endian == BFD_ENDIAN_BIG,
+					  dinfo->mach, NULL);
+	if (!dctx->disassembler) {
+		WARN("failed to create disassembler function");
+		goto error;
+	}
 
 	return dctx;
+
+error:
+	free(dctx);
+	return NULL;
 }
 
 void disas_context_destroy(struct disas_context *dctx)
@@ -33,86 +112,62 @@ void disas_context_destroy(struct disas_context *dctx)
 	free(dctx);
 }
 
-/* 'funcs' is a space-separated list of function names */
-static void disas_funcs(const char *funcs)
+/*
+ * Disassemble a single instruction. Return the size of the instruction.
+ */
+static size_t disas_insn(struct disas_context *dctx,
+			 struct instruction *insn)
 {
-	const char *objdump_str, *cross_compile;
-	int size, ret;
-	char *cmd;
-
-	cross_compile = getenv("CROSS_COMPILE");
-	if (!cross_compile)
-		cross_compile = "";
-
-	objdump_str = "%sobjdump -wdr %s | gawk -M -v _funcs='%s' '"
-			"BEGIN { split(_funcs, funcs); }"
-			"/^$/ { func_match = 0; }"
-			"/<.*>:/ { "
-				"f = gensub(/.*<(.*)>:/, \"\\\\1\", 1);"
-				"for (i in funcs) {"
-					"if (funcs[i] == f) {"
-						"func_match = 1;"
-						"base = strtonum(\"0x\" $1);"
-						"break;"
-					"}"
-				"}"
-			"}"
-			"{"
-				"if (func_match) {"
-					"addr = strtonum(\"0x\" $1);"
-					"printf(\"%%04x \", addr - base);"
-					"print;"
-				"}"
-			"}' 1>&2";
-
-	/* fake snprintf() to calculate the size */
-	size = snprintf(NULL, 0, objdump_str, cross_compile, objname, funcs) + 1;
-	if (size <= 0) {
-		WARN("objdump string size calculation failed");
-		return;
+	disassembler_ftype disasm = dctx->disassembler;
+	struct disassemble_info *dinfo = &dctx->info;
+
+	if (insn->type == INSN_NOP) {
+		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
+		return insn->len;
 	}
 
-	cmd = malloc(size);
+	/*
+	 * Set the disassembler buffer to read data from the section
+	 * containing the instruction to disassemble.
+	 */
+	dinfo->buffer = insn->sec->data->d_buf;
+	dinfo->buffer_vma = 0;
+	dinfo->buffer_length = insn->sec->sh.sh_size;
 
-	/* real snprintf() */
-	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
-	ret = system(cmd);
-	if (ret) {
-		WARN("disassembly failed: %d", ret);
-		return;
+	return disasm(insn->offset, &dctx->info);
+}
+
+/*
+ * Disassemble a function.
+ */
+static void disas_func(struct disas_context *dctx, struct symbol *func)
+{
+	struct instruction *insn;
+	size_t addr;
+
+	printf("%s:\n", func->name);
+	sym_for_each_insn(dctx->file, func, insn) {
+		addr = insn->offset;
+		printf(" %6lx:  %s+0x%-6lx      ",
+		       addr, func->name, addr - func->offset);
+		disas_insn(dctx, insn);
+		printf("\n");
 	}
+	printf("\n");
 }
 
+/*
+ * Disassemble all warned functions.
+ */
 void disas_warned_funcs(struct disas_context *dctx)
 {
 	struct symbol *sym;
-	char *funcs = NULL, *tmp;
 
 	if (!dctx)
 		return;
 
 	for_each_sym(dctx->file->elf, sym) {
-		if (sym->warned) {
-			if (!funcs) {
-				funcs = malloc(strlen(sym->name) + 1);
-				if (!funcs) {
-					ERROR_GLIBC("malloc");
-					return;
-				}
-				strcpy(funcs, sym->name);
-			} else {
-				tmp = malloc(strlen(funcs) + strlen(sym->name) + 2);
-				if (!tmp) {
-					ERROR_GLIBC("malloc");
-					return;
-				}
-				sprintf(tmp, "%s %s", funcs, sym->name);
-				free(funcs);
-				funcs = tmp;
-			}
-		}
+		if (sym->warned)
+			disas_func(dctx, sym);
 	}
-
-	if (funcs)
-		disas_funcs(funcs);
 }
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index d89f8b5..18c0e69 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -103,4 +103,13 @@ bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc);
 unsigned int arch_reloc_size(struct reloc *reloc);
 unsigned long arch_jump_table_sym_offset(struct reloc *reloc, struct reloc *table);
 
+#ifdef DISAS
+
+#include <bfd.h>
+#include <dis-asm.h>
+
+int arch_disas_info_init(struct disassemble_info *dinfo);
+
+#endif /* DISAS */
+
 #endif /* _ARCH_H */
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index d73b0c3..674f574 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -127,4 +127,9 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc
 	     insn && insn->sec == _sec;					\
 	     insn = next_insn_same_sec(file, insn))
 
+#define sym_for_each_insn(file, sym, insn)				\
+	for (insn = find_insn(file, sym->sec, sym->offset);		\
+	     insn && insn->offset < sym->offset + sym->len;		\
+	     insn = next_insn_same_sec(file, insn))
+
 #endif /* _CHECK_H */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 5c543b6..3ec3ce2 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -7,8 +7,37 @@
 #define _DISAS_H
 
 struct disas_context;
+struct disassemble_info;
+
+#ifdef DISAS
+
 struct disas_context *disas_context_create(struct objtool_file *file);
 void disas_context_destroy(struct disas_context *dctx);
 void disas_warned_funcs(struct disas_context *dctx);
+int disas_info_init(struct disassemble_info *dinfo,
+		    int arch, int mach32, int mach64,
+		    const char *options);
+
+#else /* DISAS */
+
+#include <objtool/warn.h>
+
+static inline struct disas_context *disas_context_create(struct objtool_file *file)
+{
+	WARN("Rebuild with libopcodes for disassembly support");
+	return NULL;
+}
+
+static inline void disas_context_destroy(struct disas_context *dctx) {}
+static inline void disas_warned_funcs(struct disas_context *dctx) {}
+
+static inline int disas_info_init(struct disassemble_info *dinfo,
+				  int arch, int mach32, int mach64,
+				  const char *options)
+{
+	return -1;
+}
+
+#endif /* DISAS */
 
 #endif /* _DISAS_H */