mirror of
https://github.com/netdata/libbpf.git
synced 2026-03-14 13:29:06 +08:00
Compare commits
43 Commits
libbpf_1_2
...
netdata_pa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d42052959d | ||
|
|
360a2fd909 | ||
|
|
05f94ddbb8 | ||
|
|
bf88aaa6fe | ||
|
|
f117080307 | ||
|
|
8b905090e8 | ||
|
|
6c020e6c47 | ||
|
|
1743bd1e40 | ||
|
|
a2258003f2 | ||
|
|
add1aac281 | ||
|
|
ea27ebcffd | ||
|
|
b9c4ad5468 | ||
|
|
732c4c6df2 | ||
|
|
6bec18258c | ||
|
|
3f33f9a6b8 | ||
|
|
ec6f716eda | ||
|
|
3c7fcfe0ce | ||
|
|
ef3e2ef82a | ||
|
|
45188d0d01 | ||
|
|
f02ec78083 | ||
|
|
fa1a18d38b | ||
|
|
ba7a44da68 | ||
|
|
cb23f981c3 | ||
|
|
f7eb43b90f | ||
|
|
9710829e78 | ||
|
|
e021ccbd7d | ||
|
|
0755b497cf | ||
|
|
c4ffdf1e72 | ||
|
|
c850306199 | ||
|
|
fb6998382d | ||
|
|
9aea1da2bb | ||
|
|
8b4e1b39a4 | ||
|
|
a50544ef45 | ||
|
|
bfb0454244 | ||
|
|
79811cad50 | ||
|
|
4bb0b0ca09 | ||
|
|
ac42790129 | ||
|
|
6a6cf6dcdc | ||
|
|
b9711e7015 | ||
|
|
4c484d662c | ||
|
|
1c9aa4791a | ||
|
|
3f591a6610 | ||
|
|
532293bdf4 |
2
.gitattributes
vendored
2
.gitattributes
vendored
@@ -1 +1 @@
|
||||
assets/ export-ignore
|
||||
assets/** export-ignore
|
||||
|
||||
@@ -40,6 +40,7 @@ else
|
||||
fi
|
||||
|
||||
cd ${REPO_ROOT}/${REPO_PATH}
|
||||
make headers
|
||||
make \
|
||||
CLANG=clang-${LLVM_VERSION} \
|
||||
LLC=llc-${LLVM_VERSION} \
|
||||
|
||||
152953
.github/actions/build-selftests/vmlinux.h
vendored
152953
.github/actions/build-selftests/vmlinux.h
vendored
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
71b547f561247897a0a14f3082730156c0533fed
|
||||
496720b7cfb6574a8f6f4d434f23e3d1e6cfaeb9
|
||||
|
||||
@@ -1 +1 @@
|
||||
2ddade322925641ee2a75f13665c51f2e74d7791
|
||||
a3e7e6b17946f48badce98d7ac360678a0ea7393
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
From ff8be5401b359e23ec2b74184034082564bac7c5 Mon Sep 17 00:00:00 2001
|
||||
From: =?UTF-8?q?Daniel=20M=C3=BCller?= <deso@posteo.net>
|
||||
Date: Thu, 25 May 2023 16:04:20 -0700
|
||||
Subject: [PATCH] selftests/bpf: Check whether to run selftest
|
||||
MIME-Version: 1.0
|
||||
Content-Type: text/plain; charset=UTF-8
|
||||
Content-Transfer-Encoding: 8bit
|
||||
|
||||
The sockopt test invokes test__start_subtest and then unconditionally
|
||||
asserts the success. That means that even if deny-listed, any test will
|
||||
still run and potentially fail.
|
||||
Evaluate the return value of test__start_subtest() to achieve the
|
||||
desired behavior, as other tests do.
|
||||
|
||||
Signed-off-by: Daniel Müller <deso@posteo.net>
|
||||
---
|
||||
tools/testing/selftests/bpf/prog_tests/sockopt.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/tools/testing/selftests/bpf/prog_tests/sockopt.c b/tools/testing/selftests/bpf/prog_tests/sockopt.c
|
||||
index 33dd45..9e6a5e 100644
|
||||
--- a/tools/testing/selftests/bpf/prog_tests/sockopt.c
|
||||
+++ b/tools/testing/selftests/bpf/prog_tests/sockopt.c
|
||||
@@ -1060,7 +1060,9 @@ void test_sockopt(void)
|
||||
return;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(tests); i++) {
|
||||
- test__start_subtest(tests[i].descr);
|
||||
+ if (!test__start_subtest(tests[i].descr))
|
||||
+ continue;
|
||||
+
|
||||
ASSERT_OK(run_test(cgroup_fd, &tests[i]), tests[i].descr);
|
||||
}
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
From d3484f640bc82cff459beb85a00f7ebab20f0a41 Mon Sep 17 00:00:00 2001
|
||||
From: "Masami Hiramatsu (Google)" <mhiramat@kernel.org>
|
||||
Date: Sun, 9 Apr 2023 11:28:31 +0900
|
||||
Subject: [PATCH] tracing: fprobe: Initialize ret valiable to fix smatch error
|
||||
|
||||
The commit 39d954200bf6 ("fprobe: Skip exit_handler if entry_handler returns
|
||||
!0") introduced a hidden dependency of 'ret' local variable in the
|
||||
fprobe_handler(), Smatch warns the `ret` can be accessed without
|
||||
initialization.
|
||||
|
||||
kernel/trace/fprobe.c:59 fprobe_handler()
|
||||
error: uninitialized symbol 'ret'.
|
||||
|
||||
kernel/trace/fprobe.c
|
||||
49 fpr->entry_ip = ip;
|
||||
50 if (fp->entry_data_size)
|
||||
51 entry_data = fpr->data;
|
||||
52 }
|
||||
53
|
||||
54 if (fp->entry_handler)
|
||||
55 ret = fp->entry_handler(fp, ip, ftrace_get_regs(fregs), entry_data);
|
||||
|
||||
ret is only initialized if there is an ->entry_handler
|
||||
|
||||
56
|
||||
57 /* If entry_handler returns !0, nmissed is not counted. */
|
||||
58 if (rh) {
|
||||
|
||||
rh is only true if there is an ->exit_handler. Presumably if you have
|
||||
and ->exit_handler that means you also have a ->entry_handler but Smatch
|
||||
is not smart enough to figure it out.
|
||||
|
||||
--> 59 if (ret)
|
||||
^^^
|
||||
Warning here.
|
||||
|
||||
60 rethook_recycle(rh);
|
||||
61 else
|
||||
62 rethook_hook(rh, ftrace_get_regs(fregs), true);
|
||||
63 }
|
||||
64 out:
|
||||
65 ftrace_test_recursion_unlock(bit);
|
||||
66 }
|
||||
|
||||
Reported-by: Dan Carpenter <error27@gmail.com>
|
||||
Link: https://lore.kernel.org/all/85429a5c-a4b9-499e-b6c0-cbd313291c49@kili.mountain
|
||||
Fixes: 39d954200bf6 ("fprobe: Skip exit_handler if entry_handler returns !0")
|
||||
Signed-off-by: Masami Hiramatsu (Google) <mhiramat@kernel.org>
|
||||
---
|
||||
kernel/trace/fprobe.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/kernel/trace/fprobe.c b/kernel/trace/fprobe.c
|
||||
index 9abb3905bc8e..293184227394 100644
|
||||
--- a/kernel/trace/fprobe.c
|
||||
+++ b/kernel/trace/fprobe.c
|
||||
@@ -27,7 +27,7 @@ static void fprobe_handler(unsigned long ip, unsigned long parent_ip,
|
||||
struct rethook_node *rh = NULL;
|
||||
struct fprobe *fp;
|
||||
void *entry_data = NULL;
|
||||
- int bit, ret;
|
||||
+ int bit, ret = 0;
|
||||
|
||||
fp = container_of(ops, struct fprobe, ops);
|
||||
if (fprobe_disabled(fp))
|
||||
--
|
||||
2.34.1
|
||||
|
||||
@@ -35,8 +35,6 @@ signal_pending
|
||||
skeleton
|
||||
sockmap_ktls
|
||||
sockopt
|
||||
sockopt_inherit
|
||||
sockopt_multi
|
||||
spinlock
|
||||
stacktrace_map
|
||||
stacktrace_map_raw_tp
|
||||
|
||||
@@ -78,6 +78,8 @@ sock_fields # v5.10+
|
||||
socket_cookie # v5.12+
|
||||
sockmap_basic # uses new socket fields, 5.8+
|
||||
sockmap_listen # no listen socket supportin SOCKMAP
|
||||
sockopt/getsockopt: ignore >PAGE_SIZE optlen
|
||||
sockopt/setsockopt: ignore >PAGE_SIZE optlen
|
||||
sockopt_sk
|
||||
sockopt_qos_to_cc # v5.15+
|
||||
stacktrace_build_id # v5.9+
|
||||
|
||||
@@ -1 +1,4 @@
|
||||
decap_sanity # weird failure with decap_sanity_ns netns already existing, TBD
|
||||
bpf_nf/tc-bpf-ct # test consistently failing on x86: https://github.com/libbpf/libbpf/pull/698#issuecomment-1590341200
|
||||
bpf_nf/xdp-ct # test consistently failing on x86: https://github.com/libbpf/libbpf/pull/698#issuecomment-1590341200
|
||||
kprobe_multi_bench_attach # suspected to cause crashes in CI
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# TEMPORARY
|
||||
sockmap_listen/sockhash VSOCK test_vsock_redir
|
||||
usdt/basic # failing verifier due to bounds check after LLVM update
|
||||
usdt/multispec # same as above
|
||||
|
||||
@@ -13,7 +13,7 @@ read_lists() {
|
||||
if [[ -s "$path" ]]; then
|
||||
cat "$path"
|
||||
fi;
|
||||
done) | cut -d'#' -f1 | tr -s ' \t\n' ','
|
||||
done) | cut -d'#' -f1 | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//' | tr -s '\n' ','
|
||||
}
|
||||
|
||||
test_progs() {
|
||||
@@ -22,7 +22,7 @@ test_progs() {
|
||||
# "&& true" does not change the return code (it is not executed
|
||||
# if the Python script fails), but it prevents exiting on a
|
||||
# failure due to the "set -e".
|
||||
./test_progs ${DENYLIST:+-d$DENYLIST} ${ALLOWLIST:+-a$ALLOWLIST} && true
|
||||
./test_progs ${DENYLIST:+-d"$DENYLIST"} ${ALLOWLIST:+-a"$ALLOWLIST"} && true
|
||||
echo "test_progs:$?" >> "${STATUS_FILE}"
|
||||
foldable end test_progs
|
||||
fi
|
||||
@@ -30,7 +30,7 @@ test_progs() {
|
||||
|
||||
test_progs_no_alu32() {
|
||||
foldable start test_progs-no_alu32 "Testing test_progs-no_alu32"
|
||||
./test_progs-no_alu32 ${DENYLIST:+-d$DENYLIST} ${ALLOWLIST:+-a$ALLOWLIST} && true
|
||||
./test_progs-no_alu32 ${DENYLIST:+-d"$DENYLIST"} ${ALLOWLIST:+-a"$ALLOWLIST"} && true
|
||||
echo "test_progs-no_alu32:$?" >> "${STATUS_FILE}"
|
||||
foldable end test_progs-no_alu32
|
||||
}
|
||||
@@ -55,6 +55,13 @@ test_verifier() {
|
||||
|
||||
foldable end vm_init
|
||||
|
||||
foldable start kernel_config "Kconfig"
|
||||
|
||||
zcat /proc/config.gz
|
||||
|
||||
foldable end kernel_config
|
||||
|
||||
|
||||
configs_path=/${PROJECT_NAME}/selftests/bpf
|
||||
local_configs_path=${PROJECT_NAME}/vmtest/configs
|
||||
DENYLIST=$(read_lists \
|
||||
|
||||
@@ -986,6 +986,7 @@ enum bpf_prog_type {
|
||||
BPF_PROG_TYPE_LSM,
|
||||
BPF_PROG_TYPE_SK_LOOKUP,
|
||||
BPF_PROG_TYPE_SYSCALL, /* a program that can execute syscalls */
|
||||
BPF_PROG_TYPE_NETFILTER,
|
||||
};
|
||||
|
||||
enum bpf_attach_type {
|
||||
@@ -1034,6 +1035,7 @@ enum bpf_attach_type {
|
||||
BPF_TRACE_KPROBE_MULTI,
|
||||
BPF_LSM_CGROUP,
|
||||
BPF_STRUCT_OPS,
|
||||
BPF_NETFILTER,
|
||||
__MAX_BPF_ATTACH_TYPE
|
||||
};
|
||||
|
||||
@@ -1050,6 +1052,7 @@ enum bpf_link_type {
|
||||
BPF_LINK_TYPE_PERF_EVENT = 7,
|
||||
BPF_LINK_TYPE_KPROBE_MULTI = 8,
|
||||
BPF_LINK_TYPE_STRUCT_OPS = 9,
|
||||
BPF_LINK_TYPE_NETFILTER = 10,
|
||||
|
||||
MAX_BPF_LINK_TYPE,
|
||||
};
|
||||
@@ -1270,6 +1273,9 @@ enum {
|
||||
|
||||
/* Create a map that will be registered/unregesitered by the backed bpf_link */
|
||||
BPF_F_LINK = (1U << 13),
|
||||
|
||||
/* Get path from provided FD in BPF_OBJ_PIN/BPF_OBJ_GET commands */
|
||||
BPF_F_PATH_FD = (1U << 14),
|
||||
};
|
||||
|
||||
/* Flags for BPF_PROG_QUERY. */
|
||||
@@ -1418,6 +1424,13 @@ union bpf_attr {
|
||||
__aligned_u64 pathname;
|
||||
__u32 bpf_fd;
|
||||
__u32 file_flags;
|
||||
/* Same as dirfd in openat() syscall; see openat(2)
|
||||
* manpage for details of path FD and pathname semantics;
|
||||
* path_fd should accompanied by BPF_F_PATH_FD flag set in
|
||||
* file_flags field, otherwise it should be set to zero;
|
||||
* if BPF_F_PATH_FD flag is not set, AT_FDCWD is assumed.
|
||||
*/
|
||||
__s32 path_fd;
|
||||
};
|
||||
|
||||
struct { /* anonymous struct used by BPF_PROG_ATTACH/DETACH commands */
|
||||
@@ -1560,6 +1573,12 @@ union bpf_attr {
|
||||
*/
|
||||
__u64 cookie;
|
||||
} tracing;
|
||||
struct {
|
||||
__u32 pf;
|
||||
__u32 hooknum;
|
||||
__s32 priority;
|
||||
__u32 flags;
|
||||
} netfilter;
|
||||
};
|
||||
} link_create;
|
||||
|
||||
@@ -3159,6 +3178,10 @@ union bpf_attr {
|
||||
* **BPF_FIB_LOOKUP_DIRECT**
|
||||
* Do a direct table lookup vs full lookup using FIB
|
||||
* rules.
|
||||
* **BPF_FIB_LOOKUP_TBID**
|
||||
* Used with BPF_FIB_LOOKUP_DIRECT.
|
||||
* Use the routing table ID present in *params*->tbid
|
||||
* for the fib lookup.
|
||||
* **BPF_FIB_LOOKUP_OUTPUT**
|
||||
* Perform lookup from an egress perspective (default is
|
||||
* ingress).
|
||||
@@ -6410,6 +6433,12 @@ struct bpf_link_info {
|
||||
struct {
|
||||
__u32 map_id;
|
||||
} struct_ops;
|
||||
struct {
|
||||
__u32 pf;
|
||||
__u32 hooknum;
|
||||
__s32 priority;
|
||||
__u32 flags;
|
||||
} netfilter;
|
||||
};
|
||||
} __attribute__((aligned(8)));
|
||||
|
||||
@@ -6807,6 +6836,7 @@ enum {
|
||||
BPF_FIB_LOOKUP_DIRECT = (1U << 0),
|
||||
BPF_FIB_LOOKUP_OUTPUT = (1U << 1),
|
||||
BPF_FIB_LOOKUP_SKIP_NEIGH = (1U << 2),
|
||||
BPF_FIB_LOOKUP_TBID = (1U << 3),
|
||||
};
|
||||
|
||||
enum {
|
||||
@@ -6867,9 +6897,19 @@ struct bpf_fib_lookup {
|
||||
__u32 ipv6_dst[4]; /* in6_addr; network order */
|
||||
};
|
||||
|
||||
/* output */
|
||||
__be16 h_vlan_proto;
|
||||
__be16 h_vlan_TCI;
|
||||
union {
|
||||
struct {
|
||||
/* output */
|
||||
__be16 h_vlan_proto;
|
||||
__be16 h_vlan_TCI;
|
||||
};
|
||||
/* input: when accompanied with the
|
||||
* 'BPF_FIB_LOOKUP_DIRECT | BPF_FIB_LOOKUP_TBID` flags, a
|
||||
* specific routing table to use for the fib lookup.
|
||||
*/
|
||||
__u32 tbid;
|
||||
};
|
||||
|
||||
__u8 smac[6]; /* ETH_ALEN */
|
||||
__u8 dmac[6]; /* ETH_ALEN */
|
||||
};
|
||||
|
||||
@@ -44,11 +44,11 @@ rm -rf elfutils
|
||||
git clone git://sourceware.org/git/elfutils.git
|
||||
(
|
||||
cd elfutils
|
||||
git checkout e9f3045caa5c4498f371383e5519151942d48b6d
|
||||
git checkout 67a187d4c1790058fc7fd218317851cb68bb087c
|
||||
git log --oneline -1
|
||||
|
||||
# ASan isn't compatible with -Wl,--no-undefined: https://github.com/google/sanitizers/issues/380
|
||||
find -name Makefile.am | xargs sed -i 's/,--no-undefined//'
|
||||
sed -i 's/^\(NO_UNDEFINED=\).*/\1/' configure.ac
|
||||
|
||||
# ASan isn't compatible with -Wl,-z,defs either:
|
||||
# https://clang.llvm.org/docs/AddressSanitizer.html#usage
|
||||
@@ -62,6 +62,7 @@ fi
|
||||
|
||||
autoreconf -i -f
|
||||
if ! ./configure --enable-maintainer-mode --disable-debuginfod --disable-libdebuginfod \
|
||||
--disable-demangler --without-bzlib --without-lzma --without-zstd \
|
||||
CC="$CC" CFLAGS="-Wno-error $CFLAGS" CXX="$CXX" CXXFLAGS="-Wno-error $CXXFLAGS" LDFLAGS="$CFLAGS"; then
|
||||
cat config.log
|
||||
exit 1
|
||||
|
||||
@@ -9,7 +9,7 @@ else
|
||||
endif
|
||||
|
||||
LIBBPF_MAJOR_VERSION := 1
|
||||
LIBBPF_MINOR_VERSION := 2
|
||||
LIBBPF_MINOR_VERSION := 3
|
||||
LIBBPF_PATCH_VERSION := 0
|
||||
LIBBPF_VERSION := $(LIBBPF_MAJOR_VERSION).$(LIBBPF_MINOR_VERSION).$(LIBBPF_PATCH_VERSION)
|
||||
LIBBPF_MAJMIN_VERSION := $(LIBBPF_MAJOR_VERSION).$(LIBBPF_MINOR_VERSION).0
|
||||
|
||||
25
src/bpf.c
25
src/bpf.c
@@ -572,20 +572,30 @@ int bpf_map_update_batch(int fd, const void *keys, const void *values, __u32 *co
|
||||
(void *)keys, (void *)values, count, opts);
|
||||
}
|
||||
|
||||
int bpf_obj_pin(int fd, const char *pathname)
|
||||
int bpf_obj_pin_opts(int fd, const char *pathname, const struct bpf_obj_pin_opts *opts)
|
||||
{
|
||||
const size_t attr_sz = offsetofend(union bpf_attr, file_flags);
|
||||
const size_t attr_sz = offsetofend(union bpf_attr, path_fd);
|
||||
union bpf_attr attr;
|
||||
int ret;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_obj_pin_opts))
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, attr_sz);
|
||||
attr.path_fd = OPTS_GET(opts, path_fd, 0);
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
attr.file_flags = OPTS_GET(opts, file_flags, 0);
|
||||
attr.bpf_fd = fd;
|
||||
|
||||
ret = sys_bpf(BPF_OBJ_PIN, &attr, attr_sz);
|
||||
return libbpf_err_errno(ret);
|
||||
}
|
||||
|
||||
int bpf_obj_pin(int fd, const char *pathname)
|
||||
{
|
||||
return bpf_obj_pin_opts(fd, pathname, NULL);
|
||||
}
|
||||
|
||||
int bpf_obj_get(const char *pathname)
|
||||
{
|
||||
return bpf_obj_get_opts(pathname, NULL);
|
||||
@@ -593,7 +603,7 @@ int bpf_obj_get(const char *pathname)
|
||||
|
||||
int bpf_obj_get_opts(const char *pathname, const struct bpf_obj_get_opts *opts)
|
||||
{
|
||||
const size_t attr_sz = offsetofend(union bpf_attr, file_flags);
|
||||
const size_t attr_sz = offsetofend(union bpf_attr, path_fd);
|
||||
union bpf_attr attr;
|
||||
int fd;
|
||||
|
||||
@@ -601,6 +611,7 @@ int bpf_obj_get_opts(const char *pathname, const struct bpf_obj_get_opts *opts)
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
memset(&attr, 0, attr_sz);
|
||||
attr.path_fd = OPTS_GET(opts, path_fd, 0);
|
||||
attr.pathname = ptr_to_u64((void *)pathname);
|
||||
attr.file_flags = OPTS_GET(opts, file_flags, 0);
|
||||
|
||||
@@ -730,6 +741,14 @@ int bpf_link_create(int prog_fd, int target_fd,
|
||||
if (!OPTS_ZEROED(opts, tracing))
|
||||
return libbpf_err(-EINVAL);
|
||||
break;
|
||||
case BPF_NETFILTER:
|
||||
attr.link_create.netfilter.pf = OPTS_GET(opts, netfilter.pf, 0);
|
||||
attr.link_create.netfilter.hooknum = OPTS_GET(opts, netfilter.hooknum, 0);
|
||||
attr.link_create.netfilter.priority = OPTS_GET(opts, netfilter.priority, 0);
|
||||
attr.link_create.netfilter.flags = OPTS_GET(opts, netfilter.flags, 0);
|
||||
if (!OPTS_ZEROED(opts, netfilter))
|
||||
return libbpf_err(-EINVAL);
|
||||
break;
|
||||
default:
|
||||
if (!OPTS_ZEROED(opts, flags))
|
||||
return libbpf_err(-EINVAL);
|
||||
|
||||
24
src/bpf.h
24
src/bpf.h
@@ -284,16 +284,30 @@ LIBBPF_API int bpf_map_update_batch(int fd, const void *keys, const void *values
|
||||
__u32 *count,
|
||||
const struct bpf_map_batch_opts *opts);
|
||||
|
||||
struct bpf_obj_pin_opts {
|
||||
size_t sz; /* size of this struct for forward/backward compatibility */
|
||||
|
||||
__u32 file_flags;
|
||||
int path_fd;
|
||||
|
||||
size_t :0;
|
||||
};
|
||||
#define bpf_obj_pin_opts__last_field path_fd
|
||||
|
||||
LIBBPF_API int bpf_obj_pin(int fd, const char *pathname);
|
||||
LIBBPF_API int bpf_obj_pin_opts(int fd, const char *pathname,
|
||||
const struct bpf_obj_pin_opts *opts);
|
||||
|
||||
struct bpf_obj_get_opts {
|
||||
size_t sz; /* size of this struct for forward/backward compatibility */
|
||||
|
||||
__u32 file_flags;
|
||||
int path_fd;
|
||||
|
||||
size_t :0;
|
||||
};
|
||||
#define bpf_obj_get_opts__last_field file_flags
|
||||
#define bpf_obj_get_opts__last_field path_fd
|
||||
|
||||
LIBBPF_API int bpf_obj_pin(int fd, const char *pathname);
|
||||
LIBBPF_API int bpf_obj_get(const char *pathname);
|
||||
LIBBPF_API int bpf_obj_get_opts(const char *pathname,
|
||||
const struct bpf_obj_get_opts *opts);
|
||||
@@ -335,6 +349,12 @@ struct bpf_link_create_opts {
|
||||
struct {
|
||||
__u64 cookie;
|
||||
} tracing;
|
||||
struct {
|
||||
__u32 pf;
|
||||
__u32 hooknum;
|
||||
__s32 priority;
|
||||
__u32 flags;
|
||||
} netfilter;
|
||||
};
|
||||
size_t :0;
|
||||
};
|
||||
|
||||
@@ -1832,6 +1832,10 @@ static long (*bpf_skb_load_bytes_relative)(const void *skb, __u32 offset, void *
|
||||
* **BPF_FIB_LOOKUP_DIRECT**
|
||||
* Do a direct table lookup vs full lookup using FIB
|
||||
* rules.
|
||||
* **BPF_FIB_LOOKUP_TBID**
|
||||
* Used with BPF_FIB_LOOKUP_DIRECT.
|
||||
* Use the routing table ID present in *params*->tbid
|
||||
* for the fib lookup.
|
||||
* **BPF_FIB_LOOKUP_OUTPUT**
|
||||
* Perform lookup from an egress perspective (default is
|
||||
* ingress).
|
||||
|
||||
@@ -77,16 +77,21 @@
|
||||
/*
|
||||
* Helper macros to manipulate data structures
|
||||
*/
|
||||
#ifndef offsetof
|
||||
#define offsetof(TYPE, MEMBER) ((unsigned long)&((TYPE *)0)->MEMBER)
|
||||
#endif
|
||||
#ifndef container_of
|
||||
|
||||
/* offsetof() definition that uses __builtin_offset() might not preserve field
|
||||
* offset CO-RE relocation properly, so force-redefine offsetof() using
|
||||
* old-school approach which works with CO-RE correctly
|
||||
*/
|
||||
#undef offsetof
|
||||
#define offsetof(type, member) ((unsigned long)&((type *)0)->member)
|
||||
|
||||
/* redefined container_of() to ensure we use the above offsetof() macro */
|
||||
#undef container_of
|
||||
#define container_of(ptr, type, member) \
|
||||
({ \
|
||||
void *__mptr = (void *)(ptr); \
|
||||
((type *)(__mptr - offsetof(type, member))); \
|
||||
})
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Compiler (optimization) barrier.
|
||||
|
||||
@@ -351,6 +351,7 @@ struct pt_regs___arm64 {
|
||||
* https://github.com/riscv-non-isa/riscv-elf-psabi-doc/blob/master/riscv-cc.adoc#risc-v-calling-conventions
|
||||
*/
|
||||
|
||||
/* riscv provides struct user_regs_struct instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x))
|
||||
#define __PT_PARM1_REG a0
|
||||
#define __PT_PARM2_REG a1
|
||||
@@ -383,7 +384,7 @@ struct pt_regs___arm64 {
|
||||
* https://raw.githubusercontent.com/wiki/foss-for-synopsys-dwc-arc-processors/toolchain/files/ARCv2_ABI.pdf
|
||||
*/
|
||||
|
||||
/* arc provides struct user_pt_regs instead of struct pt_regs to userspace */
|
||||
/* arc provides struct user_regs_struct instead of struct pt_regs to userspace */
|
||||
#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x))
|
||||
#define __PT_PARM1_REG scratch.r0
|
||||
#define __PT_PARM2_REG scratch.r1
|
||||
|
||||
@@ -1064,7 +1064,7 @@ static struct btf *btf_parse_raw(const char *path, struct btf *base_btf)
|
||||
int err = 0;
|
||||
long sz;
|
||||
|
||||
f = fopen(path, "rb");
|
||||
f = fopen(path, "rbe");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
goto err_out;
|
||||
|
||||
@@ -2250,9 +2250,25 @@ static int btf_dump_type_data_check_overflow(struct btf_dump *d,
|
||||
const struct btf_type *t,
|
||||
__u32 id,
|
||||
const void *data,
|
||||
__u8 bits_offset)
|
||||
__u8 bits_offset,
|
||||
__u8 bit_sz)
|
||||
{
|
||||
__s64 size = btf__resolve_size(d->btf, id);
|
||||
__s64 size;
|
||||
|
||||
if (bit_sz) {
|
||||
/* bits_offset is at most 7. bit_sz is at most 128. */
|
||||
__u8 nr_bytes = (bits_offset + bit_sz + 7) / 8;
|
||||
|
||||
/* When bit_sz is non zero, it is called from
|
||||
* btf_dump_struct_data() where it only cares about
|
||||
* negative error value.
|
||||
* Return nr_bytes in success case to make it
|
||||
* consistent as the regular integer case below.
|
||||
*/
|
||||
return data + nr_bytes > d->typed_dump->data_end ? -E2BIG : nr_bytes;
|
||||
}
|
||||
|
||||
size = btf__resolve_size(d->btf, id);
|
||||
|
||||
if (size < 0 || size >= INT_MAX) {
|
||||
pr_warn("unexpected size [%zu] for id [%u]\n",
|
||||
@@ -2407,7 +2423,7 @@ static int btf_dump_dump_type_data(struct btf_dump *d,
|
||||
{
|
||||
int size, err = 0;
|
||||
|
||||
size = btf_dump_type_data_check_overflow(d, t, id, data, bits_offset);
|
||||
size = btf_dump_type_data_check_overflow(d, t, id, data, bits_offset, bit_sz);
|
||||
if (size < 0)
|
||||
return size;
|
||||
err = btf_dump_type_data_check_zero(d, t, id, data, bits_offset, bit_sz);
|
||||
|
||||
@@ -703,17 +703,17 @@ static void emit_relo_kfunc_btf(struct bpf_gen *gen, struct ksym_relo_desc *relo
|
||||
/* obtain fd in BPF_REG_9 */
|
||||
emit(gen, BPF_MOV64_REG(BPF_REG_9, BPF_REG_7));
|
||||
emit(gen, BPF_ALU64_IMM(BPF_RSH, BPF_REG_9, 32));
|
||||
/* jump to fd_array store if fd denotes module BTF */
|
||||
/* load fd_array slot pointer */
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, blob_fd_array_off(gen, btf_fd_idx)));
|
||||
/* store BTF fd in slot, 0 for vmlinux */
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_9, 0));
|
||||
/* jump to insn[insn_idx].off store if fd denotes module BTF */
|
||||
emit(gen, BPF_JMP_IMM(BPF_JNE, BPF_REG_9, 0, 2));
|
||||
/* set the default value for off */
|
||||
emit(gen, BPF_ST_MEM(BPF_H, BPF_REG_8, offsetof(struct bpf_insn, off), 0));
|
||||
/* skip BTF fd store for vmlinux BTF */
|
||||
emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 4));
|
||||
/* load fd_array slot pointer */
|
||||
emit2(gen, BPF_LD_IMM64_RAW_FULL(BPF_REG_0, BPF_PSEUDO_MAP_IDX_VALUE,
|
||||
0, 0, 0, blob_fd_array_off(gen, btf_fd_idx)));
|
||||
/* store BTF fd in slot */
|
||||
emit(gen, BPF_STX_MEM(BPF_W, BPF_REG_0, BPF_REG_9, 0));
|
||||
emit(gen, BPF_JMP_IMM(BPF_JA, 0, 0, 1));
|
||||
/* store index into insn[insn_idx].off */
|
||||
emit(gen, BPF_ST_MEM(BPF_H, BPF_REG_8, offsetof(struct bpf_insn, off), btf_fd_idx));
|
||||
log:
|
||||
|
||||
@@ -80,16 +80,6 @@ struct hashmap {
|
||||
size_t sz;
|
||||
};
|
||||
|
||||
#define HASHMAP_INIT(hash_fn, equal_fn, ctx) { \
|
||||
.hash_fn = (hash_fn), \
|
||||
.equal_fn = (equal_fn), \
|
||||
.ctx = (ctx), \
|
||||
.buckets = NULL, \
|
||||
.cap = 0, \
|
||||
.cap_bits = 0, \
|
||||
.sz = 0, \
|
||||
}
|
||||
|
||||
void hashmap__init(struct hashmap *map, hashmap_hash_fn hash_fn,
|
||||
hashmap_equal_fn equal_fn, void *ctx);
|
||||
struct hashmap *hashmap__new(hashmap_hash_fn hash_fn,
|
||||
|
||||
418
src/libbpf.c
418
src/libbpf.c
@@ -117,6 +117,7 @@ static const char * const attach_type_name[] = {
|
||||
[BPF_PERF_EVENT] = "perf_event",
|
||||
[BPF_TRACE_KPROBE_MULTI] = "trace_kprobe_multi",
|
||||
[BPF_STRUCT_OPS] = "struct_ops",
|
||||
[BPF_NETFILTER] = "netfilter",
|
||||
};
|
||||
|
||||
static const char * const link_type_name[] = {
|
||||
@@ -130,6 +131,7 @@ static const char * const link_type_name[] = {
|
||||
[BPF_LINK_TYPE_PERF_EVENT] = "perf_event",
|
||||
[BPF_LINK_TYPE_KPROBE_MULTI] = "kprobe_multi",
|
||||
[BPF_LINK_TYPE_STRUCT_OPS] = "struct_ops",
|
||||
[BPF_LINK_TYPE_NETFILTER] = "netfilter",
|
||||
};
|
||||
|
||||
static const char * const map_type_name[] = {
|
||||
@@ -201,6 +203,7 @@ static const char * const prog_type_name[] = {
|
||||
[BPF_PROG_TYPE_LSM] = "lsm",
|
||||
[BPF_PROG_TYPE_SK_LOOKUP] = "sk_lookup",
|
||||
[BPF_PROG_TYPE_SYSCALL] = "syscall",
|
||||
[BPF_PROG_TYPE_NETFILTER] = "netfilter",
|
||||
};
|
||||
|
||||
static int __base_pr(enum libbpf_print_level level, const char *format,
|
||||
@@ -1359,7 +1362,7 @@ static int bpf_object__elf_init(struct bpf_object *obj)
|
||||
goto errout;
|
||||
}
|
||||
|
||||
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
|
||||
/* ELF is corrupted/truncated, avoid calling elf_strptr. */
|
||||
if (!elf_rawdata(elf_getscn(elf, obj->efile.shstrndx), NULL)) {
|
||||
pr_warn("elf: failed to get section names strings from %s: %s\n",
|
||||
obj->path, elf_errmsg(-1));
|
||||
@@ -1498,16 +1501,36 @@ static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
|
||||
return map;
|
||||
}
|
||||
|
||||
static size_t bpf_map_mmap_sz(const struct bpf_map *map)
|
||||
static size_t bpf_map_mmap_sz(unsigned int value_sz, unsigned int max_entries)
|
||||
{
|
||||
long page_sz = sysconf(_SC_PAGE_SIZE);
|
||||
const long page_sz = sysconf(_SC_PAGE_SIZE);
|
||||
size_t map_sz;
|
||||
|
||||
map_sz = (size_t)roundup(map->def.value_size, 8) * map->def.max_entries;
|
||||
map_sz = (size_t)roundup(value_sz, 8) * max_entries;
|
||||
map_sz = roundup(map_sz, page_sz);
|
||||
return map_sz;
|
||||
}
|
||||
|
||||
static int bpf_map_mmap_resize(struct bpf_map *map, size_t old_sz, size_t new_sz)
|
||||
{
|
||||
void *mmaped;
|
||||
|
||||
if (!map->mmaped)
|
||||
return -EINVAL;
|
||||
|
||||
if (old_sz == new_sz)
|
||||
return 0;
|
||||
|
||||
mmaped = mmap(NULL, new_sz, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if (mmaped == MAP_FAILED)
|
||||
return -errno;
|
||||
|
||||
memcpy(mmaped, map->mmaped, min(old_sz, new_sz));
|
||||
munmap(map->mmaped, old_sz);
|
||||
map->mmaped = mmaped;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static char *internal_map_name(struct bpf_object *obj, const char *real_name)
|
||||
{
|
||||
char map_name[BPF_OBJ_NAME_LEN], *p;
|
||||
@@ -1606,6 +1629,7 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
|
||||
{
|
||||
struct bpf_map_def *def;
|
||||
struct bpf_map *map;
|
||||
size_t mmap_sz;
|
||||
int err;
|
||||
|
||||
map = bpf_object__add_map(obj);
|
||||
@@ -1640,7 +1664,8 @@ bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
|
||||
pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n",
|
||||
map->name, map->sec_idx, map->sec_offset, def->map_flags);
|
||||
|
||||
map->mmaped = mmap(NULL, bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE,
|
||||
mmap_sz = bpf_map_mmap_sz(map->def.value_size, map->def.max_entries);
|
||||
map->mmaped = mmap(NULL, mmap_sz, PROT_READ | PROT_WRITE,
|
||||
MAP_SHARED | MAP_ANONYMOUS, -1, 0);
|
||||
if (map->mmaped == MAP_FAILED) {
|
||||
err = -errno;
|
||||
@@ -4327,7 +4352,7 @@ static int bpf_get_map_info_from_fdinfo(int fd, struct bpf_map_info *info)
|
||||
snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
|
||||
memset(info, 0, sizeof(*info));
|
||||
|
||||
fp = fopen(file, "r");
|
||||
fp = fopen(file, "re");
|
||||
if (!fp) {
|
||||
err = -errno;
|
||||
pr_warn("failed to open %s: %d. No procfs support?\n", file,
|
||||
@@ -4390,18 +4415,17 @@ int bpf_map__reuse_fd(struct bpf_map *map, int fd)
|
||||
if (!new_name)
|
||||
return libbpf_err(-errno);
|
||||
|
||||
new_fd = open("/", O_RDONLY | O_CLOEXEC);
|
||||
/*
|
||||
* Like dup(), but make sure new FD is >= 3 and has O_CLOEXEC set.
|
||||
* This is similar to what we do in ensure_good_fd(), but without
|
||||
* closing original FD.
|
||||
*/
|
||||
new_fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
|
||||
if (new_fd < 0) {
|
||||
err = -errno;
|
||||
goto err_free_new_name;
|
||||
}
|
||||
|
||||
new_fd = dup3(fd, new_fd, O_CLOEXEC);
|
||||
if (new_fd < 0) {
|
||||
err = -errno;
|
||||
goto err_close_new_fd;
|
||||
}
|
||||
|
||||
err = zclose(map->fd);
|
||||
if (err) {
|
||||
err = -errno;
|
||||
@@ -5447,6 +5471,10 @@ static int load_module_btfs(struct bpf_object *obj)
|
||||
err = bpf_btf_get_next_id(id, &id);
|
||||
if (err && errno == ENOENT)
|
||||
return 0;
|
||||
if (err && errno == EPERM) {
|
||||
pr_debug("skipping module BTFs loading, missing privileges\n");
|
||||
return 0;
|
||||
}
|
||||
if (err) {
|
||||
err = -errno;
|
||||
pr_warn("failed to iterate BTF objects: %d\n", err);
|
||||
@@ -6133,7 +6161,11 @@ static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_progra
|
||||
if (main_prog == subprog)
|
||||
return 0;
|
||||
relos = libbpf_reallocarray(main_prog->reloc_desc, new_cnt, sizeof(*relos));
|
||||
if (!relos)
|
||||
/* if new count is zero, reallocarray can return a valid NULL result;
|
||||
* in this case the previous pointer will be freed, so we *have to*
|
||||
* reassign old pointer to the new value (even if it's NULL)
|
||||
*/
|
||||
if (!relos && new_cnt)
|
||||
return -ENOMEM;
|
||||
if (subprog->nr_reloc)
|
||||
memcpy(relos + main_prog->nr_reloc, subprog->reloc_desc,
|
||||
@@ -7431,7 +7463,7 @@ int libbpf_kallsyms_parse(kallsyms_cb_t cb, void *ctx)
|
||||
int ret, err = 0;
|
||||
FILE *f;
|
||||
|
||||
f = fopen("/proc/kallsyms", "r");
|
||||
f = fopen("/proc/kallsyms", "re");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
pr_warn("failed to open /proc/kallsyms: %d\n", err);
|
||||
@@ -8292,7 +8324,10 @@ static void bpf_map__destroy(struct bpf_map *map)
|
||||
map->init_slots_sz = 0;
|
||||
|
||||
if (map->mmaped) {
|
||||
munmap(map->mmaped, bpf_map_mmap_sz(map));
|
||||
size_t mmap_sz;
|
||||
|
||||
mmap_sz = bpf_map_mmap_sz(map->def.value_size, map->def.max_entries);
|
||||
munmap(map->mmaped, mmap_sz);
|
||||
map->mmaped = NULL;
|
||||
}
|
||||
|
||||
@@ -8501,7 +8536,8 @@ int bpf_program__set_insns(struct bpf_program *prog,
|
||||
return -EBUSY;
|
||||
|
||||
insns = libbpf_reallocarray(prog->insns, new_insn_cnt, sizeof(*insns));
|
||||
if (!insns) {
|
||||
/* NULL is a valid return from reallocarray if the new count is zero */
|
||||
if (!insns && new_insn_cnt) {
|
||||
pr_warn("prog '%s': failed to realloc prog code\n", prog->name);
|
||||
return -ENOMEM;
|
||||
}
|
||||
@@ -8531,13 +8567,31 @@ enum bpf_prog_type bpf_program__type(const struct bpf_program *prog)
|
||||
return prog->type;
|
||||
}
|
||||
|
||||
static size_t custom_sec_def_cnt;
|
||||
static struct bpf_sec_def *custom_sec_defs;
|
||||
static struct bpf_sec_def custom_fallback_def;
|
||||
static bool has_custom_fallback_def;
|
||||
static int last_custom_sec_def_handler_id;
|
||||
|
||||
int bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
|
||||
{
|
||||
if (prog->obj->loaded)
|
||||
return libbpf_err(-EBUSY);
|
||||
|
||||
/* if type is not changed, do nothing */
|
||||
if (prog->type == type)
|
||||
return 0;
|
||||
|
||||
prog->type = type;
|
||||
prog->sec_def = NULL;
|
||||
|
||||
/* If a program type was changed, we need to reset associated SEC()
|
||||
* handler, as it will be invalid now. The only exception is a generic
|
||||
* fallback handler, which by definition is program type-agnostic and
|
||||
* is a catch-all custom handler, optionally set by the application,
|
||||
* so should be able to handle any type of BPF program.
|
||||
*/
|
||||
if (prog->sec_def != &custom_fallback_def)
|
||||
prog->sec_def = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -8710,15 +8764,9 @@ static const struct bpf_sec_def section_defs[] = {
|
||||
SEC_DEF("struct_ops+", STRUCT_OPS, 0, SEC_NONE),
|
||||
SEC_DEF("struct_ops.s+", STRUCT_OPS, 0, SEC_SLEEPABLE),
|
||||
SEC_DEF("sk_lookup", SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE),
|
||||
SEC_DEF("netfilter", NETFILTER, BPF_NETFILTER, SEC_NONE),
|
||||
};
|
||||
|
||||
static size_t custom_sec_def_cnt;
|
||||
static struct bpf_sec_def *custom_sec_defs;
|
||||
static struct bpf_sec_def custom_fallback_def;
|
||||
static bool has_custom_fallback_def;
|
||||
|
||||
static int last_custom_sec_def_handler_id;
|
||||
|
||||
int libbpf_register_prog_handler(const char *sec,
|
||||
enum bpf_prog_type prog_type,
|
||||
enum bpf_attach_type exp_attach_type,
|
||||
@@ -8798,7 +8846,11 @@ int libbpf_unregister_prog_handler(int handler_id)
|
||||
|
||||
/* try to shrink the array, but it's ok if we couldn't */
|
||||
sec_defs = libbpf_reallocarray(custom_sec_defs, custom_sec_def_cnt, sizeof(*sec_defs));
|
||||
if (sec_defs)
|
||||
/* if new count is zero, reallocarray can return a valid NULL result;
|
||||
* in this case the previous pointer will be freed, so we *have to*
|
||||
* reassign old pointer to the new value (even if it's NULL)
|
||||
*/
|
||||
if (sec_defs || custom_sec_def_cnt == 0)
|
||||
custom_sec_defs = sec_defs;
|
||||
|
||||
return 0;
|
||||
@@ -9409,10 +9461,103 @@ __u32 bpf_map__value_size(const struct bpf_map *map)
|
||||
return map->def.value_size;
|
||||
}
|
||||
|
||||
static int map_btf_datasec_resize(struct bpf_map *map, __u32 size)
|
||||
{
|
||||
struct btf *btf;
|
||||
struct btf_type *datasec_type, *var_type;
|
||||
struct btf_var_secinfo *var;
|
||||
const struct btf_type *array_type;
|
||||
const struct btf_array *array;
|
||||
int vlen, element_sz, new_array_id;
|
||||
__u32 nr_elements;
|
||||
|
||||
/* check btf existence */
|
||||
btf = bpf_object__btf(map->obj);
|
||||
if (!btf)
|
||||
return -ENOENT;
|
||||
|
||||
/* verify map is datasec */
|
||||
datasec_type = btf_type_by_id(btf, bpf_map__btf_value_type_id(map));
|
||||
if (!btf_is_datasec(datasec_type)) {
|
||||
pr_warn("map '%s': cannot be resized, map value type is not a datasec\n",
|
||||
bpf_map__name(map));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* verify datasec has at least one var */
|
||||
vlen = btf_vlen(datasec_type);
|
||||
if (vlen == 0) {
|
||||
pr_warn("map '%s': cannot be resized, map value datasec is empty\n",
|
||||
bpf_map__name(map));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* verify last var in the datasec is an array */
|
||||
var = &btf_var_secinfos(datasec_type)[vlen - 1];
|
||||
var_type = btf_type_by_id(btf, var->type);
|
||||
array_type = skip_mods_and_typedefs(btf, var_type->type, NULL);
|
||||
if (!btf_is_array(array_type)) {
|
||||
pr_warn("map '%s': cannot be resized, last var must be an array\n",
|
||||
bpf_map__name(map));
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* verify request size aligns with array */
|
||||
array = btf_array(array_type);
|
||||
element_sz = btf__resolve_size(btf, array->type);
|
||||
if (element_sz <= 0 || (size - var->offset) % element_sz != 0) {
|
||||
pr_warn("map '%s': cannot be resized, element size (%d) doesn't align with new total size (%u)\n",
|
||||
bpf_map__name(map), element_sz, size);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* create a new array based on the existing array, but with new length */
|
||||
nr_elements = (size - var->offset) / element_sz;
|
||||
new_array_id = btf__add_array(btf, array->index_type, array->type, nr_elements);
|
||||
if (new_array_id < 0)
|
||||
return new_array_id;
|
||||
|
||||
/* adding a new btf type invalidates existing pointers to btf objects,
|
||||
* so refresh pointers before proceeding
|
||||
*/
|
||||
datasec_type = btf_type_by_id(btf, map->btf_value_type_id);
|
||||
var = &btf_var_secinfos(datasec_type)[vlen - 1];
|
||||
var_type = btf_type_by_id(btf, var->type);
|
||||
|
||||
/* finally update btf info */
|
||||
datasec_type->size = size;
|
||||
var->size = size - var->offset;
|
||||
var_type->type = new_array_id;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int bpf_map__set_value_size(struct bpf_map *map, __u32 size)
|
||||
{
|
||||
if (map->fd >= 0)
|
||||
return libbpf_err(-EBUSY);
|
||||
|
||||
if (map->mmaped) {
|
||||
int err;
|
||||
size_t mmap_old_sz, mmap_new_sz;
|
||||
|
||||
mmap_old_sz = bpf_map_mmap_sz(map->def.value_size, map->def.max_entries);
|
||||
mmap_new_sz = bpf_map_mmap_sz(size, map->def.max_entries);
|
||||
err = bpf_map_mmap_resize(map, mmap_old_sz, mmap_new_sz);
|
||||
if (err) {
|
||||
pr_warn("map '%s': failed to resize memory-mapped region: %d\n",
|
||||
bpf_map__name(map), err);
|
||||
return err;
|
||||
}
|
||||
err = map_btf_datasec_resize(map, size);
|
||||
if (err && err != -ENOENT) {
|
||||
pr_warn("map '%s': failed to adjust resized BTF, clearing BTF key/value info: %d\n",
|
||||
bpf_map__name(map), err);
|
||||
map->btf_value_type_id = 0;
|
||||
map->btf_key_type_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
map->def.value_size = size;
|
||||
return 0;
|
||||
}
|
||||
@@ -9438,7 +9583,7 @@ int bpf_map__set_initial_value(struct bpf_map *map,
|
||||
return 0;
|
||||
}
|
||||
|
||||
const void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
|
||||
void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
|
||||
{
|
||||
if (!map->mmaped)
|
||||
return NULL;
|
||||
@@ -9954,7 +10099,7 @@ static int parse_uint_from_file(const char *file, const char *fmt)
|
||||
int err, ret;
|
||||
FILE *f;
|
||||
|
||||
f = fopen(file, "r");
|
||||
f = fopen(file, "re");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
pr_debug("failed to open '%s': %s\n", file,
|
||||
@@ -10103,6 +10248,18 @@ static const char *tracefs_uprobe_events(void)
|
||||
return use_debugfs() ? DEBUGFS"/uprobe_events" : TRACEFS"/uprobe_events";
|
||||
}
|
||||
|
||||
static const char *tracefs_available_filter_functions(void)
|
||||
{
|
||||
return use_debugfs() ? DEBUGFS"/available_filter_functions"
|
||||
: TRACEFS"/available_filter_functions";
|
||||
}
|
||||
|
||||
static const char *tracefs_available_filter_functions_addrs(void)
|
||||
{
|
||||
return use_debugfs() ? DEBUGFS"/available_filter_functions_addrs"
|
||||
: TRACEFS"/available_filter_functions_addrs";
|
||||
}
|
||||
|
||||
static void gen_kprobe_legacy_event_name(char *buf, size_t buf_sz,
|
||||
const char *kfunc_name, size_t offset)
|
||||
{
|
||||
@@ -10418,25 +10575,158 @@ struct kprobe_multi_resolve {
|
||||
size_t cnt;
|
||||
};
|
||||
|
||||
static int
|
||||
resolve_kprobe_multi_cb(unsigned long long sym_addr, char sym_type,
|
||||
const char *sym_name, void *ctx)
|
||||
struct avail_kallsyms_data {
|
||||
char **syms;
|
||||
size_t cnt;
|
||||
struct kprobe_multi_resolve *res;
|
||||
};
|
||||
|
||||
static int avail_func_cmp(const void *a, const void *b)
|
||||
{
|
||||
struct kprobe_multi_resolve *res = ctx;
|
||||
return strcmp(*(const char **)a, *(const char **)b);
|
||||
}
|
||||
|
||||
static int avail_kallsyms_cb(unsigned long long sym_addr, char sym_type,
|
||||
const char *sym_name, void *ctx)
|
||||
{
|
||||
struct avail_kallsyms_data *data = ctx;
|
||||
struct kprobe_multi_resolve *res = data->res;
|
||||
int err;
|
||||
|
||||
if (!glob_match(sym_name, res->pattern))
|
||||
if (!bsearch(&sym_name, data->syms, data->cnt, sizeof(*data->syms), avail_func_cmp))
|
||||
return 0;
|
||||
|
||||
err = libbpf_ensure_mem((void **) &res->addrs, &res->cap, sizeof(unsigned long),
|
||||
res->cnt + 1);
|
||||
err = libbpf_ensure_mem((void **)&res->addrs, &res->cap, sizeof(*res->addrs), res->cnt + 1);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
res->addrs[res->cnt++] = (unsigned long) sym_addr;
|
||||
res->addrs[res->cnt++] = (unsigned long)sym_addr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int libbpf_available_kallsyms_parse(struct kprobe_multi_resolve *res)
|
||||
{
|
||||
const char *available_functions_file = tracefs_available_filter_functions();
|
||||
struct avail_kallsyms_data data;
|
||||
char sym_name[500];
|
||||
FILE *f;
|
||||
int err = 0, ret, i;
|
||||
char **syms = NULL;
|
||||
size_t cap = 0, cnt = 0;
|
||||
|
||||
f = fopen(available_functions_file, "re");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
pr_warn("failed to open %s: %d\n", available_functions_file, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
char *name;
|
||||
|
||||
ret = fscanf(f, "%499s%*[^\n]\n", sym_name);
|
||||
if (ret == EOF && feof(f))
|
||||
break;
|
||||
|
||||
if (ret != 1) {
|
||||
pr_warn("failed to parse available_filter_functions entry: %d\n", ret);
|
||||
err = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!glob_match(sym_name, res->pattern))
|
||||
continue;
|
||||
|
||||
err = libbpf_ensure_mem((void **)&syms, &cap, sizeof(*syms), cnt + 1);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
name = strdup(sym_name);
|
||||
if (!name) {
|
||||
err = -errno;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
syms[cnt++] = name;
|
||||
}
|
||||
|
||||
/* no entries found, bail out */
|
||||
if (cnt == 0) {
|
||||
err = -ENOENT;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
/* sort available functions */
|
||||
qsort(syms, cnt, sizeof(*syms), avail_func_cmp);
|
||||
|
||||
data.syms = syms;
|
||||
data.res = res;
|
||||
data.cnt = cnt;
|
||||
libbpf_kallsyms_parse(avail_kallsyms_cb, &data);
|
||||
|
||||
if (res->cnt == 0)
|
||||
err = -ENOENT;
|
||||
|
||||
cleanup:
|
||||
for (i = 0; i < cnt; i++)
|
||||
free((char *)syms[i]);
|
||||
free(syms);
|
||||
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
static bool has_available_filter_functions_addrs(void)
|
||||
{
|
||||
return access(tracefs_available_filter_functions_addrs(), R_OK) != -1;
|
||||
}
|
||||
|
||||
static int libbpf_available_kprobes_parse(struct kprobe_multi_resolve *res)
|
||||
{
|
||||
const char *available_path = tracefs_available_filter_functions_addrs();
|
||||
char sym_name[500];
|
||||
FILE *f;
|
||||
int ret, err = 0;
|
||||
unsigned long long sym_addr;
|
||||
|
||||
f = fopen(available_path, "re");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
pr_warn("failed to open %s: %d\n", available_path, err);
|
||||
return err;
|
||||
}
|
||||
|
||||
while (true) {
|
||||
ret = fscanf(f, "%llx %499s%*[^\n]\n", &sym_addr, sym_name);
|
||||
if (ret == EOF && feof(f))
|
||||
break;
|
||||
|
||||
if (ret != 2) {
|
||||
pr_warn("failed to parse available_filter_functions_addrs entry: %d\n",
|
||||
ret);
|
||||
err = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
if (!glob_match(sym_name, res->pattern))
|
||||
continue;
|
||||
|
||||
err = libbpf_ensure_mem((void **)&res->addrs, &res->cap,
|
||||
sizeof(*res->addrs), res->cnt + 1);
|
||||
if (err)
|
||||
goto cleanup;
|
||||
|
||||
res->addrs[res->cnt++] = (unsigned long)sym_addr;
|
||||
}
|
||||
|
||||
if (res->cnt == 0)
|
||||
err = -ENOENT;
|
||||
|
||||
cleanup:
|
||||
fclose(f);
|
||||
return err;
|
||||
}
|
||||
|
||||
struct bpf_link *
|
||||
bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog,
|
||||
const char *pattern,
|
||||
@@ -10473,13 +10763,12 @@ bpf_program__attach_kprobe_multi_opts(const struct bpf_program *prog,
|
||||
return libbpf_err_ptr(-EINVAL);
|
||||
|
||||
if (pattern) {
|
||||
err = libbpf_kallsyms_parse(resolve_kprobe_multi_cb, &res);
|
||||
if (has_available_filter_functions_addrs())
|
||||
err = libbpf_available_kprobes_parse(&res);
|
||||
else
|
||||
err = libbpf_available_kallsyms_parse(&res);
|
||||
if (err)
|
||||
goto error;
|
||||
if (!res.cnt) {
|
||||
err = -ENOENT;
|
||||
goto error;
|
||||
}
|
||||
addrs = res.addrs;
|
||||
cnt = res.cnt;
|
||||
}
|
||||
@@ -11690,6 +11979,48 @@ static int attach_iter(const struct bpf_program *prog, long cookie, struct bpf_l
|
||||
return libbpf_get_error(*link);
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_program__attach_netfilter(const struct bpf_program *prog,
|
||||
const struct bpf_netfilter_opts *opts)
|
||||
{
|
||||
LIBBPF_OPTS(bpf_link_create_opts, lopts);
|
||||
struct bpf_link *link;
|
||||
int prog_fd, link_fd;
|
||||
|
||||
if (!OPTS_VALID(opts, bpf_netfilter_opts))
|
||||
return libbpf_err_ptr(-EINVAL);
|
||||
|
||||
prog_fd = bpf_program__fd(prog);
|
||||
if (prog_fd < 0) {
|
||||
pr_warn("prog '%s': can't attach before loaded\n", prog->name);
|
||||
return libbpf_err_ptr(-EINVAL);
|
||||
}
|
||||
|
||||
link = calloc(1, sizeof(*link));
|
||||
if (!link)
|
||||
return libbpf_err_ptr(-ENOMEM);
|
||||
|
||||
link->detach = &bpf_link__detach_fd;
|
||||
|
||||
lopts.netfilter.pf = OPTS_GET(opts, pf, 0);
|
||||
lopts.netfilter.hooknum = OPTS_GET(opts, hooknum, 0);
|
||||
lopts.netfilter.priority = OPTS_GET(opts, priority, 0);
|
||||
lopts.netfilter.flags = OPTS_GET(opts, flags, 0);
|
||||
|
||||
link_fd = bpf_link_create(prog_fd, 0, BPF_NETFILTER, &lopts);
|
||||
if (link_fd < 0) {
|
||||
char errmsg[STRERR_BUFSIZE];
|
||||
|
||||
link_fd = -errno;
|
||||
free(link);
|
||||
pr_warn("prog '%s': failed to attach to netfilter: %s\n",
|
||||
prog->name, libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg)));
|
||||
return libbpf_err_ptr(link_fd);
|
||||
}
|
||||
link->fd = link_fd;
|
||||
|
||||
return link;
|
||||
}
|
||||
|
||||
struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
|
||||
{
|
||||
struct bpf_link *link = NULL;
|
||||
@@ -12690,7 +13021,7 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
|
||||
|
||||
for (i = 0; i < s->map_cnt; i++) {
|
||||
struct bpf_map *map = *s->maps[i].map;
|
||||
size_t mmap_sz = bpf_map_mmap_sz(map);
|
||||
size_t mmap_sz = bpf_map_mmap_sz(map->def.value_size, map->def.max_entries);
|
||||
int prot, map_fd = bpf_map__fd(map);
|
||||
void **mmaped = s->maps[i].mmaped;
|
||||
|
||||
@@ -12717,8 +13048,7 @@ int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
|
||||
* as per normal clean up procedure, so we don't need to worry
|
||||
* about it from skeleton's clean up perspective.
|
||||
*/
|
||||
*mmaped = mmap(map->mmaped, mmap_sz, prot,
|
||||
MAP_SHARED | MAP_FIXED, map_fd, 0);
|
||||
*mmaped = mmap(map->mmaped, mmap_sz, prot, MAP_SHARED | MAP_FIXED, map_fd, 0);
|
||||
if (*mmaped == MAP_FAILED) {
|
||||
err = -errno;
|
||||
*mmaped = NULL;
|
||||
|
||||
33
src/libbpf.h
33
src/libbpf.h
@@ -718,6 +718,21 @@ LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_freplace(const struct bpf_program *prog,
|
||||
int target_fd, const char *attach_func_name);
|
||||
|
||||
struct bpf_netfilter_opts {
|
||||
/* size of this struct, for forward/backward compatibility */
|
||||
size_t sz;
|
||||
|
||||
__u32 pf;
|
||||
__u32 hooknum;
|
||||
__s32 priority;
|
||||
__u32 flags;
|
||||
};
|
||||
#define bpf_netfilter_opts__last_field flags
|
||||
|
||||
LIBBPF_API struct bpf_link *
|
||||
bpf_program__attach_netfilter(const struct bpf_program *prog,
|
||||
const struct bpf_netfilter_opts *opts);
|
||||
|
||||
struct bpf_map;
|
||||
|
||||
LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map);
|
||||
@@ -869,8 +884,22 @@ LIBBPF_API int bpf_map__set_numa_node(struct bpf_map *map, __u32 numa_node);
|
||||
/* get/set map key size */
|
||||
LIBBPF_API __u32 bpf_map__key_size(const struct bpf_map *map);
|
||||
LIBBPF_API int bpf_map__set_key_size(struct bpf_map *map, __u32 size);
|
||||
/* get/set map value size */
|
||||
/* get map value size */
|
||||
LIBBPF_API __u32 bpf_map__value_size(const struct bpf_map *map);
|
||||
/**
|
||||
* @brief **bpf_map__set_value_size()** sets map value size.
|
||||
* @param map the BPF map instance
|
||||
* @return 0, on success; negative error, otherwise
|
||||
*
|
||||
* There is a special case for maps with associated memory-mapped regions, like
|
||||
* the global data section maps (bss, data, rodata). When this function is used
|
||||
* on such a map, the mapped region is resized. Afterward, an attempt is made to
|
||||
* adjust the corresponding BTF info. This attempt is best-effort and can only
|
||||
* succeed if the last variable of the data section map is an array. The array
|
||||
* BTF type is replaced by a new BTF array type with a different length.
|
||||
* Any previously existing pointers returned from bpf_map__initial_value() or
|
||||
* corresponding data section skeleton pointer must be reinitialized.
|
||||
*/
|
||||
LIBBPF_API int bpf_map__set_value_size(struct bpf_map *map, __u32 size);
|
||||
/* get map key/value BTF type IDs */
|
||||
LIBBPF_API __u32 bpf_map__btf_key_type_id(const struct bpf_map *map);
|
||||
@@ -884,7 +913,7 @@ LIBBPF_API int bpf_map__set_map_extra(struct bpf_map *map, __u64 map_extra);
|
||||
|
||||
LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map,
|
||||
const void *data, size_t size);
|
||||
LIBBPF_API const void *bpf_map__initial_value(struct bpf_map *map, size_t *psize);
|
||||
LIBBPF_API void *bpf_map__initial_value(struct bpf_map *map, size_t *psize);
|
||||
|
||||
/**
|
||||
* @brief **bpf_map__is_internal()** tells the caller whether or not the
|
||||
|
||||
@@ -391,3 +391,9 @@ LIBBPF_1.2.0 {
|
||||
bpf_map_get_info_by_fd;
|
||||
bpf_prog_get_info_by_fd;
|
||||
} LIBBPF_1.1.0;
|
||||
|
||||
LIBBPF_1.3.0 {
|
||||
global:
|
||||
bpf_obj_pin_opts;
|
||||
bpf_program__attach_netfilter;
|
||||
} LIBBPF_1.2.0;
|
||||
|
||||
@@ -38,7 +38,7 @@ static __u32 get_ubuntu_kernel_version(void)
|
||||
if (faccessat(AT_FDCWD, ubuntu_kver_file, R_OK, AT_EACCESS) != 0)
|
||||
return 0;
|
||||
|
||||
f = fopen(ubuntu_kver_file, "r");
|
||||
f = fopen(ubuntu_kver_file, "re");
|
||||
if (!f)
|
||||
return 0;
|
||||
|
||||
@@ -74,6 +74,10 @@ static __u32 get_debian_kernel_version(struct utsname *info)
|
||||
if (sscanf(p, "Debian %u.%u.%u", &major, &minor, &patch) != 3)
|
||||
return 0;
|
||||
|
||||
// Patch to run on Debian 10
|
||||
if (major == 4 && minor == 19 && patch > 255)
|
||||
return KERNEL_VERSION(major, minor, 255);
|
||||
|
||||
return KERNEL_VERSION(major, minor, patch);
|
||||
}
|
||||
|
||||
@@ -181,6 +185,9 @@ static int probe_prog_load(enum bpf_prog_type prog_type,
|
||||
case BPF_PROG_TYPE_FLOW_DISSECTOR:
|
||||
case BPF_PROG_TYPE_CGROUP_SYSCTL:
|
||||
break;
|
||||
case BPF_PROG_TYPE_NETFILTER:
|
||||
opts.expected_attach_type = BPF_NETFILTER;
|
||||
break;
|
||||
default:
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,6 @@
|
||||
#define __LIBBPF_VERSION_H
|
||||
|
||||
#define LIBBPF_MAJOR_VERSION 1
|
||||
#define LIBBPF_MINOR_VERSION 2
|
||||
#define LIBBPF_MINOR_VERSION 3
|
||||
|
||||
#endif /* __LIBBPF_VERSION_H */
|
||||
|
||||
12
src/usdt.c
12
src/usdt.c
@@ -466,7 +466,7 @@ static int parse_vma_segs(int pid, const char *lib_path, struct elf_seg **segs,
|
||||
|
||||
proceed:
|
||||
sprintf(line, "/proc/%d/maps", pid);
|
||||
f = fopen(line, "r");
|
||||
f = fopen(line, "re");
|
||||
if (!f) {
|
||||
err = -errno;
|
||||
pr_warn("usdt: failed to open '%s' to get base addr of '%s': %d\n",
|
||||
@@ -771,7 +771,7 @@ static int collect_usdt_targets(struct usdt_manager *man, Elf *elf, const char *
|
||||
target->rel_ip = usdt_rel_ip;
|
||||
target->sema_off = usdt_sema_off;
|
||||
|
||||
/* notes.args references strings from Elf itself, so they can
|
||||
/* notes.args references strings from ELF itself, so they can
|
||||
* be referenced safely until elf_end() call
|
||||
*/
|
||||
target->spec_str = note.args;
|
||||
@@ -852,8 +852,11 @@ static int bpf_link_usdt_detach(struct bpf_link *link)
|
||||
* system is so exhausted on memory, it's the least of user's
|
||||
* concerns, probably.
|
||||
* So just do our best here to return those IDs to usdt_manager.
|
||||
* Another edge case when we can legitimately get NULL is when
|
||||
* new_cnt is zero, which can happen in some edge cases, so we
|
||||
* need to be careful about that.
|
||||
*/
|
||||
if (new_free_ids) {
|
||||
if (new_free_ids || new_cnt == 0) {
|
||||
memcpy(new_free_ids + man->free_spec_cnt, usdt_link->spec_ids,
|
||||
usdt_link->spec_cnt * sizeof(*usdt_link->spec_ids));
|
||||
man->free_spec_ids = new_free_ids;
|
||||
@@ -954,8 +957,7 @@ struct bpf_link *usdt_manager_attach_usdt(struct usdt_manager *man, const struct
|
||||
spec_map_fd = bpf_map__fd(man->specs_map);
|
||||
ip_map_fd = bpf_map__fd(man->ip_to_spec_id_map);
|
||||
|
||||
/* TODO: perform path resolution similar to uprobe's */
|
||||
fd = open(path, O_RDONLY);
|
||||
fd = open(path, O_RDONLY | O_CLOEXEC);
|
||||
if (fd < 0) {
|
||||
err = -errno;
|
||||
pr_warn("usdt: failed to open ELF binary '%s': %d\n", path, err);
|
||||
|
||||
Reference in New Issue
Block a user