On Mon, Sep 16, 2024 at 1:38 AM Tony Ambardar <tony.ambardar@xxxxxxxxx> wrote: > > Support for handling BTF data of either endianness was added in [1], but > did not include BTF.ext data for lack of use cases. Later, support for > static linking [2] provided a use case, but this feature and later ones > were restricted to native-endian usage. > > Add support for BTF.ext handling in either endianness. Convert BTF.ext data > to native endianness when read into memory for further processing, and > support raw data access that restores the original byte-order for output. > Add internal header functions for byte-swapping func, line, and core info > records. > > Add new API functions btf_ext__endianness() and btf_ext__set_endianness() > for query and setting byte-order, as already exist for BTF data. > > [1] 3289959b97ca ("libbpf: Support BTF loading and raw data output in both endianness") > [2] 8fd27bf69b86 ("libbpf: Add BPF static linker BTF and BTF.ext support") > > Signed-off-by: Tony Ambardar <tony.ambardar@xxxxxxxxx> > --- > tools/lib/bpf/btf.c | 280 +++++++++++++++++++++++++------- > tools/lib/bpf/btf.h | 3 + > tools/lib/bpf/libbpf.map | 2 + > tools/lib/bpf/libbpf_internal.h | 30 ++++ > 4 files changed, 258 insertions(+), 57 deletions(-) > [...] > > - /* The record size needs to meet the minimum standard */ > - record_size = *(__u32 *)info; > + /* The record size needs to meet either the minimum standard or, when > + * handling non-native endianness data, the exact standard so as > + * to allow safe byte-swapping. > + */ > + record_size = is_native ? *(__u32 *)info : bswap_32(*(__u32 *)info); > if (record_size < ext_sec->min_rec_size || > + (!is_native && record_size != ext_sec->rec_size) || ok, so the way you define min_rec_size and rec_size is actually broken. bpf_func_info, bpf_line_info, and now also bpf_core_relo all come from kernel UAPI header, and it could happen that libbpf is compiled against newest definitions of them without actually "knowing"/supporting those newer and bigger struct definitions. So assuming that sizeof(struct bpf_line_info) is a correct expected record size is wrong. As it is right now, min_rec_size is *the* rec_size, so I removed that part and updated this check to record_size != ext_sec->min_rec_size. If we ever extend .BTF.ext records, we'll need to add a bit more code to accomodate for that. > record_size & 0x03) { > pr_debug("%s section in .BTF.ext has invalid record size %u\n", > ext_sec->desc, record_size); > @@ -2956,7 +2968,7 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, > return -EINVAL; > } > > - num_records = sinfo->num_info; > + num_records = is_native ? sinfo->num_info : bswap_32(sinfo->num_info); > if (num_records == 0) { > pr_debug("%s section has incorrect num_records in .BTF.ext\n", > ext_sec->desc); > @@ -2984,64 +2996,160 @@ static int btf_ext_setup_info(struct btf_ext *btf_ext, > return 0; > } > > -static int btf_ext_setup_func_info(struct btf_ext *btf_ext) > +/* Parse all info secs in the BTF.ext info data */ > +static int btf_ext_parse_info(struct btf_ext *btf_ext, bool is_native) > { > - struct btf_ext_sec_setup_param param = { > + struct btf_ext_sec_info_param func_info = { > .off = btf_ext->hdr->func_info_off, > .len = btf_ext->hdr->func_info_len, > .min_rec_size = sizeof(struct bpf_func_info_min), > + .rec_size = sizeof(struct bpf_func_info), > .ext_info = &btf_ext->func_info, > .desc = "func_info" > }; > - > - return btf_ext_setup_info(btf_ext, ¶m); > -} > - > -static int btf_ext_setup_line_info(struct btf_ext *btf_ext) > -{ > - struct btf_ext_sec_setup_param param = { > + struct btf_ext_sec_info_param line_info = { > .off = btf_ext->hdr->line_info_off, > .len = btf_ext->hdr->line_info_len, > .min_rec_size = sizeof(struct bpf_line_info_min), > + .rec_size = sizeof(struct bpf_line_info), > .ext_info = &btf_ext->line_info, > .desc = "line_info", > }; > - > - return btf_ext_setup_info(btf_ext, ¶m); > -} > - > -static int btf_ext_setup_core_relos(struct btf_ext *btf_ext) > -{ > - struct btf_ext_sec_setup_param param = { > + struct btf_ext_sec_info_param core_relo = { > .off = btf_ext->hdr->core_relo_off, > .len = btf_ext->hdr->core_relo_len, > .min_rec_size = sizeof(struct bpf_core_relo), > + .rec_size = sizeof(struct bpf_core_relo), > .ext_info = &btf_ext->core_relo_info, > .desc = "core_relo", > }; [...] > > diff --git a/tools/lib/bpf/btf.h b/tools/lib/bpf/btf.h > index 4e349ad79ee6..47ee8f6ac489 100644 > --- a/tools/lib/bpf/btf.h > +++ b/tools/lib/bpf/btf.h > @@ -167,6 +167,9 @@ LIBBPF_API const char *btf__str_by_offset(const struct btf *btf, __u32 offset); > LIBBPF_API struct btf_ext *btf_ext__new(const __u8 *data, __u32 size); > LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext); > LIBBPF_API const void *btf_ext__raw_data(const struct btf_ext *btf_ext, __u32 *size); > +LIBBPF_API enum btf_endianness btf_ext__endianness(const struct btf_ext *btf_ext); > +LIBBPF_API int btf_ext__set_endianness(struct btf_ext *btf_ext, > + enum btf_endianness endian); > > LIBBPF_API int btf__find_str(struct btf *btf, const char *s); > LIBBPF_API int btf__add_str(struct btf *btf, const char *s); > diff --git a/tools/lib/bpf/libbpf.map b/tools/lib/bpf/libbpf.map > index 0096e483f7eb..f40ccc2946e7 100644 > --- a/tools/lib/bpf/libbpf.map > +++ b/tools/lib/bpf/libbpf.map > @@ -421,6 +421,8 @@ LIBBPF_1.5.0 { > global: > btf__distill_base; > btf__relocate; > + btf_ext__endianness; > + btf_ext__set_endianness; > bpf_map__autoattach; > bpf_map__set_autoattach; > bpf_object__token_fd; > diff --git a/tools/lib/bpf/libbpf_internal.h b/tools/lib/bpf/libbpf_internal.h > index 8cda511a1982..1307753b49b3 100644 > --- a/tools/lib/bpf/libbpf_internal.h > +++ b/tools/lib/bpf/libbpf_internal.h missing #include <byteswap.h> in libbpf_internal.h, you added too late (in the next patch). I moved that include into this patch/commit to not break bisectability, please be careful about things like that > @@ -484,6 +484,8 @@ struct btf_ext { > struct btf_ext_header *hdr; > void *data; > }; > + void *data_swapped; > + bool swapped_endian; > struct btf_ext_info func_info; > struct btf_ext_info line_info; > struct btf_ext_info core_relo_info; > @@ -511,6 +513,34 @@ struct bpf_line_info_min { > __u32 line_col; > }; > > +/* Functions to byte-swap info records */ > + > +typedef void (*info_rec_bswap_fn)(void *); > + > +static inline void bpf_func_info_bswap(struct bpf_func_info *i) > +{ > + i->insn_off = bswap_32(i->insn_off); > + i->type_id = bswap_32(i->type_id); > +} > + > +static inline void bpf_line_info_bswap(struct bpf_line_info *i) > +{ > + i->insn_off = bswap_32(i->insn_off); > + i->file_name_off = bswap_32(i->file_name_off); > + i->line_off = bswap_32(i->line_off); > + i->line_col = bswap_32(i->line_col); > +} > + > +static inline void bpf_core_relo_bswap(struct bpf_core_relo *i) > +{ > + _Static_assert(sizeof(i->kind) == sizeof(__u32), > + "enum bpf_core_relo_kind is not 32-bit\n"); Do we need the endline in _Static_assert() messages? And generally I think this is a bit too paranoid to check that that enum is backed by u32, I just dropped the assertion. This is part of kernel UAPI, it shouldn't change. > + i->insn_off = bswap_32(i->insn_off); > + i->type_id = bswap_32(i->type_id); > + i->access_str_off = bswap_32(i->access_str_off); > + i->kind = bswap_32(i->kind); > +} > + > enum btf_field_iter_kind { > BTF_FIELD_ITER_IDS, > BTF_FIELD_ITER_STRS, > -- > 2.34.1 >