Skip to content

Commit 4ff3ce2

Browse files
r41k0uclaude
andcommitted
Core: Emit nested CO-RE access strings for anonymous members
A field lifted out of an anonymous member needs an access string that walks into that member, so `struct pt_regs.cs` is `llvm.pt_regs:0:136$0:17:0` (member 0 of anonymous member 17) rather than the flat `$0:<index>` form. This is byte-for-byte what clang emits for the equivalent C, verified against `__builtin_preserve_access_index(ctx->cs)` on a struct with the same shape, and the indices and offsets agree with `bpftool btf dump file /sys/kernel/btf/vmlinux`. Only fields carrying an access path take the new path; every other field keeps the existing flat and indexed/array forms unchanged. The only .ll diff across all vmlinux/xdp test programs is four brand new globals for cs, csx, ss and ssx. No pre-existing relocation string changes. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
1 parent f2ec707 commit 4ff3ce2

1 file changed

Lines changed: 20 additions & 7 deletions

File tree

pythonbpf/vmlinux_parser/ir_gen/ir_generation.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,6 @@ def struct_processor(self, struct, processing_stack=None):
8585
# Create a members dictionary for AssignmentInfo
8686
members_dict = {}
8787
for field_name, field in struct.fields.items():
88-
if field.access_path is not None:
89-
# Member lifted out of an anonymous member. gen_ir does
90-
# not emit a global for it yet, so it stays unexported.
91-
continue
9288
# Get the generated field name from our dictionary, or use field_name if not found
9389
if (
9490
struct.name in self.generated_field_names
@@ -137,8 +133,18 @@ def gen_ir(self, struct, generated_debug_info):
137133

138134
for field_name, field in struct.fields.items():
139135
if field.access_path is not None:
140-
# Member lifted out of an anonymous member. It is not a
141-
# top-level field, so it must not consume a field index.
136+
# Member lifted out of an anonymous member. Its access string
137+
# comes from the recorded path, and it must not consume a
138+
# top-level field index.
139+
field_co_re_name, returned = self._struct_name_generator(
140+
struct, field, field.access_path[0]
141+
)
142+
globvar = ir.GlobalVariable(
143+
self.llvm_module, ir.IntType(64), name=field_co_re_name
144+
)
145+
globvar.linkage = "external"
146+
globvar.set_metadata("llvm.preserve.access.index", debug_info)
147+
self.generated_field_names[struct.name][field_name] = globvar
142148
continue
143149
# does not take arrays and similar types into consideration yet.
144150
if callable(field.ctype_complex_type):
@@ -271,12 +277,19 @@ def _struct_name_generator(
271277
)
272278
return name, True
273279
elif struct.name.startswith("struct_"):
280+
if field.access_path is not None:
281+
# Field lifted out of an anonymous member: the access string has
282+
# to walk into the anonymous member, e.g. `0:17:0` for
283+
# `struct pt_regs.cs`, which is member 0 of anonymous member 17.
284+
access_string = ":".join(str(index) for index in field.access_path)
285+
else:
286+
access_string = str(field_index)
274287
name = (
275288
"llvm."
276289
+ struct.name.removeprefix("struct_")
277290
+ f":0:{field.offset}"
278291
+ "$"
279-
+ f"0:{field_index}"
292+
+ f"0:{access_string}"
280293
)
281294
return name, True
282295
else:

0 commit comments

Comments
 (0)