Open
Description
Hi there,
I encountered incorrect bitfield access when tracing some linux kernel structure with BPF. Some attempt shows that generated code access bit fields inconsistent w/wo preserve_access_index. I managed to get a small reproducible case below:
//#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
//#pragma clang attribute push (__attribute__((preserve_access_index)), apply_to = record)
//#endif
struct S {
int x;
char y;
unsigned int a0:1;
unsigned int a1:1;
unsigned int a2:1;
unsigned int a3:1;
unsigned int a4:1;
unsigned int a5:1;
unsigned int a6:1;
unsigned int a7:1;
unsigned int b0:1;
unsigned int b1:1;
unsigned int b2:1;
unsigned int b3:1;
unsigned int b4:1;
unsigned int b5:1;
unsigned int b6:1;
unsigned int b7:1;
};
//#ifndef BPF_NO_PRESERVE_ACCESS_INDEX
//#pragma clang attribute pop
//#endif
unsigned int func1(struct S *s) {
return s->a0;
}
unsigned int func2(struct S *s) {
return s->b0;
}
Without the attribute, this produces:
$ clang-16 test.c --target=bpf -c -O3 -g
$ llvm-objdump-16 -d test.o
test.o: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <func1>:
0: 71 10 05 00 00 00 00 00 r0 = *(u8 *)(r1 + 0x5)
1: 57 00 00 00 01 00 00 00 r0 &= 0x1
2: 95 00 00 00 00 00 00 00 exit
0000000000000018 <func2>:
3: 71 10 06 00 00 00 00 00 r0 = *(u8 *)(r1 + 0x6)
4: 57 00 00 00 01 00 00 00 r0 &= 0x1
5: 95 00 00 00 00 00 00 00 exit
If I uncomment the attribute push lines, it generates:
$ clang-16 test.c --target=bpf -c -O3 -g
$ llvm-objdump-16 -d test.o
test.o: file format elf64-bpf
Disassembly of section .text:
0000000000000000 <func1>:
0: 71 10 05 00 00 00 00 00 r0 = *(u8 *)(r1 + 0x5)
1: 57 00 00 00 01 00 00 00 r0 &= 0x1
2: 95 00 00 00 00 00 00 00 exit
0000000000000018 <func2>:
3: b7 02 00 00 06 00 00 00 r2 = 0x6
4: 0f 21 00 00 00 00 00 00 r1 += r2
5: 71 10 01 00 00 00 00 00 r0 = *(u8 *)(r1 + 0x1)
6: 57 00 00 00 01 00 00 00 r0 &= 0x1
7: 95 00 00 00 00 00 00 00 exit
Compiler info:
$ clang-16 --version
Debian clang version 16.0.6 (15~deb12u1)
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Pahole output:
struct S {
int x; /* 0 4 */
char y; /* 4 1 */
/* Bitfield combined with previous fields */
unsigned int a0:1; /* 4: 8 4 */
unsigned int a1:1; /* 4: 9 4 */
unsigned int a2:1; /* 4:10 4 */
unsigned int a3:1; /* 4:11 4 */
unsigned int a4:1; /* 4:12 4 */
unsigned int a5:1; /* 4:13 4 */
unsigned int a6:1; /* 4:14 4 */
unsigned int a7:1; /* 4:15 4 */
unsigned int b0:1; /* 4:16 4 */
unsigned int b1:1; /* 4:17 4 */
unsigned int b2:1; /* 4:18 4 */
unsigned int b3:1; /* 4:19 4 */
unsigned int b4:1; /* 4:20 4 */
unsigned int b5:1; /* 4:21 4 */
unsigned int b6:1; /* 4:22 4 */
unsigned int b7:1; /* 4:23 4 */
/* size: 8, cachelines: 1, members: 18 */
/* bit_padding: 8 bits */
/* last cacheline: 8 bytes */
};
I tried 17.0.2, which also shows similar result. An interesting thing is that if I remove the hole after y, everything becomes correct.
Seems a bug to me. Please let me know if there is anything else I can provide.
best
Yan