Skip to content

Fix register state dump keyerror for register_state_dump test - #91

Merged
varun-r-mallya merged 8 commits into
masterfrom
fix/register-state-dump-keyerror
Aug 30, 2026
Merged

Fix register state dump keyerror for register_state_dump test#91
varun-r-mallya merged 8 commits into
masterfrom
fix/register-state-dump-keyerror

Conversation

@r41k0u

@r41k0u r41k0u commented Aug 9, 2026

Copy link
Copy Markdown
Collaborator

No description provided.

r41k0u and others added 7 commits August 7, 2026 04:43
has_field() consulted hasattr() on the ctypes class while get_field_type()
indexed the parsed members dict. For any member ctypes exposes but the
vmlinux parser never registered, has_field() said True and get_field_type()
then died with a raw KeyError.

Both now consult the parsed members dict, and a lookup that misses raises a
ValueError that distinguishes "no such field" from "field exists in
vmlinux.py but the parser does not support it yet".

get_field_index() additionally derives the index from the ctypes _fields_
list (the C declaration order) instead of the insertion order of the parsed
members dict, so it no longer depends on the two happening to coincide.

No change to generated IR: every vmlinux/xdp test program compiles to a
byte-identical .ll.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Carries the chain of member indices from the enclosing struct down to a
field, so a field that does not sit at the top level of the struct can still
describe where it lives. Defaults to None, which keeps meaning "this field is
a plain top-level member".

Pure data, nothing reads it yet, generated IR is unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
On this kernel `struct pt_regs` wraps `cs` and `ss` in anonymous unions.
ctypes lifts those names onto the parent class, exactly as C does, but the
parser only walked the top-level `_fields_`, so `cs` and `ss` were unknown to
the compiler and `ctx.cs` blew up.

Register the members of an anonymous member as fields of the parent, one
level deep, taking the ABSOLUTE offset ctypes reports on the parent (no
arithmetic) and recording the access path (anonymous member index, member
index). The anonymous member itself stays registered as before.

Deliberately conservative: only scalar ctypes members are flattened. Nested
vmlinux structs (`fred_cs`, `fred_ss`) and bitfields are skipped with a
warning rather than half-registered, and a flattened member never overwrites
an existing top-level field of the same name. Overlapping members are kept as
distinct fields, since `cs` and `csx` both legitimately live at offset 136.

Flattened fields are not yet emitted: IR generation and DWARF generation skip
them, and critically they do not consume a top-level field index, so existing
CO-RE names are untouched. Every vmlinux/xdp test program still compiles to a
byte-identical .ll. Entirely inert for any struct without `_anonymous_`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
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>
…p_md

Reading a context field narrower than a register was hardcoded to
struct_xdp_md's i32 fields in three places that had to agree with each other:
the destination alloca in allocation_pass, the zext in load_ctx_field, and
the store in assign_pass. struct pt_regs' cs and ss are 2 bytes, so a second
special case would have been needed.

Replace all three with the general rule: a context field is loaded at its
natural width and zero-extended to i64, so its destination is i64.
allocation_pass gains the same context discriminator load_ctx_field uses
(a context argument has no alloca of its own), so non-context field reads,
which go through load_struct_field and bpf_probe_read_kernel, keep their
natural width and are untouched.

struct_xdp_md's generated IR is byte-identical, as is every other vmlinux/xdp
test program's; the only diff in the whole corpus remains the four new globals
from the previous commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…solve

The previous commits emit `llvm.pt_regs:0:136$0:17:0` for `ctx.cs`, which llc
happily turns into a CO-RE relocation, but the local BTF it is resolved
against described member 17 as a NAMED, EMPTY composite:

    '_0' type_id=4 bits_offset=1088
    [4] STRUCT '(anon)' size=8 vlen=0

libbpf walks a CO-RE access string by member index into the local type and
then matches by field NAME in the target type, so both halves of that break
it: index 0 into a vlen=0 composite has nothing to land on, and a local
member called `_0` cannot be paired with the kernel's anonymous one. The
object would build cleanly and then fail to load.

Emit anonymous members the way a C compiler does: unnamed, with a real
DW_TAG_union_type (or structure, for an anonymous struct) carrying their
members in declaration order. The emitted BTF now reads

    '(anon)' type_id=4 bits_offset=1088
    [4] UNION '(anon)' size=8 vlen=3
            'cs' ... 'csx' ... 'fred_cs' ...

which is the same shape clang produces for the equivalent C and the same
shape `bpftool btf dump file /sys/kernel/btf/vmlinux` reports.

Gated on the struct declaring `_anonymous_`, and it bails back to the old
opaque member for anything it cannot describe faithfully. Across every
vmlinux/xdp test program the set of emitted globals, every relocation string
and every instruction are unchanged; the only diffs are the new DWARF nodes
and the DWARF node renumbering they cause, in the two pt_regs programs.

This is the one part of the fix that cannot be verified without loading a
program, so it is kept as its own commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…calar

Style only, no behaviour change and no IR change. Keeps `ruff check --select
SIM` clean on the files this branch touches.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@r41k0u
r41k0u requested a review from varun-r-mallya August 9, 2026 06:46
@varun-r-mallya
varun-r-mallya merged commit 03b1232 into master Aug 30, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants