* objtool failure caused some kernel functionality not working
@ 2025-01-30 7:10 Yonghong Song
2025-01-30 7:21 ` Yonghong Song
2025-01-30 8:25 ` Josh Poimboeuf
0 siblings, 2 replies; 5+ messages in thread
From: Yonghong Song @ 2025-01-30 7:10 UTC (permalink / raw)
To: Josh Poimboeuf, Peter Zijlstra; +Cc: Song Liu, LKML, yonghong.song, Kernel Team
In Meta, when I tried to use llvm19 compiler to build a kernel with PGO
([1]) support for 6.13 kernel, Ihit the following objtool warning:
warning: objtool: __htab_map_lookup_elem+0x3fb: can't find switch jump
table
The kernel is built successfully, but after booting the kernel,
/sys/kernel/debug/tracing/available_filter_functionsand
/sys/kernel/debug/tracing/available_filter_functions_addrs are empty. I
did some investigation and found that the warning is at func
add_jump_table(): static int add_jump_table(struct objtool_file *file,
struct instruction *insn, struct reloc *next_table) { struct symbol
*pfunc = insn_func(insn)->pfunc; struct reloc *table =
insn_jump_table(insn); struct instruction *dest_insn; unsigned int
prev_offset = 0; struct reloc *reloc = table; struct alternative *alt;
/* * Each @reloc is a switch table relocation which points to the target
* instruction. */ for_each_reloc_from(table->sec, reloc) { /* Check for
the end of the table: */ if (reloc != table && reloc == next_table)
break; /* Make sure the table entries are consecutive: */ if
(prev_offset && reloc_offset(reloc) != prev_offset + 8) break; /* Detect
function pointers from contiguous objects: */ if (reloc->sym->sec ==
pfunc->sec && reloc_addend(reloc) == pfunc->offset) break; dest_insn =
find_insn(file, reloc->sym->sec, reloc_addend(reloc)); <===== find_insn
return a NULL dest_insn with prev_offset == 0 if (!dest_insn) break; /*
Make sure the destination is in the same function: */ if
(!insn_func(dest_insn) || insn_func(dest_insn)->pfunc != pfunc) break;
alt = malloc(sizeof(*alt)); if (!alt) { WARN("malloc failed"); return
-1; } alt->insn = dest_insn; alt->next = insn->alts; insn->alts = alt;
prev_offset = reloc_offset(reloc); } if (!prev_offset) { WARN_INSN(insn,
"can't find switch jump table"); <===== error message here. return -1; }
return 0; } The find_insn failed for 'prev_offset == 0' at the second
iteration and finally the function return -1, which propagated to
decode_sections() in func check(). Since decode_sections() got an error
ret the later create_mcount_loc_sections() will not be called. This
caused the above available_filter_functions issue.
The following are some codes related to func__htab_map_lookup_elem():
Disassembly of section .text.hot.__htab_map_lookup_elem:
0000000000000000 <__htab_map_lookup_elem>:
0: e8 00 00 00 00 callq 0x5 <__htab_map_lookup_elem+0x5>
0000000000000001: R_X86_64_PLT32 __fentry__-0x4
5: 55 pushq %rbp
...
3f3: e9 03 fe ff ff jmp 0x1fb <__htab_map_lookup_elem+0x1fb>
3f8: 41 ff c8 decl %r8d
3fb: 42 ff 24 c5 00 00 00 00 jmpq *(,%r8,8)
00000000000003ff: R_X86_64_32S .rodata.hot.__htab_map_lookup_elem
403: 44 0f b6 42 0a movzbl 0xa(%rdx), %r8d
408: 41 c1 e0 10 shll $0x10, %r8d
...
45b: 44 0f b6 42 02 movzbl 0x2(%rdx), %r8d
460: 41 c1 e0 10 shll $0x10, %r8d
464: 44 01 c0 addl %r8d, %eax
467: e9 26 fe ff ff jmp 0x292 <__htab_map_lookup_elem+0x292>
46c: 41 89 d8 movl %ebx, %r8d
46f: 89 c1 movl %eax, %ecx
471: 89 c5 movl %eax, %ebp
473: 48 89 f2 movq %rsi, %rdx
476: e9 0d fe ff ff jmp 0x288 <__htab_map_lookup_elem+0x288>
47b: 44 0f b6 42 0b movzbl 0xb(%rdx), %r8d
480: 41 c1 e0 18 shll $0x18, %r8d
484: 44 01 c5 addl %r8d, %ebp
487: e9 77 ff ff ff jmp 0x403 <__htab_map_lookup_elem+0x403>
Disassembly of section .text.unlikely.bpf_percpu_hash_update:
So the func __htab_map_lookup_elem() size is 0x48c.
Here, insn 0x3fb is a jump table insn, the following is the actual jump table:
RELOCATION RECORDS FOR [.rodata.hot.__htab_map_lookup_elem]:
OFFSET TYPE VALUE
0000000000000000 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x29e
0000000000000008 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x48c
<==== problematic one
0000000000000010 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x45b
0000000000000018 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x44f
0000000000000020 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x447
0000000000000028 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x43b
0000000000000030 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x42f
0000000000000038 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x423
0000000000000040 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x41b
0000000000000048 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x40f
0000000000000050 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x403
0000000000000058 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x47b
Note that for the second entry which has .text.hot.__htab_map_lookup_elem+0x48c
which is right after the last insn. This caused the problem for objtool since
jump target is not within the function, rather immediately after the function.
There are quite some similar cases in my build.
The llvm18 does not have issues. I tried to bisect what changed in llvm19 and
found the following llvm patch is responsible:
https://github.com/llvm/llvm-project/pull/96089
Basically, the compiler might be able to create a jump target which actually
not possible at runtime. For example, in one of examples in the above llvm patch, something
like
if i >= 3 goto out; /* i unsigned */
switch i, label default_unreachable:
case 0: goto label1;
case 1: goto label2;
case 2: goto label3;
label1: ...; return;
label2: ...; return;
label3: ...; return;
default_unreachable:
I think that this should be a valid code from compiler perspective.
Can we fix objtool to handle jump target which is immediately after the func body?Thanks, Yonghong
[1]
https://patchwork.kernel.org/project/linux-kbuild/patch/20210407211704.367039-1-morbo@google.com/#24246189
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: objtool failure caused some kernel functionality not working
2025-01-30 7:10 objtool failure caused some kernel functionality not working Yonghong Song
@ 2025-01-30 7:21 ` Yonghong Song
2025-01-30 8:25 ` Josh Poimboeuf
1 sibling, 0 replies; 5+ messages in thread
From: Yonghong Song @ 2025-01-30 7:21 UTC (permalink / raw)
To: Josh Poimboeuf, Peter Zijlstra; +Cc: Song Liu, LKML, Kernel Team
On 1/29/25 11:10 PM, Yonghong Song wrote:
> In Meta, when I tried to use llvm19 compiler to build a kernel with
> PGO ([1]) support for 6.13 kernel, Ihit the following objtool warning:
> warning: objtool: __htab_map_lookup_elem+0x3fb: can't find switch
> jump table
>
> The kernel is built successfully, but after booting the kernel,
> /sys/kernel/debug/tracing/available_filter_functionsand
> /sys/kernel/debug/tracing/available_filter_functions_addrs are empty.
> I did some investigation and found that the warning is at func
> add_jump_table(): static int add_jump_table(struct objtool_file *file,
> struct instruction *insn, struct reloc *next_table) { struct symbol
> *pfunc = insn_func(insn)->pfunc; struct reloc *table =
> insn_jump_table(insn); struct instruction *dest_insn; unsigned int
> prev_offset = 0; struct reloc *reloc = table; struct alternative *alt;
> /* * Each @reloc is a switch table relocation which points to the
> target * instruction. */ for_each_reloc_from(table->sec, reloc) { /*
> Check for the end of the table: */ if (reloc != table && reloc ==
> next_table) break; /* Make sure the table entries are consecutive: */
> if (prev_offset && reloc_offset(reloc) != prev_offset + 8) break; /*
> Detect function pointers from contiguous objects: */ if
> (reloc->sym->sec == pfunc->sec && reloc_addend(reloc) ==
> pfunc->offset) break; dest_insn = find_insn(file, reloc->sym->sec,
> reloc_addend(reloc)); <===== find_insn return a NULL dest_insn with
> prev_offset == 0 if (!dest_insn) break; /* Make sure the destination
> is in the same function: */ if (!insn_func(dest_insn) ||
> insn_func(dest_insn)->pfunc != pfunc) break; alt =
> malloc(sizeof(*alt)); if (!alt) { WARN("malloc failed"); return -1; }
> alt->insn = dest_insn; alt->next = insn->alts; insn->alts = alt;
> prev_offset = reloc_offset(reloc); } if (!prev_offset) {
> WARN_INSN(insn, "can't find switch jump table"); <===== error message
> here. return -1; } return 0; } The find_insn failed for 'prev_offset
> == 0' at the second iteration and finally the function return -1,
> which propagated to decode_sections() in func check(). Since
> decode_sections() got an error ret the later
> create_mcount_loc_sections() will not be called. This caused the above
> available_filter_functions issue.
Sorry for the mess-up for the above code. The summary is:
The kernel is built successfully, but after booting the kernel,
/sys/kernel/debug/tracing/available_filter_functions and
/sys/kernel/debug/tracing/available_filter_functions_addrs
are empty. I did some investigation and found that the warning is at func
add_jump_table().
The find_insn() failed for 'prev_offset == 0' at the second
iteration and finally the function return -1, which propagated to
decode_sections() in func check(). Since decode_sections() got an error
ret the later create_mcount_loc_sections() will not be called. This
caused the above available_filter_functions issue.
>
> The following are some codes related to func__htab_map_lookup_elem():
>
> Disassembly of section .text.hot.__htab_map_lookup_elem:
> 0000000000000000 <__htab_map_lookup_elem>:
> 0: e8 00 00 00 00 callq 0x5
> <__htab_map_lookup_elem+0x5>
> 0000000000000001: R_X86_64_PLT32 __fentry__-0x4
> 5: 55 pushq %rbp
> ...
> 3f3: e9 03 fe ff ff jmp 0x1fb
> <__htab_map_lookup_elem+0x1fb>
> 3f8: 41 ff c8 decl %r8d
> 3fb: 42 ff 24 c5 00 00 00 00 jmpq *(,%r8,8)
> 00000000000003ff: R_X86_64_32S
> .rodata.hot.__htab_map_lookup_elem
> 403: 44 0f b6 42 0a movzbl 0xa(%rdx), %r8d
> 408: 41 c1 e0 10 shll $0x10, %r8d
> ...
> 45b: 44 0f b6 42 02 movzbl 0x2(%rdx), %r8d
> 460: 41 c1 e0 10 shll $0x10, %r8d
> 464: 44 01 c0 addl %r8d, %eax
> 467: e9 26 fe ff ff jmp 0x292
> <__htab_map_lookup_elem+0x292>
> 46c: 41 89 d8 movl %ebx, %r8d
> 46f: 89 c1 movl %eax, %ecx
> 471: 89 c5 movl %eax, %ebp
> 473: 48 89 f2 movq %rsi, %rdx
> 476: e9 0d fe ff ff jmp 0x288
> <__htab_map_lookup_elem+0x288>
> 47b: 44 0f b6 42 0b movzbl 0xb(%rdx), %r8d
> 480: 41 c1 e0 18 shll $0x18, %r8d
> 484: 44 01 c5 addl %r8d, %ebp
> 487: e9 77 ff ff ff jmp 0x403
> <__htab_map_lookup_elem+0x403>
>
> Disassembly of section .text.unlikely.bpf_percpu_hash_update:
>
> So the func __htab_map_lookup_elem() size is 0x48c.
>
> Here, insn 0x3fb is a jump table insn, the following is the actual
> jump table:
> RELOCATION RECORDS FOR [.rodata.hot.__htab_map_lookup_elem]:
> OFFSET TYPE VALUE
> 0000000000000000 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x29e
> 0000000000000008 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x48c
> <==== problematic one
> 0000000000000010 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x45b
> 0000000000000018 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x44f
> 0000000000000020 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x447
> 0000000000000028 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x43b
> 0000000000000030 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x42f
> 0000000000000038 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x423
> 0000000000000040 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x41b
> 0000000000000048 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x40f
> 0000000000000050 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x403
> 0000000000000058 R_X86_64_64 .text.hot.__htab_map_lookup_elem+0x47b
>
> Note that for the second entry which has
> .text.hot.__htab_map_lookup_elem+0x48c
> which is right after the last insn. This caused the problem for
> objtool since
> jump target is not within the function, rather immediately after the
> function.
>
> There are quite some similar cases in my build.
>
> The llvm18 does not have issues. I tried to bisect what changed in
> llvm19 and
> found the following llvm patch is responsible:
>
> https://github.com/llvm/llvm-project/pull/96089
>
> Basically, the compiler might be able to create a jump target which
> actually
> not possible at runtime. For example, in one of examples in the above
> llvm patch, something
> like
> if i >= 3 goto out; /* i unsigned */
> switch i, label default_unreachable:
> case 0: goto label1;
> case 1: goto label2;
> case 2: goto label3;
> label1: ...; return;
> label2: ...; return;
> label3: ...; return;
> default_unreachable:
>
> I think that this should be a valid code from compiler perspective.
>
> Can we fix objtool to handle jump target which is immediately after
> the func body?Thanks, Yonghong
>
>
> [1]
> https://patchwork.kernel.org/project/linux-kbuild/patch/20210407211704.367039-1-morbo@google.com/#24246189
^ permalink raw reply [flat|nested] 5+ messages in thread* Re: objtool failure caused some kernel functionality not working
2025-01-30 7:10 objtool failure caused some kernel functionality not working Yonghong Song
2025-01-30 7:21 ` Yonghong Song
@ 2025-01-30 8:25 ` Josh Poimboeuf
2025-01-30 16:30 ` Yonghong Song
1 sibling, 1 reply; 5+ messages in thread
From: Josh Poimboeuf @ 2025-01-30 8:25 UTC (permalink / raw)
To: Yonghong Song; +Cc: Peter Zijlstra, Song Liu, LKML, Kernel Team
On Wed, Jan 29, 2025 at 11:10:14PM -0800, Yonghong Song wrote:
> The llvm18 does not have issues. I tried to bisect what changed in llvm19 and
> found the following llvm patch is responsible:
>
> https://github.com/llvm/llvm-project/pull/96089
>
> Basically, the compiler might be able to create a jump target which actually
> not possible at runtime. For example, in one of examples in the above llvm patch, something
> like
> if i >= 3 goto out; /* i unsigned */
> switch i, label default_unreachable:
> case 0: goto label1;
> case 1: goto label2;
> case 2: goto label3;
> label1: ...; return;
> label2: ...; return;
> label3: ...; return;
> default_unreachable:
>
> I think that this should be a valid code from compiler perspective.
>
> Can we fix objtool to handle jump target which is immediately after the func body?Thanks, Yonghong
I actually have a fix for that, can you try this?
https://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git/commit/?h=objtool/core&id=fbb454b7bb39955c324693e73a5cf7e448632553
--
Josh
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: objtool failure caused some kernel functionality not working
2025-01-30 8:25 ` Josh Poimboeuf
@ 2025-01-30 16:30 ` Yonghong Song
2025-02-08 2:16 ` Josh Poimboeuf
0 siblings, 1 reply; 5+ messages in thread
From: Yonghong Song @ 2025-01-30 16:30 UTC (permalink / raw)
To: Josh Poimboeuf; +Cc: Peter Zijlstra, Song Liu, LKML, Kernel Team
On 1/30/25 12:25 AM, Josh Poimboeuf wrote:
> On Wed, Jan 29, 2025 at 11:10:14PM -0800, Yonghong Song wrote:
>> The llvm18 does not have issues. I tried to bisect what changed in llvm19 and
>> found the following llvm patch is responsible:
>>
>> https://github.com/llvm/llvm-project/pull/96089
>>
>> Basically, the compiler might be able to create a jump target which actually
>> not possible at runtime. For example, in one of examples in the above llvm patch, something
>> like
>> if i >= 3 goto out; /* i unsigned */
>> switch i, label default_unreachable:
>> case 0: goto label1;
>> case 1: goto label2;
>> case 2: goto label3;
>> label1: ...; return;
>> label2: ...; return;
>> label3: ...; return;
>> default_unreachable:
>>
>> I think that this should be a valid code from compiler perspective.
>>
>> Can we fix objtool to handle jump target which is immediately after the func body?Thanks, Yonghong
> I actually have a fix for that, can you try this?
>
> https://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git/commit/?h=objtool/core&id=fbb454b7bb39955c324693e73a5cf7e448632553
I applied the above fix and now the kernel works fine and /sys/kernel/debug/tracing/available_filter_functions
can display tracable functions properly.
The fix is in your personal branch. Any plan to send it to upstream repo?
The fix also makes sense to me. So
Tested-by: Yonghong Song <yonghong.song@linux.dev>
Acked-by: Yonghong Song <yonghong.song@linux.dev>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: objtool failure caused some kernel functionality not working
2025-01-30 16:30 ` Yonghong Song
@ 2025-02-08 2:16 ` Josh Poimboeuf
0 siblings, 0 replies; 5+ messages in thread
From: Josh Poimboeuf @ 2025-02-08 2:16 UTC (permalink / raw)
To: Yonghong Song; +Cc: Peter Zijlstra, Song Liu, LKML, Kernel Team
On Thu, Jan 30, 2025 at 08:30:52AM -0800, Yonghong Song wrote:
> > I actually have a fix for that, can you try this?
> >
> > https://git.kernel.org/pub/scm/linux/kernel/git/jpoimboe/linux.git/commit/?h=objtool/core&id=fbb454b7bb39955c324693e73a5cf7e448632553
>
> I applied the above fix and now the kernel works fine and /sys/kernel/debug/tracing/available_filter_functions
> can display tracable functions properly.
>
> The fix is in your personal branch. Any plan to send it to upstream repo?
>
> The fix also makes sense to me. So
>
> Tested-by: Yonghong Song <yonghong.song@linux.dev>
> Acked-by: Yonghong Song <yonghong.song@linux.dev>
Yes, planning to move that patch into the -tip tree soon.
Peter, are you able to do that? I have it in my objtool/core branch
right now but maybe it should actually go into tip urgent?
--
Josh
^ permalink raw reply [flat|nested] 5+ messages in thread
end of thread, other threads:[~2025-02-08 2:16 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-01-30 7:10 objtool failure caused some kernel functionality not working Yonghong Song
2025-01-30 7:21 ` Yonghong Song
2025-01-30 8:25 ` Josh Poimboeuf
2025-01-30 16:30 ` Yonghong Song
2025-02-08 2:16 ` Josh Poimboeuf
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox