Skip to content

Commit c02b769

Browse files
widen bitfields whose declared width exceeds their base type
clang2py can emit a bitfield typed as a smaller ctype than its own declared width (e.g. a 15-bit field typed ctypes.c_ubyte, which only has 8 bits) - ctypes rejects these with 'ValueError: number of bits invalid for bit field'. Surfaced by struct_vmbus_channel_offer_channel on a kernel with Hyper-V support enabled (not present locally, but present on GitHub Actions' Azure-hosted runners). Generalizes the existing c_bool-specific workaround to all integer ctypes, widening to the smallest standard type that fits.
1 parent 559d057 commit c02b769

1 file changed

Lines changed: 48 additions & 7 deletions

File tree

tools/vmlinux-gen.py

Lines changed: 48 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -247,14 +247,55 @@ def step5_postprocess(self, input_file):
247247
# Replace ('_20', ctypes.c_char, 8) with ('_20', ctypes.c_uint8, 8)
248248
data = re.sub(r"(ctypes\.c_char)(\s*,\s*\d+\))", r"ctypes.c_uint8\2", data)
249249

250-
# below to replace those c_bool with bitfield greater than 8
251-
def repl(m):
252-
name, bits = m.groups()
253-
return (
254-
f"('{name}', ctypes.c_uint32, {bits})" if int(bits) > 8 else m.group(0)
255-
)
250+
# Some bitfields come out of clang2py with a declared width that
251+
# exceeds their own base type's bit width (e.g. a 15-bit field typed
252+
# as ctypes.c_ubyte, which only has 8 bits) - ctypes rejects these
253+
# outright with "ValueError: number of bits invalid for bit field".
254+
# Widen the base type to the smallest standard integer type that can
255+
# actually hold the declared width.
256+
bitfield_type_widths = {
257+
"c_bool": 8,
258+
"c_byte": 8,
259+
"c_ubyte": 8,
260+
"c_int8": 8,
261+
"c_uint8": 8,
262+
"c_short": 16,
263+
"c_ushort": 16,
264+
"c_int16": 16,
265+
"c_uint16": 16,
266+
"c_int": 32,
267+
"c_uint": 32,
268+
"c_int32": 32,
269+
"c_uint32": 32,
270+
"c_long": 64,
271+
"c_ulong": 64,
272+
"c_longlong": 64,
273+
"c_ulonglong": 64,
274+
"c_int64": 64,
275+
"c_uint64": 64,
276+
}
277+
promoted_type_for_width = {
278+
8: "c_uint8",
279+
16: "c_uint16",
280+
32: "c_uint32",
281+
64: "c_uint64",
282+
}
256283

257-
data = re.sub(r"\('([^']+)',\s*ctypes\.c_bool,\s*(\d+)\)", repl, data)
284+
def widen_oversized_bitfields(m):
285+
name, base_type, bits = m.group(1), m.group(2), int(m.group(3))
286+
type_width = bitfield_type_widths.get(base_type)
287+
if type_width is None or bits <= type_width:
288+
return m.group(0)
289+
for width in (8, 16, 32, 64):
290+
if bits <= width:
291+
return f"('{name}', ctypes.{promoted_type_for_width[width]}, {bits})"
292+
return m.group(0)
293+
294+
data = re.sub(
295+
r"\('([^']+)',\s*ctypes\.([a-zA-Z0-9_]+),\s*(\d+)\)",
296+
widen_oversized_bitfields,
297+
data,
298+
)
258299

259300
# Remove ctypes. prefix from invalid entries
260301
invalid_ctypes = ["bpf_iter_state", "_cache_type", "fs_context_purpose"]

0 commit comments

Comments
 (0)