public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH v6 00/30] objtool: Function validation tracing
@ 2025-11-21  9:53 Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 01/30] objtool: Move disassembly functions to a separated file Alexandre Chartre
                   ` (31 more replies)
  0 siblings, 32 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Hi,

These patches change objtool to disassemble code with libopcodes instead
of running objdump. You will find below:

- Changes: list of changes made in this version
- Overview: overview of the changes
- Notes: description of some particular behavior
- Examples: output examples

Patches are now based on tip/master.

I am deferring the following changes to future patches:
- Josh: convert --disas option to subcommand
- David: provide branch distance for small branches

Thanks,

alex.

-----

Changes:
========

V6:
---
- Josh: fix commit comments (PATCH 22 and 23)
- Josh: update .gitignore (PATCH 03 and 26)
- clean feature discovery (PATCH 03)

V5: 
---
- patches are now based on tip/master
- remove the resolution of direct/PV calls
  (added in V4 but this needs more work)
- Josh: fix rname
- Josh: change names for jump tables and exception tables
- Josh: display header line for single-line alternatives
- Josh: make NOP<n> lowercase
- Josh: fix alternatives order
- Josh: trim trailing NOPs
- David: indicate the number of trailing NOPs (nop*<n>)
- David: provide compact output for alternatives disassembly.
  The compact output is now the default, and there is a --wide
  option to provide a wide output where alternatives are displayed
  side-by-side.

V4:
---
This version fixes a build issue when disassembly is not available. Compared
with V3, this is addresses by changes in patch 14 (objtool: Improve tracing
of alternative instructions). Other patches are similar to V3.

V3:
---
This version addresses comments from Josh and Peter, in particular:

- Josh: replace ERROR in disas_context_create with WARN
- Josh: do not change offstr() outside the disassembler
- Josh: duplicated "falls through to next function" warning
- Josh: validate_symbol() has extra newline before return
- Josh: "make -s" should be completely silent
- Josh: instructions with unwinding state changes are printing twice
- Josh: explain TRACE_INSN(insn, NULL): this prints an instruction with no
  	additional message.

- Peter: display alternative on a single line
- Peter: display nop-like instruction as NOP<n>
- Peter: in alternative show differences between jmp.d8 and jmp.d32 (jmp/jmpq is used now)
- Peter: show alternative feature name and flags
- Peter: alternative jumps to altinstr_aux - see NOTE below:
         Disassembly can show default alternative jumping to .altinstr_aux
- Peter: some jump label target seems wrong (jmp +0) - NOTE below:
         Disassembly can show alternative jumping to the next instruction

Other improvements:

- An alternatives is displayed on single line if each alternative has a
  single instruction. Otherwise alternatives are dispayed side-by-side,
  with one column for each lternative. XXX option?

- Each alternative of a group alternative is displayed with its feature
  name and flags: <flags><feature-name>

  <flags> is made of the following characters:

    '!' : ALT_FLAG_NOT
    '+' : ALT_FLAG_DIRECT_CALL
    '?' : unknown flag (i.e. any other flags)

- A jump table is displayed the same way as an alternative, with the default
  branch (or not) instruction, and the corresponding substitute instruction.
  It is identified with the "JUMP" name.

- An exception table is displayed the same way as an alternative, with the
  default instruction (which can cause an exception), and a "resume at <desc>"
  string which indicates where the execution resumes if there is an exception.
  It is identified with the "EXCEPTION" name.

- An exception table can be present for an instruction which also has an
  alternative. In that case, the exception table is displayed similarly
  as the different group alternatives for this instruction.

- Print the destination name of pv_ops calls  when we can figure out if
  XENPV mode is used or not. If the PV mode can't be predicted then print
  the default pv_ops destination as a destination example. **REMOVED IN V5**

- If a group alternative is a direct call then print the corresponding
  pv_ops call. **REMOVED IN V5**


Overview:
=========

This provides the following 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.

- Add the --disas <function> option to disassemble functions

  Disassembly is done using libopcodes and it will show the different code
  alternatives.

Note: some changes are architecture specific (x86, powerpc, loongarch). Any
feedback about the behavior on powerpc and loongarch is welcome.


Notes:
======

Disassembly can show default alternative jumping to .altinstr_aux
-----------------------------------------------------------------
Disassembly can show a default alternative jumping to .altinstr_aux. This
happens when the _static_cpu_has() function is used. Its default code
jumps to .altinstr_aux where a test sequence is executed (test; jnz; jmp).

At runtime, this sequence is not used because the _static_cpu_has() 
an alternative with the X86_FEATURE_ALWAYS feature. 


  debc:  perf_get_x86_pmu_capability+0xc      jmpq   0xdec1 <.altinstr_aux+0xfc> | NOP5  (X86_FEATURE_HYBRID_CPU) | jmpq   0x61a <perf_get_x86_pmu_capability+0x37>  (X86_FEATURE_ALWAYS)   # <alternative.debc>
  dec1:  perf_get_x86_pmu_capability+0x11     ud2                                                       


Disassembly can show alternative jumping to the next instruction
----------------------------------------------------------------

The disassembly can show jump tables with an alternative which jumps
to the next instruction.

For example:

def9:  perf_get_x86_pmu_capability+0x49    NOP2 | jmp    defb <perf_get_x86_pmu_capability+0x4b>  (JUMP)   # <alternative.def9>
defb:  perf_get_x86_pmu_capability+0x4b	   mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>      

This disassembly is correct. These instructions come from:

        cap->num_counters_gp = x86_pmu_num_counters(NULL)):

which will end up executing this statement:

        if (static_branch_unlikely(&perf_is_hybrid) && NULL)
	        <do something>;

This statement is optimized to do nothing because the condition is
always false. But static_branch_unlikely() is implemented with a jump
table inside an "asm goto" statement, and "asm goto" statements are
not optimized.

So basically the code is optimized like this:

        if (static_branch_unlikely(&perf_is_hybrid))
	        ;

And this translates to the assembly code above.


Examples:
=========

Example 1 (--trace option): Trace the validation of the os_save() function
--------------------------------------------------------------------------

$ ./tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --orc --retpoline --rethunk --sls --static-call --uaccess --prefix=16 --link --trace os_xsave -v vmlinux.o
os_xsave: validation begin
 59be0:  os_xsave+0x0                  push   %r12                                          - state: cfa=rsp+16 r12=(cfa-16) stack_size=16 
 59be2:  os_xsave+0x2		       mov    0x0(%rip),%eax        # 0x59be8 <alternatives_patched>
 59be8:  os_xsave+0x8		       push   %rbp                                          - state: cfa=rsp+24 rbp=(cfa-24) stack_size=24 
 59be9:  os_xsave+0x9		       mov    %rdi,%rbp                                          
 59bec:  os_xsave+0xc		       push   %rbx					     - state: cfa=rsp+32 rbx=(cfa-32) stack_size=32 
 59bed:  os_xsave+0xd		       mov    0x8(%rdi),%rbx                                     
 59bf1:  os_xsave+0x11		       mov    %rbx,%r12                                          
 59bf4:  os_xsave+0x14		       shr    $0x20,%r12                                         
 59bf8:  os_xsave+0x18		       test   %eax,%eax                                          
 59bfa:  os_xsave+0x1a		       je     0x59c22 <os_xsave+0x42>                        - jump taken
 59c22:  os_xsave+0x42		       | ud2                                                     
 59c24:  os_xsave+0x44		       | jmp    0x59bfc <os_xsave+0x1c>                      - unconditional jump
 59bfc:  os_xsave+0x1c		       | | xor    %edx,%edx                                      
 59bfe:  os_xsave+0x1e		       | | mov    %rbx,%rsi                                      
 59c01:  os_xsave+0x21		       | | mov    %rbp,%rdi                                      
 59c04:  os_xsave+0x24		       | | callq  0x59c09 <xfd_validate_state>               - call
 59c09:  os_xsave+0x29		       | | mov    %ebx,%eax                                      
 59c0b:  os_xsave+0x2b		       | | mov    %r12d,%edx                                     
 	 			       | | / <alternative.59c0e> X86_FEATURE_XSAVEOPT
  1b29:  .altinstr_replacement+0x1b29  | | | xsaveopt64 0x40(%rbp)                               
 59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                    
 59c15:  os_xsave+0x35		       | | | test   %ebx,%ebx                                    
 59c17:  os_xsave+0x37		       | | | jne    0x59c26 <os_xsave+0x46>                  - jump taken
 59c26:  os_xsave+0x46		       | | | | ud2                                               
 59c28:  os_xsave+0x48		       | | | | pop    %rbx                                   - state: cfa=rsp+24 rbx=<undef> stack_size=24 
 59c29:  os_xsave+0x49		       | | | | pop    %rbp				     - state: cfa=rsp+16 rbp=<undef> stack_size=16 
 59c2a:  os_xsave+0x4a		       | | | | pop    %r12				     - state: cfa=rsp+8 r12=<undef> stack_size=8 
 59c2c:  os_xsave+0x4c		       | | | | jmpq   0x59c31 <__x86_return_thunk>	     - return
 59c17:  os_xsave+0x37		       | | | jne    0x59c26 <os_xsave+0x46>		     - jump not taken
 59c19:  os_xsave+0x39		       | | | pop    %rbx    				     - state: cfa=rsp+24 rbx=<undef> stack_size=24 
 59c1a:  os_xsave+0x3a		       | | | pop    %rbp				     - state: cfa=rsp+16 rbp=<undef> stack_size=16 
 59c1b:  os_xsave+0x3b		       | | | pop    %r12				     - state: cfa=rsp+8 r12=<undef> stack_size=8 
 59c1d:  os_xsave+0x3d		       | | | jmpq   0x59c22 <__x86_return_thunk>	     - return
 	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVEOPT
				       | | / <alternative.59c0e> X86_FEATURE_XSAVEC
  1b2e:  .altinstr_replacement+0x1b2e  | | | xsavec64 0x40(%rbp)                                 
 59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                - already visited
 	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVEC
				       | | / <alternative.59c0e> X86_FEATURE_XSAVES
  1b33:  .altinstr_replacement+0x1b33  | | | xsaves64 0x40(%rbp)                                 
 59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                - already visited
 	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVES
				       | | / <alternative.59c0e> EXCEPTION for instruction at 0x59c0e <os_xsave+0x2e>
 59c15:  os_xsave+0x35		       | | | test   %ebx,%ebx                                - already visited
 	 			       | | \ <alternative.59c0e> EXCEPTION
				       | | / <alternative.59c0e> DEFAULT
 59c0e:  os_xsave+0x2e		       | | xsave64 0x40(%rbp)                                    
 59c13:  os_xsave+0x33		       | | xor    %ebx,%ebx                                  - already visited
 59bfa:  os_xsave+0x1a		       je     0x59c22 <os_xsave+0x42>                        - jump not taken
 59bfc:  os_xsave+0x1c		       xor    %edx,%edx                                      - already visited
os_xsave: validation end


Example 2 (--disas option): Single Instruction Alternatives
-----------------------------------------------------------

Compact Output (default):

Alternatives with single instructions are displayed each on one line,
with the instruction and a description of the alternative.

$ ./tools/objtool/objtool --disas=perf_get_x86_pmu_capability --link vmlinux.o
perf_get_x86_pmu_capability:
  deb0:  perf_get_x86_pmu_capability+0x0     endbr64                                                   
  deb4:  perf_get_x86_pmu_capability+0x4     callq  0xdeb9 <__fentry__>                                
  deb9:  perf_get_x86_pmu_capability+0x9     mov    %rdi,%rdx                                          
  debc:  perf_get_x86_pmu_capability+0xc     <alternative.debc>
  	 				     = jmpq   0xdec1 <.altinstr_aux+0xfc>                 (if DEFAULT)
					     = jmpq   0x622 <perf_get_x86_pmu_capability+0x37>    (if X86_FEATURE_ALWAYS)
					     = nop5                                               (if X86_FEATURE_HYBRID_CPU)
  dec1:  perf_get_x86_pmu_capability+0x11    ud2                                                       
  dec3:  perf_get_x86_pmu_capability+0x13    movq   $0x0,(%rdx)                                        
  deca:  perf_get_x86_pmu_capability+0x1a    movq   $0x0,0x8(%rdx)                                     
  ded2:  perf_get_x86_pmu_capability+0x22    movq   $0x0,0x10(%rdx)                                    
  deda:  perf_get_x86_pmu_capability+0x2a    movq   $0x0,0x18(%rdx)                                    
  dee2:  perf_get_x86_pmu_capability+0x32    jmpq   0xdee7 <__x86_return_thunk>                        
  dee7:  perf_get_x86_pmu_capability+0x37    cmpq   $0x0,0x0(%rip)        # 0xdeef <x86_pmu+0x10>      
  deef:  perf_get_x86_pmu_capability+0x3f    je     0xdec3 <perf_get_x86_pmu_capability+0x13>          
  def1:  perf_get_x86_pmu_capability+0x41    mov    0x0(%rip),%eax        # 0xdef7 <x86_pmu+0x8>       
  def7:  perf_get_x86_pmu_capability+0x47    mov    %eax,(%rdi)                                        
  def9:  perf_get_x86_pmu_capability+0x49    <jump_table.def9>
  	 				     = nop2                                              (if DEFAULT)
					     = jmp    defb <perf_get_x86_pmu_capability+0x4b>    (if JUMP)
  defb:  perf_get_x86_pmu_capability+0x4b    mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>      
  df02:  perf_get_x86_pmu_capability+0x52    <alternative.df02>
  	 				     = callq  0xdf07 <__sw_hweight64>    (if DEFAULT)
					     = popcnt %rdi,%rax                  (if X86_FEATURE_POPCNT)
  df07:  perf_get_x86_pmu_capability+0x57    mov    %eax,0x4(%rdx)                                     
  df0a:  perf_get_x86_pmu_capability+0x5a    <jump_table.df0a>
  	 				     = nop2                                              (if DEFAULT)
					     = jmp    df0c <perf_get_x86_pmu_capability+0x5c>    (if JUMP)
  df0c:  perf_get_x86_pmu_capability+0x5c    mov    0x0(%rip),%rdi        # 0xdf13 <x86_pmu+0xe0>      
  df13:  perf_get_x86_pmu_capability+0x63    <alternative.df13>
  	 				     = callq  0xdf18 <__sw_hweight64>    (if DEFAULT)
					     = popcnt %rdi,%rax                  (if X86_FEATURE_POPCNT)
  df18:  perf_get_x86_pmu_capability+0x68    mov    %eax,0x8(%rdx)                                     
  df1b:  perf_get_x86_pmu_capability+0x6b    mov    0x0(%rip),%eax        # 0xdf21 <x86_pmu+0xf8>      
  df21:  perf_get_x86_pmu_capability+0x71    mov    %eax,0xc(%rdx)                                     
  df24:  perf_get_x86_pmu_capability+0x74    mov    %eax,0x10(%rdx)                                    
  df27:  perf_get_x86_pmu_capability+0x77    mov    0x0(%rip),%rax        # 0xdf2e <x86_pmu+0x108>     
  df2e:  perf_get_x86_pmu_capability+0x7e    mov    %eax,0x14(%rdx)                                    
  df31:  perf_get_x86_pmu_capability+0x81    mov    0x0(%rip),%eax        # 0xdf37 <x86_pmu+0x110>     
  df37:  perf_get_x86_pmu_capability+0x87    mov    %eax,0x18(%rdx)                                    
  df3a:  perf_get_x86_pmu_capability+0x8a    movzbl 0x0(%rip),%ecx        # 0xdf41 <x86_pmu+0x1d1>     
  df41:  perf_get_x86_pmu_capability+0x91    movzbl 0x1c(%rdx),%eax                                    
  df45:  perf_get_x86_pmu_capability+0x95    shr    %cl                                                
  df47:  perf_get_x86_pmu_capability+0x97    and    $0x1,%ecx                                          
  df4a:  perf_get_x86_pmu_capability+0x9a    and    $0xfffffffe,%eax                                   
  df4d:  perf_get_x86_pmu_capability+0x9d    or     %ecx,%eax                                          
  df4f:  perf_get_x86_pmu_capability+0x9f    mov    %al,0x1c(%rdx)                                     
  df52:  perf_get_x86_pmu_capability+0xa2    jmpq   0xdf57 <__x86_return_thunk>                        


Wide Output (--wide option):

Alternatives with single instructions are displayed side-by-side,
with an header.

$ ./tools/objtool/objtool --disas=perf_get_x86_pmu_capability --wide --link vmlinux.o
perf_get_x86_pmu_capability:
  deb0:  perf_get_x86_pmu_capability+0x0       endbr64                                                   
  deb4:  perf_get_x86_pmu_capability+0x4       callq  0xdeb9 <__fentry__>                                
  deb9:  perf_get_x86_pmu_capability+0x9       mov    %rdi,%rdx                                          
  debc:  perf_get_x86_pmu_capability+0xc     | <alternative.debc>                 | X86_FEATURE_ALWAYS                              | X86_FEATURE_HYBRID_CPU 
  debc:  perf_get_x86_pmu_capability+0xc     | jmpq   0xdec1 <.altinstr_aux+0xfc> | jmpq   0x622 <perf_get_x86_pmu_capability+0x37> | nop5                   
  dec1:  perf_get_x86_pmu_capability+0x11      ud2                                                       
  dec3:  perf_get_x86_pmu_capability+0x13      movq   $0x0,(%rdx)                                        
  deca:  perf_get_x86_pmu_capability+0x1a      movq   $0x0,0x8(%rdx)                                     
  ded2:  perf_get_x86_pmu_capability+0x22      movq   $0x0,0x10(%rdx)                                    
  deda:  perf_get_x86_pmu_capability+0x2a      movq   $0x0,0x18(%rdx)                                    
  dee2:  perf_get_x86_pmu_capability+0x32      jmpq   0xdee7 <__x86_return_thunk>                        
  dee7:  perf_get_x86_pmu_capability+0x37      cmpq   $0x0,0x0(%rip)        # 0xdeef <x86_pmu+0x10>      
  deef:  perf_get_x86_pmu_capability+0x3f      je     0xdec3 <perf_get_x86_pmu_capability+0x13>          
  def1:  perf_get_x86_pmu_capability+0x41      mov    0x0(%rip),%eax        # 0xdef7 <x86_pmu+0x8>       
  def7:  perf_get_x86_pmu_capability+0x47      mov    %eax,(%rdi)                                        
  def9:  perf_get_x86_pmu_capability+0x49    | <jump_table.def9> | JUMP                                           
  def9:  perf_get_x86_pmu_capability+0x49    | nop2              | jmp    defb <perf_get_x86_pmu_capability+0x4b> 
  defb:  perf_get_x86_pmu_capability+0x4b      mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>      
  df02:  perf_get_x86_pmu_capability+0x52    | <alternative.df02>             | X86_FEATURE_POPCNT 
  df02:  perf_get_x86_pmu_capability+0x52    | callq  0xdf07 <__sw_hweight64> | popcnt %rdi,%rax   
  df07:  perf_get_x86_pmu_capability+0x57      mov    %eax,0x4(%rdx)                                     
  df0a:  perf_get_x86_pmu_capability+0x5a    | <jump_table.df0a> | JUMP                                           
  df0a:  perf_get_x86_pmu_capability+0x5a    | nop2              | jmp    df0c <perf_get_x86_pmu_capability+0x5c> 
  df0c:  perf_get_x86_pmu_capability+0x5c      mov    0x0(%rip),%rdi        # 0xdf13 <x86_pmu+0xe0>      
  df13:  perf_get_x86_pmu_capability+0x63    | <alternative.df13>             | X86_FEATURE_POPCNT 
  df13:  perf_get_x86_pmu_capability+0x63    | callq  0xdf18 <__sw_hweight64> | popcnt %rdi,%rax   
  df18:  perf_get_x86_pmu_capability+0x68      mov    %eax,0x8(%rdx)                                     
  df1b:  perf_get_x86_pmu_capability+0x6b      mov    0x0(%rip),%eax        # 0xdf21 <x86_pmu+0xf8>      
  df21:  perf_get_x86_pmu_capability+0x71      mov    %eax,0xc(%rdx)                                     
  df24:  perf_get_x86_pmu_capability+0x74      mov    %eax,0x10(%rdx)                                    
  df27:  perf_get_x86_pmu_capability+0x77      mov    0x0(%rip),%rax        # 0xdf2e <x86_pmu+0x108>     
  df2e:  perf_get_x86_pmu_capability+0x7e      mov    %eax,0x14(%rdx)                                    
  df31:  perf_get_x86_pmu_capability+0x81      mov    0x0(%rip),%eax        # 0xdf37 <x86_pmu+0x110>     
  df37:  perf_get_x86_pmu_capability+0x87      mov    %eax,0x18(%rdx)                                    
  df3a:  perf_get_x86_pmu_capability+0x8a      movzbl 0x0(%rip),%ecx        # 0xdf41 <x86_pmu+0x1d1>     
  df41:  perf_get_x86_pmu_capability+0x91      movzbl 0x1c(%rdx),%eax                                    
  df45:  perf_get_x86_pmu_capability+0x95      shr    %cl                                                
  df47:  perf_get_x86_pmu_capability+0x97      and    $0x1,%ecx                                          
  df4a:  perf_get_x86_pmu_capability+0x9a      and    $0xfffffffe,%eax                                   
  df4d:  perf_get_x86_pmu_capability+0x9d      or     %ecx,%eax                                          
  df4f:  perf_get_x86_pmu_capability+0x9f      mov    %al,0x1c(%rdx)                                     
  df52:  perf_get_x86_pmu_capability+0xa2      jmpq   0xdf57 <__x86_return_thunk>                        


Example 3 (--disas option): Alternatives with multiple instructions
-------------------------------------------------------------------

Compact Output (default):

Alternatives with multiple instructions are displayed one above the other,
with an header describing the alternative.

$ ./tools/objtool/objtool --disas=__switch_to_asm --link vmlinux.o
__switch_to_asm:
  82c0:  __switch_to_asm+0x0       push   %rbp                                               
  82c1:  __switch_to_asm+0x1	   push   %rbx                                               
  82c2:  __switch_to_asm+0x2	   push   %r12                                               
  82c4:  __switch_to_asm+0x4	   push   %r13                                               
  82c6:  __switch_to_asm+0x6	   push   %r14                                               
  82c8:  __switch_to_asm+0x8	   push   %r15                                               
  82ca:  __switch_to_asm+0xa	   mov    %rsp,0x1670(%rdi)                                  
  82d1:  __switch_to_asm+0x11	   mov    0x1670(%rsi),%rsp                                  
  82d8:  __switch_to_asm+0x18	   mov    0xad8(%rsi),%rbx                                   
  82df:  __switch_to_asm+0x1f	   mov    %rbx,%gs:0x0(%rip)        # 0x82e7 <__stack_chk_guard>
  82e7:  __switch_to_asm+0x27	   <alternative.82e7>
  	 			   = DEFAULT
  82e7:  __switch_to_asm+0x27	   | jmp    0x8312 <__switch_to_asm+0x52>
  82e9:  __switch_to_asm+0x29	   | nop*41
  	 			   |
				   = X86_FEATURE_RSB_CTXSW
  82e7:  __switch_to_asm+0x27	   | mov    $0x10,%r12
  82ee:  __switch_to_asm+0x2e	   | callq  0x82f4 <__switch_to_asm+0x34>
  82f3:  __switch_to_asm+0x33	   | int3   
  82f4:  __switch_to_asm+0x34	   | callq  0x82fa <__switch_to_asm+0x3a>
  82f9:  __switch_to_asm+0x39	   | int3   
  82fa:  __switch_to_asm+0x3a	   | add    $0x10,%rsp
  82fe:  __switch_to_asm+0x3e	   | dec    %r12
  8301:  __switch_to_asm+0x41	   | jne    0x82ee <__switch_to_asm+0x2e>
  8303:  __switch_to_asm+0x43	   | lfence 
  8306:  __switch_to_asm+0x46	   | movq   $0xffffffffffffffff,%gs:0x0(%rip)        # 0x20b <__x86_call_depth>
  	 			   |
				   = !X86_FEATURE_ALWAYS
  82e7:  __switch_to_asm+0x27	   | nop1
  82e8:  __switch_to_asm+0x28	   | nop1
  82e9:  __switch_to_asm+0x29	   | callq  0x82ef <__switch_to_asm+0x2f>
  82ee:  __switch_to_asm+0x2e	   | int3   
  82ef:  __switch_to_asm+0x2f	   | add    $0x8,%rsp
  82f3:  __switch_to_asm+0x33	   | lfence 
  	 			   |
  8312:  __switch_to_asm+0x52	   pop    %r15                                               
  8314:  __switch_to_asm+0x54	   pop    %r14                                               
  8316:  __switch_to_asm+0x56	   pop    %r13                                               
  8318:  __switch_to_asm+0x58	   pop    %r12                                               
  831a:  __switch_to_asm+0x5a	   pop    %rbx                                               
  831b:  __switch_to_asm+0x5b	   pop    %rbp                                               
  831c:  __switch_to_asm+0x5c	   jmpq   0x8321 <__switch_to>                               


Wide Output (--wide option):

Alternatives with multiple instructions are displayed side-by-side, with
an header describing the alternative. The code in the first column is the
default code of the alternative.

$ ./tools/objtool/objtool --disas=__switch_to_asm --wide  --link vmlinux.o
__switch_to_asm:
  82c0:  __switch_to_asm+0x0       push   %rbp                                               
  82c1:  __switch_to_asm+0x1	   push   %rbx                                               
  82c2:  __switch_to_asm+0x2	   push   %r12                                               
  82c4:  __switch_to_asm+0x4	   push   %r13                                               
  82c6:  __switch_to_asm+0x6	   push   %r14                                               
  82c8:  __switch_to_asm+0x8	   push   %r15                                               
  82ca:  __switch_to_asm+0xa	   mov    %rsp,0x1670(%rdi)                                  
  82d1:  __switch_to_asm+0x11	   mov    0x1670(%rsi),%rsp                                  
  82d8:  __switch_to_asm+0x18	   mov    0xad8(%rsi),%rbx                                   
  82df:  __switch_to_asm+0x1f	   mov    %rbx,%gs:0x0(%rip)        # 0x82e7 <__stack_chk_guard>
  82e7:  __switch_to_asm+0x27	 | <alternative.82e7>                   | X86_FEATURE_RSB_CTXSW                                               | !X86_FEATURE_ALWAYS
  82e7:  __switch_to_asm+0x27	 | jmp    0x8312 <__switch_to_asm+0x52> | mov    $0x10,%r12						      | nop1
  82e8:  __switch_to_asm+0x28	 |                                      | 	 							      | nop1
  82e9:  __switch_to_asm+0x29	 | nop*41                               |                                                                     | callq  0x82ef <__switch_to_asm+0x2f>
  82ee:  __switch_to_asm+0x2e	 |                                      | callq  0x82f4 <__switch_to_asm+0x34>                                | int3
  82ef:  __switch_to_asm+0x2f	 |                                      |                                                                     | add    $0x8,%rsp
  82f3:  __switch_to_asm+0x33	 |                                      | int3                                                                | lfence
  82f4:  __switch_to_asm+0x34	 |                                      | callq  0x82fa <__switch_to_asm+0x3a>                                |
  82f9:  __switch_to_asm+0x39	 |                                      | int3                                                                |
  82fa:  __switch_to_asm+0x3a	 |                                      | add    $0x10,%rsp                                                   |
  82fe:  __switch_to_asm+0x3e	 |                                      | dec    %r12                                                         |
  8301:  __switch_to_asm+0x41	 |                                      | jne    0x82ee <__switch_to_asm+0x2e>                                |
  8303:  __switch_to_asm+0x43	 |                                      | lfence                                                              |
  8306:  __switch_to_asm+0x46	 |                                      | movq   $0xffffffffffffffff,%gs:0x0(%rip) # 0x20b <__x86_call_depth> |
  8312:  __switch_to_asm+0x52	   pop    %r15                                               
  8314:  __switch_to_asm+0x54	   pop    %r14                                               
  8316:  __switch_to_asm+0x56	   pop    %r13                                               
  8318:  __switch_to_asm+0x58	   pop    %r12                                               
  831a:  __switch_to_asm+0x5a	   pop    %rbx                                               
  831b:  __switch_to_asm+0x5b	   pop    %rbp                                               
  831c:  __switch_to_asm+0x5c	   jmpq   0x8321 <__switch_to>
  
-----

Alexandre Chartre (30):
  objtool: Move disassembly functions to a separated file
  objtool: Create disassembly context
  objtool: Disassemble code with libopcodes instead of running objdump
  tool build: Remove annoying newline in build output
  objtool: Print symbol during disassembly
  objtool: Store instruction disassembly result
  objtool: Disassemble instruction on warning or backtrace
  objtool: Extract code to validate instruction from the validate branch
    loop
  objtool: Record symbol name max length
  objtool: Add option to trace function validation
  objtool: Trace instruction state changes during function validation
  objtool: Improve register reporting during function validation
  objtool: Identify the different types of alternatives
  objtool: Add functions to better name alternatives
  objtool: Improve tracing of alternative instructions
  objtool: Do not validate IBT for .return_sites and .call_sites
  objtool: Add the --disas=<function-pattern> action
  objtool: Preserve alternatives order
  objtool: Print headers for alternatives
  objtool: Disassemble group alternatives
  objtool: Print addresses with alternative instructions
  objtool: Disassemble exception table alternatives
  objtool: Disassemble jump table alternatives
  objtool: Fix address references in alternatives
  objtool: Provide access to feature and flags of group alternatives
  objtool: Function to get the name of a CPU feature
  objtool: Improve naming of group alternatives
  objtool: Compact output for alternatives with one instruction
  objtool: Add wide output for disassembly
  objtool: Trim trailing NOPs in alternative

 .../x86/tools/gen-cpu-feature-names-x86.awk   |   33 +
 tools/build/Makefile.feature                  |    4 +-
 tools/objtool/.gitignore                      |    3 +
 tools/objtool/Build                           |    3 +
 tools/objtool/Makefile                        |   26 +
 tools/objtool/arch/loongarch/decode.c         |   23 +
 tools/objtool/arch/loongarch/special.c        |    5 +
 tools/objtool/arch/powerpc/decode.c           |   24 +
 tools/objtool/arch/powerpc/special.c          |    5 +
 tools/objtool/arch/x86/Build                  |    8 +
 tools/objtool/arch/x86/decode.c               |   20 +
 tools/objtool/arch/x86/special.c              |   10 +
 tools/objtool/builtin-check.c                 |    4 +
 tools/objtool/check.c                         |  648 +++++----
 tools/objtool/disas.c                         | 1245 +++++++++++++++++
 tools/objtool/include/objtool/arch.h          |   11 +
 tools/objtool/include/objtool/builtin.h       |    3 +
 tools/objtool/include/objtool/check.h         |   35 +-
 tools/objtool/include/objtool/disas.h         |   81 ++
 tools/objtool/include/objtool/special.h       |    4 +-
 tools/objtool/include/objtool/trace.h         |  141 ++
 tools/objtool/include/objtool/warn.h          |   17 +-
 tools/objtool/special.c                       |    2 +
 tools/objtool/trace.c                         |  203 +++
 24 files changed, 2259 insertions(+), 299 deletions(-)
 create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
 create mode 100644 tools/objtool/disas.c
 create mode 100644 tools/objtool/include/objtool/disas.h
 create mode 100644 tools/objtool/include/objtool/trace.h
 create mode 100644 tools/objtool/trace.c

-- 
2.43.5


^ permalink raw reply	[flat|nested] 110+ messages in thread

* [PATCH v6 01/30] objtool: Move disassembly functions to a separated file
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 02/30] objtool: Create disassembly context Alexandre Chartre
                   ` (30 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

objtool disassembles functions which have warnings. Move the code
to do that to a dedicated file. The code is just moved, it is not
changed.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/Build                     |  1 +
 tools/objtool/check.c                   | 81 ----------------------
 tools/objtool/disas.c                   | 90 +++++++++++++++++++++++++
 tools/objtool/include/objtool/objtool.h |  2 +
 4 files changed, 93 insertions(+), 81 deletions(-)
 create mode 100644 tools/objtool/disas.c

diff --git a/tools/objtool/Build b/tools/objtool/Build
index 8cd71b9a5eef1..17e50a1766d0e 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -7,6 +7,7 @@ objtool-y += special.o
 objtool-y += builtin-check.o
 objtool-y += elf.o
 objtool-y += objtool.o
+objtool-y += 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/check.c b/tools/objtool/check.c
index 72c7f6f03350b..6a5b06052dd22 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4783,87 +4783,6 @@ static int validate_reachable_instructions(struct objtool_file *file)
 	return warnings;
 }
 
-/* 'funcs' is a space-separated list of function names */
-static void disas_funcs(const char *funcs)
-{
-	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;
-	}
-
-	cmd = malloc(size);
-
-	/* real snprintf() */
-	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
-	ret = system(cmd);
-	if (ret) {
-		WARN("disassembly failed: %d", ret);
-		return;
-	}
-}
-
-static void disas_warned_funcs(struct objtool_file *file)
-{
-	struct symbol *sym;
-	char *funcs = NULL, *tmp;
-
-	for_each_sym(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 (funcs)
-		disas_funcs(funcs);
-}
-
 __weak bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc)
 {
 	unsigned int type = reloc_type(reloc);
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
new file mode 100644
index 0000000000000..3a7cb1b8002ec
--- /dev/null
+++ b/tools/objtool/disas.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ */
+
+#include <objtool/arch.h>
+#include <objtool/warn.h>
+
+#include <linux/string.h>
+
+/* 'funcs' is a space-separated list of function names */
+static void disas_funcs(const char *funcs)
+{
+	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;
+	}
+
+	cmd = malloc(size);
+
+	/* real snprintf() */
+	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
+	ret = system(cmd);
+	if (ret) {
+		WARN("disassembly failed: %d", ret);
+		return;
+	}
+}
+
+void disas_warned_funcs(struct objtool_file *file)
+{
+	struct symbol *sym;
+	char *funcs = NULL, *tmp;
+
+	for_each_sym(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 (funcs)
+		disas_funcs(funcs);
+}
diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h
index f7051bbe0bcb2..35f926cf9c254 100644
--- a/tools/objtool/include/objtool/objtool.h
+++ b/tools/objtool/include/objtool/objtool.h
@@ -49,4 +49,6 @@ int check(struct objtool_file *file);
 int orc_dump(const char *objname);
 int orc_create(struct objtool_file *file);
 
+void disas_warned_funcs(struct objtool_file *file);
+
 #endif /* _OBJTOOL_H */
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 02/30] objtool: Create disassembly context
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 01/30] objtool: Move disassembly functions to a separated file Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump Alexandre Chartre
                   ` (29 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Create a structure to store information for disassembling functions.
For now, it is just a wrapper around an objtool file.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c                   |  6 ++++-
 tools/objtool/disas.c                   | 32 +++++++++++++++++++++++--
 tools/objtool/include/objtool/disas.h   | 14 +++++++++++
 tools/objtool/include/objtool/objtool.h |  2 --
 4 files changed, 49 insertions(+), 5 deletions(-)
 create mode 100644 tools/objtool/include/objtool/disas.h

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 6a5b06052dd22..5083e47eef08d 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -12,6 +12,7 @@
 #include <objtool/builtin.h>
 #include <objtool/cfi.h>
 #include <objtool/arch.h>
+#include <objtool/disas.h>
 #include <objtool/check.h>
 #include <objtool/special.h>
 #include <objtool/warn.h>
@@ -4854,6 +4855,7 @@ static void free_insns(struct objtool_file *file)
 
 int check(struct objtool_file *file)
 {
+	struct disas_context *disas_ctx;
 	int ret = 0, warnings = 0;
 
 	arch_initial_func_cfi_state(&initial_func_cfi);
@@ -4997,7 +4999,9 @@ int check(struct objtool_file *file)
 	if (opts.verbose) {
 		if (opts.werror && warnings)
 			WARN("%d warning(s) upgraded to errors", warnings);
-		disas_warned_funcs(file);
+		disas_ctx = disas_context_create(file);
+		disas_warned_funcs(disas_ctx);
+		disas_context_destroy(disas_ctx);
 	}
 
 	if (opts.backup && make_backup())
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 3a7cb1b8002ec..7a18e51d43e62 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -4,10 +4,35 @@
  */
 
 #include <objtool/arch.h>
+#include <objtool/disas.h>
 #include <objtool/warn.h>
 
 #include <linux/string.h>
 
+struct disas_context {
+	struct objtool_file *file;
+};
+
+struct disas_context *disas_context_create(struct objtool_file *file)
+{
+	struct disas_context *dctx;
+
+	dctx = malloc(sizeof(*dctx));
+	if (!dctx) {
+		WARN("failed to allocate disassembly context");
+		return NULL;
+	}
+
+	dctx->file = file;
+
+	return dctx;
+}
+
+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)
 {
@@ -58,12 +83,15 @@ static void disas_funcs(const char *funcs)
 	}
 }
 
-void disas_warned_funcs(struct objtool_file *file)
+void disas_warned_funcs(struct disas_context *dctx)
 {
 	struct symbol *sym;
 	char *funcs = NULL, *tmp;
 
-	for_each_sym(file->elf, sym) {
+	if (!dctx)
+		return;
+
+	for_each_sym(dctx->file->elf, sym) {
 		if (sym->warned) {
 			if (!funcs) {
 				funcs = malloc(strlen(sym->name) + 1);
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
new file mode 100644
index 0000000000000..5c543b69fc612
--- /dev/null
+++ b/tools/objtool/include/objtool/disas.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#ifndef _DISAS_H
+#define _DISAS_H
+
+struct disas_context;
+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);
+
+#endif /* _DISAS_H */
diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h
index 35f926cf9c254..f7051bbe0bcb2 100644
--- a/tools/objtool/include/objtool/objtool.h
+++ b/tools/objtool/include/objtool/objtool.h
@@ -49,6 +49,4 @@ int check(struct objtool_file *file);
 int orc_dump(const char *objname);
 int orc_create(struct objtool_file *file);
 
-void disas_warned_funcs(struct objtool_file *file);
-
 #endif /* _OBJTOOL_H */
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 01/30] objtool: Move disassembly functions to a separated file Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 02/30] objtool: Create disassembly context Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
                     ` (2 more replies)
  2025-11-21  9:53 ` [PATCH v6 04/30] tool build: Remove annoying newline in build output Alexandre Chartre
                   ` (28 subsequent siblings)
  31 siblings, 3 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

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


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 04/30] tool build: Remove annoying newline in build output
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (2 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 05/30] objtool: Print symbol during disassembly Alexandre Chartre
                   ` (27 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Remove the newline which is printed during feature discovery
when nothing else is printed.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/build/Makefile.feature | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 32bbe29fe5f6c..300a329bc581d 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -315,5 +315,7 @@ endef
 
 ifeq ($(FEATURE_DISPLAY_DEFERRED),)
   $(call feature_display_entries)
-  $(info )
+  ifeq ($(feature_display),1)
+    $(info )
+  endif
 endif
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 05/30] objtool: Print symbol during disassembly
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (3 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 04/30] tool build: Remove annoying newline in build output Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 06/30] objtool: Store instruction disassembly result Alexandre Chartre
                   ` (26 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Print symbols referenced during disassembly instead of just printing
raw addresses. Also handle address relocation.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c                 |   9 --
 tools/objtool/disas.c                 | 134 ++++++++++++++++++++++++++
 tools/objtool/include/objtool/check.h |   9 ++
 3 files changed, 143 insertions(+), 9 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index de156e91ee8b7..8937667f075df 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -134,15 +134,6 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
 	for (insn = next_insn_same_sec(file, insn); insn;		\
 	     insn = next_insn_same_sec(file, insn))
 
-static inline struct symbol *insn_call_dest(struct instruction *insn)
-{
-	if (insn->type == INSN_JUMP_DYNAMIC ||
-	    insn->type == INSN_CALL_DYNAMIC)
-		return NULL;
-
-	return insn->_call_dest;
-}
-
 static inline struct reloc *insn_jump_table(struct instruction *insn)
 {
 	if (insn->type == INSN_JUMP_DYNAMIC ||
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 11ac2ec04afc1..dee10ab86fa29 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -14,13 +14,144 @@
 
 struct disas_context {
 	struct objtool_file *file;
+	struct instruction *insn;
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
 };
 
+static int sprint_name(char *str, const char *name, unsigned long offset)
+{
+	int len;
+
+	if (offset)
+		len = sprintf(str, "%s+0x%lx", name, offset);
+	else
+		len = sprintf(str, "%s", name);
+
+	return len;
+}
+
 #define DINFO_FPRINTF(dinfo, ...)	\
 	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
 
+static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
+				 bfd_vma addr, struct disassemble_info *dinfo)
+{
+	char symstr[1024];
+	char *str;
+
+	if (sym) {
+		sprint_name(symstr, sym->name, addr - sym->offset);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
+	} else {
+		str = offstr(sec, addr);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
+		free(str);
+	}
+}
+
+static void disas_print_addr_noreloc(bfd_vma addr,
+				     struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *insn = dctx->insn;
+	struct symbol *sym = NULL;
+
+	if (insn->sym && addr >= insn->sym->offset &&
+	    addr < insn->sym->offset + insn->sym->len) {
+		sym = insn->sym;
+	}
+
+	disas_print_addr_sym(insn->sec, sym, addr, dinfo);
+}
+
+static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *insn = dctx->insn;
+	unsigned long offset;
+	struct reloc *reloc;
+	char symstr[1024];
+	char *str;
+
+	reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec,
+					 insn->offset, insn->len);
+	if (!reloc) {
+		/*
+		 * There is no relocation for this instruction although
+		 * the address to resolve points to the next instruction.
+		 * So this is an effective reference to the next IP, for
+		 * example: "lea 0x0(%rip),%rdi". The kernel can reference
+		 * the next IP with _THIS_IP_ macro.
+		 */
+		DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr);
+		return;
+	}
+
+	offset = arch_insn_adjusted_addend(insn, reloc);
+
+	/*
+	 * If the relocation symbol is a section name (for example ".bss")
+	 * then we try to further resolve the name.
+	 */
+	if (reloc->sym->type == STT_SECTION) {
+		str = offstr(reloc->sym->sec, reloc->sym->offset + offset);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
+		free(str);
+	} else {
+		sprint_name(symstr, reloc->sym->name, offset);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
+	}
+}
+
+/*
+ * Resolve an address into a "<symbol>+<offset>" string.
+ */
+static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *insn = dctx->insn;
+	struct instruction *jump_dest;
+	struct symbol *sym;
+	bool is_reloc;
+
+	/*
+	 * If the instruction is a call/jump and it references a
+	 * destination then this is likely the address we are looking
+	 * up. So check it first.
+	 */
+	jump_dest = insn->jump_dest;
+	if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
+		disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
+				     addr, dinfo);
+		return;
+	}
+
+	/*
+	 * If the address points to the next instruction then there is
+	 * probably a relocation. It can be a false positive when the
+	 * current instruction is referencing the address of the next
+	 * instruction. This particular case will be handled in
+	 * disas_print_addr_reloc().
+	 */
+	is_reloc = (addr == insn->offset + insn->len);
+
+	/*
+	 * The call destination offset can be the address we are looking
+	 * up, or 0 if there is a relocation.
+	 */
+	sym = insn_call_dest(insn);
+	if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) {
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name);
+		return;
+	}
+
+	if (!is_reloc)
+		disas_print_addr_noreloc(addr, dinfo);
+	else
+		disas_print_addr_reloc(addr, dinfo);
+}
+
 /*
  * Initialize disassemble info arch, mach (32 or 64-bit) and options.
  */
@@ -69,6 +200,7 @@ struct disas_context *disas_context_create(struct objtool_file *file)
 				     fprintf_styled);
 
 	dinfo->read_memory_func = buffer_read_memory;
+	dinfo->print_address_func = disas_print_address;
 	dinfo->application_data = dctx;
 
 	/*
@@ -121,6 +253,8 @@ static size_t disas_insn(struct disas_context *dctx,
 	disassembler_ftype disasm = dctx->disassembler;
 	struct disassemble_info *dinfo = &dctx->info;
 
+	dctx->insn = insn;
+
 	if (insn->type == INSN_NOP) {
 		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
 		return insn->len;
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index 674f57466d125..ad9c73504b120 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -117,6 +117,15 @@ static inline bool is_jump(struct instruction *insn)
 	return is_static_jump(insn) || is_dynamic_jump(insn);
 }
 
+static inline struct symbol *insn_call_dest(struct instruction *insn)
+{
+	if (insn->type == INSN_JUMP_DYNAMIC ||
+	    insn->type == INSN_CALL_DYNAMIC)
+		return NULL;
+
+	return insn->_call_dest;
+}
+
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset);
 
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 06/30] objtool: Store instruction disassembly result
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (4 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 05/30] objtool: Print symbol during disassembly Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 07/30] objtool: Disassemble instruction on warning or backtrace Alexandre Chartre
                   ` (25 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When disassembling an instruction store the result instead of directly
printing it.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 77 +++++++++++++++++++++++++++++++++++++++----
 1 file changed, 71 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index dee10ab86fa29..89daa121b40b0 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -12,9 +12,16 @@
 #include <linux/string.h>
 #include <tools/dis-asm-compat.h>
 
+/*
+ * Size of the buffer for storing the result of disassembling
+ * a single instruction.
+ */
+#define DISAS_RESULT_SIZE	1024
+
 struct disas_context {
 	struct objtool_file *file;
 	struct instruction *insn;
+	char result[DISAS_RESULT_SIZE];
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
 };
@@ -34,6 +41,59 @@ static int sprint_name(char *str, const char *name, unsigned long offset)
 #define DINFO_FPRINTF(dinfo, ...)	\
 	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
 
+static int disas_result_fprintf(struct disas_context *dctx,
+				const char *fmt, va_list ap)
+{
+	char *buf = dctx->result;
+	int avail, len;
+
+	len = strlen(buf);
+	if (len >= DISAS_RESULT_SIZE - 1) {
+		WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
+			  "disassembly buffer is full");
+		return -1;
+	}
+	avail = DISAS_RESULT_SIZE - len;
+
+	len = vsnprintf(buf + len, avail, fmt, ap);
+	if (len < 0 || len >= avail) {
+		WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
+			  "disassembly buffer is truncated");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int disas_fprintf(void *stream, const char *fmt, ...)
+{
+	va_list arg;
+	int rv;
+
+	va_start(arg, fmt);
+	rv = disas_result_fprintf(stream, fmt, arg);
+	va_end(arg);
+
+	return rv;
+}
+
+/*
+ * For init_disassemble_info_compat().
+ */
+static int disas_fprintf_styled(void *stream,
+				enum disassembler_style style,
+				const char *fmt, ...)
+{
+	va_list arg;
+	int rv;
+
+	va_start(arg, fmt);
+	rv = disas_result_fprintf(stream, fmt, arg);
+	va_end(arg);
+
+	return rv;
+}
+
 static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
 				 bfd_vma addr, struct disassemble_info *dinfo)
 {
@@ -195,9 +255,8 @@ 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);
+	init_disassemble_info_compat(dinfo, dctx,
+				     disas_fprintf, disas_fprintf_styled);
 
 	dinfo->read_memory_func = buffer_read_memory;
 	dinfo->print_address_func = disas_print_address;
@@ -244,6 +303,11 @@ void disas_context_destroy(struct disas_context *dctx)
 	free(dctx);
 }
 
+static char *disas_result(struct disas_context *dctx)
+{
+	return dctx->result;
+}
+
 /*
  * Disassemble a single instruction. Return the size of the instruction.
  */
@@ -254,6 +318,7 @@ static size_t disas_insn(struct disas_context *dctx,
 	struct disassemble_info *dinfo = &dctx->info;
 
 	dctx->insn = insn;
+	dctx->result[0] = '\0';
 
 	if (insn->type == INSN_NOP) {
 		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
@@ -282,10 +347,10 @@ static void disas_func(struct disas_context *dctx, struct symbol *func)
 	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(" %6lx:  %s+0x%-6lx      %s\n",
+		       addr, func->name, addr - func->offset,
+		       disas_result(dctx));
 	}
 	printf("\n");
 }
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 07/30] objtool: Disassemble instruction on warning or backtrace
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (5 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 06/30] objtool: Store instruction disassembly result Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 08/30] objtool: Extract code to validate instruction from the validate branch loop Alexandre Chartre
                   ` (24 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

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>
---
 tools/objtool/check.c                 | 36 ++++++++++++++++++++++-----
 tools/objtool/disas.c                 |  5 ++--
 tools/objtool/include/objtool/check.h |  2 ++
 tools/objtool/include/objtool/disas.h | 13 ++++++++++
 tools/objtool/include/objtool/warn.h  | 16 ++++++++----
 5 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 8937667f075df..dd61c3242def5 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4844,11 +4844,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);
@@ -4990,11 +5013,12 @@ int check(struct objtool_file *file)
 	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);
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 89daa121b40b0..a030b06c121d2 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -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;
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index ad9c73504b120..f96aabd7d54dc 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -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 */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 3ec3ce2e4e6f0..1aee1fbe0bb97 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -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 */
diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h
index a1e3927d8e7ce..f32abc7b1be14 100644
--- a/tools/objtool/include/objtool/warn.h
+++ b/tools/objtool/include/objtool/warn.h
@@ -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);						\
 	}							\
 })
 
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 08/30] objtool: Extract code to validate instruction from the validate branch loop
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (6 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 07/30] objtool: Disassemble instruction on warning or backtrace Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 09/30] objtool: Record symbol name max length Alexandre Chartre
                   ` (23 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

The code to validate a branch loops through all instructions of the
branch and validate each instruction. Move the code to validate an
instruction to a separated function.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c | 386 ++++++++++++++++++++++--------------------
 1 file changed, 205 insertions(+), 181 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index dd61c3242def5..9c1888e21c1d8 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3706,253 +3706,277 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	checksum_update(func, insn, &offset, sizeof(offset));
 }
 
-/*
- * Follow the branch starting at the given instruction, and recursively follow
- * any other branches (jumps).  Meanwhile, track the frame pointer state at
- * each instruction and validate all the rules described in
- * tools/objtool/Documentation/objtool.txt.
- */
 static int validate_branch(struct objtool_file *file, struct symbol *func,
-			   struct instruction *insn, struct insn_state state)
+			   struct instruction *insn, struct insn_state state);
+
+static int validate_insn(struct objtool_file *file, struct symbol *func,
+			 struct instruction *insn, struct insn_state *statep,
+			 struct instruction *prev_insn, struct instruction *next_insn,
+			 bool *dead_end)
 {
 	struct alternative *alt;
-	struct instruction *next_insn, *prev_insn = NULL;
 	u8 visited;
 	int ret;
 
-	if (func && func->ignore)
-		return 0;
+	/*
+	 * Any returns before the end of this function are effectively dead
+	 * ends, i.e. validate_branch() has reached the end of the branch.
+	 */
+	*dead_end = true;
 
-	while (1) {
-		next_insn = next_insn_to_validate(file, insn);
+	visited = VISITED_BRANCH << statep->uaccess;
+	if (insn->visited & VISITED_BRANCH_MASK) {
+		if (!insn->hint && !insn_cfi_match(insn, &statep->cfi))
+			return 1;
 
-		if (opts.checksum && func && insn->sec)
-			checksum_update_insn(file, func, insn);
+		if (insn->visited & visited)
+			return 0;
+	} else {
+		nr_insns_visited++;
+	}
 
-		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
-			/* Ignore KCFI type preambles, which always fall through */
-			if (is_prefix_func(func))
-				return 0;
+	if (statep->noinstr)
+		statep->instr += insn->instr;
 
-			if (file->ignore_unreachables)
-				return 0;
+	if (insn->hint) {
+		if (insn->restore) {
+			struct instruction *save_insn, *i;
 
-			WARN("%s() falls through to next function %s()",
-			     func->name, insn_func(insn)->name);
-			func->warned = 1;
+			i = insn;
+			save_insn = NULL;
 
-			return 1;
-		}
+			sym_for_each_insn_continue_reverse(file, func, i) {
+				if (i->save) {
+					save_insn = i;
+					break;
+				}
+			}
 
-		visited = VISITED_BRANCH << state.uaccess;
-		if (insn->visited & VISITED_BRANCH_MASK) {
-			if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
+			if (!save_insn) {
+				WARN_INSN(insn, "no corresponding CFI save for CFI restore");
 				return 1;
+			}
 
-			if (insn->visited & visited)
-				return 0;
-		} else {
-			nr_insns_visited++;
+			if (!save_insn->visited) {
+				/*
+				 * If the restore hint insn is at the
+				 * beginning of a basic block and was
+				 * branched to from elsewhere, and the
+				 * save insn hasn't been visited yet,
+				 * defer following this branch for now.
+				 * It will be seen later via the
+				 * straight-line path.
+				 */
+				if (!prev_insn)
+					return 0;
+
+				WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
+				return 1;
+			}
+
+			insn->cfi = save_insn->cfi;
+			nr_cfi_reused++;
 		}
 
-		if (state.noinstr)
-			state.instr += insn->instr;
+		statep->cfi = *insn->cfi;
+	} else {
+		/* XXX track if we actually changed statep->cfi */
 
-		if (insn->hint) {
-			if (insn->restore) {
-				struct instruction *save_insn, *i;
+		if (prev_insn && !cficmp(prev_insn->cfi, &statep->cfi)) {
+			insn->cfi = prev_insn->cfi;
+			nr_cfi_reused++;
+		} else {
+			insn->cfi = cfi_hash_find_or_add(&statep->cfi);
+		}
+	}
 
-				i = insn;
-				save_insn = NULL;
+	insn->visited |= visited;
 
-				sym_for_each_insn_continue_reverse(file, func, i) {
-					if (i->save) {
-						save_insn = i;
-						break;
-					}
-				}
+	if (propagate_alt_cfi(file, insn))
+		return 1;
 
-				if (!save_insn) {
-					WARN_INSN(insn, "no corresponding CFI save for CFI restore");
-					return 1;
-				}
+	if (insn->alts) {
+		for (alt = insn->alts; alt; alt = alt->next) {
+			ret = validate_branch(file, func, alt->insn, *statep);
+			if (ret) {
+				BT_INSN(insn, "(alt)");
+				return ret;
+			}
+		}
+	}
 
-				if (!save_insn->visited) {
-					/*
-					 * If the restore hint insn is at the
-					 * beginning of a basic block and was
-					 * branched to from elsewhere, and the
-					 * save insn hasn't been visited yet,
-					 * defer following this branch for now.
-					 * It will be seen later via the
-					 * straight-line path.
-					 */
-					if (!prev_insn)
-						return 0;
+	if (skip_alt_group(insn))
+		return 0;
 
-					WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
-					return 1;
-				}
+	if (handle_insn_ops(insn, next_insn, statep))
+		return 1;
 
-				insn->cfi = save_insn->cfi;
-				nr_cfi_reused++;
-			}
+	switch (insn->type) {
 
-			state.cfi = *insn->cfi;
-		} else {
-			/* XXX track if we actually changed state.cfi */
+	case INSN_RETURN:
+		return validate_return(func, insn, statep);
 
-			if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
-				insn->cfi = prev_insn->cfi;
-				nr_cfi_reused++;
-			} else {
-				insn->cfi = cfi_hash_find_or_add(&state.cfi);
-			}
+	case INSN_CALL:
+	case INSN_CALL_DYNAMIC:
+		ret = validate_call(file, insn, statep);
+		if (ret)
+			return ret;
+
+		if (opts.stackval && func && !is_special_call(insn) &&
+		    !has_valid_stack_frame(statep)) {
+			WARN_INSN(insn, "call without frame pointer save/setup");
+			return 1;
 		}
 
-		insn->visited |= visited;
+		break;
 
-		if (propagate_alt_cfi(file, insn))
-			return 1;
+	case INSN_JUMP_CONDITIONAL:
+	case INSN_JUMP_UNCONDITIONAL:
+		if (is_sibling_call(insn)) {
+			ret = validate_sibling_call(file, insn, statep);
+			if (ret)
+				return ret;
 
-		if (insn->alts) {
-			for (alt = insn->alts; alt; alt = alt->next) {
-				ret = validate_branch(file, func, alt->insn, state);
-				if (ret) {
-					BT_INSN(insn, "(alt)");
-					return ret;
-				}
+		} else if (insn->jump_dest) {
+			ret = validate_branch(file, func,
+					      insn->jump_dest, *statep);
+			if (ret) {
+				BT_INSN(insn, "(branch)");
+				return ret;
 			}
 		}
 
-		if (skip_alt_group(insn))
+		if (insn->type == INSN_JUMP_UNCONDITIONAL)
 			return 0;
 
-		if (handle_insn_ops(insn, next_insn, &state))
-			return 1;
-
-		switch (insn->type) {
-
-		case INSN_RETURN:
-			return validate_return(func, insn, &state);
+		break;
 
-		case INSN_CALL:
-		case INSN_CALL_DYNAMIC:
-			ret = validate_call(file, insn, &state);
+	case INSN_JUMP_DYNAMIC:
+	case INSN_JUMP_DYNAMIC_CONDITIONAL:
+		if (is_sibling_call(insn)) {
+			ret = validate_sibling_call(file, insn, statep);
 			if (ret)
 				return ret;
+		}
 
-			if (opts.stackval && func && !is_special_call(insn) &&
-			    !has_valid_stack_frame(&state)) {
-				WARN_INSN(insn, "call without frame pointer save/setup");
-				return 1;
-			}
+		if (insn->type == INSN_JUMP_DYNAMIC)
+			return 0;
 
-			break;
+		break;
 
-		case INSN_JUMP_CONDITIONAL:
-		case INSN_JUMP_UNCONDITIONAL:
-			if (is_sibling_call(insn)) {
-				ret = validate_sibling_call(file, insn, &state);
-				if (ret)
-					return ret;
+	case INSN_SYSCALL:
+		if (func && (!next_insn || !next_insn->hint)) {
+			WARN_INSN(insn, "unsupported instruction in callable function");
+			return 1;
+		}
 
-			} else if (insn->jump_dest) {
-				ret = validate_branch(file, func,
-						      insn->jump_dest, state);
-				if (ret) {
-					BT_INSN(insn, "(branch)");
-					return ret;
-				}
-			}
+		break;
 
-			if (insn->type == INSN_JUMP_UNCONDITIONAL)
-				return 0;
+	case INSN_SYSRET:
+		if (func && (!next_insn || !next_insn->hint)) {
+			WARN_INSN(insn, "unsupported instruction in callable function");
+			return 1;
+		}
+
+		return 0;
 
+	case INSN_STAC:
+		if (!opts.uaccess)
 			break;
 
-		case INSN_JUMP_DYNAMIC:
-		case INSN_JUMP_DYNAMIC_CONDITIONAL:
-			if (is_sibling_call(insn)) {
-				ret = validate_sibling_call(file, insn, &state);
-				if (ret)
-					return ret;
-			}
+		if (statep->uaccess) {
+			WARN_INSN(insn, "recursive UACCESS enable");
+			return 1;
+		}
 
-			if (insn->type == INSN_JUMP_DYNAMIC)
-				return 0;
+		statep->uaccess = true;
+		break;
 
+	case INSN_CLAC:
+		if (!opts.uaccess)
 			break;
 
-		case INSN_SYSCALL:
-			if (func && (!next_insn || !next_insn->hint)) {
-				WARN_INSN(insn, "unsupported instruction in callable function");
-				return 1;
-			}
+		if (!statep->uaccess && func) {
+			WARN_INSN(insn, "redundant UACCESS disable");
+			return 1;
+		}
 
-			break;
+		if (func_uaccess_safe(func) && !statep->uaccess_stack) {
+			WARN_INSN(insn, "UACCESS-safe disables UACCESS");
+			return 1;
+		}
 
-		case INSN_SYSRET:
-			if (func && (!next_insn || !next_insn->hint)) {
-				WARN_INSN(insn, "unsupported instruction in callable function");
-				return 1;
-			}
+		statep->uaccess = false;
+		break;
 
-			return 0;
+	case INSN_STD:
+		if (statep->df) {
+			WARN_INSN(insn, "recursive STD");
+			return 1;
+		}
 
-		case INSN_STAC:
-			if (!opts.uaccess)
-				break;
+		statep->df = true;
+		break;
 
-			if (state.uaccess) {
-				WARN_INSN(insn, "recursive UACCESS enable");
-				return 1;
-			}
+	case INSN_CLD:
+		if (!statep->df && func) {
+			WARN_INSN(insn, "redundant CLD");
+			return 1;
+		}
 
-			state.uaccess = true;
-			break;
+		statep->df = false;
+		break;
 
-		case INSN_CLAC:
-			if (!opts.uaccess)
-				break;
+	default:
+		break;
+	}
 
-			if (!state.uaccess && func) {
-				WARN_INSN(insn, "redundant UACCESS disable");
-				return 1;
-			}
+	*dead_end = insn->dead_end;
 
-			if (func_uaccess_safe(func) && !state.uaccess_stack) {
-				WARN_INSN(insn, "UACCESS-safe disables UACCESS");
-				return 1;
-			}
+	return 0;
+}
 
-			state.uaccess = false;
-			break;
+/*
+ * Follow the branch starting at the given instruction, and recursively follow
+ * any other branches (jumps).  Meanwhile, track the frame pointer state at
+ * each instruction and validate all the rules described in
+ * tools/objtool/Documentation/objtool.txt.
+ */
+static int validate_branch(struct objtool_file *file, struct symbol *func,
+			   struct instruction *insn, struct insn_state state)
+{
+	struct instruction *next_insn, *prev_insn = NULL;
+	bool dead_end;
+	int ret;
 
-		case INSN_STD:
-			if (state.df) {
-				WARN_INSN(insn, "recursive STD");
-				return 1;
-			}
+	if (func && func->ignore)
+		return 0;
 
-			state.df = true;
-			break;
+	while (1) {
+		next_insn = next_insn_to_validate(file, insn);
 
-		case INSN_CLD:
-			if (!state.df && func) {
-				WARN_INSN(insn, "redundant CLD");
-				return 1;
-			}
+		if (opts.checksum && func && insn->sec)
+			checksum_update_insn(file, func, insn);
 
-			state.df = false;
-			break;
+		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
+			/* Ignore KCFI type preambles, which always fall through */
+			if (is_prefix_func(func))
+				return 0;
 
-		default:
-			break;
+			if (file->ignore_unreachables)
+				return 0;
+
+			WARN("%s() falls through to next function %s()",
+			     func->name, insn_func(insn)->name);
+			func->warned = 1;
+
+			return 1;
 		}
 
-		if (insn->dead_end)
-			return 0;
+		ret = validate_insn(file, func, insn, &state, prev_insn, next_insn,
+				    &dead_end);
+		if (dead_end)
+			break;
 
 		if (!next_insn) {
 			if (state.cfi.cfa.base == CFI_UNDEFINED)
@@ -3970,7 +3994,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 		insn = next_insn;
 	}
 
-	return 0;
+	return ret;
 }
 
 static int validate_unwind_hint(struct objtool_file *file,
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 09/30] objtool: Record symbol name max length
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (7 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 08/30] objtool: Extract code to validate instruction from the validate branch loop Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 10/30] objtool: Add option to trace function validation Alexandre Chartre
                   ` (22 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Keep track of the maximum length of symbol names. This will help
formatting the code flow between different functions.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 9c1888e21c1d8..42a33898b695d 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -37,6 +37,8 @@ static struct cfi_state init_cfi;
 static struct cfi_state func_cfi;
 static struct cfi_state force_undefined_cfi;
 
+static size_t sym_name_max_len;
+
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset)
 {
@@ -2484,6 +2486,7 @@ static bool is_profiling_func(const char *name)
 static int classify_symbols(struct objtool_file *file)
 {
 	struct symbol *func;
+	size_t len;
 
 	for_each_sym(file->elf, func) {
 		if (is_notype_sym(func) && strstarts(func->name, ".L"))
@@ -2510,6 +2513,10 @@ static int classify_symbols(struct objtool_file *file)
 
 		if (is_profiling_func(func->name))
 			func->profiling_func = true;
+
+		len = strlen(func->name);
+		if (len > sym_name_max_len)
+			sym_name_max_len = len;
 	}
 
 	return 0;
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 10/30] objtool: Add option to trace function validation
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (8 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 09/30] objtool: Record symbol name max length Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 11/30] objtool: Trace instruction state changes during " Alexandre Chartre
                   ` (21 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Add an option to trace and have information during the validation
of specified functions. Functions are specified with the --trace
option which can be a single function name (e.g. --trace foo to
trace the function with the name "foo"), or a shell wildcard
pattern (e.g. --trace foo* to trace all functions with a name
starting with "foo").

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/Build                     |   1 +
 tools/objtool/builtin-check.c           |   1 +
 tools/objtool/check.c                   | 104 +++++++++++++++++----
 tools/objtool/disas.c                   | 115 ++++++++++++++++++++++++
 tools/objtool/include/objtool/builtin.h |   1 +
 tools/objtool/include/objtool/check.h   |   6 +-
 tools/objtool/include/objtool/disas.h   |  11 +++
 tools/objtool/include/objtool/trace.h   |  68 ++++++++++++++
 tools/objtool/include/objtool/warn.h    |   1 +
 tools/objtool/trace.c                   |   9 ++
 10 files changed, 299 insertions(+), 18 deletions(-)
 create mode 100644 tools/objtool/include/objtool/trace.h
 create mode 100644 tools/objtool/trace.c

diff --git a/tools/objtool/Build b/tools/objtool/Build
index 9d1e8f28ef953..9982e665d58da 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -9,6 +9,7 @@ objtool-y += elf.o
 objtool-y += objtool.o
 
 objtool-$(BUILD_DISAS) += disas.o
+objtool-$(BUILD_DISAS) += trace.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/builtin-check.c b/tools/objtool/builtin-check.c
index aab7fa9c7e00a..3329d370006b4 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -103,6 +103,7 @@ static const struct option check_options[] = {
 	OPT_STRING('o',		 "output", &opts.output, "file", "output file name"),
 	OPT_BOOLEAN(0,		 "sec-address", &opts.sec_address, "print section addresses in warnings"),
 	OPT_BOOLEAN(0,		 "stats", &opts.stats, "print statistics"),
+	OPT_STRING(0,		 "trace", &opts.trace, "func", "trace function validation"),
 	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
 	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
 
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 42a33898b695d..2352b9668b126 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4,6 +4,7 @@
  */
 
 #define _GNU_SOURCE /* memmem() */
+#include <fnmatch.h>
 #include <string.h>
 #include <stdlib.h>
 #include <inttypes.h>
@@ -15,6 +16,7 @@
 #include <objtool/disas.h>
 #include <objtool/check.h>
 #include <objtool/special.h>
+#include <objtool/trace.h>
 #include <objtool/warn.h>
 #include <objtool/checksum.h>
 #include <objtool/util.h>
@@ -37,7 +39,9 @@ static struct cfi_state init_cfi;
 static struct cfi_state func_cfi;
 static struct cfi_state force_undefined_cfi;
 
-static size_t sym_name_max_len;
+struct disas_context *objtool_disas_ctx;
+
+size_t sym_name_max_len;
 
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset)
@@ -3608,8 +3612,10 @@ static bool skip_alt_group(struct instruction *insn)
 		return false;
 
 	/* ANNOTATE_IGNORE_ALTERNATIVE */
-	if (insn->alt_group->ignore)
+	if (insn->alt_group->ignore) {
+		TRACE_INSN(insn, "alt group ignored");
 		return true;
+	}
 
 	/*
 	 * For NOP patched with CLAC/STAC, only follow the latter to avoid
@@ -3715,6 +3721,8 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 
 static int validate_branch(struct objtool_file *file, struct symbol *func,
 			   struct instruction *insn, struct insn_state state);
+static int do_validate_branch(struct objtool_file *file, struct symbol *func,
+			      struct instruction *insn, struct insn_state state);
 
 static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 struct instruction *insn, struct insn_state *statep,
@@ -3736,8 +3744,10 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		if (!insn->hint && !insn_cfi_match(insn, &statep->cfi))
 			return 1;
 
-		if (insn->visited & visited)
+		if (insn->visited & visited) {
+			TRACE_INSN(insn, "already visited");
 			return 0;
+		}
 	} else {
 		nr_insns_visited++;
 	}
@@ -3774,8 +3784,10 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 				 * It will be seen later via the
 				 * straight-line path.
 				 */
-				if (!prev_insn)
+				if (!prev_insn) {
+					TRACE_INSN(insn, "defer restore");
 					return 0;
+				}
 
 				WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
 				return 1;
@@ -3803,13 +3815,23 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		return 1;
 
 	if (insn->alts) {
+		int i, num_alts;
+
+		num_alts = 0;
+		for (alt = insn->alts; alt; alt = alt->next)
+			num_alts++;
+
+		i = 1;
 		for (alt = insn->alts; alt; alt = alt->next) {
+			TRACE_INSN(insn, "alternative %d/%d", i, num_alts);
 			ret = validate_branch(file, func, alt->insn, *statep);
 			if (ret) {
 				BT_INSN(insn, "(alt)");
 				return ret;
 			}
+			i++;
 		}
+		TRACE_INSN(insn, "alternative DEFAULT");
 	}
 
 	if (skip_alt_group(insn))
@@ -3821,10 +3843,16 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	switch (insn->type) {
 
 	case INSN_RETURN:
+		TRACE_INSN(insn, "return");
 		return validate_return(func, insn, statep);
 
 	case INSN_CALL:
 	case INSN_CALL_DYNAMIC:
+		if (insn->type == INSN_CALL)
+			TRACE_INSN(insn, "call");
+		else
+			TRACE_INSN(insn, "indirect call");
+
 		ret = validate_call(file, insn, statep);
 		if (ret)
 			return ret;
@@ -3840,13 +3868,18 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	case INSN_JUMP_CONDITIONAL:
 	case INSN_JUMP_UNCONDITIONAL:
 		if (is_sibling_call(insn)) {
+			TRACE_INSN(insn, "sibling call");
 			ret = validate_sibling_call(file, insn, statep);
 			if (ret)
 				return ret;
 
 		} else if (insn->jump_dest) {
-			ret = validate_branch(file, func,
-					      insn->jump_dest, *statep);
+			if (insn->type == INSN_JUMP_UNCONDITIONAL)
+				TRACE_INSN(insn, "unconditional jump");
+			else
+				TRACE_INSN(insn, "jump taken");
+
+			ret = validate_branch(file, func, insn->jump_dest, *statep);
 			if (ret) {
 				BT_INSN(insn, "(branch)");
 				return ret;
@@ -3856,10 +3889,12 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		if (insn->type == INSN_JUMP_UNCONDITIONAL)
 			return 0;
 
+		TRACE_INSN(insn, "jump not taken");
 		break;
 
 	case INSN_JUMP_DYNAMIC:
 	case INSN_JUMP_DYNAMIC_CONDITIONAL:
+		TRACE_INSN(insn, "indirect jump");
 		if (is_sibling_call(insn)) {
 			ret = validate_sibling_call(file, insn, statep);
 			if (ret)
@@ -3872,6 +3907,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_SYSCALL:
+		TRACE_INSN(insn, "syscall");
 		if (func && (!next_insn || !next_insn->hint)) {
 			WARN_INSN(insn, "unsupported instruction in callable function");
 			return 1;
@@ -3880,6 +3916,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_SYSRET:
+		TRACE_INSN(insn, "sysret");
 		if (func && (!next_insn || !next_insn->hint)) {
 			WARN_INSN(insn, "unsupported instruction in callable function");
 			return 1;
@@ -3888,6 +3925,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		return 0;
 
 	case INSN_STAC:
+		TRACE_INSN(insn, "stac");
 		if (!opts.uaccess)
 			break;
 
@@ -3900,6 +3938,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_CLAC:
+		TRACE_INSN(insn, "clac");
 		if (!opts.uaccess)
 			break;
 
@@ -3917,6 +3956,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_STD:
+		TRACE_INSN(insn, "std");
 		if (statep->df) {
 			WARN_INSN(insn, "recursive STD");
 			return 1;
@@ -3926,6 +3966,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_CLD:
+		TRACE_INSN(insn, "cld");
 		if (!statep->df && func) {
 			WARN_INSN(insn, "redundant CLD");
 			return 1;
@@ -3938,8 +3979,10 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 	}
 
-	*dead_end = insn->dead_end;
+	if (insn->dead_end)
+		TRACE_INSN(insn, "dead end");
 
+	*dead_end = insn->dead_end;
 	return 0;
 }
 
@@ -3949,8 +3992,8 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
  * each instruction and validate all the rules described in
  * tools/objtool/Documentation/objtool.txt.
  */
-static int validate_branch(struct objtool_file *file, struct symbol *func,
-			   struct instruction *insn, struct insn_state state)
+static int do_validate_branch(struct objtool_file *file, struct symbol *func,
+			      struct instruction *insn, struct insn_state state)
 {
 	struct instruction *next_insn, *prev_insn = NULL;
 	bool dead_end;
@@ -3959,7 +4002,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 	if (func && func->ignore)
 		return 0;
 
-	while (1) {
+	do {
+		insn->trace = 0;
 		next_insn = next_insn_to_validate(file, insn);
 
 		if (opts.checksum && func && insn->sec)
@@ -3982,10 +4026,15 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 		ret = validate_insn(file, func, insn, &state, prev_insn, next_insn,
 				    &dead_end);
-		if (dead_end)
-			break;
 
-		if (!next_insn) {
+		if (!insn->trace) {
+			if (ret)
+				TRACE_INSN(insn, "warning (%d)", ret);
+			else
+				TRACE_INSN(insn, NULL);
+		}
+
+		if (!dead_end && !next_insn) {
 			if (state.cfi.cfa.base == CFI_UNDEFINED)
 				return 0;
 			if (file->ignore_unreachables)
@@ -3999,7 +4048,20 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 		prev_insn = insn;
 		insn = next_insn;
-	}
+
+	} while (!dead_end);
+
+	return ret;
+}
+
+static int validate_branch(struct objtool_file *file, struct symbol *func,
+			   struct instruction *insn, struct insn_state state)
+{
+	int ret;
+
+	trace_depth_inc();
+	ret = do_validate_branch(file, func, insn, state);
+	trace_depth_dec();
 
 	return ret;
 }
@@ -4460,10 +4522,18 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
 	if (opts.checksum)
 		checksum_init(func);
 
+	if (opts.trace && !fnmatch(opts.trace, sym->name, 0)) {
+		trace_enable();
+		TRACE("%s: validation begin\n", sym->name);
+	}
+
 	ret = validate_branch(file, func, insn, *state);
 	if (ret)
 		BT_INSN(insn, "<=== (sym)");
 
+	TRACE("%s: validation %s\n\n", sym->name, ret ? "failed" : "end");
+	trace_disable();
+
 	if (opts.checksum)
 		checksum_finish(func);
 
@@ -4875,8 +4945,6 @@ 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;
@@ -4898,8 +4966,10 @@ int check(struct objtool_file *file)
 	 * disassembly context to disassemble instruction or function
 	 * on warning or backtrace.
 	 */
-	if (opts.verbose || opts.backtrace) {
+	if (opts.verbose || opts.backtrace || opts.trace) {
 		disas_ctx = disas_context_create(file);
+		if (!disas_ctx)
+			opts.trace = false;
 		objtool_disas_ctx = disas_ctx;
 	}
 
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index a030b06c121d2..0ca6e6c8559fd 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -308,6 +308,121 @@ char *disas_result(struct disas_context *dctx)
 	return dctx->result;
 }
 
+#define DISAS_INSN_OFFSET_SPACE		10
+#define DISAS_INSN_SPACE		60
+
+/*
+ * Print a message in the instruction flow. If insn is not NULL then
+ * the instruction address is printed in addition of the message,
+ * otherwise only the message is printed. In all cases, the instruction
+ * itself is not printed.
+ */
+static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
+			int depth, const char *format, va_list ap)
+{
+	const char *addr_str;
+	int i, n;
+	int len;
+
+	len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE;
+	if (depth < 0) {
+		len += depth;
+		depth = 0;
+	}
+
+	n = 0;
+
+	if (sec) {
+		addr_str = offstr(sec, offset);
+		n += fprintf(stream, "%6lx:  %-*s  ", offset, len, addr_str);
+		free((char *)addr_str);
+	} else {
+		len += DISAS_INSN_OFFSET_SPACE + 1;
+		n += fprintf(stream, "%-*s", len, "");
+	}
+
+	/* print vertical bars to show the code flow */
+	for (i = 0; i < depth; i++)
+		n += fprintf(stream, "| ");
+
+	if (format)
+		n += vfprintf(stream, format, ap);
+
+	return n;
+}
+
+/*
+ * Print a message in the instruction flow. If insn is not NULL then
+ * the instruction address is printed in addition of the message,
+ * otherwise only the message is printed. In all cases, the instruction
+ * itself is not printed.
+ */
+void disas_print_info(FILE *stream, struct instruction *insn, int depth,
+		      const char *format, ...)
+{
+	struct section *sec;
+	unsigned long off;
+	va_list args;
+
+	if (insn) {
+		sec = insn->sec;
+		off = insn->offset;
+	} else {
+		sec = NULL;
+		off = 0;
+	}
+
+	va_start(args, format);
+	disas_vprint(stream, sec, off, depth, format, args);
+	va_end(args);
+}
+
+/*
+ * Print an instruction address (offset and function), the instruction itself
+ * and an optional message.
+ */
+void disas_print_insn(FILE *stream, struct disas_context *dctx,
+		      struct instruction *insn, int depth,
+		      const char *format, ...)
+{
+	char fake_nop_insn[32];
+	const char *insn_str;
+	bool fake_nop;
+	va_list args;
+	int len;
+
+	/*
+	 * Alternative can insert a fake nop, sometimes with no
+	 * associated section so nothing to disassemble.
+	 */
+	fake_nop = (!insn->sec && insn->type == INSN_NOP);
+	if (fake_nop) {
+		snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len);
+		insn_str = fake_nop_insn;
+	} else {
+		disas_insn(dctx, insn);
+		insn_str = disas_result(dctx);
+	}
+
+	/* print the instruction */
+	len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1;
+	disas_print_info(stream, insn, depth, "%-*s", len, insn_str);
+
+	/* print message if any */
+	if (!format)
+		return;
+
+	if (strcmp(format, "\n") == 0) {
+		fprintf(stream, "\n");
+		return;
+	}
+
+	fprintf(stream, " - ");
+	va_start(args, format);
+	vfprintf(stream, format, args);
+	va_end(args);
+}
+
 /*
  * Disassemble a single instruction. Return the size of the instruction.
  */
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index bb0b25eb08ba4..991365c10f0e9 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -41,6 +41,7 @@ struct opts {
 	const char *output;
 	bool sec_address;
 	bool stats;
+	const char *trace;
 	bool verbose;
 	bool werror;
 };
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index f96aabd7d54dc..fde958683485f 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -66,7 +66,8 @@ struct instruction {
 	    visited		: 4,
 	    no_reloc		: 1,
 	    hole		: 1,
-	    fake		: 1;
+	    fake		: 1,
+	    trace		: 1;
 		/* 9 bit hole */
 
 	struct alt_group *alt_group;
@@ -143,4 +144,7 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc
 
 const char *objtool_disas_insn(struct instruction *insn);
 
+extern size_t sym_name_max_len;
+extern struct disas_context *objtool_disas_ctx;
+
 #endif /* _CHECK_H */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 1aee1fbe0bb97..5db75d06f2197 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -19,6 +19,11 @@ int disas_info_init(struct disassemble_info *dinfo,
 		    const char *options);
 size_t disas_insn(struct disas_context *dctx, struct instruction *insn);
 char *disas_result(struct disas_context *dctx);
+void disas_print_info(FILE *stream, struct instruction *insn, int depth,
+		      const char *format, ...);
+void disas_print_insn(FILE *stream, struct disas_context *dctx,
+		      struct instruction *insn, int depth,
+		      const char *format, ...);
 
 #else /* DISAS */
 
@@ -51,6 +56,12 @@ static inline char *disas_result(struct disas_context *dctx)
 	return NULL;
 }
 
+static inline void disas_print_info(FILE *stream, struct instruction *insn,
+				    int depth, const char *format, ...) {}
+static inline void disas_print_insn(FILE *stream, struct disas_context *dctx,
+				    struct instruction *insn, int depth,
+				    const char *format, ...) {}
+
 #endif /* DISAS */
 
 #endif /* _DISAS_H */
diff --git a/tools/objtool/include/objtool/trace.h b/tools/objtool/include/objtool/trace.h
new file mode 100644
index 0000000000000..3f3c830ed114e
--- /dev/null
+++ b/tools/objtool/include/objtool/trace.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#ifndef _TRACE_H
+#define _TRACE_H
+
+#include <objtool/check.h>
+#include <objtool/disas.h>
+
+#ifdef DISAS
+
+extern bool trace;
+extern int trace_depth;
+
+#define TRACE(fmt, ...)						\
+({	if (trace)						\
+		fprintf(stderr, fmt, ##__VA_ARGS__);		\
+})
+
+#define TRACE_INSN(insn, fmt, ...)				\
+({								\
+	if (trace) {						\
+		disas_print_insn(stderr, objtool_disas_ctx,	\
+				 insn, trace_depth - 1,	\
+				 fmt, ##__VA_ARGS__);		\
+		fprintf(stderr, "\n");				\
+		insn->trace = 1;				\
+	}							\
+})
+
+static inline void trace_enable(void)
+{
+	trace = true;
+	trace_depth = 0;
+}
+
+static inline void trace_disable(void)
+{
+	trace = false;
+}
+
+static inline void trace_depth_inc(void)
+{
+	if (trace)
+		trace_depth++;
+}
+
+static inline void trace_depth_dec(void)
+{
+	if (trace)
+		trace_depth--;
+}
+
+#else /* DISAS */
+
+#define TRACE(fmt, ...) ({})
+#define TRACE_INSN(insn, fmt, ...) ({})
+
+static inline void trace_enable(void) {}
+static inline void trace_disable(void) {}
+static inline void trace_depth_inc(void) {}
+static inline void trace_depth_dec(void) {}
+
+#endif
+
+#endif /* _TRACE_H */
diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h
index f32abc7b1be14..25ff7942b4d54 100644
--- a/tools/objtool/include/objtool/warn.h
+++ b/tools/objtool/include/objtool/warn.h
@@ -97,6 +97,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 		_len = (_len < 50) ? 50 - _len : 0;		\
 		WARN("  %s: " format "  %*s%s", _str, ##__VA_ARGS__, _len, "", _istr); \
 		free(_str);						\
+		__insn->trace = 1;				\
 	}							\
 })
 
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
new file mode 100644
index 0000000000000..134cc33ffe970
--- /dev/null
+++ b/tools/objtool/trace.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#include <objtool/trace.h>
+
+bool trace;
+int trace_depth;
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 11/30] objtool: Trace instruction state changes during function validation
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (9 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 10/30] objtool: Add option to trace function validation Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-12-01 20:23   ` [PATCH v6 11/30] " Nathan Chancellor
  2025-11-21  9:53 ` [PATCH v6 12/30] objtool: Improve register reporting " Alexandre Chartre
                   ` (20 subsequent siblings)
  31 siblings, 2 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

During function validation, objtool maintains a per-instruction state,
in particular to track call frame information. When tracing validation,
print any instruction state changes.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c                 |   8 +-
 tools/objtool/include/objtool/trace.h |  10 ++
 tools/objtool/trace.c                 | 132 ++++++++++++++++++++++++++
 3 files changed, 149 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 2352b9668b126..e12dba144731f 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3729,6 +3729,8 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 struct instruction *prev_insn, struct instruction *next_insn,
 			 bool *dead_end)
 {
+	/* prev_state is not used if there is no disassembly support */
+	struct insn_state prev_state __maybe_unused;
 	struct alternative *alt;
 	u8 visited;
 	int ret;
@@ -3837,7 +3839,11 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	if (skip_alt_group(insn))
 		return 0;
 
-	if (handle_insn_ops(insn, next_insn, statep))
+	prev_state = *statep;
+	ret = handle_insn_ops(insn, next_insn, statep);
+	TRACE_INSN_STATE(insn, &prev_state, statep);
+
+	if (ret)
 		return 1;
 
 	switch (insn->type) {
diff --git a/tools/objtool/include/objtool/trace.h b/tools/objtool/include/objtool/trace.h
index 3f3c830ed114e..33fe9c6acb4fd 100644
--- a/tools/objtool/include/objtool/trace.h
+++ b/tools/objtool/include/objtool/trace.h
@@ -30,6 +30,12 @@ extern int trace_depth;
 	}							\
 })
 
+#define TRACE_INSN_STATE(insn, sprev, snext)			\
+({								\
+	if (trace)						\
+		trace_insn_state(insn, sprev, snext);		\
+})
+
 static inline void trace_enable(void)
 {
 	trace = true;
@@ -53,10 +59,14 @@ static inline void trace_depth_dec(void)
 		trace_depth--;
 }
 
+void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
+		      struct insn_state *snext);
+
 #else /* DISAS */
 
 #define TRACE(fmt, ...) ({})
 #define TRACE_INSN(insn, fmt, ...) ({})
+#define TRACE_INSN_STATE(insn, sprev, snext) ({})
 
 static inline void trace_enable(void) {}
 static inline void trace_disable(void) {}
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
index 134cc33ffe970..12bbad09d9c02 100644
--- a/tools/objtool/trace.c
+++ b/tools/objtool/trace.c
@@ -7,3 +7,135 @@
 
 bool trace;
 int trace_depth;
+
+/*
+ * Macros to trace CFI state attributes changes.
+ */
+
+#define TRACE_CFI_ATTR(attr, prev, next, fmt, ...)		\
+({								\
+	if ((prev)->attr != (next)->attr)			\
+		TRACE("%s=" fmt " ", #attr, __VA_ARGS__);	\
+})
+
+#define TRACE_CFI_ATTR_BOOL(attr, prev, next)			\
+	TRACE_CFI_ATTR(attr, prev, next,			\
+		       "%s", (next)->attr ? "true" : "false")
+
+#define TRACE_CFI_ATTR_NUM(attr, prev, next, fmt)		\
+	TRACE_CFI_ATTR(attr, prev, next, fmt, (next)->attr)
+
+#define CFI_REG_NAME_MAXLEN   16
+
+/*
+ * Return the name of a register. Note that the same static buffer
+ * is returned if the name is dynamically generated.
+ */
+static const char *cfi_reg_name(unsigned int reg)
+{
+	static char rname_buffer[CFI_REG_NAME_MAXLEN];
+
+	switch (reg) {
+	case CFI_UNDEFINED:
+		return "<undefined>";
+	case CFI_CFA:
+		return "cfa";
+	case CFI_SP_INDIRECT:
+		return "(sp)";
+	case CFI_BP_INDIRECT:
+		return "(bp)";
+	}
+
+	if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
+		return "<error>";
+
+	return (const char *)rname_buffer;
+}
+
+/*
+ * Functions and macros to trace CFI registers changes.
+ */
+
+static void trace_cfi_reg(const char *prefix, int reg, const char *fmt,
+			  int base_prev, int offset_prev,
+			  int base_next, int offset_next)
+{
+	char *rname;
+
+	if (base_prev == base_next && offset_prev == offset_next)
+		return;
+
+	if (prefix)
+		TRACE("%s:", prefix);
+
+	if (base_next == CFI_UNDEFINED) {
+		TRACE("%1$s=<undef> ", cfi_reg_name(reg));
+	} else {
+		rname = strdup(cfi_reg_name(reg));
+		TRACE(fmt, rname, cfi_reg_name(base_next), offset_next);
+		free(rname);
+	}
+}
+
+static void trace_cfi_reg_val(const char *prefix, int reg,
+			      int base_prev, int offset_prev,
+			      int base_next, int offset_next)
+{
+	trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ",
+		      base_prev, offset_prev, base_next, offset_next);
+}
+
+static void trace_cfi_reg_ref(const char *prefix, int reg,
+			      int base_prev, int offset_prev,
+			      int base_next, int offset_next)
+{
+	trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ",
+		      base_prev, offset_prev, base_next, offset_next);
+}
+
+#define TRACE_CFI_REG_VAL(reg, prev, next)				\
+	trace_cfi_reg_val(NULL, reg, prev.base, prev.offset,		\
+			  next.base, next.offset)
+
+#define TRACE_CFI_REG_REF(reg, prev, next)				\
+	trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset,		\
+			  next.base, next.offset)
+
+void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
+		      struct insn_state *snext)
+{
+	struct cfi_state *cprev, *cnext;
+	int i;
+
+	if (!memcmp(sprev, snext, sizeof(struct insn_state)))
+		return;
+
+	cprev = &sprev->cfi;
+	cnext = &snext->cfi;
+
+	disas_print_insn(stderr, objtool_disas_ctx, insn,
+			 trace_depth - 1, "state: ");
+
+	/* print registers changes */
+	TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa);
+	for (i = 0; i < CFI_NUM_REGS; i++) {
+		TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]);
+		TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]);
+	}
+
+	/* print attributes changes */
+	TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d");
+	TRACE_CFI_ATTR_BOOL(drap, cprev, cnext);
+	if (cnext->drap) {
+		trace_cfi_reg_val("drap", cnext->drap_reg,
+				  cprev->drap_reg, cprev->drap_offset,
+				  cnext->drap_reg, cnext->drap_offset);
+	}
+	TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext);
+	TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d");
+	TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u");
+
+	TRACE("\n");
+
+	insn->trace = 1;
+}
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 12/30] objtool: Improve register reporting during function validation
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (10 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 11/30] objtool: Trace instruction state changes during " Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 13/30] objtool: Identify the different types of alternatives Alexandre Chartre
                   ` (19 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When tracing function validation, instruction state changes can
report changes involving registers. These registers are reported
with the name "r<num>" (e.g. "r3"). Print the CPU specific register
name instead of a generic name (e.g. print "rbx" instead of "r3"
on x86).

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/arch/loongarch/decode.c | 11 +++++++++++
 tools/objtool/arch/powerpc/decode.c   | 12 ++++++++++++
 tools/objtool/arch/x86/decode.c       |  8 ++++++++
 tools/objtool/include/objtool/arch.h  |  2 ++
 tools/objtool/trace.c                 |  7 +++++++
 5 files changed, 40 insertions(+)

diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 1de86ebb637d5..6cd288150f495 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -8,6 +8,17 @@
 #include <linux/objtool_types.h>
 #include <arch/elf.h>
 
+const char *arch_reg_name[CFI_NUM_REGS] = {
+	"zero", "ra", "tp", "sp",
+	"a0", "a1", "a2", "a3",
+	"a4", "a5", "a6", "a7",
+	"t0", "t1", "t2", "t3",
+	"t4", "t5", "t6", "t7",
+	"t8", "u0", "fp", "s0",
+	"s1", "s2", "s3", "s4",
+	"s5", "s6", "s7", "s8"
+};
+
 int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "_mcount");
diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c
index 4f68b402e7855..e534ac1123b33 100644
--- a/tools/objtool/arch/powerpc/decode.c
+++ b/tools/objtool/arch/powerpc/decode.c
@@ -9,6 +9,18 @@
 #include <objtool/warn.h>
 #include <objtool/builtin.h>
 
+const char *arch_reg_name[CFI_NUM_REGS] = {
+	"r0",  "sp",  "r2",  "r3",
+	"r4",  "r5",  "r6",  "r7",
+	"r8",  "r9",  "r10", "r11",
+	"r12", "r13", "r14", "r15",
+	"r16", "r17", "r18", "r19",
+	"r20", "r21", "r22", "r23",
+	"r24", "r25", "r26", "r27",
+	"r28", "r29", "r30", "r31",
+	"ra"
+};
+
 int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "_mcount");
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 83e9c604ce105..f4af825082284 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -23,6 +23,14 @@
 #include <objtool/builtin.h>
 #include <arch/elf.h>
 
+const char *arch_reg_name[CFI_NUM_REGS] = {
+	"rax", "rcx", "rdx", "rbx",
+	"rsp", "rbp", "rsi", "rdi",
+	"r8",  "r9",  "r10", "r11",
+	"r12", "r13", "r14", "r15",
+	"ra"
+};
+
 int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "__fentry__");
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index 18c0e69ee6170..8866158975fcb 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -103,6 +103,8 @@ 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);
 
+extern const char *arch_reg_name[CFI_NUM_REGS];
+
 #ifdef DISAS
 
 #include <bfd.h>
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
index 12bbad09d9c02..d70d47081e82d 100644
--- a/tools/objtool/trace.c
+++ b/tools/objtool/trace.c
@@ -34,6 +34,7 @@ int trace_depth;
 static const char *cfi_reg_name(unsigned int reg)
 {
 	static char rname_buffer[CFI_REG_NAME_MAXLEN];
+	const char *rname;
 
 	switch (reg) {
 	case CFI_UNDEFINED:
@@ -46,6 +47,12 @@ static const char *cfi_reg_name(unsigned int reg)
 		return "(bp)";
 	}
 
+	if (reg < CFI_NUM_REGS) {
+		rname = arch_reg_name[reg];
+		if (rname)
+			return rname;
+	}
+
 	if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
 		return "<error>";
 
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 13/30] objtool: Identify the different types of alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (11 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 12/30] objtool: Improve register reporting " Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 14/30] objtool: Add functions to better name alternatives Alexandre Chartre
                   ` (18 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Alternative code, including jump table and exception table, is represented
with the same struct alternative structure. But there is no obvious way to
identify whether the struct represents alternative instructions, a jump
table or an exception table.

So add a type to struct alternative to clearly identify the type of
alternative.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c                 | 13 ++++++++-----
 tools/objtool/include/objtool/check.h | 12 ++++++++++++
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index e12dba144731f..20630df83e85c 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -27,11 +27,6 @@
 #include <linux/static_call_types.h>
 #include <linux/string.h>
 
-struct alternative {
-	struct alternative *next;
-	struct instruction *insn;
-};
-
 static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
 
 static struct cfi_init_state initial_func_cfi;
@@ -1945,6 +1940,7 @@ static int add_special_section_alts(struct objtool_file *file)
 	struct list_head special_alts;
 	struct instruction *orig_insn, *new_insn;
 	struct special_alt *special_alt, *tmp;
+	enum alternative_type alt_type;
 	struct alternative *alt;
 
 	if (special_get_alts(file->elf, &special_alts))
@@ -1980,9 +1976,15 @@ static int add_special_section_alts(struct objtool_file *file)
 			if (handle_group_alt(file, special_alt, orig_insn, &new_insn))
 				return -1;
 
+			alt_type = ALT_TYPE_INSTRUCTIONS;
+
 		} else if (special_alt->jump_or_nop) {
 			if (handle_jump_alt(file, special_alt, orig_insn, &new_insn))
 				return -1;
+
+			alt_type = ALT_TYPE_JUMP_TABLE;
+		} else {
+			alt_type = ALT_TYPE_EX_TABLE;
 		}
 
 		alt = calloc(1, sizeof(*alt));
@@ -1993,6 +1995,7 @@ static int add_special_section_alts(struct objtool_file *file)
 
 		alt->insn = new_insn;
 		alt->next = orig_insn->alts;
+		alt->type = alt_type;
 		orig_insn->alts = alt;
 
 		list_del(&special_alt->list);
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index fde958683485f..cbf4af58e29b2 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -38,6 +38,18 @@ struct alt_group {
 	bool ignore;
 };
 
+enum alternative_type {
+	ALT_TYPE_INSTRUCTIONS,
+	ALT_TYPE_JUMP_TABLE,
+	ALT_TYPE_EX_TABLE,
+};
+
+struct alternative {
+	struct alternative *next;
+	struct instruction *insn;
+	enum alternative_type type;
+};
+
 #define INSN_CHUNK_BITS		8
 #define INSN_CHUNK_SIZE		(1 << INSN_CHUNK_BITS)
 #define INSN_CHUNK_MAX		(INSN_CHUNK_SIZE - 1)
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 14/30] objtool: Add functions to better name alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (12 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 13/30] objtool: Identify the different types of alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 15/30] objtool: Improve tracing of alternative instructions Alexandre Chartre
                   ` (17 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Add the disas_alt_name() and disas_alt_type_name() to provide a
name and a type name for an alternative. This will be used to
better name alternatives when tracing their execution.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c                 | 72 +++++++++++++++++++++++++++
 tools/objtool/include/objtool/disas.h | 12 +++++
 2 files changed, 84 insertions(+)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 0ca6e6c8559fd..b53be240825da 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -3,6 +3,8 @@
  * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
  */
 
+#define _GNU_SOURCE
+
 #include <objtool/arch.h>
 #include <objtool/check.h>
 #include <objtool/disas.h>
@@ -450,6 +452,76 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 	return disasm(insn->offset, &dctx->info);
 }
 
+/*
+ * Provide a name for the type of alternatives present at the
+ * specified instruction.
+ *
+ * An instruction can have alternatives with different types, for
+ * example alternative instructions and an exception table. In that
+ * case the name for the alternative instructions type is used.
+ *
+ * Return NULL if the instruction as no alternative.
+ */
+const char *disas_alt_type_name(struct instruction *insn)
+{
+	struct alternative *alt;
+	const char *name;
+
+	name = NULL;
+	for (alt = insn->alts; alt; alt = alt->next) {
+		if (alt->type == ALT_TYPE_INSTRUCTIONS) {
+			name = "alternative";
+			break;
+		}
+
+		switch (alt->type) {
+		case ALT_TYPE_EX_TABLE:
+			name = "ex_table";
+			break;
+		case ALT_TYPE_JUMP_TABLE:
+			name = "jump_table";
+			break;
+		default:
+			name = "unknown";
+			break;
+		}
+	}
+
+	return name;
+}
+
+/*
+ * Provide a name for an alternative.
+ */
+char *disas_alt_name(struct alternative *alt)
+{
+	char *str = NULL;
+
+	switch (alt->type) {
+
+	case ALT_TYPE_EX_TABLE:
+		str = strdup("EXCEPTION");
+		break;
+
+	case ALT_TYPE_JUMP_TABLE:
+		str = strdup("JUMP");
+		break;
+
+	case ALT_TYPE_INSTRUCTIONS:
+		/*
+		 * This is a non-default group alternative. Create a unique
+		 * name using the offset of the first original and alternative
+		 * instructions.
+		 */
+		asprintf(&str, "ALTERNATIVE %lx.%lx",
+			 alt->insn->alt_group->orig_group->first_insn->offset,
+			 alt->insn->alt_group->first_insn->offset);
+		break;
+	}
+
+	return str;
+}
+
 /*
  * Disassemble a function.
  */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 5db75d06f2197..8959d4c455622 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -6,6 +6,7 @@
 #ifndef _DISAS_H
 #define _DISAS_H
 
+struct alternative;
 struct disas_context;
 struct disassemble_info;
 
@@ -24,6 +25,8 @@ void disas_print_info(FILE *stream, struct instruction *insn, int depth,
 void disas_print_insn(FILE *stream, struct disas_context *dctx,
 		      struct instruction *insn, int depth,
 		      const char *format, ...);
+char *disas_alt_name(struct alternative *alt);
+const char *disas_alt_type_name(struct instruction *insn);
 
 #else /* DISAS */
 
@@ -61,6 +64,15 @@ static inline void disas_print_info(FILE *stream, struct instruction *insn,
 static inline void disas_print_insn(FILE *stream, struct disas_context *dctx,
 				    struct instruction *insn, int depth,
 				    const char *format, ...) {}
+static inline char *disas_alt_name(struct alternative *alt)
+{
+	return NULL;
+}
+
+static inline const char *disas_alt_type_name(struct instruction *insn)
+{
+	return NULL;
+}
 
 #endif /* DISAS */
 
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 15/30] objtool: Improve tracing of alternative instructions
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (13 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 14/30] objtool: Add functions to better name alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 16/30] objtool: Do not validate IBT for .return_sites and .call_sites Alexandre Chartre
                   ` (16 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When tracing function validation, improve the reporting of
alternative instruction by more clearly showing the different
alternatives beginning and end.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c                 | 18 +++-----
 tools/objtool/include/objtool/trace.h | 65 ++++++++++++++++++++++++++-
 tools/objtool/trace.c                 | 55 +++++++++++++++++++++++
 3 files changed, 125 insertions(+), 13 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 20630df83e85c..fb000923718dc 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3616,7 +3616,7 @@ static bool skip_alt_group(struct instruction *insn)
 
 	/* ANNOTATE_IGNORE_ALTERNATIVE */
 	if (insn->alt_group->ignore) {
-		TRACE_INSN(insn, "alt group ignored");
+		TRACE_ALT(insn, "alt group ignored");
 		return true;
 	}
 
@@ -3732,8 +3732,9 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 struct instruction *prev_insn, struct instruction *next_insn,
 			 bool *dead_end)
 {
-	/* prev_state is not used if there is no disassembly support */
+	/* prev_state and alt_name are not used if there is no disassembly support */
 	struct insn_state prev_state __maybe_unused;
+	char *alt_name __maybe_unused = NULL;
 	struct alternative *alt;
 	u8 visited;
 	int ret;
@@ -3820,23 +3821,16 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		return 1;
 
 	if (insn->alts) {
-		int i, num_alts;
-
-		num_alts = 0;
-		for (alt = insn->alts; alt; alt = alt->next)
-			num_alts++;
-
-		i = 1;
 		for (alt = insn->alts; alt; alt = alt->next) {
-			TRACE_INSN(insn, "alternative %d/%d", i, num_alts);
+			TRACE_ALT_BEGIN(insn, alt, alt_name);
 			ret = validate_branch(file, func, alt->insn, *statep);
+			TRACE_ALT_END(insn, alt, alt_name);
 			if (ret) {
 				BT_INSN(insn, "(alt)");
 				return ret;
 			}
-			i++;
 		}
-		TRACE_INSN(insn, "alternative DEFAULT");
+		TRACE_ALT_INFO_NOADDR(insn, "/ ", "DEFAULT");
 	}
 
 	if (skip_alt_group(insn))
diff --git a/tools/objtool/include/objtool/trace.h b/tools/objtool/include/objtool/trace.h
index 33fe9c6acb4fd..70b574366797b 100644
--- a/tools/objtool/include/objtool/trace.h
+++ b/tools/objtool/include/objtool/trace.h
@@ -19,11 +19,26 @@ extern int trace_depth;
 		fprintf(stderr, fmt, ##__VA_ARGS__);		\
 })
 
+/*
+ * Print the instruction address and a message. The instruction
+ * itself is not printed.
+ */
+#define TRACE_ADDR(insn, fmt, ...)				\
+({								\
+	if (trace) {						\
+		disas_print_info(stderr, insn, trace_depth - 1, \
+				 fmt "\n", ##__VA_ARGS__);	\
+	}							\
+})
+
+/*
+ * Print the instruction address, the instruction and a message.
+ */
 #define TRACE_INSN(insn, fmt, ...)				\
 ({								\
 	if (trace) {						\
 		disas_print_insn(stderr, objtool_disas_ctx,	\
-				 insn, trace_depth - 1,	\
+				 insn, trace_depth - 1,		\
 				 fmt, ##__VA_ARGS__);		\
 		fprintf(stderr, "\n");				\
 		insn->trace = 1;				\
@@ -36,6 +51,37 @@ extern int trace_depth;
 		trace_insn_state(insn, sprev, snext);		\
 })
 
+#define TRACE_ALT_FMT(pfx, fmt) pfx "<%s.%lx> " fmt
+#define TRACE_ALT_ARG(insn) disas_alt_type_name(insn), (insn)->offset
+
+#define TRACE_ALT(insn, fmt, ...)				\
+	TRACE_INSN(insn, TRACE_ALT_FMT("", fmt),		\
+		   TRACE_ALT_ARG(insn), ##__VA_ARGS__)
+
+#define TRACE_ALT_INFO(insn, pfx, fmt, ...)			\
+	TRACE_ADDR(insn, TRACE_ALT_FMT(pfx, fmt),		\
+		   TRACE_ALT_ARG(insn), ##__VA_ARGS__)
+
+#define TRACE_ALT_INFO_NOADDR(insn, pfx, fmt, ...)		\
+	TRACE_ADDR(NULL, TRACE_ALT_FMT(pfx, fmt),		\
+		   TRACE_ALT_ARG(insn), ##__VA_ARGS__)
+
+#define TRACE_ALT_BEGIN(insn, alt, alt_name)			\
+({								\
+	if (trace) {						\
+		alt_name = disas_alt_name(alt);			\
+		trace_alt_begin(insn, alt, alt_name);		\
+	}							\
+})
+
+#define TRACE_ALT_END(insn, alt, alt_name)			\
+({								\
+	if (trace) {						\
+		trace_alt_end(insn, alt, alt_name);		\
+		free(alt_name);					\
+	}							\
+})
+
 static inline void trace_enable(void)
 {
 	trace = true;
@@ -61,17 +107,34 @@ static inline void trace_depth_dec(void)
 
 void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
 		      struct insn_state *snext);
+void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
+		     char *alt_name);
+void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
+		   char *alt_name);
 
 #else /* DISAS */
 
 #define TRACE(fmt, ...) ({})
+#define TRACE_ADDR(insn, fmt, ...) ({})
 #define TRACE_INSN(insn, fmt, ...) ({})
 #define TRACE_INSN_STATE(insn, sprev, snext) ({})
+#define TRACE_ALT(insn, fmt, ...) ({})
+#define TRACE_ALT_INFO(insn, fmt, ...) ({})
+#define TRACE_ALT_INFO_NOADDR(insn, fmt, ...) ({})
+#define TRACE_ALT_BEGIN(insn, alt, alt_name) ({})
+#define TRACE_ALT_END(insn, alt, alt_name) ({})
+
 
 static inline void trace_enable(void) {}
 static inline void trace_disable(void) {}
 static inline void trace_depth_inc(void) {}
 static inline void trace_depth_dec(void) {}
+static inline void trace_alt_begin(struct instruction *orig_insn,
+				   struct alternative *alt,
+				   char *alt_name) {};
+static inline void trace_alt_end(struct instruction *orig_insn,
+				 struct alternative *alt,
+				 char *alt_name) {};
 
 #endif
 
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
index d70d47081e82d..5dec44dab781c 100644
--- a/tools/objtool/trace.c
+++ b/tools/objtool/trace.c
@@ -146,3 +146,58 @@ void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
 
 	insn->trace = 1;
 }
+
+void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
+		     char *alt_name)
+{
+	struct instruction *alt_insn;
+	char suffix[2];
+
+	alt_insn = alt->insn;
+
+	if (alt->type == ALT_TYPE_EX_TABLE) {
+		/*
+		 * When there is an exception table then the instruction
+		 * at the original location is executed but it can cause
+		 * an exception. In that case, the execution will be
+		 * redirected to the alternative instruction.
+		 *
+		 * The instruction at the original location can have
+		 * instruction alternatives, so we just print the location
+		 * of the instruction that can cause the exception and
+		 * not the instruction itself.
+		 */
+		TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s for instruction at 0x%lx <%s+0x%lx>",
+				      alt_name,
+				      orig_insn->offset, orig_insn->sym->name,
+				      orig_insn->offset - orig_insn->sym->offset);
+	} else {
+		TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s", alt_name);
+	}
+
+	if (alt->type == ALT_TYPE_JUMP_TABLE) {
+		/*
+		 * For a jump alternative, if the default instruction is
+		 * a NOP then it is replaced with the jmp instruction,
+		 * otherwise it is replaced with a NOP instruction.
+		 */
+		trace_depth++;
+		if (orig_insn->type == INSN_NOP) {
+			suffix[0] = (orig_insn->len == 5) ? 'q' : '\0';
+			TRACE_ADDR(orig_insn, "jmp%-3s %lx <%s+0x%lx>", suffix,
+				   alt_insn->offset, alt_insn->sym->name,
+				   alt_insn->offset - alt_insn->sym->offset);
+		} else {
+			TRACE_ADDR(orig_insn, "nop%d", orig_insn->len);
+			trace_depth--;
+		}
+	}
+}
+
+void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
+		   char *alt_name)
+{
+	if (alt->type == ALT_TYPE_JUMP_TABLE && orig_insn->type == INSN_NOP)
+		trace_depth--;
+	TRACE_ALT_INFO_NOADDR(orig_insn, "\\ ", "%s", alt_name);
+}
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 16/30] objtool: Do not validate IBT for .return_sites and .call_sites
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (14 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 15/30] objtool: Improve tracing of alternative instructions Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 17/30] objtool: Add the --disas=<function-pattern> action Alexandre Chartre
                   ` (15 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

The .return_sites and .call_sites sections reference text addresses,
but not with the intent to indirect branch to them, so they don't
need to be validated for IBT.

This is useful when running objtool on object files which already
have .return_sites or .call_sites sections, for example to re-run
objtool after it has reported an error or a warning.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index fb000923718dc..0da86834ab2ab 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4805,6 +4805,8 @@ static int validate_ibt(struct objtool_file *file)
 		    !strcmp(sec->name, ".llvm.call-graph-profile")	||
 		    !strcmp(sec->name, ".llvm_bb_addr_map")		||
 		    !strcmp(sec->name, "__tracepoints")			||
+		    !strcmp(sec->name, ".return_sites")			||
+		    !strcmp(sec->name, ".call_sites")			||
 		    !strcmp(sec->name, "__patchable_function_entries"))
 			continue;
 
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 17/30] objtool: Add the --disas=<function-pattern> action
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (15 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 16/30] objtool: Do not validate IBT for .return_sites and .call_sites Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 18/30] objtool: Preserve alternatives order Alexandre Chartre
                   ` (14 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Add the --disas=<function-pattern> actions to disassemble the specified
functions. The function pattern can be a single function name (e.g.
--disas foo to disassemble the function with the name "foo"), or a shell
wildcard pattern (e.g. --disas foo* to disassemble all functions with a
name starting with "foo").

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/builtin-check.c           |  2 ++
 tools/objtool/check.c                   | 38 ++++++++++++++-----------
 tools/objtool/disas.c                   | 27 ++++++++++++++++++
 tools/objtool/include/objtool/builtin.h |  1 +
 tools/objtool/include/objtool/disas.h   |  2 ++
 5 files changed, 53 insertions(+), 17 deletions(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 3329d370006b4..a0371312fe55a 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -75,6 +75,7 @@ static const struct option check_options[] = {
 	OPT_GROUP("Actions:"),
 	OPT_BOOLEAN(0,		 "checksum", &opts.checksum, "generate per-function checksums"),
 	OPT_BOOLEAN(0,		 "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
+	OPT_STRING_OPTARG('d',	 "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
 	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
 	OPT_BOOLEAN('i',	 "ibt", &opts.ibt, "validate and annotate IBT"),
 	OPT_BOOLEAN('m',	 "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
@@ -176,6 +177,7 @@ static bool opts_valid(void)
 	}
 
 	if (opts.checksum		||
+	    opts.disas			||
 	    opts.hack_jump_label	||
 	    opts.hack_noinstr		||
 	    opts.ibt			||
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0da86834ab2ab..5bb932f211f6b 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2632,7 +2632,7 @@ static int decode_sections(struct objtool_file *file)
 	 * Must be before add_jump_destinations(), which depends on 'func'
 	 * being set for alternatives, to enable proper sibling call detection.
 	 */
-	if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label) {
+	if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label || opts.disas) {
 		if (add_special_section_alts(file))
 			return -1;
 	}
@@ -4967,14 +4967,15 @@ int check(struct objtool_file *file)
 	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.
+	 * Create a disassembly context if we might disassemble any
+	 * instruction or function.
 	 */
-	if (opts.verbose || opts.backtrace || opts.trace) {
+	if (opts.verbose || opts.backtrace || opts.trace || opts.disas) {
 		disas_ctx = disas_context_create(file);
-		if (!disas_ctx)
+		if (!disas_ctx) {
+			opts.disas = false;
 			opts.trace = false;
+		}
 		objtool_disas_ctx = disas_ctx;
 	}
 
@@ -5108,20 +5109,20 @@ int check(struct objtool_file *file)
 	}
 
 out:
-	if (!ret && !warnings) {
-		free_insns(file);
-		return 0;
-	}
-
-	if (opts.werror && warnings)
-		ret = 1;
-
-	if (opts.verbose) {
+	if (ret || warnings) {
 		if (opts.werror && warnings)
-			WARN("%d warning(s) upgraded to errors", warnings);
-		disas_warned_funcs(disas_ctx);
+			ret = 1;
+
+		if (opts.verbose) {
+			if (opts.werror && warnings)
+				WARN("%d warning(s) upgraded to errors", warnings);
+			disas_warned_funcs(disas_ctx);
+		}
 	}
 
+	if (opts.disas)
+		disas_funcs(disas_ctx);
+
 	if (disas_ctx) {
 		disas_context_destroy(disas_ctx);
 		objtool_disas_ctx = NULL;
@@ -5129,6 +5130,9 @@ int check(struct objtool_file *file)
 
 	free_insns(file);
 
+	if (!ret && !warnings)
+		return 0;
+
 	if (opts.backup && make_backup())
 		return 1;
 
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index b53be240825da..9cc952e03c356 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -4,6 +4,7 @@
  */
 
 #define _GNU_SOURCE
+#include <fnmatch.h>
 
 #include <objtool/arch.h>
 #include <objtool/check.h>
@@ -556,3 +557,29 @@ void disas_warned_funcs(struct disas_context *dctx)
 			disas_func(dctx, sym);
 	}
 }
+
+void disas_funcs(struct disas_context *dctx)
+{
+	bool disas_all = !strcmp(opts.disas, "*");
+	struct section *sec;
+	struct symbol *sym;
+
+	for_each_sec(dctx->file->elf, sec) {
+
+		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
+			continue;
+
+		sec_for_each_sym(sec, sym) {
+			/*
+			 * If the function had a warning and the verbose
+			 * option is used then the function was already
+			 * disassemble.
+			 */
+			if (opts.verbose && sym->warned)
+				continue;
+
+			if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0)
+				disas_func(dctx, sym);
+		}
+	}
+}
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index 991365c10f0e9..e3af664864f30 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -28,6 +28,7 @@ struct opts {
 	bool static_call;
 	bool uaccess;
 	int prefix;
+	const char *disas;
 
 	/* options: */
 	bool backtrace;
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 8959d4c455622..e8f395eff1598 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -15,6 +15,7 @@ struct disassemble_info;
 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);
+void disas_funcs(struct disas_context *dctx);
 int disas_info_init(struct disassemble_info *dinfo,
 		    int arch, int mach32, int mach64,
 		    const char *options);
@@ -40,6 +41,7 @@ static inline struct disas_context *disas_context_create(struct objtool_file *fi
 
 static inline void disas_context_destroy(struct disas_context *dctx) {}
 static inline void disas_warned_funcs(struct disas_context *dctx) {}
+static inline void disas_funcs(struct disas_context *dctx) {}
 
 static inline int disas_info_init(struct disassemble_info *dinfo,
 				  int arch, int mach32, int mach64,
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 18/30] objtool: Preserve alternatives order
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (16 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 17/30] objtool: Add the --disas=<function-pattern> action Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 19/30] objtool: Print headers for alternatives Alexandre Chartre
                   ` (13 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Preserve the order in which alternatives are defined. Currently
objtool stores alternatives in a list in reverse order.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 5bb932f211f6b..25839c3950a3c 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1942,6 +1942,7 @@ static int add_special_section_alts(struct objtool_file *file)
 	struct special_alt *special_alt, *tmp;
 	enum alternative_type alt_type;
 	struct alternative *alt;
+	struct alternative *a;
 
 	if (special_get_alts(file->elf, &special_alts))
 		return -1;
@@ -1994,9 +1995,20 @@ static int add_special_section_alts(struct objtool_file *file)
 		}
 
 		alt->insn = new_insn;
-		alt->next = orig_insn->alts;
 		alt->type = alt_type;
-		orig_insn->alts = alt;
+		alt->next = NULL;
+
+		/*
+		 * Store alternatives in the same order they have been
+		 * defined.
+		 */
+		if (!orig_insn->alts) {
+			orig_insn->alts = alt;
+		} else {
+			for (a = orig_insn->alts; a->next; a = a->next)
+				;
+			a->next = alt;
+		}
 
 		list_del(&special_alt->list);
 		free(special_alt);
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 19/30] objtool: Print headers for alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (17 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 18/30] objtool: Preserve alternatives order Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 20/30] objtool: Disassemble group alternatives Alexandre Chartre
                   ` (12 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When using the --disas option, objtool doesn't currently disassemble
any alternative. Print an header for each alternative. This identifies
places where alternatives are present but alternative code is still
not disassembled at the moment.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 188 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 182 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 9cc952e03c356..f9b13d56acab7 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -29,6 +29,43 @@ struct disas_context {
 	struct disassemble_info info;
 };
 
+/*
+ * Maximum number of alternatives
+ */
+#define DISAS_ALT_MAX		5
+
+/*
+ * Maximum number of instructions per alternative
+ */
+#define DISAS_ALT_INSN_MAX	50
+
+/*
+ * Information to disassemble an alternative
+ */
+struct disas_alt {
+	struct instruction *orig_insn;		/* original instruction */
+	struct alternative *alt;		/* alternative or NULL if default code */
+	char *name;				/* name for this alternative */
+	int width;				/* formatting width */
+};
+
+/*
+ * Wrapper around asprintf() to allocate and format a string.
+ * Return the allocated string or NULL on error.
+ */
+static char *strfmt(const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+	int rv;
+
+	va_start(ap, fmt);
+	rv = vasprintf(&str, fmt, ap);
+	va_end(ap);
+
+	return rv == -1 ? NULL : str;
+}
+
 static int sprint_name(char *str, const char *name, unsigned long offset)
 {
 	int len;
@@ -314,6 +351,9 @@ char *disas_result(struct disas_context *dctx)
 #define DISAS_INSN_OFFSET_SPACE		10
 #define DISAS_INSN_SPACE		60
 
+#define DISAS_PRINSN(dctx, insn, depth)			\
+	disas_print_insn(stdout, dctx, insn, depth, "\n")
+
 /*
  * Print a message in the instruction flow. If insn is not NULL then
  * the instruction address is printed in addition of the message,
@@ -354,6 +394,19 @@ static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
 	return n;
 }
 
+static int disas_print(FILE *stream, struct section *sec, unsigned long offset,
+			int depth, const char *format, ...)
+{
+	va_list args;
+	int len;
+
+	va_start(args, format);
+	len = disas_vprint(stream, sec, offset, depth, format, args);
+	va_end(args);
+
+	return len;
+}
+
 /*
  * Print a message in the instruction flow. If insn is not NULL then
  * the instruction address is printed in addition of the message,
@@ -523,21 +576,144 @@ char *disas_alt_name(struct alternative *alt)
 	return str;
 }
 
+/*
+ * Initialize an alternative. The default alternative should be initialized
+ * with alt=NULL.
+ */
+static int disas_alt_init(struct disas_alt *dalt,
+			  struct instruction *orig_insn,
+			  struct alternative *alt)
+{
+	dalt->orig_insn = orig_insn;
+	dalt->alt = alt;
+	dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT");
+	if (!dalt->name)
+		return -1;
+	dalt->width = strlen(dalt->name);
+
+	return 0;
+}
+
+/*
+ * Print all alternatives one above the other.
+ */
+static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
+				    int alt_count)
+{
+	struct instruction *orig_insn;
+	int len;
+	int i;
+
+	orig_insn = dalts[0].orig_insn;
+
+	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
+	printf("%s\n", alt_name);
+
+	for (i = 0; i < alt_count; i++)
+		printf("%*s= %s\n", len, "", dalts[i].name);
+}
+
+/*
+ * Disassemble an alternative.
+ *
+ * Return the last instruction in the default alternative so that
+ * disassembly can continue with the next instruction. Return NULL
+ * on error.
+ */
+static void *disas_alt(struct disas_context *dctx,
+		       struct instruction *orig_insn)
+{
+	struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
+	struct alternative *alt;
+	int alt_count = 0;
+	char *alt_name;
+	int err;
+	int i;
+
+	alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
+			  orig_insn->offset);
+	if (!alt_name) {
+		WARN("Failed to define name for alternative at instruction 0x%lx",
+		     orig_insn->offset);
+		goto done;
+	}
+
+	/*
+	 * Initialize the default alternative.
+	 */
+	err = disas_alt_init(&dalts[0], orig_insn, NULL);
+	if (err) {
+		WARN("%s: failed to initialize default alternative", alt_name);
+		goto done;
+	}
+
+	/*
+	 * Initialize all other alternatives.
+	 */
+	i = 1;
+	for (alt = orig_insn->alts; alt; alt = alt->next) {
+		if (i >= DISAS_ALT_MAX) {
+			WARN("%s has more alternatives than supported", alt_name);
+			break;
+		}
+		err = disas_alt_init(&dalts[i], orig_insn, alt);
+		if (err) {
+			WARN("%s: failed to disassemble alternative", alt_name);
+			goto done;
+		}
+
+		i++;
+	}
+	alt_count = i;
+
+	/*
+	 * Print default and non-default alternatives.
+	 *
+	 * At the moment, this just prints an header for each alternative.
+	 */
+	disas_alt_print_compact(alt_name, dalts, alt_count);
+
+done:
+	for (i = 0; i < alt_count; i++)
+		free(dalts[i].name);
+
+	free(alt_name);
+
+	/*
+	 * Currently we are not disassembling any alternative but just
+	 * printing alternative names. Return NULL to have disas_func()
+	 * resume the disassembly with the default alternative.
+	 */
+	return NULL;
+}
+
 /*
  * Disassemble a function.
  */
 static void disas_func(struct disas_context *dctx, struct symbol *func)
 {
+	struct instruction *insn_start;
 	struct instruction *insn;
-	size_t addr;
 
 	printf("%s:\n", func->name);
 	sym_for_each_insn(dctx->file, func, insn) {
-		addr = insn->offset;
-		disas_insn(dctx, insn);
-		printf(" %6lx:  %s+0x%-6lx      %s\n",
-		       addr, func->name, addr - func->offset,
-		       disas_result(dctx));
+		if (insn->alts) {
+			insn_start = insn;
+			insn = disas_alt(dctx, insn);
+			if (insn)
+				continue;
+			/*
+			 * There was an error with disassembling
+			 * the alternative. Resume disassembling
+			 * at the current instruction, this will
+			 * disassemble the default alternative
+			 * only and continue with the code after
+			 * the alternative.
+			 */
+			insn = insn_start;
+		}
+
+		DISAS_PRINSN(dctx, insn, 0);
 	}
 	printf("\n");
 }
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 20/30] objtool: Disassemble group alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (18 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 19/30] objtool: Print headers for alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 21/30] objtool: Print addresses with alternative instructions Alexandre Chartre
                   ` (11 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When using the --disas option, disassemble all group alternatives.
Jump tables and exception tables (which are handled as alternatives)
are not disassembled at the moment.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 166 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 149 insertions(+), 17 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f9b13d56acab7..ae69bef2eb37e 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -47,8 +47,14 @@ struct disas_alt {
 	struct alternative *alt;		/* alternative or NULL if default code */
 	char *name;				/* name for this alternative */
 	int width;				/* formatting width */
+	char *insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
 };
 
+#define DALT_DEFAULT(dalt)	(!(dalt)->alt)
+#define DALT_INSN(dalt)		(DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn)
+#define DALT_GROUP(dalt)	(DALT_INSN(dalt)->alt_group)
+#define DALT_ALTID(dalt)	((dalt)->orig_insn->offset)
+
 /*
  * Wrapper around asprintf() to allocate and format a string.
  * Return the allocated string or NULL on error.
@@ -506,6 +512,21 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 	return disasm(insn->offset, &dctx->info);
 }
 
+static struct instruction *next_insn_same_alt(struct objtool_file *file,
+					      struct alt_group *alt_grp,
+					      struct instruction *insn)
+{
+	if (alt_grp->last_insn == insn || alt_grp->nop == insn)
+		return NULL;
+
+	return next_insn_same_sec(file, insn);
+}
+
+#define alt_for_each_insn(file, alt_grp, insn)			\
+	for (insn = alt_grp->first_insn; 			\
+	     insn;						\
+	     insn = next_insn_same_alt(file, alt_grp, insn))
+
 /*
  * Provide a name for the type of alternatives present at the
  * specified instruction.
@@ -594,23 +615,107 @@ static int disas_alt_init(struct disas_alt *dalt,
 	return 0;
 }
 
+static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
+{
+	int len;
+
+	if (index >= DISAS_ALT_INSN_MAX) {
+		WARN("Alternative %lx.%s has more instructions than supported",
+		     DALT_ALTID(dalt), dalt->name);
+		return -1;
+	}
+
+	len = strlen(insn_str);
+	dalt->insn[index] = insn_str;
+	if (len > dalt->width)
+		dalt->width = len;
+
+	return 0;
+}
+
+/*
+ * Disassemble an alternative and store instructions in the disas_alt
+ * structure. Return the number of instructions in the alternative.
+ */
+static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
+{
+	struct objtool_file *file;
+	struct instruction *insn;
+	char *str;
+	int count;
+	int err;
+
+	file = dctx->file;
+	count = 0;
+
+	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
+
+		disas_insn(dctx, insn);
+		str = strdup(disas_result(dctx));
+		if (!str)
+			return -1;
+
+		err = disas_alt_add_insn(dalt, count, str);
+		if (err)
+			break;
+		count++;
+	}
+
+	return count;
+}
+
+/*
+ * Disassemble the default alternative.
+ */
+static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
+{
+	char *str;
+	int err;
+
+	if (DALT_GROUP(dalt))
+		return disas_alt_group(dctx, dalt);
+
+	/*
+	 * Default alternative with no alt_group: this is the default
+	 * code associated with either a jump table or an exception
+	 * table and no other instruction alternatives. In that case
+	 * the default alternative is made of a single instruction.
+	 */
+	disas_insn(dctx, dalt->orig_insn);
+	str = strdup(disas_result(dctx));
+	if (!str)
+		return -1;
+	err = disas_alt_add_insn(dalt, 0, str);
+	if (err)
+		return -1;
+
+	return 1;
+}
+
 /*
  * Print all alternatives one above the other.
  */
 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
-				    int alt_count)
+				    int alt_count, int insn_count)
 {
 	struct instruction *orig_insn;
+	int i, j;
 	int len;
-	int i;
 
 	orig_insn = dalts[0].orig_insn;
 
 	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
 	printf("%s\n", alt_name);
 
-	for (i = 0; i < alt_count; i++)
+	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
+		for (j = 0; j < insn_count; j++) {
+			if (!dalts[i].insn[j])
+				break;
+			printf("%*s| %s\n", len, "", dalts[i].insn[j]);
+		}
+		printf("%*s|\n", len, "");
+	}
 }
 
 /*
@@ -624,11 +729,15 @@ static void *disas_alt(struct disas_context *dctx,
 		       struct instruction *orig_insn)
 {
 	struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
+	struct instruction *last_insn = NULL;
 	struct alternative *alt;
+	struct disas_alt *dalt;
+	int insn_count = 0;
 	int alt_count = 0;
 	char *alt_name;
+	int count;
+	int i, j;
 	int err;
-	int i;
 
 	alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
 			  orig_insn->offset);
@@ -639,7 +748,7 @@ static void *disas_alt(struct disas_context *dctx,
 	}
 
 	/*
-	 * Initialize the default alternative.
+	 * Initialize and disassemble the default alternative.
 	 */
 	err = disas_alt_init(&dalts[0], orig_insn, NULL);
 	if (err) {
@@ -647,8 +756,14 @@ static void *disas_alt(struct disas_context *dctx,
 		goto done;
 	}
 
+	insn_count = disas_alt_default(dctx, &dalts[0]);
+	if (insn_count < 0) {
+		WARN("%s: failed to disassemble default alternative", alt_name);
+		goto done;
+	}
+
 	/*
-	 * Initialize all other alternatives.
+	 * Initialize and disassemble all other alternatives.
 	 */
 	i = 1;
 	for (alt = orig_insn->alts; alt; alt = alt->next) {
@@ -656,35 +771,52 @@ static void *disas_alt(struct disas_context *dctx,
 			WARN("%s has more alternatives than supported", alt_name);
 			break;
 		}
-		err = disas_alt_init(&dalts[i], orig_insn, alt);
+		dalt = &dalts[i];
+		err = disas_alt_init(dalt, orig_insn, alt);
 		if (err) {
 			WARN("%s: failed to disassemble alternative", alt_name);
 			goto done;
 		}
 
+		/*
+		 * Only group alternatives are supported at the moment.
+		 */
+		switch (dalt->alt->type) {
+		case ALT_TYPE_INSTRUCTIONS:
+			count = disas_alt_group(dctx, dalt);
+			break;
+		default:
+			count = 0;
+		}
+		if (count < 0) {
+			WARN("%s: failed to disassemble alternative %s",
+			     alt_name, dalt->name);
+			goto done;
+		}
+
+		insn_count = count > insn_count ? count : insn_count;
 		i++;
 	}
 	alt_count = i;
 
 	/*
 	 * Print default and non-default alternatives.
-	 *
-	 * At the moment, this just prints an header for each alternative.
 	 */
-	disas_alt_print_compact(alt_name, dalts, alt_count);
+	disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
+
+	last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
+		orig_insn;
 
 done:
-	for (i = 0; i < alt_count; i++)
+	for (i = 0; i < alt_count; i++) {
 		free(dalts[i].name);
+		for (j = 0; j < insn_count; j++)
+			free(dalts[i].insn[j]);
+	}
 
 	free(alt_name);
 
-	/*
-	 * Currently we are not disassembling any alternative but just
-	 * printing alternative names. Return NULL to have disas_func()
-	 * resume the disassembly with the default alternative.
-	 */
-	return NULL;
+	return last_insn;
 }
 
 /*
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 21/30] objtool: Print addresses with alternative instructions
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (19 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 20/30] objtool: Disassemble group alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 22/30] objtool: Disassemble exception table alternatives Alexandre Chartre
                   ` (10 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

All alternatives are disassemble side-by-side when using the --disas
option. However the address of each instruction is not printed because
instructions from different alternatives are not necessarily aligned.

Change this behavior to print the address of each instruction. Spaces
will appear between instructions from the same alternative when
instructions from different alternatives do not have the same alignment.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 35 +++++++++++++++++++++++------------
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index ae69bef2eb37e..6083a64f6ae49 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -47,7 +47,11 @@ struct disas_alt {
 	struct alternative *alt;		/* alternative or NULL if default code */
 	char *name;				/* name for this alternative */
 	int width;				/* formatting width */
-	char *insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
+	struct {
+		char *str;			/* instruction string */
+		int offset;			/* instruction offset */
+	} insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
+	int insn_idx;				/* index of the next instruction to print */
 };
 
 #define DALT_DEFAULT(dalt)	(!(dalt)->alt)
@@ -361,10 +365,9 @@ char *disas_result(struct disas_context *dctx)
 	disas_print_insn(stdout, dctx, insn, depth, "\n")
 
 /*
- * Print a message in the instruction flow. If insn is not NULL then
- * the instruction address is printed in addition of the message,
- * otherwise only the message is printed. In all cases, the instruction
- * itself is not printed.
+ * Print a message in the instruction flow. If sec is not NULL then the
+ * address at the section offset is printed in addition of the message,
+ * otherwise only the message is printed.
  */
 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
 			int depth, const char *format, va_list ap)
@@ -607,6 +610,7 @@ static int disas_alt_init(struct disas_alt *dalt,
 {
 	dalt->orig_insn = orig_insn;
 	dalt->alt = alt;
+	dalt->insn_idx = 0;
 	dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT");
 	if (!dalt->name)
 		return -1;
@@ -615,7 +619,8 @@ static int disas_alt_init(struct disas_alt *dalt,
 	return 0;
 }
 
-static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
+static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
+			      int offset)
 {
 	int len;
 
@@ -626,7 +631,8 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
 	}
 
 	len = strlen(insn_str);
-	dalt->insn[index] = insn_str;
+	dalt->insn[index].str = insn_str;
+	dalt->insn[index].offset = offset;
 	if (len > dalt->width)
 		dalt->width = len;
 
@@ -641,12 +647,14 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 {
 	struct objtool_file *file;
 	struct instruction *insn;
+	int offset;
 	char *str;
 	int count;
 	int err;
 
 	file = dctx->file;
 	count = 0;
+	offset = 0;
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
@@ -655,9 +663,10 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 		if (!str)
 			return -1;
 
-		err = disas_alt_add_insn(dalt, count, str);
+		err = disas_alt_add_insn(dalt, count, str, offset);
 		if (err)
 			break;
+		offset += insn->len;
 		count++;
 	}
 
@@ -685,7 +694,7 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	str = strdup(disas_result(dctx));
 	if (!str)
 		return -1;
-	err = disas_alt_add_insn(dalt, 0, str);
+	err = disas_alt_add_insn(dalt, 0, str, 0);
 	if (err)
 		return -1;
 
@@ -710,9 +719,11 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
 		for (j = 0; j < insn_count; j++) {
-			if (!dalts[i].insn[j])
+			if (!dalts[i].insn[j].str)
 				break;
-			printf("%*s| %s\n", len, "", dalts[i].insn[j]);
+			disas_print(stdout, orig_insn->sec,
+				    orig_insn->offset + dalts[i].insn[j].offset, 0,
+				    "| %s\n", dalts[i].insn[j].str);
 		}
 		printf("%*s|\n", len, "");
 	}
@@ -811,7 +822,7 @@ static void *disas_alt(struct disas_context *dctx,
 	for (i = 0; i < alt_count; i++) {
 		free(dalts[i].name);
 		for (j = 0; j < insn_count; j++)
-			free(dalts[i].insn[j]);
+			free(dalts[i].insn[j].str);
 	}
 
 	free(alt_name);
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 22/30] objtool: Disassemble exception table alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (20 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 21/30] objtool: Print addresses with alternative instructions Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 23/30] objtool: Disassemble jump " Alexandre Chartre
                   ` (9 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When using the --disas option, also disassemble exception tables
(EX_TABLE).

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 6083a64f6ae49..018aba37b996b 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -639,6 +639,26 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	return 0;
 }
 
+/*
+ * Disassemble an exception table alternative.
+ */
+static int disas_alt_extable(struct disas_alt *dalt)
+{
+	struct instruction *alt_insn;
+	char *str;
+
+	alt_insn = dalt->alt->insn;
+	str = strfmt("resume at 0x%lx <%s+0x%lx>",
+		     alt_insn->offset, alt_insn->sym->name,
+		     alt_insn->offset - alt_insn->sym->offset);
+	if (!str)
+		return -1;
+
+	disas_alt_add_insn(dalt, 0, str, 0);
+
+	return 1;
+}
+
 /*
  * Disassemble an alternative and store instructions in the disas_alt
  * structure. Return the number of instructions in the alternative.
@@ -790,12 +810,16 @@ static void *disas_alt(struct disas_context *dctx,
 		}
 
 		/*
-		 * Only group alternatives are supported at the moment.
+		 * Only group alternatives and exception tables are
+		 * supported at the moment.
 		 */
 		switch (dalt->alt->type) {
 		case ALT_TYPE_INSTRUCTIONS:
 			count = disas_alt_group(dctx, dalt);
 			break;
+		case ALT_TYPE_EX_TABLE:
+			count = disas_alt_extable(dalt);
+			break;
 		default:
 			count = 0;
 		}
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 23/30] objtool: Disassemble jump table alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (21 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 22/30] objtool: Disassemble exception table alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 24/30] objtool: Fix address references in alternatives Alexandre Chartre
                   ` (8 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When using the --disas option, also disassemble jump tables.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 38 ++++++++++++++++++++++++++++++++------
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 018aba37b996b..326e16c9f30a8 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -639,6 +639,34 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	return 0;
 }
 
+static int disas_alt_jump(struct disas_alt *dalt)
+{
+	struct instruction *orig_insn;
+	struct instruction *dest_insn;
+	char suffix[2] = { 0 };
+	char *str;
+
+	orig_insn = dalt->orig_insn;
+	dest_insn = dalt->alt->insn;
+
+	if (orig_insn->type == INSN_NOP) {
+		if (orig_insn->len == 5)
+			suffix[0] = 'q';
+		str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
+			     dest_insn->offset, dest_insn->sym->name,
+			     dest_insn->offset - dest_insn->sym->offset);
+	} else {
+		str = strfmt("nop%d", orig_insn->len);
+	}
+
+	if (!str)
+		return -1;
+
+	disas_alt_add_insn(dalt, 0, str, 0);
+
+	return 1;
+}
+
 /*
  * Disassemble an exception table alternative.
  */
@@ -809,10 +837,7 @@ static void *disas_alt(struct disas_context *dctx,
 			goto done;
 		}
 
-		/*
-		 * Only group alternatives and exception tables are
-		 * supported at the moment.
-		 */
+		count = -1;
 		switch (dalt->alt->type) {
 		case ALT_TYPE_INSTRUCTIONS:
 			count = disas_alt_group(dctx, dalt);
@@ -820,8 +845,9 @@ static void *disas_alt(struct disas_context *dctx,
 		case ALT_TYPE_EX_TABLE:
 			count = disas_alt_extable(dalt);
 			break;
-		default:
-			count = 0;
+		case ALT_TYPE_JUMP_TABLE:
+			count = disas_alt_jump(dalt);
+			break;
 		}
 		if (count < 0) {
 			WARN("%s: failed to disassemble alternative %s",
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 24/30] objtool: Fix address references in alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (22 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 23/30] objtool: Disassemble jump " Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 25/30] objtool: Provide access to feature and flags of group alternatives Alexandre Chartre
                   ` (7 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When using the --disas option, alternatives are disassembled but
address references in non-default alternatives can be incorrect.

The problem is that alternatives are shown as if they were replacing the
original code of the alternative. So if an alternative is referencing
an address inside the alternative then the reference has to be
adjusted to the location of the original code.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 70 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 326e16c9f30a8..f8917c8405d32 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -24,6 +24,7 @@
 struct disas_context {
 	struct objtool_file *file;
 	struct instruction *insn;
+	bool alt_applied;
 	char result[DISAS_RESULT_SIZE];
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
@@ -160,6 +161,43 @@ static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
 	}
 }
 
+static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *orig_first_insn;
+	struct alt_group *alt_group;
+	unsigned long offset;
+	struct symbol *sym;
+
+	/*
+	 * Check if we are processing an alternative at the original
+	 * instruction address (i.e. if alt_applied is true) and if
+	 * we are referencing an address inside the alternative.
+	 *
+	 * For example, this happens if there is a branch inside an
+	 * alternative. In that case, the address should be updated
+	 * to a reference inside the original instruction flow.
+	 */
+	if (!dctx->alt_applied)
+		return false;
+
+	alt_group = dctx->insn->alt_group;
+	if (!alt_group || !alt_group->orig_group ||
+	    addr < alt_group->first_insn->offset ||
+	    addr > alt_group->last_insn->offset)
+		return false;
+
+	orig_first_insn = alt_group->orig_group->first_insn;
+	offset = addr - alt_group->first_insn->offset;
+
+	addr = orig_first_insn->offset + offset;
+	sym = orig_first_insn->sym;
+
+	disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo);
+
+	return true;
+}
+
 static void disas_print_addr_noreloc(bfd_vma addr,
 				     struct disassemble_info *dinfo)
 {
@@ -167,6 +205,9 @@ static void disas_print_addr_noreloc(bfd_vma addr,
 	struct instruction *insn = dctx->insn;
 	struct symbol *sym = NULL;
 
+	if (disas_print_addr_alt(addr, dinfo))
+		return;
+
 	if (insn->sym && addr >= insn->sym->offset &&
 	    addr < insn->sym->offset + insn->sym->len) {
 		sym = insn->sym;
@@ -232,8 +273,9 @@ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
 	 */
 	jump_dest = insn->jump_dest;
 	if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
-		disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
-				     addr, dinfo);
+		if (!disas_print_addr_alt(addr, dinfo))
+			disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
+					     addr, dinfo);
 		return;
 	}
 
@@ -490,13 +532,22 @@ void disas_print_insn(FILE *stream, struct disas_context *dctx,
 
 /*
  * Disassemble a single instruction. Return the size of the instruction.
+ *
+ * If alt_applied is true then insn should be an instruction from of an
+ * alternative (i.e. insn->alt_group != NULL), and it is disassembled
+ * at the location of the original code it is replacing. When the
+ * instruction references any address inside the alternative then
+ * these references will be re-adjusted to replace the original code.
  */
-size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
+static size_t disas_insn_common(struct disas_context *dctx,
+				struct instruction *insn,
+				bool alt_applied)
 {
 	disassembler_ftype disasm = dctx->disassembler;
 	struct disassemble_info *dinfo = &dctx->info;
 
 	dctx->insn = insn;
+	dctx->alt_applied = alt_applied;
 	dctx->result[0] = '\0';
 
 	if (insn->type == INSN_NOP) {
@@ -515,6 +566,17 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 	return disasm(insn->offset, &dctx->info);
 }
 
+size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
+{
+	return disas_insn_common(dctx, insn, false);
+}
+
+static size_t disas_insn_alt(struct disas_context *dctx,
+			     struct instruction *insn)
+{
+	return disas_insn_common(dctx, insn, true);
+}
+
 static struct instruction *next_insn_same_alt(struct objtool_file *file,
 					      struct alt_group *alt_grp,
 					      struct instruction *insn)
@@ -706,7 +768,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
-		disas_insn(dctx, insn);
+		disas_insn_alt(dctx, insn);
 		str = strdup(disas_result(dctx));
 		if (!str)
 			return -1;
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 25/30] objtool: Provide access to feature and flags of group alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (23 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 24/30] objtool: Fix address references in alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-21  9:53 ` [PATCH v6 26/30] objtool: Function to get the name of a CPU feature Alexandre Chartre
                   ` (6 subsequent siblings)
  31 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Each alternative of a group alternative depends on a specific
feature and flags. Provide access to the feature/flags for each
alternative as an attribute (feature) in struct alt_group.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/check.c                   | 2 ++
 tools/objtool/include/objtool/check.h   | 1 +
 tools/objtool/include/objtool/special.h | 2 +-
 tools/objtool/special.c                 | 2 ++
 4 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 25839c3950a3c..ddc5ec74d9c99 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1772,6 +1772,7 @@ static int handle_group_alt(struct objtool_file *file,
 		orig_alt_group->last_insn = last_orig_insn;
 		orig_alt_group->nop = NULL;
 		orig_alt_group->ignore = orig_insn->ignore_alts;
+		orig_alt_group->feature = 0;
 	} else {
 		if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
 		    orig_alt_group->first_insn->offset != special_alt->orig_len) {
@@ -1876,6 +1877,7 @@ static int handle_group_alt(struct objtool_file *file,
 	new_alt_group->nop = nop;
 	new_alt_group->ignore = (*new_insn)->ignore_alts;
 	new_alt_group->cfi = orig_alt_group->cfi;
+	new_alt_group->feature = special_alt->feature;
 	return 0;
 }
 
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index cbf4af58e29b2..2e1346ad5e926 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -36,6 +36,7 @@ struct alt_group {
 	struct cfi_state **cfi;
 
 	bool ignore;
+	unsigned int feature;
 };
 
 enum alternative_type {
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index 72d09c0adf1a1..b22410745e4a1 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -25,7 +25,7 @@ struct special_alt {
 	struct section *new_sec;
 	unsigned long new_off;
 
-	unsigned int orig_len, new_len; /* group only */
+	unsigned int orig_len, new_len, feature; /* group only */
 };
 
 int special_get_alts(struct elf *elf, struct list_head *alts);
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index e262af9171436..2a533afbc69aa 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -81,6 +81,8 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
 						   entry->orig_len);
 		alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
 						  entry->new_len);
+		alt->feature = *(unsigned int *)(sec->data->d_buf + offset +
+						 entry->feature);
 	}
 
 	orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 26/30] objtool: Function to get the name of a CPU feature
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (24 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 25/30] objtool: Provide access to feature and flags of group alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
                     ` (2 more replies)
  2025-11-21  9:53 ` [PATCH v6 27/30] objtool: Improve naming of group alternatives Alexandre Chartre
                   ` (5 subsequent siblings)
  31 siblings, 3 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Add a function to get the name of a CPU feature. The function is
architecture dependent and currently only implemented for x86. The
feature names are automatically generated from the cpufeatures.h
include file.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 .../x86/tools/gen-cpu-feature-names-x86.awk   | 33 +++++++++++++++++++
 tools/objtool/.gitignore                      |  1 +
 tools/objtool/Makefile                        |  1 +
 tools/objtool/arch/loongarch/special.c        |  5 +++
 tools/objtool/arch/powerpc/special.c          |  5 +++
 tools/objtool/arch/x86/Build                  |  8 +++++
 tools/objtool/arch/x86/special.c              | 10 ++++++
 tools/objtool/include/objtool/special.h       |  2 ++
 8 files changed, 65 insertions(+)
 create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk

diff --git a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
new file mode 100644
index 0000000000000..1b1c1d84225c2
--- /dev/null
+++ b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
@@ -0,0 +1,33 @@
+#!/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Usage: awk -f gen-cpu-feature-names-x86.awk cpufeatures.h > cpu-feature-names.c
+#
+
+BEGIN {
+	print "/* cpu feature name array generated from cpufeatures.h */"
+	print "/* Do not change this code. */"
+	print
+	print "static const char *cpu_feature_names[(NCAPINTS+NBUGINTS)*32] = {"
+
+	feature_expr = "(X86_FEATURE_[A-Z0-9_]+)\\s+\\(([0-9*+ ]+)\\)"
+	debug_expr = "(X86_BUG_[A-Z0-9_]+)\\s+X86_BUG\\(([0-9*+ ]+)\\)"
+}
+
+/^#define X86_FEATURE_/ {
+	if (match($0, feature_expr, m)) {
+		print "\t[" m[2] "] = \"" m[1] "\","
+	}
+}
+
+/^#define X86_BUG_/ {
+	if (match($0, debug_expr, m)) {
+		print "\t[NCAPINTS*32+(" m[2] ")] = \"" m[1] "\","
+	}
+}
+
+END {
+	print "};"
+}
diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 759303657bd7c..73d883128511f 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+arch/x86/lib/cpu-feature-names.c
 arch/x86/lib/inat-tables.c
 /objtool
 feature
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index df793ca6fc1a1..66397d755fe4b 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -125,6 +125,7 @@ $(LIBSUBCMD)-clean:
 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
diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c
index a80b75f7b061f..aba774109437f 100644
--- a/tools/objtool/arch/loongarch/special.c
+++ b/tools/objtool/arch/loongarch/special.c
@@ -194,3 +194,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/powerpc/special.c b/tools/objtool/arch/powerpc/special.c
index 51610689abf72..8f9bf61ca0899 100644
--- a/tools/objtool/arch/powerpc/special.c
+++ b/tools/objtool/arch/powerpc/special.c
@@ -18,3 +18,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 {
 	exit(-1);
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build
index 3dedb2fd8f3a0..1067355361b56 100644
--- a/tools/objtool/arch/x86/Build
+++ b/tools/objtool/arch/x86/Build
@@ -11,4 +11,12 @@ $(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
 
 $(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
 
+cpu_features = ../arch/x86/include/asm/cpufeatures.h
+cpu_features_script = ../arch/x86/tools/gen-cpu-feature-names-x86.awk
+
+$(OUTPUT)arch/x86/lib/cpu-feature-names.c: $(cpu_features_script) $(cpu_features)
+	$(Q)$(call echo-cmd,gen)$(AWK) -f $(cpu_features_script) $(cpu_features) > $@
+
+$(OUTPUT)arch/x86/special.o: $(OUTPUT)arch/x86/lib/cpu-feature-names.c
+
 CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index 09300761f1085..b6b40c56da896 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -4,6 +4,10 @@
 #include <objtool/special.h>
 #include <objtool/builtin.h>
 #include <objtool/warn.h>
+#include <asm/cpufeatures.h>
+
+/* cpu feature name array generated from cpufeatures.h */
+#include "lib/cpu-feature-names.c"
 
 void arch_handle_alternative(struct special_alt *alt)
 {
@@ -134,3 +138,9 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 	*table_size = 0;
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return (feature_number < ARRAY_SIZE(cpu_feature_names)) ?
+		cpu_feature_names[feature_number] : NULL;
+}
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index b22410745e4a1..121c3761899c1 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -38,4 +38,6 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
 struct reloc *arch_find_switch_table(struct objtool_file *file,
 				     struct instruction *insn,
 				     unsigned long *table_size);
+const char *arch_cpu_feature_name(int feature_number);
+
 #endif /* _SPECIAL_H */
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 27/30] objtool: Improve naming of group alternatives
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (25 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 26/30] objtool: Function to get the name of a CPU feature Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
                     ` (2 more replies)
  2025-11-21  9:53 ` [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction Alexandre Chartre
                   ` (4 subsequent siblings)
  31 siblings, 3 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Improve the naming of group alternatives by showing the feature name and
flags used by the alternative.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 58 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f8917c8405d32..731c4495b53c7 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -9,6 +9,7 @@
 #include <objtool/arch.h>
 #include <objtool/check.h>
 #include <objtool/disas.h>
+#include <objtool/special.h>
 #include <objtool/warn.h>
 
 #include <bfd.h>
@@ -60,6 +61,21 @@ struct disas_alt {
 #define DALT_GROUP(dalt)	(DALT_INSN(dalt)->alt_group)
 #define DALT_ALTID(dalt)	((dalt)->orig_insn->offset)
 
+#define ALT_FLAGS_SHIFT		16
+#define ALT_FLAG_NOT		(1 << 0)
+#define ALT_FLAG_DIRECT_CALL	(1 << 1)
+#define ALT_FEATURE_MASK	((1 << ALT_FLAGS_SHIFT) - 1)
+
+static int alt_feature(unsigned int ft_flags)
+{
+	return (ft_flags & ALT_FEATURE_MASK);
+}
+
+static int alt_flags(unsigned int ft_flags)
+{
+	return (ft_flags >> ALT_FLAGS_SHIFT);
+}
+
 /*
  * Wrapper around asprintf() to allocate and format a string.
  * Return the allocated string or NULL on error.
@@ -635,7 +651,12 @@ const char *disas_alt_type_name(struct instruction *insn)
  */
 char *disas_alt_name(struct alternative *alt)
 {
+	char pfx[4] = { 0 };
 	char *str = NULL;
+	const char *name;
+	int feature;
+	int flags;
+	int num;
 
 	switch (alt->type) {
 
@@ -649,13 +670,37 @@ char *disas_alt_name(struct alternative *alt)
 
 	case ALT_TYPE_INSTRUCTIONS:
 		/*
-		 * This is a non-default group alternative. Create a unique
-		 * name using the offset of the first original and alternative
-		 * instructions.
+		 * This is a non-default group alternative. Create a name
+		 * based on the feature and flags associated with this
+		 * alternative. Use either the feature name (it is available)
+		 * or the feature number. And add a prefix to show the flags
+		 * used.
+		 *
+		 * Prefix flags characters:
+		 *
+		 *   '!'  alternative used when feature not enabled
+		 *   '+'  direct call alternative
+		 *   '?'  unknown flag
 		 */
-		asprintf(&str, "ALTERNATIVE %lx.%lx",
-			 alt->insn->alt_group->orig_group->first_insn->offset,
-			 alt->insn->alt_group->first_insn->offset);
+
+		feature = alt->insn->alt_group->feature;
+		num = alt_feature(feature);
+		flags = alt_flags(feature);
+		str = pfx;
+
+		if (flags & ~(ALT_FLAG_NOT | ALT_FLAG_DIRECT_CALL))
+			*str++ = '?';
+		if (flags & ALT_FLAG_DIRECT_CALL)
+			*str++ = '+';
+		if (flags & ALT_FLAG_NOT)
+			*str++ = '!';
+
+		name = arch_cpu_feature_name(num);
+		if (!name)
+			str = strfmt("%sFEATURE 0x%X", pfx, num);
+		else
+			str = strfmt("%s%s", pfx, name);
+
 		break;
 	}
 
@@ -892,6 +937,7 @@ static void *disas_alt(struct disas_context *dctx,
 			WARN("%s has more alternatives than supported", alt_name);
 			break;
 		}
+
 		dalt = &dalts[i];
 		err = disas_alt_init(dalt, orig_insn, alt);
 		if (err) {
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (26 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 27/30] objtool: Improve naming of group alternatives Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
                     ` (2 more replies)
  2025-11-21  9:53 ` [PATCH v6 29/30] objtool: Add wide output for disassembly Alexandre Chartre
                   ` (3 subsequent siblings)
  31 siblings, 3 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When disassembling, if an instruction has alternatives which are all
made of a single instruction then print each alternative on a single
line (instruction + description) so that the output is more compact.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 731c4495b53c7..a4f905eac4e63 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -863,6 +863,7 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 				    int alt_count, int insn_count)
 {
 	struct instruction *orig_insn;
+	int width;
 	int i, j;
 	int len;
 
@@ -871,6 +872,27 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
 	printf("%s\n", alt_name);
 
+	/*
+	 * If all alternatives have a single instruction then print each
+	 * alternative on a single line. Otherwise, print alternatives
+	 * one above the other with a clear separation.
+	 */
+
+	if (insn_count == 1) {
+		width = 0;
+		for (i = 0; i < alt_count; i++) {
+			if (dalts[i].width > width)
+				width = dalts[i].width;
+		}
+
+		for (i = 0; i < alt_count; i++) {
+			printf("%*s= %-*s    (if %s)\n", len, "", width,
+			       dalts[i].insn[0].str, dalts[i].name);
+		}
+
+		return;
+	}
+
 	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
 		for (j = 0; j < insn_count; j++) {
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 29/30] objtool: Add wide output for disassembly
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (27 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
                     ` (2 more replies)
  2025-11-21  9:53 ` [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative Alexandre Chartre
                   ` (2 subsequent siblings)
  31 siblings, 3 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

Add the --wide option to provide a wide output when disassembling.
With this option, the disassembly of alternatives is displayed
side-by-side instead of one above the other.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/builtin-check.c           |  1 +
 tools/objtool/disas.c                   | 95 ++++++++++++++++++++++++-
 tools/objtool/include/objtool/builtin.h |  1 +
 3 files changed, 96 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index a0371312fe55a..b780df5137152 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -107,6 +107,7 @@ static const struct option check_options[] = {
 	OPT_STRING(0,		 "trace", &opts.trace, "func", "trace function validation"),
 	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
 	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
+	OPT_BOOLEAN(0,		 "wide", &opts.wide, "wide output"),
 
 	OPT_END(),
 };
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index a4f905eac4e63..f04bc14bef39e 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -856,6 +856,95 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	return 1;
 }
 
+/*
+ * For each alternative, if there is an instruction at the specified
+ * offset then print this instruction, otherwise print a blank entry.
+ * The offset is an offset from the start of the alternative.
+ *
+ * Return the offset for the next instructions to print, or -1 if all
+ * instructions have been printed.
+ */
+static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count,
+				int insn_count, int offset)
+{
+	struct disas_alt *dalt;
+	int offset_next;
+	char *str;
+	int i, j;
+
+	offset_next = -1;
+
+	for (i = 0; i < alt_count; i++) {
+		dalt = &dalts[i];
+		j = dalt->insn_idx;
+		if (j == -1) {
+			printf("| %-*s ", dalt->width, "");
+			continue;
+		}
+
+		if (dalt->insn[j].offset == offset) {
+			str = dalt->insn[j].str;
+			printf("| %-*s ", dalt->width, str ?: "");
+			if (++j < insn_count) {
+				dalt->insn_idx = j;
+			} else {
+				dalt->insn_idx = -1;
+				continue;
+			}
+		} else {
+			printf("| %-*s ", dalt->width, "");
+		}
+
+		if (dalt->insn[j].offset > 0 &&
+		    (offset_next == -1 ||
+		     (dalt->insn[j].offset < offset_next)))
+			offset_next = dalt->insn[j].offset;
+	}
+	printf("\n");
+
+	return offset_next;
+}
+
+/*
+ * Print all alternatives side-by-side.
+ */
+static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count,
+				 int insn_count)
+{
+	struct instruction *orig_insn;
+	int offset_next;
+	int offset;
+	int i;
+
+	orig_insn = dalts[0].orig_insn;
+
+	/*
+	 * Print an header with the name of each alternative.
+	 */
+	disas_print_info(stdout, orig_insn, -2, NULL);
+
+	if (strlen(alt_name) > dalts[0].width)
+		dalts[0].width = strlen(alt_name);
+	printf("| %-*s ", dalts[0].width, alt_name);
+
+	for (i = 1; i < alt_count; i++)
+		printf("| %-*s ", dalts[i].width, dalts[i].name);
+
+	printf("\n");
+
+	/*
+	 * Print instructions for each alternative.
+	 */
+	offset_next = 0;
+	do {
+		offset = offset_next;
+		disas_print(stdout, orig_insn->sec, orig_insn->offset + offset,
+			    -2, NULL);
+		offset_next = disas_alt_print_insn(dalts, alt_count, insn_count,
+						   offset);
+	} while (offset_next > offset);
+}
+
 /*
  * Print all alternatives one above the other.
  */
@@ -993,7 +1082,11 @@ static void *disas_alt(struct disas_context *dctx,
 	/*
 	 * Print default and non-default alternatives.
 	 */
-	disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
+
+	if (opts.wide)
+		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
+	else
+		disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
 
 	last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
 		orig_insn;
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index e3af664864f30..b9e229ed4dc05 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -45,6 +45,7 @@ struct opts {
 	const char *trace;
 	bool verbose;
 	bool werror;
+	bool wide;
 };
 
 extern struct opts opts;
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (28 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 29/30] objtool: Add wide output for disassembly Alexandre Chartre
@ 2025-11-21  9:53 ` Alexandre Chartre
  2025-11-24  9:10   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
                     ` (2 more replies)
  2025-11-21 10:36 ` [PATCH v6 00/30] objtool: Function validation tracing Peter Zijlstra
  2025-11-24 11:04 ` Borislav Petkov
  31 siblings, 3 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21  9:53 UTC (permalink / raw)
  To: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux
  Cc: alexandre.chartre

When disassembling alternatives replace trailing NOPs with a single
indication of the number of bytes covered with NOPs.

Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
---
 tools/objtool/disas.c | 78 ++++++++++++++++++++++++++++++++++++++++---
 1 file changed, 73 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f04bc14bef39e..441b9306eafcc 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -52,6 +52,7 @@ struct disas_alt {
 	struct {
 		char *str;			/* instruction string */
 		int offset;			/* instruction offset */
+		int nops;			/* number of nops */
 	} insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
 	int insn_idx;				/* index of the next instruction to print */
 };
@@ -727,7 +728,7 @@ static int disas_alt_init(struct disas_alt *dalt,
 }
 
 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
-			      int offset)
+			      int offset, int nops)
 {
 	int len;
 
@@ -740,6 +741,7 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	len = strlen(insn_str);
 	dalt->insn[index].str = insn_str;
 	dalt->insn[index].offset = offset;
+	dalt->insn[index].nops = nops;
 	if (len > dalt->width)
 		dalt->width = len;
 
@@ -752,6 +754,7 @@ static int disas_alt_jump(struct disas_alt *dalt)
 	struct instruction *dest_insn;
 	char suffix[2] = { 0 };
 	char *str;
+	int nops;
 
 	orig_insn = dalt->orig_insn;
 	dest_insn = dalt->alt->insn;
@@ -762,14 +765,16 @@ static int disas_alt_jump(struct disas_alt *dalt)
 		str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
 			     dest_insn->offset, dest_insn->sym->name,
 			     dest_insn->offset - dest_insn->sym->offset);
+		nops = 0;
 	} else {
 		str = strfmt("nop%d", orig_insn->len);
+		nops = orig_insn->len;
 	}
 
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, nops);
 
 	return 1;
 }
@@ -789,7 +794,7 @@ static int disas_alt_extable(struct disas_alt *dalt)
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, 0);
 
 	return 1;
 }
@@ -805,11 +810,13 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 	int offset;
 	char *str;
 	int count;
+	int nops;
 	int err;
 
 	file = dctx->file;
 	count = 0;
 	offset = 0;
+	nops = 0;
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
@@ -818,7 +825,8 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 		if (!str)
 			return -1;
 
-		err = disas_alt_add_insn(dalt, count, str, offset);
+		nops = insn->type == INSN_NOP ? insn->len : 0;
+		err = disas_alt_add_insn(dalt, count, str, offset, nops);
 		if (err)
 			break;
 		offset += insn->len;
@@ -834,6 +842,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 {
 	char *str;
+	int nops;
 	int err;
 
 	if (DALT_GROUP(dalt))
@@ -849,7 +858,8 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	str = strdup(disas_result(dctx));
 	if (!str)
 		return -1;
-	err = disas_alt_add_insn(dalt, 0, str, 0);
+	nops = dalt->orig_insn->type == INSN_NOP ? dalt->orig_insn->len : 0;
+	err = disas_alt_add_insn(dalt, 0, str, 0, nops);
 	if (err)
 		return -1;
 
@@ -995,6 +1005,62 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	}
 }
 
+/*
+ * Trim NOPs in alternatives. This replaces trailing NOPs in alternatives
+ * with a single indication of the number of bytes covered with NOPs.
+ *
+ * Return the maximum numbers of instructions in all alternatives after
+ * trailing NOPs have been trimmed.
+ */
+static int disas_alt_trim_nops(struct disas_alt *dalts, int alt_count,
+			       int insn_count)
+{
+	struct disas_alt *dalt;
+	int nops_count;
+	const char *s;
+	int offset;
+	int count;
+	int nops;
+	int i, j;
+
+	count = 0;
+	for (i = 0; i < alt_count; i++) {
+		offset = 0;
+		nops = 0;
+		nops_count = 0;
+		dalt = &dalts[i];
+		for (j = insn_count - 1; j >= 0; j--) {
+			if (!dalt->insn[j].str || !dalt->insn[j].nops)
+				break;
+			offset = dalt->insn[j].offset;
+			free(dalt->insn[j].str);
+			dalt->insn[j].offset = 0;
+			dalt->insn[j].str = NULL;
+			nops += dalt->insn[j].nops;
+			nops_count++;
+		}
+
+		/*
+		 * All trailing NOPs have been removed. If there was a single
+		 * NOP instruction then re-add it. If there was a block of
+		 * NOPs then indicate the number of bytes than the block
+		 * covers (nop*<number-of-bytes>).
+		 */
+		if (nops_count) {
+			s = nops_count == 1 ? "" : "*";
+			dalt->insn[j + 1].str = strfmt("nop%s%d", s, nops);
+			dalt->insn[j + 1].offset = offset;
+			dalt->insn[j + 1].nops = nops;
+			j++;
+		}
+
+		if (j > count)
+			count = j;
+	}
+
+	return count + 1;
+}
+
 /*
  * Disassemble an alternative.
  *
@@ -1083,6 +1149,8 @@ static void *disas_alt(struct disas_context *dctx,
 	 * Print default and non-default alternatives.
 	 */
 
+	insn_count = disas_alt_trim_nops(dalts, alt_count, insn_count);
+
 	if (opts.wide)
 		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
 	else
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (29 preceding siblings ...)
  2025-11-21  9:53 ` [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative Alexandre Chartre
@ 2025-11-21 10:36 ` Peter Zijlstra
  2025-11-21 13:16   ` Alexandre Chartre
  2025-11-24 11:04 ` Borislav Petkov
  31 siblings, 1 reply; 110+ messages in thread
From: Peter Zijlstra @ 2025-11-21 10:36 UTC (permalink / raw)
  To: Alexandre Chartre; +Cc: linux-kernel, mingo, jpoimboe, david.laight.linux

On Fri, Nov 21, 2025 at 10:53:10AM +0100, Alexandre Chartre wrote:
> Alexandre Chartre (30):
>   objtool: Move disassembly functions to a separated file
>   objtool: Create disassembly context
>   objtool: Disassemble code with libopcodes instead of running objdump
>   tool build: Remove annoying newline in build output
>   objtool: Print symbol during disassembly
>   objtool: Store instruction disassembly result
>   objtool: Disassemble instruction on warning or backtrace
>   objtool: Extract code to validate instruction from the validate branch
>     loop
>   objtool: Record symbol name max length
>   objtool: Add option to trace function validation
>   objtool: Trace instruction state changes during function validation
>   objtool: Improve register reporting during function validation
>   objtool: Identify the different types of alternatives
>   objtool: Add functions to better name alternatives
>   objtool: Improve tracing of alternative instructions
>   objtool: Do not validate IBT for .return_sites and .call_sites
>   objtool: Add the --disas=<function-pattern> action
>   objtool: Preserve alternatives order
>   objtool: Print headers for alternatives
>   objtool: Disassemble group alternatives
>   objtool: Print addresses with alternative instructions
>   objtool: Disassemble exception table alternatives
>   objtool: Disassemble jump table alternatives
>   objtool: Fix address references in alternatives
>   objtool: Provide access to feature and flags of group alternatives
>   objtool: Function to get the name of a CPU feature
>   objtool: Improve naming of group alternatives
>   objtool: Compact output for alternatives with one instruction
>   objtool: Add wide output for disassembly
>   objtool: Trim trailing NOPs in alternative

I've pushed out these patches to queue/objtool/core, however when
building defconfig I get this:

  CC      /mnt/hirez/usr/src/linux-2.6/defconfig-build/tools/objtool/librbtree.o
arch/x86/special.c:10:10: fatal error: lib/cpu-feature-names.c: No such file or directory
   10 | #include "lib/cpu-feature-names.c"
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.

:-(

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-21 10:36 ` [PATCH v6 00/30] objtool: Function validation tracing Peter Zijlstra
@ 2025-11-21 13:16   ` Alexandre Chartre
  2025-11-21 13:51     ` Alexandre Chartre
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21 13:16 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: alexandre.chartre, linux-kernel, mingo, jpoimboe,
	david.laight.linux


On 11/21/25 11:36, Peter Zijlstra wrote:
> On Fri, Nov 21, 2025 at 10:53:10AM +0100, Alexandre Chartre wrote:
>> Alexandre Chartre (30):
>>    objtool: Move disassembly functions to a separated file
>>    objtool: Create disassembly context
>>    objtool: Disassemble code with libopcodes instead of running objdump
>>    tool build: Remove annoying newline in build output
>>    objtool: Print symbol during disassembly
>>    objtool: Store instruction disassembly result
>>    objtool: Disassemble instruction on warning or backtrace
>>    objtool: Extract code to validate instruction from the validate branch
>>      loop
>>    objtool: Record symbol name max length
>>    objtool: Add option to trace function validation
>>    objtool: Trace instruction state changes during function validation
>>    objtool: Improve register reporting during function validation
>>    objtool: Identify the different types of alternatives
>>    objtool: Add functions to better name alternatives
>>    objtool: Improve tracing of alternative instructions
>>    objtool: Do not validate IBT for .return_sites and .call_sites
>>    objtool: Add the --disas=<function-pattern> action
>>    objtool: Preserve alternatives order
>>    objtool: Print headers for alternatives
>>    objtool: Disassemble group alternatives
>>    objtool: Print addresses with alternative instructions
>>    objtool: Disassemble exception table alternatives
>>    objtool: Disassemble jump table alternatives
>>    objtool: Fix address references in alternatives
>>    objtool: Provide access to feature and flags of group alternatives
>>    objtool: Function to get the name of a CPU feature
>>    objtool: Improve naming of group alternatives
>>    objtool: Compact output for alternatives with one instruction
>>    objtool: Add wide output for disassembly
>>    objtool: Trim trailing NOPs in alternative
> 
> I've pushed out these patches to queue/objtool/core, however when
> building defconfig I get this:
> 
>    CC      /mnt/hirez/usr/src/linux-2.6/defconfig-build/tools/objtool/librbtree.o
> arch/x86/special.c:10:10: fatal error: lib/cpu-feature-names.c: No such file or directory
>     10 | #include "lib/cpu-feature-names.c"
>        |          ^~~~~~~~~~~~~~~~~~~~~~~~~
> compilation terminated.
>

I having a look. The problem is when using the O=<something> option.

Sorry,

alex.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-21 13:16   ` Alexandre Chartre
@ 2025-11-21 13:51     ` Alexandre Chartre
  2025-11-21 14:30       ` Peter Zijlstra
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-21 13:51 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: alexandre.chartre, linux-kernel, mingo, jpoimboe,
	david.laight.linux


On 11/21/25 14:16, Alexandre Chartre wrote:
> 
> On 11/21/25 11:36, Peter Zijlstra wrote:
>> On Fri, Nov 21, 2025 at 10:53:10AM +0100, Alexandre Chartre wrote:
>>> Alexandre Chartre (30):
>>>    objtool: Move disassembly functions to a separated file
>>>    objtool: Create disassembly context
>>>    objtool: Disassemble code with libopcodes instead of running objdump
>>>    tool build: Remove annoying newline in build output
>>>    objtool: Print symbol during disassembly
>>>    objtool: Store instruction disassembly result
>>>    objtool: Disassemble instruction on warning or backtrace
>>>    objtool: Extract code to validate instruction from the validate branch
>>>      loop
>>>    objtool: Record symbol name max length
>>>    objtool: Add option to trace function validation
>>>    objtool: Trace instruction state changes during function validation
>>>    objtool: Improve register reporting during function validation
>>>    objtool: Identify the different types of alternatives
>>>    objtool: Add functions to better name alternatives
>>>    objtool: Improve tracing of alternative instructions
>>>    objtool: Do not validate IBT for .return_sites and .call_sites
>>>    objtool: Add the --disas=<function-pattern> action
>>>    objtool: Preserve alternatives order
>>>    objtool: Print headers for alternatives
>>>    objtool: Disassemble group alternatives
>>>    objtool: Print addresses with alternative instructions
>>>    objtool: Disassemble exception table alternatives
>>>    objtool: Disassemble jump table alternatives
>>>    objtool: Fix address references in alternatives
>>>    objtool: Provide access to feature and flags of group alternatives
>>>    objtool: Function to get the name of a CPU feature
>>>    objtool: Improve naming of group alternatives
>>>    objtool: Compact output for alternatives with one instruction
>>>    objtool: Add wide output for disassembly
>>>    objtool: Trim trailing NOPs in alternative
>>
>> I've pushed out these patches to queue/objtool/core, however when
>> building defconfig I get this:
>>
>>    CC      /mnt/hirez/usr/src/linux-2.6/defconfig-build/tools/objtool/librbtree.o
>> arch/x86/special.c:10:10: fatal error: lib/cpu-feature-names.c: No such file or directory
>>     10 | #include "lib/cpu-feature-names.c"
>>        |          ^~~~~~~~~~~~~~~~~~~~~~~~~
>> compilation terminated.
>>

See fix below. This should be fold into patch 26 ("objtool: Function to get the name of a CPU feature").
I can resend this patch or the entire patchset if you want.

alex.

----

diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build
index 1067355361b56..b95448ee01ee4 100644
--- a/tools/objtool/arch/x86/Build
+++ b/tools/objtool/arch/x86/Build
@@ -11,6 +11,8 @@ $(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
  
  $(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
  
+CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
+
  cpu_features = ../arch/x86/include/asm/cpufeatures.h
  cpu_features_script = ../arch/x86/tools/gen-cpu-feature-names-x86.awk
  
@@ -19,4 +21,4 @@ $(OUTPUT)arch/x86/lib/cpu-feature-names.c: $(cpu_features_script) $(cpu_features
  
  $(OUTPUT)arch/x86/special.o: $(OUTPUT)arch/x86/lib/cpu-feature-names.c
  
-CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
+CFLAGS_special.o := -I$(OUTPUT)arch/x86/lib
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index b6b40c56da896..e817a3fff4491 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -7,7 +7,7 @@
  #include <asm/cpufeatures.h>
  
  /* cpu feature name array generated from cpufeatures.h */
-#include "lib/cpu-feature-names.c"
+#include "cpu-feature-names.c"
  
  void arch_handle_alternative(struct special_alt *alt)
  {



^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-21 13:51     ` Alexandre Chartre
@ 2025-11-21 14:30       ` Peter Zijlstra
  0 siblings, 0 replies; 110+ messages in thread
From: Peter Zijlstra @ 2025-11-21 14:30 UTC (permalink / raw)
  To: Alexandre Chartre; +Cc: linux-kernel, mingo, jpoimboe, david.laight.linux

On Fri, Nov 21, 2025 at 02:51:57PM +0100, Alexandre Chartre wrote:

> > > I've pushed out these patches to queue/objtool/core, however when
> > > building defconfig I get this:
> > > 
> > >    CC      /mnt/hirez/usr/src/linux-2.6/defconfig-build/tools/objtool/librbtree.o
> > > arch/x86/special.c:10:10: fatal error: lib/cpu-feature-names.c: No such file or directory
> > >     10 | #include "lib/cpu-feature-names.c"
> > >        |          ^~~~~~~~~~~~~~~~~~~~~~~~~
> > > compilation terminated.
> > > 
> 
> See fix below. This should be fold into patch 26 ("objtool: Function to get the name of a CPU feature").
> I can resend this patch or the entire patchset if you want.

Folded and pushed out again. Local build succeeds, lets see if the
robots like it.

Thanks!

^ permalink raw reply	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Trim trailing NOPs in alternative
  2025-11-21  9:53 ` [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative Alexandre Chartre
@ 2025-11-24  9:10   ` tip-bot2 for Alexandre Chartre
  2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:10 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Trim trailing NOPs in alternative

When disassembling alternatives replace trailing NOPs with a single
indication of the number of bytes covered with NOPs.

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-31-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 78 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 73 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f04bc14..441b930 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -52,6 +52,7 @@ struct disas_alt {
 	struct {
 		char *str;			/* instruction string */
 		int offset;			/* instruction offset */
+		int nops;			/* number of nops */
 	} insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
 	int insn_idx;				/* index of the next instruction to print */
 };
@@ -727,7 +728,7 @@ static int disas_alt_init(struct disas_alt *dalt,
 }
 
 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
-			      int offset)
+			      int offset, int nops)
 {
 	int len;
 
@@ -740,6 +741,7 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	len = strlen(insn_str);
 	dalt->insn[index].str = insn_str;
 	dalt->insn[index].offset = offset;
+	dalt->insn[index].nops = nops;
 	if (len > dalt->width)
 		dalt->width = len;
 
@@ -752,6 +754,7 @@ static int disas_alt_jump(struct disas_alt *dalt)
 	struct instruction *dest_insn;
 	char suffix[2] = { 0 };
 	char *str;
+	int nops;
 
 	orig_insn = dalt->orig_insn;
 	dest_insn = dalt->alt->insn;
@@ -762,14 +765,16 @@ static int disas_alt_jump(struct disas_alt *dalt)
 		str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
 			     dest_insn->offset, dest_insn->sym->name,
 			     dest_insn->offset - dest_insn->sym->offset);
+		nops = 0;
 	} else {
 		str = strfmt("nop%d", orig_insn->len);
+		nops = orig_insn->len;
 	}
 
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, nops);
 
 	return 1;
 }
@@ -789,7 +794,7 @@ static int disas_alt_extable(struct disas_alt *dalt)
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, 0);
 
 	return 1;
 }
@@ -805,11 +810,13 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 	int offset;
 	char *str;
 	int count;
+	int nops;
 	int err;
 
 	file = dctx->file;
 	count = 0;
 	offset = 0;
+	nops = 0;
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
@@ -818,7 +825,8 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 		if (!str)
 			return -1;
 
-		err = disas_alt_add_insn(dalt, count, str, offset);
+		nops = insn->type == INSN_NOP ? insn->len : 0;
+		err = disas_alt_add_insn(dalt, count, str, offset, nops);
 		if (err)
 			break;
 		offset += insn->len;
@@ -834,6 +842,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 {
 	char *str;
+	int nops;
 	int err;
 
 	if (DALT_GROUP(dalt))
@@ -849,7 +858,8 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	str = strdup(disas_result(dctx));
 	if (!str)
 		return -1;
-	err = disas_alt_add_insn(dalt, 0, str, 0);
+	nops = dalt->orig_insn->type == INSN_NOP ? dalt->orig_insn->len : 0;
+	err = disas_alt_add_insn(dalt, 0, str, 0, nops);
 	if (err)
 		return -1;
 
@@ -996,6 +1006,62 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 }
 
 /*
+ * Trim NOPs in alternatives. This replaces trailing NOPs in alternatives
+ * with a single indication of the number of bytes covered with NOPs.
+ *
+ * Return the maximum numbers of instructions in all alternatives after
+ * trailing NOPs have been trimmed.
+ */
+static int disas_alt_trim_nops(struct disas_alt *dalts, int alt_count,
+			       int insn_count)
+{
+	struct disas_alt *dalt;
+	int nops_count;
+	const char *s;
+	int offset;
+	int count;
+	int nops;
+	int i, j;
+
+	count = 0;
+	for (i = 0; i < alt_count; i++) {
+		offset = 0;
+		nops = 0;
+		nops_count = 0;
+		dalt = &dalts[i];
+		for (j = insn_count - 1; j >= 0; j--) {
+			if (!dalt->insn[j].str || !dalt->insn[j].nops)
+				break;
+			offset = dalt->insn[j].offset;
+			free(dalt->insn[j].str);
+			dalt->insn[j].offset = 0;
+			dalt->insn[j].str = NULL;
+			nops += dalt->insn[j].nops;
+			nops_count++;
+		}
+
+		/*
+		 * All trailing NOPs have been removed. If there was a single
+		 * NOP instruction then re-add it. If there was a block of
+		 * NOPs then indicate the number of bytes than the block
+		 * covers (nop*<number-of-bytes>).
+		 */
+		if (nops_count) {
+			s = nops_count == 1 ? "" : "*";
+			dalt->insn[j + 1].str = strfmt("nop%s%d", s, nops);
+			dalt->insn[j + 1].offset = offset;
+			dalt->insn[j + 1].nops = nops;
+			j++;
+		}
+
+		if (j > count)
+			count = j;
+	}
+
+	return count + 1;
+}
+
+/*
  * Disassemble an alternative.
  *
  * Return the last instruction in the default alternative so that
@@ -1083,6 +1149,8 @@ static void *disas_alt(struct disas_context *dctx,
 	 * Print default and non-default alternatives.
 	 */
 
+	insn_count = disas_alt_trim_nops(dalts, alt_count, insn_count);
+
 	if (opts.wide)
 		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
 	else

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add wide output for disassembly
  2025-11-21  9:53 ` [PATCH v6 29/30] objtool: Add wide output for disassembly Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Add wide output for disassembly

Add the --wide option to provide a wide output when disassembling.
With this option, the disassembly of alternatives is displayed
side-by-side instead of one above the other.

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-30-alexandre.chartre@oracle.com
---
 tools/objtool/builtin-check.c           |  1 +-
 tools/objtool/disas.c                   | 95 +++++++++++++++++++++++-
 tools/objtool/include/objtool/builtin.h |  1 +-
 3 files changed, 96 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index a037131..b780df5 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -107,6 +107,7 @@ static const struct option check_options[] = {
 	OPT_STRING(0,		 "trace", &opts.trace, "func", "trace function validation"),
 	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
 	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
+	OPT_BOOLEAN(0,		 "wide", &opts.wide, "wide output"),
 
 	OPT_END(),
 };
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index a4f905e..f04bc14 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -857,6 +857,95 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 }
 
 /*
+ * For each alternative, if there is an instruction at the specified
+ * offset then print this instruction, otherwise print a blank entry.
+ * The offset is an offset from the start of the alternative.
+ *
+ * Return the offset for the next instructions to print, or -1 if all
+ * instructions have been printed.
+ */
+static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count,
+				int insn_count, int offset)
+{
+	struct disas_alt *dalt;
+	int offset_next;
+	char *str;
+	int i, j;
+
+	offset_next = -1;
+
+	for (i = 0; i < alt_count; i++) {
+		dalt = &dalts[i];
+		j = dalt->insn_idx;
+		if (j == -1) {
+			printf("| %-*s ", dalt->width, "");
+			continue;
+		}
+
+		if (dalt->insn[j].offset == offset) {
+			str = dalt->insn[j].str;
+			printf("| %-*s ", dalt->width, str ?: "");
+			if (++j < insn_count) {
+				dalt->insn_idx = j;
+			} else {
+				dalt->insn_idx = -1;
+				continue;
+			}
+		} else {
+			printf("| %-*s ", dalt->width, "");
+		}
+
+		if (dalt->insn[j].offset > 0 &&
+		    (offset_next == -1 ||
+		     (dalt->insn[j].offset < offset_next)))
+			offset_next = dalt->insn[j].offset;
+	}
+	printf("\n");
+
+	return offset_next;
+}
+
+/*
+ * Print all alternatives side-by-side.
+ */
+static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count,
+				 int insn_count)
+{
+	struct instruction *orig_insn;
+	int offset_next;
+	int offset;
+	int i;
+
+	orig_insn = dalts[0].orig_insn;
+
+	/*
+	 * Print an header with the name of each alternative.
+	 */
+	disas_print_info(stdout, orig_insn, -2, NULL);
+
+	if (strlen(alt_name) > dalts[0].width)
+		dalts[0].width = strlen(alt_name);
+	printf("| %-*s ", dalts[0].width, alt_name);
+
+	for (i = 1; i < alt_count; i++)
+		printf("| %-*s ", dalts[i].width, dalts[i].name);
+
+	printf("\n");
+
+	/*
+	 * Print instructions for each alternative.
+	 */
+	offset_next = 0;
+	do {
+		offset = offset_next;
+		disas_print(stdout, orig_insn->sec, orig_insn->offset + offset,
+			    -2, NULL);
+		offset_next = disas_alt_print_insn(dalts, alt_count, insn_count,
+						   offset);
+	} while (offset_next > offset);
+}
+
+/*
  * Print all alternatives one above the other.
  */
 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
@@ -993,7 +1082,11 @@ static void *disas_alt(struct disas_context *dctx,
 	/*
 	 * Print default and non-default alternatives.
 	 */
-	disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
+
+	if (opts.wide)
+		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
+	else
+		disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
 
 	last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
 		orig_insn;
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index e3af664..b9e229e 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -45,6 +45,7 @@ struct opts {
 	const char *trace;
 	bool verbose;
 	bool werror;
+	bool wide;
 };
 
 extern struct opts opts;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Compact output for alternatives with one instruction
  2025-11-21  9:53 ` [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Compact output for alternatives with one instruction

When disassembling, if an instruction has alternatives which are all
made of a single instruction then print each alternative on a single
line (instruction + description) so that the output is more compact.

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-29-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 731c449..a4f905e 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -863,6 +863,7 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 				    int alt_count, int insn_count)
 {
 	struct instruction *orig_insn;
+	int width;
 	int i, j;
 	int len;
 
@@ -871,6 +872,27 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
 	printf("%s\n", alt_name);
 
+	/*
+	 * If all alternatives have a single instruction then print each
+	 * alternative on a single line. Otherwise, print alternatives
+	 * one above the other with a clear separation.
+	 */
+
+	if (insn_count == 1) {
+		width = 0;
+		for (i = 0; i < alt_count; i++) {
+			if (dalts[i].width > width)
+				width = dalts[i].width;
+		}
+
+		for (i = 0; i < alt_count; i++) {
+			printf("%*s= %-*s    (if %s)\n", len, "", width,
+			       dalts[i].insn[0].str, dalts[i].name);
+		}
+
+		return;
+	}
+
 	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
 		for (j = 0; j < insn_count; j++) {

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Improve naming of group alternatives
  2025-11-21  9:53 ` [PATCH v6 27/30] objtool: Improve naming of group alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Improve naming of group alternatives

Improve the naming of group alternatives by showing the feature name and
flags used by the alternative.

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-28-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 58 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f8917c8..731c449 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -9,6 +9,7 @@
 #include <objtool/arch.h>
 #include <objtool/check.h>
 #include <objtool/disas.h>
+#include <objtool/special.h>
 #include <objtool/warn.h>
 
 #include <bfd.h>
@@ -60,6 +61,21 @@ struct disas_alt {
 #define DALT_GROUP(dalt)	(DALT_INSN(dalt)->alt_group)
 #define DALT_ALTID(dalt)	((dalt)->orig_insn->offset)
 
+#define ALT_FLAGS_SHIFT		16
+#define ALT_FLAG_NOT		(1 << 0)
+#define ALT_FLAG_DIRECT_CALL	(1 << 1)
+#define ALT_FEATURE_MASK	((1 << ALT_FLAGS_SHIFT) - 1)
+
+static int alt_feature(unsigned int ft_flags)
+{
+	return (ft_flags & ALT_FEATURE_MASK);
+}
+
+static int alt_flags(unsigned int ft_flags)
+{
+	return (ft_flags >> ALT_FLAGS_SHIFT);
+}
+
 /*
  * Wrapper around asprintf() to allocate and format a string.
  * Return the allocated string or NULL on error.
@@ -635,7 +651,12 @@ const char *disas_alt_type_name(struct instruction *insn)
  */
 char *disas_alt_name(struct alternative *alt)
 {
+	char pfx[4] = { 0 };
 	char *str = NULL;
+	const char *name;
+	int feature;
+	int flags;
+	int num;
 
 	switch (alt->type) {
 
@@ -649,13 +670,37 @@ char *disas_alt_name(struct alternative *alt)
 
 	case ALT_TYPE_INSTRUCTIONS:
 		/*
-		 * This is a non-default group alternative. Create a unique
-		 * name using the offset of the first original and alternative
-		 * instructions.
+		 * This is a non-default group alternative. Create a name
+		 * based on the feature and flags associated with this
+		 * alternative. Use either the feature name (it is available)
+		 * or the feature number. And add a prefix to show the flags
+		 * used.
+		 *
+		 * Prefix flags characters:
+		 *
+		 *   '!'  alternative used when feature not enabled
+		 *   '+'  direct call alternative
+		 *   '?'  unknown flag
 		 */
-		asprintf(&str, "ALTERNATIVE %lx.%lx",
-			 alt->insn->alt_group->orig_group->first_insn->offset,
-			 alt->insn->alt_group->first_insn->offset);
+
+		feature = alt->insn->alt_group->feature;
+		num = alt_feature(feature);
+		flags = alt_flags(feature);
+		str = pfx;
+
+		if (flags & ~(ALT_FLAG_NOT | ALT_FLAG_DIRECT_CALL))
+			*str++ = '?';
+		if (flags & ALT_FLAG_DIRECT_CALL)
+			*str++ = '+';
+		if (flags & ALT_FLAG_NOT)
+			*str++ = '!';
+
+		name = arch_cpu_feature_name(num);
+		if (!name)
+			str = strfmt("%sFEATURE 0x%X", pfx, num);
+		else
+			str = strfmt("%s%s", pfx, name);
+
 		break;
 	}
 
@@ -892,6 +937,7 @@ static void *disas_alt(struct disas_context *dctx,
 			WARN("%s has more alternatives than supported", alt_name);
 			break;
 		}
+
 		dalt = &dalts[i];
 		err = disas_alt_init(dalt, orig_insn, alt);
 		if (err) {

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-21  9:53 ` [PATCH v6 26/30] objtool: Function to get the name of a CPU feature Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` [tip: objtool/core] objtool: Add " tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Function to get the name of a CPU feature

Add a function to get the name of a CPU feature. The function is
architecture dependent and currently only implemented for x86. The
feature names are automatically generated from the cpufeatures.h
include file.

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-27-alexandre.chartre@oracle.com
---
 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk | 33 +++++++++++++-
 tools/objtool/.gitignore                           |  1 +-
 tools/objtool/Makefile                             |  1 +-
 tools/objtool/arch/loongarch/special.c             |  5 ++-
 tools/objtool/arch/powerpc/special.c               |  5 ++-
 tools/objtool/arch/x86/Build                       | 10 ++++-
 tools/objtool/arch/x86/special.c                   | 10 ++++-
 tools/objtool/include/objtool/special.h            |  2 +-
 8 files changed, 67 insertions(+)
 create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk

diff --git a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
new file mode 100644
index 0000000..1b1c1d8
--- /dev/null
+++ b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
@@ -0,0 +1,33 @@
+#!/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Usage: awk -f gen-cpu-feature-names-x86.awk cpufeatures.h > cpu-feature-names.c
+#
+
+BEGIN {
+	print "/* cpu feature name array generated from cpufeatures.h */"
+	print "/* Do not change this code. */"
+	print
+	print "static const char *cpu_feature_names[(NCAPINTS+NBUGINTS)*32] = {"
+
+	feature_expr = "(X86_FEATURE_[A-Z0-9_]+)\\s+\\(([0-9*+ ]+)\\)"
+	debug_expr = "(X86_BUG_[A-Z0-9_]+)\\s+X86_BUG\\(([0-9*+ ]+)\\)"
+}
+
+/^#define X86_FEATURE_/ {
+	if (match($0, feature_expr, m)) {
+		print "\t[" m[2] "] = \"" m[1] "\","
+	}
+}
+
+/^#define X86_BUG_/ {
+	if (match($0, debug_expr, m)) {
+		print "\t[NCAPINTS*32+(" m[2] ")] = \"" m[1] "\","
+	}
+}
+
+END {
+	print "};"
+}
diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 7593036..73d8831 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+arch/x86/lib/cpu-feature-names.c
 arch/x86/lib/inat-tables.c
 /objtool
 feature
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index df793ca..66397d7 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -125,6 +125,7 @@ $(LIBSUBCMD)-clean:
 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
diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c
index a80b75f..aba7741 100644
--- a/tools/objtool/arch/loongarch/special.c
+++ b/tools/objtool/arch/loongarch/special.c
@@ -194,3 +194,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/powerpc/special.c b/tools/objtool/arch/powerpc/special.c
index 5161068..8f9bf61 100644
--- a/tools/objtool/arch/powerpc/special.c
+++ b/tools/objtool/arch/powerpc/special.c
@@ -18,3 +18,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 {
 	exit(-1);
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build
index 3dedb2f..8b37c36 100644
--- a/tools/objtool/arch/x86/Build
+++ b/tools/objtool/arch/x86/Build
@@ -12,3 +12,13 @@ $(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
 $(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
 
 CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
+
+cpu_features = ../arch/x86/include/asm/cpufeatures.h
+cpu_features_script = ../arch/x86/tools/gen-cpu-feature-names-x86.awk
+
+$(OUTPUT)arch/x86/lib/cpu-feature-names.c: $(cpu_features_script) $(cpu_features)
+	$(Q)$(call echo-cmd,gen)$(AWK) -f $(cpu_features_script) $(cpu_features) > $@
+
+$(OUTPUT)arch/x86/special.o: $(OUTPUT)arch/x86/lib/cpu-feature-names.c
+
+CFLAGS_special.o += -I$(OUTPUT)arch/x86/lib
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index 0930076..e817a3f 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -4,6 +4,10 @@
 #include <objtool/special.h>
 #include <objtool/builtin.h>
 #include <objtool/warn.h>
+#include <asm/cpufeatures.h>
+
+/* cpu feature name array generated from cpufeatures.h */
+#include "cpu-feature-names.c"
 
 void arch_handle_alternative(struct special_alt *alt)
 {
@@ -134,3 +138,9 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 	*table_size = 0;
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return (feature_number < ARRAY_SIZE(cpu_feature_names)) ?
+		cpu_feature_names[feature_number] : NULL;
+}
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index b224107..121c376 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -38,4 +38,6 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
 struct reloc *arch_find_switch_table(struct objtool_file *file,
 				     struct instruction *insn,
 				     unsigned long *table_size);
+const char *arch_cpu_feature_name(int feature_number);
+
 #endif /* _SPECIAL_H */

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Provide access to feature and flags of group alternatives
  2025-11-21  9:53 ` [PATCH v6 25/30] objtool: Provide access to feature and flags of group alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Provide access to feature and flags of group alternatives

Each alternative of a group alternative depends on a specific
feature and flags. Provide access to the feature/flags for each
alternative as an attribute (feature) in struct alt_group.

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-26-alexandre.chartre@oracle.com
---
 tools/objtool/check.c                   | 2 ++
 tools/objtool/include/objtool/check.h   | 1 +
 tools/objtool/include/objtool/special.h | 2 +-
 tools/objtool/special.c                 | 2 ++
 4 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index f75364f..9ec0e07 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1751,6 +1751,7 @@ static int handle_group_alt(struct objtool_file *file,
 		orig_alt_group->last_insn = last_orig_insn;
 		orig_alt_group->nop = NULL;
 		orig_alt_group->ignore = orig_insn->ignore_alts;
+		orig_alt_group->feature = 0;
 	} else {
 		if (orig_alt_group->last_insn->offset + orig_alt_group->last_insn->len -
 		    orig_alt_group->first_insn->offset != special_alt->orig_len) {
@@ -1855,6 +1856,7 @@ end:
 	new_alt_group->nop = nop;
 	new_alt_group->ignore = (*new_insn)->ignore_alts;
 	new_alt_group->cfi = orig_alt_group->cfi;
+	new_alt_group->feature = special_alt->feature;
 	return 0;
 }
 
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index cbf4af5..2e1346a 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -36,6 +36,7 @@ struct alt_group {
 	struct cfi_state **cfi;
 
 	bool ignore;
+	unsigned int feature;
 };
 
 enum alternative_type {
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index 72d09c0..b224107 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -25,7 +25,7 @@ struct special_alt {
 	struct section *new_sec;
 	unsigned long new_off;
 
-	unsigned int orig_len, new_len; /* group only */
+	unsigned int orig_len, new_len, feature; /* group only */
 };
 
 int special_get_alts(struct elf *elf, struct list_head *alts);
diff --git a/tools/objtool/special.c b/tools/objtool/special.c
index e262af9..2a533af 100644
--- a/tools/objtool/special.c
+++ b/tools/objtool/special.c
@@ -81,6 +81,8 @@ static int get_alt_entry(struct elf *elf, const struct special_entry *entry,
 						   entry->orig_len);
 		alt->new_len = *(unsigned char *)(sec->data->d_buf + offset +
 						  entry->new_len);
+		alt->feature = *(unsigned int *)(sec->data->d_buf + offset +
+						 entry->feature);
 	}
 
 	orig_reloc = find_reloc_by_dest(elf, sec, offset + entry->orig);

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Fix address references in alternatives
  2025-11-21  9:53 ` [PATCH v6 24/30] objtool: Fix address references in alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Fix address references in alternatives

When using the --disas option, alternatives are disassembled but
address references in non-default alternatives can be incorrect.

The problem is that alternatives are shown as if they were replacing the
original code of the alternative. So if an alternative is referencing
an address inside the alternative then the reference has to be
adjusted to the location of the original code.

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-25-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 70 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 66 insertions(+), 4 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 326e16c..f8917c8 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -24,6 +24,7 @@
 struct disas_context {
 	struct objtool_file *file;
 	struct instruction *insn;
+	bool alt_applied;
 	char result[DISAS_RESULT_SIZE];
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
@@ -160,6 +161,43 @@ static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
 	}
 }
 
+static bool disas_print_addr_alt(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *orig_first_insn;
+	struct alt_group *alt_group;
+	unsigned long offset;
+	struct symbol *sym;
+
+	/*
+	 * Check if we are processing an alternative at the original
+	 * instruction address (i.e. if alt_applied is true) and if
+	 * we are referencing an address inside the alternative.
+	 *
+	 * For example, this happens if there is a branch inside an
+	 * alternative. In that case, the address should be updated
+	 * to a reference inside the original instruction flow.
+	 */
+	if (!dctx->alt_applied)
+		return false;
+
+	alt_group = dctx->insn->alt_group;
+	if (!alt_group || !alt_group->orig_group ||
+	    addr < alt_group->first_insn->offset ||
+	    addr > alt_group->last_insn->offset)
+		return false;
+
+	orig_first_insn = alt_group->orig_group->first_insn;
+	offset = addr - alt_group->first_insn->offset;
+
+	addr = orig_first_insn->offset + offset;
+	sym = orig_first_insn->sym;
+
+	disas_print_addr_sym(orig_first_insn->sec, sym, addr, dinfo);
+
+	return true;
+}
+
 static void disas_print_addr_noreloc(bfd_vma addr,
 				     struct disassemble_info *dinfo)
 {
@@ -167,6 +205,9 @@ static void disas_print_addr_noreloc(bfd_vma addr,
 	struct instruction *insn = dctx->insn;
 	struct symbol *sym = NULL;
 
+	if (disas_print_addr_alt(addr, dinfo))
+		return;
+
 	if (insn->sym && addr >= insn->sym->offset &&
 	    addr < insn->sym->offset + insn->sym->len) {
 		sym = insn->sym;
@@ -232,8 +273,9 @@ static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
 	 */
 	jump_dest = insn->jump_dest;
 	if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
-		disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
-				     addr, dinfo);
+		if (!disas_print_addr_alt(addr, dinfo))
+			disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
+					     addr, dinfo);
 		return;
 	}
 
@@ -490,13 +532,22 @@ void disas_print_insn(FILE *stream, struct disas_context *dctx,
 
 /*
  * Disassemble a single instruction. Return the size of the instruction.
+ *
+ * If alt_applied is true then insn should be an instruction from of an
+ * alternative (i.e. insn->alt_group != NULL), and it is disassembled
+ * at the location of the original code it is replacing. When the
+ * instruction references any address inside the alternative then
+ * these references will be re-adjusted to replace the original code.
  */
-size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
+static size_t disas_insn_common(struct disas_context *dctx,
+				struct instruction *insn,
+				bool alt_applied)
 {
 	disassembler_ftype disasm = dctx->disassembler;
 	struct disassemble_info *dinfo = &dctx->info;
 
 	dctx->insn = insn;
+	dctx->alt_applied = alt_applied;
 	dctx->result[0] = '\0';
 
 	if (insn->type == INSN_NOP) {
@@ -515,6 +566,17 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 	return disasm(insn->offset, &dctx->info);
 }
 
+size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
+{
+	return disas_insn_common(dctx, insn, false);
+}
+
+static size_t disas_insn_alt(struct disas_context *dctx,
+			     struct instruction *insn)
+{
+	return disas_insn_common(dctx, insn, true);
+}
+
 static struct instruction *next_insn_same_alt(struct objtool_file *file,
 					      struct alt_group *alt_grp,
 					      struct instruction *insn)
@@ -706,7 +768,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
-		disas_insn(dctx, insn);
+		disas_insn_alt(dctx, insn);
 		str = strdup(disas_result(dctx));
 		if (!str)
 			return -1;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Disassemble jump table alternatives
  2025-11-21  9:53 ` [PATCH v6 23/30] objtool: Disassemble jump " Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Disassemble jump table alternatives

When using the --disas option, also disassemble jump tables.

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-24-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 38 ++++++++++++++++++++++++++++++++------
 1 file changed, 32 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 018aba3..326e16c 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -639,6 +639,34 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	return 0;
 }
 
+static int disas_alt_jump(struct disas_alt *dalt)
+{
+	struct instruction *orig_insn;
+	struct instruction *dest_insn;
+	char suffix[2] = { 0 };
+	char *str;
+
+	orig_insn = dalt->orig_insn;
+	dest_insn = dalt->alt->insn;
+
+	if (orig_insn->type == INSN_NOP) {
+		if (orig_insn->len == 5)
+			suffix[0] = 'q';
+		str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
+			     dest_insn->offset, dest_insn->sym->name,
+			     dest_insn->offset - dest_insn->sym->offset);
+	} else {
+		str = strfmt("nop%d", orig_insn->len);
+	}
+
+	if (!str)
+		return -1;
+
+	disas_alt_add_insn(dalt, 0, str, 0);
+
+	return 1;
+}
+
 /*
  * Disassemble an exception table alternative.
  */
@@ -809,10 +837,7 @@ static void *disas_alt(struct disas_context *dctx,
 			goto done;
 		}
 
-		/*
-		 * Only group alternatives and exception tables are
-		 * supported at the moment.
-		 */
+		count = -1;
 		switch (dalt->alt->type) {
 		case ALT_TYPE_INSTRUCTIONS:
 			count = disas_alt_group(dctx, dalt);
@@ -820,8 +845,9 @@ static void *disas_alt(struct disas_context *dctx,
 		case ALT_TYPE_EX_TABLE:
 			count = disas_alt_extable(dalt);
 			break;
-		default:
-			count = 0;
+		case ALT_TYPE_JUMP_TABLE:
+			count = disas_alt_jump(dalt);
+			break;
 		}
 		if (count < 0) {
 			WARN("%s: failed to disassemble alternative %s",

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Disassemble exception table alternatives
  2025-11-21  9:53 ` [PATCH v6 22/30] objtool: Disassemble exception table alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Disassemble exception table alternatives

When using the --disas option, also disassemble exception tables
(EX_TABLE).

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-23-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 26 +++++++++++++++++++++++++-
 1 file changed, 25 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 6083a64..018aba3 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -640,6 +640,26 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 }
 
 /*
+ * Disassemble an exception table alternative.
+ */
+static int disas_alt_extable(struct disas_alt *dalt)
+{
+	struct instruction *alt_insn;
+	char *str;
+
+	alt_insn = dalt->alt->insn;
+	str = strfmt("resume at 0x%lx <%s+0x%lx>",
+		     alt_insn->offset, alt_insn->sym->name,
+		     alt_insn->offset - alt_insn->sym->offset);
+	if (!str)
+		return -1;
+
+	disas_alt_add_insn(dalt, 0, str, 0);
+
+	return 1;
+}
+
+/*
  * Disassemble an alternative and store instructions in the disas_alt
  * structure. Return the number of instructions in the alternative.
  */
@@ -790,12 +810,16 @@ static void *disas_alt(struct disas_context *dctx,
 		}
 
 		/*
-		 * Only group alternatives are supported at the moment.
+		 * Only group alternatives and exception tables are
+		 * supported at the moment.
 		 */
 		switch (dalt->alt->type) {
 		case ALT_TYPE_INSTRUCTIONS:
 			count = disas_alt_group(dctx, dalt);
 			break;
+		case ALT_TYPE_EX_TABLE:
+			count = disas_alt_extable(dalt);
+			break;
 		default:
 			count = 0;
 		}

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Print addresses with alternative instructions
  2025-11-21  9:53 ` [PATCH v6 21/30] objtool: Print addresses with alternative instructions Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Print addresses with alternative instructions

All alternatives are disassemble side-by-side when using the --disas
option. However the address of each instruction is not printed because
instructions from different alternatives are not necessarily aligned.

Change this behavior to print the address of each instruction. Spaces
will appear between instructions from the same alternative when
instructions from different alternatives do not have the same alignment.

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-22-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 35 +++++++++++++++++++++++------------
 1 file changed, 23 insertions(+), 12 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index ae69bef..6083a64 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -47,7 +47,11 @@ struct disas_alt {
 	struct alternative *alt;		/* alternative or NULL if default code */
 	char *name;				/* name for this alternative */
 	int width;				/* formatting width */
-	char *insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
+	struct {
+		char *str;			/* instruction string */
+		int offset;			/* instruction offset */
+	} insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
+	int insn_idx;				/* index of the next instruction to print */
 };
 
 #define DALT_DEFAULT(dalt)	(!(dalt)->alt)
@@ -361,10 +365,9 @@ char *disas_result(struct disas_context *dctx)
 	disas_print_insn(stdout, dctx, insn, depth, "\n")
 
 /*
- * Print a message in the instruction flow. If insn is not NULL then
- * the instruction address is printed in addition of the message,
- * otherwise only the message is printed. In all cases, the instruction
- * itself is not printed.
+ * Print a message in the instruction flow. If sec is not NULL then the
+ * address at the section offset is printed in addition of the message,
+ * otherwise only the message is printed.
  */
 static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
 			int depth, const char *format, va_list ap)
@@ -607,6 +610,7 @@ static int disas_alt_init(struct disas_alt *dalt,
 {
 	dalt->orig_insn = orig_insn;
 	dalt->alt = alt;
+	dalt->insn_idx = 0;
 	dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT");
 	if (!dalt->name)
 		return -1;
@@ -615,7 +619,8 @@ static int disas_alt_init(struct disas_alt *dalt,
 	return 0;
 }
 
-static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
+static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
+			      int offset)
 {
 	int len;
 
@@ -626,7 +631,8 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
 	}
 
 	len = strlen(insn_str);
-	dalt->insn[index] = insn_str;
+	dalt->insn[index].str = insn_str;
+	dalt->insn[index].offset = offset;
 	if (len > dalt->width)
 		dalt->width = len;
 
@@ -641,12 +647,14 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 {
 	struct objtool_file *file;
 	struct instruction *insn;
+	int offset;
 	char *str;
 	int count;
 	int err;
 
 	file = dctx->file;
 	count = 0;
+	offset = 0;
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
@@ -655,9 +663,10 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 		if (!str)
 			return -1;
 
-		err = disas_alt_add_insn(dalt, count, str);
+		err = disas_alt_add_insn(dalt, count, str, offset);
 		if (err)
 			break;
+		offset += insn->len;
 		count++;
 	}
 
@@ -685,7 +694,7 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	str = strdup(disas_result(dctx));
 	if (!str)
 		return -1;
-	err = disas_alt_add_insn(dalt, 0, str);
+	err = disas_alt_add_insn(dalt, 0, str, 0);
 	if (err)
 		return -1;
 
@@ -710,9 +719,11 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
 		for (j = 0; j < insn_count; j++) {
-			if (!dalts[i].insn[j])
+			if (!dalts[i].insn[j].str)
 				break;
-			printf("%*s| %s\n", len, "", dalts[i].insn[j]);
+			disas_print(stdout, orig_insn->sec,
+				    orig_insn->offset + dalts[i].insn[j].offset, 0,
+				    "| %s\n", dalts[i].insn[j].str);
 		}
 		printf("%*s|\n", len, "");
 	}
@@ -811,7 +822,7 @@ done:
 	for (i = 0; i < alt_count; i++) {
 		free(dalts[i].name);
 		for (j = 0; j < insn_count; j++)
-			free(dalts[i].insn[j]);
+			free(dalts[i].insn[j].str);
 	}
 
 	free(alt_name);

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Disassemble group alternatives
  2025-11-21  9:53 ` [PATCH v6 20/30] objtool: Disassemble group alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Disassemble group alternatives

When using the --disas option, disassemble all group alternatives.
Jump tables and exception tables (which are handled as alternatives)
are not disassembled at the moment.

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-21-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 166 ++++++++++++++++++++++++++++++++++++-----
 1 file changed, 149 insertions(+), 17 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f9b13d5..ae69bef 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -47,8 +47,14 @@ struct disas_alt {
 	struct alternative *alt;		/* alternative or NULL if default code */
 	char *name;				/* name for this alternative */
 	int width;				/* formatting width */
+	char *insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
 };
 
+#define DALT_DEFAULT(dalt)	(!(dalt)->alt)
+#define DALT_INSN(dalt)		(DALT_DEFAULT(dalt) ? (dalt)->orig_insn : (dalt)->alt->insn)
+#define DALT_GROUP(dalt)	(DALT_INSN(dalt)->alt_group)
+#define DALT_ALTID(dalt)	((dalt)->orig_insn->offset)
+
 /*
  * Wrapper around asprintf() to allocate and format a string.
  * Return the allocated string or NULL on error.
@@ -506,6 +512,21 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 	return disasm(insn->offset, &dctx->info);
 }
 
+static struct instruction *next_insn_same_alt(struct objtool_file *file,
+					      struct alt_group *alt_grp,
+					      struct instruction *insn)
+{
+	if (alt_grp->last_insn == insn || alt_grp->nop == insn)
+		return NULL;
+
+	return next_insn_same_sec(file, insn);
+}
+
+#define alt_for_each_insn(file, alt_grp, insn)			\
+	for (insn = alt_grp->first_insn; 			\
+	     insn;						\
+	     insn = next_insn_same_alt(file, alt_grp, insn))
+
 /*
  * Provide a name for the type of alternatives present at the
  * specified instruction.
@@ -594,23 +615,107 @@ static int disas_alt_init(struct disas_alt *dalt,
 	return 0;
 }
 
+static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str)
+{
+	int len;
+
+	if (index >= DISAS_ALT_INSN_MAX) {
+		WARN("Alternative %lx.%s has more instructions than supported",
+		     DALT_ALTID(dalt), dalt->name);
+		return -1;
+	}
+
+	len = strlen(insn_str);
+	dalt->insn[index] = insn_str;
+	if (len > dalt->width)
+		dalt->width = len;
+
+	return 0;
+}
+
+/*
+ * Disassemble an alternative and store instructions in the disas_alt
+ * structure. Return the number of instructions in the alternative.
+ */
+static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
+{
+	struct objtool_file *file;
+	struct instruction *insn;
+	char *str;
+	int count;
+	int err;
+
+	file = dctx->file;
+	count = 0;
+
+	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
+
+		disas_insn(dctx, insn);
+		str = strdup(disas_result(dctx));
+		if (!str)
+			return -1;
+
+		err = disas_alt_add_insn(dalt, count, str);
+		if (err)
+			break;
+		count++;
+	}
+
+	return count;
+}
+
+/*
+ * Disassemble the default alternative.
+ */
+static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
+{
+	char *str;
+	int err;
+
+	if (DALT_GROUP(dalt))
+		return disas_alt_group(dctx, dalt);
+
+	/*
+	 * Default alternative with no alt_group: this is the default
+	 * code associated with either a jump table or an exception
+	 * table and no other instruction alternatives. In that case
+	 * the default alternative is made of a single instruction.
+	 */
+	disas_insn(dctx, dalt->orig_insn);
+	str = strdup(disas_result(dctx));
+	if (!str)
+		return -1;
+	err = disas_alt_add_insn(dalt, 0, str);
+	if (err)
+		return -1;
+
+	return 1;
+}
+
 /*
  * Print all alternatives one above the other.
  */
 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
-				    int alt_count)
+				    int alt_count, int insn_count)
 {
 	struct instruction *orig_insn;
+	int i, j;
 	int len;
-	int i;
 
 	orig_insn = dalts[0].orig_insn;
 
 	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
 	printf("%s\n", alt_name);
 
-	for (i = 0; i < alt_count; i++)
+	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
+		for (j = 0; j < insn_count; j++) {
+			if (!dalts[i].insn[j])
+				break;
+			printf("%*s| %s\n", len, "", dalts[i].insn[j]);
+		}
+		printf("%*s|\n", len, "");
+	}
 }
 
 /*
@@ -624,11 +729,15 @@ static void *disas_alt(struct disas_context *dctx,
 		       struct instruction *orig_insn)
 {
 	struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
+	struct instruction *last_insn = NULL;
 	struct alternative *alt;
+	struct disas_alt *dalt;
+	int insn_count = 0;
 	int alt_count = 0;
 	char *alt_name;
+	int count;
+	int i, j;
 	int err;
-	int i;
 
 	alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
 			  orig_insn->offset);
@@ -639,7 +748,7 @@ static void *disas_alt(struct disas_context *dctx,
 	}
 
 	/*
-	 * Initialize the default alternative.
+	 * Initialize and disassemble the default alternative.
 	 */
 	err = disas_alt_init(&dalts[0], orig_insn, NULL);
 	if (err) {
@@ -647,8 +756,14 @@ static void *disas_alt(struct disas_context *dctx,
 		goto done;
 	}
 
+	insn_count = disas_alt_default(dctx, &dalts[0]);
+	if (insn_count < 0) {
+		WARN("%s: failed to disassemble default alternative", alt_name);
+		goto done;
+	}
+
 	/*
-	 * Initialize all other alternatives.
+	 * Initialize and disassemble all other alternatives.
 	 */
 	i = 1;
 	for (alt = orig_insn->alts; alt; alt = alt->next) {
@@ -656,35 +771,52 @@ static void *disas_alt(struct disas_context *dctx,
 			WARN("%s has more alternatives than supported", alt_name);
 			break;
 		}
-		err = disas_alt_init(&dalts[i], orig_insn, alt);
+		dalt = &dalts[i];
+		err = disas_alt_init(dalt, orig_insn, alt);
 		if (err) {
 			WARN("%s: failed to disassemble alternative", alt_name);
 			goto done;
 		}
 
+		/*
+		 * Only group alternatives are supported at the moment.
+		 */
+		switch (dalt->alt->type) {
+		case ALT_TYPE_INSTRUCTIONS:
+			count = disas_alt_group(dctx, dalt);
+			break;
+		default:
+			count = 0;
+		}
+		if (count < 0) {
+			WARN("%s: failed to disassemble alternative %s",
+			     alt_name, dalt->name);
+			goto done;
+		}
+
+		insn_count = count > insn_count ? count : insn_count;
 		i++;
 	}
 	alt_count = i;
 
 	/*
 	 * Print default and non-default alternatives.
-	 *
-	 * At the moment, this just prints an header for each alternative.
 	 */
-	disas_alt_print_compact(alt_name, dalts, alt_count);
+	disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
+
+	last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
+		orig_insn;
 
 done:
-	for (i = 0; i < alt_count; i++)
+	for (i = 0; i < alt_count; i++) {
 		free(dalts[i].name);
+		for (j = 0; j < insn_count; j++)
+			free(dalts[i].insn[j]);
+	}
 
 	free(alt_name);
 
-	/*
-	 * Currently we are not disassembling any alternative but just
-	 * printing alternative names. Return NULL to have disas_func()
-	 * resume the disassembly with the default alternative.
-	 */
-	return NULL;
+	return last_insn;
 }
 
 /*

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Print headers for alternatives
  2025-11-21  9:53 ` [PATCH v6 19/30] objtool: Print headers for alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Print headers for alternatives

When using the --disas option, objtool doesn't currently disassemble
any alternative. Print an header for each alternative. This identifies
places where alternatives are present but alternative code is still
not disassembled at the moment.

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-20-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 188 +++++++++++++++++++++++++++++++++++++++--
 1 file changed, 182 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 9cc952e..f9b13d5 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -29,6 +29,43 @@ struct disas_context {
 	struct disassemble_info info;
 };
 
+/*
+ * Maximum number of alternatives
+ */
+#define DISAS_ALT_MAX		5
+
+/*
+ * Maximum number of instructions per alternative
+ */
+#define DISAS_ALT_INSN_MAX	50
+
+/*
+ * Information to disassemble an alternative
+ */
+struct disas_alt {
+	struct instruction *orig_insn;		/* original instruction */
+	struct alternative *alt;		/* alternative or NULL if default code */
+	char *name;				/* name for this alternative */
+	int width;				/* formatting width */
+};
+
+/*
+ * Wrapper around asprintf() to allocate and format a string.
+ * Return the allocated string or NULL on error.
+ */
+static char *strfmt(const char *fmt, ...)
+{
+	va_list ap;
+	char *str;
+	int rv;
+
+	va_start(ap, fmt);
+	rv = vasprintf(&str, fmt, ap);
+	va_end(ap);
+
+	return rv == -1 ? NULL : str;
+}
+
 static int sprint_name(char *str, const char *name, unsigned long offset)
 {
 	int len;
@@ -314,6 +351,9 @@ char *disas_result(struct disas_context *dctx)
 #define DISAS_INSN_OFFSET_SPACE		10
 #define DISAS_INSN_SPACE		60
 
+#define DISAS_PRINSN(dctx, insn, depth)			\
+	disas_print_insn(stdout, dctx, insn, depth, "\n")
+
 /*
  * Print a message in the instruction flow. If insn is not NULL then
  * the instruction address is printed in addition of the message,
@@ -354,6 +394,19 @@ static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
 	return n;
 }
 
+static int disas_print(FILE *stream, struct section *sec, unsigned long offset,
+			int depth, const char *format, ...)
+{
+	va_list args;
+	int len;
+
+	va_start(args, format);
+	len = disas_vprint(stream, sec, offset, depth, format, args);
+	va_end(args);
+
+	return len;
+}
+
 /*
  * Print a message in the instruction flow. If insn is not NULL then
  * the instruction address is printed in addition of the message,
@@ -524,20 +577,143 @@ char *disas_alt_name(struct alternative *alt)
 }
 
 /*
+ * Initialize an alternative. The default alternative should be initialized
+ * with alt=NULL.
+ */
+static int disas_alt_init(struct disas_alt *dalt,
+			  struct instruction *orig_insn,
+			  struct alternative *alt)
+{
+	dalt->orig_insn = orig_insn;
+	dalt->alt = alt;
+	dalt->name = alt ? disas_alt_name(alt) : strdup("DEFAULT");
+	if (!dalt->name)
+		return -1;
+	dalt->width = strlen(dalt->name);
+
+	return 0;
+}
+
+/*
+ * Print all alternatives one above the other.
+ */
+static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
+				    int alt_count)
+{
+	struct instruction *orig_insn;
+	int len;
+	int i;
+
+	orig_insn = dalts[0].orig_insn;
+
+	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
+	printf("%s\n", alt_name);
+
+	for (i = 0; i < alt_count; i++)
+		printf("%*s= %s\n", len, "", dalts[i].name);
+}
+
+/*
+ * Disassemble an alternative.
+ *
+ * Return the last instruction in the default alternative so that
+ * disassembly can continue with the next instruction. Return NULL
+ * on error.
+ */
+static void *disas_alt(struct disas_context *dctx,
+		       struct instruction *orig_insn)
+{
+	struct disas_alt dalts[DISAS_ALT_MAX] = { 0 };
+	struct alternative *alt;
+	int alt_count = 0;
+	char *alt_name;
+	int err;
+	int i;
+
+	alt_name = strfmt("<%s.%lx>", disas_alt_type_name(orig_insn),
+			  orig_insn->offset);
+	if (!alt_name) {
+		WARN("Failed to define name for alternative at instruction 0x%lx",
+		     orig_insn->offset);
+		goto done;
+	}
+
+	/*
+	 * Initialize the default alternative.
+	 */
+	err = disas_alt_init(&dalts[0], orig_insn, NULL);
+	if (err) {
+		WARN("%s: failed to initialize default alternative", alt_name);
+		goto done;
+	}
+
+	/*
+	 * Initialize all other alternatives.
+	 */
+	i = 1;
+	for (alt = orig_insn->alts; alt; alt = alt->next) {
+		if (i >= DISAS_ALT_MAX) {
+			WARN("%s has more alternatives than supported", alt_name);
+			break;
+		}
+		err = disas_alt_init(&dalts[i], orig_insn, alt);
+		if (err) {
+			WARN("%s: failed to disassemble alternative", alt_name);
+			goto done;
+		}
+
+		i++;
+	}
+	alt_count = i;
+
+	/*
+	 * Print default and non-default alternatives.
+	 *
+	 * At the moment, this just prints an header for each alternative.
+	 */
+	disas_alt_print_compact(alt_name, dalts, alt_count);
+
+done:
+	for (i = 0; i < alt_count; i++)
+		free(dalts[i].name);
+
+	free(alt_name);
+
+	/*
+	 * Currently we are not disassembling any alternative but just
+	 * printing alternative names. Return NULL to have disas_func()
+	 * resume the disassembly with the default alternative.
+	 */
+	return NULL;
+}
+
+/*
  * Disassemble a function.
  */
 static void disas_func(struct disas_context *dctx, struct symbol *func)
 {
+	struct instruction *insn_start;
 	struct instruction *insn;
-	size_t addr;
 
 	printf("%s:\n", func->name);
 	sym_for_each_insn(dctx->file, func, insn) {
-		addr = insn->offset;
-		disas_insn(dctx, insn);
-		printf(" %6lx:  %s+0x%-6lx      %s\n",
-		       addr, func->name, addr - func->offset,
-		       disas_result(dctx));
+		if (insn->alts) {
+			insn_start = insn;
+			insn = disas_alt(dctx, insn);
+			if (insn)
+				continue;
+			/*
+			 * There was an error with disassembling
+			 * the alternative. Resume disassembling
+			 * at the current instruction, this will
+			 * disassemble the default alternative
+			 * only and continue with the code after
+			 * the alternative.
+			 */
+			insn = insn_start;
+		}
+
+		DISAS_PRINSN(dctx, insn, 0);
 	}
 	printf("\n");
 }

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Preserve alternatives order
  2025-11-21  9:53 ` [PATCH v6 18/30] objtool: Preserve alternatives order Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Preserve alternatives order

Preserve the order in which alternatives are defined. Currently
objtool stores alternatives in a list in reverse order.

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-19-alexandre.chartre@oracle.com
---
 tools/objtool/check.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 9cd9f9d..f75364f 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -1921,6 +1921,7 @@ static int add_special_section_alts(struct objtool_file *file)
 	struct special_alt *special_alt, *tmp;
 	enum alternative_type alt_type;
 	struct alternative *alt;
+	struct alternative *a;
 
 	if (special_get_alts(file->elf, &special_alts))
 		return -1;
@@ -1973,9 +1974,20 @@ static int add_special_section_alts(struct objtool_file *file)
 		}
 
 		alt->insn = new_insn;
-		alt->next = orig_insn->alts;
 		alt->type = alt_type;
-		orig_insn->alts = alt;
+		alt->next = NULL;
+
+		/*
+		 * Store alternatives in the same order they have been
+		 * defined.
+		 */
+		if (!orig_insn->alts) {
+			orig_insn->alts = alt;
+		} else {
+			for (a = orig_insn->alts; a->next; a = a->next)
+				;
+			a->next = alt;
+		}
 
 		list_del(&special_alt->list);
 		free(special_alt);

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Do not validate IBT for .return_sites and .call_sites
  2025-11-21  9:53 ` [PATCH v6 16/30] objtool: Do not validate IBT for .return_sites and .call_sites Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Do not validate IBT for .return_sites and .call_sites

The .return_sites and .call_sites sections reference text addresses,
but not with the intent to indirect branch to them, so they don't
need to be validated for IBT.

This is useful when running objtool on object files which already
have .return_sites or .call_sites sections, for example to re-run
objtool after it has reported an error or a warning.

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-17-alexandre.chartre@oracle.com
---
 tools/objtool/check.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 442b655..4ebadf9 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4753,6 +4753,8 @@ static int validate_ibt(struct objtool_file *file)
 		    !strcmp(sec->name, ".llvm.call-graph-profile")	||
 		    !strcmp(sec->name, ".llvm_bb_addr_map")		||
 		    !strcmp(sec->name, "__tracepoints")			||
+		    !strcmp(sec->name, ".return_sites")			||
+		    !strcmp(sec->name, ".call_sites")			||
 		    !strcmp(sec->name, "__patchable_function_entries"))
 			continue;
 

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add the --disas=<function-pattern> action
  2025-11-21  9:53 ` [PATCH v6 17/30] objtool: Add the --disas=<function-pattern> action Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Add the --disas=<function-pattern> action

Add the --disas=<function-pattern> actions to disassemble the specified
functions. The function pattern can be a single function name (e.g.
--disas foo to disassemble the function with the name "foo"), or a shell
wildcard pattern (e.g. --disas foo* to disassemble all functions with a
name starting with "foo").

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-18-alexandre.chartre@oracle.com
---
 tools/objtool/builtin-check.c           |  2 +-
 tools/objtool/check.c                   | 38 +++++++++++++-----------
 tools/objtool/disas.c                   | 27 +++++++++++++++++-
 tools/objtool/include/objtool/builtin.h |  1 +-
 tools/objtool/include/objtool/disas.h   |  2 +-
 5 files changed, 53 insertions(+), 17 deletions(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index 3329d37..a037131 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -75,6 +75,7 @@ static const struct option check_options[] = {
 	OPT_GROUP("Actions:"),
 	OPT_BOOLEAN(0,		 "checksum", &opts.checksum, "generate per-function checksums"),
 	OPT_BOOLEAN(0,		 "cfi", &opts.cfi, "annotate kernel control flow integrity (kCFI) function preambles"),
+	OPT_STRING_OPTARG('d',	 "disas", &opts.disas, "function-pattern", "disassemble functions", "*"),
 	OPT_CALLBACK_OPTARG('h', "hacks", NULL, NULL, "jump_label,noinstr,skylake", "patch toolchain bugs/limitations", parse_hacks),
 	OPT_BOOLEAN('i',	 "ibt", &opts.ibt, "validate and annotate IBT"),
 	OPT_BOOLEAN('m',	 "mcount", &opts.mcount, "annotate mcount/fentry calls for ftrace"),
@@ -176,6 +177,7 @@ static bool opts_valid(void)
 	}
 
 	if (opts.checksum		||
+	    opts.disas			||
 	    opts.hack_jump_label	||
 	    opts.hack_noinstr		||
 	    opts.ibt			||
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 4ebadf9..9cd9f9d 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -2611,7 +2611,7 @@ static int decode_sections(struct objtool_file *file)
 	 * Must be before add_jump_destinations(), which depends on 'func'
 	 * being set for alternatives, to enable proper sibling call detection.
 	 */
-	if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label) {
+	if (validate_branch_enabled() || opts.noinstr || opts.hack_jump_label || opts.disas) {
 		if (add_special_section_alts(file))
 			return -1;
 	}
@@ -4915,14 +4915,15 @@ int check(struct objtool_file *file)
 	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.
+	 * Create a disassembly context if we might disassemble any
+	 * instruction or function.
 	 */
-	if (opts.verbose || opts.backtrace || opts.trace) {
+	if (opts.verbose || opts.backtrace || opts.trace || opts.disas) {
 		disas_ctx = disas_context_create(file);
-		if (!disas_ctx)
+		if (!disas_ctx) {
+			opts.disas = false;
 			opts.trace = false;
+		}
 		objtool_disas_ctx = disas_ctx;
 	}
 
@@ -5054,20 +5055,20 @@ int check(struct objtool_file *file)
 	}
 
 out:
-	if (!ret && !warnings) {
-		free_insns(file);
-		return 0;
-	}
-
-	if (opts.werror && warnings)
-		ret = 1;
-
-	if (opts.verbose) {
+	if (ret || warnings) {
 		if (opts.werror && warnings)
-			WARN("%d warning(s) upgraded to errors", warnings);
-		disas_warned_funcs(disas_ctx);
+			ret = 1;
+
+		if (opts.verbose) {
+			if (opts.werror && warnings)
+				WARN("%d warning(s) upgraded to errors", warnings);
+			disas_warned_funcs(disas_ctx);
+		}
 	}
 
+	if (opts.disas)
+		disas_funcs(disas_ctx);
+
 	if (disas_ctx) {
 		disas_context_destroy(disas_ctx);
 		objtool_disas_ctx = NULL;
@@ -5075,6 +5076,9 @@ out:
 
 	free_insns(file);
 
+	if (!ret && !warnings)
+		return 0;
+
 	if (opts.backup && make_backup())
 		return 1;
 
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index b53be24..9cc952e 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -4,6 +4,7 @@
  */
 
 #define _GNU_SOURCE
+#include <fnmatch.h>
 
 #include <objtool/arch.h>
 #include <objtool/check.h>
@@ -556,3 +557,29 @@ void disas_warned_funcs(struct disas_context *dctx)
 			disas_func(dctx, sym);
 	}
 }
+
+void disas_funcs(struct disas_context *dctx)
+{
+	bool disas_all = !strcmp(opts.disas, "*");
+	struct section *sec;
+	struct symbol *sym;
+
+	for_each_sec(dctx->file->elf, sec) {
+
+		if (!(sec->sh.sh_flags & SHF_EXECINSTR))
+			continue;
+
+		sec_for_each_sym(sec, sym) {
+			/*
+			 * If the function had a warning and the verbose
+			 * option is used then the function was already
+			 * disassemble.
+			 */
+			if (opts.verbose && sym->warned)
+				continue;
+
+			if (disas_all || fnmatch(opts.disas, sym->name, 0) == 0)
+				disas_func(dctx, sym);
+		}
+	}
+}
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index 991365c..e3af664 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -28,6 +28,7 @@ struct opts {
 	bool static_call;
 	bool uaccess;
 	int prefix;
+	const char *disas;
 
 	/* options: */
 	bool backtrace;
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 8959d4c..e8f395e 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -15,6 +15,7 @@ struct disassemble_info;
 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);
+void disas_funcs(struct disas_context *dctx);
 int disas_info_init(struct disassemble_info *dinfo,
 		    int arch, int mach32, int mach64,
 		    const char *options);
@@ -40,6 +41,7 @@ static inline struct disas_context *disas_context_create(struct objtool_file *fi
 
 static inline void disas_context_destroy(struct disas_context *dctx) {}
 static inline void disas_warned_funcs(struct disas_context *dctx) {}
+static inline void disas_funcs(struct disas_context *dctx) {}
 
 static inline int disas_info_init(struct disassemble_info *dinfo,
 				  int arch, int mach32, int mach64,

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Improve tracing of alternative instructions
  2025-11-21  9:53 ` [PATCH v6 15/30] objtool: Improve tracing of alternative instructions Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Improve tracing of alternative instructions

When tracing function validation, improve the reporting of
alternative instruction by more clearly showing the different
alternatives beginning and end.

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-16-alexandre.chartre@oracle.com
---
 tools/objtool/check.c                 | 18 ++-----
 tools/objtool/include/objtool/trace.h | 65 +++++++++++++++++++++++++-
 tools/objtool/trace.c                 | 55 ++++++++++++++++++++++-
 3 files changed, 125 insertions(+), 13 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 93aaa4b..442b655 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3564,7 +3564,7 @@ static bool skip_alt_group(struct instruction *insn)
 
 	/* ANNOTATE_IGNORE_ALTERNATIVE */
 	if (insn->alt_group->ignore) {
-		TRACE_INSN(insn, "alt group ignored");
+		TRACE_ALT(insn, "alt group ignored");
 		return true;
 	}
 
@@ -3680,8 +3680,9 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 struct instruction *prev_insn, struct instruction *next_insn,
 			 bool *dead_end)
 {
-	/* prev_state is not used if there is no disassembly support */
+	/* prev_state and alt_name are not used if there is no disassembly support */
 	struct insn_state prev_state __maybe_unused;
+	char *alt_name __maybe_unused = NULL;
 	struct alternative *alt;
 	u8 visited;
 	int ret;
@@ -3768,23 +3769,16 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		return 1;
 
 	if (insn->alts) {
-		int i, num_alts;
-
-		num_alts = 0;
-		for (alt = insn->alts; alt; alt = alt->next)
-			num_alts++;
-
-		i = 1;
 		for (alt = insn->alts; alt; alt = alt->next) {
-			TRACE_INSN(insn, "alternative %d/%d", i, num_alts);
+			TRACE_ALT_BEGIN(insn, alt, alt_name);
 			ret = validate_branch(file, func, alt->insn, *statep);
+			TRACE_ALT_END(insn, alt, alt_name);
 			if (ret) {
 				BT_INSN(insn, "(alt)");
 				return ret;
 			}
-			i++;
 		}
-		TRACE_INSN(insn, "alternative DEFAULT");
+		TRACE_ALT_INFO_NOADDR(insn, "/ ", "DEFAULT");
 	}
 
 	if (skip_alt_group(insn))
diff --git a/tools/objtool/include/objtool/trace.h b/tools/objtool/include/objtool/trace.h
index 33fe9c6..70b5743 100644
--- a/tools/objtool/include/objtool/trace.h
+++ b/tools/objtool/include/objtool/trace.h
@@ -19,11 +19,26 @@ extern int trace_depth;
 		fprintf(stderr, fmt, ##__VA_ARGS__);		\
 })
 
+/*
+ * Print the instruction address and a message. The instruction
+ * itself is not printed.
+ */
+#define TRACE_ADDR(insn, fmt, ...)				\
+({								\
+	if (trace) {						\
+		disas_print_info(stderr, insn, trace_depth - 1, \
+				 fmt "\n", ##__VA_ARGS__);	\
+	}							\
+})
+
+/*
+ * Print the instruction address, the instruction and a message.
+ */
 #define TRACE_INSN(insn, fmt, ...)				\
 ({								\
 	if (trace) {						\
 		disas_print_insn(stderr, objtool_disas_ctx,	\
-				 insn, trace_depth - 1,	\
+				 insn, trace_depth - 1,		\
 				 fmt, ##__VA_ARGS__);		\
 		fprintf(stderr, "\n");				\
 		insn->trace = 1;				\
@@ -36,6 +51,37 @@ extern int trace_depth;
 		trace_insn_state(insn, sprev, snext);		\
 })
 
+#define TRACE_ALT_FMT(pfx, fmt) pfx "<%s.%lx> " fmt
+#define TRACE_ALT_ARG(insn) disas_alt_type_name(insn), (insn)->offset
+
+#define TRACE_ALT(insn, fmt, ...)				\
+	TRACE_INSN(insn, TRACE_ALT_FMT("", fmt),		\
+		   TRACE_ALT_ARG(insn), ##__VA_ARGS__)
+
+#define TRACE_ALT_INFO(insn, pfx, fmt, ...)			\
+	TRACE_ADDR(insn, TRACE_ALT_FMT(pfx, fmt),		\
+		   TRACE_ALT_ARG(insn), ##__VA_ARGS__)
+
+#define TRACE_ALT_INFO_NOADDR(insn, pfx, fmt, ...)		\
+	TRACE_ADDR(NULL, TRACE_ALT_FMT(pfx, fmt),		\
+		   TRACE_ALT_ARG(insn), ##__VA_ARGS__)
+
+#define TRACE_ALT_BEGIN(insn, alt, alt_name)			\
+({								\
+	if (trace) {						\
+		alt_name = disas_alt_name(alt);			\
+		trace_alt_begin(insn, alt, alt_name);		\
+	}							\
+})
+
+#define TRACE_ALT_END(insn, alt, alt_name)			\
+({								\
+	if (trace) {						\
+		trace_alt_end(insn, alt, alt_name);		\
+		free(alt_name);					\
+	}							\
+})
+
 static inline void trace_enable(void)
 {
 	trace = true;
@@ -61,17 +107,34 @@ static inline void trace_depth_dec(void)
 
 void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
 		      struct insn_state *snext);
+void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
+		     char *alt_name);
+void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
+		   char *alt_name);
 
 #else /* DISAS */
 
 #define TRACE(fmt, ...) ({})
+#define TRACE_ADDR(insn, fmt, ...) ({})
 #define TRACE_INSN(insn, fmt, ...) ({})
 #define TRACE_INSN_STATE(insn, sprev, snext) ({})
+#define TRACE_ALT(insn, fmt, ...) ({})
+#define TRACE_ALT_INFO(insn, fmt, ...) ({})
+#define TRACE_ALT_INFO_NOADDR(insn, fmt, ...) ({})
+#define TRACE_ALT_BEGIN(insn, alt, alt_name) ({})
+#define TRACE_ALT_END(insn, alt, alt_name) ({})
+
 
 static inline void trace_enable(void) {}
 static inline void trace_disable(void) {}
 static inline void trace_depth_inc(void) {}
 static inline void trace_depth_dec(void) {}
+static inline void trace_alt_begin(struct instruction *orig_insn,
+				   struct alternative *alt,
+				   char *alt_name) {};
+static inline void trace_alt_end(struct instruction *orig_insn,
+				 struct alternative *alt,
+				 char *alt_name) {};
 
 #endif
 
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
index d70d470..5dec44d 100644
--- a/tools/objtool/trace.c
+++ b/tools/objtool/trace.c
@@ -146,3 +146,58 @@ void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
 
 	insn->trace = 1;
 }
+
+void trace_alt_begin(struct instruction *orig_insn, struct alternative *alt,
+		     char *alt_name)
+{
+	struct instruction *alt_insn;
+	char suffix[2];
+
+	alt_insn = alt->insn;
+
+	if (alt->type == ALT_TYPE_EX_TABLE) {
+		/*
+		 * When there is an exception table then the instruction
+		 * at the original location is executed but it can cause
+		 * an exception. In that case, the execution will be
+		 * redirected to the alternative instruction.
+		 *
+		 * The instruction at the original location can have
+		 * instruction alternatives, so we just print the location
+		 * of the instruction that can cause the exception and
+		 * not the instruction itself.
+		 */
+		TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s for instruction at 0x%lx <%s+0x%lx>",
+				      alt_name,
+				      orig_insn->offset, orig_insn->sym->name,
+				      orig_insn->offset - orig_insn->sym->offset);
+	} else {
+		TRACE_ALT_INFO_NOADDR(orig_insn, "/ ", "%s", alt_name);
+	}
+
+	if (alt->type == ALT_TYPE_JUMP_TABLE) {
+		/*
+		 * For a jump alternative, if the default instruction is
+		 * a NOP then it is replaced with the jmp instruction,
+		 * otherwise it is replaced with a NOP instruction.
+		 */
+		trace_depth++;
+		if (orig_insn->type == INSN_NOP) {
+			suffix[0] = (orig_insn->len == 5) ? 'q' : '\0';
+			TRACE_ADDR(orig_insn, "jmp%-3s %lx <%s+0x%lx>", suffix,
+				   alt_insn->offset, alt_insn->sym->name,
+				   alt_insn->offset - alt_insn->sym->offset);
+		} else {
+			TRACE_ADDR(orig_insn, "nop%d", orig_insn->len);
+			trace_depth--;
+		}
+	}
+}
+
+void trace_alt_end(struct instruction *orig_insn, struct alternative *alt,
+		   char *alt_name)
+{
+	if (alt->type == ALT_TYPE_JUMP_TABLE && orig_insn->type == INSN_NOP)
+		trace_depth--;
+	TRACE_ALT_INFO_NOADDR(orig_insn, "\\ ", "%s", alt_name);
+}

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add functions to better name alternatives
  2025-11-21  9:53 ` [PATCH v6 14/30] objtool: Add functions to better name alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Add functions to better name alternatives

Add the disas_alt_name() and disas_alt_type_name() to provide a
name and a type name for an alternative. This will be used to
better name alternatives when tracing their execution.

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-15-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c                 | 72 ++++++++++++++++++++++++++-
 tools/objtool/include/objtool/disas.h | 12 ++++-
 2 files changed, 84 insertions(+)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 0ca6e6c..b53be24 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -3,6 +3,8 @@
  * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
  */
 
+#define _GNU_SOURCE
+
 #include <objtool/arch.h>
 #include <objtool/check.h>
 #include <objtool/disas.h>
@@ -451,6 +453,76 @@ size_t disas_insn(struct disas_context *dctx, struct instruction *insn)
 }
 
 /*
+ * Provide a name for the type of alternatives present at the
+ * specified instruction.
+ *
+ * An instruction can have alternatives with different types, for
+ * example alternative instructions and an exception table. In that
+ * case the name for the alternative instructions type is used.
+ *
+ * Return NULL if the instruction as no alternative.
+ */
+const char *disas_alt_type_name(struct instruction *insn)
+{
+	struct alternative *alt;
+	const char *name;
+
+	name = NULL;
+	for (alt = insn->alts; alt; alt = alt->next) {
+		if (alt->type == ALT_TYPE_INSTRUCTIONS) {
+			name = "alternative";
+			break;
+		}
+
+		switch (alt->type) {
+		case ALT_TYPE_EX_TABLE:
+			name = "ex_table";
+			break;
+		case ALT_TYPE_JUMP_TABLE:
+			name = "jump_table";
+			break;
+		default:
+			name = "unknown";
+			break;
+		}
+	}
+
+	return name;
+}
+
+/*
+ * Provide a name for an alternative.
+ */
+char *disas_alt_name(struct alternative *alt)
+{
+	char *str = NULL;
+
+	switch (alt->type) {
+
+	case ALT_TYPE_EX_TABLE:
+		str = strdup("EXCEPTION");
+		break;
+
+	case ALT_TYPE_JUMP_TABLE:
+		str = strdup("JUMP");
+		break;
+
+	case ALT_TYPE_INSTRUCTIONS:
+		/*
+		 * This is a non-default group alternative. Create a unique
+		 * name using the offset of the first original and alternative
+		 * instructions.
+		 */
+		asprintf(&str, "ALTERNATIVE %lx.%lx",
+			 alt->insn->alt_group->orig_group->first_insn->offset,
+			 alt->insn->alt_group->first_insn->offset);
+		break;
+	}
+
+	return str;
+}
+
+/*
  * Disassemble a function.
  */
 static void disas_func(struct disas_context *dctx, struct symbol *func)
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 5db75d0..8959d4c 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -6,6 +6,7 @@
 #ifndef _DISAS_H
 #define _DISAS_H
 
+struct alternative;
 struct disas_context;
 struct disassemble_info;
 
@@ -24,6 +25,8 @@ void disas_print_info(FILE *stream, struct instruction *insn, int depth,
 void disas_print_insn(FILE *stream, struct disas_context *dctx,
 		      struct instruction *insn, int depth,
 		      const char *format, ...);
+char *disas_alt_name(struct alternative *alt);
+const char *disas_alt_type_name(struct instruction *insn);
 
 #else /* DISAS */
 
@@ -61,6 +64,15 @@ static inline void disas_print_info(FILE *stream, struct instruction *insn,
 static inline void disas_print_insn(FILE *stream, struct disas_context *dctx,
 				    struct instruction *insn, int depth,
 				    const char *format, ...) {}
+static inline char *disas_alt_name(struct alternative *alt)
+{
+	return NULL;
+}
+
+static inline const char *disas_alt_type_name(struct instruction *insn)
+{
+	return NULL;
+}
 
 #endif /* DISAS */
 

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Identify the different types of alternatives
  2025-11-21  9:53 ` [PATCH v6 13/30] objtool: Identify the different types of alternatives Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Identify the different types of alternatives

Alternative code, including jump table and exception table, is represented
with the same struct alternative structure. But there is no obvious way to
identify whether the struct represents alternative instructions, a jump
table or an exception table.

So add a type to struct alternative to clearly identify the type of
alternative.

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-14-alexandre.chartre@oracle.com
---
 tools/objtool/check.c                 | 13 ++++++++-----
 tools/objtool/include/objtool/check.h | 12 ++++++++++++
 2 files changed, 20 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index a02f8db..93aaa4b 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -27,11 +27,6 @@
 #include <linux/static_call_types.h>
 #include <linux/string.h>
 
-struct alternative {
-	struct alternative *next;
-	struct instruction *insn;
-};
-
 static unsigned long nr_cfi, nr_cfi_reused, nr_cfi_cache;
 
 static struct cfi_init_state initial_func_cfi;
@@ -1924,6 +1919,7 @@ static int add_special_section_alts(struct objtool_file *file)
 	struct list_head special_alts;
 	struct instruction *orig_insn, *new_insn;
 	struct special_alt *special_alt, *tmp;
+	enum alternative_type alt_type;
 	struct alternative *alt;
 
 	if (special_get_alts(file->elf, &special_alts))
@@ -1959,9 +1955,15 @@ static int add_special_section_alts(struct objtool_file *file)
 			if (handle_group_alt(file, special_alt, orig_insn, &new_insn))
 				return -1;
 
+			alt_type = ALT_TYPE_INSTRUCTIONS;
+
 		} else if (special_alt->jump_or_nop) {
 			if (handle_jump_alt(file, special_alt, orig_insn, &new_insn))
 				return -1;
+
+			alt_type = ALT_TYPE_JUMP_TABLE;
+		} else {
+			alt_type = ALT_TYPE_EX_TABLE;
 		}
 
 		alt = calloc(1, sizeof(*alt));
@@ -1972,6 +1974,7 @@ static int add_special_section_alts(struct objtool_file *file)
 
 		alt->insn = new_insn;
 		alt->next = orig_insn->alts;
+		alt->type = alt_type;
 		orig_insn->alts = alt;
 
 		list_del(&special_alt->list);
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index fde9586..cbf4af5 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -38,6 +38,18 @@ struct alt_group {
 	bool ignore;
 };
 
+enum alternative_type {
+	ALT_TYPE_INSTRUCTIONS,
+	ALT_TYPE_JUMP_TABLE,
+	ALT_TYPE_EX_TABLE,
+};
+
+struct alternative {
+	struct alternative *next;
+	struct instruction *insn;
+	enum alternative_type type;
+};
+
 #define INSN_CHUNK_BITS		8
 #define INSN_CHUNK_SIZE		(1 << INSN_CHUNK_BITS)
 #define INSN_CHUNK_MAX		(INSN_CHUNK_SIZE - 1)

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Improve register reporting during function validation
  2025-11-21  9:53 ` [PATCH v6 12/30] objtool: Improve register reporting " Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Improve register reporting during function validation

When tracing function validation, instruction state changes can
report changes involving registers. These registers are reported
with the name "r<num>" (e.g. "r3"). Print the CPU specific register
name instead of a generic name (e.g. print "rbx" instead of "r3"
on x86).

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-13-alexandre.chartre@oracle.com
---
 tools/objtool/arch/loongarch/decode.c | 11 +++++++++++
 tools/objtool/arch/powerpc/decode.c   | 12 ++++++++++++
 tools/objtool/arch/x86/decode.c       |  8 ++++++++
 tools/objtool/include/objtool/arch.h  |  2 ++
 tools/objtool/trace.c                 |  7 +++++++
 5 files changed, 40 insertions(+)

diff --git a/tools/objtool/arch/loongarch/decode.c b/tools/objtool/arch/loongarch/decode.c
index 1de86eb..6cd2881 100644
--- a/tools/objtool/arch/loongarch/decode.c
+++ b/tools/objtool/arch/loongarch/decode.c
@@ -8,6 +8,17 @@
 #include <linux/objtool_types.h>
 #include <arch/elf.h>
 
+const char *arch_reg_name[CFI_NUM_REGS] = {
+	"zero", "ra", "tp", "sp",
+	"a0", "a1", "a2", "a3",
+	"a4", "a5", "a6", "a7",
+	"t0", "t1", "t2", "t3",
+	"t4", "t5", "t6", "t7",
+	"t8", "u0", "fp", "s0",
+	"s1", "s2", "s3", "s4",
+	"s5", "s6", "s7", "s8"
+};
+
 int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "_mcount");
diff --git a/tools/objtool/arch/powerpc/decode.c b/tools/objtool/arch/powerpc/decode.c
index 4f68b40..e534ac1 100644
--- a/tools/objtool/arch/powerpc/decode.c
+++ b/tools/objtool/arch/powerpc/decode.c
@@ -9,6 +9,18 @@
 #include <objtool/warn.h>
 #include <objtool/builtin.h>
 
+const char *arch_reg_name[CFI_NUM_REGS] = {
+	"r0",  "sp",  "r2",  "r3",
+	"r4",  "r5",  "r6",  "r7",
+	"r8",  "r9",  "r10", "r11",
+	"r12", "r13", "r14", "r15",
+	"r16", "r17", "r18", "r19",
+	"r20", "r21", "r22", "r23",
+	"r24", "r25", "r26", "r27",
+	"r28", "r29", "r30", "r31",
+	"ra"
+};
+
 int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "_mcount");
diff --git a/tools/objtool/arch/x86/decode.c b/tools/objtool/arch/x86/decode.c
index 83e9c60..f4af825 100644
--- a/tools/objtool/arch/x86/decode.c
+++ b/tools/objtool/arch/x86/decode.c
@@ -23,6 +23,14 @@
 #include <objtool/builtin.h>
 #include <arch/elf.h>
 
+const char *arch_reg_name[CFI_NUM_REGS] = {
+	"rax", "rcx", "rdx", "rbx",
+	"rsp", "rbp", "rsi", "rdi",
+	"r8",  "r9",  "r10", "r11",
+	"r12", "r13", "r14", "r15",
+	"ra"
+};
+
 int arch_ftrace_match(const char *name)
 {
 	return !strcmp(name, "__fentry__");
diff --git a/tools/objtool/include/objtool/arch.h b/tools/objtool/include/objtool/arch.h
index 18c0e69..8866158 100644
--- a/tools/objtool/include/objtool/arch.h
+++ b/tools/objtool/include/objtool/arch.h
@@ -103,6 +103,8 @@ 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);
 
+extern const char *arch_reg_name[CFI_NUM_REGS];
+
 #ifdef DISAS
 
 #include <bfd.h>
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
index 12bbad0..d70d470 100644
--- a/tools/objtool/trace.c
+++ b/tools/objtool/trace.c
@@ -34,6 +34,7 @@ int trace_depth;
 static const char *cfi_reg_name(unsigned int reg)
 {
 	static char rname_buffer[CFI_REG_NAME_MAXLEN];
+	const char *rname;
 
 	switch (reg) {
 	case CFI_UNDEFINED:
@@ -46,6 +47,12 @@ static const char *cfi_reg_name(unsigned int reg)
 		return "(bp)";
 	}
 
+	if (reg < CFI_NUM_REGS) {
+		rname = arch_reg_name[reg];
+		if (rname)
+			return rname;
+	}
+
 	if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
 		return "<error>";
 

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Trace instruction state changes during function validation
  2025-11-21  9:53 ` [PATCH v6 11/30] objtool: Trace instruction state changes during " Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  2025-12-01 20:23   ` [PATCH v6 11/30] " Nathan Chancellor
  1 sibling, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Trace instruction state changes during function validation

During function validation, objtool maintains a per-instruction state,
in particular to track call frame information. When tracing validation,
print any instruction state changes.

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-12-alexandre.chartre@oracle.com
---
 tools/objtool/check.c                 |   8 +-
 tools/objtool/include/objtool/trace.h |  10 ++-
 tools/objtool/trace.c                 | 132 +++++++++++++++++++++++++-
 3 files changed, 149 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 409dec9..a02f8db 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3677,6 +3677,8 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 struct instruction *prev_insn, struct instruction *next_insn,
 			 bool *dead_end)
 {
+	/* prev_state is not used if there is no disassembly support */
+	struct insn_state prev_state __maybe_unused;
 	struct alternative *alt;
 	u8 visited;
 	int ret;
@@ -3785,7 +3787,11 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	if (skip_alt_group(insn))
 		return 0;
 
-	if (handle_insn_ops(insn, next_insn, statep))
+	prev_state = *statep;
+	ret = handle_insn_ops(insn, next_insn, statep);
+	TRACE_INSN_STATE(insn, &prev_state, statep);
+
+	if (ret)
 		return 1;
 
 	switch (insn->type) {
diff --git a/tools/objtool/include/objtool/trace.h b/tools/objtool/include/objtool/trace.h
index 3f3c830..33fe9c6 100644
--- a/tools/objtool/include/objtool/trace.h
+++ b/tools/objtool/include/objtool/trace.h
@@ -30,6 +30,12 @@ extern int trace_depth;
 	}							\
 })
 
+#define TRACE_INSN_STATE(insn, sprev, snext)			\
+({								\
+	if (trace)						\
+		trace_insn_state(insn, sprev, snext);		\
+})
+
 static inline void trace_enable(void)
 {
 	trace = true;
@@ -53,10 +59,14 @@ static inline void trace_depth_dec(void)
 		trace_depth--;
 }
 
+void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
+		      struct insn_state *snext);
+
 #else /* DISAS */
 
 #define TRACE(fmt, ...) ({})
 #define TRACE_INSN(insn, fmt, ...) ({})
+#define TRACE_INSN_STATE(insn, sprev, snext) ({})
 
 static inline void trace_enable(void) {}
 static inline void trace_disable(void) {}
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
index 134cc33..12bbad0 100644
--- a/tools/objtool/trace.c
+++ b/tools/objtool/trace.c
@@ -7,3 +7,135 @@
 
 bool trace;
 int trace_depth;
+
+/*
+ * Macros to trace CFI state attributes changes.
+ */
+
+#define TRACE_CFI_ATTR(attr, prev, next, fmt, ...)		\
+({								\
+	if ((prev)->attr != (next)->attr)			\
+		TRACE("%s=" fmt " ", #attr, __VA_ARGS__);	\
+})
+
+#define TRACE_CFI_ATTR_BOOL(attr, prev, next)			\
+	TRACE_CFI_ATTR(attr, prev, next,			\
+		       "%s", (next)->attr ? "true" : "false")
+
+#define TRACE_CFI_ATTR_NUM(attr, prev, next, fmt)		\
+	TRACE_CFI_ATTR(attr, prev, next, fmt, (next)->attr)
+
+#define CFI_REG_NAME_MAXLEN   16
+
+/*
+ * Return the name of a register. Note that the same static buffer
+ * is returned if the name is dynamically generated.
+ */
+static const char *cfi_reg_name(unsigned int reg)
+{
+	static char rname_buffer[CFI_REG_NAME_MAXLEN];
+
+	switch (reg) {
+	case CFI_UNDEFINED:
+		return "<undefined>";
+	case CFI_CFA:
+		return "cfa";
+	case CFI_SP_INDIRECT:
+		return "(sp)";
+	case CFI_BP_INDIRECT:
+		return "(bp)";
+	}
+
+	if (snprintf(rname_buffer, CFI_REG_NAME_MAXLEN, "r%d", reg) == -1)
+		return "<error>";
+
+	return (const char *)rname_buffer;
+}
+
+/*
+ * Functions and macros to trace CFI registers changes.
+ */
+
+static void trace_cfi_reg(const char *prefix, int reg, const char *fmt,
+			  int base_prev, int offset_prev,
+			  int base_next, int offset_next)
+{
+	char *rname;
+
+	if (base_prev == base_next && offset_prev == offset_next)
+		return;
+
+	if (prefix)
+		TRACE("%s:", prefix);
+
+	if (base_next == CFI_UNDEFINED) {
+		TRACE("%1$s=<undef> ", cfi_reg_name(reg));
+	} else {
+		rname = strdup(cfi_reg_name(reg));
+		TRACE(fmt, rname, cfi_reg_name(base_next), offset_next);
+		free(rname);
+	}
+}
+
+static void trace_cfi_reg_val(const char *prefix, int reg,
+			      int base_prev, int offset_prev,
+			      int base_next, int offset_next)
+{
+	trace_cfi_reg(prefix, reg, "%1$s=%2$s%3$+d ",
+		      base_prev, offset_prev, base_next, offset_next);
+}
+
+static void trace_cfi_reg_ref(const char *prefix, int reg,
+			      int base_prev, int offset_prev,
+			      int base_next, int offset_next)
+{
+	trace_cfi_reg(prefix, reg, "%1$s=(%2$s%3$+d) ",
+		      base_prev, offset_prev, base_next, offset_next);
+}
+
+#define TRACE_CFI_REG_VAL(reg, prev, next)				\
+	trace_cfi_reg_val(NULL, reg, prev.base, prev.offset,		\
+			  next.base, next.offset)
+
+#define TRACE_CFI_REG_REF(reg, prev, next)				\
+	trace_cfi_reg_ref(NULL, reg, prev.base, prev.offset,		\
+			  next.base, next.offset)
+
+void trace_insn_state(struct instruction *insn, struct insn_state *sprev,
+		      struct insn_state *snext)
+{
+	struct cfi_state *cprev, *cnext;
+	int i;
+
+	if (!memcmp(sprev, snext, sizeof(struct insn_state)))
+		return;
+
+	cprev = &sprev->cfi;
+	cnext = &snext->cfi;
+
+	disas_print_insn(stderr, objtool_disas_ctx, insn,
+			 trace_depth - 1, "state: ");
+
+	/* print registers changes */
+	TRACE_CFI_REG_VAL(CFI_CFA, cprev->cfa, cnext->cfa);
+	for (i = 0; i < CFI_NUM_REGS; i++) {
+		TRACE_CFI_REG_VAL(i, cprev->vals[i], cnext->vals[i]);
+		TRACE_CFI_REG_REF(i, cprev->regs[i], cnext->regs[i]);
+	}
+
+	/* print attributes changes */
+	TRACE_CFI_ATTR_NUM(stack_size, cprev, cnext, "%d");
+	TRACE_CFI_ATTR_BOOL(drap, cprev, cnext);
+	if (cnext->drap) {
+		trace_cfi_reg_val("drap", cnext->drap_reg,
+				  cprev->drap_reg, cprev->drap_offset,
+				  cnext->drap_reg, cnext->drap_offset);
+	}
+	TRACE_CFI_ATTR_BOOL(bp_scratch, cprev, cnext);
+	TRACE_CFI_ATTR_NUM(instr, sprev, snext, "%d");
+	TRACE_CFI_ATTR_NUM(uaccess_stack, sprev, snext, "%u");
+
+	TRACE("\n");
+
+	insn->trace = 1;
+}

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add option to trace function validation
  2025-11-21  9:53 ` [PATCH v6 10/30] objtool: Add option to trace function validation Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Add option to trace function validation

Add an option to trace and have information during the validation
of specified functions. Functions are specified with the --trace
option which can be a single function name (e.g. --trace foo to
trace the function with the name "foo"), or a shell wildcard
pattern (e.g. --trace foo* to trace all functions with a name
starting with "foo").

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-11-alexandre.chartre@oracle.com
---
 tools/objtool/Build                     |   1 +-
 tools/objtool/builtin-check.c           |   1 +-
 tools/objtool/check.c                   | 104 +++++++++++++++++----
 tools/objtool/disas.c                   | 115 +++++++++++++++++++++++-
 tools/objtool/include/objtool/builtin.h |   1 +-
 tools/objtool/include/objtool/check.h   |   6 +-
 tools/objtool/include/objtool/disas.h   |  11 ++-
 tools/objtool/include/objtool/trace.h   |  68 ++++++++++++++-
 tools/objtool/include/objtool/warn.h    |   1 +-
 tools/objtool/trace.c                   |   9 ++-
 10 files changed, 299 insertions(+), 18 deletions(-)
 create mode 100644 tools/objtool/include/objtool/trace.h
 create mode 100644 tools/objtool/trace.c

diff --git a/tools/objtool/Build b/tools/objtool/Build
index 9d1e8f2..9982e66 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -9,6 +9,7 @@ objtool-y += elf.o
 objtool-y += objtool.o
 
 objtool-$(BUILD_DISAS) += disas.o
+objtool-$(BUILD_DISAS) += trace.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/builtin-check.c b/tools/objtool/builtin-check.c
index aab7fa9..3329d37 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -103,6 +103,7 @@ static const struct option check_options[] = {
 	OPT_STRING('o',		 "output", &opts.output, "file", "output file name"),
 	OPT_BOOLEAN(0,		 "sec-address", &opts.sec_address, "print section addresses in warnings"),
 	OPT_BOOLEAN(0,		 "stats", &opts.stats, "print statistics"),
+	OPT_STRING(0,		 "trace", &opts.trace, "func", "trace function validation"),
 	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
 	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
 
diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0fbf0eb..409dec9 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4,6 +4,7 @@
  */
 
 #define _GNU_SOURCE /* memmem() */
+#include <fnmatch.h>
 #include <string.h>
 #include <stdlib.h>
 #include <inttypes.h>
@@ -15,6 +16,7 @@
 #include <objtool/disas.h>
 #include <objtool/check.h>
 #include <objtool/special.h>
+#include <objtool/trace.h>
 #include <objtool/warn.h>
 #include <objtool/checksum.h>
 #include <objtool/util.h>
@@ -37,7 +39,9 @@ static struct cfi_state init_cfi;
 static struct cfi_state func_cfi;
 static struct cfi_state force_undefined_cfi;
 
-static size_t sym_name_max_len;
+struct disas_context *objtool_disas_ctx;
+
+size_t sym_name_max_len;
 
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset)
@@ -3556,8 +3560,10 @@ static bool skip_alt_group(struct instruction *insn)
 		return false;
 
 	/* ANNOTATE_IGNORE_ALTERNATIVE */
-	if (insn->alt_group->ignore)
+	if (insn->alt_group->ignore) {
+		TRACE_INSN(insn, "alt group ignored");
 		return true;
+	}
 
 	/*
 	 * For NOP patched with CLAC/STAC, only follow the latter to avoid
@@ -3663,6 +3669,8 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 
 static int validate_branch(struct objtool_file *file, struct symbol *func,
 			   struct instruction *insn, struct insn_state state);
+static int do_validate_branch(struct objtool_file *file, struct symbol *func,
+			      struct instruction *insn, struct insn_state state);
 
 static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 struct instruction *insn, struct insn_state *statep,
@@ -3684,8 +3692,10 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		if (!insn->hint && !insn_cfi_match(insn, &statep->cfi))
 			return 1;
 
-		if (insn->visited & visited)
+		if (insn->visited & visited) {
+			TRACE_INSN(insn, "already visited");
 			return 0;
+		}
 	} else {
 		nr_insns_visited++;
 	}
@@ -3722,8 +3732,10 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 				 * It will be seen later via the
 				 * straight-line path.
 				 */
-				if (!prev_insn)
+				if (!prev_insn) {
+					TRACE_INSN(insn, "defer restore");
 					return 0;
+				}
 
 				WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
 				return 1;
@@ -3751,13 +3763,23 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		return 1;
 
 	if (insn->alts) {
+		int i, num_alts;
+
+		num_alts = 0;
+		for (alt = insn->alts; alt; alt = alt->next)
+			num_alts++;
+
+		i = 1;
 		for (alt = insn->alts; alt; alt = alt->next) {
+			TRACE_INSN(insn, "alternative %d/%d", i, num_alts);
 			ret = validate_branch(file, func, alt->insn, *statep);
 			if (ret) {
 				BT_INSN(insn, "(alt)");
 				return ret;
 			}
+			i++;
 		}
+		TRACE_INSN(insn, "alternative DEFAULT");
 	}
 
 	if (skip_alt_group(insn))
@@ -3769,10 +3791,16 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	switch (insn->type) {
 
 	case INSN_RETURN:
+		TRACE_INSN(insn, "return");
 		return validate_return(func, insn, statep);
 
 	case INSN_CALL:
 	case INSN_CALL_DYNAMIC:
+		if (insn->type == INSN_CALL)
+			TRACE_INSN(insn, "call");
+		else
+			TRACE_INSN(insn, "indirect call");
+
 		ret = validate_call(file, insn, statep);
 		if (ret)
 			return ret;
@@ -3788,13 +3816,18 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 	case INSN_JUMP_CONDITIONAL:
 	case INSN_JUMP_UNCONDITIONAL:
 		if (is_sibling_call(insn)) {
+			TRACE_INSN(insn, "sibling call");
 			ret = validate_sibling_call(file, insn, statep);
 			if (ret)
 				return ret;
 
 		} else if (insn->jump_dest) {
-			ret = validate_branch(file, func,
-					      insn->jump_dest, *statep);
+			if (insn->type == INSN_JUMP_UNCONDITIONAL)
+				TRACE_INSN(insn, "unconditional jump");
+			else
+				TRACE_INSN(insn, "jump taken");
+
+			ret = validate_branch(file, func, insn->jump_dest, *statep);
 			if (ret) {
 				BT_INSN(insn, "(branch)");
 				return ret;
@@ -3804,10 +3837,12 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		if (insn->type == INSN_JUMP_UNCONDITIONAL)
 			return 0;
 
+		TRACE_INSN(insn, "jump not taken");
 		break;
 
 	case INSN_JUMP_DYNAMIC:
 	case INSN_JUMP_DYNAMIC_CONDITIONAL:
+		TRACE_INSN(insn, "indirect jump");
 		if (is_sibling_call(insn)) {
 			ret = validate_sibling_call(file, insn, statep);
 			if (ret)
@@ -3820,6 +3855,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_SYSCALL:
+		TRACE_INSN(insn, "syscall");
 		if (func && (!next_insn || !next_insn->hint)) {
 			WARN_INSN(insn, "unsupported instruction in callable function");
 			return 1;
@@ -3828,6 +3864,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_SYSRET:
+		TRACE_INSN(insn, "sysret");
 		if (func && (!next_insn || !next_insn->hint)) {
 			WARN_INSN(insn, "unsupported instruction in callable function");
 			return 1;
@@ -3836,6 +3873,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		return 0;
 
 	case INSN_STAC:
+		TRACE_INSN(insn, "stac");
 		if (!opts.uaccess)
 			break;
 
@@ -3848,6 +3886,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_CLAC:
+		TRACE_INSN(insn, "clac");
 		if (!opts.uaccess)
 			break;
 
@@ -3865,6 +3904,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_STD:
+		TRACE_INSN(insn, "std");
 		if (statep->df) {
 			WARN_INSN(insn, "recursive STD");
 			return 1;
@@ -3874,6 +3914,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 
 	case INSN_CLD:
+		TRACE_INSN(insn, "cld");
 		if (!statep->df && func) {
 			WARN_INSN(insn, "redundant CLD");
 			return 1;
@@ -3886,8 +3927,10 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 		break;
 	}
 
-	*dead_end = insn->dead_end;
+	if (insn->dead_end)
+		TRACE_INSN(insn, "dead end");
 
+	*dead_end = insn->dead_end;
 	return 0;
 }
 
@@ -3897,8 +3940,8 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
  * each instruction and validate all the rules described in
  * tools/objtool/Documentation/objtool.txt.
  */
-static int validate_branch(struct objtool_file *file, struct symbol *func,
-			   struct instruction *insn, struct insn_state state)
+static int do_validate_branch(struct objtool_file *file, struct symbol *func,
+			      struct instruction *insn, struct insn_state state)
 {
 	struct instruction *next_insn, *prev_insn = NULL;
 	bool dead_end;
@@ -3907,7 +3950,8 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 	if (func && func->ignore)
 		return 0;
 
-	while (1) {
+	do {
+		insn->trace = 0;
 		next_insn = next_insn_to_validate(file, insn);
 
 		if (opts.checksum && func && insn->sec)
@@ -3930,10 +3974,15 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 		ret = validate_insn(file, func, insn, &state, prev_insn, next_insn,
 				    &dead_end);
-		if (dead_end)
-			break;
 
-		if (!next_insn) {
+		if (!insn->trace) {
+			if (ret)
+				TRACE_INSN(insn, "warning (%d)", ret);
+			else
+				TRACE_INSN(insn, NULL);
+		}
+
+		if (!dead_end && !next_insn) {
 			if (state.cfi.cfa.base == CFI_UNDEFINED)
 				return 0;
 			if (file->ignore_unreachables)
@@ -3947,7 +3996,20 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 
 		prev_insn = insn;
 		insn = next_insn;
-	}
+
+	} while (!dead_end);
+
+	return ret;
+}
+
+static int validate_branch(struct objtool_file *file, struct symbol *func,
+			   struct instruction *insn, struct insn_state state)
+{
+	int ret;
+
+	trace_depth_inc();
+	ret = do_validate_branch(file, func, insn, state);
+	trace_depth_dec();
 
 	return ret;
 }
@@ -4408,10 +4470,18 @@ static int validate_symbol(struct objtool_file *file, struct section *sec,
 	if (opts.checksum)
 		checksum_init(func);
 
+	if (opts.trace && !fnmatch(opts.trace, sym->name, 0)) {
+		trace_enable();
+		TRACE("%s: validation begin\n", sym->name);
+	}
+
 	ret = validate_branch(file, func, insn, *state);
 	if (ret)
 		BT_INSN(insn, "<=== (sym)");
 
+	TRACE("%s: validation %s\n\n", sym->name, ret ? "failed" : "end");
+	trace_disable();
+
 	if (opts.checksum)
 		checksum_finish(func);
 
@@ -4823,8 +4893,6 @@ 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;
@@ -4846,8 +4914,10 @@ int check(struct objtool_file *file)
 	 * disassembly context to disassemble instruction or function
 	 * on warning or backtrace.
 	 */
-	if (opts.verbose || opts.backtrace) {
+	if (opts.verbose || opts.backtrace || opts.trace) {
 		disas_ctx = disas_context_create(file);
+		if (!disas_ctx)
+			opts.trace = false;
 		objtool_disas_ctx = disas_ctx;
 	}
 
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index a030b06..0ca6e6c 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -308,6 +308,121 @@ char *disas_result(struct disas_context *dctx)
 	return dctx->result;
 }
 
+#define DISAS_INSN_OFFSET_SPACE		10
+#define DISAS_INSN_SPACE		60
+
+/*
+ * Print a message in the instruction flow. If insn is not NULL then
+ * the instruction address is printed in addition of the message,
+ * otherwise only the message is printed. In all cases, the instruction
+ * itself is not printed.
+ */
+static int disas_vprint(FILE *stream, struct section *sec, unsigned long offset,
+			int depth, const char *format, va_list ap)
+{
+	const char *addr_str;
+	int i, n;
+	int len;
+
+	len = sym_name_max_len + DISAS_INSN_OFFSET_SPACE;
+	if (depth < 0) {
+		len += depth;
+		depth = 0;
+	}
+
+	n = 0;
+
+	if (sec) {
+		addr_str = offstr(sec, offset);
+		n += fprintf(stream, "%6lx:  %-*s  ", offset, len, addr_str);
+		free((char *)addr_str);
+	} else {
+		len += DISAS_INSN_OFFSET_SPACE + 1;
+		n += fprintf(stream, "%-*s", len, "");
+	}
+
+	/* print vertical bars to show the code flow */
+	for (i = 0; i < depth; i++)
+		n += fprintf(stream, "| ");
+
+	if (format)
+		n += vfprintf(stream, format, ap);
+
+	return n;
+}
+
+/*
+ * Print a message in the instruction flow. If insn is not NULL then
+ * the instruction address is printed in addition of the message,
+ * otherwise only the message is printed. In all cases, the instruction
+ * itself is not printed.
+ */
+void disas_print_info(FILE *stream, struct instruction *insn, int depth,
+		      const char *format, ...)
+{
+	struct section *sec;
+	unsigned long off;
+	va_list args;
+
+	if (insn) {
+		sec = insn->sec;
+		off = insn->offset;
+	} else {
+		sec = NULL;
+		off = 0;
+	}
+
+	va_start(args, format);
+	disas_vprint(stream, sec, off, depth, format, args);
+	va_end(args);
+}
+
+/*
+ * Print an instruction address (offset and function), the instruction itself
+ * and an optional message.
+ */
+void disas_print_insn(FILE *stream, struct disas_context *dctx,
+		      struct instruction *insn, int depth,
+		      const char *format, ...)
+{
+	char fake_nop_insn[32];
+	const char *insn_str;
+	bool fake_nop;
+	va_list args;
+	int len;
+
+	/*
+	 * Alternative can insert a fake nop, sometimes with no
+	 * associated section so nothing to disassemble.
+	 */
+	fake_nop = (!insn->sec && insn->type == INSN_NOP);
+	if (fake_nop) {
+		snprintf(fake_nop_insn, 32, "<fake nop> (%d bytes)", insn->len);
+		insn_str = fake_nop_insn;
+	} else {
+		disas_insn(dctx, insn);
+		insn_str = disas_result(dctx);
+	}
+
+	/* print the instruction */
+	len = (depth + 1) * 2 < DISAS_INSN_SPACE ? DISAS_INSN_SPACE - (depth+1) * 2 : 1;
+	disas_print_info(stream, insn, depth, "%-*s", len, insn_str);
+
+	/* print message if any */
+	if (!format)
+		return;
+
+	if (strcmp(format, "\n") == 0) {
+		fprintf(stream, "\n");
+		return;
+	}
+
+	fprintf(stream, " - ");
+	va_start(args, format);
+	vfprintf(stream, format, args);
+	va_end(args);
+}
+
 /*
  * Disassemble a single instruction. Return the size of the instruction.
  */
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index bb0b25e..991365c 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -41,6 +41,7 @@ struct opts {
 	const char *output;
 	bool sec_address;
 	bool stats;
+	const char *trace;
 	bool verbose;
 	bool werror;
 };
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index f96aabd..fde9586 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -66,7 +66,8 @@ struct instruction {
 	    visited		: 4,
 	    no_reloc		: 1,
 	    hole		: 1,
-	    fake		: 1;
+	    fake		: 1,
+	    trace		: 1;
 		/* 9 bit hole */
 
 	struct alt_group *alt_group;
@@ -143,4 +144,7 @@ struct instruction *next_insn_same_sec(struct objtool_file *file, struct instruc
 
 const char *objtool_disas_insn(struct instruction *insn);
 
+extern size_t sym_name_max_len;
+extern struct disas_context *objtool_disas_ctx;
+
 #endif /* _CHECK_H */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 1aee1fb..5db75d0 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -19,6 +19,11 @@ int disas_info_init(struct disassemble_info *dinfo,
 		    const char *options);
 size_t disas_insn(struct disas_context *dctx, struct instruction *insn);
 char *disas_result(struct disas_context *dctx);
+void disas_print_info(FILE *stream, struct instruction *insn, int depth,
+		      const char *format, ...);
+void disas_print_insn(FILE *stream, struct disas_context *dctx,
+		      struct instruction *insn, int depth,
+		      const char *format, ...);
 
 #else /* DISAS */
 
@@ -51,6 +56,12 @@ static inline char *disas_result(struct disas_context *dctx)
 	return NULL;
 }
 
+static inline void disas_print_info(FILE *stream, struct instruction *insn,
+				    int depth, const char *format, ...) {}
+static inline void disas_print_insn(FILE *stream, struct disas_context *dctx,
+				    struct instruction *insn, int depth,
+				    const char *format, ...) {}
+
 #endif /* DISAS */
 
 #endif /* _DISAS_H */
diff --git a/tools/objtool/include/objtool/trace.h b/tools/objtool/include/objtool/trace.h
new file mode 100644
index 0000000..3f3c830
--- /dev/null
+++ b/tools/objtool/include/objtool/trace.h
@@ -0,0 +1,68 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#ifndef _TRACE_H
+#define _TRACE_H
+
+#include <objtool/check.h>
+#include <objtool/disas.h>
+
+#ifdef DISAS
+
+extern bool trace;
+extern int trace_depth;
+
+#define TRACE(fmt, ...)						\
+({	if (trace)						\
+		fprintf(stderr, fmt, ##__VA_ARGS__);		\
+})
+
+#define TRACE_INSN(insn, fmt, ...)				\
+({								\
+	if (trace) {						\
+		disas_print_insn(stderr, objtool_disas_ctx,	\
+				 insn, trace_depth - 1,	\
+				 fmt, ##__VA_ARGS__);		\
+		fprintf(stderr, "\n");				\
+		insn->trace = 1;				\
+	}							\
+})
+
+static inline void trace_enable(void)
+{
+	trace = true;
+	trace_depth = 0;
+}
+
+static inline void trace_disable(void)
+{
+	trace = false;
+}
+
+static inline void trace_depth_inc(void)
+{
+	if (trace)
+		trace_depth++;
+}
+
+static inline void trace_depth_dec(void)
+{
+	if (trace)
+		trace_depth--;
+}
+
+#else /* DISAS */
+
+#define TRACE(fmt, ...) ({})
+#define TRACE_INSN(insn, fmt, ...) ({})
+
+static inline void trace_enable(void) {}
+static inline void trace_disable(void) {}
+static inline void trace_depth_inc(void) {}
+static inline void trace_depth_dec(void) {}
+
+#endif
+
+#endif /* _TRACE_H */
diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h
index f32abc7..25ff794 100644
--- a/tools/objtool/include/objtool/warn.h
+++ b/tools/objtool/include/objtool/warn.h
@@ -97,6 +97,7 @@ static inline char *offstr(struct section *sec, unsigned long offset)
 		_len = (_len < 50) ? 50 - _len : 0;		\
 		WARN("  %s: " format "  %*s%s", _str, ##__VA_ARGS__, _len, "", _istr); \
 		free(_str);						\
+		__insn->trace = 1;				\
 	}							\
 })
 
diff --git a/tools/objtool/trace.c b/tools/objtool/trace.c
new file mode 100644
index 0000000..134cc33
--- /dev/null
+++ b/tools/objtool/trace.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#include <objtool/trace.h>
+
+bool trace;
+int trace_depth;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Record symbol name max length
  2025-11-21  9:53 ` [PATCH v6 09/30] objtool: Record symbol name max length Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Record symbol name max length

Keep track of the maximum length of symbol names. This will help
formatting the code flow between different functions.

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-10-alexandre.chartre@oracle.com
---
 tools/objtool/check.c | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 6573056..0fbf0eb 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -37,6 +37,8 @@ static struct cfi_state init_cfi;
 static struct cfi_state func_cfi;
 static struct cfi_state force_undefined_cfi;
 
+static size_t sym_name_max_len;
+
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset)
 {
@@ -2463,6 +2465,7 @@ static bool is_profiling_func(const char *name)
 static int classify_symbols(struct objtool_file *file)
 {
 	struct symbol *func;
+	size_t len;
 
 	for_each_sym(file->elf, func) {
 		if (is_notype_sym(func) && strstarts(func->name, ".L"))
@@ -2489,6 +2492,10 @@ static int classify_symbols(struct objtool_file *file)
 
 		if (is_profiling_func(func->name))
 			func->profiling_func = true;
+
+		len = strlen(func->name);
+		if (len > sym_name_max_len)
+			sym_name_max_len = len;
 	}
 
 	return 0;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Extract code to validate instruction from the validate branch loop
  2025-11-21  9:53 ` [PATCH v6 08/30] objtool: Extract code to validate instruction from the validate branch loop Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Extract code to validate instruction from the validate branch loop

The code to validate a branch loops through all instructions of the
branch and validate each instruction. Move the code to validate an
instruction to a separated function.

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-9-alexandre.chartre@oracle.com
---
 tools/objtool/check.c | 386 +++++++++++++++++++++--------------------
 1 file changed, 205 insertions(+), 181 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 4da1f07..6573056 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3654,253 +3654,277 @@ static void checksum_update_insn(struct objtool_file *file, struct symbol *func,
 	checksum_update(func, insn, &offset, sizeof(offset));
 }
 
-/*
- * Follow the branch starting at the given instruction, and recursively follow
- * any other branches (jumps).  Meanwhile, track the frame pointer state at
- * each instruction and validate all the rules described in
- * tools/objtool/Documentation/objtool.txt.
- */
 static int validate_branch(struct objtool_file *file, struct symbol *func,
-			   struct instruction *insn, struct insn_state state)
+			   struct instruction *insn, struct insn_state state);
+
+static int validate_insn(struct objtool_file *file, struct symbol *func,
+			 struct instruction *insn, struct insn_state *statep,
+			 struct instruction *prev_insn, struct instruction *next_insn,
+			 bool *dead_end)
 {
 	struct alternative *alt;
-	struct instruction *next_insn, *prev_insn = NULL;
 	u8 visited;
 	int ret;
 
-	if (func && func->ignore)
-		return 0;
+	/*
+	 * Any returns before the end of this function are effectively dead
+	 * ends, i.e. validate_branch() has reached the end of the branch.
+	 */
+	*dead_end = true;
 
-	while (1) {
-		next_insn = next_insn_to_validate(file, insn);
+	visited = VISITED_BRANCH << statep->uaccess;
+	if (insn->visited & VISITED_BRANCH_MASK) {
+		if (!insn->hint && !insn_cfi_match(insn, &statep->cfi))
+			return 1;
 
-		if (opts.checksum && func && insn->sec)
-			checksum_update_insn(file, func, insn);
+		if (insn->visited & visited)
+			return 0;
+	} else {
+		nr_insns_visited++;
+	}
 
-		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
-			/* Ignore KCFI type preambles, which always fall through */
-			if (is_prefix_func(func))
-				return 0;
+	if (statep->noinstr)
+		statep->instr += insn->instr;
 
-			if (file->ignore_unreachables)
-				return 0;
+	if (insn->hint) {
+		if (insn->restore) {
+			struct instruction *save_insn, *i;
 
-			WARN("%s() falls through to next function %s()",
-			     func->name, insn_func(insn)->name);
-			func->warned = 1;
+			i = insn;
+			save_insn = NULL;
 
-			return 1;
-		}
+			sym_for_each_insn_continue_reverse(file, func, i) {
+				if (i->save) {
+					save_insn = i;
+					break;
+				}
+			}
 
-		visited = VISITED_BRANCH << state.uaccess;
-		if (insn->visited & VISITED_BRANCH_MASK) {
-			if (!insn->hint && !insn_cfi_match(insn, &state.cfi))
+			if (!save_insn) {
+				WARN_INSN(insn, "no corresponding CFI save for CFI restore");
 				return 1;
+			}
 
-			if (insn->visited & visited)
-				return 0;
-		} else {
-			nr_insns_visited++;
+			if (!save_insn->visited) {
+				/*
+				 * If the restore hint insn is at the
+				 * beginning of a basic block and was
+				 * branched to from elsewhere, and the
+				 * save insn hasn't been visited yet,
+				 * defer following this branch for now.
+				 * It will be seen later via the
+				 * straight-line path.
+				 */
+				if (!prev_insn)
+					return 0;
+
+				WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
+				return 1;
+			}
+
+			insn->cfi = save_insn->cfi;
+			nr_cfi_reused++;
 		}
 
-		if (state.noinstr)
-			state.instr += insn->instr;
+		statep->cfi = *insn->cfi;
+	} else {
+		/* XXX track if we actually changed statep->cfi */
 
-		if (insn->hint) {
-			if (insn->restore) {
-				struct instruction *save_insn, *i;
+		if (prev_insn && !cficmp(prev_insn->cfi, &statep->cfi)) {
+			insn->cfi = prev_insn->cfi;
+			nr_cfi_reused++;
+		} else {
+			insn->cfi = cfi_hash_find_or_add(&statep->cfi);
+		}
+	}
 
-				i = insn;
-				save_insn = NULL;
+	insn->visited |= visited;
 
-				sym_for_each_insn_continue_reverse(file, func, i) {
-					if (i->save) {
-						save_insn = i;
-						break;
-					}
-				}
+	if (propagate_alt_cfi(file, insn))
+		return 1;
 
-				if (!save_insn) {
-					WARN_INSN(insn, "no corresponding CFI save for CFI restore");
-					return 1;
-				}
+	if (insn->alts) {
+		for (alt = insn->alts; alt; alt = alt->next) {
+			ret = validate_branch(file, func, alt->insn, *statep);
+			if (ret) {
+				BT_INSN(insn, "(alt)");
+				return ret;
+			}
+		}
+	}
 
-				if (!save_insn->visited) {
-					/*
-					 * If the restore hint insn is at the
-					 * beginning of a basic block and was
-					 * branched to from elsewhere, and the
-					 * save insn hasn't been visited yet,
-					 * defer following this branch for now.
-					 * It will be seen later via the
-					 * straight-line path.
-					 */
-					if (!prev_insn)
-						return 0;
+	if (skip_alt_group(insn))
+		return 0;
 
-					WARN_INSN(insn, "objtool isn't smart enough to handle this CFI save/restore combo");
-					return 1;
-				}
+	if (handle_insn_ops(insn, next_insn, statep))
+		return 1;
 
-				insn->cfi = save_insn->cfi;
-				nr_cfi_reused++;
-			}
+	switch (insn->type) {
 
-			state.cfi = *insn->cfi;
-		} else {
-			/* XXX track if we actually changed state.cfi */
+	case INSN_RETURN:
+		return validate_return(func, insn, statep);
 
-			if (prev_insn && !cficmp(prev_insn->cfi, &state.cfi)) {
-				insn->cfi = prev_insn->cfi;
-				nr_cfi_reused++;
-			} else {
-				insn->cfi = cfi_hash_find_or_add(&state.cfi);
-			}
+	case INSN_CALL:
+	case INSN_CALL_DYNAMIC:
+		ret = validate_call(file, insn, statep);
+		if (ret)
+			return ret;
+
+		if (opts.stackval && func && !is_special_call(insn) &&
+		    !has_valid_stack_frame(statep)) {
+			WARN_INSN(insn, "call without frame pointer save/setup");
+			return 1;
 		}
 
-		insn->visited |= visited;
+		break;
 
-		if (propagate_alt_cfi(file, insn))
-			return 1;
+	case INSN_JUMP_CONDITIONAL:
+	case INSN_JUMP_UNCONDITIONAL:
+		if (is_sibling_call(insn)) {
+			ret = validate_sibling_call(file, insn, statep);
+			if (ret)
+				return ret;
 
-		if (insn->alts) {
-			for (alt = insn->alts; alt; alt = alt->next) {
-				ret = validate_branch(file, func, alt->insn, state);
-				if (ret) {
-					BT_INSN(insn, "(alt)");
-					return ret;
-				}
+		} else if (insn->jump_dest) {
+			ret = validate_branch(file, func,
+					      insn->jump_dest, *statep);
+			if (ret) {
+				BT_INSN(insn, "(branch)");
+				return ret;
 			}
 		}
 
-		if (skip_alt_group(insn))
+		if (insn->type == INSN_JUMP_UNCONDITIONAL)
 			return 0;
 
-		if (handle_insn_ops(insn, next_insn, &state))
-			return 1;
-
-		switch (insn->type) {
-
-		case INSN_RETURN:
-			return validate_return(func, insn, &state);
+		break;
 
-		case INSN_CALL:
-		case INSN_CALL_DYNAMIC:
-			ret = validate_call(file, insn, &state);
+	case INSN_JUMP_DYNAMIC:
+	case INSN_JUMP_DYNAMIC_CONDITIONAL:
+		if (is_sibling_call(insn)) {
+			ret = validate_sibling_call(file, insn, statep);
 			if (ret)
 				return ret;
+		}
 
-			if (opts.stackval && func && !is_special_call(insn) &&
-			    !has_valid_stack_frame(&state)) {
-				WARN_INSN(insn, "call without frame pointer save/setup");
-				return 1;
-			}
+		if (insn->type == INSN_JUMP_DYNAMIC)
+			return 0;
 
-			break;
+		break;
 
-		case INSN_JUMP_CONDITIONAL:
-		case INSN_JUMP_UNCONDITIONAL:
-			if (is_sibling_call(insn)) {
-				ret = validate_sibling_call(file, insn, &state);
-				if (ret)
-					return ret;
+	case INSN_SYSCALL:
+		if (func && (!next_insn || !next_insn->hint)) {
+			WARN_INSN(insn, "unsupported instruction in callable function");
+			return 1;
+		}
 
-			} else if (insn->jump_dest) {
-				ret = validate_branch(file, func,
-						      insn->jump_dest, state);
-				if (ret) {
-					BT_INSN(insn, "(branch)");
-					return ret;
-				}
-			}
+		break;
 
-			if (insn->type == INSN_JUMP_UNCONDITIONAL)
-				return 0;
+	case INSN_SYSRET:
+		if (func && (!next_insn || !next_insn->hint)) {
+			WARN_INSN(insn, "unsupported instruction in callable function");
+			return 1;
+		}
+
+		return 0;
 
+	case INSN_STAC:
+		if (!opts.uaccess)
 			break;
 
-		case INSN_JUMP_DYNAMIC:
-		case INSN_JUMP_DYNAMIC_CONDITIONAL:
-			if (is_sibling_call(insn)) {
-				ret = validate_sibling_call(file, insn, &state);
-				if (ret)
-					return ret;
-			}
+		if (statep->uaccess) {
+			WARN_INSN(insn, "recursive UACCESS enable");
+			return 1;
+		}
 
-			if (insn->type == INSN_JUMP_DYNAMIC)
-				return 0;
+		statep->uaccess = true;
+		break;
 
+	case INSN_CLAC:
+		if (!opts.uaccess)
 			break;
 
-		case INSN_SYSCALL:
-			if (func && (!next_insn || !next_insn->hint)) {
-				WARN_INSN(insn, "unsupported instruction in callable function");
-				return 1;
-			}
+		if (!statep->uaccess && func) {
+			WARN_INSN(insn, "redundant UACCESS disable");
+			return 1;
+		}
 
-			break;
+		if (func_uaccess_safe(func) && !statep->uaccess_stack) {
+			WARN_INSN(insn, "UACCESS-safe disables UACCESS");
+			return 1;
+		}
 
-		case INSN_SYSRET:
-			if (func && (!next_insn || !next_insn->hint)) {
-				WARN_INSN(insn, "unsupported instruction in callable function");
-				return 1;
-			}
+		statep->uaccess = false;
+		break;
 
-			return 0;
+	case INSN_STD:
+		if (statep->df) {
+			WARN_INSN(insn, "recursive STD");
+			return 1;
+		}
 
-		case INSN_STAC:
-			if (!opts.uaccess)
-				break;
+		statep->df = true;
+		break;
 
-			if (state.uaccess) {
-				WARN_INSN(insn, "recursive UACCESS enable");
-				return 1;
-			}
+	case INSN_CLD:
+		if (!statep->df && func) {
+			WARN_INSN(insn, "redundant CLD");
+			return 1;
+		}
 
-			state.uaccess = true;
-			break;
+		statep->df = false;
+		break;
 
-		case INSN_CLAC:
-			if (!opts.uaccess)
-				break;
+	default:
+		break;
+	}
 
-			if (!state.uaccess && func) {
-				WARN_INSN(insn, "redundant UACCESS disable");
-				return 1;
-			}
+	*dead_end = insn->dead_end;
 
-			if (func_uaccess_safe(func) && !state.uaccess_stack) {
-				WARN_INSN(insn, "UACCESS-safe disables UACCESS");
-				return 1;
-			}
+	return 0;
+}
 
-			state.uaccess = false;
-			break;
+/*
+ * Follow the branch starting at the given instruction, and recursively follow
+ * any other branches (jumps).  Meanwhile, track the frame pointer state at
+ * each instruction and validate all the rules described in
+ * tools/objtool/Documentation/objtool.txt.
+ */
+static int validate_branch(struct objtool_file *file, struct symbol *func,
+			   struct instruction *insn, struct insn_state state)
+{
+	struct instruction *next_insn, *prev_insn = NULL;
+	bool dead_end;
+	int ret;
 
-		case INSN_STD:
-			if (state.df) {
-				WARN_INSN(insn, "recursive STD");
-				return 1;
-			}
+	if (func && func->ignore)
+		return 0;
 
-			state.df = true;
-			break;
+	while (1) {
+		next_insn = next_insn_to_validate(file, insn);
 
-		case INSN_CLD:
-			if (!state.df && func) {
-				WARN_INSN(insn, "redundant CLD");
-				return 1;
-			}
+		if (opts.checksum && func && insn->sec)
+			checksum_update_insn(file, func, insn);
 
-			state.df = false;
-			break;
+		if (func && insn_func(insn) && func != insn_func(insn)->pfunc) {
+			/* Ignore KCFI type preambles, which always fall through */
+			if (is_prefix_func(func))
+				return 0;
 
-		default:
-			break;
+			if (file->ignore_unreachables)
+				return 0;
+
+			WARN("%s() falls through to next function %s()",
+			     func->name, insn_func(insn)->name);
+			func->warned = 1;
+
+			return 1;
 		}
 
-		if (insn->dead_end)
-			return 0;
+		ret = validate_insn(file, func, insn, &state, prev_insn, next_insn,
+				    &dead_end);
+		if (dead_end)
+			break;
 
 		if (!next_insn) {
 			if (state.cfi.cfa.base == CFI_UNDEFINED)
@@ -3918,7 +3942,7 @@ static int validate_branch(struct objtool_file *file, struct symbol *func,
 		insn = next_insn;
 	}
 
-	return 0;
+	return ret;
 }
 
 static int validate_unwind_hint(struct objtool_file *file,

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Disassemble instruction on warning or backtrace
  2025-11-21  9:53 ` [PATCH v6 07/30] objtool: Disassemble instruction on warning or backtrace Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Disassemble instruction on warning or backtrace

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                 | 36 +++++++++++++++++++++-----
 tools/objtool/disas.c                 |  5 +---
 tools/objtool/include/objtool/check.h |  2 +-
 tools/objtool/include/objtool/disas.h | 13 +++++++++-
 tools/objtool/include/objtool/warn.h  | 16 ++++++++----
 5 files changed, 58 insertions(+), 14 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 0999717..4da1f07 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -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);
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 89daa12..a030b06 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -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;
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index ad9c735..f96aabd 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -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 */
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
index 3ec3ce2..1aee1fb 100644
--- a/tools/objtool/include/objtool/disas.h
+++ b/tools/objtool/include/objtool/disas.h
@@ -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 */
diff --git a/tools/objtool/include/objtool/warn.h b/tools/objtool/include/objtool/warn.h
index a1e3927..f32abc7 100644
--- a/tools/objtool/include/objtool/warn.h
+++ b/tools/objtool/include/objtool/warn.h
@@ -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);						\
 	}							\
 })
 

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Store instruction disassembly result
  2025-11-21  9:53 ` [PATCH v6 06/30] objtool: Store instruction disassembly result Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Store instruction disassembly result

When disassembling an instruction store the result instead of directly
printing it.

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-7-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 77 ++++++++++++++++++++++++++++++++++++++----
 1 file changed, 71 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index dee10ab..89daa12 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -12,9 +12,16 @@
 #include <linux/string.h>
 #include <tools/dis-asm-compat.h>
 
+/*
+ * Size of the buffer for storing the result of disassembling
+ * a single instruction.
+ */
+#define DISAS_RESULT_SIZE	1024
+
 struct disas_context {
 	struct objtool_file *file;
 	struct instruction *insn;
+	char result[DISAS_RESULT_SIZE];
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
 };
@@ -34,6 +41,59 @@ static int sprint_name(char *str, const char *name, unsigned long offset)
 #define DINFO_FPRINTF(dinfo, ...)	\
 	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
 
+static int disas_result_fprintf(struct disas_context *dctx,
+				const char *fmt, va_list ap)
+{
+	char *buf = dctx->result;
+	int avail, len;
+
+	len = strlen(buf);
+	if (len >= DISAS_RESULT_SIZE - 1) {
+		WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
+			  "disassembly buffer is full");
+		return -1;
+	}
+	avail = DISAS_RESULT_SIZE - len;
+
+	len = vsnprintf(buf + len, avail, fmt, ap);
+	if (len < 0 || len >= avail) {
+		WARN_FUNC(dctx->insn->sec, dctx->insn->offset,
+			  "disassembly buffer is truncated");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int disas_fprintf(void *stream, const char *fmt, ...)
+{
+	va_list arg;
+	int rv;
+
+	va_start(arg, fmt);
+	rv = disas_result_fprintf(stream, fmt, arg);
+	va_end(arg);
+
+	return rv;
+}
+
+/*
+ * For init_disassemble_info_compat().
+ */
+static int disas_fprintf_styled(void *stream,
+				enum disassembler_style style,
+				const char *fmt, ...)
+{
+	va_list arg;
+	int rv;
+
+	va_start(arg, fmt);
+	rv = disas_result_fprintf(stream, fmt, arg);
+	va_end(arg);
+
+	return rv;
+}
+
 static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
 				 bfd_vma addr, struct disassemble_info *dinfo)
 {
@@ -195,9 +255,8 @@ 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);
+	init_disassemble_info_compat(dinfo, dctx,
+				     disas_fprintf, disas_fprintf_styled);
 
 	dinfo->read_memory_func = buffer_read_memory;
 	dinfo->print_address_func = disas_print_address;
@@ -244,6 +303,11 @@ void disas_context_destroy(struct disas_context *dctx)
 	free(dctx);
 }
 
+static char *disas_result(struct disas_context *dctx)
+{
+	return dctx->result;
+}
+
 /*
  * Disassemble a single instruction. Return the size of the instruction.
  */
@@ -254,6 +318,7 @@ static size_t disas_insn(struct disas_context *dctx,
 	struct disassemble_info *dinfo = &dctx->info;
 
 	dctx->insn = insn;
+	dctx->result[0] = '\0';
 
 	if (insn->type == INSN_NOP) {
 		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
@@ -282,10 +347,10 @@ static void disas_func(struct disas_context *dctx, struct symbol *func)
 	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(" %6lx:  %s+0x%-6lx      %s\n",
+		       addr, func->name, addr - func->offset,
+		       disas_result(dctx));
 	}
 	printf("\n");
 }

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Print symbol during disassembly
  2025-11-21  9:53 ` [PATCH v6 05/30] objtool: Print symbol during disassembly Alexandre Chartre
@ 2025-11-24  9:11   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:11 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Print symbol during disassembly

Print symbols referenced during disassembly instead of just printing
raw addresses. Also handle address relocation.

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-6-alexandre.chartre@oracle.com
---
 tools/objtool/check.c                 |   9 +--
 tools/objtool/disas.c                 | 134 +++++++++++++++++++++++++-
 tools/objtool/include/objtool/check.h |   9 ++-
 3 files changed, 143 insertions(+), 9 deletions(-)

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 21d45a3..0999717 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -134,15 +134,6 @@ static struct instruction *prev_insn_same_sym(struct objtool_file *file,
 	for (insn = next_insn_same_sec(file, insn); insn;		\
 	     insn = next_insn_same_sec(file, insn))
 
-static inline struct symbol *insn_call_dest(struct instruction *insn)
-{
-	if (insn->type == INSN_JUMP_DYNAMIC ||
-	    insn->type == INSN_CALL_DYNAMIC)
-		return NULL;
-
-	return insn->_call_dest;
-}
-
 static inline struct reloc *insn_jump_table(struct instruction *insn)
 {
 	if (insn->type == INSN_JUMP_DYNAMIC ||
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 11ac2ec..dee10ab 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -14,13 +14,144 @@
 
 struct disas_context {
 	struct objtool_file *file;
+	struct instruction *insn;
 	disassembler_ftype disassembler;
 	struct disassemble_info info;
 };
 
+static int sprint_name(char *str, const char *name, unsigned long offset)
+{
+	int len;
+
+	if (offset)
+		len = sprintf(str, "%s+0x%lx", name, offset);
+	else
+		len = sprintf(str, "%s", name);
+
+	return len;
+}
+
 #define DINFO_FPRINTF(dinfo, ...)	\
 	((*(dinfo)->fprintf_func)((dinfo)->stream, __VA_ARGS__))
 
+static void disas_print_addr_sym(struct section *sec, struct symbol *sym,
+				 bfd_vma addr, struct disassemble_info *dinfo)
+{
+	char symstr[1024];
+	char *str;
+
+	if (sym) {
+		sprint_name(symstr, sym->name, addr - sym->offset);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
+	} else {
+		str = offstr(sec, addr);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
+		free(str);
+	}
+}
+
+static void disas_print_addr_noreloc(bfd_vma addr,
+				     struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *insn = dctx->insn;
+	struct symbol *sym = NULL;
+
+	if (insn->sym && addr >= insn->sym->offset &&
+	    addr < insn->sym->offset + insn->sym->len) {
+		sym = insn->sym;
+	}
+
+	disas_print_addr_sym(insn->sec, sym, addr, dinfo);
+}
+
+static void disas_print_addr_reloc(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *insn = dctx->insn;
+	unsigned long offset;
+	struct reloc *reloc;
+	char symstr[1024];
+	char *str;
+
+	reloc = find_reloc_by_dest_range(dctx->file->elf, insn->sec,
+					 insn->offset, insn->len);
+	if (!reloc) {
+		/*
+		 * There is no relocation for this instruction although
+		 * the address to resolve points to the next instruction.
+		 * So this is an effective reference to the next IP, for
+		 * example: "lea 0x0(%rip),%rdi". The kernel can reference
+		 * the next IP with _THIS_IP_ macro.
+		 */
+		DINFO_FPRINTF(dinfo, "0x%lx <_THIS_IP_>", addr);
+		return;
+	}
+
+	offset = arch_insn_adjusted_addend(insn, reloc);
+
+	/*
+	 * If the relocation symbol is a section name (for example ".bss")
+	 * then we try to further resolve the name.
+	 */
+	if (reloc->sym->type == STT_SECTION) {
+		str = offstr(reloc->sym->sec, reloc->sym->offset + offset);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, str);
+		free(str);
+	} else {
+		sprint_name(symstr, reloc->sym->name, offset);
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, symstr);
+	}
+}
+
+/*
+ * Resolve an address into a "<symbol>+<offset>" string.
+ */
+static void disas_print_address(bfd_vma addr, struct disassemble_info *dinfo)
+{
+	struct disas_context *dctx = dinfo->application_data;
+	struct instruction *insn = dctx->insn;
+	struct instruction *jump_dest;
+	struct symbol *sym;
+	bool is_reloc;
+
+	/*
+	 * If the instruction is a call/jump and it references a
+	 * destination then this is likely the address we are looking
+	 * up. So check it first.
+	 */
+	jump_dest = insn->jump_dest;
+	if (jump_dest && jump_dest->sym && jump_dest->offset == addr) {
+		disas_print_addr_sym(jump_dest->sec, jump_dest->sym,
+				     addr, dinfo);
+		return;
+	}
+
+	/*
+	 * If the address points to the next instruction then there is
+	 * probably a relocation. It can be a false positive when the
+	 * current instruction is referencing the address of the next
+	 * instruction. This particular case will be handled in
+	 * disas_print_addr_reloc().
+	 */
+	is_reloc = (addr == insn->offset + insn->len);
+
+	/*
+	 * The call destination offset can be the address we are looking
+	 * up, or 0 if there is a relocation.
+	 */
+	sym = insn_call_dest(insn);
+	if (sym && (sym->offset == addr || (sym->offset == 0 && is_reloc))) {
+		DINFO_FPRINTF(dinfo, "0x%lx <%s>", addr, sym->name);
+		return;
+	}
+
+	if (!is_reloc)
+		disas_print_addr_noreloc(addr, dinfo);
+	else
+		disas_print_addr_reloc(addr, dinfo);
+}
+
 /*
  * Initialize disassemble info arch, mach (32 or 64-bit) and options.
  */
@@ -69,6 +200,7 @@ struct disas_context *disas_context_create(struct objtool_file *file)
 				     fprintf_styled);
 
 	dinfo->read_memory_func = buffer_read_memory;
+	dinfo->print_address_func = disas_print_address;
 	dinfo->application_data = dctx;
 
 	/*
@@ -121,6 +253,8 @@ static size_t disas_insn(struct disas_context *dctx,
 	disassembler_ftype disasm = dctx->disassembler;
 	struct disassemble_info *dinfo = &dctx->info;
 
+	dctx->insn = insn;
+
 	if (insn->type == INSN_NOP) {
 		DINFO_FPRINTF(dinfo, "nop%d", insn->len);
 		return insn->len;
diff --git a/tools/objtool/include/objtool/check.h b/tools/objtool/include/objtool/check.h
index 674f574..ad9c735 100644
--- a/tools/objtool/include/objtool/check.h
+++ b/tools/objtool/include/objtool/check.h
@@ -117,6 +117,15 @@ static inline bool is_jump(struct instruction *insn)
 	return is_static_jump(insn) || is_dynamic_jump(insn);
 }
 
+static inline struct symbol *insn_call_dest(struct instruction *insn)
+{
+	if (insn->type == INSN_JUMP_DYNAMIC ||
+	    insn->type == INSN_CALL_DYNAMIC)
+		return NULL;
+
+	return insn->_call_dest;
+}
+
 struct instruction *find_insn(struct objtool_file *file,
 			      struct section *sec, unsigned long offset);
 

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] tool build: Remove annoying newline in build output
  2025-11-21  9:53 ` [PATCH v6 04/30] tool build: Remove annoying newline in build output Alexandre Chartre
@ 2025-11-24  9:12   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:12 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

tool build: Remove annoying newline in build output

Remove the newline which is printed during feature discovery
when nothing else is printed.

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-5-alexandre.chartre@oracle.com
---
 tools/build/Makefile.feature | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/tools/build/Makefile.feature b/tools/build/Makefile.feature
index 32bbe29..300a329 100644
--- a/tools/build/Makefile.feature
+++ b/tools/build/Makefile.feature
@@ -315,5 +315,7 @@ endef
 
 ifeq ($(FEATURE_DISPLAY_DEFERRED),)
   $(call feature_display_entries)
-  $(info )
+  ifeq ($(feature_display),1)
+    $(info )
+  endif
 endif

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-21  9:53 ` [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump Alexandre Chartre
@ 2025-11-24  9:12   ` tip-bot2 for Alexandre Chartre
  2025-11-25 18:16   ` [PATCH v6 03/30] " Nathan Chancellor
  2025-12-09  6:58   ` Guenter Roeck
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:12 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

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 */

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Create disassembly context
  2025-11-21  9:53 ` [PATCH v6 02/30] objtool: Create disassembly context Alexandre Chartre
@ 2025-11-24  9:12   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:12 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Create disassembly context

Create a structure to store information for disassembling functions.
For now, it is just a wrapper around an objtool file.

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-3-alexandre.chartre@oracle.com
---
 tools/objtool/check.c                   |  6 ++++-
 tools/objtool/disas.c                   | 32 ++++++++++++++++++++++--
 tools/objtool/include/objtool/disas.h   | 14 +++++++++++-
 tools/objtool/include/objtool/objtool.h |  2 +--
 4 files changed, 49 insertions(+), 5 deletions(-)
 create mode 100644 tools/objtool/include/objtool/disas.h

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index 1c7186f..8b1a6a5 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -12,6 +12,7 @@
 #include <objtool/builtin.h>
 #include <objtool/cfi.h>
 #include <objtool/arch.h>
+#include <objtool/disas.h>
 #include <objtool/check.h>
 #include <objtool/special.h>
 #include <objtool/warn.h>
@@ -4802,6 +4803,7 @@ static void free_insns(struct objtool_file *file)
 
 int check(struct objtool_file *file)
 {
+	struct disas_context *disas_ctx;
 	int ret = 0, warnings = 0;
 
 	arch_initial_func_cfi_state(&initial_func_cfi);
@@ -4943,7 +4945,9 @@ out:
 	if (opts.verbose) {
 		if (opts.werror && warnings)
 			WARN("%d warning(s) upgraded to errors", warnings);
-		disas_warned_funcs(file);
+		disas_ctx = disas_context_create(file);
+		disas_warned_funcs(disas_ctx);
+		disas_context_destroy(disas_ctx);
 	}
 
 	if (opts.backup && make_backup())
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 3a7cb1b..7a18e51 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -4,10 +4,35 @@
  */
 
 #include <objtool/arch.h>
+#include <objtool/disas.h>
 #include <objtool/warn.h>
 
 #include <linux/string.h>
 
+struct disas_context {
+	struct objtool_file *file;
+};
+
+struct disas_context *disas_context_create(struct objtool_file *file)
+{
+	struct disas_context *dctx;
+
+	dctx = malloc(sizeof(*dctx));
+	if (!dctx) {
+		WARN("failed to allocate disassembly context");
+		return NULL;
+	}
+
+	dctx->file = file;
+
+	return dctx;
+}
+
+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)
 {
@@ -58,12 +83,15 @@ static void disas_funcs(const char *funcs)
 	}
 }
 
-void disas_warned_funcs(struct objtool_file *file)
+void disas_warned_funcs(struct disas_context *dctx)
 {
 	struct symbol *sym;
 	char *funcs = NULL, *tmp;
 
-	for_each_sym(file->elf, sym) {
+	if (!dctx)
+		return;
+
+	for_each_sym(dctx->file->elf, sym) {
 		if (sym->warned) {
 			if (!funcs) {
 				funcs = malloc(strlen(sym->name) + 1);
diff --git a/tools/objtool/include/objtool/disas.h b/tools/objtool/include/objtool/disas.h
new file mode 100644
index 0000000..5c543b6
--- /dev/null
+++ b/tools/objtool/include/objtool/disas.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025, Oracle and/or its affiliates.
+ */
+
+#ifndef _DISAS_H
+#define _DISAS_H
+
+struct disas_context;
+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);
+
+#endif /* _DISAS_H */
diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h
index 35f926c..f7051bb 100644
--- a/tools/objtool/include/objtool/objtool.h
+++ b/tools/objtool/include/objtool/objtool.h
@@ -49,6 +49,4 @@ int check(struct objtool_file *file);
 int orc_dump(const char *objname);
 int orc_create(struct objtool_file *file);
 
-void disas_warned_funcs(struct objtool_file *file);
-
 #endif /* _OBJTOOL_H */

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Move disassembly functions to a separated file
  2025-11-21  9:53 ` [PATCH v6 01/30] objtool: Move disassembly functions to a separated file Alexandre Chartre
@ 2025-11-24  9:12   ` tip-bot2 for Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24  9:12 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

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

objtool: Move disassembly functions to a separated file

objtool disassembles functions which have warnings. Move the code
to do that to a dedicated file. The code is just moved, it is not
changed.

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-2-alexandre.chartre@oracle.com
---
 tools/objtool/Build                     |  1 +-
 tools/objtool/check.c                   | 81 +----------------------
 tools/objtool/disas.c                   | 90 ++++++++++++++++++++++++-
 tools/objtool/include/objtool/objtool.h |  2 +-
 4 files changed, 93 insertions(+), 81 deletions(-)
 create mode 100644 tools/objtool/disas.c

diff --git a/tools/objtool/Build b/tools/objtool/Build
index 8cd71b9..17e50a1 100644
--- a/tools/objtool/Build
+++ b/tools/objtool/Build
@@ -7,6 +7,7 @@ objtool-y += special.o
 objtool-y += builtin-check.o
 objtool-y += elf.o
 objtool-y += objtool.o
+objtool-y += 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/check.c b/tools/objtool/check.c
index 490cf78..1c7186f 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -4731,87 +4731,6 @@ static int validate_reachable_instructions(struct objtool_file *file)
 	return warnings;
 }
 
-/* 'funcs' is a space-separated list of function names */
-static void disas_funcs(const char *funcs)
-{
-	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;
-	}
-
-	cmd = malloc(size);
-
-	/* real snprintf() */
-	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
-	ret = system(cmd);
-	if (ret) {
-		WARN("disassembly failed: %d", ret);
-		return;
-	}
-}
-
-static void disas_warned_funcs(struct objtool_file *file)
-{
-	struct symbol *sym;
-	char *funcs = NULL, *tmp;
-
-	for_each_sym(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 (funcs)
-		disas_funcs(funcs);
-}
-
 __weak bool arch_absolute_reloc(struct elf *elf, struct reloc *reloc)
 {
 	unsigned int type = reloc_type(reloc);
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
new file mode 100644
index 0000000..3a7cb1b
--- /dev/null
+++ b/tools/objtool/disas.c
@@ -0,0 +1,90 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2015-2017 Josh Poimboeuf <jpoimboe@redhat.com>
+ */
+
+#include <objtool/arch.h>
+#include <objtool/warn.h>
+
+#include <linux/string.h>
+
+/* 'funcs' is a space-separated list of function names */
+static void disas_funcs(const char *funcs)
+{
+	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;
+	}
+
+	cmd = malloc(size);
+
+	/* real snprintf() */
+	snprintf(cmd, size, objdump_str, cross_compile, objname, funcs);
+	ret = system(cmd);
+	if (ret) {
+		WARN("disassembly failed: %d", ret);
+		return;
+	}
+}
+
+void disas_warned_funcs(struct objtool_file *file)
+{
+	struct symbol *sym;
+	char *funcs = NULL, *tmp;
+
+	for_each_sym(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 (funcs)
+		disas_funcs(funcs);
+}
diff --git a/tools/objtool/include/objtool/objtool.h b/tools/objtool/include/objtool/objtool.h
index f7051bb..35f926c 100644
--- a/tools/objtool/include/objtool/objtool.h
+++ b/tools/objtool/include/objtool/objtool.h
@@ -49,4 +49,6 @@ int check(struct objtool_file *file);
 int orc_dump(const char *objname);
 int orc_create(struct objtool_file *file);
 
+void disas_warned_funcs(struct objtool_file *file);
+
 #endif /* _OBJTOOL_H */

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Trim trailing NOPs in alternative
  2025-11-21  9:53 ` [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative Alexandre Chartre
  2025-11-24  9:10   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 10:43 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     81dbe4067f3c923b5771fd8c8d97cdcca62e401d
Gitweb:        https://git.kernel.org/tip/81dbe4067f3c923b5771fd8c8d97cdcca62e401d
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:40 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 11:35:17 +01:00

objtool: Trim trailing NOPs in alternative

When disassembling alternatives replace trailing NOPs with a single
indication of the number of bytes covered with NOPs.

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-31-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 78 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 73 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f04bc14..441b930 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -52,6 +52,7 @@ struct disas_alt {
 	struct {
 		char *str;			/* instruction string */
 		int offset;			/* instruction offset */
+		int nops;			/* number of nops */
 	} insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
 	int insn_idx;				/* index of the next instruction to print */
 };
@@ -727,7 +728,7 @@ static int disas_alt_init(struct disas_alt *dalt,
 }
 
 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
-			      int offset)
+			      int offset, int nops)
 {
 	int len;
 
@@ -740,6 +741,7 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	len = strlen(insn_str);
 	dalt->insn[index].str = insn_str;
 	dalt->insn[index].offset = offset;
+	dalt->insn[index].nops = nops;
 	if (len > dalt->width)
 		dalt->width = len;
 
@@ -752,6 +754,7 @@ static int disas_alt_jump(struct disas_alt *dalt)
 	struct instruction *dest_insn;
 	char suffix[2] = { 0 };
 	char *str;
+	int nops;
 
 	orig_insn = dalt->orig_insn;
 	dest_insn = dalt->alt->insn;
@@ -762,14 +765,16 @@ static int disas_alt_jump(struct disas_alt *dalt)
 		str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
 			     dest_insn->offset, dest_insn->sym->name,
 			     dest_insn->offset - dest_insn->sym->offset);
+		nops = 0;
 	} else {
 		str = strfmt("nop%d", orig_insn->len);
+		nops = orig_insn->len;
 	}
 
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, nops);
 
 	return 1;
 }
@@ -789,7 +794,7 @@ static int disas_alt_extable(struct disas_alt *dalt)
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, 0);
 
 	return 1;
 }
@@ -805,11 +810,13 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 	int offset;
 	char *str;
 	int count;
+	int nops;
 	int err;
 
 	file = dctx->file;
 	count = 0;
 	offset = 0;
+	nops = 0;
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
@@ -818,7 +825,8 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 		if (!str)
 			return -1;
 
-		err = disas_alt_add_insn(dalt, count, str, offset);
+		nops = insn->type == INSN_NOP ? insn->len : 0;
+		err = disas_alt_add_insn(dalt, count, str, offset, nops);
 		if (err)
 			break;
 		offset += insn->len;
@@ -834,6 +842,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 {
 	char *str;
+	int nops;
 	int err;
 
 	if (DALT_GROUP(dalt))
@@ -849,7 +858,8 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	str = strdup(disas_result(dctx));
 	if (!str)
 		return -1;
-	err = disas_alt_add_insn(dalt, 0, str, 0);
+	nops = dalt->orig_insn->type == INSN_NOP ? dalt->orig_insn->len : 0;
+	err = disas_alt_add_insn(dalt, 0, str, 0, nops);
 	if (err)
 		return -1;
 
@@ -996,6 +1006,62 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 }
 
 /*
+ * Trim NOPs in alternatives. This replaces trailing NOPs in alternatives
+ * with a single indication of the number of bytes covered with NOPs.
+ *
+ * Return the maximum numbers of instructions in all alternatives after
+ * trailing NOPs have been trimmed.
+ */
+static int disas_alt_trim_nops(struct disas_alt *dalts, int alt_count,
+			       int insn_count)
+{
+	struct disas_alt *dalt;
+	int nops_count;
+	const char *s;
+	int offset;
+	int count;
+	int nops;
+	int i, j;
+
+	count = 0;
+	for (i = 0; i < alt_count; i++) {
+		offset = 0;
+		nops = 0;
+		nops_count = 0;
+		dalt = &dalts[i];
+		for (j = insn_count - 1; j >= 0; j--) {
+			if (!dalt->insn[j].str || !dalt->insn[j].nops)
+				break;
+			offset = dalt->insn[j].offset;
+			free(dalt->insn[j].str);
+			dalt->insn[j].offset = 0;
+			dalt->insn[j].str = NULL;
+			nops += dalt->insn[j].nops;
+			nops_count++;
+		}
+
+		/*
+		 * All trailing NOPs have been removed. If there was a single
+		 * NOP instruction then re-add it. If there was a block of
+		 * NOPs then indicate the number of bytes than the block
+		 * covers (nop*<number-of-bytes>).
+		 */
+		if (nops_count) {
+			s = nops_count == 1 ? "" : "*";
+			dalt->insn[j + 1].str = strfmt("nop%s%d", s, nops);
+			dalt->insn[j + 1].offset = offset;
+			dalt->insn[j + 1].nops = nops;
+			j++;
+		}
+
+		if (j > count)
+			count = j;
+	}
+
+	return count + 1;
+}
+
+/*
  * Disassemble an alternative.
  *
  * Return the last instruction in the default alternative so that
@@ -1083,6 +1149,8 @@ static void *disas_alt(struct disas_context *dctx,
 	 * Print default and non-default alternatives.
 	 */
 
+	insn_count = disas_alt_trim_nops(dalts, alt_count, insn_count);
+
 	if (opts.wide)
 		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
 	else

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add wide output for disassembly
  2025-11-21  9:53 ` [PATCH v6 29/30] objtool: Add wide output for disassembly Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 10:43 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     5c2ae4f240fe1b3f6d96f54397d3a14f070c84c3
Gitweb:        https://git.kernel.org/tip/5c2ae4f240fe1b3f6d96f54397d3a14f070c84c3
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:39 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 11:35:16 +01:00

objtool: Add wide output for disassembly

Add the --wide option to provide a wide output when disassembling.
With this option, the disassembly of alternatives is displayed
side-by-side instead of one above the other.

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-30-alexandre.chartre@oracle.com
---
 tools/objtool/builtin-check.c           |  1 +-
 tools/objtool/disas.c                   | 95 +++++++++++++++++++++++-
 tools/objtool/include/objtool/builtin.h |  1 +-
 3 files changed, 96 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index a037131..b780df5 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -107,6 +107,7 @@ static const struct option check_options[] = {
 	OPT_STRING(0,		 "trace", &opts.trace, "func", "trace function validation"),
 	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
 	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
+	OPT_BOOLEAN(0,		 "wide", &opts.wide, "wide output"),
 
 	OPT_END(),
 };
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index a4f905e..f04bc14 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -857,6 +857,95 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 }
 
 /*
+ * For each alternative, if there is an instruction at the specified
+ * offset then print this instruction, otherwise print a blank entry.
+ * The offset is an offset from the start of the alternative.
+ *
+ * Return the offset for the next instructions to print, or -1 if all
+ * instructions have been printed.
+ */
+static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count,
+				int insn_count, int offset)
+{
+	struct disas_alt *dalt;
+	int offset_next;
+	char *str;
+	int i, j;
+
+	offset_next = -1;
+
+	for (i = 0; i < alt_count; i++) {
+		dalt = &dalts[i];
+		j = dalt->insn_idx;
+		if (j == -1) {
+			printf("| %-*s ", dalt->width, "");
+			continue;
+		}
+
+		if (dalt->insn[j].offset == offset) {
+			str = dalt->insn[j].str;
+			printf("| %-*s ", dalt->width, str ?: "");
+			if (++j < insn_count) {
+				dalt->insn_idx = j;
+			} else {
+				dalt->insn_idx = -1;
+				continue;
+			}
+		} else {
+			printf("| %-*s ", dalt->width, "");
+		}
+
+		if (dalt->insn[j].offset > 0 &&
+		    (offset_next == -1 ||
+		     (dalt->insn[j].offset < offset_next)))
+			offset_next = dalt->insn[j].offset;
+	}
+	printf("\n");
+
+	return offset_next;
+}
+
+/*
+ * Print all alternatives side-by-side.
+ */
+static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count,
+				 int insn_count)
+{
+	struct instruction *orig_insn;
+	int offset_next;
+	int offset;
+	int i;
+
+	orig_insn = dalts[0].orig_insn;
+
+	/*
+	 * Print an header with the name of each alternative.
+	 */
+	disas_print_info(stdout, orig_insn, -2, NULL);
+
+	if (strlen(alt_name) > dalts[0].width)
+		dalts[0].width = strlen(alt_name);
+	printf("| %-*s ", dalts[0].width, alt_name);
+
+	for (i = 1; i < alt_count; i++)
+		printf("| %-*s ", dalts[i].width, dalts[i].name);
+
+	printf("\n");
+
+	/*
+	 * Print instructions for each alternative.
+	 */
+	offset_next = 0;
+	do {
+		offset = offset_next;
+		disas_print(stdout, orig_insn->sec, orig_insn->offset + offset,
+			    -2, NULL);
+		offset_next = disas_alt_print_insn(dalts, alt_count, insn_count,
+						   offset);
+	} while (offset_next > offset);
+}
+
+/*
  * Print all alternatives one above the other.
  */
 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
@@ -993,7 +1082,11 @@ static void *disas_alt(struct disas_context *dctx,
 	/*
 	 * Print default and non-default alternatives.
 	 */
-	disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
+
+	if (opts.wide)
+		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
+	else
+		disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
 
 	last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
 		orig_insn;
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index e3af664..b9e229e 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -45,6 +45,7 @@ struct opts {
 	const char *trace;
 	bool verbose;
 	bool werror;
+	bool wide;
 };
 
 extern struct opts opts;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Compact output for alternatives with one instruction
  2025-11-21  9:53 ` [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 10:43 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     59736d6418ace7537e10aaf9b28b671a100abe45
Gitweb:        https://git.kernel.org/tip/59736d6418ace7537e10aaf9b28b671a100abe45
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:38 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 11:35:16 +01:00

objtool: Compact output for alternatives with one instruction

When disassembling, if an instruction has alternatives which are all
made of a single instruction then print each alternative on a single
line (instruction + description) so that the output is more compact.

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-29-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 731c449..a4f905e 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -863,6 +863,7 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 				    int alt_count, int insn_count)
 {
 	struct instruction *orig_insn;
+	int width;
 	int i, j;
 	int len;
 
@@ -871,6 +872,27 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
 	printf("%s\n", alt_name);
 
+	/*
+	 * If all alternatives have a single instruction then print each
+	 * alternative on a single line. Otherwise, print alternatives
+	 * one above the other with a clear separation.
+	 */
+
+	if (insn_count == 1) {
+		width = 0;
+		for (i = 0; i < alt_count; i++) {
+			if (dalts[i].width > width)
+				width = dalts[i].width;
+		}
+
+		for (i = 0; i < alt_count; i++) {
+			printf("%*s= %-*s    (if %s)\n", len, "", width,
+			       dalts[i].insn[0].str, dalts[i].name);
+		}
+
+		return;
+	}
+
 	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
 		for (j = 0; j < insn_count; j++) {

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Improve naming of group alternatives
  2025-11-21  9:53 ` [PATCH v6 27/30] objtool: Improve naming of group alternatives Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
  2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 10:44 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     47793c0677e11bef7eb546d90e73f1d51e6eb22b
Gitweb:        https://git.kernel.org/tip/47793c0677e11bef7eb546d90e73f1d51e6eb22b
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:37 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 11:35:16 +01:00

objtool: Improve naming of group alternatives

Improve the naming of group alternatives by showing the feature name and
flags used by the alternative.

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-28-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 58 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f8917c8..731c449 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -9,6 +9,7 @@
 #include <objtool/arch.h>
 #include <objtool/check.h>
 #include <objtool/disas.h>
+#include <objtool/special.h>
 #include <objtool/warn.h>
 
 #include <bfd.h>
@@ -60,6 +61,21 @@ struct disas_alt {
 #define DALT_GROUP(dalt)	(DALT_INSN(dalt)->alt_group)
 #define DALT_ALTID(dalt)	((dalt)->orig_insn->offset)
 
+#define ALT_FLAGS_SHIFT		16
+#define ALT_FLAG_NOT		(1 << 0)
+#define ALT_FLAG_DIRECT_CALL	(1 << 1)
+#define ALT_FEATURE_MASK	((1 << ALT_FLAGS_SHIFT) - 1)
+
+static int alt_feature(unsigned int ft_flags)
+{
+	return (ft_flags & ALT_FEATURE_MASK);
+}
+
+static int alt_flags(unsigned int ft_flags)
+{
+	return (ft_flags >> ALT_FLAGS_SHIFT);
+}
+
 /*
  * Wrapper around asprintf() to allocate and format a string.
  * Return the allocated string or NULL on error.
@@ -635,7 +651,12 @@ const char *disas_alt_type_name(struct instruction *insn)
  */
 char *disas_alt_name(struct alternative *alt)
 {
+	char pfx[4] = { 0 };
 	char *str = NULL;
+	const char *name;
+	int feature;
+	int flags;
+	int num;
 
 	switch (alt->type) {
 
@@ -649,13 +670,37 @@ char *disas_alt_name(struct alternative *alt)
 
 	case ALT_TYPE_INSTRUCTIONS:
 		/*
-		 * This is a non-default group alternative. Create a unique
-		 * name using the offset of the first original and alternative
-		 * instructions.
+		 * This is a non-default group alternative. Create a name
+		 * based on the feature and flags associated with this
+		 * alternative. Use either the feature name (it is available)
+		 * or the feature number. And add a prefix to show the flags
+		 * used.
+		 *
+		 * Prefix flags characters:
+		 *
+		 *   '!'  alternative used when feature not enabled
+		 *   '+'  direct call alternative
+		 *   '?'  unknown flag
 		 */
-		asprintf(&str, "ALTERNATIVE %lx.%lx",
-			 alt->insn->alt_group->orig_group->first_insn->offset,
-			 alt->insn->alt_group->first_insn->offset);
+
+		feature = alt->insn->alt_group->feature;
+		num = alt_feature(feature);
+		flags = alt_flags(feature);
+		str = pfx;
+
+		if (flags & ~(ALT_FLAG_NOT | ALT_FLAG_DIRECT_CALL))
+			*str++ = '?';
+		if (flags & ALT_FLAG_DIRECT_CALL)
+			*str++ = '+';
+		if (flags & ALT_FLAG_NOT)
+			*str++ = '!';
+
+		name = arch_cpu_feature_name(num);
+		if (!name)
+			str = strfmt("%sFEATURE 0x%X", pfx, num);
+		else
+			str = strfmt("%s%s", pfx, name);
+
 		break;
 	}
 
@@ -892,6 +937,7 @@ static void *disas_alt(struct disas_context *dctx,
 			WARN("%s has more alternatives than supported", alt_name);
 			break;
 		}
+
 		dalt = &dalts[i];
 		err = disas_alt_init(dalt, orig_insn, alt);
 		if (err) {

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-21  9:53 ` [PATCH v6 26/30] objtool: Function to get the name of a CPU feature Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
  2025-11-24 11:59     ` Peter Zijlstra
  2025-11-24 19:49   ` [tip: objtool/core] objtool: Add " tip-bot2 for Alexandre Chartre
  2 siblings, 1 reply; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 10:44 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     afff4e5820e9a0d609740a83c366f3f0335db342
Gitweb:        https://git.kernel.org/tip/afff4e5820e9a0d609740a83c366f3f0335db342
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 11:35:06 +01:00

objtool: Function to get the name of a CPU feature

Add a function to get the name of a CPU feature. The function is
architecture dependent and currently only implemented for x86. The
feature names are automatically generated from the cpufeatures.h
include file.

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-27-alexandre.chartre@oracle.com
---
 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk | 33 +++++++++++++-
 tools/objtool/.gitignore                           |  1 +-
 tools/objtool/Makefile                             |  1 +-
 tools/objtool/arch/loongarch/special.c             |  5 ++-
 tools/objtool/arch/powerpc/special.c               |  5 ++-
 tools/objtool/arch/x86/Build                       | 13 ++++-
 tools/objtool/arch/x86/special.c                   | 10 ++++-
 tools/objtool/include/objtool/special.h            |  2 +-
 8 files changed, 69 insertions(+), 1 deletion(-)
 create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk

diff --git a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
new file mode 100644
index 0000000..1b1c1d8
--- /dev/null
+++ b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
@@ -0,0 +1,33 @@
+#!/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Usage: awk -f gen-cpu-feature-names-x86.awk cpufeatures.h > cpu-feature-names.c
+#
+
+BEGIN {
+	print "/* cpu feature name array generated from cpufeatures.h */"
+	print "/* Do not change this code. */"
+	print
+	print "static const char *cpu_feature_names[(NCAPINTS+NBUGINTS)*32] = {"
+
+	feature_expr = "(X86_FEATURE_[A-Z0-9_]+)\\s+\\(([0-9*+ ]+)\\)"
+	debug_expr = "(X86_BUG_[A-Z0-9_]+)\\s+X86_BUG\\(([0-9*+ ]+)\\)"
+}
+
+/^#define X86_FEATURE_/ {
+	if (match($0, feature_expr, m)) {
+		print "\t[" m[2] "] = \"" m[1] "\","
+	}
+}
+
+/^#define X86_BUG_/ {
+	if (match($0, debug_expr, m)) {
+		print "\t[NCAPINTS*32+(" m[2] ")] = \"" m[1] "\","
+	}
+}
+
+END {
+	print "};"
+}
diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 7593036..73d8831 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+arch/x86/lib/cpu-feature-names.c
 arch/x86/lib/inat-tables.c
 /objtool
 feature
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index df793ca..66397d7 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -125,6 +125,7 @@ $(LIBSUBCMD)-clean:
 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
diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c
index a80b75f..aba7741 100644
--- a/tools/objtool/arch/loongarch/special.c
+++ b/tools/objtool/arch/loongarch/special.c
@@ -194,3 +194,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/powerpc/special.c b/tools/objtool/arch/powerpc/special.c
index 5161068..8f9bf61 100644
--- a/tools/objtool/arch/powerpc/special.c
+++ b/tools/objtool/arch/powerpc/special.c
@@ -18,3 +18,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 {
 	exit(-1);
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build
index 3dedb2f..febee0b 100644
--- a/tools/objtool/arch/x86/Build
+++ b/tools/objtool/arch/x86/Build
@@ -1,5 +1,5 @@
-objtool-y += special.o
 objtool-y += decode.o
+objtool-y += special.o
 objtool-y += orc.o
 
 inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk
@@ -12,3 +12,14 @@ $(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
 $(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
 
 CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
+
+cpu_features = ../arch/x86/include/asm/cpufeatures.h
+cpu_features_script = ../arch/x86/tools/gen-cpu-feature-names-x86.awk
+
+$(OUTPUT)arch/x86/lib/cpu-feature-names.c: $(cpu_features_script) $(cpu_features)
+	$(call rule_mkdir)
+	$(Q)$(call echo-cmd,gen)$(AWK) -f $(cpu_features_script) $(cpu_features) > $@
+
+$(OUTPUT)arch/x86/special.o: $(OUTPUT)arch/x86/lib/cpu-feature-names.c
+
+CFLAGS_special.o += -I$(OUTPUT)arch/x86/lib
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index 0930076..e817a3f 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -4,6 +4,10 @@
 #include <objtool/special.h>
 #include <objtool/builtin.h>
 #include <objtool/warn.h>
+#include <asm/cpufeatures.h>
+
+/* cpu feature name array generated from cpufeatures.h */
+#include "cpu-feature-names.c"
 
 void arch_handle_alternative(struct special_alt *alt)
 {
@@ -134,3 +138,9 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 	*table_size = 0;
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return (feature_number < ARRAY_SIZE(cpu_feature_names)) ?
+		cpu_feature_names[feature_number] : NULL;
+}
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index b224107..121c376 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -38,4 +38,6 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
 struct reloc *arch_find_switch_table(struct objtool_file *file,
 				     struct instruction *insn,
 				     unsigned long *table_size);
+const char *arch_cpu_feature_name(int feature_number);
+
 #endif /* _SPECIAL_H */

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
                   ` (30 preceding siblings ...)
  2025-11-21 10:36 ` [PATCH v6 00/30] objtool: Function validation tracing Peter Zijlstra
@ 2025-11-24 11:04 ` Borislav Petkov
  2025-11-24 11:39   ` Alexandre Chartre
  2025-11-24 12:27   ` David Laight
  31 siblings, 2 replies; 110+ messages in thread
From: Borislav Petkov @ 2025-11-24 11:04 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux

On Fri, Nov 21, 2025 at 10:53:10AM +0100, Alexandre Chartre wrote:
> Notes:
> ======

So those examples below look very useful and cool. Can you pls document them
so that I can find the breadcrumbs next time and use them?

:-)

Thx.

> Disassembly can show default alternative jumping to .altinstr_aux
> -----------------------------------------------------------------
> Disassembly can show a default alternative jumping to .altinstr_aux. This
> happens when the _static_cpu_has() function is used. Its default code
> jumps to .altinstr_aux where a test sequence is executed (test; jnz; jmp).
> 
> At runtime, this sequence is not used because the _static_cpu_has() 
> an alternative with the X86_FEATURE_ALWAYS feature. 
> 
> 
>   debc:  perf_get_x86_pmu_capability+0xc      jmpq   0xdec1 <.altinstr_aux+0xfc> | NOP5  (X86_FEATURE_HYBRID_CPU) | jmpq   0x61a <perf_get_x86_pmu_capability+0x37>  (X86_FEATURE_ALWAYS)   # <alternative.debc>
>   dec1:  perf_get_x86_pmu_capability+0x11     ud2                                                       
> 
> 
> Disassembly can show alternative jumping to the next instruction
> ----------------------------------------------------------------
> 
> The disassembly can show jump tables with an alternative which jumps
> to the next instruction.
> 
> For example:
> 
> def9:  perf_get_x86_pmu_capability+0x49    NOP2 | jmp    defb <perf_get_x86_pmu_capability+0x4b>  (JUMP)   # <alternative.def9>
> defb:  perf_get_x86_pmu_capability+0x4b	   mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>      
> 
> This disassembly is correct. These instructions come from:
> 
>         cap->num_counters_gp = x86_pmu_num_counters(NULL)):
> 
> which will end up executing this statement:
> 
>         if (static_branch_unlikely(&perf_is_hybrid) && NULL)
> 	        <do something>;
> 
> This statement is optimized to do nothing because the condition is
> always false. But static_branch_unlikely() is implemented with a jump
> table inside an "asm goto" statement, and "asm goto" statements are
> not optimized.
> 
> So basically the code is optimized like this:
> 
>         if (static_branch_unlikely(&perf_is_hybrid))
> 	        ;
> 
> And this translates to the assembly code above.
> 
> 
> Examples:
> =========
> 
> Example 1 (--trace option): Trace the validation of the os_save() function
> --------------------------------------------------------------------------
> 
> $ ./tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --orc --retpoline --rethunk --sls --static-call --uaccess --prefix=16 --link --trace os_xsave -v vmlinux.o
> os_xsave: validation begin
>  59be0:  os_xsave+0x0                  push   %r12                                          - state: cfa=rsp+16 r12=(cfa-16) stack_size=16 
>  59be2:  os_xsave+0x2		       mov    0x0(%rip),%eax        # 0x59be8 <alternatives_patched>
>  59be8:  os_xsave+0x8		       push   %rbp                                          - state: cfa=rsp+24 rbp=(cfa-24) stack_size=24 
>  59be9:  os_xsave+0x9		       mov    %rdi,%rbp                                          
>  59bec:  os_xsave+0xc		       push   %rbx					     - state: cfa=rsp+32 rbx=(cfa-32) stack_size=32 
>  59bed:  os_xsave+0xd		       mov    0x8(%rdi),%rbx                                     
>  59bf1:  os_xsave+0x11		       mov    %rbx,%r12                                          
>  59bf4:  os_xsave+0x14		       shr    $0x20,%r12                                         
>  59bf8:  os_xsave+0x18		       test   %eax,%eax                                          
>  59bfa:  os_xsave+0x1a		       je     0x59c22 <os_xsave+0x42>                        - jump taken
>  59c22:  os_xsave+0x42		       | ud2                                                     
>  59c24:  os_xsave+0x44		       | jmp    0x59bfc <os_xsave+0x1c>                      - unconditional jump
>  59bfc:  os_xsave+0x1c		       | | xor    %edx,%edx                                      
>  59bfe:  os_xsave+0x1e		       | | mov    %rbx,%rsi                                      
>  59c01:  os_xsave+0x21		       | | mov    %rbp,%rdi                                      
>  59c04:  os_xsave+0x24		       | | callq  0x59c09 <xfd_validate_state>               - call
>  59c09:  os_xsave+0x29		       | | mov    %ebx,%eax                                      
>  59c0b:  os_xsave+0x2b		       | | mov    %r12d,%edx                                     
>  	 			       | | / <alternative.59c0e> X86_FEATURE_XSAVEOPT
>   1b29:  .altinstr_replacement+0x1b29  | | | xsaveopt64 0x40(%rbp)                               
>  59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                    
>  59c15:  os_xsave+0x35		       | | | test   %ebx,%ebx                                    
>  59c17:  os_xsave+0x37		       | | | jne    0x59c26 <os_xsave+0x46>                  - jump taken
>  59c26:  os_xsave+0x46		       | | | | ud2                                               
>  59c28:  os_xsave+0x48		       | | | | pop    %rbx                                   - state: cfa=rsp+24 rbx=<undef> stack_size=24 
>  59c29:  os_xsave+0x49		       | | | | pop    %rbp				     - state: cfa=rsp+16 rbp=<undef> stack_size=16 
>  59c2a:  os_xsave+0x4a		       | | | | pop    %r12				     - state: cfa=rsp+8 r12=<undef> stack_size=8 
>  59c2c:  os_xsave+0x4c		       | | | | jmpq   0x59c31 <__x86_return_thunk>	     - return
>  59c17:  os_xsave+0x37		       | | | jne    0x59c26 <os_xsave+0x46>		     - jump not taken
>  59c19:  os_xsave+0x39		       | | | pop    %rbx    				     - state: cfa=rsp+24 rbx=<undef> stack_size=24 
>  59c1a:  os_xsave+0x3a		       | | | pop    %rbp				     - state: cfa=rsp+16 rbp=<undef> stack_size=16 
>  59c1b:  os_xsave+0x3b		       | | | pop    %r12				     - state: cfa=rsp+8 r12=<undef> stack_size=8 
>  59c1d:  os_xsave+0x3d		       | | | jmpq   0x59c22 <__x86_return_thunk>	     - return
>  	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVEOPT
> 				       | | / <alternative.59c0e> X86_FEATURE_XSAVEC
>   1b2e:  .altinstr_replacement+0x1b2e  | | | xsavec64 0x40(%rbp)                                 
>  59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                - already visited
>  	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVEC
> 				       | | / <alternative.59c0e> X86_FEATURE_XSAVES
>   1b33:  .altinstr_replacement+0x1b33  | | | xsaves64 0x40(%rbp)                                 
>  59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                - already visited
>  	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVES
> 				       | | / <alternative.59c0e> EXCEPTION for instruction at 0x59c0e <os_xsave+0x2e>
>  59c15:  os_xsave+0x35		       | | | test   %ebx,%ebx                                - already visited
>  	 			       | | \ <alternative.59c0e> EXCEPTION
> 				       | | / <alternative.59c0e> DEFAULT
>  59c0e:  os_xsave+0x2e		       | | xsave64 0x40(%rbp)                                    
>  59c13:  os_xsave+0x33		       | | xor    %ebx,%ebx                                  - already visited
>  59bfa:  os_xsave+0x1a		       je     0x59c22 <os_xsave+0x42>                        - jump not taken
>  59bfc:  os_xsave+0x1c		       xor    %edx,%edx                                      - already visited
> os_xsave: validation end
> 
> 
> Example 2 (--disas option): Single Instruction Alternatives
> -----------------------------------------------------------
> 
> Compact Output (default):
> 
> Alternatives with single instructions are displayed each on one line,
> with the instruction and a description of the alternative.
> 
> $ ./tools/objtool/objtool --disas=perf_get_x86_pmu_capability --link vmlinux.o
> perf_get_x86_pmu_capability:
>   deb0:  perf_get_x86_pmu_capability+0x0     endbr64                                                   
>   deb4:  perf_get_x86_pmu_capability+0x4     callq  0xdeb9 <__fentry__>                                
>   deb9:  perf_get_x86_pmu_capability+0x9     mov    %rdi,%rdx                                          
>   debc:  perf_get_x86_pmu_capability+0xc     <alternative.debc>
>   	 				     = jmpq   0xdec1 <.altinstr_aux+0xfc>                 (if DEFAULT)
> 					     = jmpq   0x622 <perf_get_x86_pmu_capability+0x37>    (if X86_FEATURE_ALWAYS)
> 					     = nop5                                               (if X86_FEATURE_HYBRID_CPU)
>   dec1:  perf_get_x86_pmu_capability+0x11    ud2                                                       
>   dec3:  perf_get_x86_pmu_capability+0x13    movq   $0x0,(%rdx)                                        
>   deca:  perf_get_x86_pmu_capability+0x1a    movq   $0x0,0x8(%rdx)                                     
>   ded2:  perf_get_x86_pmu_capability+0x22    movq   $0x0,0x10(%rdx)                                    
>   deda:  perf_get_x86_pmu_capability+0x2a    movq   $0x0,0x18(%rdx)                                    
>   dee2:  perf_get_x86_pmu_capability+0x32    jmpq   0xdee7 <__x86_return_thunk>                        
>   dee7:  perf_get_x86_pmu_capability+0x37    cmpq   $0x0,0x0(%rip)        # 0xdeef <x86_pmu+0x10>      
>   deef:  perf_get_x86_pmu_capability+0x3f    je     0xdec3 <perf_get_x86_pmu_capability+0x13>          
>   def1:  perf_get_x86_pmu_capability+0x41    mov    0x0(%rip),%eax        # 0xdef7 <x86_pmu+0x8>       
>   def7:  perf_get_x86_pmu_capability+0x47    mov    %eax,(%rdi)                                        
>   def9:  perf_get_x86_pmu_capability+0x49    <jump_table.def9>
>   	 				     = nop2                                              (if DEFAULT)
> 					     = jmp    defb <perf_get_x86_pmu_capability+0x4b>    (if JUMP)
>   defb:  perf_get_x86_pmu_capability+0x4b    mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>      
>   df02:  perf_get_x86_pmu_capability+0x52    <alternative.df02>
>   	 				     = callq  0xdf07 <__sw_hweight64>    (if DEFAULT)
> 					     = popcnt %rdi,%rax                  (if X86_FEATURE_POPCNT)
>   df07:  perf_get_x86_pmu_capability+0x57    mov    %eax,0x4(%rdx)                                     
>   df0a:  perf_get_x86_pmu_capability+0x5a    <jump_table.df0a>
>   	 				     = nop2                                              (if DEFAULT)
> 					     = jmp    df0c <perf_get_x86_pmu_capability+0x5c>    (if JUMP)
>   df0c:  perf_get_x86_pmu_capability+0x5c    mov    0x0(%rip),%rdi        # 0xdf13 <x86_pmu+0xe0>      
>   df13:  perf_get_x86_pmu_capability+0x63    <alternative.df13>
>   	 				     = callq  0xdf18 <__sw_hweight64>    (if DEFAULT)
> 					     = popcnt %rdi,%rax                  (if X86_FEATURE_POPCNT)
>   df18:  perf_get_x86_pmu_capability+0x68    mov    %eax,0x8(%rdx)                                     
>   df1b:  perf_get_x86_pmu_capability+0x6b    mov    0x0(%rip),%eax        # 0xdf21 <x86_pmu+0xf8>      
>   df21:  perf_get_x86_pmu_capability+0x71    mov    %eax,0xc(%rdx)                                     
>   df24:  perf_get_x86_pmu_capability+0x74    mov    %eax,0x10(%rdx)                                    
>   df27:  perf_get_x86_pmu_capability+0x77    mov    0x0(%rip),%rax        # 0xdf2e <x86_pmu+0x108>     
>   df2e:  perf_get_x86_pmu_capability+0x7e    mov    %eax,0x14(%rdx)                                    
>   df31:  perf_get_x86_pmu_capability+0x81    mov    0x0(%rip),%eax        # 0xdf37 <x86_pmu+0x110>     
>   df37:  perf_get_x86_pmu_capability+0x87    mov    %eax,0x18(%rdx)                                    
>   df3a:  perf_get_x86_pmu_capability+0x8a    movzbl 0x0(%rip),%ecx        # 0xdf41 <x86_pmu+0x1d1>     
>   df41:  perf_get_x86_pmu_capability+0x91    movzbl 0x1c(%rdx),%eax                                    
>   df45:  perf_get_x86_pmu_capability+0x95    shr    %cl                                                
>   df47:  perf_get_x86_pmu_capability+0x97    and    $0x1,%ecx                                          
>   df4a:  perf_get_x86_pmu_capability+0x9a    and    $0xfffffffe,%eax                                   
>   df4d:  perf_get_x86_pmu_capability+0x9d    or     %ecx,%eax                                          
>   df4f:  perf_get_x86_pmu_capability+0x9f    mov    %al,0x1c(%rdx)                                     
>   df52:  perf_get_x86_pmu_capability+0xa2    jmpq   0xdf57 <__x86_return_thunk>                        
> 
> 
> Wide Output (--wide option):
> 
> Alternatives with single instructions are displayed side-by-side,
> with an header.
> 
> $ ./tools/objtool/objtool --disas=perf_get_x86_pmu_capability --wide --link vmlinux.o
> perf_get_x86_pmu_capability:
>   deb0:  perf_get_x86_pmu_capability+0x0       endbr64                                                   
>   deb4:  perf_get_x86_pmu_capability+0x4       callq  0xdeb9 <__fentry__>                                
>   deb9:  perf_get_x86_pmu_capability+0x9       mov    %rdi,%rdx                                          
>   debc:  perf_get_x86_pmu_capability+0xc     | <alternative.debc>                 | X86_FEATURE_ALWAYS                              | X86_FEATURE_HYBRID_CPU 
>   debc:  perf_get_x86_pmu_capability+0xc     | jmpq   0xdec1 <.altinstr_aux+0xfc> | jmpq   0x622 <perf_get_x86_pmu_capability+0x37> | nop5                   
>   dec1:  perf_get_x86_pmu_capability+0x11      ud2                                                       
>   dec3:  perf_get_x86_pmu_capability+0x13      movq   $0x0,(%rdx)                                        
>   deca:  perf_get_x86_pmu_capability+0x1a      movq   $0x0,0x8(%rdx)                                     
>   ded2:  perf_get_x86_pmu_capability+0x22      movq   $0x0,0x10(%rdx)                                    
>   deda:  perf_get_x86_pmu_capability+0x2a      movq   $0x0,0x18(%rdx)                                    
>   dee2:  perf_get_x86_pmu_capability+0x32      jmpq   0xdee7 <__x86_return_thunk>                        
>   dee7:  perf_get_x86_pmu_capability+0x37      cmpq   $0x0,0x0(%rip)        # 0xdeef <x86_pmu+0x10>      
>   deef:  perf_get_x86_pmu_capability+0x3f      je     0xdec3 <perf_get_x86_pmu_capability+0x13>          
>   def1:  perf_get_x86_pmu_capability+0x41      mov    0x0(%rip),%eax        # 0xdef7 <x86_pmu+0x8>       
>   def7:  perf_get_x86_pmu_capability+0x47      mov    %eax,(%rdi)                                        
>   def9:  perf_get_x86_pmu_capability+0x49    | <jump_table.def9> | JUMP                                           
>   def9:  perf_get_x86_pmu_capability+0x49    | nop2              | jmp    defb <perf_get_x86_pmu_capability+0x4b> 
>   defb:  perf_get_x86_pmu_capability+0x4b      mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>      
>   df02:  perf_get_x86_pmu_capability+0x52    | <alternative.df02>             | X86_FEATURE_POPCNT 
>   df02:  perf_get_x86_pmu_capability+0x52    | callq  0xdf07 <__sw_hweight64> | popcnt %rdi,%rax   
>   df07:  perf_get_x86_pmu_capability+0x57      mov    %eax,0x4(%rdx)                                     
>   df0a:  perf_get_x86_pmu_capability+0x5a    | <jump_table.df0a> | JUMP                                           
>   df0a:  perf_get_x86_pmu_capability+0x5a    | nop2              | jmp    df0c <perf_get_x86_pmu_capability+0x5c> 
>   df0c:  perf_get_x86_pmu_capability+0x5c      mov    0x0(%rip),%rdi        # 0xdf13 <x86_pmu+0xe0>      
>   df13:  perf_get_x86_pmu_capability+0x63    | <alternative.df13>             | X86_FEATURE_POPCNT 
>   df13:  perf_get_x86_pmu_capability+0x63    | callq  0xdf18 <__sw_hweight64> | popcnt %rdi,%rax   
>   df18:  perf_get_x86_pmu_capability+0x68      mov    %eax,0x8(%rdx)                                     
>   df1b:  perf_get_x86_pmu_capability+0x6b      mov    0x0(%rip),%eax        # 0xdf21 <x86_pmu+0xf8>      
>   df21:  perf_get_x86_pmu_capability+0x71      mov    %eax,0xc(%rdx)                                     
>   df24:  perf_get_x86_pmu_capability+0x74      mov    %eax,0x10(%rdx)                                    
>   df27:  perf_get_x86_pmu_capability+0x77      mov    0x0(%rip),%rax        # 0xdf2e <x86_pmu+0x108>     
>   df2e:  perf_get_x86_pmu_capability+0x7e      mov    %eax,0x14(%rdx)                                    
>   df31:  perf_get_x86_pmu_capability+0x81      mov    0x0(%rip),%eax        # 0xdf37 <x86_pmu+0x110>     
>   df37:  perf_get_x86_pmu_capability+0x87      mov    %eax,0x18(%rdx)                                    
>   df3a:  perf_get_x86_pmu_capability+0x8a      movzbl 0x0(%rip),%ecx        # 0xdf41 <x86_pmu+0x1d1>     
>   df41:  perf_get_x86_pmu_capability+0x91      movzbl 0x1c(%rdx),%eax                                    
>   df45:  perf_get_x86_pmu_capability+0x95      shr    %cl                                                
>   df47:  perf_get_x86_pmu_capability+0x97      and    $0x1,%ecx                                          
>   df4a:  perf_get_x86_pmu_capability+0x9a      and    $0xfffffffe,%eax                                   
>   df4d:  perf_get_x86_pmu_capability+0x9d      or     %ecx,%eax                                          
>   df4f:  perf_get_x86_pmu_capability+0x9f      mov    %al,0x1c(%rdx)                                     
>   df52:  perf_get_x86_pmu_capability+0xa2      jmpq   0xdf57 <__x86_return_thunk>                        
> 
> 
> Example 3 (--disas option): Alternatives with multiple instructions
> -------------------------------------------------------------------
> 
> Compact Output (default):
> 
> Alternatives with multiple instructions are displayed one above the other,
> with an header describing the alternative.
> 
> $ ./tools/objtool/objtool --disas=__switch_to_asm --link vmlinux.o
> __switch_to_asm:
>   82c0:  __switch_to_asm+0x0       push   %rbp                                               
>   82c1:  __switch_to_asm+0x1	   push   %rbx                                               
>   82c2:  __switch_to_asm+0x2	   push   %r12                                               
>   82c4:  __switch_to_asm+0x4	   push   %r13                                               
>   82c6:  __switch_to_asm+0x6	   push   %r14                                               
>   82c8:  __switch_to_asm+0x8	   push   %r15                                               
>   82ca:  __switch_to_asm+0xa	   mov    %rsp,0x1670(%rdi)                                  
>   82d1:  __switch_to_asm+0x11	   mov    0x1670(%rsi),%rsp                                  
>   82d8:  __switch_to_asm+0x18	   mov    0xad8(%rsi),%rbx                                   
>   82df:  __switch_to_asm+0x1f	   mov    %rbx,%gs:0x0(%rip)        # 0x82e7 <__stack_chk_guard>
>   82e7:  __switch_to_asm+0x27	   <alternative.82e7>
>   	 			   = DEFAULT
>   82e7:  __switch_to_asm+0x27	   | jmp    0x8312 <__switch_to_asm+0x52>
>   82e9:  __switch_to_asm+0x29	   | nop*41
>   	 			   |
> 				   = X86_FEATURE_RSB_CTXSW
>   82e7:  __switch_to_asm+0x27	   | mov    $0x10,%r12
>   82ee:  __switch_to_asm+0x2e	   | callq  0x82f4 <__switch_to_asm+0x34>
>   82f3:  __switch_to_asm+0x33	   | int3   
>   82f4:  __switch_to_asm+0x34	   | callq  0x82fa <__switch_to_asm+0x3a>
>   82f9:  __switch_to_asm+0x39	   | int3   
>   82fa:  __switch_to_asm+0x3a	   | add    $0x10,%rsp
>   82fe:  __switch_to_asm+0x3e	   | dec    %r12
>   8301:  __switch_to_asm+0x41	   | jne    0x82ee <__switch_to_asm+0x2e>
>   8303:  __switch_to_asm+0x43	   | lfence 
>   8306:  __switch_to_asm+0x46	   | movq   $0xffffffffffffffff,%gs:0x0(%rip)        # 0x20b <__x86_call_depth>
>   	 			   |
> 				   = !X86_FEATURE_ALWAYS
>   82e7:  __switch_to_asm+0x27	   | nop1
>   82e8:  __switch_to_asm+0x28	   | nop1
>   82e9:  __switch_to_asm+0x29	   | callq  0x82ef <__switch_to_asm+0x2f>
>   82ee:  __switch_to_asm+0x2e	   | int3   
>   82ef:  __switch_to_asm+0x2f	   | add    $0x8,%rsp
>   82f3:  __switch_to_asm+0x33	   | lfence 
>   	 			   |
>   8312:  __switch_to_asm+0x52	   pop    %r15                                               
>   8314:  __switch_to_asm+0x54	   pop    %r14                                               
>   8316:  __switch_to_asm+0x56	   pop    %r13                                               
>   8318:  __switch_to_asm+0x58	   pop    %r12                                               
>   831a:  __switch_to_asm+0x5a	   pop    %rbx                                               
>   831b:  __switch_to_asm+0x5b	   pop    %rbp                                               
>   831c:  __switch_to_asm+0x5c	   jmpq   0x8321 <__switch_to>                               
> 
> 
> Wide Output (--wide option):
> 
> Alternatives with multiple instructions are displayed side-by-side, with
> an header describing the alternative. The code in the first column is the
> default code of the alternative.
> 
> $ ./tools/objtool/objtool --disas=__switch_to_asm --wide  --link vmlinux.o
> __switch_to_asm:
>   82c0:  __switch_to_asm+0x0       push   %rbp                                               
>   82c1:  __switch_to_asm+0x1	   push   %rbx                                               
>   82c2:  __switch_to_asm+0x2	   push   %r12                                               
>   82c4:  __switch_to_asm+0x4	   push   %r13                                               
>   82c6:  __switch_to_asm+0x6	   push   %r14                                               
>   82c8:  __switch_to_asm+0x8	   push   %r15                                               
>   82ca:  __switch_to_asm+0xa	   mov    %rsp,0x1670(%rdi)                                  
>   82d1:  __switch_to_asm+0x11	   mov    0x1670(%rsi),%rsp                                  
>   82d8:  __switch_to_asm+0x18	   mov    0xad8(%rsi),%rbx                                   
>   82df:  __switch_to_asm+0x1f	   mov    %rbx,%gs:0x0(%rip)        # 0x82e7 <__stack_chk_guard>
>   82e7:  __switch_to_asm+0x27	 | <alternative.82e7>                   | X86_FEATURE_RSB_CTXSW                                               | !X86_FEATURE_ALWAYS
>   82e7:  __switch_to_asm+0x27	 | jmp    0x8312 <__switch_to_asm+0x52> | mov    $0x10,%r12						      | nop1
>   82e8:  __switch_to_asm+0x28	 |                                      | 	 							      | nop1
>   82e9:  __switch_to_asm+0x29	 | nop*41                               |                                                                     | callq  0x82ef <__switch_to_asm+0x2f>
>   82ee:  __switch_to_asm+0x2e	 |                                      | callq  0x82f4 <__switch_to_asm+0x34>                                | int3
>   82ef:  __switch_to_asm+0x2f	 |                                      |                                                                     | add    $0x8,%rsp
>   82f3:  __switch_to_asm+0x33	 |                                      | int3                                                                | lfence
>   82f4:  __switch_to_asm+0x34	 |                                      | callq  0x82fa <__switch_to_asm+0x3a>                                |
>   82f9:  __switch_to_asm+0x39	 |                                      | int3                                                                |
>   82fa:  __switch_to_asm+0x3a	 |                                      | add    $0x10,%rsp                                                   |
>   82fe:  __switch_to_asm+0x3e	 |                                      | dec    %r12                                                         |
>   8301:  __switch_to_asm+0x41	 |                                      | jne    0x82ee <__switch_to_asm+0x2e>                                |
>   8303:  __switch_to_asm+0x43	 |                                      | lfence                                                              |
>   8306:  __switch_to_asm+0x46	 |                                      | movq   $0xffffffffffffffff,%gs:0x0(%rip) # 0x20b <__x86_call_depth> |
>   8312:  __switch_to_asm+0x52	   pop    %r15                                               
>   8314:  __switch_to_asm+0x54	   pop    %r14                                               
>   8316:  __switch_to_asm+0x56	   pop    %r13                                               
>   8318:  __switch_to_asm+0x58	   pop    %r12                                               
>   831a:  __switch_to_asm+0x5a	   pop    %rbx                                               
>   831b:  __switch_to_asm+0x5b	   pop    %rbp                                               
>   831c:  __switch_to_asm+0x5c	   jmpq   0x8321 <__switch_to>
>   
> -----
> 
> Alexandre Chartre (30):
>   objtool: Move disassembly functions to a separated file
>   objtool: Create disassembly context
>   objtool: Disassemble code with libopcodes instead of running objdump
>   tool build: Remove annoying newline in build output
>   objtool: Print symbol during disassembly
>   objtool: Store instruction disassembly result
>   objtool: Disassemble instruction on warning or backtrace
>   objtool: Extract code to validate instruction from the validate branch
>     loop
>   objtool: Record symbol name max length
>   objtool: Add option to trace function validation
>   objtool: Trace instruction state changes during function validation
>   objtool: Improve register reporting during function validation
>   objtool: Identify the different types of alternatives
>   objtool: Add functions to better name alternatives
>   objtool: Improve tracing of alternative instructions
>   objtool: Do not validate IBT for .return_sites and .call_sites
>   objtool: Add the --disas=<function-pattern> action
>   objtool: Preserve alternatives order
>   objtool: Print headers for alternatives
>   objtool: Disassemble group alternatives
>   objtool: Print addresses with alternative instructions
>   objtool: Disassemble exception table alternatives
>   objtool: Disassemble jump table alternatives
>   objtool: Fix address references in alternatives
>   objtool: Provide access to feature and flags of group alternatives
>   objtool: Function to get the name of a CPU feature
>   objtool: Improve naming of group alternatives
>   objtool: Compact output for alternatives with one instruction
>   objtool: Add wide output for disassembly
>   objtool: Trim trailing NOPs in alternative
> 
>  .../x86/tools/gen-cpu-feature-names-x86.awk   |   33 +
>  tools/build/Makefile.feature                  |    4 +-
>  tools/objtool/.gitignore                      |    3 +
>  tools/objtool/Build                           |    3 +
>  tools/objtool/Makefile                        |   26 +
>  tools/objtool/arch/loongarch/decode.c         |   23 +
>  tools/objtool/arch/loongarch/special.c        |    5 +
>  tools/objtool/arch/powerpc/decode.c           |   24 +
>  tools/objtool/arch/powerpc/special.c          |    5 +
>  tools/objtool/arch/x86/Build                  |    8 +
>  tools/objtool/arch/x86/decode.c               |   20 +
>  tools/objtool/arch/x86/special.c              |   10 +
>  tools/objtool/builtin-check.c                 |    4 +
>  tools/objtool/check.c                         |  648 +++++----
>  tools/objtool/disas.c                         | 1245 +++++++++++++++++
>  tools/objtool/include/objtool/arch.h          |   11 +
>  tools/objtool/include/objtool/builtin.h       |    3 +
>  tools/objtool/include/objtool/check.h         |   35 +-
>  tools/objtool/include/objtool/disas.h         |   81 ++
>  tools/objtool/include/objtool/special.h       |    4 +-
>  tools/objtool/include/objtool/trace.h         |  141 ++
>  tools/objtool/include/objtool/warn.h          |   17 +-
>  tools/objtool/special.c                       |    2 +
>  tools/objtool/trace.c                         |  203 +++
>  24 files changed, 2259 insertions(+), 299 deletions(-)
>  create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
>  create mode 100644 tools/objtool/disas.c
>  create mode 100644 tools/objtool/include/objtool/disas.h
>  create mode 100644 tools/objtool/include/objtool/trace.h
>  create mode 100644 tools/objtool/trace.c
> 
> -- 
> 2.43.5
> 
> 
> 

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-24 11:04 ` Borislav Petkov
@ 2025-11-24 11:39   ` Alexandre Chartre
  2025-11-24 12:39     ` Borislav Petkov
  2025-11-24 12:27   ` David Laight
  1 sibling, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-24 11:39 UTC (permalink / raw)
  To: Borislav Petkov
  Cc: alexandre.chartre, linux-kernel, mingo, jpoimboe, peterz,
	david.laight.linux


On 11/24/25 12:04, Borislav Petkov wrote:
> On Fri, Nov 21, 2025 at 10:53:10AM +0100, Alexandre Chartre wrote:
>> Notes:
>> ======
> 
> So those examples below look very useful and cool. Can you pls document them
> so that I can find the breadcrumbs next time and use them?
> 
> :-)
> 
> Thx.

Sure. I will add a doc in tools/objtool/Documentation/ in followup patches.

Rgds,

alex.

> 
>> Disassembly can show default alternative jumping to .altinstr_aux
>> -----------------------------------------------------------------
>> Disassembly can show a default alternative jumping to .altinstr_aux. This
>> happens when the _static_cpu_has() function is used. Its default code
>> jumps to .altinstr_aux where a test sequence is executed (test; jnz; jmp).
>>
>> At runtime, this sequence is not used because the _static_cpu_has()
>> an alternative with the X86_FEATURE_ALWAYS feature.
>>
>>
>>    debc:  perf_get_x86_pmu_capability+0xc      jmpq   0xdec1 <.altinstr_aux+0xfc> | NOP5  (X86_FEATURE_HYBRID_CPU) | jmpq   0x61a <perf_get_x86_pmu_capability+0x37>  (X86_FEATURE_ALWAYS)   # <alternative.debc>
>>    dec1:  perf_get_x86_pmu_capability+0x11     ud2
>>
>>
>> Disassembly can show alternative jumping to the next instruction
>> ----------------------------------------------------------------
>>
>> The disassembly can show jump tables with an alternative which jumps
>> to the next instruction.
>>
>> For example:
>>
>> def9:  perf_get_x86_pmu_capability+0x49    NOP2 | jmp    defb <perf_get_x86_pmu_capability+0x4b>  (JUMP)   # <alternative.def9>
>> defb:  perf_get_x86_pmu_capability+0x4b	   mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>
>>
>> This disassembly is correct. These instructions come from:
>>
>>          cap->num_counters_gp = x86_pmu_num_counters(NULL)):
>>
>> which will end up executing this statement:
>>
>>          if (static_branch_unlikely(&perf_is_hybrid) && NULL)
>> 	        <do something>;
>>
>> This statement is optimized to do nothing because the condition is
>> always false. But static_branch_unlikely() is implemented with a jump
>> table inside an "asm goto" statement, and "asm goto" statements are
>> not optimized.
>>
>> So basically the code is optimized like this:
>>
>>          if (static_branch_unlikely(&perf_is_hybrid))
>> 	        ;
>>
>> And this translates to the assembly code above.
>>
>>
>> Examples:
>> =========
>>
>> Example 1 (--trace option): Trace the validation of the os_save() function
>> --------------------------------------------------------------------------
>>
>> $ ./tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --orc --retpoline --rethunk --sls --static-call --uaccess --prefix=16 --link --trace os_xsave -v vmlinux.o
>> os_xsave: validation begin
>>   59be0:  os_xsave+0x0                  push   %r12                                          - state: cfa=rsp+16 r12=(cfa-16) stack_size=16
>>   59be2:  os_xsave+0x2		       mov    0x0(%rip),%eax        # 0x59be8 <alternatives_patched>
>>   59be8:  os_xsave+0x8		       push   %rbp                                          - state: cfa=rsp+24 rbp=(cfa-24) stack_size=24
>>   59be9:  os_xsave+0x9		       mov    %rdi,%rbp
>>   59bec:  os_xsave+0xc		       push   %rbx					     - state: cfa=rsp+32 rbx=(cfa-32) stack_size=32
>>   59bed:  os_xsave+0xd		       mov    0x8(%rdi),%rbx
>>   59bf1:  os_xsave+0x11		       mov    %rbx,%r12
>>   59bf4:  os_xsave+0x14		       shr    $0x20,%r12
>>   59bf8:  os_xsave+0x18		       test   %eax,%eax
>>   59bfa:  os_xsave+0x1a		       je     0x59c22 <os_xsave+0x42>                        - jump taken
>>   59c22:  os_xsave+0x42		       | ud2
>>   59c24:  os_xsave+0x44		       | jmp    0x59bfc <os_xsave+0x1c>                      - unconditional jump
>>   59bfc:  os_xsave+0x1c		       | | xor    %edx,%edx
>>   59bfe:  os_xsave+0x1e		       | | mov    %rbx,%rsi
>>   59c01:  os_xsave+0x21		       | | mov    %rbp,%rdi
>>   59c04:  os_xsave+0x24		       | | callq  0x59c09 <xfd_validate_state>               - call
>>   59c09:  os_xsave+0x29		       | | mov    %ebx,%eax
>>   59c0b:  os_xsave+0x2b		       | | mov    %r12d,%edx
>>   	 			       | | / <alternative.59c0e> X86_FEATURE_XSAVEOPT
>>    1b29:  .altinstr_replacement+0x1b29  | | | xsaveopt64 0x40(%rbp)
>>   59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx
>>   59c15:  os_xsave+0x35		       | | | test   %ebx,%ebx
>>   59c17:  os_xsave+0x37		       | | | jne    0x59c26 <os_xsave+0x46>                  - jump taken
>>   59c26:  os_xsave+0x46		       | | | | ud2
>>   59c28:  os_xsave+0x48		       | | | | pop    %rbx                                   - state: cfa=rsp+24 rbx=<undef> stack_size=24
>>   59c29:  os_xsave+0x49		       | | | | pop    %rbp				     - state: cfa=rsp+16 rbp=<undef> stack_size=16
>>   59c2a:  os_xsave+0x4a		       | | | | pop    %r12				     - state: cfa=rsp+8 r12=<undef> stack_size=8
>>   59c2c:  os_xsave+0x4c		       | | | | jmpq   0x59c31 <__x86_return_thunk>	     - return
>>   59c17:  os_xsave+0x37		       | | | jne    0x59c26 <os_xsave+0x46>		     - jump not taken
>>   59c19:  os_xsave+0x39		       | | | pop    %rbx    				     - state: cfa=rsp+24 rbx=<undef> stack_size=24
>>   59c1a:  os_xsave+0x3a		       | | | pop    %rbp				     - state: cfa=rsp+16 rbp=<undef> stack_size=16
>>   59c1b:  os_xsave+0x3b		       | | | pop    %r12				     - state: cfa=rsp+8 r12=<undef> stack_size=8
>>   59c1d:  os_xsave+0x3d		       | | | jmpq   0x59c22 <__x86_return_thunk>	     - return
>>   	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVEOPT
>> 				       | | / <alternative.59c0e> X86_FEATURE_XSAVEC
>>    1b2e:  .altinstr_replacement+0x1b2e  | | | xsavec64 0x40(%rbp)
>>   59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                - already visited
>>   	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVEC
>> 				       | | / <alternative.59c0e> X86_FEATURE_XSAVES
>>    1b33:  .altinstr_replacement+0x1b33  | | | xsaves64 0x40(%rbp)
>>   59c13:  os_xsave+0x33		       | | | xor    %ebx,%ebx                                - already visited
>>   	 			       | | \ <alternative.59c0e> X86_FEATURE_XSAVES
>> 				       | | / <alternative.59c0e> EXCEPTION for instruction at 0x59c0e <os_xsave+0x2e>
>>   59c15:  os_xsave+0x35		       | | | test   %ebx,%ebx                                - already visited
>>   	 			       | | \ <alternative.59c0e> EXCEPTION
>> 				       | | / <alternative.59c0e> DEFAULT
>>   59c0e:  os_xsave+0x2e		       | | xsave64 0x40(%rbp)
>>   59c13:  os_xsave+0x33		       | | xor    %ebx,%ebx                                  - already visited
>>   59bfa:  os_xsave+0x1a		       je     0x59c22 <os_xsave+0x42>                        - jump not taken
>>   59bfc:  os_xsave+0x1c		       xor    %edx,%edx                                      - already visited
>> os_xsave: validation end
>>
>>
>> Example 2 (--disas option): Single Instruction Alternatives
>> -----------------------------------------------------------
>>
>> Compact Output (default):
>>
>> Alternatives with single instructions are displayed each on one line,
>> with the instruction and a description of the alternative.
>>
>> $ ./tools/objtool/objtool --disas=perf_get_x86_pmu_capability --link vmlinux.o
>> perf_get_x86_pmu_capability:
>>    deb0:  perf_get_x86_pmu_capability+0x0     endbr64
>>    deb4:  perf_get_x86_pmu_capability+0x4     callq  0xdeb9 <__fentry__>
>>    deb9:  perf_get_x86_pmu_capability+0x9     mov    %rdi,%rdx
>>    debc:  perf_get_x86_pmu_capability+0xc     <alternative.debc>
>>    	 				     = jmpq   0xdec1 <.altinstr_aux+0xfc>                 (if DEFAULT)
>> 					     = jmpq   0x622 <perf_get_x86_pmu_capability+0x37>    (if X86_FEATURE_ALWAYS)
>> 					     = nop5                                               (if X86_FEATURE_HYBRID_CPU)
>>    dec1:  perf_get_x86_pmu_capability+0x11    ud2
>>    dec3:  perf_get_x86_pmu_capability+0x13    movq   $0x0,(%rdx)
>>    deca:  perf_get_x86_pmu_capability+0x1a    movq   $0x0,0x8(%rdx)
>>    ded2:  perf_get_x86_pmu_capability+0x22    movq   $0x0,0x10(%rdx)
>>    deda:  perf_get_x86_pmu_capability+0x2a    movq   $0x0,0x18(%rdx)
>>    dee2:  perf_get_x86_pmu_capability+0x32    jmpq   0xdee7 <__x86_return_thunk>
>>    dee7:  perf_get_x86_pmu_capability+0x37    cmpq   $0x0,0x0(%rip)        # 0xdeef <x86_pmu+0x10>
>>    deef:  perf_get_x86_pmu_capability+0x3f    je     0xdec3 <perf_get_x86_pmu_capability+0x13>
>>    def1:  perf_get_x86_pmu_capability+0x41    mov    0x0(%rip),%eax        # 0xdef7 <x86_pmu+0x8>
>>    def7:  perf_get_x86_pmu_capability+0x47    mov    %eax,(%rdi)
>>    def9:  perf_get_x86_pmu_capability+0x49    <jump_table.def9>
>>    	 				     = nop2                                              (if DEFAULT)
>> 					     = jmp    defb <perf_get_x86_pmu_capability+0x4b>    (if JUMP)
>>    defb:  perf_get_x86_pmu_capability+0x4b    mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>
>>    df02:  perf_get_x86_pmu_capability+0x52    <alternative.df02>
>>    	 				     = callq  0xdf07 <__sw_hweight64>    (if DEFAULT)
>> 					     = popcnt %rdi,%rax                  (if X86_FEATURE_POPCNT)
>>    df07:  perf_get_x86_pmu_capability+0x57    mov    %eax,0x4(%rdx)
>>    df0a:  perf_get_x86_pmu_capability+0x5a    <jump_table.df0a>
>>    	 				     = nop2                                              (if DEFAULT)
>> 					     = jmp    df0c <perf_get_x86_pmu_capability+0x5c>    (if JUMP)
>>    df0c:  perf_get_x86_pmu_capability+0x5c    mov    0x0(%rip),%rdi        # 0xdf13 <x86_pmu+0xe0>
>>    df13:  perf_get_x86_pmu_capability+0x63    <alternative.df13>
>>    	 				     = callq  0xdf18 <__sw_hweight64>    (if DEFAULT)
>> 					     = popcnt %rdi,%rax                  (if X86_FEATURE_POPCNT)
>>    df18:  perf_get_x86_pmu_capability+0x68    mov    %eax,0x8(%rdx)
>>    df1b:  perf_get_x86_pmu_capability+0x6b    mov    0x0(%rip),%eax        # 0xdf21 <x86_pmu+0xf8>
>>    df21:  perf_get_x86_pmu_capability+0x71    mov    %eax,0xc(%rdx)
>>    df24:  perf_get_x86_pmu_capability+0x74    mov    %eax,0x10(%rdx)
>>    df27:  perf_get_x86_pmu_capability+0x77    mov    0x0(%rip),%rax        # 0xdf2e <x86_pmu+0x108>
>>    df2e:  perf_get_x86_pmu_capability+0x7e    mov    %eax,0x14(%rdx)
>>    df31:  perf_get_x86_pmu_capability+0x81    mov    0x0(%rip),%eax        # 0xdf37 <x86_pmu+0x110>
>>    df37:  perf_get_x86_pmu_capability+0x87    mov    %eax,0x18(%rdx)
>>    df3a:  perf_get_x86_pmu_capability+0x8a    movzbl 0x0(%rip),%ecx        # 0xdf41 <x86_pmu+0x1d1>
>>    df41:  perf_get_x86_pmu_capability+0x91    movzbl 0x1c(%rdx),%eax
>>    df45:  perf_get_x86_pmu_capability+0x95    shr    %cl
>>    df47:  perf_get_x86_pmu_capability+0x97    and    $0x1,%ecx
>>    df4a:  perf_get_x86_pmu_capability+0x9a    and    $0xfffffffe,%eax
>>    df4d:  perf_get_x86_pmu_capability+0x9d    or     %ecx,%eax
>>    df4f:  perf_get_x86_pmu_capability+0x9f    mov    %al,0x1c(%rdx)
>>    df52:  perf_get_x86_pmu_capability+0xa2    jmpq   0xdf57 <__x86_return_thunk>
>>
>>
>> Wide Output (--wide option):
>>
>> Alternatives with single instructions are displayed side-by-side,
>> with an header.
>>
>> $ ./tools/objtool/objtool --disas=perf_get_x86_pmu_capability --wide --link vmlinux.o
>> perf_get_x86_pmu_capability:
>>    deb0:  perf_get_x86_pmu_capability+0x0       endbr64
>>    deb4:  perf_get_x86_pmu_capability+0x4       callq  0xdeb9 <__fentry__>
>>    deb9:  perf_get_x86_pmu_capability+0x9       mov    %rdi,%rdx
>>    debc:  perf_get_x86_pmu_capability+0xc     | <alternative.debc>                 | X86_FEATURE_ALWAYS                              | X86_FEATURE_HYBRID_CPU
>>    debc:  perf_get_x86_pmu_capability+0xc     | jmpq   0xdec1 <.altinstr_aux+0xfc> | jmpq   0x622 <perf_get_x86_pmu_capability+0x37> | nop5
>>    dec1:  perf_get_x86_pmu_capability+0x11      ud2
>>    dec3:  perf_get_x86_pmu_capability+0x13      movq   $0x0,(%rdx)
>>    deca:  perf_get_x86_pmu_capability+0x1a      movq   $0x0,0x8(%rdx)
>>    ded2:  perf_get_x86_pmu_capability+0x22      movq   $0x0,0x10(%rdx)
>>    deda:  perf_get_x86_pmu_capability+0x2a      movq   $0x0,0x18(%rdx)
>>    dee2:  perf_get_x86_pmu_capability+0x32      jmpq   0xdee7 <__x86_return_thunk>
>>    dee7:  perf_get_x86_pmu_capability+0x37      cmpq   $0x0,0x0(%rip)        # 0xdeef <x86_pmu+0x10>
>>    deef:  perf_get_x86_pmu_capability+0x3f      je     0xdec3 <perf_get_x86_pmu_capability+0x13>
>>    def1:  perf_get_x86_pmu_capability+0x41      mov    0x0(%rip),%eax        # 0xdef7 <x86_pmu+0x8>
>>    def7:  perf_get_x86_pmu_capability+0x47      mov    %eax,(%rdi)
>>    def9:  perf_get_x86_pmu_capability+0x49    | <jump_table.def9> | JUMP
>>    def9:  perf_get_x86_pmu_capability+0x49    | nop2              | jmp    defb <perf_get_x86_pmu_capability+0x4b>
>>    defb:  perf_get_x86_pmu_capability+0x4b      mov    0x0(%rip),%rdi        # 0xdf02 <x86_pmu+0xd8>
>>    df02:  perf_get_x86_pmu_capability+0x52    | <alternative.df02>             | X86_FEATURE_POPCNT
>>    df02:  perf_get_x86_pmu_capability+0x52    | callq  0xdf07 <__sw_hweight64> | popcnt %rdi,%rax
>>    df07:  perf_get_x86_pmu_capability+0x57      mov    %eax,0x4(%rdx)
>>    df0a:  perf_get_x86_pmu_capability+0x5a    | <jump_table.df0a> | JUMP
>>    df0a:  perf_get_x86_pmu_capability+0x5a    | nop2              | jmp    df0c <perf_get_x86_pmu_capability+0x5c>
>>    df0c:  perf_get_x86_pmu_capability+0x5c      mov    0x0(%rip),%rdi        # 0xdf13 <x86_pmu+0xe0>
>>    df13:  perf_get_x86_pmu_capability+0x63    | <alternative.df13>             | X86_FEATURE_POPCNT
>>    df13:  perf_get_x86_pmu_capability+0x63    | callq  0xdf18 <__sw_hweight64> | popcnt %rdi,%rax
>>    df18:  perf_get_x86_pmu_capability+0x68      mov    %eax,0x8(%rdx)
>>    df1b:  perf_get_x86_pmu_capability+0x6b      mov    0x0(%rip),%eax        # 0xdf21 <x86_pmu+0xf8>
>>    df21:  perf_get_x86_pmu_capability+0x71      mov    %eax,0xc(%rdx)
>>    df24:  perf_get_x86_pmu_capability+0x74      mov    %eax,0x10(%rdx)
>>    df27:  perf_get_x86_pmu_capability+0x77      mov    0x0(%rip),%rax        # 0xdf2e <x86_pmu+0x108>
>>    df2e:  perf_get_x86_pmu_capability+0x7e      mov    %eax,0x14(%rdx)
>>    df31:  perf_get_x86_pmu_capability+0x81      mov    0x0(%rip),%eax        # 0xdf37 <x86_pmu+0x110>
>>    df37:  perf_get_x86_pmu_capability+0x87      mov    %eax,0x18(%rdx)
>>    df3a:  perf_get_x86_pmu_capability+0x8a      movzbl 0x0(%rip),%ecx        # 0xdf41 <x86_pmu+0x1d1>
>>    df41:  perf_get_x86_pmu_capability+0x91      movzbl 0x1c(%rdx),%eax
>>    df45:  perf_get_x86_pmu_capability+0x95      shr    %cl
>>    df47:  perf_get_x86_pmu_capability+0x97      and    $0x1,%ecx
>>    df4a:  perf_get_x86_pmu_capability+0x9a      and    $0xfffffffe,%eax
>>    df4d:  perf_get_x86_pmu_capability+0x9d      or     %ecx,%eax
>>    df4f:  perf_get_x86_pmu_capability+0x9f      mov    %al,0x1c(%rdx)
>>    df52:  perf_get_x86_pmu_capability+0xa2      jmpq   0xdf57 <__x86_return_thunk>
>>
>>
>> Example 3 (--disas option): Alternatives with multiple instructions
>> -------------------------------------------------------------------
>>
>> Compact Output (default):
>>
>> Alternatives with multiple instructions are displayed one above the other,
>> with an header describing the alternative.
>>
>> $ ./tools/objtool/objtool --disas=__switch_to_asm --link vmlinux.o
>> __switch_to_asm:
>>    82c0:  __switch_to_asm+0x0       push   %rbp
>>    82c1:  __switch_to_asm+0x1	   push   %rbx
>>    82c2:  __switch_to_asm+0x2	   push   %r12
>>    82c4:  __switch_to_asm+0x4	   push   %r13
>>    82c6:  __switch_to_asm+0x6	   push   %r14
>>    82c8:  __switch_to_asm+0x8	   push   %r15
>>    82ca:  __switch_to_asm+0xa	   mov    %rsp,0x1670(%rdi)
>>    82d1:  __switch_to_asm+0x11	   mov    0x1670(%rsi),%rsp
>>    82d8:  __switch_to_asm+0x18	   mov    0xad8(%rsi),%rbx
>>    82df:  __switch_to_asm+0x1f	   mov    %rbx,%gs:0x0(%rip)        # 0x82e7 <__stack_chk_guard>
>>    82e7:  __switch_to_asm+0x27	   <alternative.82e7>
>>    	 			   = DEFAULT
>>    82e7:  __switch_to_asm+0x27	   | jmp    0x8312 <__switch_to_asm+0x52>
>>    82e9:  __switch_to_asm+0x29	   | nop*41
>>    	 			   |
>> 				   = X86_FEATURE_RSB_CTXSW
>>    82e7:  __switch_to_asm+0x27	   | mov    $0x10,%r12
>>    82ee:  __switch_to_asm+0x2e	   | callq  0x82f4 <__switch_to_asm+0x34>
>>    82f3:  __switch_to_asm+0x33	   | int3
>>    82f4:  __switch_to_asm+0x34	   | callq  0x82fa <__switch_to_asm+0x3a>
>>    82f9:  __switch_to_asm+0x39	   | int3
>>    82fa:  __switch_to_asm+0x3a	   | add    $0x10,%rsp
>>    82fe:  __switch_to_asm+0x3e	   | dec    %r12
>>    8301:  __switch_to_asm+0x41	   | jne    0x82ee <__switch_to_asm+0x2e>
>>    8303:  __switch_to_asm+0x43	   | lfence
>>    8306:  __switch_to_asm+0x46	   | movq   $0xffffffffffffffff,%gs:0x0(%rip)        # 0x20b <__x86_call_depth>
>>    	 			   |
>> 				   = !X86_FEATURE_ALWAYS
>>    82e7:  __switch_to_asm+0x27	   | nop1
>>    82e8:  __switch_to_asm+0x28	   | nop1
>>    82e9:  __switch_to_asm+0x29	   | callq  0x82ef <__switch_to_asm+0x2f>
>>    82ee:  __switch_to_asm+0x2e	   | int3
>>    82ef:  __switch_to_asm+0x2f	   | add    $0x8,%rsp
>>    82f3:  __switch_to_asm+0x33	   | lfence
>>    	 			   |
>>    8312:  __switch_to_asm+0x52	   pop    %r15
>>    8314:  __switch_to_asm+0x54	   pop    %r14
>>    8316:  __switch_to_asm+0x56	   pop    %r13
>>    8318:  __switch_to_asm+0x58	   pop    %r12
>>    831a:  __switch_to_asm+0x5a	   pop    %rbx
>>    831b:  __switch_to_asm+0x5b	   pop    %rbp
>>    831c:  __switch_to_asm+0x5c	   jmpq   0x8321 <__switch_to>
>>
>>
>> Wide Output (--wide option):
>>
>> Alternatives with multiple instructions are displayed side-by-side, with
>> an header describing the alternative. The code in the first column is the
>> default code of the alternative.
>>
>> $ ./tools/objtool/objtool --disas=__switch_to_asm --wide  --link vmlinux.o
>> __switch_to_asm:
>>    82c0:  __switch_to_asm+0x0       push   %rbp
>>    82c1:  __switch_to_asm+0x1	   push   %rbx
>>    82c2:  __switch_to_asm+0x2	   push   %r12
>>    82c4:  __switch_to_asm+0x4	   push   %r13
>>    82c6:  __switch_to_asm+0x6	   push   %r14
>>    82c8:  __switch_to_asm+0x8	   push   %r15
>>    82ca:  __switch_to_asm+0xa	   mov    %rsp,0x1670(%rdi)
>>    82d1:  __switch_to_asm+0x11	   mov    0x1670(%rsi),%rsp
>>    82d8:  __switch_to_asm+0x18	   mov    0xad8(%rsi),%rbx
>>    82df:  __switch_to_asm+0x1f	   mov    %rbx,%gs:0x0(%rip)        # 0x82e7 <__stack_chk_guard>
>>    82e7:  __switch_to_asm+0x27	 | <alternative.82e7>                   | X86_FEATURE_RSB_CTXSW                                               | !X86_FEATURE_ALWAYS
>>    82e7:  __switch_to_asm+0x27	 | jmp    0x8312 <__switch_to_asm+0x52> | mov    $0x10,%r12						      | nop1
>>    82e8:  __switch_to_asm+0x28	 |                                      | 	 							      | nop1
>>    82e9:  __switch_to_asm+0x29	 | nop*41                               |                                                                     | callq  0x82ef <__switch_to_asm+0x2f>
>>    82ee:  __switch_to_asm+0x2e	 |                                      | callq  0x82f4 <__switch_to_asm+0x34>                                | int3
>>    82ef:  __switch_to_asm+0x2f	 |                                      |                                                                     | add    $0x8,%rsp
>>    82f3:  __switch_to_asm+0x33	 |                                      | int3                                                                | lfence
>>    82f4:  __switch_to_asm+0x34	 |                                      | callq  0x82fa <__switch_to_asm+0x3a>                                |
>>    82f9:  __switch_to_asm+0x39	 |                                      | int3                                                                |
>>    82fa:  __switch_to_asm+0x3a	 |                                      | add    $0x10,%rsp                                                   |
>>    82fe:  __switch_to_asm+0x3e	 |                                      | dec    %r12                                                         |
>>    8301:  __switch_to_asm+0x41	 |                                      | jne    0x82ee <__switch_to_asm+0x2e>                                |
>>    8303:  __switch_to_asm+0x43	 |                                      | lfence                                                              |
>>    8306:  __switch_to_asm+0x46	 |                                      | movq   $0xffffffffffffffff,%gs:0x0(%rip) # 0x20b <__x86_call_depth> |
>>    8312:  __switch_to_asm+0x52	   pop    %r15
>>    8314:  __switch_to_asm+0x54	   pop    %r14
>>    8316:  __switch_to_asm+0x56	   pop    %r13
>>    8318:  __switch_to_asm+0x58	   pop    %r12
>>    831a:  __switch_to_asm+0x5a	   pop    %rbx
>>    831b:  __switch_to_asm+0x5b	   pop    %rbp
>>    831c:  __switch_to_asm+0x5c	   jmpq   0x8321 <__switch_to>
>>    
>> -----
>>
>> Alexandre Chartre (30):
>>    objtool: Move disassembly functions to a separated file
>>    objtool: Create disassembly context
>>    objtool: Disassemble code with libopcodes instead of running objdump
>>    tool build: Remove annoying newline in build output
>>    objtool: Print symbol during disassembly
>>    objtool: Store instruction disassembly result
>>    objtool: Disassemble instruction on warning or backtrace
>>    objtool: Extract code to validate instruction from the validate branch
>>      loop
>>    objtool: Record symbol name max length
>>    objtool: Add option to trace function validation
>>    objtool: Trace instruction state changes during function validation
>>    objtool: Improve register reporting during function validation
>>    objtool: Identify the different types of alternatives
>>    objtool: Add functions to better name alternatives
>>    objtool: Improve tracing of alternative instructions
>>    objtool: Do not validate IBT for .return_sites and .call_sites
>>    objtool: Add the --disas=<function-pattern> action
>>    objtool: Preserve alternatives order
>>    objtool: Print headers for alternatives
>>    objtool: Disassemble group alternatives
>>    objtool: Print addresses with alternative instructions
>>    objtool: Disassemble exception table alternatives
>>    objtool: Disassemble jump table alternatives
>>    objtool: Fix address references in alternatives
>>    objtool: Provide access to feature and flags of group alternatives
>>    objtool: Function to get the name of a CPU feature
>>    objtool: Improve naming of group alternatives
>>    objtool: Compact output for alternatives with one instruction
>>    objtool: Add wide output for disassembly
>>    objtool: Trim trailing NOPs in alternative
>>
>>   .../x86/tools/gen-cpu-feature-names-x86.awk   |   33 +
>>   tools/build/Makefile.feature                  |    4 +-
>>   tools/objtool/.gitignore                      |    3 +
>>   tools/objtool/Build                           |    3 +
>>   tools/objtool/Makefile                        |   26 +
>>   tools/objtool/arch/loongarch/decode.c         |   23 +
>>   tools/objtool/arch/loongarch/special.c        |    5 +
>>   tools/objtool/arch/powerpc/decode.c           |   24 +
>>   tools/objtool/arch/powerpc/special.c          |    5 +
>>   tools/objtool/arch/x86/Build                  |    8 +
>>   tools/objtool/arch/x86/decode.c               |   20 +
>>   tools/objtool/arch/x86/special.c              |   10 +
>>   tools/objtool/builtin-check.c                 |    4 +
>>   tools/objtool/check.c                         |  648 +++++----
>>   tools/objtool/disas.c                         | 1245 +++++++++++++++++
>>   tools/objtool/include/objtool/arch.h          |   11 +
>>   tools/objtool/include/objtool/builtin.h       |    3 +
>>   tools/objtool/include/objtool/check.h         |   35 +-
>>   tools/objtool/include/objtool/disas.h         |   81 ++
>>   tools/objtool/include/objtool/special.h       |    4 +-
>>   tools/objtool/include/objtool/trace.h         |  141 ++
>>   tools/objtool/include/objtool/warn.h          |   17 +-
>>   tools/objtool/special.c                       |    2 +
>>   tools/objtool/trace.c                         |  203 +++
>>   24 files changed, 2259 insertions(+), 299 deletions(-)
>>   create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
>>   create mode 100644 tools/objtool/disas.c
>>   create mode 100644 tools/objtool/include/objtool/disas.h
>>   create mode 100644 tools/objtool/include/objtool/trace.h
>>   create mode 100644 tools/objtool/trace.c
>>
>> -- 
>> 2.43.5
>>
>>
>>
> 


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
@ 2025-11-24 11:59     ` Peter Zijlstra
  2025-11-24 12:26       ` Borislav Petkov
  0 siblings, 1 reply; 110+ messages in thread
From: Peter Zijlstra @ 2025-11-24 11:59 UTC (permalink / raw)
  To: linux-kernel; +Cc: linux-tip-commits, Alexandre Chartre, Josh Poimboeuf, x86

On Mon, Nov 24, 2025 at 10:44:01AM -0000, tip-bot2 for Alexandre Chartre wrote:
> The following commit has been merged into the objtool/core branch of tip:
> 
> Commit-ID:     afff4e5820e9a0d609740a83c366f3f0335db342
> Gitweb:        https://git.kernel.org/tip/afff4e5820e9a0d609740a83c366f3f0335db342
> Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
> AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
> Committer:     Peter Zijlstra <peterz@infradead.org>
> CommitterDate: Mon, 24 Nov 2025 11:35:06 +01:00
> 
> objtool: Function to get the name of a CPU feature
> 
> Add a function to get the name of a CPU feature. The function is
> architecture dependent and currently only implemented for x86. The
> feature names are automatically generated from the cpufeatures.h
> include file.
> 
> 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-27-alexandre.chartre@oracle.com
> ---
>  tools/arch/x86/tools/gen-cpu-feature-names-x86.awk | 33 +++++++++++++-
>  tools/objtool/.gitignore                           |  1 +-
>  tools/objtool/Makefile                             |  1 +-
>  tools/objtool/arch/loongarch/special.c             |  5 ++-
>  tools/objtool/arch/powerpc/special.c               |  5 ++-
>  tools/objtool/arch/x86/Build                       | 13 ++++-
>  tools/objtool/arch/x86/special.c                   | 10 ++++-
>  tools/objtool/include/objtool/special.h            |  2 +-
>  8 files changed, 69 insertions(+), 1 deletion(-)
>  create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
> 
> diff --git a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
> new file mode 100644
> index 0000000..1b1c1d8
> --- /dev/null
> +++ b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
> @@ -0,0 +1,33 @@
> +#!/bin/awk -f
> +# SPDX-License-Identifier: GPL-2.0
> +#
> +# Copyright (c) 2025, Oracle and/or its affiliates.
> +#
> +# Usage: awk -f gen-cpu-feature-names-x86.awk cpufeatures.h > cpu-feature-names.c
> +#
> +
> +BEGIN {
> +	print "/* cpu feature name array generated from cpufeatures.h */"
> +	print "/* Do not change this code. */"
> +	print
> +	print "static const char *cpu_feature_names[(NCAPINTS+NBUGINTS)*32] = {"
> +
> +	feature_expr = "(X86_FEATURE_[A-Z0-9_]+)\\s+\\(([0-9*+ ]+)\\)"
> +	debug_expr = "(X86_BUG_[A-Z0-9_]+)\\s+X86_BUG\\(([0-9*+ ]+)\\)"
> +}
> +
> +/^#define X86_FEATURE_/ {
> +	if (match($0, feature_expr, m)) {
> +		print "\t[" m[2] "] = \"" m[1] "\","
> +	}
> +}
> +
> +/^#define X86_BUG_/ {
> +	if (match($0, debug_expr, m)) {
> +		print "\t[NCAPINTS*32+(" m[2] ")] = \"" m[1] "\","
> +	}
> +}
> +
> +END {
> +	print "};"
> +}

Boris just reported that this doesn't work on mawk, since it uses a GNU
awk extension (3rd argument for match()).

Could you please look at writing this in strict POSIX awk?

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-24 11:59     ` Peter Zijlstra
@ 2025-11-24 12:26       ` Borislav Petkov
  2025-11-24 12:40         ` Alexandre Chartre
  2025-11-24 16:43         ` Alexandre Chartre
  0 siblings, 2 replies; 110+ messages in thread
From: Borislav Petkov @ 2025-11-24 12:26 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: linux-kernel, linux-tip-commits, Alexandre Chartre,
	Josh Poimboeuf, x86

On Mon, Nov 24, 2025 at 12:59:42PM +0100, Peter Zijlstra wrote:
> On Mon, Nov 24, 2025 at 10:44:01AM -0000, tip-bot2 for Alexandre Chartre wrote:
> > The following commit has been merged into the objtool/core branch of tip:
> > 
> > Commit-ID:     afff4e5820e9a0d609740a83c366f3f0335db342
> > Gitweb:        https://git.kernel.org/tip/afff4e5820e9a0d609740a83c366f3f0335db342
> > Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
> > AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
> > Committer:     Peter Zijlstra <peterz@infradead.org>
> > CommitterDate: Mon, 24 Nov 2025 11:35:06 +01:00
> > 
> > objtool: Function to get the name of a CPU feature

Also, this commit name needs a verb.

> Boris just reported that this doesn't work on mawk, since it uses a GNU
> awk extension (3rd argument for match()).
> 
> Could you please look at writing this in strict POSIX awk?

The fail is:

awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 20: syntax error at or near ,
awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 23: syntax error at or near }
awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 26: syntax error at or near ,
awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 29: syntax error at or near }
make[5]: *** [arch/x86/Build:21: /root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c] Error 2
make[5]: *** Deleting file '/root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c'
make[4]: *** [/root/kernel/linux/tools/build/Makefile.build:142: arch/x86] Error 2
make[4]: *** Waiting for unfinished jobs....
make[3]: *** [Makefile:104: /root/kernel/linux/tools/objtool/objtool-in.o] Error 2
make[2]: *** [Makefile:73: objtool] Error 2
make[1]: *** [/root/kernel/linux/Makefile:1449: tools/objtool] Error 2
make: *** [Makefile:248: __sub-make] Error 2

ls -al /usr/bin/awk 
lrwxrwxrwx 1 root root 21 Feb 19  2021 /usr/bin/awk -> /etc/alternatives/awk
ls -al /etc/alternatives/awk
lrwxrwxrwx 1 root root 13 Feb 19  2021 /etc/alternatives/awk -> /usr/bin/mawk

That's debian.

Thx.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-24 11:04 ` Borislav Petkov
  2025-11-24 11:39   ` Alexandre Chartre
@ 2025-11-24 12:27   ` David Laight
  1 sibling, 0 replies; 110+ messages in thread
From: David Laight @ 2025-11-24 12:27 UTC (permalink / raw)
  To: Borislav Petkov; +Cc: Alexandre Chartre, linux-kernel, mingo, jpoimboe, peterz

On Mon, 24 Nov 2025 12:04:25 +0100
Borislav Petkov <bp@alien8.de> wrote:

> On Fri, Nov 21, 2025 at 10:53:10AM +0100, Alexandre Chartre wrote:
> > Notes:
> > ======  
> 
> So those examples below look very useful and cool. Can you pls document them
> so that I can find the breadcrumbs next time and use them?
> 

Yes, it looks like the correct objdump 'spell' will be more useful
than 'objdump -dr[w]' for looking at object files.

	David

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 00/30] objtool: Function validation tracing
  2025-11-24 11:39   ` Alexandre Chartre
@ 2025-11-24 12:39     ` Borislav Petkov
  0 siblings, 0 replies; 110+ messages in thread
From: Borislav Petkov @ 2025-11-24 12:39 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux

On Mon, Nov 24, 2025 at 12:39:08PM +0100, Alexandre Chartre wrote:
> Sure. I will add a doc in tools/objtool/Documentation/ in followup patches.

Thanks, sounds good.

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-24 12:26       ` Borislav Petkov
@ 2025-11-24 12:40         ` Alexandre Chartre
  2025-11-24 19:05           ` david laight
  2025-11-24 16:43         ` Alexandre Chartre
  1 sibling, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-24 12:40 UTC (permalink / raw)
  To: Borislav Petkov, Peter Zijlstra
  Cc: alexandre.chartre, linux-kernel, linux-tip-commits,
	Josh Poimboeuf, x86



On 11/24/25 13:26, Borislav Petkov wrote:
> On Mon, Nov 24, 2025 at 12:59:42PM +0100, Peter Zijlstra wrote:
>> On Mon, Nov 24, 2025 at 10:44:01AM -0000, tip-bot2 for Alexandre Chartre wrote:
>>> The following commit has been merged into the objtool/core branch of tip:
>>>
>>> Commit-ID:     afff4e5820e9a0d609740a83c366f3f0335db342
>>> Gitweb:        https://git.kernel.org/tip/afff4e5820e9a0d609740a83c366f3f0335db342
>>> Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
>>> AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
>>> Committer:     Peter Zijlstra <peterz@infradead.org>
>>> CommitterDate: Mon, 24 Nov 2025 11:35:06 +01:00
>>>
>>> objtool: Function to get the name of a CPU feature
> 
> Also, this commit name needs a verb.
> 
>> Boris just reported that this doesn't work on mawk, since it uses a GNU
>> awk extension (3rd argument for match()).
>>
>> Could you please look at writing this in strict POSIX awk?
> 
> The fail is:
> 
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 20: syntax error at or near ,
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 23: syntax error at or near }
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 26: syntax error at or near ,
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 29: syntax error at or near }
> make[5]: *** [arch/x86/Build:21: /root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c] Error 2
> make[5]: *** Deleting file '/root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c'
> make[4]: *** [/root/kernel/linux/tools/build/Makefile.build:142: arch/x86] Error 2
> make[4]: *** Waiting for unfinished jobs....
> make[3]: *** [Makefile:104: /root/kernel/linux/tools/objtool/objtool-in.o] Error 2
> make[2]: *** [Makefile:73: objtool] Error 2
> make[1]: *** [/root/kernel/linux/Makefile:1449: tools/objtool] Error 2
> make: *** [Makefile:248: __sub-make] Error 2
> 
> ls -al /usr/bin/awk
> lrwxrwxrwx 1 root root 21 Feb 19  2021 /usr/bin/awk -> /etc/alternatives/awk
> ls -al /etc/alternatives/awk
> lrwxrwxrwx 1 root root 13 Feb 19  2021 /etc/alternatives/awk -> /usr/bin/mawk
> 
> That's debian.
> 

Ok. I am working on it.

alex.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-24 12:26       ` Borislav Petkov
  2025-11-24 12:40         ` Alexandre Chartre
@ 2025-11-24 16:43         ` Alexandre Chartre
  2025-11-24 20:14           ` Borislav Petkov
  1 sibling, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-24 16:43 UTC (permalink / raw)
  To: Borislav Petkov, Peter Zijlstra
  Cc: alexandre.chartre, linux-kernel, linux-tip-commits,
	Josh Poimboeuf, x86


On 11/24/25 13:26, Borislav Petkov wrote:
> On Mon, Nov 24, 2025 at 12:59:42PM +0100, Peter Zijlstra wrote:
>> On Mon, Nov 24, 2025 at 10:44:01AM -0000, tip-bot2 for Alexandre Chartre wrote:
>>> The following commit has been merged into the objtool/core branch of tip:
>>>
>>> Commit-ID:     afff4e5820e9a0d609740a83c366f3f0335db342
>>> Gitweb:        https://git.kernel.org/tip/afff4e5820e9a0d609740a83c366f3f0335db342
>>> Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
>>> AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
>>> Committer:     Peter Zijlstra <peterz@infradead.org>
>>> CommitterDate: Mon, 24 Nov 2025 11:35:06 +01:00
>>>
>>> objtool: Function to get the name of a CPU feature
> 
> Also, this commit name needs a verb.

Ok, then:

objtool: Add function to get the name of a CPU feature

>> Boris just reported that this doesn't work on mawk, since it uses a GNU
>> awk extension (3rd argument for match()).
>>
>> Could you please look at writing this in strict POSIX awk?
> 
> The fail is:
> 
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 20: syntax error at or near ,
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 23: syntax error at or near }
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 26: syntax error at or near ,
> awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 29: syntax error at or near }
> make[5]: *** [arch/x86/Build:21: /root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c] Error 2
> make[5]: *** Deleting file '/root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c'
> make[4]: *** [/root/kernel/linux/tools/build/Makefile.build:142: arch/x86] Error 2
> make[4]: *** Waiting for unfinished jobs....
> make[3]: *** [Makefile:104: /root/kernel/linux/tools/objtool/objtool-in.o] Error 2
> make[2]: *** [Makefile:73: objtool] Error 2
> make[1]: *** [/root/kernel/linux/Makefile:1449: tools/objtool] Error 2
> make: *** [Makefile:248: __sub-make] Error 2
> 
> ls -al /usr/bin/awk
> lrwxrwxrwx 1 root root 21 Feb 19  2021 /usr/bin/awk -> /etc/alternatives/awk
> ls -al /etc/alternatives/awk
> lrwxrwxrwx 1 root root 13 Feb 19  2021 /etc/alternatives/awk -> /usr/bin/mawk
>

Here is a fix. It works with gawk and mawk:

diff --git a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
index 1b1c1d84225c2..cc4c7a3e6c2e2 100644
--- a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
+++ b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
@@ -12,19 +12,20 @@ BEGIN {
         print
         print "static const char *cpu_feature_names[(NCAPINTS+NBUGINTS)*32] = {"
  
-       feature_expr = "(X86_FEATURE_[A-Z0-9_]+)\\s+\\(([0-9*+ ]+)\\)"
-       debug_expr = "(X86_BUG_[A-Z0-9_]+)\\s+X86_BUG\\(([0-9*+ ]+)\\)"
+       value_expr = "\\([0-9*+ ]+\\)"
  }
  
  /^#define X86_FEATURE_/ {
-       if (match($0, feature_expr, m)) {
-               print "\t[" m[2] "] = \"" m[1] "\","
+       if (match($0, value_expr)) {
+               value = substr($0, RSTART + 1, RLENGTH - 2)
+               print "\t[" value "] = \"" $2 "\","
         }
  }
  
  /^#define X86_BUG_/ {
-       if (match($0, debug_expr, m)) {
-               print "\t[NCAPINTS*32+(" m[2] ")] = \"" m[1] "\","
+       if (match($0, value_expr)) {
+               value = substr($0, RSTART + 1, RLENGTH - 2)
+               print "\t[NCAPINTS*32+(" value ")] = \"" $2 "\","
         }
  }
  
I am going to send the updated patch.

Rgds,

alex.


^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-24 12:40         ` Alexandre Chartre
@ 2025-11-24 19:05           ` david laight
  0 siblings, 0 replies; 110+ messages in thread
From: david laight @ 2025-11-24 19:05 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: Borislav Petkov, Peter Zijlstra, linux-kernel, linux-tip-commits,
	Josh Poimboeuf, x86

On Mon, 24 Nov 2025 13:40:47 +0100
Alexandre Chartre <alexandre.chartre@oracle.com> wrote:

> On 11/24/25 13:26, Borislav Petkov wrote:
> > On Mon, Nov 24, 2025 at 12:59:42PM +0100, Peter Zijlstra wrote:  
> >> On Mon, Nov 24, 2025 at 10:44:01AM -0000, tip-bot2 for Alexandre Chartre wrote:  
> >>> The following commit has been merged into the objtool/core branch of tip:
> >>>
> >>> Commit-ID:     afff4e5820e9a0d609740a83c366f3f0335db342
> >>> Gitweb:        https://git.kernel.org/tip/afff4e5820e9a0d609740a83c366f3f0335db342
> >>> Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
> >>> AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
> >>> Committer:     Peter Zijlstra <peterz@infradead.org>
> >>> CommitterDate: Mon, 24 Nov 2025 11:35:06 +01:00
> >>>
> >>> objtool: Function to get the name of a CPU feature  
> > 
> > Also, this commit name needs a verb.
> >   
> >> Boris just reported that this doesn't work on mawk, since it uses a GNU
> >> awk extension (3rd argument for match()).
> >>
> >> Could you please look at writing this in strict POSIX awk?  
> > 
> > The fail is:
> > 
> > awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 20: syntax error at or near ,
> > awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 23: syntax error at or near }
> > awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 26: syntax error at or near ,
> > awk: ../arch/x86/tools/gen-cpu-feature-names-x86.awk: line 29: syntax error at or near }
> > make[5]: *** [arch/x86/Build:21: /root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c] Error 2
> > make[5]: *** Deleting file '/root/kernel/linux/tools/objtool/arch/x86/lib/cpu-feature-names.c'
> > make[4]: *** [/root/kernel/linux/tools/build/Makefile.build:142: arch/x86] Error 2
> > make[4]: *** Waiting for unfinished jobs....
> > make[3]: *** [Makefile:104: /root/kernel/linux/tools/objtool/objtool-in.o] Error 2
> > make[2]: *** [Makefile:73: objtool] Error 2
> > make[1]: *** [/root/kernel/linux/Makefile:1449: tools/objtool] Error 2
> > make: *** [Makefile:248: __sub-make] Error 2
> > 
> > ls -al /usr/bin/awk
> > lrwxrwxrwx 1 root root 21 Feb 19  2021 /usr/bin/awk -> /etc/alternatives/awk
> > ls -al /etc/alternatives/awk
> > lrwxrwxrwx 1 root root 13 Feb 19  2021 /etc/alternatives/awk -> /usr/bin/mawk
> > 
> > That's debian.
> >   
> 
> Ok. I am working on it.

Can't you just use sed ?
There is no read need for the header or final }
Since you need the header file included for the array bounds you might as well use the
names for the array index.
So something like (worked before I retyped it):
	sed -n -E '/^#define (X86_(FEATURE|BUG)_([^	]*)).*/s//  [ \1 ] = "\1",/p'
(That is [^<space><tab>]* in the middle)
The final .* matches the rest of the line, you could add some 'pattern' to further
verify the match.

It should be possible to just put that into the Makefile generating the include
file in the object directory.

Getting the comments would be more 'interesting', the " would need escaping as well.

	David



^ permalink raw reply	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Trim trailing NOPs in alternative
  2025-11-21  9:53 ` [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative Alexandre Chartre
  2025-11-24  9:10   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
@ 2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 19:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     c0a67900dc129825c48d3638480297aa00f39c00
Gitweb:        https://git.kernel.org/tip/c0a67900dc129825c48d3638480297aa00f39c00
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:40 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 20:40:48 +01:00

objtool: Trim trailing NOPs in alternative

When disassembling alternatives replace trailing NOPs with a single
indication of the number of bytes covered with NOPs.

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-31-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 78 +++++++++++++++++++++++++++++++++++++++---
 1 file changed, 73 insertions(+), 5 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f04bc14..441b930 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -52,6 +52,7 @@ struct disas_alt {
 	struct {
 		char *str;			/* instruction string */
 		int offset;			/* instruction offset */
+		int nops;			/* number of nops */
 	} insn[DISAS_ALT_INSN_MAX];		/* alternative instructions */
 	int insn_idx;				/* index of the next instruction to print */
 };
@@ -727,7 +728,7 @@ static int disas_alt_init(struct disas_alt *dalt,
 }
 
 static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
-			      int offset)
+			      int offset, int nops)
 {
 	int len;
 
@@ -740,6 +741,7 @@ static int disas_alt_add_insn(struct disas_alt *dalt, int index, char *insn_str,
 	len = strlen(insn_str);
 	dalt->insn[index].str = insn_str;
 	dalt->insn[index].offset = offset;
+	dalt->insn[index].nops = nops;
 	if (len > dalt->width)
 		dalt->width = len;
 
@@ -752,6 +754,7 @@ static int disas_alt_jump(struct disas_alt *dalt)
 	struct instruction *dest_insn;
 	char suffix[2] = { 0 };
 	char *str;
+	int nops;
 
 	orig_insn = dalt->orig_insn;
 	dest_insn = dalt->alt->insn;
@@ -762,14 +765,16 @@ static int disas_alt_jump(struct disas_alt *dalt)
 		str = strfmt("jmp%-3s %lx <%s+0x%lx>", suffix,
 			     dest_insn->offset, dest_insn->sym->name,
 			     dest_insn->offset - dest_insn->sym->offset);
+		nops = 0;
 	} else {
 		str = strfmt("nop%d", orig_insn->len);
+		nops = orig_insn->len;
 	}
 
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, nops);
 
 	return 1;
 }
@@ -789,7 +794,7 @@ static int disas_alt_extable(struct disas_alt *dalt)
 	if (!str)
 		return -1;
 
-	disas_alt_add_insn(dalt, 0, str, 0);
+	disas_alt_add_insn(dalt, 0, str, 0, 0);
 
 	return 1;
 }
@@ -805,11 +810,13 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 	int offset;
 	char *str;
 	int count;
+	int nops;
 	int err;
 
 	file = dctx->file;
 	count = 0;
 	offset = 0;
+	nops = 0;
 
 	alt_for_each_insn(file, DALT_GROUP(dalt), insn) {
 
@@ -818,7 +825,8 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 		if (!str)
 			return -1;
 
-		err = disas_alt_add_insn(dalt, count, str, offset);
+		nops = insn->type == INSN_NOP ? insn->len : 0;
+		err = disas_alt_add_insn(dalt, count, str, offset, nops);
 		if (err)
 			break;
 		offset += insn->len;
@@ -834,6 +842,7 @@ static int disas_alt_group(struct disas_context *dctx, struct disas_alt *dalt)
 static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 {
 	char *str;
+	int nops;
 	int err;
 
 	if (DALT_GROUP(dalt))
@@ -849,7 +858,8 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 	str = strdup(disas_result(dctx));
 	if (!str)
 		return -1;
-	err = disas_alt_add_insn(dalt, 0, str, 0);
+	nops = dalt->orig_insn->type == INSN_NOP ? dalt->orig_insn->len : 0;
+	err = disas_alt_add_insn(dalt, 0, str, 0, nops);
 	if (err)
 		return -1;
 
@@ -996,6 +1006,62 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 }
 
 /*
+ * Trim NOPs in alternatives. This replaces trailing NOPs in alternatives
+ * with a single indication of the number of bytes covered with NOPs.
+ *
+ * Return the maximum numbers of instructions in all alternatives after
+ * trailing NOPs have been trimmed.
+ */
+static int disas_alt_trim_nops(struct disas_alt *dalts, int alt_count,
+			       int insn_count)
+{
+	struct disas_alt *dalt;
+	int nops_count;
+	const char *s;
+	int offset;
+	int count;
+	int nops;
+	int i, j;
+
+	count = 0;
+	for (i = 0; i < alt_count; i++) {
+		offset = 0;
+		nops = 0;
+		nops_count = 0;
+		dalt = &dalts[i];
+		for (j = insn_count - 1; j >= 0; j--) {
+			if (!dalt->insn[j].str || !dalt->insn[j].nops)
+				break;
+			offset = dalt->insn[j].offset;
+			free(dalt->insn[j].str);
+			dalt->insn[j].offset = 0;
+			dalt->insn[j].str = NULL;
+			nops += dalt->insn[j].nops;
+			nops_count++;
+		}
+
+		/*
+		 * All trailing NOPs have been removed. If there was a single
+		 * NOP instruction then re-add it. If there was a block of
+		 * NOPs then indicate the number of bytes than the block
+		 * covers (nop*<number-of-bytes>).
+		 */
+		if (nops_count) {
+			s = nops_count == 1 ? "" : "*";
+			dalt->insn[j + 1].str = strfmt("nop%s%d", s, nops);
+			dalt->insn[j + 1].offset = offset;
+			dalt->insn[j + 1].nops = nops;
+			j++;
+		}
+
+		if (j > count)
+			count = j;
+	}
+
+	return count + 1;
+}
+
+/*
  * Disassemble an alternative.
  *
  * Return the last instruction in the default alternative so that
@@ -1083,6 +1149,8 @@ static void *disas_alt(struct disas_context *dctx,
 	 * Print default and non-default alternatives.
 	 */
 
+	insn_count = disas_alt_trim_nops(dalts, alt_count, insn_count);
+
 	if (opts.wide)
 		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
 	else

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add wide output for disassembly
  2025-11-21  9:53 ` [PATCH v6 29/30] objtool: Add wide output for disassembly Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
@ 2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 19:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     aff95e0d4e277c53fa274f4a5b6854849f3fc84d
Gitweb:        https://git.kernel.org/tip/aff95e0d4e277c53fa274f4a5b6854849f3fc84d
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:39 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 20:40:48 +01:00

objtool: Add wide output for disassembly

Add the --wide option to provide a wide output when disassembling.
With this option, the disassembly of alternatives is displayed
side-by-side instead of one above the other.

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-30-alexandre.chartre@oracle.com
---
 tools/objtool/builtin-check.c           |  1 +-
 tools/objtool/disas.c                   | 95 +++++++++++++++++++++++-
 tools/objtool/include/objtool/builtin.h |  1 +-
 3 files changed, 96 insertions(+), 1 deletion(-)

diff --git a/tools/objtool/builtin-check.c b/tools/objtool/builtin-check.c
index a037131..b780df5 100644
--- a/tools/objtool/builtin-check.c
+++ b/tools/objtool/builtin-check.c
@@ -107,6 +107,7 @@ static const struct option check_options[] = {
 	OPT_STRING(0,		 "trace", &opts.trace, "func", "trace function validation"),
 	OPT_BOOLEAN('v',	 "verbose", &opts.verbose, "verbose warnings"),
 	OPT_BOOLEAN(0,		 "werror", &opts.werror, "return error on warnings"),
+	OPT_BOOLEAN(0,		 "wide", &opts.wide, "wide output"),
 
 	OPT_END(),
 };
diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index a4f905e..f04bc14 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -857,6 +857,95 @@ static int disas_alt_default(struct disas_context *dctx, struct disas_alt *dalt)
 }
 
 /*
+ * For each alternative, if there is an instruction at the specified
+ * offset then print this instruction, otherwise print a blank entry.
+ * The offset is an offset from the start of the alternative.
+ *
+ * Return the offset for the next instructions to print, or -1 if all
+ * instructions have been printed.
+ */
+static int disas_alt_print_insn(struct disas_alt *dalts, int alt_count,
+				int insn_count, int offset)
+{
+	struct disas_alt *dalt;
+	int offset_next;
+	char *str;
+	int i, j;
+
+	offset_next = -1;
+
+	for (i = 0; i < alt_count; i++) {
+		dalt = &dalts[i];
+		j = dalt->insn_idx;
+		if (j == -1) {
+			printf("| %-*s ", dalt->width, "");
+			continue;
+		}
+
+		if (dalt->insn[j].offset == offset) {
+			str = dalt->insn[j].str;
+			printf("| %-*s ", dalt->width, str ?: "");
+			if (++j < insn_count) {
+				dalt->insn_idx = j;
+			} else {
+				dalt->insn_idx = -1;
+				continue;
+			}
+		} else {
+			printf("| %-*s ", dalt->width, "");
+		}
+
+		if (dalt->insn[j].offset > 0 &&
+		    (offset_next == -1 ||
+		     (dalt->insn[j].offset < offset_next)))
+			offset_next = dalt->insn[j].offset;
+	}
+	printf("\n");
+
+	return offset_next;
+}
+
+/*
+ * Print all alternatives side-by-side.
+ */
+static void disas_alt_print_wide(char *alt_name, struct disas_alt *dalts, int alt_count,
+				 int insn_count)
+{
+	struct instruction *orig_insn;
+	int offset_next;
+	int offset;
+	int i;
+
+	orig_insn = dalts[0].orig_insn;
+
+	/*
+	 * Print an header with the name of each alternative.
+	 */
+	disas_print_info(stdout, orig_insn, -2, NULL);
+
+	if (strlen(alt_name) > dalts[0].width)
+		dalts[0].width = strlen(alt_name);
+	printf("| %-*s ", dalts[0].width, alt_name);
+
+	for (i = 1; i < alt_count; i++)
+		printf("| %-*s ", dalts[i].width, dalts[i].name);
+
+	printf("\n");
+
+	/*
+	 * Print instructions for each alternative.
+	 */
+	offset_next = 0;
+	do {
+		offset = offset_next;
+		disas_print(stdout, orig_insn->sec, orig_insn->offset + offset,
+			    -2, NULL);
+		offset_next = disas_alt_print_insn(dalts, alt_count, insn_count,
+						   offset);
+	} while (offset_next > offset);
+}
+
+/*
  * Print all alternatives one above the other.
  */
 static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
@@ -993,7 +1082,11 @@ static void *disas_alt(struct disas_context *dctx,
 	/*
 	 * Print default and non-default alternatives.
 	 */
-	disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
+
+	if (opts.wide)
+		disas_alt_print_wide(alt_name, dalts, alt_count, insn_count);
+	else
+		disas_alt_print_compact(alt_name, dalts, alt_count, insn_count);
 
 	last_insn = orig_insn->alt_group ? orig_insn->alt_group->last_insn :
 		orig_insn;
diff --git a/tools/objtool/include/objtool/builtin.h b/tools/objtool/include/objtool/builtin.h
index e3af664..b9e229e 100644
--- a/tools/objtool/include/objtool/builtin.h
+++ b/tools/objtool/include/objtool/builtin.h
@@ -45,6 +45,7 @@ struct opts {
 	const char *trace;
 	bool verbose;
 	bool werror;
+	bool wide;
 };
 
 extern struct opts opts;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Compact output for alternatives with one instruction
  2025-11-21  9:53 ` [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
@ 2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 19:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     07d70b271a6fc4f546b153081f3685931561be7b
Gitweb:        https://git.kernel.org/tip/07d70b271a6fc4f546b153081f3685931561be7b
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:38 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 20:40:48 +01:00

objtool: Compact output for alternatives with one instruction

When disassembling, if an instruction has alternatives which are all
made of a single instruction then print each alternative on a single
line (instruction + description) so that the output is more compact.

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-29-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 22 ++++++++++++++++++++++
 1 file changed, 22 insertions(+)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index 731c449..a4f905e 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -863,6 +863,7 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 				    int alt_count, int insn_count)
 {
 	struct instruction *orig_insn;
+	int width;
 	int i, j;
 	int len;
 
@@ -871,6 +872,27 @@ static void disas_alt_print_compact(char *alt_name, struct disas_alt *dalts,
 	len = disas_print(stdout, orig_insn->sec, orig_insn->offset, 0, NULL);
 	printf("%s\n", alt_name);
 
+	/*
+	 * If all alternatives have a single instruction then print each
+	 * alternative on a single line. Otherwise, print alternatives
+	 * one above the other with a clear separation.
+	 */
+
+	if (insn_count == 1) {
+		width = 0;
+		for (i = 0; i < alt_count; i++) {
+			if (dalts[i].width > width)
+				width = dalts[i].width;
+		}
+
+		for (i = 0; i < alt_count; i++) {
+			printf("%*s= %-*s    (if %s)\n", len, "", width,
+			       dalts[i].insn[0].str, dalts[i].name);
+		}
+
+		return;
+	}
+
 	for (i = 0; i < alt_count; i++) {
 		printf("%*s= %s\n", len, "", dalts[i].name);
 		for (j = 0; j < insn_count; j++) {

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Improve naming of group alternatives
  2025-11-21  9:53 ` [PATCH v6 27/30] objtool: Improve naming of group alternatives Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
@ 2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 19:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     56967b9a772298ad276858ddab5a655b1d167623
Gitweb:        https://git.kernel.org/tip/56967b9a772298ad276858ddab5a655b1d167623
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:37 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 20:40:48 +01:00

objtool: Improve naming of group alternatives

Improve the naming of group alternatives by showing the feature name and
flags used by the alternative.

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-28-alexandre.chartre@oracle.com
---
 tools/objtool/disas.c | 58 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 52 insertions(+), 6 deletions(-)

diff --git a/tools/objtool/disas.c b/tools/objtool/disas.c
index f8917c8..731c449 100644
--- a/tools/objtool/disas.c
+++ b/tools/objtool/disas.c
@@ -9,6 +9,7 @@
 #include <objtool/arch.h>
 #include <objtool/check.h>
 #include <objtool/disas.h>
+#include <objtool/special.h>
 #include <objtool/warn.h>
 
 #include <bfd.h>
@@ -60,6 +61,21 @@ struct disas_alt {
 #define DALT_GROUP(dalt)	(DALT_INSN(dalt)->alt_group)
 #define DALT_ALTID(dalt)	((dalt)->orig_insn->offset)
 
+#define ALT_FLAGS_SHIFT		16
+#define ALT_FLAG_NOT		(1 << 0)
+#define ALT_FLAG_DIRECT_CALL	(1 << 1)
+#define ALT_FEATURE_MASK	((1 << ALT_FLAGS_SHIFT) - 1)
+
+static int alt_feature(unsigned int ft_flags)
+{
+	return (ft_flags & ALT_FEATURE_MASK);
+}
+
+static int alt_flags(unsigned int ft_flags)
+{
+	return (ft_flags >> ALT_FLAGS_SHIFT);
+}
+
 /*
  * Wrapper around asprintf() to allocate and format a string.
  * Return the allocated string or NULL on error.
@@ -635,7 +651,12 @@ const char *disas_alt_type_name(struct instruction *insn)
  */
 char *disas_alt_name(struct alternative *alt)
 {
+	char pfx[4] = { 0 };
 	char *str = NULL;
+	const char *name;
+	int feature;
+	int flags;
+	int num;
 
 	switch (alt->type) {
 
@@ -649,13 +670,37 @@ char *disas_alt_name(struct alternative *alt)
 
 	case ALT_TYPE_INSTRUCTIONS:
 		/*
-		 * This is a non-default group alternative. Create a unique
-		 * name using the offset of the first original and alternative
-		 * instructions.
+		 * This is a non-default group alternative. Create a name
+		 * based on the feature and flags associated with this
+		 * alternative. Use either the feature name (it is available)
+		 * or the feature number. And add a prefix to show the flags
+		 * used.
+		 *
+		 * Prefix flags characters:
+		 *
+		 *   '!'  alternative used when feature not enabled
+		 *   '+'  direct call alternative
+		 *   '?'  unknown flag
 		 */
-		asprintf(&str, "ALTERNATIVE %lx.%lx",
-			 alt->insn->alt_group->orig_group->first_insn->offset,
-			 alt->insn->alt_group->first_insn->offset);
+
+		feature = alt->insn->alt_group->feature;
+		num = alt_feature(feature);
+		flags = alt_flags(feature);
+		str = pfx;
+
+		if (flags & ~(ALT_FLAG_NOT | ALT_FLAG_DIRECT_CALL))
+			*str++ = '?';
+		if (flags & ALT_FLAG_DIRECT_CALL)
+			*str++ = '+';
+		if (flags & ALT_FLAG_NOT)
+			*str++ = '!';
+
+		name = arch_cpu_feature_name(num);
+		if (!name)
+			str = strfmt("%sFEATURE 0x%X", pfx, num);
+		else
+			str = strfmt("%s%s", pfx, name);
+
 		break;
 	}
 
@@ -892,6 +937,7 @@ static void *disas_alt(struct disas_context *dctx,
 			WARN("%s has more alternatives than supported", alt_name);
 			break;
 		}
+
 		dalt = &dalts[i];
 		err = disas_alt_init(dalt, orig_insn, alt);
 		if (err) {

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* [tip: objtool/core] objtool: Add Function to get the name of a CPU feature
  2025-11-21  9:53 ` [PATCH v6 26/30] objtool: Function to get the name of a CPU feature Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
@ 2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
  2 siblings, 0 replies; 110+ messages in thread
From: tip-bot2 for Alexandre Chartre @ 2025-11-24 19:49 UTC (permalink / raw)
  To: linux-tip-commits
  Cc: Alexandre Chartre, Peter Zijlstra (Intel), Josh Poimboeuf, x86,
	linux-kernel

The following commit has been merged into the objtool/core branch of tip:

Commit-ID:     8308fd001927f5bdc37a9c9f9c413baec3fb7bbe
Gitweb:        https://git.kernel.org/tip/8308fd001927f5bdc37a9c9f9c413baec3fb7bbe
Author:        Alexandre Chartre <alexandre.chartre@oracle.com>
AuthorDate:    Fri, 21 Nov 2025 10:53:36 +01:00
Committer:     Peter Zijlstra <peterz@infradead.org>
CommitterDate: Mon, 24 Nov 2025 20:39:47 +01:00

objtool: Add Function to get the name of a CPU feature

Add a function to get the name of a CPU feature. The function is
architecture dependent and currently only implemented for x86. The
feature names are automatically generated from the cpufeatures.h
include file.

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-27-alexandre.chartre@oracle.com
---
 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk | 34 +++++++++++++-
 tools/objtool/.gitignore                           |  1 +-
 tools/objtool/Makefile                             |  1 +-
 tools/objtool/arch/loongarch/special.c             |  5 ++-
 tools/objtool/arch/powerpc/special.c               |  5 ++-
 tools/objtool/arch/x86/Build                       | 13 ++++-
 tools/objtool/arch/x86/special.c                   | 10 ++++-
 tools/objtool/include/objtool/special.h            |  2 +-
 8 files changed, 70 insertions(+), 1 deletion(-)
 create mode 100644 tools/arch/x86/tools/gen-cpu-feature-names-x86.awk

diff --git a/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
new file mode 100644
index 0000000..cc4c7a3
--- /dev/null
+++ b/tools/arch/x86/tools/gen-cpu-feature-names-x86.awk
@@ -0,0 +1,34 @@
+#!/bin/awk -f
+# SPDX-License-Identifier: GPL-2.0
+#
+# Copyright (c) 2025, Oracle and/or its affiliates.
+#
+# Usage: awk -f gen-cpu-feature-names-x86.awk cpufeatures.h > cpu-feature-names.c
+#
+
+BEGIN {
+	print "/* cpu feature name array generated from cpufeatures.h */"
+	print "/* Do not change this code. */"
+	print
+	print "static const char *cpu_feature_names[(NCAPINTS+NBUGINTS)*32] = {"
+
+	value_expr = "\\([0-9*+ ]+\\)"
+}
+
+/^#define X86_FEATURE_/ {
+	if (match($0, value_expr)) {
+		value = substr($0, RSTART + 1, RLENGTH - 2)
+		print "\t[" value "] = \"" $2 "\","
+	}
+}
+
+/^#define X86_BUG_/ {
+	if (match($0, value_expr)) {
+		value = substr($0, RSTART + 1, RLENGTH - 2)
+		print "\t[NCAPINTS*32+(" value ")] = \"" $2 "\","
+	}
+}
+
+END {
+	print "};"
+}
diff --git a/tools/objtool/.gitignore b/tools/objtool/.gitignore
index 7593036..73d8831 100644
--- a/tools/objtool/.gitignore
+++ b/tools/objtool/.gitignore
@@ -1,4 +1,5 @@
 # SPDX-License-Identifier: GPL-2.0-only
+arch/x86/lib/cpu-feature-names.c
 arch/x86/lib/inat-tables.c
 /objtool
 feature
diff --git a/tools/objtool/Makefile b/tools/objtool/Makefile
index df793ca..66397d7 100644
--- a/tools/objtool/Makefile
+++ b/tools/objtool/Makefile
@@ -125,6 +125,7 @@ $(LIBSUBCMD)-clean:
 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
diff --git a/tools/objtool/arch/loongarch/special.c b/tools/objtool/arch/loongarch/special.c
index a80b75f..aba7741 100644
--- a/tools/objtool/arch/loongarch/special.c
+++ b/tools/objtool/arch/loongarch/special.c
@@ -194,3 +194,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/powerpc/special.c b/tools/objtool/arch/powerpc/special.c
index 5161068..8f9bf61 100644
--- a/tools/objtool/arch/powerpc/special.c
+++ b/tools/objtool/arch/powerpc/special.c
@@ -18,3 +18,8 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 {
 	exit(-1);
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return NULL;
+}
diff --git a/tools/objtool/arch/x86/Build b/tools/objtool/arch/x86/Build
index 3dedb2f..febee0b 100644
--- a/tools/objtool/arch/x86/Build
+++ b/tools/objtool/arch/x86/Build
@@ -1,5 +1,5 @@
-objtool-y += special.o
 objtool-y += decode.o
+objtool-y += special.o
 objtool-y += orc.o
 
 inat_tables_script = ../arch/x86/tools/gen-insn-attr-x86.awk
@@ -12,3 +12,14 @@ $(OUTPUT)arch/x86/lib/inat-tables.c: $(inat_tables_script) $(inat_tables_maps)
 $(OUTPUT)arch/x86/decode.o: $(OUTPUT)arch/x86/lib/inat-tables.c
 
 CFLAGS_decode.o += -I$(OUTPUT)arch/x86/lib
+
+cpu_features = ../arch/x86/include/asm/cpufeatures.h
+cpu_features_script = ../arch/x86/tools/gen-cpu-feature-names-x86.awk
+
+$(OUTPUT)arch/x86/lib/cpu-feature-names.c: $(cpu_features_script) $(cpu_features)
+	$(call rule_mkdir)
+	$(Q)$(call echo-cmd,gen)$(AWK) -f $(cpu_features_script) $(cpu_features) > $@
+
+$(OUTPUT)arch/x86/special.o: $(OUTPUT)arch/x86/lib/cpu-feature-names.c
+
+CFLAGS_special.o += -I$(OUTPUT)arch/x86/lib
diff --git a/tools/objtool/arch/x86/special.c b/tools/objtool/arch/x86/special.c
index 0930076..e817a3f 100644
--- a/tools/objtool/arch/x86/special.c
+++ b/tools/objtool/arch/x86/special.c
@@ -4,6 +4,10 @@
 #include <objtool/special.h>
 #include <objtool/builtin.h>
 #include <objtool/warn.h>
+#include <asm/cpufeatures.h>
+
+/* cpu feature name array generated from cpufeatures.h */
+#include "cpu-feature-names.c"
 
 void arch_handle_alternative(struct special_alt *alt)
 {
@@ -134,3 +138,9 @@ struct reloc *arch_find_switch_table(struct objtool_file *file,
 	*table_size = 0;
 	return rodata_reloc;
 }
+
+const char *arch_cpu_feature_name(int feature_number)
+{
+	return (feature_number < ARRAY_SIZE(cpu_feature_names)) ?
+		cpu_feature_names[feature_number] : NULL;
+}
diff --git a/tools/objtool/include/objtool/special.h b/tools/objtool/include/objtool/special.h
index b224107..121c376 100644
--- a/tools/objtool/include/objtool/special.h
+++ b/tools/objtool/include/objtool/special.h
@@ -38,4 +38,6 @@ bool arch_support_alt_relocation(struct special_alt *special_alt,
 struct reloc *arch_find_switch_table(struct objtool_file *file,
 				     struct instruction *insn,
 				     unsigned long *table_size);
+const char *arch_cpu_feature_name(int feature_number);
+
 #endif /* _SPECIAL_H */

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [tip: objtool/core] objtool: Function to get the name of a CPU feature
  2025-11-24 16:43         ` Alexandre Chartre
@ 2025-11-24 20:14           ` Borislav Petkov
  0 siblings, 0 replies; 110+ messages in thread
From: Borislav Petkov @ 2025-11-24 20:14 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: Peter Zijlstra, linux-kernel, linux-tip-commits, Josh Poimboeuf,
	x86

On Mon, Nov 24, 2025 at 05:43:04PM +0100, Alexandre Chartre wrote:
> Here is a fix. It works with gawk and mawk:

Yap, works, thanks!

Reported-by: Borislav Petkov (AMD) <bp@alien8.de>
Tested-by: Borislav Petkov (AMD) <bp@alien8.de>

-- 
Regards/Gruss,
    Boris.

https://people.kernel.org/tglx/notes-about-netiquette

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-21  9:53 ` [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump Alexandre Chartre
  2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-11-25 18:16   ` Nathan Chancellor
  2025-11-26  9:00     ` Alexandre Chartre
  2025-12-09  6:58   ` Guenter Roeck
  2 siblings, 1 reply; 110+ messages in thread
From: Nathan Chancellor @ 2025-11-25 18:16 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux

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
 

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-25 18:16   ` [PATCH v6 03/30] " Nathan Chancellor
@ 2025-11-26  9:00     ` Alexandre Chartre
  2025-11-26 10:07       ` David Laight
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-26  9:00 UTC (permalink / raw)
  To: Nathan Chancellor
  Cc: alexandre.chartre, linux-kernel, mingo, jpoimboe, peterz,
	david.laight.linux


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.



^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-26  9:00     ` Alexandre Chartre
@ 2025-11-26 10:07       ` David Laight
  2025-11-26 10:29         ` Alexandre Chartre
  0 siblings, 1 reply; 110+ messages in thread
From: David Laight @ 2025-11-26 10:07 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: Nathan Chancellor, linux-kernel, mingo, jpoimboe, peterz

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.
> 
> 


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-26 10:07       ` David Laight
@ 2025-11-26 10:29         ` Alexandre Chartre
  0 siblings, 0 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-11-26 10:29 UTC (permalink / raw)
  To: David Laight
  Cc: alexandre.chartre, Nathan Chancellor, linux-kernel, mingo,
	jpoimboe, peterz


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.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 11/30] objtool: Trace instruction state changes during function validation
  2025-11-21  9:53 ` [PATCH v6 11/30] objtool: Trace instruction state changes during " Alexandre Chartre
  2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
@ 2025-12-01 20:23   ` Nathan Chancellor
  2025-12-02  1:30     ` Josh Poimboeuf
  1 sibling, 1 reply; 110+ messages in thread
From: Nathan Chancellor @ 2025-12-01 20:23 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux, llvm

Hi Alexandre,

On Fri, Nov 21, 2025 at 10:53:21AM +0100, Alexandre Chartre wrote:
> During function validation, objtool maintains a per-instruction state,
> in particular to track call frame information. When tracing validation,
> print any instruction state changes.
> 
> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>

I am seeing a segfault after this change in -next as commit fcb268b47a2f
("objtool: Trace instruction state changes during function validation")
when building allmodconfig with clang 21.1.6 [1] (I did not check
earlier versions).

  $ clang --version | head -1
  ClangBuiltLinux clang version 21.1.6 (https://github.com/llvm/llvm-project.git a832a5222e489298337fbb5876f8dcaf072c5cca)

  $ make -skj"$(nproc)" ARCH=x86_64 LLVM=1 clean allmodconfig drivers/scsi/qla2xxx/qla2xxx.o
  make[7]: *** [scripts/Makefile.build:503: drivers/scsi/qla2xxx/qla2xxx.o] Error 139
  ...

  $ ld.lld -m elf_x86_64 --fatal-warnings -z noexecstack -r -o drivers/scsi/qla2xxx/qla2xxx.o @drivers/scsi/qla2xxx/qla2xxx.mod

  $ tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --cfi --mcount --mnop --orc --retpoline --rethunk --sls --static-call --uaccess --no-unreachable --link --module drivers/scsi/qla2xxx/qla2xxx.o
  fish: Job 1, 'tools/objtool/objtool --hacks=j…' terminated by signal SIGSEGV (Address boundary error)

If there is any other information I can provide or patches I can test, I
am more than happy to do so.

[1]: https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-21.1.6-x86_64.tar.xz

Cheers,
Nathan

# bad: [95cb2fd6ce0ad61af54191fe5ef271d7177f9c3a] Add linux-next specific files for 20251201
# good: [e69c7c175115c51c7f95394fc55425a395b3af59] Merge tag 'timers_urgent_for_v6.18_rc8' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
git bisect start '95cb2fd6ce0ad61af54191fe5ef271d7177f9c3a' 'e69c7c175115c51c7f95394fc55425a395b3af59'
# good: [87d5c4addc7e535618586e7205191a7f402288ba] Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/herbert/cryptodev-2.6.git
git bisect good 87d5c4addc7e535618586e7205191a7f402288ba
# good: [a4ad48eac682ccdc21e2f16b8f27abbf615d8d3d] Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator.git
git bisect good a4ad48eac682ccdc21e2f16b8f27abbf615d8d3d
# bad: [b99f4ac0a6c7ccf37be14f5ef61b160b1c8a74b0] Merge branch 'driver-core-next' of https://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
git bisect bad b99f4ac0a6c7ccf37be14f5ef61b160b1c8a74b0
# bad: [24cefd05bbf969c95fff3733da174e8a352c1cb2] Merge branch 'master' of https://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git
git bisect bad 24cefd05bbf969c95fff3733da174e8a352c1cb2
# bad: [4fa1e8d3340bc660d72a21fc8d4c566045e488fc] Merge branch into tip/master: 'timers/clocksource'
git bisect bad 4fa1e8d3340bc660d72a21fc8d4c566045e488fc
# good: [51446852de95594c02d6b3b700e15b7c886534d6] Merge branch into tip/master: 'core/bugs'
git bisect good 51446852de95594c02d6b3b700e15b7c886534d6
# good: [b032713af9475274762fa664ca2705372b414215] Merge branch into tip/master: 'locking/core'
git bisect good b032713af9475274762fa664ca2705372b414215
# good: [9929dffce5ed7e2988e0274f4db98035508b16d9] perf/x86/intel: Fix and clean up intel_pmu_drain_arch_pebs() type use
git bisect good 9929dffce5ed7e2988e0274f4db98035508b16d9
# bad: [59bfa6408214b6533d8691715cf5459e89b45b89] objtool: Build with disassembly can fail when including bdf.h
git bisect bad 59bfa6408214b6533d8691715cf5459e89b45b89
# bad: [350c7ab8577a32c101a097f4c072220d9ce64f3b] objtool: Improve tracing of alternative instructions
git bisect bad 350c7ab8577a32c101a097f4c072220d9ce64f3b
# good: [0bb080ba6469a573bc85122153d931334d10a173] objtool: Disassemble instruction on warning or backtrace
git bisect good 0bb080ba6469a573bc85122153d931334d10a173
# bad: [fcb268b47a2f4a497fdb40ef24bb9e06488b7213] objtool: Trace instruction state changes during function validation
git bisect bad fcb268b47a2f4a497fdb40ef24bb9e06488b7213
# good: [de0248fbbf999d0fd3ca2aa5ba515ab78703d129] objtool: Record symbol name max length
git bisect good de0248fbbf999d0fd3ca2aa5ba515ab78703d129
# good: [70589843b36fee0c6e73632469da4e5fd11f0968] objtool: Add option to trace function validation
git bisect good 70589843b36fee0c6e73632469da4e5fd11f0968
# first bad commit: [fcb268b47a2f4a497fdb40ef24bb9e06488b7213] objtool: Trace instruction state changes during function validation

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 11/30] objtool: Trace instruction state changes during function validation
  2025-12-01 20:23   ` [PATCH v6 11/30] " Nathan Chancellor
@ 2025-12-02  1:30     ` Josh Poimboeuf
  2025-12-02  8:34       ` Alexandre Chartre
  0 siblings, 1 reply; 110+ messages in thread
From: Josh Poimboeuf @ 2025-12-02  1:30 UTC (permalink / raw)
  To: Nathan Chancellor
  Cc: Alexandre Chartre, linux-kernel, mingo, peterz,
	david.laight.linux, llvm

On Mon, Dec 01, 2025 at 01:23:29PM -0700, Nathan Chancellor wrote:
> Hi Alexandre,
> 
> On Fri, Nov 21, 2025 at 10:53:21AM +0100, Alexandre Chartre wrote:
> > During function validation, objtool maintains a per-instruction state,
> > in particular to track call frame information. When tracing validation,
> > print any instruction state changes.
> > 
> > Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
> 
> I am seeing a segfault after this change in -next as commit fcb268b47a2f
> ("objtool: Trace instruction state changes during function validation")
> when building allmodconfig with clang 21.1.6 [1] (I did not check
> earlier versions).
> 
>   $ clang --version | head -1
>   ClangBuiltLinux clang version 21.1.6 (https://github.com/llvm/llvm-project.git a832a5222e489298337fbb5876f8dcaf072c5cca)
> 
>   $ make -skj"$(nproc)" ARCH=x86_64 LLVM=1 clean allmodconfig drivers/scsi/qla2xxx/qla2xxx.o
>   make[7]: *** [scripts/Makefile.build:503: drivers/scsi/qla2xxx/qla2xxx.o] Error 139
>   ...
> 
>   $ ld.lld -m elf_x86_64 --fatal-warnings -z noexecstack -r -o drivers/scsi/qla2xxx/qla2xxx.o @drivers/scsi/qla2xxx/qla2xxx.mod
> 
>   $ tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --cfi --mcount --mnop --orc --retpoline --rethunk --sls --static-call --uaccess --no-unreachable --link --module drivers/scsi/qla2xxx/qla2xxx.o
>   fish: Job 1, 'tools/objtool/objtool --hacks=j…' terminated by signal SIGSEGV (Address boundary error)
> 
> If there is any other information I can provide or patches I can test, I
> am more than happy to do so.
> 
> [1]: https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-21.1.6-x86_64.tar.xz

Objtool is overflowing the stack due to the large number of jumps it has
to follow in that code, thanks to kasan.  The above mentioned patch

  fcb268b47a2f ("objtool: Trace instruction state changes during function validation")

added a 328-byte struct to the stack in validate_insn() which
drastically increased the amount of stack size needed.

I suppose we could hack a fix by making it a static local variable, like
below.

Or, objtool could setrlimit(RLIMIT_STACK) to 16MB?

diff --git a/tools/objtool/check.c b/tools/objtool/check.c
index a02f8db75827..206b8589d82b 100644
--- a/tools/objtool/check.c
+++ b/tools/objtool/check.c
@@ -3678,7 +3678,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
 			 bool *dead_end)
 {
 	/* prev_state is not used if there is no disassembly support */
-	struct insn_state prev_state __maybe_unused;
+	static struct insn_state prev_state __maybe_unused;
 	struct alternative *alt;
 	u8 visited;
 	int ret;

^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 11/30] objtool: Trace instruction state changes during function validation
  2025-12-02  1:30     ` Josh Poimboeuf
@ 2025-12-02  8:34       ` Alexandre Chartre
  2025-12-02 16:13         ` Josh Poimboeuf
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-12-02  8:34 UTC (permalink / raw)
  To: Josh Poimboeuf, Nathan Chancellor
  Cc: alexandre.chartre, linux-kernel, mingo, peterz,
	david.laight.linux, llvm


On 12/2/25 02:30, Josh Poimboeuf wrote:
> On Mon, Dec 01, 2025 at 01:23:29PM -0700, Nathan Chancellor wrote:
>> Hi Alexandre,
>>
>> On Fri, Nov 21, 2025 at 10:53:21AM +0100, Alexandre Chartre wrote:
>>> During function validation, objtool maintains a per-instruction state,
>>> in particular to track call frame information. When tracing validation,
>>> print any instruction state changes.
>>>
>>> Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
>>
>> I am seeing a segfault after this change in -next as commit fcb268b47a2f
>> ("objtool: Trace instruction state changes during function validation")
>> when building allmodconfig with clang 21.1.6 [1] (I did not check
>> earlier versions).
>>
>>    $ clang --version | head -1
>>    ClangBuiltLinux clang version 21.1.6 (https://github.com/llvm/llvm-project.git a832a5222e489298337fbb5876f8dcaf072c5cca)
>>
>>    $ make -skj"$(nproc)" ARCH=x86_64 LLVM=1 clean allmodconfig drivers/scsi/qla2xxx/qla2xxx.o
>>    make[7]: *** [scripts/Makefile.build:503: drivers/scsi/qla2xxx/qla2xxx.o] Error 139
>>    ...
>>
>>    $ ld.lld -m elf_x86_64 --fatal-warnings -z noexecstack -r -o drivers/scsi/qla2xxx/qla2xxx.o @drivers/scsi/qla2xxx/qla2xxx.mod
>>
>>    $ tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --cfi --mcount --mnop --orc --retpoline --rethunk --sls --static-call --uaccess --no-unreachable --link --module drivers/scsi/qla2xxx/qla2xxx.o
>>    fish: Job 1, 'tools/objtool/objtool --hacks=j…' terminated by signal SIGSEGV (Address boundary error)
>>
>> If there is any other information I can provide or patches I can test, I
>> am more than happy to do so.
>>
>> [1]: https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-21.1.6-x86_64.tar.xz
> 
> Objtool is overflowing the stack due to the large number of jumps it has
> to follow in that code, thanks to kasan.  The above mentioned patch
> 
>    fcb268b47a2f ("objtool: Trace instruction state changes during function validation")
> 
> added a 328-byte struct to the stack in validate_insn() which
> drastically increased the amount of stack size needed.
> 
> I suppose we could hack a fix by making it a static local variable, like
> below.
> 
> Or, objtool could setrlimit(RLIMIT_STACK) to 16MB?
> 
> diff --git a/tools/objtool/check.c b/tools/objtool/check.c
> index a02f8db75827..206b8589d82b 100644
> --- a/tools/objtool/check.c
> +++ b/tools/objtool/check.c
> @@ -3678,7 +3678,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
>   			 bool *dead_end)
>   {
>   	/* prev_state is not used if there is no disassembly support */
> -	struct insn_state prev_state __maybe_unused;
> +	static struct insn_state prev_state __maybe_unused;
>   	struct alternative *alt;
>   	u8 visited;
>   	int ret;

static looks good enough to me.

Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>

Thanks,

alex.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 11/30] objtool: Trace instruction state changes during function validation
  2025-12-02  8:34       ` Alexandre Chartre
@ 2025-12-02 16:13         ` Josh Poimboeuf
  2025-12-02 18:09           ` David Laight
  0 siblings, 1 reply; 110+ messages in thread
From: Josh Poimboeuf @ 2025-12-02 16:13 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: Nathan Chancellor, linux-kernel, mingo, peterz,
	david.laight.linux, llvm

On Tue, Dec 02, 2025 at 09:34:20AM +0100, Alexandre Chartre wrote:
> 
> On 12/2/25 02:30, Josh Poimboeuf wrote:
> > On Mon, Dec 01, 2025 at 01:23:29PM -0700, Nathan Chancellor wrote:
> > > Hi Alexandre,
> > > 
> > > On Fri, Nov 21, 2025 at 10:53:21AM +0100, Alexandre Chartre wrote:
> > > > During function validation, objtool maintains a per-instruction state,
> > > > in particular to track call frame information. When tracing validation,
> > > > print any instruction state changes.
> > > > 
> > > > Signed-off-by: Alexandre Chartre <alexandre.chartre@oracle.com>
> > > 
> > > I am seeing a segfault after this change in -next as commit fcb268b47a2f
> > > ("objtool: Trace instruction state changes during function validation")
> > > when building allmodconfig with clang 21.1.6 [1] (I did not check
> > > earlier versions).
> > > 
> > >    $ clang --version | head -1
> > >    ClangBuiltLinux clang version 21.1.6 (https://github.com/llvm/llvm-project.git a832a5222e489298337fbb5876f8dcaf072c5cca)
> > > 
> > >    $ make -skj"$(nproc)" ARCH=x86_64 LLVM=1 clean allmodconfig drivers/scsi/qla2xxx/qla2xxx.o
> > >    make[7]: *** [scripts/Makefile.build:503: drivers/scsi/qla2xxx/qla2xxx.o] Error 139
> > >    ...
> > > 
> > >    $ ld.lld -m elf_x86_64 --fatal-warnings -z noexecstack -r -o drivers/scsi/qla2xxx/qla2xxx.o @drivers/scsi/qla2xxx/qla2xxx.mod
> > > 
> > >    $ tools/objtool/objtool --hacks=jump_label --hacks=noinstr --hacks=skylake --ibt --cfi --mcount --mnop --orc --retpoline --rethunk --sls --static-call --uaccess --no-unreachable --link --module drivers/scsi/qla2xxx/qla2xxx.o
> > >    fish: Job 1, 'tools/objtool/objtool --hacks=j…' terminated by signal SIGSEGV (Address boundary error)
> > > 
> > > If there is any other information I can provide or patches I can test, I
> > > am more than happy to do so.
> > > 
> > > [1]: https://mirrors.edge.kernel.org/pub/tools/llvm/files/llvm-21.1.6-x86_64.tar.xz
> > 
> > Objtool is overflowing the stack due to the large number of jumps it has
> > to follow in that code, thanks to kasan.  The above mentioned patch
> > 
> >    fcb268b47a2f ("objtool: Trace instruction state changes during function validation")
> > 
> > added a 328-byte struct to the stack in validate_insn() which
> > drastically increased the amount of stack size needed.
> > 
> > I suppose we could hack a fix by making it a static local variable, like
> > below.
> > 
> > Or, objtool could setrlimit(RLIMIT_STACK) to 16MB?
> > 
> > diff --git a/tools/objtool/check.c b/tools/objtool/check.c
> > index a02f8db75827..206b8589d82b 100644
> > --- a/tools/objtool/check.c
> > +++ b/tools/objtool/check.c
> > @@ -3678,7 +3678,7 @@ static int validate_insn(struct objtool_file *file, struct symbol *func,
> >   			 bool *dead_end)
> >   {
> >   	/* prev_state is not used if there is no disassembly support */
> > -	struct insn_state prev_state __maybe_unused;
> > +	static struct insn_state prev_state __maybe_unused;
> >   	struct alternative *alt;
> >   	u8 visited;
> >   	int ret;
> 
> static looks good enough to me.
> 
> Reviewed-by: Alexandre Chartre <alexandre.chartre@oracle.com>
> 
> Thanks,

The static local bothered me, I went with a different approach to move
that variable (and its usage) to handle_insn_ops().  Will post shortly.

-- 
Josh

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 11/30] objtool: Trace instruction state changes during function validation
  2025-12-02 16:13         ` Josh Poimboeuf
@ 2025-12-02 18:09           ` David Laight
  0 siblings, 0 replies; 110+ messages in thread
From: David Laight @ 2025-12-02 18:09 UTC (permalink / raw)
  To: Josh Poimboeuf
  Cc: Alexandre Chartre, Nathan Chancellor, linux-kernel, mingo, peterz,
	llvm

On Tue, 2 Dec 2025 08:13:21 -0800
Josh Poimboeuf <jpoimboe@kernel.org> wrote:

...
> The static local bothered me, I went with a different approach to move
> that variable (and its usage) to handle_insn_ops().  Will post shortly.
> 

Does look a little wrong inside a recursively called function.

How does the code get on for 32bit builds?
I'd guess they have a lot less stack available?

	David

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-11-21  9:53 ` [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump Alexandre Chartre
  2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
  2025-11-25 18:16   ` [PATCH v6 03/30] " Nathan Chancellor
@ 2025-12-09  6:58   ` Guenter Roeck
  2025-12-09  7:51     ` Alexandre Chartre
  2 siblings, 1 reply; 110+ messages in thread
From: Guenter Roeck @ 2025-12-09  6:58 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux

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

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-09  6:58   ` Guenter Roeck
@ 2025-12-09  7:51     ` Alexandre Chartre
  2025-12-09 15:11       ` Guenter Roeck
  2025-12-09 22:25       ` Maciej W. Rozycki
  0 siblings, 2 replies; 110+ messages in thread
From: Alexandre Chartre @ 2025-12-09  7:51 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: alexandre.chartre, linux-kernel, mingo, jpoimboe, peterz,
	david.laight.linux


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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-09  7:51     ` Alexandre Chartre
@ 2025-12-09 15:11       ` Guenter Roeck
  2025-12-09 22:25       ` Maciej W. Rozycki
  1 sibling, 0 replies; 110+ messages in thread
From: Guenter Roeck @ 2025-12-09 15:11 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: linux-kernel, mingo, jpoimboe, peterz, david.laight.linux

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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-09  7:51     ` Alexandre Chartre
  2025-12-09 15:11       ` Guenter Roeck
@ 2025-12-09 22:25       ` Maciej W. Rozycki
  2025-12-10  5:08         ` Guenter Roeck
                           ` (2 more replies)
  1 sibling, 3 replies; 110+ messages in thread
From: Maciej W. Rozycki @ 2025-12-09 22:25 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: Guenter Roeck, linux-kernel, Ingo Molnar, jpoimboe, peterz,
	david.laight.linux

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

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-09 22:25       ` Maciej W. Rozycki
@ 2025-12-10  5:08         ` Guenter Roeck
  2025-12-10  7:53           ` Alexandre Chartre
  2025-12-10  7:06         ` Alexandre Chartre
  2025-12-10  8:53         ` Peter Zijlstra
  2 siblings, 1 reply; 110+ messages in thread
From: Guenter Roeck @ 2025-12-10  5:08 UTC (permalink / raw)
  To: Maciej W. Rozycki, Alexandre Chartre
  Cc: linux-kernel, Ingo Molnar, jpoimboe, peterz, david.laight.linux

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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-09 22:25       ` Maciej W. Rozycki
  2025-12-10  5:08         ` Guenter Roeck
@ 2025-12-10  7:06         ` Alexandre Chartre
  2025-12-10  8:56           ` Peter Zijlstra
  2025-12-10  8:53         ` Peter Zijlstra
  2 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-12-10  7:06 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: alexandre.chartre, Guenter Roeck, linux-kernel, Ingo Molnar,
	jpoimboe, peterz, david.laight.linux


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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10  5:08         ` Guenter Roeck
@ 2025-12-10  7:53           ` Alexandre Chartre
  2025-12-10 15:26             ` Guenter Roeck
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-12-10  7:53 UTC (permalink / raw)
  To: Guenter Roeck, Maciej W. Rozycki
  Cc: alexandre.chartre, linux-kernel, Ingo Molnar, jpoimboe, peterz,
	david.laight.linux



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.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-09 22:25       ` Maciej W. Rozycki
  2025-12-10  5:08         ` Guenter Roeck
  2025-12-10  7:06         ` Alexandre Chartre
@ 2025-12-10  8:53         ` Peter Zijlstra
  2025-12-10 12:01           ` Maciej W. Rozycki
  2 siblings, 1 reply; 110+ messages in thread
From: Peter Zijlstra @ 2025-12-10  8:53 UTC (permalink / raw)
  To: Maciej W. Rozycki
  Cc: Alexandre Chartre, Guenter Roeck, linux-kernel, Ingo Molnar,
	jpoimboe, david.laight.linux

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.


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10  7:06         ` Alexandre Chartre
@ 2025-12-10  8:56           ` Peter Zijlstra
  0 siblings, 0 replies; 110+ messages in thread
From: Peter Zijlstra @ 2025-12-10  8:56 UTC (permalink / raw)
  To: Alexandre Chartre
  Cc: Maciej W. Rozycki, Guenter Roeck, linux-kernel, Ingo Molnar,
	jpoimboe, david.laight.linux

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.

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10  8:53         ` Peter Zijlstra
@ 2025-12-10 12:01           ` Maciej W. Rozycki
  0 siblings, 0 replies; 110+ messages in thread
From: Maciej W. Rozycki @ 2025-12-10 12:01 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Alexandre Chartre, Guenter Roeck, linux-kernel, Ingo Molnar,
	jpoimboe, david.laight.linux

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

^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10  7:53           ` Alexandre Chartre
@ 2025-12-10 15:26             ` Guenter Roeck
  2025-12-10 16:36               ` Alexandre Chartre
  0 siblings, 1 reply; 110+ messages in thread
From: Guenter Roeck @ 2025-12-10 15:26 UTC (permalink / raw)
  To: Alexandre Chartre, Maciej W. Rozycki
  Cc: linux-kernel, Ingo Molnar, jpoimboe, peterz, david.laight.linux

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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10 15:26             ` Guenter Roeck
@ 2025-12-10 16:36               ` Alexandre Chartre
  2025-12-10 18:42                 ` Guenter Roeck
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-12-10 16:36 UTC (permalink / raw)
  To: Guenter Roeck, Maciej W. Rozycki
  Cc: alexandre.chartre, linux-kernel, Ingo Molnar, jpoimboe, peterz,
	david.laight.linux



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.






^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10 16:36               ` Alexandre Chartre
@ 2025-12-10 18:42                 ` Guenter Roeck
  2025-12-10 21:54                   ` Guenter Roeck
  0 siblings, 1 reply; 110+ messages in thread
From: Guenter Roeck @ 2025-12-10 18:42 UTC (permalink / raw)
  To: Alexandre Chartre, Maciej W. Rozycki
  Cc: linux-kernel, Ingo Molnar, jpoimboe, peterz, david.laight.linux

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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10 18:42                 ` Guenter Roeck
@ 2025-12-10 21:54                   ` Guenter Roeck
  2025-12-15 16:00                     ` Alexandre Chartre
  0 siblings, 1 reply; 110+ messages in thread
From: Guenter Roeck @ 2025-12-10 21:54 UTC (permalink / raw)
  To: Alexandre Chartre, Maciej W. Rozycki
  Cc: linux-kernel, Ingo Molnar, jpoimboe, peterz, david.laight.linux

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


^ permalink raw reply	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-10 21:54                   ` Guenter Roeck
@ 2025-12-15 16:00                     ` Alexandre Chartre
  2025-12-15 16:46                       ` Guenter Roeck
  0 siblings, 1 reply; 110+ messages in thread
From: Alexandre Chartre @ 2025-12-15 16:00 UTC (permalink / raw)
  To: Guenter Roeck, Maciej W. Rozycki
  Cc: alexandre.chartre, linux-kernel, Ingo Molnar, jpoimboe, peterz,
	david.laight.linux


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



^ permalink raw reply related	[flat|nested] 110+ messages in thread

* Re: [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump
  2025-12-15 16:00                     ` Alexandre Chartre
@ 2025-12-15 16:46                       ` Guenter Roeck
  0 siblings, 0 replies; 110+ messages in thread
From: Guenter Roeck @ 2025-12-15 16:46 UTC (permalink / raw)
  To: Alexandre Chartre, Maciej W. Rozycki
  Cc: linux-kernel, Ingo Molnar, jpoimboe, peterz, david.laight.linux

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


^ permalink raw reply	[flat|nested] 110+ messages in thread

end of thread, other threads:[~2025-12-15 16:46 UTC | newest]

Thread overview: 110+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-11-21  9:53 [PATCH v6 00/30] objtool: Function validation tracing Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 01/30] objtool: Move disassembly functions to a separated file Alexandre Chartre
2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 02/30] objtool: Create disassembly context Alexandre Chartre
2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 03/30] objtool: Disassemble code with libopcodes instead of running objdump Alexandre Chartre
2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-25 18:16   ` [PATCH v6 03/30] " Nathan Chancellor
2025-11-26  9:00     ` Alexandre Chartre
2025-11-26 10:07       ` David Laight
2025-11-26 10:29         ` Alexandre Chartre
2025-12-09  6:58   ` Guenter Roeck
2025-12-09  7:51     ` Alexandre Chartre
2025-12-09 15:11       ` Guenter Roeck
2025-12-09 22:25       ` Maciej W. Rozycki
2025-12-10  5:08         ` Guenter Roeck
2025-12-10  7:53           ` Alexandre Chartre
2025-12-10 15:26             ` Guenter Roeck
2025-12-10 16:36               ` Alexandre Chartre
2025-12-10 18:42                 ` Guenter Roeck
2025-12-10 21:54                   ` Guenter Roeck
2025-12-15 16:00                     ` Alexandre Chartre
2025-12-15 16:46                       ` Guenter Roeck
2025-12-10  7:06         ` Alexandre Chartre
2025-12-10  8:56           ` Peter Zijlstra
2025-12-10  8:53         ` Peter Zijlstra
2025-12-10 12:01           ` Maciej W. Rozycki
2025-11-21  9:53 ` [PATCH v6 04/30] tool build: Remove annoying newline in build output Alexandre Chartre
2025-11-24  9:12   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 05/30] objtool: Print symbol during disassembly Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 06/30] objtool: Store instruction disassembly result Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 07/30] objtool: Disassemble instruction on warning or backtrace Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 08/30] objtool: Extract code to validate instruction from the validate branch loop Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 09/30] objtool: Record symbol name max length Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 10/30] objtool: Add option to trace function validation Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 11/30] objtool: Trace instruction state changes during " Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-12-01 20:23   ` [PATCH v6 11/30] " Nathan Chancellor
2025-12-02  1:30     ` Josh Poimboeuf
2025-12-02  8:34       ` Alexandre Chartre
2025-12-02 16:13         ` Josh Poimboeuf
2025-12-02 18:09           ` David Laight
2025-11-21  9:53 ` [PATCH v6 12/30] objtool: Improve register reporting " Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 13/30] objtool: Identify the different types of alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 14/30] objtool: Add functions to better name alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 15/30] objtool: Improve tracing of alternative instructions Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 16/30] objtool: Do not validate IBT for .return_sites and .call_sites Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 17/30] objtool: Add the --disas=<function-pattern> action Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 18/30] objtool: Preserve alternatives order Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 19/30] objtool: Print headers for alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 20/30] objtool: Disassemble group alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 21/30] objtool: Print addresses with alternative instructions Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 22/30] objtool: Disassemble exception table alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 23/30] objtool: Disassemble jump " Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 24/30] objtool: Fix address references in alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 25/30] objtool: Provide access to feature and flags of group alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 26/30] objtool: Function to get the name of a CPU feature Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
2025-11-24 11:59     ` Peter Zijlstra
2025-11-24 12:26       ` Borislav Petkov
2025-11-24 12:40         ` Alexandre Chartre
2025-11-24 19:05           ` david laight
2025-11-24 16:43         ` Alexandre Chartre
2025-11-24 20:14           ` Borislav Petkov
2025-11-24 19:49   ` [tip: objtool/core] objtool: Add " tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 27/30] objtool: Improve naming of group alternatives Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-24 10:44   ` tip-bot2 for Alexandre Chartre
2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 28/30] objtool: Compact output for alternatives with one instruction Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 29/30] objtool: Add wide output for disassembly Alexandre Chartre
2025-11-24  9:11   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
2025-11-21  9:53 ` [PATCH v6 30/30] objtool: Trim trailing NOPs in alternative Alexandre Chartre
2025-11-24  9:10   ` [tip: objtool/core] " tip-bot2 for Alexandre Chartre
2025-11-24 10:43   ` tip-bot2 for Alexandre Chartre
2025-11-24 19:49   ` tip-bot2 for Alexandre Chartre
2025-11-21 10:36 ` [PATCH v6 00/30] objtool: Function validation tracing Peter Zijlstra
2025-11-21 13:16   ` Alexandre Chartre
2025-11-21 13:51     ` Alexandre Chartre
2025-11-21 14:30       ` Peter Zijlstra
2025-11-24 11:04 ` Borislav Petkov
2025-11-24 11:39   ` Alexandre Chartre
2025-11-24 12:39     ` Borislav Petkov
2025-11-24 12:27   ` David Laight

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox