selftest/bpf: Add relocatable bitfield reading tests
Add a bunch of selftests verifying correctness of relocatable bitfield reading support in libbpf. Both bpf_probe_read()-based and direct read-based bitfield macros are tested. core_reloc.c "test_harness" is extended to support raw tracepoint and new typed raw tracepoints as test BPF program types. Signed-off-by: Andrii Nakryiko <andriin@fb.com> Signed-off-by: Daniel Borkmann <daniel@iogearbox.net> Link: https://lore.kernel.org/bpf/20191101222810.1246166-5-andriin@fb.com
This commit is contained in:
parent
94f060e984
commit
8b1cb1c960
@ -189,6 +189,42 @@
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
#define BITFIELDS_CASE_COMMON(objfile, test_name_prefix, name) \
|
||||
.case_name = test_name_prefix#name, \
|
||||
.bpf_obj_file = objfile, \
|
||||
.btf_src_file = "btf__core_reloc_" #name ".o"
|
||||
|
||||
#define BITFIELDS_CASE(name, ...) { \
|
||||
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
|
||||
"direct:", name), \
|
||||
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
|
||||
__VA_ARGS__, \
|
||||
.output_len = sizeof(struct core_reloc_bitfields_output), \
|
||||
}, { \
|
||||
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
|
||||
"probed:", name), \
|
||||
.input = STRUCT_TO_CHAR_PTR(core_reloc_##name) __VA_ARGS__, \
|
||||
.input_len = sizeof(struct core_reloc_##name), \
|
||||
.output = STRUCT_TO_CHAR_PTR(core_reloc_bitfields_output) \
|
||||
__VA_ARGS__, \
|
||||
.output_len = sizeof(struct core_reloc_bitfields_output), \
|
||||
.direct_raw_tp = true, \
|
||||
}
|
||||
|
||||
|
||||
#define BITFIELDS_ERR_CASE(name) { \
|
||||
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_probed.o", \
|
||||
"probed:", name), \
|
||||
.fails = true, \
|
||||
}, { \
|
||||
BITFIELDS_CASE_COMMON("test_core_reloc_bitfields_direct.o", \
|
||||
"direct:", name), \
|
||||
.direct_raw_tp = true, \
|
||||
.fails = true, \
|
||||
}
|
||||
|
||||
struct core_reloc_test_case {
|
||||
const char *case_name;
|
||||
const char *bpf_obj_file;
|
||||
@ -199,6 +235,7 @@ struct core_reloc_test_case {
|
||||
int output_len;
|
||||
bool fails;
|
||||
bool relaxed_core_relocs;
|
||||
bool direct_raw_tp;
|
||||
};
|
||||
|
||||
static struct core_reloc_test_case test_cases[] = {
|
||||
@ -352,6 +389,40 @@ static struct core_reloc_test_case test_cases[] = {
|
||||
EXISTENCE_ERR_CASE(existence__err_arr_kind),
|
||||
EXISTENCE_ERR_CASE(existence__err_arr_value_type),
|
||||
EXISTENCE_ERR_CASE(existence__err_struct_type),
|
||||
|
||||
/* bitfield relocation checks */
|
||||
BITFIELDS_CASE(bitfields, {
|
||||
.ub1 = 1,
|
||||
.ub2 = 2,
|
||||
.ub7 = 96,
|
||||
.sb4 = -7,
|
||||
.sb20 = -0x76543,
|
||||
.u32 = 0x80000000,
|
||||
.s32 = -0x76543210,
|
||||
}),
|
||||
BITFIELDS_CASE(bitfields___bit_sz_change, {
|
||||
.ub1 = 6,
|
||||
.ub2 = 0xABCDE,
|
||||
.ub7 = 1,
|
||||
.sb4 = -1,
|
||||
.sb20 = -0x17654321,
|
||||
.u32 = 0xBEEF,
|
||||
.s32 = -0x3FEDCBA987654321,
|
||||
}),
|
||||
BITFIELDS_CASE(bitfields___bitfield_vs_int, {
|
||||
.ub1 = 0xFEDCBA9876543210,
|
||||
.ub2 = 0xA6,
|
||||
.ub7 = -0x7EDCBA987654321,
|
||||
.sb4 = -0x6123456789ABCDE,
|
||||
.sb20 = 0xD00D,
|
||||
.u32 = -0x76543,
|
||||
.s32 = 0x0ADEADBEEFBADB0B,
|
||||
}),
|
||||
BITFIELDS_CASE(bitfields___just_big_enough, {
|
||||
.ub1 = 0xF,
|
||||
.ub2 = 0x0812345678FEDCBA,
|
||||
}),
|
||||
BITFIELDS_ERR_CASE(bitfields___err_too_big_bitfield),
|
||||
};
|
||||
|
||||
struct data {
|
||||
@ -361,9 +432,9 @@ struct data {
|
||||
|
||||
void test_core_reloc(void)
|
||||
{
|
||||
const char *probe_name = "raw_tracepoint/sys_enter";
|
||||
struct bpf_object_load_attr load_attr = {};
|
||||
struct core_reloc_test_case *test_case;
|
||||
const char *tp_name, *probe_name;
|
||||
int err, duration = 0, i, equal;
|
||||
struct bpf_link *link = NULL;
|
||||
struct bpf_map *data_map;
|
||||
@ -387,6 +458,15 @@ void test_core_reloc(void)
|
||||
test_case->bpf_obj_file, PTR_ERR(obj)))
|
||||
continue;
|
||||
|
||||
/* for typed raw tracepoints, NULL should be specified */
|
||||
if (test_case->direct_raw_tp) {
|
||||
probe_name = "tp_btf/sys_enter";
|
||||
tp_name = NULL;
|
||||
} else {
|
||||
probe_name = "raw_tracepoint/sys_enter";
|
||||
tp_name = "sys_enter";
|
||||
}
|
||||
|
||||
prog = bpf_object__find_program_by_title(obj, probe_name);
|
||||
if (CHECK(!prog, "find_probe",
|
||||
"prog '%s' not found\n", probe_name))
|
||||
@ -407,7 +487,7 @@ void test_core_reloc(void)
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
link = bpf_program__attach_raw_tracepoint(prog, "sys_enter");
|
||||
link = bpf_program__attach_raw_tracepoint(prog, tp_name);
|
||||
if (CHECK(IS_ERR(link), "attach_raw_tp", "err %ld\n",
|
||||
PTR_ERR(link)))
|
||||
goto cleanup;
|
||||
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_bitfields x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_bitfields___bit_sz_change x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_bitfields___bitfield_vs_int x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_bitfields___err_too_big_bitfield x) {}
|
@ -0,0 +1,3 @@
|
||||
#include "core_reloc_types.h"
|
||||
|
||||
void f(struct core_reloc_bitfields___just_big_enough x) {}
|
@ -662,3 +662,75 @@ struct core_reloc_existence___err_wrong_arr_value_type {
|
||||
struct core_reloc_existence___err_wrong_struct_type {
|
||||
int s;
|
||||
};
|
||||
|
||||
/*
|
||||
* BITFIELDS
|
||||
*/
|
||||
/* bitfield read results, all as plain integers */
|
||||
struct core_reloc_bitfields_output {
|
||||
int64_t ub1;
|
||||
int64_t ub2;
|
||||
int64_t ub7;
|
||||
int64_t sb4;
|
||||
int64_t sb20;
|
||||
int64_t u32;
|
||||
int64_t s32;
|
||||
};
|
||||
|
||||
struct core_reloc_bitfields {
|
||||
/* unsigned bitfields */
|
||||
uint8_t ub1: 1;
|
||||
uint8_t ub2: 2;
|
||||
uint32_t ub7: 7;
|
||||
/* signed bitfields */
|
||||
int8_t sb4: 4;
|
||||
int32_t sb20: 20;
|
||||
/* non-bitfields */
|
||||
uint32_t u32;
|
||||
int32_t s32;
|
||||
};
|
||||
|
||||
/* different bit sizes (both up and down) */
|
||||
struct core_reloc_bitfields___bit_sz_change {
|
||||
/* unsigned bitfields */
|
||||
uint16_t ub1: 3; /* 1 -> 3 */
|
||||
uint32_t ub2: 20; /* 2 -> 20 */
|
||||
uint8_t ub7: 1; /* 7 -> 1 */
|
||||
/* signed bitfields */
|
||||
int8_t sb4: 1; /* 4 -> 1 */
|
||||
int32_t sb20: 30; /* 20 -> 30 */
|
||||
/* non-bitfields */
|
||||
uint16_t u32; /* 32 -> 16 */
|
||||
int64_t s32; /* 32 -> 64 */
|
||||
};
|
||||
|
||||
/* turn bitfield into non-bitfield and vice versa */
|
||||
struct core_reloc_bitfields___bitfield_vs_int {
|
||||
uint64_t ub1; /* 3 -> 64 non-bitfield */
|
||||
uint8_t ub2; /* 20 -> 8 non-bitfield */
|
||||
int64_t ub7; /* 7 -> 64 non-bitfield signed */
|
||||
int64_t sb4; /* 4 -> 64 non-bitfield signed */
|
||||
uint64_t sb20; /* 20 -> 16 non-bitfield unsigned */
|
||||
int32_t u32: 20; /* 32 non-bitfield -> 20 bitfield */
|
||||
uint64_t s32: 60; /* 32 non-bitfield -> 60 bitfield */
|
||||
};
|
||||
|
||||
struct core_reloc_bitfields___just_big_enough {
|
||||
uint64_t ub1: 4;
|
||||
uint64_t ub2: 60; /* packed tightly */
|
||||
uint32_t ub7;
|
||||
uint32_t sb4;
|
||||
uint32_t sb20;
|
||||
uint32_t u32;
|
||||
uint32_t s32;
|
||||
} __attribute__((packed)) ;
|
||||
|
||||
struct core_reloc_bitfields___err_too_big_bitfield {
|
||||
uint64_t ub1: 4;
|
||||
uint64_t ub2: 61; /* packed tightly */
|
||||
uint32_t ub7;
|
||||
uint32_t sb4;
|
||||
uint32_t sb20;
|
||||
uint32_t u32;
|
||||
uint32_t s32;
|
||||
} __attribute__((packed)) ;
|
||||
|
@ -0,0 +1,63 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
#include "bpf_core_read.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_bitfields {
|
||||
/* unsigned bitfields */
|
||||
uint8_t ub1: 1;
|
||||
uint8_t ub2: 2;
|
||||
uint32_t ub7: 7;
|
||||
/* signed bitfields */
|
||||
int8_t sb4: 4;
|
||||
int32_t sb20: 20;
|
||||
/* non-bitfields */
|
||||
uint32_t u32;
|
||||
int32_t s32;
|
||||
};
|
||||
|
||||
/* bitfield read results, all as plain integers */
|
||||
struct core_reloc_bitfields_output {
|
||||
int64_t ub1;
|
||||
int64_t ub2;
|
||||
int64_t ub7;
|
||||
int64_t sb4;
|
||||
int64_t sb20;
|
||||
int64_t u32;
|
||||
int64_t s32;
|
||||
};
|
||||
|
||||
struct pt_regs;
|
||||
|
||||
struct trace_sys_enter {
|
||||
struct pt_regs *regs;
|
||||
long id;
|
||||
};
|
||||
|
||||
SEC("tp_btf/sys_enter")
|
||||
int test_core_bitfields_direct(void *ctx)
|
||||
{
|
||||
struct core_reloc_bitfields *in = (void *)&data.in;
|
||||
struct core_reloc_bitfields_output *out = (void *)&data.out;
|
||||
|
||||
out->ub1 = BPF_CORE_READ_BITFIELD(in, ub1);
|
||||
out->ub2 = BPF_CORE_READ_BITFIELD(in, ub2);
|
||||
out->ub7 = BPF_CORE_READ_BITFIELD(in, ub7);
|
||||
out->sb4 = BPF_CORE_READ_BITFIELD(in, sb4);
|
||||
out->sb20 = BPF_CORE_READ_BITFIELD(in, sb20);
|
||||
out->u32 = BPF_CORE_READ_BITFIELD(in, u32);
|
||||
out->s32 = BPF_CORE_READ_BITFIELD(in, s32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -0,0 +1,62 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
|
||||
#include <linux/bpf.h>
|
||||
#include <stdint.h>
|
||||
#include "bpf_helpers.h"
|
||||
#include "bpf_core_read.h"
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
||||
|
||||
static volatile struct data {
|
||||
char in[256];
|
||||
char out[256];
|
||||
} data;
|
||||
|
||||
struct core_reloc_bitfields {
|
||||
/* unsigned bitfields */
|
||||
uint8_t ub1: 1;
|
||||
uint8_t ub2: 2;
|
||||
uint32_t ub7: 7;
|
||||
/* signed bitfields */
|
||||
int8_t sb4: 4;
|
||||
int32_t sb20: 20;
|
||||
/* non-bitfields */
|
||||
uint32_t u32;
|
||||
int32_t s32;
|
||||
};
|
||||
|
||||
/* bitfield read results, all as plain integers */
|
||||
struct core_reloc_bitfields_output {
|
||||
int64_t ub1;
|
||||
int64_t ub2;
|
||||
int64_t ub7;
|
||||
int64_t sb4;
|
||||
int64_t sb20;
|
||||
int64_t u32;
|
||||
int64_t s32;
|
||||
};
|
||||
|
||||
#define TRANSFER_BITFIELD(in, out, field) \
|
||||
if (BPF_CORE_READ_BITFIELD_PROBED(in, field, &res)) \
|
||||
return 1; \
|
||||
out->field = res
|
||||
|
||||
SEC("raw_tracepoint/sys_enter")
|
||||
int test_core_bitfields(void *ctx)
|
||||
{
|
||||
struct core_reloc_bitfields *in = (void *)&data.in;
|
||||
struct core_reloc_bitfields_output *out = (void *)&data.out;
|
||||
uint64_t res;
|
||||
|
||||
TRANSFER_BITFIELD(in, out, ub1);
|
||||
TRANSFER_BITFIELD(in, out, ub2);
|
||||
TRANSFER_BITFIELD(in, out, ub7);
|
||||
TRANSFER_BITFIELD(in, out, sb4);
|
||||
TRANSFER_BITFIELD(in, out, sb20);
|
||||
TRANSFER_BITFIELD(in, out, u32);
|
||||
TRANSFER_BITFIELD(in, out, s32);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user