]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
objtool: Disassemble instruction on warning or backtrace
authorAlexandre Chartre <alexandre.chartre@oracle.com>
Fri, 21 Nov 2025 09:53:17 +0000 (10:53 +0100)
committerPeter Zijlstra <peterz@infradead.org>
Fri, 21 Nov 2025 14:30:08 +0000 (15:30 +0100)
When an instruction warning (WARN_INSN) or backtrace (BT_INSN) is issued,
disassemble the instruction to provide more context.

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-8-alexandre.chartre@oracle.com
tools/objtool/check.c
tools/objtool/disas.c
tools/objtool/include/objtool/check.h
tools/objtool/include/objtool/disas.h
tools/objtool/include/objtool/warn.h

index 0999717abc9cb2523bc0f134aa02f3cd83e3f14d..4da1f07b353832ca04d0299247c0e006030130f5 100644 (file)
@@ -4792,11 +4792,34 @@ static void free_insns(struct objtool_file *file)
                free(chunk->addr);
 }
 
+static struct disas_context *objtool_disas_ctx;
+
+const char *objtool_disas_insn(struct instruction *insn)
+{
+       struct disas_context *dctx = objtool_disas_ctx;
+
+       if (!dctx)
+               return "";
+
+       disas_insn(dctx, insn);
+       return disas_result(dctx);
+}
+
 int check(struct objtool_file *file)
 {
-       struct disas_context *disas_ctx;
+       struct disas_context *disas_ctx = NULL;
        int ret = 0, warnings = 0;
 
+       /*
+        * If the verbose or backtrace option is used then we need a
+        * disassembly context to disassemble instruction or function
+        * on warning or backtrace.
+        */
+       if (opts.verbose || opts.backtrace) {
+               disas_ctx = disas_context_create(file);
+               objtool_disas_ctx = disas_ctx;
+       }
+
        arch_initial_func_cfi_state(&initial_func_cfi);
        init_cfi_state(&init_cfi);
        init_cfi_state(&func_cfi);
@@ -4936,11 +4959,12 @@ out:
        if (opts.verbose) {
                if (opts.werror && warnings)
                        WARN("%d warning(s) upgraded to errors", warnings);
-               disas_ctx = disas_context_create(file);
-               if (disas_ctx) {
-                       disas_warned_funcs(disas_ctx);
-                       disas_context_destroy(disas_ctx);
-               }
+               disas_warned_funcs(disas_ctx);
+       }
+
+       if (disas_ctx) {
+               disas_context_destroy(disas_ctx);
+               objtool_disas_ctx = NULL;
        }
 
        free_insns(file);
index 89daa121b40b027dc0c43f6737c2ffdf492fc04d..a030b06c121d2ed3c024c158fe78784bae5f7de8 100644 (file)
@@ -303,7 +303,7 @@ void disas_context_destroy(struct disas_context *dctx)
        free(dctx);
 }
 
-static char *disas_result(struct disas_context *dctx)
+char *disas_result(struct disas_context *dctx)
 {
        return dctx->result;
 }
@@ -311,8 +311,7 @@ static char *disas_result(struct disas_context *dctx)
 /*
  * Disassemble a single instruction. Return the size of the instruction.
  */
-static size_t disas_insn(struct disas_context *dctx,
-                        struct instruction *insn)
+size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 {
        disassembler_ftype disasm = dctx->disassembler;
        struct disassemble_info *dinfo = &dctx->info;
index ad9c73504b120df51a2cc273a47b64809723756e..f96aabd7d54dc06d847327be3066ca6ffaf00c0d 100644 (file)
@@ -141,4 +141,6 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc
             insn && insn->offset < sym->offset + sym->len;             \
             insn = next_insn_same_sec(file, insn))
 
+const char *objtool_disas_insn(struct instruction *insn);
+
 #endif /* _CHECK_H */
index 3ec3ce2e4e6f0106aa9e03323827d0a47c5ae41d..1aee1fbe0bb978bc095c265ce054292ddf8c29a4 100644 (file)
@@ -17,6 +17,8 @@ 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);
+size_t disas_insn(struct disas_context *dctx, struct instruction *insn);
+char *disas_result(struct disas_context *dctx);
 
 #else /* DISAS */
 
@@ -38,6 +40,17 @@ static inline int disas_info_init(struct disassemble_info *dinfo,
        return -1;
 }
 
+static inline size_t disas_insn(struct disas_context *dctx,
+                               struct instruction *insn)
+{
+       return -1;
+}
+
+static inline char *disas_result(struct disas_context *dctx)
+{
+       return NULL;
+}
+
 #endif /* DISAS */
 
 #endif /* _DISAS_H */
index a1e3927d8e7ceda61287cfd17318eb5e69ddbbde..f32abc7b1be14dd5d9a8d86cd27bdcf98fcda52d 100644 (file)
@@ -77,9 +77,11 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 #define WARN_INSN(insn, format, ...)                                   \
 ({                                                                     \
        struct instruction *_insn = (insn);                             \
-       if (!_insn->sym || !_insn->sym->warned)                         \
+       if (!_insn->sym || !_insn->sym->warned) {                       \
                WARN_FUNC(_insn->sec, _insn->offset, format,            \
                          ##__VA_ARGS__);                               \
+               BT_INSN(_insn, "");                                     \
+       }                                                               \
        if (_insn->sym)                                                 \
                _insn->sym->warned = 1;                                 \
 })
@@ -87,10 +89,14 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 #define BT_INSN(insn, format, ...)                             \
 ({                                                             \
        if (opts.verbose || opts.backtrace) {                   \
-               struct instruction *_insn = (insn);             \
-               char *_str = offstr(_insn->sec, _insn->offset); \
-               WARN("  %s: " format, _str, ##__VA_ARGS__);     \
-               free(_str);                                     \
+               struct instruction *__insn = (insn);            \
+               char *_str = offstr(__insn->sec, __insn->offset); \
+               const char *_istr = objtool_disas_insn(__insn); \
+               int _len;                                       \
+               _len = snprintf(NULL, 0, "  %s: " format,  _str, ##__VA_ARGS__);        \
+               _len = (_len < 50) ? 50 - _len : 0;             \
+               WARN("  %s: " format "  %*s%s", _str, ##__VA_ARGS__, _len, "", _istr); \
+               free(_str);                                             \
        }                                                       \
 })