-
Notifications
You must be signed in to change notification settings - Fork 3.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Miscalulation of offset with consecutive bitfields #4890
Comments
the full reproducer can be found here, and below is the LLVM IR of the program for completeness. As far as I can tell the IR looks correct.
|
@shunghsiyu thanks for reporting. This is not a compiler bug but rather it is an unfortunate situation with the interaction between x86 IR and BPF backend. Note that currently, currently, the original bpf program is compiled first with x86 arch. The reason is we need x86 headers in compiling bpf programs. Once IR is generated, the x86 IR will feed into bpf backend. In certain situations, this may cause the problem. The following is a simple example to demonstrate:
The compilation steps:
With llvm17, the llvm with x86 target has Since BPF does not have i192, it rounds up to alignment 16 and this caused a problem. The fix is rather simple, do not use bitfield and proper alignments will be the same for both x86 and bpf. |
Thanks for the explanation. So if I understand correctly the reason that
On the other hand Is the above correct? |
Your interpretation largely correct. I didn't dig out in llvm why for i192 type BPF backend put an alignment of 16 though. This probably related to how to handle i<> -> alignment in the arch-specific string. |
I think I read on the LLVM's Phabricator archive that "the alignment of the largest defined type ( |
I'm still trying to figure out from the original bug report why consecutive bitfields are used in the first place, and whether it is strictly necessary. Let's close this issue for now, and I'll reopen if needed. Thanks! |
The consecutive bitfields came from From the existence of libbpf-tools I would guess that bcc is not meant to be used with But so far I haven't found reference that confirm or deny the above guess. @yonghong-song do yon know if this is the case? It'd be nice to get an explicit statement on this subject. Thanks! |
Yes, bcc won't use vmlinux.h and libbpf-tools is using vmlinux.h. |
With commit f35dae0 "adjust layout string in JIT with llvm11 128bit spec support", the alignment of i128 type in LLVM's BPF backend is explicitly defined, which made the alignment different from the those used in LLVM's x86 IR. Such alignment difference can result in incorrect offset, for example: struct my_struct { long: 64; long: 64; long: 64; void* ptr; }; int kprobe__netif_rx(struct pt_regs *ctx) { struct event_t event = {}; void* p1 = (void*)ctx->di; void* p2 = NULL; event.offset1 = ((long)&((struct my_struct*)p1)->ptr) - (long)p1; event.offset2 = ((long)&((struct my_struct*)p2)->ptr) - (long)p2; events.perf_submit(ctx, &event, sizeof(event)); return 0; } Will produce a BPF program seen below, where the struct my_struct.ptr field is calculated to be 32 for p1, but 24 for p2, which is absurd because both are suppose to be the offset of the same field. Disassembly of function kprobe__netif_rx ; { // Line 34 0: b7 02 00 00 18 00 00 00 r2 = 24 ; event.offset2 = (long)&((struct my_struct*)p2)->ptr - (long)p2; // Line 41 1: 7b 2a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r2 2: b7 02 00 00 20 00 00 00 r2 = 32 ; event.offset1 = (long)&((struct my_struct*)p1)->ptr - (long)p1; // Line 40 3: 7b 2a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r2 ; bpf_perf_event_output(ctx, (void *)bpf_pseudo_fd(1, -1), CUR_CPU_IDENTIFIER, &event, sizeof(event)); // Line 42 ... This happens because the three consecutive `long :64` anonymous bitfields were merged into a single i192 type, which uses the alignment of the largest defined datatype, thus i128 for BPF and i64 for x86, which were 16 and 8, respectively. Since this mismatch in alignment is mainly an issue only where there's a large, undefined data type, we can simply pass the -ffine-grained-bitfield-access to Clang during the x86 IR pass to prevent consecutive bitfield from being merged, and thus workaround the issue. Link: iovisor#4890 Link: https://bugzilla.suse.com/show_bug.cgi?id=1219096
With commit f35dae0 "adjust layout string in JIT with llvm11 128bit spec support", the alignment of i128 type in LLVM's BPF backend is explicitly defined, which made the alignment different from the those used in LLVM's x86 IR. Such alignment difference can result in incorrect offset, for example: struct my_struct { long: 64; long: 64; long: 64; void* ptr; }; int kprobe__netif_rx(struct pt_regs *ctx) { struct event_t event = {}; void* p1 = (void*)ctx->di; void* p2 = NULL; event.offset1 = ((long)&((struct my_struct*)p1)->ptr) - (long)p1; event.offset2 = ((long)&((struct my_struct*)p2)->ptr) - (long)p2; events.perf_submit(ctx, &event, sizeof(event)); return 0; } Will produce a BPF program seen below, where the struct my_struct.ptr field is calculated to be 32 for p1, but 24 for p2, which is absurd because both are suppose to be the offset of the same field. Disassembly of function kprobe__netif_rx ; { // Line 34 0: b7 02 00 00 18 00 00 00 r2 = 24 ; event.offset2 = (long)&((struct my_struct*)p2)->ptr - (long)p2; // Line 41 1: 7b 2a f8 ff 00 00 00 00 *(u64 *)(r10 - 8) = r2 2: b7 02 00 00 20 00 00 00 r2 = 32 ; event.offset1 = (long)&((struct my_struct*)p1)->ptr - (long)p1; // Line 40 3: 7b 2a f0 ff 00 00 00 00 *(u64 *)(r10 - 16) = r2 ; bpf_perf_event_output(ctx, (void *)bpf_pseudo_fd(1, -1), CUR_CPU_IDENTIFIER, &event, sizeof(event)); // Line 42 ... This happens because the three consecutive `long :64` anonymous bitfields were merged into a single i192 type, which uses the alignment of the largest defined datatype, thus i128 for BPF and i64 for x86, which were 16 and 8, respectively. Since this mismatch in alignment is mainly an issue only where there's a large, undefined data type, we can simply pass the -ffine-grained-bitfield-access to Clang during the x86 IR pass to prevent consecutive bitfield from being merged, and thus workaround the issue. Link: iovisor#4890 Link: https://bugzilla.suse.com/show_bug.cgi?id=1219096
FWIW we end up working around this by passing the |
With latest bcc v0.29.1 and LLVM 17.0.6. bcc will produce a BPF program with incorrect offset for
struct my_struct.ptr
in the following (incomplete) snipped:where the produced BPF program is
In the above we can see
offset1 == 32
whileoffset2 == 24
despite both are calculating the offset ofptr
.Bisect landed me on f35dae07 (merged in #2863) which was meant to address #2827 by introducing i128 type to the BPF layout spec (supported since LLVM 11); but I don't know what's the proper fix here.
To my best knowledge this has to do with the consecutive bitfields merging together and become a singe
i192
type, which will use the alignment of the largest defined type (i64:64
/8 bytes before andi128:128
/16 bytes after f35dae07).Note: LLVM release 18.1.0-rc1 has llvm/llvm-project@a21abc7 that made the issue go away for me. However, I cannot judge whether it is the proper fix in this case.
(CC @yonghong-song)
The text was updated successfully, but these errors were encountered: