Re: [RFC 00/13] objtool: Function validation tracing

From: Alexandre Chartre
Date: Tue Jun 10 2025 - 09:14:38 EST



On 6/10/25 09:07, Alexandre Chartre wrote:


On 6/9/25 20:31, Josh Poimboeuf wrote:
On Fri, Jun 06, 2025 at 05:34:27PM +0200, Alexandre Chartre wrote:
Hi,

This RFC provides two changes to objtool.

- Disassemble code with libopcodes instead of running objdump

   objtool executes the objdump command to disassemble code. In particular,
   if objtool fails to validate a function then it will use objdump to
   disassemble the entire file which is not very helpful when processing
   a large file (like vmlinux.o).

   Using libopcodes provides more control about the disassembly scope and
   output, and it is possible to disassemble a single instruction or
   a single function. Now when objtool fails to validate a function it
   will disassemble that single function instead of disassembling the
   entire file.

- Add the --trace <function> option to trace function validation

   Figuring out why a function validation has failed can be difficult because
   objtool checks all code flows (including alternatives) and maintains
   instructions states (in particular call frame information).

   The trace option allows to follow the function validation done by objtool
   instruction per instruction, see what objtool is doing and get function
   validation information. An output example is shown below.

What do I need for this to build?  It wasn't compiling due to missing
bfd.h, so I installed binutils-devel (Fedora) and now I get:

That's because of the more recent binutils versions, while I have been using an old
one (2.30) and some functions have changed. But tools/dis-asm-compat.h handles that
when the appropriate #define is set.

Below is a quick fix (tested with binutils 2.41), and I will work on a proper fix
(by using the tools/ features to check the disassembler version).


Here is the patch to handle both old and new binutils versions:

8<------------------------------------------------------------------->8
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 00350fc7c662..91a2858fea14 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -7,6 +7,11 @@ srctree := $(patsubst %/,%,$(dir $(CURDIR)))
srctree := $(patsubst %/,%,$(dir $(srctree)))
endif
+FEATURE_USER = .objtool
+FEATURE_TESTS = disassembler-init-styled
+FEATURE_DISPLAY = disassembler-init-styled
+include $(srctree)/tools/build/Makefile.feature
+
LIBSUBCMD_DIR = $(srctree)/tools/lib/subcmd/
ifneq ($(OUTPUT),)
LIBSUBCMD_OUTPUT = $(abspath $(OUTPUT))/libsubcmd
@@ -40,6 +45,10 @@ OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) -lopcodes
elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)
+ifeq ($(feature-disassembler-init-styled), 1)
+OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED
+endif
+
# Always want host compilation.
HOST_OVERRIDES := CC="$(HOSTCC)" LD="$(HOSTLD)" AR="$(HOSTAR)"
8<------------------------------------------------------------------->8

alex.


diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index 00350fc7c662..300ea727e454 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -36,6 +36,8 @@ WARNINGS := $(EXTRA_WARNINGS) -Wno-switch-default -Wno-switch-enum -Wno-packed -
 OBJTOOL_CFLAGS := -Werror $(WARNINGS) $(KBUILD_HOSTCFLAGS) -g $(INCLUDES) $(LIBELF_FLAGS)
 OBJTOOL_LDFLAGS := $(LIBELF_LIBS) $(LIBSUBCMD) $(KBUILD_HOSTLDFLAGS) -lopcodes

+OBJTOOL_CFLAGS += -DDISASM_INIT_STYLED
+
 # Allow old libelf to be used:
 elfshdr := $(shell echo '$(pound)include <libelf.h>' | $(HOSTCC) $(OBJTOOL_CFLAGS) -x c -E - 2>/dev/null | grep elf_getshdr)
 OBJTOOL_CFLAGS += $(if $(elfshdr),,-DLIBELF_USE_DEPRECATED)


alex.


In file included from disas.c:12:
/home/jpoimboe/git/linux/tools/include/tools/dis-asm-compat.h:10:6: error: redeclaration of ‘enum disassembler_style’
    10 | enum disassembler_style {DISASSEMBLER_STYLE_NOT_EMPTY};
       |      ^~~~~~~~~~~~~~~~~~
In file included from /home/jpoimboe/git/linux/tools/objtool/include/objtool/arch.h:10,
                  from disas.c:6:
/usr/include/dis-asm.h:53:6: note: originally defined here
    53 | enum disassembler_style
       |      ^~~~~~~~~~~~~~~~~~
/home/jpoimboe/git/linux/tools/include/tools/dis-asm-compat.h: In function ‘init_disassemble_info_compat’:
/home/jpoimboe/git/linux/tools/include/tools/dis-asm-compat.h:50:9: error: too few arguments to function ‘init_disassemble_info’
    50 |         init_disassemble_info(info, stream,
       |         ^~~~~~~~~~~~~~~~~~~~~
/usr/include/dis-asm.h:480:13: note: declared here
   480 | extern void init_disassemble_info (struct disassemble_info *dinfo, void *stream,
       |             ^~~~~~~~~~~~~~~~~~~~~
make[4]: *** [/home/jpoimboe/git/linux/tools/build/Makefile.build:86: /home/jpoimboe/git/linux/tools/objtool/disas.o] Error 1
make[4]: *** Waiting for unfinished jobs....
make[3]: *** [Makefile:65: /home/jpoimboe/git/linux/tools/objtool/objtool-in.o] Error 2
make[2]: *** [Makefile:73: objtool] Error 2
make[1]: *** [/home/jpoimboe/git/linux/Makefile:1448: tools/objtool] Error 2
make: *** [Makefile:248: __sub-make] Error 2