Merge branch 'extern-var-support'
Andrii Nakryiko says: ==================== It's often important for BPF program to know kernel version or some specific config values (e.g., CONFIG_HZ to convert jiffies to seconds) and change or adjust program logic based on their values. As of today, any such need has to be resolved by recompiling BPF program for specific kernel and kernel configuration. In practice this is usually achieved by using BCC and its embedded LLVM/Clang. With such set up #ifdef CONFIG_XXX and similar compile-time constructs allow to deal with kernel varieties. With CO-RE (Compile Once – Run Everywhere) approach, this is not an option, unfortunately. All such logic variations have to be done as a normal C language constructs (i.e., if/else, variables, etc), not a preprocessor directives. This patch series add support for such advanced scenarios through C extern variables. These extern variables will be recognized by libbpf and supplied through extra .extern internal map, similarly to global data. This .extern map is read-only, which allows BPF verifier to track its content precisely as constants. That gives an opportunity to have pre-compiled BPF program, which can potentially use BPF functionality (e.g., BPF helpers) or kernel features (types, fields, etc), that are available only on a subset of targeted kernels, while effectively eleminating (through verifier's dead code detection) such unsupported functionality for other kernels (typically, older versions). Patch #3 explicitly tests a scenario of using unsupported BPF helper, to validate the approach. This patch set heavily relies on BTF type information emitted by compiler for each extern variable declaration. Based on specific types, libbpf does strict checks of config data values correctness. See patch #1 for details. Outline of the patch set: - patch #1 does a small clean up of internal map names contants; - patch #2 adds all of the libbpf internal machinery for externs support, including setting up BTF information for .extern data section; - patch #3 adds support for .extern into BPF skeleton; - patch #4 adds externs selftests, as well as enhances test_skeleton.c test to validate mmap()-ed .extern datasection functionality. v3->v4: - clean up copyrights and rebase onto latest skeleton patches (Alexei); v2->v3: - truncate too long strings (Alexei); - clean ups, adding comments (Alexei); v1->v2: - use BTF type information for externs (Alexei); - add strings support; - add BPF skeleton support for .extern. ==================== Signed-off-by: Alexei Starovoitov <ast@kernel.org>
This commit is contained in:
commit
01c6f7aaac
|
@ -142,7 +142,8 @@ struct btf_param {
|
|||
|
||||
enum {
|
||||
BTF_VAR_STATIC = 0,
|
||||
BTF_VAR_GLOBAL_ALLOCATED,
|
||||
BTF_VAR_GLOBAL_ALLOCATED = 1,
|
||||
BTF_VAR_GLOBAL_EXTERN = 2,
|
||||
};
|
||||
|
||||
/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
|
||||
|
|
|
@ -82,6 +82,8 @@ static const char *get_map_ident(const struct bpf_map *map)
|
|||
return "rodata";
|
||||
else if (str_has_suffix(name, ".bss"))
|
||||
return "bss";
|
||||
else if (str_has_suffix(name, ".extern"))
|
||||
return "externs"; /* extern is a C keyword */
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
@ -109,6 +111,8 @@ static int codegen_datasec_def(struct bpf_object *obj,
|
|||
sec_ident = "bss";
|
||||
else if (strcmp(sec_name, ".rodata") == 0)
|
||||
sec_ident = "rodata";
|
||||
else if (strcmp(sec_name, ".extern") == 0)
|
||||
sec_ident = "externs"; /* extern is a C keyword */
|
||||
else
|
||||
return 0;
|
||||
|
||||
|
|
|
@ -22,9 +22,9 @@ struct btf_header {
|
|||
};
|
||||
|
||||
/* Max # of type identifier */
|
||||
#define BTF_MAX_TYPE 0x0000ffff
|
||||
#define BTF_MAX_TYPE 0x000fffff
|
||||
/* Max offset into the string section */
|
||||
#define BTF_MAX_NAME_OFFSET 0x0000ffff
|
||||
#define BTF_MAX_NAME_OFFSET 0x00ffffff
|
||||
/* Max # of struct/union/enum members or func args */
|
||||
#define BTF_MAX_VLEN 0xffff
|
||||
|
||||
|
@ -142,7 +142,8 @@ struct btf_param {
|
|||
|
||||
enum {
|
||||
BTF_VAR_STATIC = 0,
|
||||
BTF_VAR_GLOBAL_ALLOCATED,
|
||||
BTF_VAR_GLOBAL_ALLOCATED = 1,
|
||||
BTF_VAR_GLOBAL_EXTERN = 2,
|
||||
};
|
||||
|
||||
/* BTF_KIND_VAR is followed by a single "struct btf_var" to describe
|
||||
|
|
|
@ -56,8 +56,8 @@ ifndef VERBOSE
|
|||
endif
|
||||
|
||||
FEATURE_USER = .libbpf
|
||||
FEATURE_TESTS = libelf libelf-mmap bpf reallocarray
|
||||
FEATURE_DISPLAY = libelf bpf
|
||||
FEATURE_TESTS = libelf libelf-mmap zlib bpf reallocarray
|
||||
FEATURE_DISPLAY = libelf zlib bpf
|
||||
|
||||
INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi
|
||||
FEATURE_CHECK_CFLAGS-bpf = $(INCLUDES)
|
||||
|
@ -160,7 +160,7 @@ all: fixdep
|
|||
|
||||
all_cmd: $(CMD_TARGETS) check
|
||||
|
||||
$(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h
|
||||
$(BPF_IN_SHARED): force elfdep zdep bpfdep bpf_helper_defs.h
|
||||
@(test -f ../../include/uapi/linux/bpf.h -a -f ../../../include/uapi/linux/bpf.h && ( \
|
||||
(diff -B ../../include/uapi/linux/bpf.h ../../../include/uapi/linux/bpf.h >/dev/null) || \
|
||||
echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/bpf.h' differs from latest version at 'include/uapi/linux/bpf.h'" >&2 )) || true
|
||||
|
@ -178,7 +178,7 @@ $(BPF_IN_SHARED): force elfdep bpfdep bpf_helper_defs.h
|
|||
echo "Warning: Kernel ABI header at 'tools/include/uapi/linux/if_xdp.h' differs from latest version at 'include/uapi/linux/if_xdp.h'" >&2 )) || true
|
||||
$(Q)$(MAKE) $(build)=libbpf OUTPUT=$(SHARED_OBJDIR) CFLAGS="$(CFLAGS) $(SHLIB_FLAGS)"
|
||||
|
||||
$(BPF_IN_STATIC): force elfdep bpfdep bpf_helper_defs.h
|
||||
$(BPF_IN_STATIC): force elfdep zdep bpfdep bpf_helper_defs.h
|
||||
$(Q)$(MAKE) $(build)=libbpf OUTPUT=$(STATIC_OBJDIR)
|
||||
|
||||
bpf_helper_defs.h: $(srctree)/tools/include/uapi/linux/bpf.h
|
||||
|
@ -190,7 +190,7 @@ $(OUTPUT)libbpf.so: $(OUTPUT)libbpf.so.$(LIBBPF_VERSION)
|
|||
$(OUTPUT)libbpf.so.$(LIBBPF_VERSION): $(BPF_IN_SHARED)
|
||||
$(QUIET_LINK)$(CC) $(LDFLAGS) \
|
||||
--shared -Wl,-soname,libbpf.so.$(LIBBPF_MAJOR_VERSION) \
|
||||
-Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -o $@
|
||||
-Wl,--version-script=$(VERSION_SCRIPT) $^ -lelf -lz -o $@
|
||||
@ln -sf $(@F) $(OUTPUT)libbpf.so
|
||||
@ln -sf $(@F) $(OUTPUT)libbpf.so.$(LIBBPF_MAJOR_VERSION)
|
||||
|
||||
|
@ -279,12 +279,15 @@ clean:
|
|||
|
||||
|
||||
|
||||
PHONY += force elfdep bpfdep cscope tags
|
||||
PHONY += force elfdep zdep bpfdep cscope tags
|
||||
force:
|
||||
|
||||
elfdep:
|
||||
@if [ "$(feature-libelf)" != "1" ]; then echo "No libelf found"; exit 1 ; fi
|
||||
|
||||
zdep:
|
||||
@if [ "$(feature-zlib)" != "1" ]; then echo "No zlib found"; exit 1 ; fi
|
||||
|
||||
bpfdep:
|
||||
@if [ "$(feature-bpf)" != "1" ]; then echo "BPF API too old"; exit 1 ; fi
|
||||
|
||||
|
|
|
@ -25,6 +25,9 @@
|
|||
#ifndef __always_inline
|
||||
#define __always_inline __attribute__((always_inline))
|
||||
#endif
|
||||
#ifndef __weak
|
||||
#define __weak __attribute__((weak))
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Helper structure used by eBPF C program
|
||||
|
@ -44,4 +47,10 @@ enum libbpf_pin_type {
|
|||
LIBBPF_PIN_BY_NAME,
|
||||
};
|
||||
|
||||
enum libbpf_tristate {
|
||||
TRI_NO = 0,
|
||||
TRI_YES = 1,
|
||||
TRI_MODULE = 2,
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -578,6 +578,12 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
|
|||
return -ENOENT;
|
||||
}
|
||||
|
||||
/* .extern datasec size and var offsets were set correctly during
|
||||
* extern collection step, so just skip straight to sorting variables
|
||||
*/
|
||||
if (t->size)
|
||||
goto sort_vars;
|
||||
|
||||
ret = bpf_object__section_size(obj, name, &size);
|
||||
if (ret || !size || (t->size && t->size != size)) {
|
||||
pr_debug("Invalid size for section %s: %u bytes\n", name, size);
|
||||
|
@ -614,7 +620,8 @@ static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
|
|||
vsi->offset = off;
|
||||
}
|
||||
|
||||
qsort(t + 1, vars, sizeof(*vsi), compare_vsi_off);
|
||||
sort_vars:
|
||||
qsort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -85,8 +85,12 @@ struct bpf_object_open_opts {
|
|||
*/
|
||||
const char *pin_root_path;
|
||||
__u32 attach_prog_fd;
|
||||
/* kernel config file path override (for CONFIG_ externs); can point
|
||||
* to either uncompressed text file or .gz file
|
||||
*/
|
||||
const char *kconfig_path;
|
||||
};
|
||||
#define bpf_object_open_opts__last_field attach_prog_fd
|
||||
#define bpf_object_open_opts__last_field kconfig_path
|
||||
|
||||
LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
|
||||
LIBBPF_API struct bpf_object *
|
||||
|
@ -669,6 +673,12 @@ LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
|
|||
LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
|
||||
LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
|
||||
|
||||
enum libbpf_tristate {
|
||||
TRI_NO = 0,
|
||||
TRI_YES = 1,
|
||||
TRI_MODULE = 2,
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* extern "C" */
|
||||
#endif
|
||||
|
|
|
@ -24,7 +24,7 @@ CFLAGS += -g -Wall -O2 $(GENFLAGS) -I$(APIDIR) -I$(LIBDIR) -I$(BPFDIR) \
|
|||
-I$(GENDIR) -I$(TOOLSINCDIR) -I$(CURDIR) \
|
||||
-Dbpf_prog_load=bpf_prog_test_load \
|
||||
-Dbpf_load_program=bpf_test_load_program
|
||||
LDLIBS += -lcap -lelf -lrt -lpthread
|
||||
LDLIBS += -lcap -lelf -lz -lrt -lpthread
|
||||
|
||||
# Order correspond to 'make run_tests' order
|
||||
TEST_GEN_PROGS = test_verifier test_tag test_maps test_lru_map test_lpm_map test_progs \
|
||||
|
|
195
tools/testing/selftests/bpf/prog_tests/core_extern.c
Normal file
195
tools/testing/selftests/bpf/prog_tests/core_extern.c
Normal file
|
@ -0,0 +1,195 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
|
||||
#include <test_progs.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/utsname.h>
|
||||
#include <linux/version.h>
|
||||
#include "test_core_extern.skel.h"
|
||||
|
||||
static uint32_t get_kernel_version(void)
|
||||
{
|
||||
uint32_t major, minor, patch;
|
||||
struct utsname info;
|
||||
|
||||
uname(&info);
|
||||
if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
|
||||
return 0;
|
||||
return KERNEL_VERSION(major, minor, patch);
|
||||
}
|
||||
|
||||
#define CFG "CONFIG_BPF_SYSCALL=n\n"
|
||||
|
||||
static struct test_case {
|
||||
const char *name;
|
||||
const char *cfg;
|
||||
const char *cfg_path;
|
||||
bool fails;
|
||||
struct test_core_extern__data data;
|
||||
} test_cases[] = {
|
||||
{ .name = "default search path", .cfg_path = NULL,
|
||||
.data = { .bpf_syscall = true } },
|
||||
{ .name = "/proc/config.gz", .cfg_path = "/proc/config.gz",
|
||||
.data = { .bpf_syscall = true } },
|
||||
{ .name = "missing config", .fails = true,
|
||||
.cfg_path = "/proc/invalid-config.gz" },
|
||||
{
|
||||
.name = "custom values",
|
||||
.cfg = "CONFIG_BPF_SYSCALL=y\n"
|
||||
"CONFIG_TRISTATE=m\n"
|
||||
"CONFIG_BOOL=y\n"
|
||||
"CONFIG_CHAR=100\n"
|
||||
"CONFIG_USHORT=30000\n"
|
||||
"CONFIG_INT=123456\n"
|
||||
"CONFIG_ULONG=0xDEADBEEFC0DE\n"
|
||||
"CONFIG_STR=\"abracad\"\n"
|
||||
"CONFIG_MISSING=0",
|
||||
.data = {
|
||||
.bpf_syscall = true,
|
||||
.tristate_val = TRI_MODULE,
|
||||
.bool_val = true,
|
||||
.char_val = 100,
|
||||
.ushort_val = 30000,
|
||||
.int_val = 123456,
|
||||
.ulong_val = 0xDEADBEEFC0DE,
|
||||
.str_val = "abracad",
|
||||
},
|
||||
},
|
||||
/* TRISTATE */
|
||||
{ .name = "tristate (y)", .cfg = CFG"CONFIG_TRISTATE=y\n",
|
||||
.data = { .tristate_val = TRI_YES } },
|
||||
{ .name = "tristate (n)", .cfg = CFG"CONFIG_TRISTATE=n\n",
|
||||
.data = { .tristate_val = TRI_NO } },
|
||||
{ .name = "tristate (m)", .cfg = CFG"CONFIG_TRISTATE=m\n",
|
||||
.data = { .tristate_val = TRI_MODULE } },
|
||||
{ .name = "tristate (int)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=1" },
|
||||
{ .name = "tristate (bad)", .fails = 1, .cfg = CFG"CONFIG_TRISTATE=M" },
|
||||
/* BOOL */
|
||||
{ .name = "bool (y)", .cfg = CFG"CONFIG_BOOL=y\n",
|
||||
.data = { .bool_val = true } },
|
||||
{ .name = "bool (n)", .cfg = CFG"CONFIG_BOOL=n\n",
|
||||
.data = { .bool_val = false } },
|
||||
{ .name = "bool (tristate)", .fails = 1, .cfg = CFG"CONFIG_BOOL=m" },
|
||||
{ .name = "bool (int)", .fails = 1, .cfg = CFG"CONFIG_BOOL=1" },
|
||||
/* CHAR */
|
||||
{ .name = "char (tristate)", .cfg = CFG"CONFIG_CHAR=m\n",
|
||||
.data = { .char_val = 'm' } },
|
||||
{ .name = "char (bad)", .fails = 1, .cfg = CFG"CONFIG_CHAR=q\n" },
|
||||
{ .name = "char (empty)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\n" },
|
||||
{ .name = "char (str)", .fails = 1, .cfg = CFG"CONFIG_CHAR=\"y\"\n" },
|
||||
/* STRING */
|
||||
{ .name = "str (empty)", .cfg = CFG"CONFIG_STR=\"\"\n",
|
||||
.data = { .str_val = "\0\0\0\0\0\0\0" } },
|
||||
{ .name = "str (padded)", .cfg = CFG"CONFIG_STR=\"abra\"\n",
|
||||
.data = { .str_val = "abra\0\0\0" } },
|
||||
{ .name = "str (too long)", .cfg = CFG"CONFIG_STR=\"abracada\"\n",
|
||||
.data = { .str_val = "abracad" } },
|
||||
{ .name = "str (no value)", .fails = 1, .cfg = CFG"CONFIG_STR=\n" },
|
||||
{ .name = "str (bad value)", .fails = 1, .cfg = CFG"CONFIG_STR=bla\n" },
|
||||
/* INTEGERS */
|
||||
{
|
||||
.name = "integer forms",
|
||||
.cfg = CFG
|
||||
"CONFIG_CHAR=0xA\n"
|
||||
"CONFIG_USHORT=0462\n"
|
||||
"CONFIG_INT=-100\n"
|
||||
"CONFIG_ULONG=+1000000000000",
|
||||
.data = {
|
||||
.char_val = 0xA,
|
||||
.ushort_val = 0462,
|
||||
.int_val = -100,
|
||||
.ulong_val = 1000000000000,
|
||||
},
|
||||
},
|
||||
{ .name = "int (bad)", .fails = 1, .cfg = CFG"CONFIG_INT=abc" },
|
||||
{ .name = "int (str)", .fails = 1, .cfg = CFG"CONFIG_INT=\"abc\"" },
|
||||
{ .name = "int (empty)", .fails = 1, .cfg = CFG"CONFIG_INT=" },
|
||||
{ .name = "int (mixed)", .fails = 1, .cfg = CFG"CONFIG_INT=123abc" },
|
||||
{ .name = "int (max)", .cfg = CFG"CONFIG_INT=2147483647",
|
||||
.data = { .int_val = 2147483647 } },
|
||||
{ .name = "int (min)", .cfg = CFG"CONFIG_INT=-2147483648",
|
||||
.data = { .int_val = -2147483648 } },
|
||||
{ .name = "int (max+1)", .fails = 1, .cfg = CFG"CONFIG_INT=2147483648" },
|
||||
{ .name = "int (min-1)", .fails = 1, .cfg = CFG"CONFIG_INT=-2147483649" },
|
||||
{ .name = "ushort (max)", .cfg = CFG"CONFIG_USHORT=65535",
|
||||
.data = { .ushort_val = 65535 } },
|
||||
{ .name = "ushort (min)", .cfg = CFG"CONFIG_USHORT=0",
|
||||
.data = { .ushort_val = 0 } },
|
||||
{ .name = "ushort (max+1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=65536" },
|
||||
{ .name = "ushort (min-1)", .fails = 1, .cfg = CFG"CONFIG_USHORT=-1" },
|
||||
{ .name = "u64 (max)", .cfg = CFG"CONFIG_ULONG=0xffffffffffffffff",
|
||||
.data = { .ulong_val = 0xffffffffffffffff } },
|
||||
{ .name = "u64 (min)", .cfg = CFG"CONFIG_ULONG=0",
|
||||
.data = { .ulong_val = 0 } },
|
||||
{ .name = "u64 (max+1)", .fails = 1, .cfg = CFG"CONFIG_ULONG=0x10000000000000000" },
|
||||
};
|
||||
|
||||
BPF_EMBED_OBJ(core_extern, "test_core_extern.o");
|
||||
|
||||
void test_core_extern(void)
|
||||
{
|
||||
const uint32_t kern_ver = get_kernel_version();
|
||||
int err, duration = 0, i, j;
|
||||
struct test_core_extern *skel = NULL;
|
||||
uint64_t *got, *exp;
|
||||
int n = sizeof(*skel->data) / sizeof(uint64_t);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(test_cases); i++) {
|
||||
char tmp_cfg_path[] = "/tmp/test_core_extern_cfg.XXXXXX";
|
||||
struct test_case *t = &test_cases[i];
|
||||
DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
|
||||
.kconfig_path = t->cfg_path,
|
||||
);
|
||||
|
||||
if (!test__start_subtest(t->name))
|
||||
continue;
|
||||
|
||||
if (t->cfg) {
|
||||
size_t n = strlen(t->cfg) + 1;
|
||||
int fd = mkstemp(tmp_cfg_path);
|
||||
int written;
|
||||
|
||||
if (CHECK(fd < 0, "mkstemp", "errno: %d\n", errno))
|
||||
continue;
|
||||
printf("using '%s' as config file\n", tmp_cfg_path);
|
||||
written = write(fd, t->cfg, n);
|
||||
close(fd);
|
||||
if (CHECK_FAIL(written != n))
|
||||
goto cleanup;
|
||||
opts.kconfig_path = tmp_cfg_path;
|
||||
}
|
||||
|
||||
skel = test_core_extern__open_opts(&core_extern_embed, &opts);
|
||||
if (CHECK(!skel, "skel_open", "skeleton open failed\n"))
|
||||
goto cleanup;
|
||||
err = test_core_extern__load(skel);
|
||||
if (t->fails) {
|
||||
CHECK(!err, "skel_load",
|
||||
"shouldn't succeed open/load of skeleton\n");
|
||||
goto cleanup;
|
||||
} else if (CHECK(err, "skel_load",
|
||||
"failed to open/load skeleton\n")) {
|
||||
goto cleanup;
|
||||
}
|
||||
err = test_core_extern__attach(skel);
|
||||
if (CHECK(err, "attach_raw_tp", "failed attach: %d\n", err))
|
||||
goto cleanup;
|
||||
|
||||
usleep(1);
|
||||
|
||||
t->data.kern_ver = kern_ver;
|
||||
t->data.missing_val = 0xDEADC0DE;
|
||||
got = (uint64_t *)skel->data;
|
||||
exp = (uint64_t *)&t->data;
|
||||
for (j = 0; j < n; j++) {
|
||||
CHECK(got[j] != exp[j], "check_res",
|
||||
"result #%d: expected %lx, but got %lx\n",
|
||||
j, exp[j], got[j]);
|
||||
}
|
||||
cleanup:
|
||||
if (t->cfg)
|
||||
unlink(tmp_cfg_path);
|
||||
test_core_extern__destroy(skel);
|
||||
skel = NULL;
|
||||
}
|
||||
}
|
|
@ -17,11 +17,21 @@ void test_skeleton(void)
|
|||
int duration = 0, err;
|
||||
struct test_skeleton* skel;
|
||||
struct test_skeleton__bss *bss;
|
||||
struct test_skeleton__externs *exts;
|
||||
|
||||
skel = test_skeleton__open_and_load(&skeleton_embed);
|
||||
skel = test_skeleton__open(&skeleton_embed);
|
||||
if (CHECK(!skel, "skel_open", "failed to open skeleton\n"))
|
||||
return;
|
||||
|
||||
printf("EXTERNS BEFORE: %p\n", skel->externs);
|
||||
if (CHECK(skel->externs, "skel_externs", "externs are mmaped()!\n"))
|
||||
goto cleanup;
|
||||
|
||||
err = test_skeleton__load(skel);
|
||||
if (CHECK(err, "skel_load", "failed to load skeleton: %d\n", err))
|
||||
goto cleanup;
|
||||
printf("EXTERNS AFTER: %p\n", skel->externs);
|
||||
|
||||
bss = skel->bss;
|
||||
bss->in1 = 1;
|
||||
bss->in2 = 2;
|
||||
|
@ -29,6 +39,7 @@ void test_skeleton(void)
|
|||
bss->in4 = 4;
|
||||
bss->in5.a = 5;
|
||||
bss->in5.b = 6;
|
||||
exts = skel->externs;
|
||||
|
||||
err = test_skeleton__attach(skel);
|
||||
if (CHECK(err, "skel_attach", "skeleton attach failed: %d\n", err))
|
||||
|
@ -46,6 +57,11 @@ void test_skeleton(void)
|
|||
CHECK(bss->handler_out5.b != 6, "res6", "got %lld != exp %d\n",
|
||||
bss->handler_out5.b, 6);
|
||||
|
||||
CHECK(bss->bpf_syscall != exts->CONFIG_BPF_SYSCALL, "ext1",
|
||||
"got %d != exp %d\n", bss->bpf_syscall, exts->CONFIG_BPF_SYSCALL);
|
||||
CHECK(bss->kern_ver != exts->LINUX_KERNEL_VERSION, "ext2",
|
||||
"got %d != exp %d\n", bss->kern_ver, exts->LINUX_KERNEL_VERSION);
|
||||
|
||||
cleanup:
|
||||
test_skeleton__destroy(skel);
|
||||
}
|
||||
|
|
62
tools/testing/selftests/bpf/progs/test_core_extern.c
Normal file
62
tools/testing/selftests/bpf/progs/test_core_extern.c
Normal file
|
@ -0,0 +1,62 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <linux/ptrace.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
/* non-existing BPF helper, to test dead code elimination */
|
||||
static int (*bpf_missing_helper)(const void *arg1, int arg2) = (void *) 999;
|
||||
|
||||
extern int LINUX_KERNEL_VERSION;
|
||||
extern bool CONFIG_BPF_SYSCALL; /* strong */
|
||||
extern enum libbpf_tristate CONFIG_TRISTATE __weak;
|
||||
extern bool CONFIG_BOOL __weak;
|
||||
extern char CONFIG_CHAR __weak;
|
||||
extern uint16_t CONFIG_USHORT __weak;
|
||||
extern int CONFIG_INT __weak;
|
||||
extern uint64_t CONFIG_ULONG __weak;
|
||||
extern const char CONFIG_STR[8] __weak;
|
||||
extern uint64_t CONFIG_MISSING __weak;
|
||||
|
||||
uint64_t kern_ver = -1;
|
||||
uint64_t bpf_syscall = -1;
|
||||
uint64_t tristate_val = -1;
|
||||
uint64_t bool_val = -1;
|
||||
uint64_t char_val = -1;
|
||||
uint64_t ushort_val = -1;
|
||||
uint64_t int_val = -1;
|
||||
uint64_t ulong_val = -1;
|
||||
char str_val[8] = {-1, -1, -1, -1, -1, -1, -1, -1};
|
||||
uint64_t missing_val = -1;
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int handle_sys_enter(struct pt_regs *ctx)
|
||||
{
|
||||
int i;
|
||||
|
||||
kern_ver = LINUX_KERNEL_VERSION;
|
||||
bpf_syscall = CONFIG_BPF_SYSCALL;
|
||||
tristate_val = CONFIG_TRISTATE;
|
||||
bool_val = CONFIG_BOOL;
|
||||
char_val = CONFIG_CHAR;
|
||||
ushort_val = CONFIG_USHORT;
|
||||
int_val = CONFIG_INT;
|
||||
ulong_val = CONFIG_ULONG;
|
||||
|
||||
for (i = 0; i < sizeof(CONFIG_STR); i++) {
|
||||
str_val[i] = CONFIG_STR[i];
|
||||
}
|
||||
|
||||
if (CONFIG_MISSING)
|
||||
/* invalid, but dead code - never executed */
|
||||
missing_val = bpf_missing_helper(ctx, 123);
|
||||
else
|
||||
missing_val = 0xDEADC0DE;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
char _license[] SEC("license") = "GPL";
|
|
@ -1,6 +1,7 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
/* Copyright (c) 2019 Facebook */
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <linux/bpf.h>
|
||||
#include "bpf_helpers.h"
|
||||
|
||||
|
@ -20,6 +21,10 @@ char out3 = 0;
|
|||
long long out4 = 0;
|
||||
int out1 = 0;
|
||||
|
||||
extern bool CONFIG_BPF_SYSCALL;
|
||||
extern int LINUX_KERNEL_VERSION;
|
||||
bool bpf_syscall = 0;
|
||||
int kern_ver = 0;
|
||||
|
||||
SEC("raw_tp/sys_enter")
|
||||
int handler(const void *ctx)
|
||||
|
@ -31,6 +36,10 @@ int handler(const void *ctx)
|
|||
out3 = in3;
|
||||
out4 = in4;
|
||||
out5 = in5;
|
||||
|
||||
bpf_syscall = CONFIG_BPF_SYSCALL;
|
||||
kern_ver = LINUX_KERNEL_VERSION;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue