From e379a9c6fb3f5a54f423a144228e006965888859 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 12 Oct 2023 14:23:28 +0100 Subject: [PATCH 1/4] lisa._btf: Fix comment FIX --- lisa/_btf.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lisa/_btf.py b/lisa/_btf.py index a10e0d31c..9a2b16012 100644 --- a/lisa/_btf.py +++ b/lisa/_btf.py @@ -1735,11 +1735,11 @@ def _dedup_names(typs): for typ in typs: if isinstance(typ, BTFTypedef): cat = typedef_names - # BTFForwardDecl are not renamed since we don't know what they - # logically point to. It could be any of the types that share that - # name, or yet another unknown. Fortunately, they are kind of useless - # since we will create any actually needed forward decl when dumping C - # code. + # BTFForwardDecl are renamed independently from the type they declare + # since we don't know what they logically point to. It could be any of + # the types that share that name, or yet another unknown. Fortunately, + # they are kind of useless since we will create any actually needed + # forward decl when dumping C code. elif isinstance(typ, (BTFStruct, BTFUnion, BTFEnum)): cat = tagged_names # We still dedup names there, in case they end up being printed and -- GitLab From a0b35580a9cb40e10f8f16319b269faf7df5229e Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 12 Oct 2023 17:35:51 +0100 Subject: [PATCH 2/4] lisa._btf: Ensure the BTF types are dumped as deeply as possible when forward decls are used FIX Avoid accidentally stopping the recursive type printing when encountering a forward declerence, since user code might be following a pointer chain (a->b->c) where "b" is of an internal type, and suddenly overnight becomes a forward decl, making "b->c" break. Instead, resolve forward decl to the type they reference. This is inaccurate since "struct foo;" is not necessarily a forward declaration of "struct foo {...};" as they could both appear in totally unrelated files. However, the risk of that happening _in the set of types_ we care about is quite low, and is lower than the risk of having forward decl popping up randomly. --- .../kmodules/lisa/introspect_header.py | 43 +++++++++++++++++-- lisa/_btf.py | 6 +++ 2 files changed, 46 insertions(+), 3 deletions(-) diff --git a/lisa/_assets/kmodules/lisa/introspect_header.py b/lisa/_assets/kmodules/lisa/introspect_header.py index eedd77ac0..bce6ee5a6 100755 --- a/lisa/_assets/kmodules/lisa/introspect_header.py +++ b/lisa/_assets/kmodules/lisa/introspect_header.py @@ -22,7 +22,7 @@ import abc import sys import itertools import argparse -from collections import namedtuple, deque +from collections import namedtuple, deque, defaultdict import functools import json import re @@ -80,8 +80,20 @@ def process_btf(out, path, introspect, internal_type_prefix, define_typ_names): else: return rename(name) + tagged = { + cls: defaultdict(set) + for cls in (btf.BTFStruct, btf.BTFUnion, btf.BTFEnum) + } + fwd_decls = set() + for typ in reachable_typs: - if isinstance(typ, btf.BTFEnum): + for _cls, _typs in tagged.items(): + if isinstance(typ, _cls): + _typs[typ.name].add(typ) + + if isinstance(typ, btf.BTFForwardDecl): + fwd_decls.add(typ) + elif isinstance(typ, btf.BTFEnum): typ.enumerators = { ( name @@ -90,11 +102,36 @@ def process_btf(out, path, introspect, internal_type_prefix, define_typ_names): ): value for name, value in typ.enumerators.items() } + + resolved = {} + for typ in fwd_decls: + for _cls, _typs in tagged.items(): + if issubclass(typ.typ_cls, _cls): + try: + (target,) = _typs[typ.name] + except (KeyError, ValueError): + pass + else: + resolved[typ] = target + break + + # Resolve all the forward declarations we can to the type they point to. + # This prevents arbitrary breakage of code in pointer chains spanning + # multiple structs that were not explicitly asked by the user. + # + # The main risk is e.g. "struct foo;" forward decl being matched with a + # "struct foo {...};" that was done in another file and has nothing to do + # with it. But as long as we only print a subset of kernel types, we are + # unlikely to run into that, or at least less likely to run into that than + # running into broken pointer chains because of forward decls. + btf.BTFType.map_typs(lambda typ: resolved.get(typ, typ), reachable_typs) + + for typ in reachable_typs: if isinstance(typ, named_classes) and typ.name: typ.name = rename_internal_typ(typ) # Dump declaration for all the types we are interested in - btf.dump_c(define_typs, fileobj=out, introspection=False, decls=True) + btf.dump_c(reachable_typs, fileobj=out, introspection=False, decls=True) def is_exported_symbol(code): diff --git a/lisa/_btf.py b/lisa/_btf.py index 9a2b16012..5ce001b01 100644 --- a/lisa/_btf.py +++ b/lisa/_btf.py @@ -133,6 +133,12 @@ class BTFType(metaclass=_BTFTypeMeta): typ._map_typs(lambda x: x, visited=reachable_typs) return reachable_typs + @classmethod + def map_typs(cls, f, typs): + visited = set() + for typ in typs: + typ._map_typs(f, visited=visited) + def _map_typs(self, f, visited): if self in visited: return -- GitLab From 27d8377b3251ed3e2d0729fd98a3664389092294 Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 12 Oct 2023 18:38:28 +0100 Subject: [PATCH 3/4] lisa._btf: Fix nested anonymous struct/union dumping FIX Ensure that anonymous struct/union are dumped correctly as anonymous struct/union instead of being put behind a typdef, which breaks the semantic. --- lisa/_btf.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lisa/_btf.py b/lisa/_btf.py index 5ce001b01..a8aa5a63c 100644 --- a/lisa/_btf.py +++ b/lisa/_btf.py @@ -182,8 +182,8 @@ class _CDecl: def _do_dump_c_decls(self, ctx): pass - def _dump_c_decls(self, ctx, memoize=True, **kwargs): - if memoize: + def _dump_c_decls(self, ctx, **kwargs): + if kwargs.get('memoize', True): try: x = ctx._memo[self] except KeyError: @@ -483,7 +483,7 @@ class _BTFStructUnion(_CDecl, BTFType): for member in self.members: member._dump_c_introspection(ctx) - def _do_dump_c_decls(self, ctx, anonymous=False): + def _do_dump_c_decls(self, ctx, anonymous=False, memoize=True): members = self._all_members size = self.size kind = self._KIND -- GitLab From b4db88d056c1a437be0151b6fd59bac3a524256a Mon Sep 17 00:00:00 2001 From: Douglas Raillard Date: Thu, 12 Oct 2023 18:57:56 +0100 Subject: [PATCH 4/4] lisa._btf: Fix alignment inference FIX Fix alignment inference on packed structs to return 1 instead of what would be the alignment without __attribute__((packed)). --- lisa/_btf.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/lisa/_btf.py b/lisa/_btf.py index a8aa5a63c..44805a31e 100644 --- a/lisa/_btf.py +++ b/lisa/_btf.py @@ -738,21 +738,27 @@ class BTFStruct(_BTFStructUnion): @property def alignment(self): - alignment = self._alignment - if alignment is None: - max_alignment = self._max_alignment - # We have something weird going on, like a struct manually padded using - # anonymous bitfields at the end. - self._alignment = self._min_alignment if self.size % max_alignment else max_alignment - return self._alignment + size = self.size + max_alignment = self._max_alignment + + # We have something weird going on, like a struct manually padded using + # anonymous bitfields at the end. + if size % max_alignment: + return 1 else: - return alignment + alignment = self._alignment + if alignment is None: + # We have something weird going on, like a struct manually padded using + # anonymous bitfields at the end. + self._alignment = self._min_alignment if self.size % max_alignment else max_alignment + return self._alignment + else: + return alignment @property def _align_attribute(self): members = self.members size = self.size - alignment = self.alignment min_alignment = self._min_alignment max_alignment = self._max_alignment @@ -782,9 +788,10 @@ class BTFStruct(_BTFStructUnion): ) align = None else: + alignment = self.alignment padding = None align = f'aligned({alignment})' if alignment > min_alignment else '' - packed = '' + packed = None attrs = ','.join(attr for attr in (packed, align) if attr) return ( -- GitLab