root/tools/testing/selftests/bpf/prog_tests/btf_endian.c
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2020 Facebook */
#define _GNU_SOURCE
#include <string.h>
#include <byteswap.h>
#include <test_progs.h>
#include <bpf/btf.h>

void test_btf_endian() {
#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
        enum btf_endianness endian = BTF_LITTLE_ENDIAN;
#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
        enum btf_endianness endian = BTF_BIG_ENDIAN;
#else
#error "Unrecognized __BYTE_ORDER__"
#endif
        enum btf_endianness swap_endian = 1 - endian;
        struct btf *btf = NULL, *swap_btf = NULL;
        const void *raw_data, *swap_raw_data;
        const struct btf_type *t;
        const struct btf_header *hdr;
        __u32 raw_sz, swap_raw_sz;
        int var_id;

        /* Load BTF in native endianness */
        btf = btf__parse_elf("btf_dump_test_case_syntax.bpf.o", NULL);
        if (!ASSERT_OK_PTR(btf, "parse_native_btf"))
                goto err_out;

        ASSERT_EQ(btf__endianness(btf), endian, "endian");
        btf__set_endianness(btf, swap_endian);
        ASSERT_EQ(btf__endianness(btf), swap_endian, "endian");

        /* Get raw BTF data in non-native endianness... */
        raw_data = btf__raw_data(btf, &raw_sz);
        if (!ASSERT_OK_PTR(raw_data, "raw_data_inverted"))
                goto err_out;

        /* ...and open it as a new BTF instance */
        swap_btf = btf__new(raw_data, raw_sz);
        if (!ASSERT_OK_PTR(swap_btf, "parse_swap_btf"))
                goto err_out;

        ASSERT_EQ(btf__endianness(swap_btf), swap_endian, "endian");
        ASSERT_EQ(btf__type_cnt(swap_btf), btf__type_cnt(btf), "nr_types");

        swap_raw_data = btf__raw_data(swap_btf, &swap_raw_sz);
        if (!ASSERT_OK_PTR(swap_raw_data, "swap_raw_data"))
                goto err_out;

        /* both raw data should be identical (with non-native endianness) */
        ASSERT_OK(memcmp(raw_data, swap_raw_data, raw_sz), "mem_identical");

        /* make sure that at least BTF header data is really swapped */
        hdr = swap_raw_data;
        ASSERT_EQ(bswap_16(hdr->magic), BTF_MAGIC, "btf_magic_swapped");
        ASSERT_EQ(raw_sz, swap_raw_sz, "raw_sizes");

        /* swap it back to native endianness */
        btf__set_endianness(swap_btf, endian);
        swap_raw_data = btf__raw_data(swap_btf, &swap_raw_sz);
        if (!ASSERT_OK_PTR(swap_raw_data, "swap_raw_data"))
                goto err_out;

        /* now header should have native BTF_MAGIC */
        hdr = swap_raw_data;
        ASSERT_EQ(hdr->magic, BTF_MAGIC, "btf_magic_native");
        ASSERT_EQ(raw_sz, swap_raw_sz, "raw_sizes");

        /* now modify original BTF */
        var_id = btf__add_var(btf, "some_var", BTF_VAR_GLOBAL_ALLOCATED, 1);
        ASSERT_GT(var_id, 0, "var_id");

        btf__free(swap_btf);
        swap_btf = NULL;

        btf__set_endianness(btf, swap_endian);
        raw_data = btf__raw_data(btf, &raw_sz);
        if (!ASSERT_OK_PTR(raw_data, "raw_data_inverted"))
                goto err_out;

        /* and re-open swapped raw data again */
        swap_btf = btf__new(raw_data, raw_sz);
        if (!ASSERT_OK_PTR(swap_btf, "parse_swap_btf"))
                goto err_out;

        ASSERT_EQ(btf__endianness(swap_btf), swap_endian, "endian");
        ASSERT_EQ(btf__type_cnt(swap_btf), btf__type_cnt(btf), "nr_types");

        /* the type should appear as if it was stored in native endianness */
        t = btf__type_by_id(swap_btf, var_id);
        ASSERT_STREQ(btf__str_by_offset(swap_btf, t->name_off), "some_var", "var_name");
        ASSERT_EQ(btf_var(t)->linkage, BTF_VAR_GLOBAL_ALLOCATED, "var_linkage");
        ASSERT_EQ(t->type, 1, "var_type");

err_out:
        btf__free(btf);
        btf__free(swap_btf);
}