On Wed, 2023-09-13 at 15:26 +0100, Alan Maguire wrote: > When a newer pahole is run on an older kernel, it often knows about BTF > kinds that the kernel does not support. This is a problem because the BTF > generated is then embedded in the kernel image and read, and if unknown > kinds are found, BTF handling fails and core BPF functionality is > unavailable. > > The scripts/pahole-flags.sh script enumerates the various pahole options > available associated with various versions of pahole, but the problem is > what matters in the case of an older kernel is the set of kinds the kernel > understands. Because recent features such as BTF_KIND_ENUM64 are added > by default (and only skipped if --skip_encoding_btf_* is set), BTF will > be created with these newer kinds that the older kernel cannot read. > This can be fixed by stable-backporting --skip options, but this is > cumbersome and would have to be done every time a new BTF kind is > introduced. > > Here instead we pre-process the DWARF information associated with the > target for BTF generation; if we find an enum with a BTF_KIND_MAX > value in the DWARF associated with the object, we use that to > determine the maximum BTF kind supported. Note that the enum > representation of BTF kinds starts for the 5.16 kernel; prior to this > The benefit of auto-detection is that no work is required for older > kernels when new kinds are added, and --skip_encoding options are > less needed. > > Signed-off-by: Alan Maguire <alan.maguire@xxxxxxxxxx> > --- > btf_encoder.c | 12 ++++++++++++ > dwarf_loader.c | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ > dwarves.h | 2 ++ > 3 files changed, 66 insertions(+) > > diff --git a/btf_encoder.c b/btf_encoder.c > index 65f6e71..98c7529 100644 > --- a/btf_encoder.c > +++ b/btf_encoder.c > @@ -1889,3 +1889,15 @@ struct btf *btf_encoder__btf(struct btf_encoder *encoder) > { > return encoder->btf; > } > + > +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max) > +{ > + if (btf_kind_max < 0 || btf_kind_max >= BTF_KIND_MAX) > + return; > + if (btf_kind_max < BTF_KIND_DECL_TAG) > + conf_load->skip_encoding_btf_decl_tag = true; > + if (btf_kind_max < BTF_KIND_TYPE_TAG) > + conf_load->skip_encoding_btf_type_tag = true; > + if (btf_kind_max < BTF_KIND_ENUM64) > + conf_load->skip_encoding_btf_enum64 = true; > +} > diff --git a/dwarf_loader.c b/dwarf_loader.c > index ccf3194..8984043 100644 > --- a/dwarf_loader.c > +++ b/dwarf_loader.c > @@ -3358,8 +3358,60 @@ static int __dwarf_cus__process_cus(struct dwarf_cus *dcus) > return 0; > } > > +/* Find enumeration value for BTF_KIND_MAX; replace conf_load->btf_kind_max with > + * this value if found since it indicates that the target object does not know > + * about kinds > its BTF_KIND_MAX value. This is valuable for kernel/module > + * BTF where a newer pahole/libbpf operate on an older kernel which cannot > + * parse some of the newer kinds pahole can generate. > + */ > +static void dwarf__find_btf_kind_max(struct dwarf_cus *dcus) > +{ > + struct conf_load *conf = dcus->conf; > + uint8_t pointer_size, offset_size; > + Dwarf_Off off = 0, noff; > + size_t cuhl; > + > + while (dwarf_nextcu(dcus->dw, off, &noff, &cuhl, NULL, &pointer_size, &offset_size) == 0) { > + Dwarf_Die die_mem; > + Dwarf_Die *cu_die = dwarf_offdie(dcus->dw, off + cuhl, &die_mem); > + Dwarf_Die child; > + > + if (cu_die == NULL) > + break; > + if (dwarf_child(cu_die, &child) == 0) { > + Dwarf_Die *die = &child; > + > + do { > + Dwarf_Die echild, *edie; > + > + if (dwarf_tag(die) != DW_TAG_enumeration_type || > + !dwarf_haschildren(die) || > + dwarf_child(die, &echild) != 0) > + continue; > + edie = &echild; > + do { > + const char *ename; > + int btf_kind_max; > + > + if (dwarf_tag(edie) != DW_TAG_enumerator) > + continue; > + ename = attr_string(edie, DW_AT_name, conf); > + if (!ename || strcmp(ename, "BTF_KIND_MAX") != 0) > + continue; > + btf_kind_max = attr_numeric(edie, DW_AT_const_value); Nitpick: attr_numeric() returns 0 in case of an error, when 0 is passed to dwarves__set_btf_kind_max() it would turn off all optional kinds. Probably should bail out on 0 instead. > + dwarves__set_btf_kind_max(conf, btf_kind_max); > + return; > + } while (dwarf_siblingof(edie, edie) == 0); > + } while (dwarf_siblingof(die, die) == 0); > + } > + off = noff; > + } > +} > + > static int dwarf_cus__process_cus(struct dwarf_cus *dcus) > { > + dwarf__find_btf_kind_max(dcus); > + > if (dcus->conf->nr_jobs > 1) > return dwarf_cus__threaded_process_cus(dcus); > > diff --git a/dwarves.h b/dwarves.h > index eb1a6df..f4d9347 100644 > --- a/dwarves.h > +++ b/dwarves.h > @@ -1480,4 +1480,6 @@ extern const char tabs[]; > #define DW_TAG_skeleton_unit 0x4a > #endif > > +void dwarves__set_btf_kind_max(struct conf_load *conf_load, int btf_kind_max); > + > #endif /* _DWARVES_H_ */