Commit 85e3f318 authored by Alexei Starovoitov's avatar Alexei Starovoitov

Merge branch 'libbpf: support loading/storing any BTF'

Andrii Nakryiko says:

====================
Add support for loading and storing BTF in either little- or big-endian
integer encodings, regardless of host endianness. This allows users of libbpf
to not care about endianness when they don't want to and transparently
open/load BTF of any endianness. libbpf will preserve original endianness and
will convert output raw data as necessary back to original endianness, if
necessary. This allows tools like pahole to be ignorant to such issues during
cross-compilation.

While working with BTF data in memory, the endianness is always native to the
host. Convetion can happen only during btf__get_raw_data() call, and only in
a raw data copy.

Additionally, it's possible to force output BTF endianness through new
btf__set_endianness() API. This which allows to create flexible tools doing
arbitrary conversions of BTF endianness, just by relying on libbpf.

Cc: Arnaldo Carvalho de Melo <arnaldo.melo@gmail.com>
Cc: Tony Ambardar <tony.ambardar@gmail.com>
Cc: Ilya Leoshkevich <iii@linux.ibm.com>
Cc: Luka Perkov <luka.perkov@sartura.hr>
====================
Signed-off-by: default avatarAlexei Starovoitov <ast@kernel.org>
parents f970cbcd ed9cf248
This diff is collapsed.
...@@ -25,6 +25,11 @@ struct btf_type; ...@@ -25,6 +25,11 @@ struct btf_type;
struct bpf_object; struct bpf_object;
enum btf_endianness {
BTF_LITTLE_ENDIAN = 0,
BTF_BIG_ENDIAN = 1,
};
LIBBPF_API void btf__free(struct btf *btf); LIBBPF_API void btf__free(struct btf *btf);
LIBBPF_API struct btf *btf__new(const void *data, __u32 size); LIBBPF_API struct btf *btf__new(const void *data, __u32 size);
LIBBPF_API struct btf *btf__new_empty(void); LIBBPF_API struct btf *btf__new_empty(void);
...@@ -42,6 +47,8 @@ LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf, ...@@ -42,6 +47,8 @@ LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf,
__u32 id); __u32 id);
LIBBPF_API size_t btf__pointer_size(const struct btf *btf); LIBBPF_API size_t btf__pointer_size(const struct btf *btf);
LIBBPF_API int btf__set_pointer_size(struct btf *btf, size_t ptr_sz); LIBBPF_API int btf__set_pointer_size(struct btf *btf, size_t ptr_sz);
LIBBPF_API enum btf_endianness btf__endianness(const struct btf *btf);
LIBBPF_API int btf__set_endianness(struct btf *btf, enum btf_endianness endian);
LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id); LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id); LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id);
LIBBPF_API int btf__align_of(const struct btf *btf, __u32 id); LIBBPF_API int btf__align_of(const struct btf *btf, __u32 id);
......
...@@ -325,8 +325,10 @@ LIBBPF_0.2.0 { ...@@ -325,8 +325,10 @@ LIBBPF_0.2.0 {
btf__add_union; btf__add_union;
btf__add_var; btf__add_var;
btf__add_volatile; btf__add_volatile;
btf__endianness;
btf__find_str; btf__find_str;
btf__new_empty; btf__new_empty;
btf__set_endianness;
btf__str_by_offset; btf__str_by_offset;
perf_buffer__buffer_cnt; perf_buffer__buffer_cnt;
perf_buffer__buffer_fd; perf_buffer__buffer_fd;
......
// 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>
static int duration = 0;
void test_btf_endian() {
#if __BYTE_ORDER == __LITTLE_ENDIAN
enum btf_endianness endian = BTF_LITTLE_ENDIAN;
#elif __BYTE_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.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__get_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__get_nr_types(swap_btf), btf__get_nr_types(btf), "nr_types");
swap_raw_data = btf__get_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__get_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);
CHECK(var_id <= 0, "var_id", "failed %d\n", var_id);
btf__free(swap_btf);
swap_btf = NULL;
btf__set_endianness(btf, swap_endian);
raw_data = btf__get_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__get_nr_types(swap_btf), btf__get_nr_types(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);
}
...@@ -3,40 +3,6 @@ ...@@ -3,40 +3,6 @@
#include <test_progs.h> #include <test_progs.h>
#include <bpf/btf.h> #include <bpf/btf.h>
#define ASSERT_EQ(actual, expected, name) ({ \
typeof(actual) ___act = (actual); \
typeof(expected) ___exp = (expected); \
bool ___ok = ___act == ___exp; \
CHECK(!___ok, (name), \
"unexpected %s: actual %lld != expected %lld\n", \
(name), (long long)(___act), (long long)(___exp)); \
___ok; \
})
#define ASSERT_STREQ(actual, expected, name) ({ \
const char *___act = actual; \
const char *___exp = expected; \
bool ___ok = strcmp(___act, ___exp) == 0; \
CHECK(!___ok, (name), \
"unexpected %s: actual '%s' != expected '%s'\n", \
(name), ___act, ___exp); \
___ok; \
})
#define ASSERT_OK(res, name) ({ \
long long ___res = (res); \
bool ___ok = ___res == 0; \
CHECK(!___ok, (name), "unexpected error: %lld\n", ___res); \
___ok; \
})
#define ASSERT_ERR(res, name) ({ \
long long ___res = (res); \
bool ___ok = ___res < 0; \
CHECK(!___ok, (name), "unexpected success: %lld\n", ___res); \
___ok; \
})
static int duration = 0; static int duration = 0;
void test_btf_write() { void test_btf_write() {
......
...@@ -130,6 +130,69 @@ extern int test__join_cgroup(const char *path); ...@@ -130,6 +130,69 @@ extern int test__join_cgroup(const char *path);
#define CHECK_ATTR(condition, tag, format...) \ #define CHECK_ATTR(condition, tag, format...) \
_CHECK(condition, tag, tattr.duration, format) _CHECK(condition, tag, tattr.duration, format)
#define ASSERT_EQ(actual, expected, name) ({ \
static int duration = 0; \
typeof(actual) ___act = (actual); \
typeof(expected) ___exp = (expected); \
bool ___ok = ___act == ___exp; \
CHECK(!___ok, (name), \
"unexpected %s: actual %lld != expected %lld\n", \
(name), (long long)(___act), (long long)(___exp)); \
___ok; \
})
#define ASSERT_STREQ(actual, expected, name) ({ \
static int duration = 0; \
const char *___act = actual; \
const char *___exp = expected; \
bool ___ok = strcmp(___act, ___exp) == 0; \
CHECK(!___ok, (name), \
"unexpected %s: actual '%s' != expected '%s'\n", \
(name), ___act, ___exp); \
___ok; \
})
#define ASSERT_OK(res, name) ({ \
static int duration = 0; \
long long ___res = (res); \
bool ___ok = ___res == 0; \
CHECK(!___ok, (name), "unexpected error: %lld\n", ___res); \
___ok; \
})
#define ASSERT_ERR(res, name) ({ \
static int duration = 0; \
long long ___res = (res); \
bool ___ok = ___res < 0; \
CHECK(!___ok, (name), "unexpected success: %lld\n", ___res); \
___ok; \
})
#define ASSERT_NULL(ptr, name) ({ \
static int duration = 0; \
const void *___res = (ptr); \
bool ___ok = !___res; \
CHECK(!___ok, (name), "unexpected pointer: %p\n", ___res); \
___ok; \
})
#define ASSERT_OK_PTR(ptr, name) ({ \
static int duration = 0; \
const void *___res = (ptr); \
bool ___ok = !IS_ERR_OR_NULL(___res); \
CHECK(!___ok, (name), \
"unexpected error: %ld\n", PTR_ERR(___res)); \
___ok; \
})
#define ASSERT_ERR_PTR(ptr, name) ({ \
static int duration = 0; \
const void *___res = (ptr); \
bool ___ok = IS_ERR(___res) \
CHECK(!___ok, (name), "unexpected pointer: %p\n", ___res); \
___ok; \
})
static inline __u64 ptr_to_u64(const void *ptr) static inline __u64 ptr_to_u64(const void *ptr)
{ {
return (__u64) (unsigned long) ptr; return (__u64) (unsigned long) ptr;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment