2018-01-30 20:55:03 +00:00
|
|
|
// SPDX-License-Identifier: LGPL-2.1
|
|
|
|
|
bpf tools: Introduce 'bpf' library and add bpf feature check
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existence of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Because libbpf requires 'kern_version' field set for 'union bpf_attr'
(bpfdep" is used for that dependency), Kernel BPF API is also checked
by intruducing a new feature check 'bpf' into tools/build/feature,
which checks the existence and version of linux/bpf.h. When building
libbpf, it searches that file from include/uapi/linux in kernel source
tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel
source tree it reside, installing of newest kernel headers is not
required, except we are trying to port these files to an old kernel.
To avoid checking that file when perf building, the newly introduced
'bpf' feature check doesn't added into FEATURE_TESTS and
FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added
into libbpf's specific.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Bcc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-4-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:51 +00:00
|
|
|
/*
|
|
|
|
* Common eBPF ELF object loading operations.
|
|
|
|
*
|
|
|
|
* Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
|
|
|
|
* Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
|
|
|
|
* Copyright (C) 2015 Huawei Inc.
|
tools lib bpf: Add BPF program pinning APIs
Add new APIs to pin a BPF program (or specific instances) to the
filesystem. The user can specify the path full path within a BPF
filesystem to pin the program.
bpf_program__pin_instance(prog, path, n) will pin the nth instance of
'prog' to the specified path.
bpf_program__pin(prog, path) will create the directory 'path' (if it
does not exist) and pin each instance within that directory. For
instance, path/0, path/1, path/2.
Committer notes:
- Add missing headers for mkdir()
- Check strdup() for failure
- Check snprintf >= size, not >, as == also means truncated, see 'man
snprintf', return value.
- Conditionally define BPF_FS_MAGIC, as it isn't in magic.h in older
systems and we're not yet having a tools/include/uapi/linux/magic.h
copy.
- Do not include linux/magic.h, not present in older distros.
Signed-off-by: Joe Stringer <joe@ovn.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20170126212001.14103-2-joe@ovn.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-26 21:19:56 +00:00
|
|
|
* Copyright (C) 2017 Nicira, Inc.
|
2016-07-04 11:02:42 +00:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License as published by the Free Software Foundation;
|
|
|
|
* version 2.1 of the License (not later!)
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this program; if not, see <http://www.gnu.org/licenses>
|
bpf tools: Introduce 'bpf' library and add bpf feature check
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existence of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Because libbpf requires 'kern_version' field set for 'union bpf_attr'
(bpfdep" is used for that dependency), Kernel BPF API is also checked
by intruducing a new feature check 'bpf' into tools/build/feature,
which checks the existence and version of linux/bpf.h. When building
libbpf, it searches that file from include/uapi/linux in kernel source
tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel
source tree it reside, installing of newest kernel headers is not
required, except we are trying to port these files to an old kernel.
To avoid checking that file when perf building, the newly introduced
'bpf' feature check doesn't added into FEATURE_TESTS and
FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added
into libbpf's specific.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Bcc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-4-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:51 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <stdlib.h>
|
2015-07-01 02:13:52 +00:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdarg.h>
|
tools lib bpf: Add BPF program pinning APIs
Add new APIs to pin a BPF program (or specific instances) to the
filesystem. The user can specify the path full path within a BPF
filesystem to pin the program.
bpf_program__pin_instance(prog, path, n) will pin the nth instance of
'prog' to the specified path.
bpf_program__pin(prog, path) will create the directory 'path' (if it
does not exist) and pin each instance within that directory. For
instance, path/0, path/1, path/2.
Committer notes:
- Add missing headers for mkdir()
- Check strdup() for failure
- Check snprintf >= size, not >, as == also means truncated, see 'man
snprintf', return value.
- Conditionally define BPF_FS_MAGIC, as it isn't in magic.h in older
systems and we're not yet having a tools/include/uapi/linux/magic.h
copy.
- Do not include linux/magic.h, not present in older distros.
Signed-off-by: Joe Stringer <joe@ovn.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20170126212001.14103-2-joe@ovn.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-26 21:19:56 +00:00
|
|
|
#include <libgen.h>
|
2015-07-01 02:14:02 +00:00
|
|
|
#include <inttypes.h>
|
2015-07-01 02:13:52 +00:00
|
|
|
#include <string.h>
|
bpf tools: Introduce 'bpf' library and add bpf feature check
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existence of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Because libbpf requires 'kern_version' field set for 'union bpf_attr'
(bpfdep" is used for that dependency), Kernel BPF API is also checked
by intruducing a new feature check 'bpf' into tools/build/feature,
which checks the existence and version of linux/bpf.h. When building
libbpf, it searches that file from include/uapi/linux in kernel source
tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel
source tree it reside, installing of newest kernel headers is not
required, except we are trying to port these files to an old kernel.
To avoid checking that file when perf building, the newly introduced
'bpf' feature check doesn't added into FEATURE_TESTS and
FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added
into libbpf's specific.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Bcc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-4-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:51 +00:00
|
|
|
#include <unistd.h>
|
2015-07-01 02:13:53 +00:00
|
|
|
#include <fcntl.h>
|
|
|
|
#include <errno.h>
|
2018-05-10 17:24:40 +00:00
|
|
|
#include <perf-sys.h>
|
bpf tools: Introduce 'bpf' library and add bpf feature check
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existence of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Because libbpf requires 'kern_version' field set for 'union bpf_attr'
(bpfdep" is used for that dependency), Kernel BPF API is also checked
by intruducing a new feature check 'bpf' into tools/build/feature,
which checks the existence and version of linux/bpf.h. When building
libbpf, it searches that file from include/uapi/linux in kernel source
tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel
source tree it reside, installing of newest kernel headers is not
required, except we are trying to port these files to an old kernel.
To avoid checking that file when perf building, the newly introduced
'bpf' feature check doesn't added into FEATURE_TESTS and
FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added
into libbpf's specific.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Bcc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-4-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:51 +00:00
|
|
|
#include <asm/unistd.h>
|
2017-01-23 01:11:25 +00:00
|
|
|
#include <linux/err.h>
|
2015-07-01 02:13:57 +00:00
|
|
|
#include <linux/kernel.h>
|
bpf tools: Introduce 'bpf' library and add bpf feature check
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existence of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Because libbpf requires 'kern_version' field set for 'union bpf_attr'
(bpfdep" is used for that dependency), Kernel BPF API is also checked
by intruducing a new feature check 'bpf' into tools/build/feature,
which checks the existence and version of linux/bpf.h. When building
libbpf, it searches that file from include/uapi/linux in kernel source
tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel
source tree it reside, installing of newest kernel headers is not
required, except we are trying to port these files to an old kernel.
To avoid checking that file when perf building, the newly introduced
'bpf' feature check doesn't added into FEATURE_TESTS and
FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added
into libbpf's specific.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Bcc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-4-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:51 +00:00
|
|
|
#include <linux/bpf.h>
|
2015-07-01 02:14:10 +00:00
|
|
|
#include <linux/list.h>
|
tools lib bpf: Add BPF program pinning APIs
Add new APIs to pin a BPF program (or specific instances) to the
filesystem. The user can specify the path full path within a BPF
filesystem to pin the program.
bpf_program__pin_instance(prog, path, n) will pin the nth instance of
'prog' to the specified path.
bpf_program__pin(prog, path) will create the directory 'path' (if it
does not exist) and pin each instance within that directory. For
instance, path/0, path/1, path/2.
Committer notes:
- Add missing headers for mkdir()
- Check strdup() for failure
- Check snprintf >= size, not >, as == also means truncated, see 'man
snprintf', return value.
- Conditionally define BPF_FS_MAGIC, as it isn't in magic.h in older
systems and we're not yet having a tools/include/uapi/linux/magic.h
copy.
- Do not include linux/magic.h, not present in older distros.
Signed-off-by: Joe Stringer <joe@ovn.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20170126212001.14103-2-joe@ovn.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-26 21:19:56 +00:00
|
|
|
#include <linux/limits.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/vfs.h>
|
2015-07-01 02:13:53 +00:00
|
|
|
#include <libelf.h>
|
|
|
|
#include <gelf.h>
|
bpf tools: Introduce 'bpf' library and add bpf feature check
This is the first patch of libbpf. The goal of libbpf is to create a
standard way for accessing eBPF object files. This patch creates
'Makefile' and 'Build' for it, allows 'make' to build libbpf.a and
libbpf.so, 'make install' to put them into proper directories.
Most part of Makefile is borrowed from traceevent.
Before building, it checks the existence of libelf in Makefile, and deny
to build if not found. Instead of throwing an error if libelf not found,
the error raises in a phony target "elfdep". This design is to ensure
'make clean' still workable even if libelf is not found.
Because libbpf requires 'kern_version' field set for 'union bpf_attr'
(bpfdep" is used for that dependency), Kernel BPF API is also checked
by intruducing a new feature check 'bpf' into tools/build/feature,
which checks the existence and version of linux/bpf.h. When building
libbpf, it searches that file from include/uapi/linux in kernel source
tree (controlled by FEATURE_CHECK_CFLAGS-bpf). Since it searches kernel
source tree it reside, installing of newest kernel headers is not
required, except we are trying to port these files to an old kernel.
To avoid checking that file when perf building, the newly introduced
'bpf' feature check doesn't added into FEATURE_TESTS and
FEATURE_DISPLAY by default in tools/build/Makefile.feature, but added
into libbpf's specific.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Bcc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-4-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:51 +00:00
|
|
|
|
|
|
|
#include "libbpf.h"
|
2015-07-01 02:14:04 +00:00
|
|
|
#include "bpf.h"
|
2018-04-18 22:56:05 +00:00
|
|
|
#include "btf.h"
|
2015-07-01 02:13:52 +00:00
|
|
|
|
2016-07-18 06:01:08 +00:00
|
|
|
#ifndef EM_BPF
|
|
|
|
#define EM_BPF 247
|
|
|
|
#endif
|
|
|
|
|
tools lib bpf: Add BPF program pinning APIs
Add new APIs to pin a BPF program (or specific instances) to the
filesystem. The user can specify the path full path within a BPF
filesystem to pin the program.
bpf_program__pin_instance(prog, path, n) will pin the nth instance of
'prog' to the specified path.
bpf_program__pin(prog, path) will create the directory 'path' (if it
does not exist) and pin each instance within that directory. For
instance, path/0, path/1, path/2.
Committer notes:
- Add missing headers for mkdir()
- Check strdup() for failure
- Check snprintf >= size, not >, as == also means truncated, see 'man
snprintf', return value.
- Conditionally define BPF_FS_MAGIC, as it isn't in magic.h in older
systems and we're not yet having a tools/include/uapi/linux/magic.h
copy.
- Do not include linux/magic.h, not present in older distros.
Signed-off-by: Joe Stringer <joe@ovn.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20170126212001.14103-2-joe@ovn.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-26 21:19:56 +00:00
|
|
|
#ifndef BPF_FS_MAGIC
|
|
|
|
#define BPF_FS_MAGIC 0xcafe4a11
|
|
|
|
#endif
|
|
|
|
|
2015-07-01 02:13:52 +00:00
|
|
|
#define __printf(a, b) __attribute__((format(printf, a, b)))
|
|
|
|
|
|
|
|
__printf(1, 2)
|
|
|
|
static int __base_pr(const char *format, ...)
|
|
|
|
{
|
|
|
|
va_list args;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
va_start(args, format);
|
|
|
|
err = vfprintf(stderr, format, args);
|
|
|
|
va_end(args);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static __printf(1, 2) libbpf_print_fn_t __pr_warning = __base_pr;
|
|
|
|
static __printf(1, 2) libbpf_print_fn_t __pr_info = __base_pr;
|
|
|
|
static __printf(1, 2) libbpf_print_fn_t __pr_debug;
|
|
|
|
|
|
|
|
#define __pr(func, fmt, ...) \
|
|
|
|
do { \
|
|
|
|
if ((func)) \
|
|
|
|
(func)("libbpf: " fmt, ##__VA_ARGS__); \
|
|
|
|
} while (0)
|
|
|
|
|
|
|
|
#define pr_warning(fmt, ...) __pr(__pr_warning, fmt, ##__VA_ARGS__)
|
|
|
|
#define pr_info(fmt, ...) __pr(__pr_info, fmt, ##__VA_ARGS__)
|
|
|
|
#define pr_debug(fmt, ...) __pr(__pr_debug, fmt, ##__VA_ARGS__)
|
|
|
|
|
|
|
|
void libbpf_set_print(libbpf_print_fn_t warn,
|
|
|
|
libbpf_print_fn_t info,
|
|
|
|
libbpf_print_fn_t debug)
|
|
|
|
{
|
|
|
|
__pr_warning = warn;
|
|
|
|
__pr_info = info;
|
|
|
|
__pr_debug = debug;
|
|
|
|
}
|
2015-07-01 02:13:53 +00:00
|
|
|
|
2015-11-06 13:49:37 +00:00
|
|
|
#define STRERR_BUFSIZE 128
|
|
|
|
|
|
|
|
#define ERRNO_OFFSET(e) ((e) - __LIBBPF_ERRNO__START)
|
|
|
|
#define ERRCODE_OFFSET(c) ERRNO_OFFSET(LIBBPF_ERRNO__##c)
|
|
|
|
#define NR_ERRNO (__LIBBPF_ERRNO__END - __LIBBPF_ERRNO__START)
|
|
|
|
|
|
|
|
static const char *libbpf_strerror_table[NR_ERRNO] = {
|
|
|
|
[ERRCODE_OFFSET(LIBELF)] = "Something wrong in libelf",
|
|
|
|
[ERRCODE_OFFSET(FORMAT)] = "BPF object format invalid",
|
|
|
|
[ERRCODE_OFFSET(KVERSION)] = "'version' section incorrect or lost",
|
2016-06-28 12:23:37 +00:00
|
|
|
[ERRCODE_OFFSET(ENDIAN)] = "Endian mismatch",
|
2015-11-06 13:49:37 +00:00
|
|
|
[ERRCODE_OFFSET(INTERNAL)] = "Internal error in libbpf",
|
|
|
|
[ERRCODE_OFFSET(RELOC)] = "Relocation failed",
|
|
|
|
[ERRCODE_OFFSET(VERIFY)] = "Kernel verifier blocks program loading",
|
|
|
|
[ERRCODE_OFFSET(PROG2BIG)] = "Program too big",
|
|
|
|
[ERRCODE_OFFSET(KVER)] = "Incorrect kernel version",
|
2016-07-13 10:44:02 +00:00
|
|
|
[ERRCODE_OFFSET(PROGTYPE)] = "Kernel doesn't support this program type",
|
2018-01-30 20:55:01 +00:00
|
|
|
[ERRCODE_OFFSET(WRNGPID)] = "Wrong pid in netlink message",
|
|
|
|
[ERRCODE_OFFSET(INVSEQ)] = "Invalid netlink sequence",
|
2015-11-06 13:49:37 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
int libbpf_strerror(int err, char *buf, size_t size)
|
|
|
|
{
|
|
|
|
if (!buf || !size)
|
|
|
|
return -1;
|
|
|
|
|
|
|
|
err = err > 0 ? err : -err;
|
|
|
|
|
|
|
|
if (err < __LIBBPF_ERRNO__START) {
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
ret = strerror_r(err, buf, size);
|
|
|
|
buf[size - 1] = '\0';
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (err < __LIBBPF_ERRNO__END) {
|
|
|
|
const char *msg;
|
|
|
|
|
|
|
|
msg = libbpf_strerror_table[ERRNO_OFFSET(err)];
|
|
|
|
snprintf(buf, size, "%s", msg);
|
|
|
|
buf[size - 1] = '\0';
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
snprintf(buf, size, "Unknown libbpf error %d", err);
|
|
|
|
buf[size - 1] = '\0';
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define CHECK_ERR(action, err, out) do { \
|
|
|
|
err = action; \
|
|
|
|
if (err) \
|
|
|
|
goto out; \
|
|
|
|
} while(0)
|
|
|
|
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
/* Copied from tools/perf/util/util.h */
|
|
|
|
#ifndef zfree
|
|
|
|
# define zfree(ptr) ({ free(*ptr); *ptr = NULL; })
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef zclose
|
|
|
|
# define zclose(fd) ({ \
|
|
|
|
int ___err = 0; \
|
|
|
|
if ((fd) >= 0) \
|
|
|
|
___err = close((fd)); \
|
|
|
|
fd = -1; \
|
|
|
|
___err; })
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef HAVE_LIBELF_MMAP_SUPPORT
|
|
|
|
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ_MMAP
|
|
|
|
#else
|
|
|
|
# define LIBBPF_ELF_C_READ_MMAP ELF_C_READ
|
|
|
|
#endif
|
|
|
|
|
2015-07-01 02:14:00 +00:00
|
|
|
/*
|
|
|
|
* bpf_prog should be a better name but it has been used in
|
|
|
|
* linux/filter.h.
|
|
|
|
*/
|
|
|
|
struct bpf_program {
|
|
|
|
/* Index in elf obj file, for relocation use. */
|
|
|
|
int idx;
|
2017-09-27 21:37:54 +00:00
|
|
|
char *name;
|
2018-05-16 21:02:49 +00:00
|
|
|
int prog_ifindex;
|
2015-07-01 02:14:00 +00:00
|
|
|
char *section_name;
|
|
|
|
struct bpf_insn *insns;
|
2017-12-15 01:55:10 +00:00
|
|
|
size_t insns_cnt, main_prog_cnt;
|
2016-07-13 10:44:01 +00:00
|
|
|
enum bpf_prog_type type;
|
2015-07-01 02:14:02 +00:00
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
struct reloc_desc {
|
|
|
|
enum {
|
|
|
|
RELO_LD64,
|
|
|
|
RELO_CALL,
|
|
|
|
} type;
|
2015-07-01 02:14:02 +00:00
|
|
|
int insn_idx;
|
2017-12-15 01:55:10 +00:00
|
|
|
union {
|
|
|
|
int map_idx;
|
|
|
|
int text_off;
|
|
|
|
};
|
2015-07-01 02:14:02 +00:00
|
|
|
} *reloc_desc;
|
|
|
|
int nr_reloc;
|
2015-07-01 02:14:07 +00:00
|
|
|
|
2015-11-16 12:10:09 +00:00
|
|
|
struct {
|
|
|
|
int nr;
|
|
|
|
int *fds;
|
|
|
|
} instances;
|
|
|
|
bpf_program_prep_t preprocessor;
|
2015-07-01 02:14:08 +00:00
|
|
|
|
|
|
|
struct bpf_object *obj;
|
|
|
|
void *priv;
|
|
|
|
bpf_program_clear_priv_t clear_priv;
|
2018-03-30 22:08:01 +00:00
|
|
|
|
|
|
|
enum bpf_attach_type expected_attach_type;
|
2015-07-01 02:14:00 +00:00
|
|
|
};
|
|
|
|
|
2015-11-27 08:47:35 +00:00
|
|
|
struct bpf_map {
|
|
|
|
int fd;
|
2015-11-27 08:47:36 +00:00
|
|
|
char *name;
|
2016-11-15 04:05:47 +00:00
|
|
|
size_t offset;
|
2018-05-16 21:02:49 +00:00
|
|
|
int map_ifindex;
|
2015-11-27 08:47:35 +00:00
|
|
|
struct bpf_map_def def;
|
2018-05-22 22:04:24 +00:00
|
|
|
uint32_t btf_key_type_id;
|
|
|
|
uint32_t btf_value_type_id;
|
2015-11-27 08:47:35 +00:00
|
|
|
void *priv;
|
|
|
|
bpf_map_clear_priv_t clear_priv;
|
|
|
|
};
|
|
|
|
|
2015-07-01 02:14:10 +00:00
|
|
|
static LIST_HEAD(bpf_objects_list);
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
struct bpf_object {
|
2015-07-01 02:13:57 +00:00
|
|
|
char license[64];
|
|
|
|
u32 kern_version;
|
2015-07-01 02:13:58 +00:00
|
|
|
|
2015-07-01 02:14:00 +00:00
|
|
|
struct bpf_program *programs;
|
|
|
|
size_t nr_programs;
|
2015-11-27 08:47:35 +00:00
|
|
|
struct bpf_map *maps;
|
|
|
|
size_t nr_maps;
|
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
bool loaded;
|
2015-07-01 02:14:00 +00:00
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
/*
|
|
|
|
* Information when doing elf related work. Only valid if fd
|
|
|
|
* is valid.
|
|
|
|
*/
|
|
|
|
struct {
|
|
|
|
int fd;
|
2015-07-01 02:13:54 +00:00
|
|
|
void *obj_buf;
|
|
|
|
size_t obj_buf_sz;
|
2015-07-01 02:13:53 +00:00
|
|
|
Elf *elf;
|
|
|
|
GElf_Ehdr ehdr;
|
bpf tools: Collect symbol table from SHT_SYMTAB section
This patch collects symbols section. This section is useful when linking
BPF maps.
What 'bpf_map_xxx()' functions actually require are map's file
descriptors (and the internal verifier converts fds into pointers to
'struct bpf_map'), which we don't know when compiling. Therefore, we
should make compiler generate a 'ldr_64 r1, <imm>' instruction, and
fill the 'imm' field with the actual file descriptor when loading in
libbpf.
BPF programs should be written in this way:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};
SEC("my_func=sys_write")
int my_func(void *ctx)
{
...
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
...
}
Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>'
instruction, where imm should be the address of 'my_map'. According to
the address, libbpf knows which map it actually referenced, and then
fills the imm field with the 'fd' of that map created by it.
However, since we never really 'link' the object file, the imm field is
only a record in relocation section. Therefore libbpf should do the
relocation:
1. In relocation section (type == SHT_REL), positions of each such
'ldr_64' instruction are recorded with a reference of an entry in
symbol table (SHT_SYMTAB);
2. From records in symbol table we can find the indics of map
variables.
Libbpf first record SHT_SYMTAB and positions of each instruction which
required bu such operation. Then create file descriptor. Finally, after
map creation complete, replace the imm field.
This is the first patch of BPF map related stuff. It records SHT_SYMTAB
into object's efile field for further use.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-12-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:59 +00:00
|
|
|
Elf_Data *symbols;
|
2015-12-08 02:25:30 +00:00
|
|
|
size_t strtabidx;
|
2015-07-01 02:14:01 +00:00
|
|
|
struct {
|
|
|
|
GElf_Shdr shdr;
|
|
|
|
Elf_Data *data;
|
|
|
|
} *reloc;
|
|
|
|
int nr_reloc;
|
perf bpf: Check relocation target section
Libbpf should check the target section before doing relocation to ensure
the relocation is correct. If not, a bug in LLVM causes an error. See
[1]. Also, if an incorrect BPF script uses both global variable and
map, global variable whould be treated as map and be relocated without
error.
This patch saves the id of the map section into obj->efile and compare
target section of a relocation symbol against it during relocation.
Previous patch introduces a test case about this problem. After this
patch:
# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : Ok
# perf test -v BPF
...
37.3: Test BPF relocation checker :
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: Program 'func=sys_write' contains non-map related relo data pointing to section 65522
bpf: failed to load buffer
Compile BPF program failed.
test child finished with 0
---- end ----
Test BPF filter subtest 2: Ok
[1] https://llvm.org/bugs/show_bug.cgi?id=26243
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-01-25 09:55:49 +00:00
|
|
|
int maps_shndx;
|
2017-12-15 01:55:10 +00:00
|
|
|
int text_shndx;
|
2015-07-01 02:13:53 +00:00
|
|
|
} efile;
|
2015-07-01 02:14:10 +00:00
|
|
|
/*
|
|
|
|
* All loaded bpf_object is linked in a list, which is
|
|
|
|
* hidden to caller. bpf_objects__<func> handlers deal with
|
|
|
|
* all objects.
|
|
|
|
*/
|
|
|
|
struct list_head list;
|
2016-11-26 07:03:26 +00:00
|
|
|
|
2018-04-18 22:56:05 +00:00
|
|
|
struct btf *btf;
|
|
|
|
|
2016-11-26 07:03:26 +00:00
|
|
|
void *priv;
|
|
|
|
bpf_object_clear_priv_t clear_priv;
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
char path[];
|
|
|
|
};
|
|
|
|
#define obj_elf_valid(o) ((o)->efile.elf)
|
|
|
|
|
2015-07-01 02:14:07 +00:00
|
|
|
static void bpf_program__unload(struct bpf_program *prog)
|
|
|
|
{
|
2015-11-16 12:10:09 +00:00
|
|
|
int i;
|
|
|
|
|
2015-07-01 02:14:07 +00:00
|
|
|
if (!prog)
|
|
|
|
return;
|
|
|
|
|
2015-11-16 12:10:09 +00:00
|
|
|
/*
|
|
|
|
* If the object is opened but the program was never loaded,
|
|
|
|
* it is possible that prog->instances.nr == -1.
|
|
|
|
*/
|
|
|
|
if (prog->instances.nr > 0) {
|
|
|
|
for (i = 0; i < prog->instances.nr; i++)
|
|
|
|
zclose(prog->instances.fds[i]);
|
|
|
|
} else if (prog->instances.nr != -1) {
|
|
|
|
pr_warning("Internal error: instances.nr is %d\n",
|
|
|
|
prog->instances.nr);
|
|
|
|
}
|
|
|
|
|
|
|
|
prog->instances.nr = -1;
|
|
|
|
zfree(&prog->instances.fds);
|
2015-07-01 02:14:07 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:00 +00:00
|
|
|
static void bpf_program__exit(struct bpf_program *prog)
|
|
|
|
{
|
|
|
|
if (!prog)
|
|
|
|
return;
|
|
|
|
|
2015-07-01 02:14:08 +00:00
|
|
|
if (prog->clear_priv)
|
|
|
|
prog->clear_priv(prog, prog->priv);
|
|
|
|
|
|
|
|
prog->priv = NULL;
|
|
|
|
prog->clear_priv = NULL;
|
|
|
|
|
2015-07-01 02:14:07 +00:00
|
|
|
bpf_program__unload(prog);
|
2017-09-27 21:37:54 +00:00
|
|
|
zfree(&prog->name);
|
2015-07-01 02:14:00 +00:00
|
|
|
zfree(&prog->section_name);
|
|
|
|
zfree(&prog->insns);
|
2015-07-01 02:14:02 +00:00
|
|
|
zfree(&prog->reloc_desc);
|
|
|
|
|
|
|
|
prog->nr_reloc = 0;
|
2015-07-01 02:14:00 +00:00
|
|
|
prog->insns_cnt = 0;
|
|
|
|
prog->idx = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-09-27 21:37:54 +00:00
|
|
|
bpf_program__init(void *data, size_t size, char *section_name, int idx,
|
|
|
|
struct bpf_program *prog)
|
2015-07-01 02:14:00 +00:00
|
|
|
{
|
|
|
|
if (size < sizeof(struct bpf_insn)) {
|
2017-09-27 21:37:54 +00:00
|
|
|
pr_warning("corrupted section '%s'\n", section_name);
|
2015-07-01 02:14:00 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
bzero(prog, sizeof(*prog));
|
|
|
|
|
2017-09-27 21:37:54 +00:00
|
|
|
prog->section_name = strdup(section_name);
|
2015-07-01 02:14:00 +00:00
|
|
|
if (!prog->section_name) {
|
2018-02-08 11:48:17 +00:00
|
|
|
pr_warning("failed to alloc name for prog under section(%d) %s\n",
|
|
|
|
idx, section_name);
|
2015-07-01 02:14:00 +00:00
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
|
|
|
prog->insns = malloc(size);
|
|
|
|
if (!prog->insns) {
|
2017-09-27 21:37:54 +00:00
|
|
|
pr_warning("failed to alloc insns for prog under section %s\n",
|
|
|
|
section_name);
|
2015-07-01 02:14:00 +00:00
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
prog->insns_cnt = size / sizeof(struct bpf_insn);
|
|
|
|
memcpy(prog->insns, data,
|
|
|
|
prog->insns_cnt * sizeof(struct bpf_insn));
|
|
|
|
prog->idx = idx;
|
2015-11-16 12:10:09 +00:00
|
|
|
prog->instances.fds = NULL;
|
|
|
|
prog->instances.nr = -1;
|
2016-07-13 10:44:01 +00:00
|
|
|
prog->type = BPF_PROG_TYPE_KPROBE;
|
2015-07-01 02:14:00 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
errout:
|
|
|
|
bpf_program__exit(prog);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bpf_object__add_program(struct bpf_object *obj, void *data, size_t size,
|
2017-09-27 21:37:54 +00:00
|
|
|
char *section_name, int idx)
|
2015-07-01 02:14:00 +00:00
|
|
|
{
|
|
|
|
struct bpf_program prog, *progs;
|
|
|
|
int nr_progs, err;
|
|
|
|
|
2017-09-27 21:37:54 +00:00
|
|
|
err = bpf_program__init(data, size, section_name, idx, &prog);
|
2015-07-01 02:14:00 +00:00
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
progs = obj->programs;
|
|
|
|
nr_progs = obj->nr_programs;
|
|
|
|
|
|
|
|
progs = realloc(progs, sizeof(progs[0]) * (nr_progs + 1));
|
|
|
|
if (!progs) {
|
|
|
|
/*
|
|
|
|
* In this case the original obj->programs
|
|
|
|
* is still valid, so don't need special treat for
|
|
|
|
* bpf_close_object().
|
|
|
|
*/
|
2017-09-27 21:37:54 +00:00
|
|
|
pr_warning("failed to alloc a new program under section '%s'\n",
|
|
|
|
section_name);
|
2015-07-01 02:14:00 +00:00
|
|
|
bpf_program__exit(&prog);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("found program %s\n", prog.section_name);
|
|
|
|
obj->programs = progs;
|
|
|
|
obj->nr_programs = nr_progs + 1;
|
2015-07-01 02:14:08 +00:00
|
|
|
prog.obj = obj;
|
2015-07-01 02:14:00 +00:00
|
|
|
progs[nr_progs] = prog;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-09-27 21:37:54 +00:00
|
|
|
static int
|
|
|
|
bpf_object__init_prog_names(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
Elf_Data *symbols = obj->efile.symbols;
|
|
|
|
struct bpf_program *prog;
|
|
|
|
size_t pi, si;
|
|
|
|
|
|
|
|
for (pi = 0; pi < obj->nr_programs; pi++) {
|
2017-12-15 01:55:10 +00:00
|
|
|
const char *name = NULL;
|
2017-09-27 21:37:54 +00:00
|
|
|
|
|
|
|
prog = &obj->programs[pi];
|
2017-12-15 01:55:10 +00:00
|
|
|
if (prog->idx == obj->efile.text_shndx) {
|
|
|
|
name = ".text";
|
|
|
|
goto skip_search;
|
|
|
|
}
|
2017-09-27 21:37:54 +00:00
|
|
|
|
|
|
|
for (si = 0; si < symbols->d_size / sizeof(GElf_Sym) && !name;
|
|
|
|
si++) {
|
|
|
|
GElf_Sym sym;
|
|
|
|
|
|
|
|
if (!gelf_getsym(symbols, si, &sym))
|
|
|
|
continue;
|
|
|
|
if (sym.st_shndx != prog->idx)
|
|
|
|
continue;
|
2017-12-13 15:18:52 +00:00
|
|
|
if (GELF_ST_BIND(sym.st_info) != STB_GLOBAL)
|
|
|
|
continue;
|
2017-09-27 21:37:54 +00:00
|
|
|
|
|
|
|
name = elf_strptr(obj->efile.elf,
|
|
|
|
obj->efile.strtabidx,
|
|
|
|
sym.st_name);
|
|
|
|
if (!name) {
|
|
|
|
pr_warning("failed to get sym name string for prog %s\n",
|
|
|
|
prog->section_name);
|
|
|
|
return -LIBBPF_ERRNO__LIBELF;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!name) {
|
|
|
|
pr_warning("failed to find sym for prog %s\n",
|
|
|
|
prog->section_name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2017-12-15 01:55:10 +00:00
|
|
|
skip_search:
|
2017-09-27 21:37:54 +00:00
|
|
|
prog->name = strdup(name);
|
|
|
|
if (!prog->name) {
|
|
|
|
pr_warning("failed to allocate memory for prog sym %s\n",
|
|
|
|
name);
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:54 +00:00
|
|
|
static struct bpf_object *bpf_object__new(const char *path,
|
|
|
|
void *obj_buf,
|
|
|
|
size_t obj_buf_sz)
|
2015-07-01 02:13:53 +00:00
|
|
|
{
|
|
|
|
struct bpf_object *obj;
|
|
|
|
|
|
|
|
obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
|
|
|
|
if (!obj) {
|
|
|
|
pr_warning("alloc memory failed for %s\n", path);
|
2015-11-06 13:49:37 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
strcpy(obj->path, path);
|
|
|
|
obj->efile.fd = -1;
|
2015-07-01 02:13:54 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Caller of this function should also calls
|
|
|
|
* bpf_object__elf_finish() after data collection to return
|
|
|
|
* obj_buf to user. If not, we should duplicate the buffer to
|
|
|
|
* avoid user freeing them before elf finish.
|
|
|
|
*/
|
|
|
|
obj->efile.obj_buf = obj_buf;
|
|
|
|
obj->efile.obj_buf_sz = obj_buf_sz;
|
perf bpf: Check relocation target section
Libbpf should check the target section before doing relocation to ensure
the relocation is correct. If not, a bug in LLVM causes an error. See
[1]. Also, if an incorrect BPF script uses both global variable and
map, global variable whould be treated as map and be relocated without
error.
This patch saves the id of the map section into obj->efile and compare
target section of a relocation symbol against it during relocation.
Previous patch introduces a test case about this problem. After this
patch:
# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : Ok
# perf test -v BPF
...
37.3: Test BPF relocation checker :
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: Program 'func=sys_write' contains non-map related relo data pointing to section 65522
bpf: failed to load buffer
Compile BPF program failed.
test child finished with 0
---- end ----
Test BPF filter subtest 2: Ok
[1] https://llvm.org/bugs/show_bug.cgi?id=26243
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-01-25 09:55:49 +00:00
|
|
|
obj->efile.maps_shndx = -1;
|
2015-07-01 02:13:54 +00:00
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
obj->loaded = false;
|
2015-07-01 02:14:10 +00:00
|
|
|
|
|
|
|
INIT_LIST_HEAD(&obj->list);
|
|
|
|
list_add(&obj->list, &bpf_objects_list);
|
2015-07-01 02:13:53 +00:00
|
|
|
return obj;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void bpf_object__elf_finish(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
if (!obj_elf_valid(obj))
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (obj->efile.elf) {
|
|
|
|
elf_end(obj->efile.elf);
|
|
|
|
obj->efile.elf = NULL;
|
|
|
|
}
|
bpf tools: Collect symbol table from SHT_SYMTAB section
This patch collects symbols section. This section is useful when linking
BPF maps.
What 'bpf_map_xxx()' functions actually require are map's file
descriptors (and the internal verifier converts fds into pointers to
'struct bpf_map'), which we don't know when compiling. Therefore, we
should make compiler generate a 'ldr_64 r1, <imm>' instruction, and
fill the 'imm' field with the actual file descriptor when loading in
libbpf.
BPF programs should be written in this way:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};
SEC("my_func=sys_write")
int my_func(void *ctx)
{
...
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
...
}
Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>'
instruction, where imm should be the address of 'my_map'. According to
the address, libbpf knows which map it actually referenced, and then
fills the imm field with the 'fd' of that map created by it.
However, since we never really 'link' the object file, the imm field is
only a record in relocation section. Therefore libbpf should do the
relocation:
1. In relocation section (type == SHT_REL), positions of each such
'ldr_64' instruction are recorded with a reference of an entry in
symbol table (SHT_SYMTAB);
2. From records in symbol table we can find the indics of map
variables.
Libbpf first record SHT_SYMTAB and positions of each instruction which
required bu such operation. Then create file descriptor. Finally, after
map creation complete, replace the imm field.
This is the first patch of BPF map related stuff. It records SHT_SYMTAB
into object's efile field for further use.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-12-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:59 +00:00
|
|
|
obj->efile.symbols = NULL;
|
2015-07-01 02:14:01 +00:00
|
|
|
|
|
|
|
zfree(&obj->efile.reloc);
|
|
|
|
obj->efile.nr_reloc = 0;
|
2015-07-01 02:13:53 +00:00
|
|
|
zclose(obj->efile.fd);
|
2015-07-01 02:13:54 +00:00
|
|
|
obj->efile.obj_buf = NULL;
|
|
|
|
obj->efile.obj_buf_sz = 0;
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static int bpf_object__elf_init(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
GElf_Ehdr *ep;
|
|
|
|
|
|
|
|
if (obj_elf_valid(obj)) {
|
|
|
|
pr_warning("elf init: internal error\n");
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__LIBELF;
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:54 +00:00
|
|
|
if (obj->efile.obj_buf_sz > 0) {
|
|
|
|
/*
|
|
|
|
* obj_buf should have been validated by
|
|
|
|
* bpf_object__open_buffer().
|
|
|
|
*/
|
|
|
|
obj->efile.elf = elf_memory(obj->efile.obj_buf,
|
|
|
|
obj->efile.obj_buf_sz);
|
|
|
|
} else {
|
|
|
|
obj->efile.fd = open(obj->path, O_RDONLY);
|
|
|
|
if (obj->efile.fd < 0) {
|
|
|
|
pr_warning("failed to open %s: %s\n", obj->path,
|
|
|
|
strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj->efile.elf = elf_begin(obj->efile.fd,
|
|
|
|
LIBBPF_ELF_C_READ_MMAP,
|
|
|
|
NULL);
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!obj->efile.elf) {
|
|
|
|
pr_warning("failed to open %s as ELF file\n",
|
|
|
|
obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__LIBELF;
|
2015-07-01 02:13:53 +00:00
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!gelf_getehdr(obj->efile.elf, &obj->efile.ehdr)) {
|
|
|
|
pr_warning("failed to get EHDR from %s\n",
|
|
|
|
obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:53 +00:00
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
ep = &obj->efile.ehdr;
|
|
|
|
|
2016-07-18 06:01:08 +00:00
|
|
|
/* Old LLVM set e_machine to EM_NONE */
|
|
|
|
if ((ep->e_type != ET_REL) || (ep->e_machine && (ep->e_machine != EM_BPF))) {
|
2015-07-01 02:13:53 +00:00
|
|
|
pr_warning("%s is not an eBPF object file\n",
|
|
|
|
obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:53 +00:00
|
|
|
goto errout;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
errout:
|
|
|
|
bpf_object__elf_finish(obj);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:55 +00:00
|
|
|
static int
|
|
|
|
bpf_object__check_endianness(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
static unsigned int const endian = 1;
|
|
|
|
|
|
|
|
switch (obj->efile.ehdr.e_ident[EI_DATA]) {
|
|
|
|
case ELFDATA2LSB:
|
|
|
|
/* We are big endian, BPF obj is little endian. */
|
|
|
|
if (*(unsigned char const *)&endian != 1)
|
|
|
|
goto mismatch;
|
|
|
|
break;
|
|
|
|
|
|
|
|
case ELFDATA2MSB:
|
|
|
|
/* We are little endian, BPF obj is big endian. */
|
|
|
|
if (*(unsigned char const *)&endian != 0)
|
|
|
|
goto mismatch;
|
|
|
|
break;
|
|
|
|
default:
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__ENDIAN;
|
2015-07-01 02:13:55 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
mismatch:
|
|
|
|
pr_warning("Error: endianness mismatch.\n");
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__ENDIAN;
|
2015-07-01 02:13:55 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:57 +00:00
|
|
|
static int
|
|
|
|
bpf_object__init_license(struct bpf_object *obj,
|
|
|
|
void *data, size_t size)
|
|
|
|
{
|
|
|
|
memcpy(obj->license, data,
|
|
|
|
min(size, sizeof(obj->license) - 1));
|
|
|
|
pr_debug("license of %s is %s\n", obj->path, obj->license);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bpf_object__init_kversion(struct bpf_object *obj,
|
|
|
|
void *data, size_t size)
|
|
|
|
{
|
|
|
|
u32 kver;
|
|
|
|
|
|
|
|
if (size != sizeof(kver)) {
|
|
|
|
pr_warning("invalid kver section in %s\n", obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:57 +00:00
|
|
|
}
|
|
|
|
memcpy(&kver, data, sizeof(kver));
|
|
|
|
obj->kern_version = kver;
|
|
|
|
pr_debug("kernel version of %s is %x\n", obj->path,
|
|
|
|
obj->kern_version);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-11-15 04:05:47 +00:00
|
|
|
static int compare_bpf_map(const void *_a, const void *_b)
|
|
|
|
{
|
|
|
|
const struct bpf_map *a = _a;
|
|
|
|
const struct bpf_map *b = _b;
|
2015-11-27 08:47:35 +00:00
|
|
|
|
2016-11-15 04:05:47 +00:00
|
|
|
return a->offset - b->offset;
|
2015-07-01 02:13:58 +00:00
|
|
|
}
|
|
|
|
|
2015-12-08 02:25:29 +00:00
|
|
|
static int
|
2016-11-15 04:05:47 +00:00
|
|
|
bpf_object__init_maps(struct bpf_object *obj)
|
2015-11-27 08:47:36 +00:00
|
|
|
{
|
2017-10-05 14:41:57 +00:00
|
|
|
int i, map_idx, map_def_sz, nr_maps = 0;
|
2016-11-15 04:05:47 +00:00
|
|
|
Elf_Scn *scn;
|
|
|
|
Elf_Data *data;
|
2015-11-27 08:47:36 +00:00
|
|
|
Elf_Data *symbols = obj->efile.symbols;
|
|
|
|
|
2016-11-15 04:05:47 +00:00
|
|
|
if (obj->efile.maps_shndx < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
if (!symbols)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
scn = elf_getscn(obj->efile.elf, obj->efile.maps_shndx);
|
|
|
|
if (scn)
|
|
|
|
data = elf_getdata(scn, NULL);
|
|
|
|
if (!scn || !data) {
|
|
|
|
pr_warning("failed to get Elf_Data from map section %d\n",
|
|
|
|
obj->efile.maps_shndx);
|
2015-12-08 02:25:29 +00:00
|
|
|
return -EINVAL;
|
2016-11-15 04:05:47 +00:00
|
|
|
}
|
2015-11-27 08:47:36 +00:00
|
|
|
|
2016-11-15 04:05:47 +00:00
|
|
|
/*
|
|
|
|
* Count number of maps. Each map has a name.
|
|
|
|
* Array of maps is not supported: only the first element is
|
|
|
|
* considered.
|
|
|
|
*
|
|
|
|
* TODO: Detect array of map and report error.
|
|
|
|
*/
|
2015-11-27 08:47:36 +00:00
|
|
|
for (i = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
|
|
|
|
GElf_Sym sym;
|
2016-11-15 04:05:47 +00:00
|
|
|
|
|
|
|
if (!gelf_getsym(symbols, i, &sym))
|
|
|
|
continue;
|
|
|
|
if (sym.st_shndx != obj->efile.maps_shndx)
|
|
|
|
continue;
|
|
|
|
nr_maps++;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Alloc obj->maps and fill nr_maps. */
|
|
|
|
pr_debug("maps in %s: %d maps in %zd bytes\n", obj->path,
|
|
|
|
nr_maps, data->d_size);
|
|
|
|
|
|
|
|
if (!nr_maps)
|
|
|
|
return 0;
|
|
|
|
|
2017-10-05 14:41:57 +00:00
|
|
|
/* Assume equally sized map definitions */
|
|
|
|
map_def_sz = data->d_size / nr_maps;
|
|
|
|
if (!data->d_size || (data->d_size % nr_maps) != 0) {
|
|
|
|
pr_warning("unable to determine map definition size "
|
|
|
|
"section %s, %d maps in %zd bytes\n",
|
|
|
|
obj->path, nr_maps, data->d_size);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2016-11-15 04:05:47 +00:00
|
|
|
obj->maps = calloc(nr_maps, sizeof(obj->maps[0]));
|
|
|
|
if (!obj->maps) {
|
|
|
|
pr_warning("alloc maps for object failed\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
obj->nr_maps = nr_maps;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* fill all fd with -1 so won't close incorrect
|
|
|
|
* fd (fd=0 is stdin) when failure (zclose won't close
|
|
|
|
* negative fd)).
|
|
|
|
*/
|
|
|
|
for (i = 0; i < nr_maps; i++)
|
|
|
|
obj->maps[i].fd = -1;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Fill obj->maps using data in "maps" section.
|
|
|
|
*/
|
|
|
|
for (i = 0, map_idx = 0; i < symbols->d_size / sizeof(GElf_Sym); i++) {
|
|
|
|
GElf_Sym sym;
|
2015-11-27 08:47:36 +00:00
|
|
|
const char *map_name;
|
2016-11-15 04:05:47 +00:00
|
|
|
struct bpf_map_def *def;
|
2015-11-27 08:47:36 +00:00
|
|
|
|
|
|
|
if (!gelf_getsym(symbols, i, &sym))
|
|
|
|
continue;
|
perf bpf: Check relocation target section
Libbpf should check the target section before doing relocation to ensure
the relocation is correct. If not, a bug in LLVM causes an error. See
[1]. Also, if an incorrect BPF script uses both global variable and
map, global variable whould be treated as map and be relocated without
error.
This patch saves the id of the map section into obj->efile and compare
target section of a relocation symbol against it during relocation.
Previous patch introduces a test case about this problem. After this
patch:
# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : Ok
# perf test -v BPF
...
37.3: Test BPF relocation checker :
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: Program 'func=sys_write' contains non-map related relo data pointing to section 65522
bpf: failed to load buffer
Compile BPF program failed.
test child finished with 0
---- end ----
Test BPF filter subtest 2: Ok
[1] https://llvm.org/bugs/show_bug.cgi?id=26243
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-01-25 09:55:49 +00:00
|
|
|
if (sym.st_shndx != obj->efile.maps_shndx)
|
2015-11-27 08:47:36 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
map_name = elf_strptr(obj->efile.elf,
|
2015-12-08 02:25:30 +00:00
|
|
|
obj->efile.strtabidx,
|
2015-11-27 08:47:36 +00:00
|
|
|
sym.st_name);
|
2016-11-15 04:05:47 +00:00
|
|
|
obj->maps[map_idx].offset = sym.st_value;
|
2017-10-05 14:41:57 +00:00
|
|
|
if (sym.st_value + map_def_sz > data->d_size) {
|
2016-11-15 04:05:47 +00:00
|
|
|
pr_warning("corrupted maps section in %s: last map \"%s\" too small\n",
|
|
|
|
obj->path, map_name);
|
|
|
|
return -EINVAL;
|
2015-11-27 08:47:36 +00:00
|
|
|
}
|
2016-11-15 04:05:47 +00:00
|
|
|
|
2015-11-27 08:47:36 +00:00
|
|
|
obj->maps[map_idx].name = strdup(map_name);
|
2015-12-08 02:25:29 +00:00
|
|
|
if (!obj->maps[map_idx].name) {
|
|
|
|
pr_warning("failed to alloc map name\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
2016-11-15 04:05:47 +00:00
|
|
|
pr_debug("map %d is \"%s\"\n", map_idx,
|
2015-11-27 08:47:36 +00:00
|
|
|
obj->maps[map_idx].name);
|
2016-11-15 04:05:47 +00:00
|
|
|
def = (struct bpf_map_def *)(data->d_buf + sym.st_value);
|
2017-10-05 14:41:57 +00:00
|
|
|
/*
|
|
|
|
* If the definition of the map in the object file fits in
|
|
|
|
* bpf_map_def, copy it. Any extra fields in our version
|
|
|
|
* of bpf_map_def will default to zero as a result of the
|
|
|
|
* calloc above.
|
|
|
|
*/
|
|
|
|
if (map_def_sz <= sizeof(struct bpf_map_def)) {
|
|
|
|
memcpy(&obj->maps[map_idx].def, def, map_def_sz);
|
|
|
|
} else {
|
|
|
|
/*
|
|
|
|
* Here the map structure being read is bigger than what
|
|
|
|
* we expect, truncate if the excess bits are all zero.
|
|
|
|
* If they are not zero, reject this map as
|
|
|
|
* incompatible.
|
|
|
|
*/
|
|
|
|
char *b;
|
|
|
|
for (b = ((char *)def) + sizeof(struct bpf_map_def);
|
|
|
|
b < ((char *)def) + map_def_sz; b++) {
|
|
|
|
if (*b != 0) {
|
|
|
|
pr_warning("maps section in %s: \"%s\" "
|
|
|
|
"has unrecognized, non-zero "
|
|
|
|
"options\n",
|
|
|
|
obj->path, map_name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
memcpy(&obj->maps[map_idx].def, def,
|
|
|
|
sizeof(struct bpf_map_def));
|
|
|
|
}
|
2016-11-15 04:05:47 +00:00
|
|
|
map_idx++;
|
2015-11-27 08:47:36 +00:00
|
|
|
}
|
2016-11-15 04:05:47 +00:00
|
|
|
|
|
|
|
qsort(obj->maps, obj->nr_maps, sizeof(obj->maps[0]), compare_bpf_map);
|
2017-10-05 14:41:57 +00:00
|
|
|
return 0;
|
2015-11-27 08:47:36 +00:00
|
|
|
}
|
|
|
|
|
2018-02-08 11:48:32 +00:00
|
|
|
static bool section_have_execinstr(struct bpf_object *obj, int idx)
|
|
|
|
{
|
|
|
|
Elf_Scn *scn;
|
|
|
|
GElf_Shdr sh;
|
|
|
|
|
|
|
|
scn = elf_getscn(obj->efile.elf, idx);
|
|
|
|
if (!scn)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (gelf_getshdr(scn, &sh) != &sh)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
if (sh.sh_flags & SHF_EXECINSTR)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:56 +00:00
|
|
|
static int bpf_object__elf_collect(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
Elf *elf = obj->efile.elf;
|
|
|
|
GElf_Ehdr *ep = &obj->efile.ehdr;
|
|
|
|
Elf_Scn *scn = NULL;
|
perf bpf: Check relocation target section
Libbpf should check the target section before doing relocation to ensure
the relocation is correct. If not, a bug in LLVM causes an error. See
[1]. Also, if an incorrect BPF script uses both global variable and
map, global variable whould be treated as map and be relocated without
error.
This patch saves the id of the map section into obj->efile and compare
target section of a relocation symbol against it during relocation.
Previous patch introduces a test case about this problem. After this
patch:
# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : Ok
# perf test -v BPF
...
37.3: Test BPF relocation checker :
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: Program 'func=sys_write' contains non-map related relo data pointing to section 65522
bpf: failed to load buffer
Compile BPF program failed.
test child finished with 0
---- end ----
Test BPF filter subtest 2: Ok
[1] https://llvm.org/bugs/show_bug.cgi?id=26243
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-01-25 09:55:49 +00:00
|
|
|
int idx = 0, err = 0;
|
2015-07-01 02:13:56 +00:00
|
|
|
|
|
|
|
/* Elf is corrupted/truncated, avoid calling elf_strptr. */
|
|
|
|
if (!elf_rawdata(elf_getscn(elf, ep->e_shstrndx), NULL)) {
|
|
|
|
pr_warning("failed to get e_shstrndx from %s\n",
|
|
|
|
obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
while ((scn = elf_nextscn(elf, scn)) != NULL) {
|
|
|
|
char *name;
|
|
|
|
GElf_Shdr sh;
|
|
|
|
Elf_Data *data;
|
|
|
|
|
|
|
|
idx++;
|
|
|
|
if (gelf_getshdr(scn, &sh) != &sh) {
|
2018-02-08 11:48:17 +00:00
|
|
|
pr_warning("failed to get section(%d) header from %s\n",
|
|
|
|
idx, obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:56 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
name = elf_strptr(elf, ep->e_shstrndx, sh.sh_name);
|
|
|
|
if (!name) {
|
2018-02-08 11:48:17 +00:00
|
|
|
pr_warning("failed to get section(%d) name from %s\n",
|
|
|
|
idx, obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:56 +00:00
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
data = elf_getdata(scn, 0);
|
|
|
|
if (!data) {
|
2018-02-08 11:48:17 +00:00
|
|
|
pr_warning("failed to get section(%d) data from %s(%s)\n",
|
|
|
|
idx, name, obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:13:56 +00:00
|
|
|
goto out;
|
|
|
|
}
|
2018-02-08 11:48:17 +00:00
|
|
|
pr_debug("section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
|
|
|
|
idx, name, (unsigned long)data->d_size,
|
2015-07-01 02:13:56 +00:00
|
|
|
(int)sh.sh_link, (unsigned long)sh.sh_flags,
|
|
|
|
(int)sh.sh_type);
|
2015-07-01 02:13:57 +00:00
|
|
|
|
|
|
|
if (strcmp(name, "license") == 0)
|
|
|
|
err = bpf_object__init_license(obj,
|
|
|
|
data->d_buf,
|
|
|
|
data->d_size);
|
|
|
|
else if (strcmp(name, "version") == 0)
|
|
|
|
err = bpf_object__init_kversion(obj,
|
|
|
|
data->d_buf,
|
|
|
|
data->d_size);
|
2016-11-15 04:05:47 +00:00
|
|
|
else if (strcmp(name, "maps") == 0)
|
perf bpf: Check relocation target section
Libbpf should check the target section before doing relocation to ensure
the relocation is correct. If not, a bug in LLVM causes an error. See
[1]. Also, if an incorrect BPF script uses both global variable and
map, global variable whould be treated as map and be relocated without
error.
This patch saves the id of the map section into obj->efile and compare
target section of a relocation symbol against it during relocation.
Previous patch introduces a test case about this problem. After this
patch:
# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : Ok
# perf test -v BPF
...
37.3: Test BPF relocation checker :
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: Program 'func=sys_write' contains non-map related relo data pointing to section 65522
bpf: failed to load buffer
Compile BPF program failed.
test child finished with 0
---- end ----
Test BPF filter subtest 2: Ok
[1] https://llvm.org/bugs/show_bug.cgi?id=26243
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-01-25 09:55:49 +00:00
|
|
|
obj->efile.maps_shndx = idx;
|
2018-04-18 22:56:05 +00:00
|
|
|
else if (strcmp(name, BTF_ELF_SEC) == 0) {
|
|
|
|
obj->btf = btf__new(data->d_buf, data->d_size,
|
|
|
|
__pr_debug);
|
|
|
|
if (IS_ERR(obj->btf)) {
|
|
|
|
pr_warning("Error loading ELF section %s: %ld. Ignored and continue.\n",
|
|
|
|
BTF_ELF_SEC, PTR_ERR(obj->btf));
|
|
|
|
obj->btf = NULL;
|
|
|
|
}
|
|
|
|
} else if (sh.sh_type == SHT_SYMTAB) {
|
bpf tools: Collect symbol table from SHT_SYMTAB section
This patch collects symbols section. This section is useful when linking
BPF maps.
What 'bpf_map_xxx()' functions actually require are map's file
descriptors (and the internal verifier converts fds into pointers to
'struct bpf_map'), which we don't know when compiling. Therefore, we
should make compiler generate a 'ldr_64 r1, <imm>' instruction, and
fill the 'imm' field with the actual file descriptor when loading in
libbpf.
BPF programs should be written in this way:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};
SEC("my_func=sys_write")
int my_func(void *ctx)
{
...
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
...
}
Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>'
instruction, where imm should be the address of 'my_map'. According to
the address, libbpf knows which map it actually referenced, and then
fills the imm field with the 'fd' of that map created by it.
However, since we never really 'link' the object file, the imm field is
only a record in relocation section. Therefore libbpf should do the
relocation:
1. In relocation section (type == SHT_REL), positions of each such
'ldr_64' instruction are recorded with a reference of an entry in
symbol table (SHT_SYMTAB);
2. From records in symbol table we can find the indics of map
variables.
Libbpf first record SHT_SYMTAB and positions of each instruction which
required bu such operation. Then create file descriptor. Finally, after
map creation complete, replace the imm field.
This is the first patch of BPF map related stuff. It records SHT_SYMTAB
into object's efile field for further use.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-12-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:59 +00:00
|
|
|
if (obj->efile.symbols) {
|
|
|
|
pr_warning("bpf: multiple SYMTAB in %s\n",
|
|
|
|
obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
err = -LIBBPF_ERRNO__FORMAT;
|
2015-12-08 02:25:30 +00:00
|
|
|
} else {
|
bpf tools: Collect symbol table from SHT_SYMTAB section
This patch collects symbols section. This section is useful when linking
BPF maps.
What 'bpf_map_xxx()' functions actually require are map's file
descriptors (and the internal verifier converts fds into pointers to
'struct bpf_map'), which we don't know when compiling. Therefore, we
should make compiler generate a 'ldr_64 r1, <imm>' instruction, and
fill the 'imm' field with the actual file descriptor when loading in
libbpf.
BPF programs should be written in this way:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};
SEC("my_func=sys_write")
int my_func(void *ctx)
{
...
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
...
}
Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>'
instruction, where imm should be the address of 'my_map'. According to
the address, libbpf knows which map it actually referenced, and then
fills the imm field with the 'fd' of that map created by it.
However, since we never really 'link' the object file, the imm field is
only a record in relocation section. Therefore libbpf should do the
relocation:
1. In relocation section (type == SHT_REL), positions of each such
'ldr_64' instruction are recorded with a reference of an entry in
symbol table (SHT_SYMTAB);
2. From records in symbol table we can find the indics of map
variables.
Libbpf first record SHT_SYMTAB and positions of each instruction which
required bu such operation. Then create file descriptor. Finally, after
map creation complete, replace the imm field.
This is the first patch of BPF map related stuff. It records SHT_SYMTAB
into object's efile field for further use.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-12-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:59 +00:00
|
|
|
obj->efile.symbols = data;
|
2015-12-08 02:25:30 +00:00
|
|
|
obj->efile.strtabidx = sh.sh_link;
|
|
|
|
}
|
2015-07-01 02:14:00 +00:00
|
|
|
} else if ((sh.sh_type == SHT_PROGBITS) &&
|
|
|
|
(sh.sh_flags & SHF_EXECINSTR) &&
|
|
|
|
(data->d_size > 0)) {
|
2017-12-15 01:55:10 +00:00
|
|
|
if (strcmp(name, ".text") == 0)
|
|
|
|
obj->efile.text_shndx = idx;
|
2015-07-01 02:14:00 +00:00
|
|
|
err = bpf_object__add_program(obj, data->d_buf,
|
|
|
|
data->d_size, name, idx);
|
|
|
|
if (err) {
|
2015-11-06 13:49:37 +00:00
|
|
|
char errmsg[STRERR_BUFSIZE];
|
|
|
|
|
2015-07-01 02:14:00 +00:00
|
|
|
strerror_r(-err, errmsg, sizeof(errmsg));
|
|
|
|
pr_warning("failed to alloc program %s (%s): %s",
|
|
|
|
name, obj->path, errmsg);
|
|
|
|
}
|
2015-07-01 02:14:01 +00:00
|
|
|
} else if (sh.sh_type == SHT_REL) {
|
|
|
|
void *reloc = obj->efile.reloc;
|
|
|
|
int nr_reloc = obj->efile.nr_reloc + 1;
|
2018-02-08 11:48:32 +00:00
|
|
|
int sec = sh.sh_info; /* points to other section */
|
|
|
|
|
|
|
|
/* Only do relo for section with exec instructions */
|
|
|
|
if (!section_have_execinstr(obj, sec)) {
|
|
|
|
pr_debug("skip relo %s(%d) for section(%d)\n",
|
|
|
|
name, idx, sec);
|
|
|
|
continue;
|
|
|
|
}
|
2015-07-01 02:14:01 +00:00
|
|
|
|
|
|
|
reloc = realloc(reloc,
|
|
|
|
sizeof(*obj->efile.reloc) * nr_reloc);
|
|
|
|
if (!reloc) {
|
|
|
|
pr_warning("realloc failed\n");
|
|
|
|
err = -ENOMEM;
|
|
|
|
} else {
|
|
|
|
int n = nr_reloc - 1;
|
|
|
|
|
|
|
|
obj->efile.reloc = reloc;
|
|
|
|
obj->efile.nr_reloc = nr_reloc;
|
|
|
|
|
|
|
|
obj->efile.reloc[n].shdr = sh;
|
|
|
|
obj->efile.reloc[n].data = data;
|
|
|
|
}
|
2018-02-08 11:48:17 +00:00
|
|
|
} else {
|
|
|
|
pr_debug("skip section(%d) %s\n", idx, name);
|
bpf tools: Collect symbol table from SHT_SYMTAB section
This patch collects symbols section. This section is useful when linking
BPF maps.
What 'bpf_map_xxx()' functions actually require are map's file
descriptors (and the internal verifier converts fds into pointers to
'struct bpf_map'), which we don't know when compiling. Therefore, we
should make compiler generate a 'ldr_64 r1, <imm>' instruction, and
fill the 'imm' field with the actual file descriptor when loading in
libbpf.
BPF programs should be written in this way:
struct bpf_map_def SEC("maps") my_map = {
.type = BPF_MAP_TYPE_HASH,
.key_size = sizeof(unsigned long),
.value_size = sizeof(unsigned long),
.max_entries = 1000000,
};
SEC("my_func=sys_write")
int my_func(void *ctx)
{
...
bpf_map_update_elem(&my_map, &key, &value, BPF_ANY);
...
}
Compiler should convert '&my_map' into a 'ldr_64, r1, <imm>'
instruction, where imm should be the address of 'my_map'. According to
the address, libbpf knows which map it actually referenced, and then
fills the imm field with the 'fd' of that map created by it.
However, since we never really 'link' the object file, the imm field is
only a record in relocation section. Therefore libbpf should do the
relocation:
1. In relocation section (type == SHT_REL), positions of each such
'ldr_64' instruction are recorded with a reference of an entry in
symbol table (SHT_SYMTAB);
2. From records in symbol table we can find the indics of map
variables.
Libbpf first record SHT_SYMTAB and positions of each instruction which
required bu such operation. Then create file descriptor. Finally, after
map creation complete, replace the imm field.
This is the first patch of BPF map related stuff. It records SHT_SYMTAB
into object's efile field for further use.
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@plumgrid.com>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: David Ahern <dsahern@gmail.com>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Kaixu Xia <xiakaixu@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <a.p.zijlstra@chello.nl>
Cc: Zefan Li <lizefan@huawei.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1435716878-189507-12-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2015-07-01 02:13:59 +00:00
|
|
|
}
|
2015-07-01 02:13:57 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
2015-07-01 02:13:56 +00:00
|
|
|
}
|
2015-11-27 08:47:36 +00:00
|
|
|
|
2015-12-08 02:25:30 +00:00
|
|
|
if (!obj->efile.strtabidx || obj->efile.strtabidx >= idx) {
|
|
|
|
pr_warning("Corrupted ELF file: index of strtab invalid\n");
|
|
|
|
return LIBBPF_ERRNO__FORMAT;
|
|
|
|
}
|
2017-09-27 21:37:54 +00:00
|
|
|
if (obj->efile.maps_shndx >= 0) {
|
2016-11-15 04:05:47 +00:00
|
|
|
err = bpf_object__init_maps(obj);
|
2017-09-27 21:37:54 +00:00
|
|
|
if (err)
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
err = bpf_object__init_prog_names(obj);
|
2015-07-01 02:13:56 +00:00
|
|
|
out:
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:02 +00:00
|
|
|
static struct bpf_program *
|
|
|
|
bpf_object__find_prog_by_idx(struct bpf_object *obj, int idx)
|
|
|
|
{
|
|
|
|
struct bpf_program *prog;
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
for (i = 0; i < obj->nr_programs; i++) {
|
|
|
|
prog = &obj->programs[i];
|
|
|
|
if (prog->idx == idx)
|
|
|
|
return prog;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
2017-12-15 01:55:10 +00:00
|
|
|
bpf_program__collect_reloc(struct bpf_program *prog, GElf_Shdr *shdr,
|
|
|
|
Elf_Data *data, struct bpf_object *obj)
|
2015-07-01 02:14:02 +00:00
|
|
|
{
|
2017-12-15 01:55:10 +00:00
|
|
|
Elf_Data *symbols = obj->efile.symbols;
|
|
|
|
int text_shndx = obj->efile.text_shndx;
|
|
|
|
int maps_shndx = obj->efile.maps_shndx;
|
|
|
|
struct bpf_map *maps = obj->maps;
|
|
|
|
size_t nr_maps = obj->nr_maps;
|
2015-07-01 02:14:02 +00:00
|
|
|
int i, nrels;
|
|
|
|
|
|
|
|
pr_debug("collecting relocating info for: '%s'\n",
|
|
|
|
prog->section_name);
|
|
|
|
nrels = shdr->sh_size / shdr->sh_entsize;
|
|
|
|
|
|
|
|
prog->reloc_desc = malloc(sizeof(*prog->reloc_desc) * nrels);
|
|
|
|
if (!prog->reloc_desc) {
|
|
|
|
pr_warning("failed to alloc memory in relocation\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
prog->nr_reloc = nrels;
|
|
|
|
|
|
|
|
for (i = 0; i < nrels; i++) {
|
|
|
|
GElf_Sym sym;
|
|
|
|
GElf_Rel rel;
|
|
|
|
unsigned int insn_idx;
|
|
|
|
struct bpf_insn *insns = prog->insns;
|
|
|
|
size_t map_idx;
|
|
|
|
|
|
|
|
if (!gelf_getrel(data, i, &rel)) {
|
|
|
|
pr_warning("relocation: failed to get %d reloc\n", i);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!gelf_getsym(symbols,
|
|
|
|
GELF_R_SYM(rel.r_info),
|
|
|
|
&sym)) {
|
|
|
|
pr_warning("relocation: symbol %"PRIx64" not found\n",
|
|
|
|
GELF_R_SYM(rel.r_info));
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__FORMAT;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
2017-12-19 20:53:11 +00:00
|
|
|
pr_debug("relo for %lld value %lld name %d\n",
|
|
|
|
(long long) (rel.r_info >> 32),
|
|
|
|
(long long) sym.st_value, sym.st_name);
|
2015-07-01 02:14:02 +00:00
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
if (sym.st_shndx != maps_shndx && sym.st_shndx != text_shndx) {
|
perf bpf: Check relocation target section
Libbpf should check the target section before doing relocation to ensure
the relocation is correct. If not, a bug in LLVM causes an error. See
[1]. Also, if an incorrect BPF script uses both global variable and
map, global variable whould be treated as map and be relocated without
error.
This patch saves the id of the map section into obj->efile and compare
target section of a relocation symbol against it during relocation.
Previous patch introduces a test case about this problem. After this
patch:
# ~/perf test BPF
37: Test BPF filter :
37.1: Test basic BPF filtering : Ok
37.2: Test BPF prologue generation : Ok
37.3: Test BPF relocation checker : Ok
# perf test -v BPF
...
37.3: Test BPF relocation checker :
...
libbpf: loading object '[bpf_relocation_test]' from buffer
libbpf: section .strtab, size 126, link 0, flags 0, type=3
libbpf: section .text, size 0, link 0, flags 6, type=1
libbpf: section .data, size 0, link 0, flags 3, type=1
libbpf: section .bss, size 0, link 0, flags 3, type=8
libbpf: section func=sys_write, size 104, link 0, flags 6, type=1
libbpf: found program func=sys_write
libbpf: section .relfunc=sys_write, size 16, link 10, flags 0, type=9
libbpf: section maps, size 16, link 0, flags 3, type=1
libbpf: maps in [bpf_relocation_test]: 16 bytes
libbpf: section license, size 4, link 0, flags 3, type=1
libbpf: license of [bpf_relocation_test] is GPL
libbpf: section version, size 4, link 0, flags 3, type=1
libbpf: kernel version of [bpf_relocation_test] is 40400
libbpf: section .symtab, size 144, link 1, flags 0, type=2
libbpf: map 0 is "my_table"
libbpf: collecting relocating info for: 'func=sys_write'
libbpf: Program 'func=sys_write' contains non-map related relo data pointing to section 65522
bpf: failed to load buffer
Compile BPF program failed.
test child finished with 0
---- end ----
Test BPF filter subtest 2: Ok
[1] https://llvm.org/bugs/show_bug.cgi?id=26243
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Acked-by: Alexei Starovoitov <ast@kernel.org>
Tested-by: Arnaldo Carvalho de Melo <acme@redhat.com>
Cc: "David S. Miller" <davem@davemloft.net>
Cc: Brendan Gregg <brendan.d.gregg@gmail.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: He Kuang <hekuang@huawei.com>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: Li Zefan <lizefan@huawei.com>
Cc: Masami Hiramatsu <masami.hiramatsu.pt@hitachi.com>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Will Deacon <will.deacon@arm.com>
Cc: pi3orama@163.com
Link: http://lkml.kernel.org/r/1453715801-7732-3-git-send-email-wangnan0@huawei.com
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2016-01-25 09:55:49 +00:00
|
|
|
pr_warning("Program '%s' contains non-map related relo data pointing to section %u\n",
|
|
|
|
prog->section_name, sym.st_shndx);
|
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
|
|
}
|
|
|
|
|
|
|
|
insn_idx = rel.r_offset / sizeof(struct bpf_insn);
|
|
|
|
pr_debug("relocation: insn_idx=%u\n", insn_idx);
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
if (insns[insn_idx].code == (BPF_JMP | BPF_CALL)) {
|
|
|
|
if (insns[insn_idx].src_reg != BPF_PSEUDO_CALL) {
|
|
|
|
pr_warning("incorrect bpf_call opcode\n");
|
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
|
|
}
|
|
|
|
prog->reloc_desc[i].type = RELO_CALL;
|
|
|
|
prog->reloc_desc[i].insn_idx = insn_idx;
|
|
|
|
prog->reloc_desc[i].text_off = sym.st_value;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:02 +00:00
|
|
|
if (insns[insn_idx].code != (BPF_LD | BPF_IMM | BPF_DW)) {
|
|
|
|
pr_warning("bpf: relocation: invalid relo for insns[%d].code 0x%x\n",
|
|
|
|
insn_idx, insns[insn_idx].code);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
|
tools lib bpf: Fix map offsets in relocation
Commit 4708bbda5cb2 ("tools lib bpf: Fix maps resolution") attempted to
fix map resolution by identifying the number of symbols that point to
maps, and using this number to resolve each of the maps.
However, during relocation the original definition of the map size was
still in use. For up to two maps, the calculation was correct if there
was a small difference in size between the map definition in libbpf and
the one that the client library uses. However if the difference was
large, particularly if more than two maps were used in the BPF program,
the relocation would fail.
For example, when using a map definition with size 28, with three maps,
map relocation would count:
(sym_offset / sizeof(struct bpf_map_def) => map_idx)
(0 / 16 => 0), ie map_idx = 0
(28 / 16 => 1), ie map_idx = 1
(56 / 16 => 3), ie map_idx = 3
So, libbpf reports:
libbpf: bpf relocation: map_idx 3 large than 2
Fix map relocation by checking the exact offset of maps when doing
relocation.
Signed-off-by: Joe Stringer <joe@ovn.org>
[Allow different map size in an object]
Signed-off-by: Wang Nan <wangnan0@huawei.com>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: netdev@vger.kernel.org
Fixes: 4708bbda5cb2 ("tools lib bpf: Fix maps resolution")
Link: http://lkml.kernel.org/r/20170123011128.26534-2-joe@ovn.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-23 01:11:22 +00:00
|
|
|
/* TODO: 'maps' is sorted. We can use bsearch to make it faster. */
|
|
|
|
for (map_idx = 0; map_idx < nr_maps; map_idx++) {
|
|
|
|
if (maps[map_idx].offset == sym.st_value) {
|
|
|
|
pr_debug("relocation: find map %zd (%s) for insn %u\n",
|
|
|
|
map_idx, maps[map_idx].name, insn_idx);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:02 +00:00
|
|
|
if (map_idx >= nr_maps) {
|
|
|
|
pr_warning("bpf relocation: map_idx %d large than %d\n",
|
|
|
|
(int)map_idx, (int)nr_maps - 1);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
prog->reloc_desc[i].type = RELO_LD64;
|
2015-07-01 02:14:02 +00:00
|
|
|
prog->reloc_desc[i].insn_idx = insn_idx;
|
|
|
|
prog->reloc_desc[i].map_idx = map_idx;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-04-18 22:56:05 +00:00
|
|
|
static int bpf_map_find_btf_info(struct bpf_map *map, const struct btf *btf)
|
|
|
|
{
|
|
|
|
struct bpf_map_def *def = &map->def;
|
|
|
|
const size_t max_name = 256;
|
|
|
|
int64_t key_size, value_size;
|
|
|
|
int32_t key_id, value_id;
|
|
|
|
char name[max_name];
|
|
|
|
|
|
|
|
/* Find key type by name from BTF */
|
|
|
|
if (snprintf(name, max_name, "%s_key", map->name) == max_name) {
|
|
|
|
pr_warning("map:%s length of BTF key_type:%s_key is too long\n",
|
|
|
|
map->name, map->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
key_id = btf__find_by_name(btf, name);
|
|
|
|
if (key_id < 0) {
|
|
|
|
pr_debug("map:%s key_type:%s cannot be found in BTF\n",
|
|
|
|
map->name, name);
|
|
|
|
return key_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
key_size = btf__resolve_size(btf, key_id);
|
|
|
|
if (key_size < 0) {
|
|
|
|
pr_warning("map:%s key_type:%s cannot get the BTF type_size\n",
|
|
|
|
map->name, name);
|
|
|
|
return key_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->key_size != key_size) {
|
2018-05-23 16:17:07 +00:00
|
|
|
pr_warning("map:%s key_type:%s has BTF type_size:%u != key_size:%u\n",
|
|
|
|
map->name, name, (unsigned int)key_size, def->key_size);
|
2018-04-18 22:56:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Find value type from BTF */
|
|
|
|
if (snprintf(name, max_name, "%s_value", map->name) == max_name) {
|
|
|
|
pr_warning("map:%s length of BTF value_type:%s_value is too long\n",
|
|
|
|
map->name, map->name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
value_id = btf__find_by_name(btf, name);
|
|
|
|
if (value_id < 0) {
|
|
|
|
pr_debug("map:%s value_type:%s cannot be found in BTF\n",
|
|
|
|
map->name, name);
|
|
|
|
return value_id;
|
|
|
|
}
|
|
|
|
|
|
|
|
value_size = btf__resolve_size(btf, value_id);
|
|
|
|
if (value_size < 0) {
|
|
|
|
pr_warning("map:%s value_type:%s cannot get the BTF type_size\n",
|
|
|
|
map->name, name);
|
|
|
|
return value_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (def->value_size != value_size) {
|
2018-05-23 16:17:07 +00:00
|
|
|
pr_warning("map:%s value_type:%s has BTF type_size:%u != value_size:%u\n",
|
|
|
|
map->name, name, (unsigned int)value_size, def->value_size);
|
2018-04-18 22:56:05 +00:00
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
2018-05-22 22:04:24 +00:00
|
|
|
map->btf_key_type_id = key_id;
|
|
|
|
map->btf_value_type_id = value_id;
|
2018-04-18 22:56:05 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
static int
|
|
|
|
bpf_object__create_maps(struct bpf_object *obj)
|
|
|
|
{
|
2018-04-18 22:56:05 +00:00
|
|
|
struct bpf_create_map_attr create_attr = {};
|
2015-07-01 02:14:04 +00:00
|
|
|
unsigned int i;
|
2018-04-18 22:56:05 +00:00
|
|
|
int err;
|
2015-07-01 02:14:04 +00:00
|
|
|
|
2015-11-27 08:47:35 +00:00
|
|
|
for (i = 0; i < obj->nr_maps; i++) {
|
2018-04-18 22:56:05 +00:00
|
|
|
struct bpf_map *map = &obj->maps[i];
|
|
|
|
struct bpf_map_def *def = &map->def;
|
|
|
|
int *pfd = &map->fd;
|
|
|
|
|
|
|
|
create_attr.name = map->name;
|
2018-05-16 21:02:49 +00:00
|
|
|
create_attr.map_ifindex = map->map_ifindex;
|
2018-04-18 22:56:05 +00:00
|
|
|
create_attr.map_type = def->type;
|
|
|
|
create_attr.map_flags = def->map_flags;
|
|
|
|
create_attr.key_size = def->key_size;
|
|
|
|
create_attr.value_size = def->value_size;
|
|
|
|
create_attr.max_entries = def->max_entries;
|
|
|
|
create_attr.btf_fd = 0;
|
2018-05-22 22:04:24 +00:00
|
|
|
create_attr.btf_key_type_id = 0;
|
|
|
|
create_attr.btf_value_type_id = 0;
|
2018-04-18 22:56:05 +00:00
|
|
|
|
|
|
|
if (obj->btf && !bpf_map_find_btf_info(map, obj->btf)) {
|
|
|
|
create_attr.btf_fd = btf__fd(obj->btf);
|
2018-05-22 22:04:24 +00:00
|
|
|
create_attr.btf_key_type_id = map->btf_key_type_id;
|
|
|
|
create_attr.btf_value_type_id = map->btf_value_type_id;
|
2018-04-18 22:56:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
*pfd = bpf_create_map_xattr(&create_attr);
|
2018-05-22 22:04:24 +00:00
|
|
|
if (*pfd < 0 && create_attr.btf_key_type_id) {
|
2018-04-18 22:56:05 +00:00
|
|
|
pr_warning("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
|
|
|
|
map->name, strerror(errno), errno);
|
|
|
|
create_attr.btf_fd = 0;
|
2018-05-22 22:04:24 +00:00
|
|
|
create_attr.btf_key_type_id = 0;
|
|
|
|
create_attr.btf_value_type_id = 0;
|
|
|
|
map->btf_key_type_id = 0;
|
|
|
|
map->btf_value_type_id = 0;
|
2018-04-18 22:56:05 +00:00
|
|
|
*pfd = bpf_create_map_xattr(&create_attr);
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
if (*pfd < 0) {
|
|
|
|
size_t j;
|
|
|
|
|
2018-04-18 22:56:05 +00:00
|
|
|
err = *pfd;
|
2017-08-20 19:48:14 +00:00
|
|
|
pr_warning("failed to create map (name: '%s'): %s\n",
|
2018-04-18 22:56:05 +00:00
|
|
|
map->name,
|
2015-07-01 02:14:04 +00:00
|
|
|
strerror(errno));
|
|
|
|
for (j = 0; j < i; j++)
|
2015-11-27 08:47:35 +00:00
|
|
|
zclose(obj->maps[j].fd);
|
2015-07-01 02:14:04 +00:00
|
|
|
return err;
|
|
|
|
}
|
2018-04-18 22:56:05 +00:00
|
|
|
pr_debug("create map %s: fd=%d\n", map->name, *pfd);
|
2015-07-01 02:14:04 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
static int
|
|
|
|
bpf_program__reloc_text(struct bpf_program *prog, struct bpf_object *obj,
|
|
|
|
struct reloc_desc *relo)
|
|
|
|
{
|
|
|
|
struct bpf_insn *insn, *new_insn;
|
|
|
|
struct bpf_program *text;
|
|
|
|
size_t new_cnt;
|
|
|
|
|
|
|
|
if (relo->type != RELO_CALL)
|
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
|
|
|
|
|
|
if (prog->idx == obj->efile.text_shndx) {
|
|
|
|
pr_warning("relo in .text insn %d into off %d\n",
|
|
|
|
relo->insn_idx, relo->text_off);
|
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prog->main_prog_cnt == 0) {
|
|
|
|
text = bpf_object__find_prog_by_idx(obj, obj->efile.text_shndx);
|
|
|
|
if (!text) {
|
|
|
|
pr_warning("no .text section found yet relo into text exist\n");
|
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
|
|
}
|
|
|
|
new_cnt = prog->insns_cnt + text->insns_cnt;
|
|
|
|
new_insn = realloc(prog->insns, new_cnt * sizeof(*insn));
|
|
|
|
if (!new_insn) {
|
|
|
|
pr_warning("oom in prog realloc\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
memcpy(new_insn + prog->insns_cnt, text->insns,
|
|
|
|
text->insns_cnt * sizeof(*insn));
|
|
|
|
prog->insns = new_insn;
|
|
|
|
prog->main_prog_cnt = prog->insns_cnt;
|
|
|
|
prog->insns_cnt = new_cnt;
|
2018-02-20 01:00:07 +00:00
|
|
|
pr_debug("added %zd insn from %s to prog %s\n",
|
|
|
|
text->insns_cnt, text->section_name,
|
|
|
|
prog->section_name);
|
2017-12-15 01:55:10 +00:00
|
|
|
}
|
|
|
|
insn = &prog->insns[relo->insn_idx];
|
|
|
|
insn->imm += prog->main_prog_cnt - relo->insn_idx;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:05 +00:00
|
|
|
static int
|
2015-11-27 08:47:35 +00:00
|
|
|
bpf_program__relocate(struct bpf_program *prog, struct bpf_object *obj)
|
2015-07-01 02:14:05 +00:00
|
|
|
{
|
2017-12-15 01:55:10 +00:00
|
|
|
int i, err;
|
2015-07-01 02:14:05 +00:00
|
|
|
|
|
|
|
if (!prog || !prog->reloc_desc)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < prog->nr_reloc; i++) {
|
2017-12-15 01:55:10 +00:00
|
|
|
if (prog->reloc_desc[i].type == RELO_LD64) {
|
|
|
|
struct bpf_insn *insns = prog->insns;
|
|
|
|
int insn_idx, map_idx;
|
2015-07-01 02:14:05 +00:00
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
insn_idx = prog->reloc_desc[i].insn_idx;
|
|
|
|
map_idx = prog->reloc_desc[i].map_idx;
|
2015-07-01 02:14:05 +00:00
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
if (insn_idx >= (int)prog->insns_cnt) {
|
|
|
|
pr_warning("relocation out of range: '%s'\n",
|
|
|
|
prog->section_name);
|
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
|
|
|
}
|
|
|
|
insns[insn_idx].src_reg = BPF_PSEUDO_MAP_FD;
|
|
|
|
insns[insn_idx].imm = obj->maps[map_idx].fd;
|
|
|
|
} else {
|
|
|
|
err = bpf_program__reloc_text(prog, obj,
|
|
|
|
&prog->reloc_desc[i]);
|
|
|
|
if (err)
|
|
|
|
return err;
|
2015-07-01 02:14:05 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
zfree(&prog->reloc_desc);
|
|
|
|
prog->nr_reloc = 0;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int
|
|
|
|
bpf_object__relocate(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
struct bpf_program *prog;
|
|
|
|
size_t i;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
for (i = 0; i < obj->nr_programs; i++) {
|
|
|
|
prog = &obj->programs[i];
|
|
|
|
|
2015-11-27 08:47:35 +00:00
|
|
|
err = bpf_program__relocate(prog, obj);
|
2015-07-01 02:14:05 +00:00
|
|
|
if (err) {
|
|
|
|
pr_warning("failed to relocate '%s'\n",
|
|
|
|
prog->section_name);
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:02 +00:00
|
|
|
static int bpf_object__collect_reloc(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
if (!obj_elf_valid(obj)) {
|
|
|
|
pr_warning("Internal error: elf object is closed\n");
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__INTERNAL;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < obj->efile.nr_reloc; i++) {
|
|
|
|
GElf_Shdr *shdr = &obj->efile.reloc[i].shdr;
|
|
|
|
Elf_Data *data = obj->efile.reloc[i].data;
|
|
|
|
int idx = shdr->sh_info;
|
|
|
|
struct bpf_program *prog;
|
|
|
|
|
|
|
|
if (shdr->sh_type != SHT_REL) {
|
|
|
|
pr_warning("internal error at %d\n", __LINE__);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__INTERNAL;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
prog = bpf_object__find_prog_by_idx(obj, idx);
|
|
|
|
if (!prog) {
|
2018-02-08 11:48:17 +00:00
|
|
|
pr_warning("relocation failed: no section(%d)\n", idx);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__RELOC;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
err = bpf_program__collect_reloc(prog,
|
2015-07-01 02:14:02 +00:00
|
|
|
shdr, data,
|
2017-12-15 01:55:10 +00:00
|
|
|
obj);
|
2015-07-01 02:14:02 +00:00
|
|
|
if (err)
|
2015-11-06 13:49:37 +00:00
|
|
|
return err;
|
2015-07-01 02:14:02 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:07 +00:00
|
|
|
static int
|
2018-03-30 22:08:01 +00:00
|
|
|
load_program(enum bpf_prog_type type, enum bpf_attach_type expected_attach_type,
|
|
|
|
const char *name, struct bpf_insn *insns, int insns_cnt,
|
2018-05-16 21:02:49 +00:00
|
|
|
char *license, u32 kern_version, int *pfd, int prog_ifindex)
|
2015-07-01 02:14:07 +00:00
|
|
|
{
|
2018-03-30 22:08:01 +00:00
|
|
|
struct bpf_load_program_attr load_attr;
|
2015-07-01 02:14:07 +00:00
|
|
|
char *log_buf;
|
2018-03-30 22:08:01 +00:00
|
|
|
int ret;
|
2015-07-01 02:14:07 +00:00
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
|
|
|
|
load_attr.prog_type = type;
|
|
|
|
load_attr.expected_attach_type = expected_attach_type;
|
|
|
|
load_attr.name = name;
|
|
|
|
load_attr.insns = insns;
|
|
|
|
load_attr.insns_cnt = insns_cnt;
|
|
|
|
load_attr.license = license;
|
|
|
|
load_attr.kern_version = kern_version;
|
2018-05-16 21:02:49 +00:00
|
|
|
load_attr.prog_ifindex = prog_ifindex;
|
2018-03-30 22:08:01 +00:00
|
|
|
|
|
|
|
if (!load_attr.insns || !load_attr.insns_cnt)
|
2015-07-01 02:14:07 +00:00
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
log_buf = malloc(BPF_LOG_BUF_SIZE);
|
|
|
|
if (!log_buf)
|
|
|
|
pr_warning("Alloc log buffer for bpf loader error, continue without log\n");
|
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
ret = bpf_load_program_xattr(&load_attr, log_buf, BPF_LOG_BUF_SIZE);
|
2015-07-01 02:14:07 +00:00
|
|
|
|
|
|
|
if (ret >= 0) {
|
|
|
|
*pfd = ret;
|
|
|
|
ret = 0;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
2015-11-06 13:49:37 +00:00
|
|
|
ret = -LIBBPF_ERRNO__LOAD;
|
2015-07-01 02:14:07 +00:00
|
|
|
pr_warning("load bpf program failed: %s\n", strerror(errno));
|
|
|
|
|
2015-11-06 13:49:37 +00:00
|
|
|
if (log_buf && log_buf[0] != '\0') {
|
|
|
|
ret = -LIBBPF_ERRNO__VERIFY;
|
2015-07-01 02:14:07 +00:00
|
|
|
pr_warning("-- BEGIN DUMP LOG ---\n");
|
|
|
|
pr_warning("\n%s\n", log_buf);
|
|
|
|
pr_warning("-- END LOG --\n");
|
2018-03-30 22:08:01 +00:00
|
|
|
} else if (load_attr.insns_cnt >= BPF_MAXINSNS) {
|
|
|
|
pr_warning("Program too large (%zu insns), at most %d insns\n",
|
|
|
|
load_attr.insns_cnt, BPF_MAXINSNS);
|
2016-07-13 10:44:02 +00:00
|
|
|
ret = -LIBBPF_ERRNO__PROG2BIG;
|
2015-11-06 13:49:37 +00:00
|
|
|
} else {
|
2016-07-13 10:44:02 +00:00
|
|
|
/* Wrong program type? */
|
2018-03-30 22:08:01 +00:00
|
|
|
if (load_attr.prog_type != BPF_PROG_TYPE_KPROBE) {
|
2016-07-13 10:44:02 +00:00
|
|
|
int fd;
|
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
load_attr.prog_type = BPF_PROG_TYPE_KPROBE;
|
|
|
|
load_attr.expected_attach_type = 0;
|
|
|
|
fd = bpf_load_program_xattr(&load_attr, NULL, 0);
|
2016-07-13 10:44:02 +00:00
|
|
|
if (fd >= 0) {
|
|
|
|
close(fd);
|
|
|
|
ret = -LIBBPF_ERRNO__PROGTYPE;
|
|
|
|
goto out;
|
|
|
|
}
|
2015-11-06 13:49:37 +00:00
|
|
|
}
|
2016-07-13 10:44:02 +00:00
|
|
|
|
|
|
|
if (log_buf)
|
|
|
|
ret = -LIBBPF_ERRNO__KVER;
|
2015-07-01 02:14:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
out:
|
|
|
|
free(log_buf);
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bpf_program__load(struct bpf_program *prog,
|
|
|
|
char *license, u32 kern_version)
|
|
|
|
{
|
2015-11-16 12:10:09 +00:00
|
|
|
int err = 0, fd, i;
|
2015-07-01 02:14:07 +00:00
|
|
|
|
2015-11-16 12:10:09 +00:00
|
|
|
if (prog->instances.nr < 0 || !prog->instances.fds) {
|
|
|
|
if (prog->preprocessor) {
|
|
|
|
pr_warning("Internal error: can't load program '%s'\n",
|
|
|
|
prog->section_name);
|
|
|
|
return -LIBBPF_ERRNO__INTERNAL;
|
|
|
|
}
|
2015-07-01 02:14:07 +00:00
|
|
|
|
2015-11-16 12:10:09 +00:00
|
|
|
prog->instances.fds = malloc(sizeof(int));
|
|
|
|
if (!prog->instances.fds) {
|
|
|
|
pr_warning("Not enough memory for BPF fds\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
prog->instances.nr = 1;
|
|
|
|
prog->instances.fds[0] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!prog->preprocessor) {
|
|
|
|
if (prog->instances.nr != 1) {
|
|
|
|
pr_warning("Program '%s' is inconsistent: nr(%d) != 1\n",
|
|
|
|
prog->section_name, prog->instances.nr);
|
|
|
|
}
|
2018-03-30 22:08:01 +00:00
|
|
|
err = load_program(prog->type, prog->expected_attach_type,
|
|
|
|
prog->name, prog->insns, prog->insns_cnt,
|
2018-05-16 21:02:49 +00:00
|
|
|
license, kern_version, &fd,
|
|
|
|
prog->prog_ifindex);
|
2015-11-16 12:10:09 +00:00
|
|
|
if (!err)
|
|
|
|
prog->instances.fds[0] = fd;
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < prog->instances.nr; i++) {
|
|
|
|
struct bpf_prog_prep_result result;
|
|
|
|
bpf_program_prep_t preprocessor = prog->preprocessor;
|
|
|
|
|
|
|
|
bzero(&result, sizeof(result));
|
|
|
|
err = preprocessor(prog, i, prog->insns,
|
|
|
|
prog->insns_cnt, &result);
|
|
|
|
if (err) {
|
|
|
|
pr_warning("Preprocessing the %dth instance of program '%s' failed\n",
|
|
|
|
i, prog->section_name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!result.new_insn_ptr || !result.new_insn_cnt) {
|
|
|
|
pr_debug("Skip loading the %dth instance of program '%s'\n",
|
|
|
|
i, prog->section_name);
|
|
|
|
prog->instances.fds[i] = -1;
|
|
|
|
if (result.pfd)
|
|
|
|
*result.pfd = -1;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
err = load_program(prog->type, prog->expected_attach_type,
|
|
|
|
prog->name, result.new_insn_ptr,
|
2015-11-16 12:10:09 +00:00
|
|
|
result.new_insn_cnt,
|
2018-05-16 21:02:49 +00:00
|
|
|
license, kern_version, &fd,
|
|
|
|
prog->prog_ifindex);
|
2015-11-16 12:10:09 +00:00
|
|
|
|
|
|
|
if (err) {
|
|
|
|
pr_warning("Loading the %dth instance of program '%s' failed\n",
|
|
|
|
i, prog->section_name);
|
|
|
|
goto out;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result.pfd)
|
|
|
|
*result.pfd = fd;
|
|
|
|
prog->instances.fds[i] = fd;
|
|
|
|
}
|
|
|
|
out:
|
2015-07-01 02:14:07 +00:00
|
|
|
if (err)
|
|
|
|
pr_warning("failed to load program '%s'\n",
|
|
|
|
prog->section_name);
|
|
|
|
zfree(&prog->insns);
|
|
|
|
prog->insns_cnt = 0;
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
bpf_object__load_progs(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
for (i = 0; i < obj->nr_programs; i++) {
|
2017-12-15 01:55:10 +00:00
|
|
|
if (obj->programs[i].idx == obj->efile.text_shndx)
|
|
|
|
continue;
|
2015-07-01 02:14:07 +00:00
|
|
|
err = bpf_program__load(&obj->programs[i],
|
|
|
|
obj->license,
|
|
|
|
obj->kern_version);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2018-05-10 17:24:42 +00:00
|
|
|
static bool bpf_prog_type__needs_kver(enum bpf_prog_type type)
|
|
|
|
{
|
|
|
|
switch (type) {
|
|
|
|
case BPF_PROG_TYPE_SOCKET_FILTER:
|
|
|
|
case BPF_PROG_TYPE_SCHED_CLS:
|
|
|
|
case BPF_PROG_TYPE_SCHED_ACT:
|
|
|
|
case BPF_PROG_TYPE_XDP:
|
|
|
|
case BPF_PROG_TYPE_CGROUP_SKB:
|
|
|
|
case BPF_PROG_TYPE_CGROUP_SOCK:
|
|
|
|
case BPF_PROG_TYPE_LWT_IN:
|
|
|
|
case BPF_PROG_TYPE_LWT_OUT:
|
|
|
|
case BPF_PROG_TYPE_LWT_XMIT:
|
|
|
|
case BPF_PROG_TYPE_SOCK_OPS:
|
|
|
|
case BPF_PROG_TYPE_SK_SKB:
|
|
|
|
case BPF_PROG_TYPE_CGROUP_DEVICE:
|
|
|
|
case BPF_PROG_TYPE_SK_MSG:
|
|
|
|
case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
|
|
|
|
return false;
|
|
|
|
case BPF_PROG_TYPE_UNSPEC:
|
|
|
|
case BPF_PROG_TYPE_KPROBE:
|
|
|
|
case BPF_PROG_TYPE_TRACEPOINT:
|
|
|
|
case BPF_PROG_TYPE_PERF_EVENT:
|
|
|
|
case BPF_PROG_TYPE_RAW_TRACEPOINT:
|
|
|
|
default:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bpf_object__validate(struct bpf_object *obj, bool needs_kver)
|
2015-07-01 02:13:57 +00:00
|
|
|
{
|
2018-05-10 17:24:42 +00:00
|
|
|
if (needs_kver && obj->kern_version == 0) {
|
2015-07-01 02:13:57 +00:00
|
|
|
pr_warning("%s doesn't provide kernel version\n",
|
|
|
|
obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
return -LIBBPF_ERRNO__KVERSION;
|
2015-07-01 02:13:57 +00:00
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
static struct bpf_object *
|
2018-05-10 17:24:42 +00:00
|
|
|
__bpf_object__open(const char *path, void *obj_buf, size_t obj_buf_sz,
|
|
|
|
bool needs_kver)
|
2015-07-01 02:13:53 +00:00
|
|
|
{
|
|
|
|
struct bpf_object *obj;
|
2015-11-06 13:49:37 +00:00
|
|
|
int err;
|
2015-07-01 02:13:53 +00:00
|
|
|
|
|
|
|
if (elf_version(EV_CURRENT) == EV_NONE) {
|
|
|
|
pr_warning("failed to init libelf for %s\n", path);
|
2015-11-06 13:49:37 +00:00
|
|
|
return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:54 +00:00
|
|
|
obj = bpf_object__new(path, obj_buf, obj_buf_sz);
|
2015-11-06 13:49:37 +00:00
|
|
|
if (IS_ERR(obj))
|
|
|
|
return obj;
|
2015-07-01 02:13:53 +00:00
|
|
|
|
2015-11-06 13:49:37 +00:00
|
|
|
CHECK_ERR(bpf_object__elf_init(obj), err, out);
|
|
|
|
CHECK_ERR(bpf_object__check_endianness(obj), err, out);
|
|
|
|
CHECK_ERR(bpf_object__elf_collect(obj), err, out);
|
|
|
|
CHECK_ERR(bpf_object__collect_reloc(obj), err, out);
|
2018-05-10 17:24:42 +00:00
|
|
|
CHECK_ERR(bpf_object__validate(obj, needs_kver), err, out);
|
2015-07-01 02:13:53 +00:00
|
|
|
|
|
|
|
bpf_object__elf_finish(obj);
|
|
|
|
return obj;
|
|
|
|
out:
|
|
|
|
bpf_object__close(obj);
|
2015-11-06 13:49:37 +00:00
|
|
|
return ERR_PTR(err);
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct bpf_object *bpf_object__open(const char *path)
|
|
|
|
{
|
|
|
|
/* param validation */
|
|
|
|
if (!path)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
pr_debug("loading %s\n", path);
|
|
|
|
|
2018-05-10 17:24:42 +00:00
|
|
|
return __bpf_object__open(path, NULL, 0, true);
|
2015-07-01 02:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct bpf_object *bpf_object__open_buffer(void *obj_buf,
|
2015-08-27 02:30:55 +00:00
|
|
|
size_t obj_buf_sz,
|
|
|
|
const char *name)
|
2015-07-01 02:13:54 +00:00
|
|
|
{
|
2015-08-27 02:30:55 +00:00
|
|
|
char tmp_name[64];
|
|
|
|
|
2015-07-01 02:13:54 +00:00
|
|
|
/* param validation */
|
|
|
|
if (!obj_buf || obj_buf_sz <= 0)
|
|
|
|
return NULL;
|
|
|
|
|
2015-08-27 02:30:55 +00:00
|
|
|
if (!name) {
|
|
|
|
snprintf(tmp_name, sizeof(tmp_name), "%lx-%lx",
|
|
|
|
(unsigned long)obj_buf,
|
|
|
|
(unsigned long)obj_buf_sz);
|
|
|
|
tmp_name[sizeof(tmp_name) - 1] = '\0';
|
|
|
|
name = tmp_name;
|
|
|
|
}
|
|
|
|
pr_debug("loading object '%s' from buffer\n",
|
|
|
|
name);
|
2015-07-01 02:13:54 +00:00
|
|
|
|
2018-05-10 17:24:42 +00:00
|
|
|
return __bpf_object__open(name, obj_buf, obj_buf_sz, true);
|
2015-07-01 02:13:53 +00:00
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
int bpf_object__unload(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
size_t i;
|
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return -EINVAL;
|
|
|
|
|
2015-11-27 08:47:35 +00:00
|
|
|
for (i = 0; i < obj->nr_maps; i++)
|
|
|
|
zclose(obj->maps[i].fd);
|
2015-07-01 02:14:04 +00:00
|
|
|
|
2015-07-01 02:14:07 +00:00
|
|
|
for (i = 0; i < obj->nr_programs; i++)
|
|
|
|
bpf_program__unload(&obj->programs[i]);
|
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_object__load(struct bpf_object *obj)
|
|
|
|
{
|
2015-11-06 13:49:37 +00:00
|
|
|
int err;
|
|
|
|
|
2015-07-01 02:14:04 +00:00
|
|
|
if (!obj)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (obj->loaded) {
|
|
|
|
pr_warning("object should not be loaded twice\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj->loaded = true;
|
2015-11-06 13:49:37 +00:00
|
|
|
|
|
|
|
CHECK_ERR(bpf_object__create_maps(obj), err, out);
|
|
|
|
CHECK_ERR(bpf_object__relocate(obj), err, out);
|
|
|
|
CHECK_ERR(bpf_object__load_progs(obj), err, out);
|
2015-07-01 02:14:04 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
out:
|
|
|
|
bpf_object__unload(obj);
|
|
|
|
pr_warning("failed to load object '%s'\n", obj->path);
|
2015-11-06 13:49:37 +00:00
|
|
|
return err;
|
2015-07-01 02:14:04 +00:00
|
|
|
}
|
|
|
|
|
tools lib bpf: Add BPF program pinning APIs
Add new APIs to pin a BPF program (or specific instances) to the
filesystem. The user can specify the path full path within a BPF
filesystem to pin the program.
bpf_program__pin_instance(prog, path, n) will pin the nth instance of
'prog' to the specified path.
bpf_program__pin(prog, path) will create the directory 'path' (if it
does not exist) and pin each instance within that directory. For
instance, path/0, path/1, path/2.
Committer notes:
- Add missing headers for mkdir()
- Check strdup() for failure
- Check snprintf >= size, not >, as == also means truncated, see 'man
snprintf', return value.
- Conditionally define BPF_FS_MAGIC, as it isn't in magic.h in older
systems and we're not yet having a tools/include/uapi/linux/magic.h
copy.
- Do not include linux/magic.h, not present in older distros.
Signed-off-by: Joe Stringer <joe@ovn.org>
Cc: Alexei Starovoitov <ast@fb.com>
Cc: Daniel Borkmann <daniel@iogearbox.net>
Cc: Wang Nan <wangnan0@huawei.com>
Cc: netdev@vger.kernel.org
Link: http://lkml.kernel.org/r/20170126212001.14103-2-joe@ovn.org
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2017-01-26 21:19:56 +00:00
|
|
|
static int check_path(const char *path)
|
|
|
|
{
|
|
|
|
struct statfs st_fs;
|
|
|
|
char *dname, *dir;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (path == NULL)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
dname = strdup(path);
|
|
|
|
if (dname == NULL)
|
|
|
|
return -ENOMEM;
|
|
|
|
|
|
|
|
dir = dirname(dname);
|
|
|
|
if (statfs(dir, &st_fs)) {
|
|
|
|
pr_warning("failed to statfs %s: %s\n", dir, strerror(errno));
|
|
|
|
err = -errno;
|
|
|
|
}
|
|
|
|
free(dname);
|
|
|
|
|
|
|
|
if (!err && st_fs.f_type != BPF_FS_MAGIC) {
|
|
|
|
pr_warning("specified path %s is not on BPF FS\n", path);
|
|
|
|
err = -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_program__pin_instance(struct bpf_program *prog, const char *path,
|
|
|
|
int instance)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = check_path(path);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (prog == NULL) {
|
|
|
|
pr_warning("invalid program pointer\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (instance < 0 || instance >= prog->instances.nr) {
|
|
|
|
pr_warning("invalid prog instance %d of prog %s (max %d)\n",
|
|
|
|
instance, prog->section_name, prog->instances.nr);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bpf_obj_pin(prog->instances.fds[instance], path)) {
|
|
|
|
pr_warning("failed to pin program: %s\n", strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
pr_debug("pinned program '%s'\n", path);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int make_dir(const char *path)
|
|
|
|
{
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (mkdir(path, 0700) && errno != EEXIST)
|
|
|
|
err = -errno;
|
|
|
|
|
|
|
|
if (err)
|
|
|
|
pr_warning("failed to mkdir %s: %s\n", path, strerror(-err));
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_program__pin(struct bpf_program *prog, const char *path)
|
|
|
|
{
|
|
|
|
int i, err;
|
|
|
|
|
|
|
|
err = check_path(path);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (prog == NULL) {
|
|
|
|
pr_warning("invalid program pointer\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (prog->instances.nr <= 0) {
|
|
|
|
pr_warning("no instances of prog %s to pin\n",
|
|
|
|
prog->section_name);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = make_dir(path);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
for (i = 0; i < prog->instances.nr; i++) {
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
|
|
|
|
if (len < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
else if (len >= PATH_MAX)
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
|
|
|
err = bpf_program__pin_instance(prog, buf, i);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-26 21:19:57 +00:00
|
|
|
int bpf_map__pin(struct bpf_map *map, const char *path)
|
|
|
|
{
|
|
|
|
int err;
|
|
|
|
|
|
|
|
err = check_path(path);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
if (map == NULL) {
|
|
|
|
pr_warning("invalid map pointer\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bpf_obj_pin(map->fd, path)) {
|
|
|
|
pr_warning("failed to pin map: %s\n", strerror(errno));
|
|
|
|
return -errno;
|
|
|
|
}
|
|
|
|
|
|
|
|
pr_debug("pinned map '%s'\n", path);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2017-01-26 21:19:58 +00:00
|
|
|
int bpf_object__pin(struct bpf_object *obj, const char *path)
|
|
|
|
{
|
|
|
|
struct bpf_program *prog;
|
|
|
|
struct bpf_map *map;
|
|
|
|
int err;
|
|
|
|
|
|
|
|
if (!obj)
|
|
|
|
return -ENOENT;
|
|
|
|
|
|
|
|
if (!obj->loaded) {
|
|
|
|
pr_warning("object not yet loaded; load it first\n");
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
err = make_dir(path);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
|
|
|
|
bpf_map__for_each(map, obj) {
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = snprintf(buf, PATH_MAX, "%s/%s", path,
|
|
|
|
bpf_map__name(map));
|
|
|
|
if (len < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
else if (len >= PATH_MAX)
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
|
|
|
err = bpf_map__pin(map, buf);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
bpf_object__for_each_program(prog, obj) {
|
|
|
|
char buf[PATH_MAX];
|
|
|
|
int len;
|
|
|
|
|
|
|
|
len = snprintf(buf, PATH_MAX, "%s/%s", path,
|
|
|
|
prog->section_name);
|
|
|
|
if (len < 0)
|
|
|
|
return -EINVAL;
|
|
|
|
else if (len >= PATH_MAX)
|
|
|
|
return -ENAMETOOLONG;
|
|
|
|
|
|
|
|
err = bpf_program__pin(prog, buf);
|
|
|
|
if (err)
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
void bpf_object__close(struct bpf_object *obj)
|
|
|
|
{
|
2015-07-01 02:14:00 +00:00
|
|
|
size_t i;
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
if (!obj)
|
|
|
|
return;
|
|
|
|
|
2016-11-26 07:03:26 +00:00
|
|
|
if (obj->clear_priv)
|
|
|
|
obj->clear_priv(obj, obj->priv);
|
|
|
|
|
2015-07-01 02:13:53 +00:00
|
|
|
bpf_object__elf_finish(obj);
|
2015-07-01 02:14:04 +00:00
|
|
|
bpf_object__unload(obj);
|
2018-04-18 22:56:05 +00:00
|
|
|
btf__free(obj->btf);
|
2015-07-01 02:13:53 +00:00
|
|
|
|
2015-11-27 08:47:35 +00:00
|
|
|
for (i = 0; i < obj->nr_maps; i++) {
|
2015-11-27 08:47:36 +00:00
|
|
|
zfree(&obj->maps[i].name);
|
2015-11-27 08:47:35 +00:00
|
|
|
if (obj->maps[i].clear_priv)
|
|
|
|
obj->maps[i].clear_priv(&obj->maps[i],
|
|
|
|
obj->maps[i].priv);
|
|
|
|
obj->maps[i].priv = NULL;
|
|
|
|
obj->maps[i].clear_priv = NULL;
|
|
|
|
}
|
|
|
|
zfree(&obj->maps);
|
|
|
|
obj->nr_maps = 0;
|
2015-07-01 02:14:00 +00:00
|
|
|
|
|
|
|
if (obj->programs && obj->nr_programs) {
|
|
|
|
for (i = 0; i < obj->nr_programs; i++)
|
|
|
|
bpf_program__exit(&obj->programs[i]);
|
|
|
|
}
|
|
|
|
zfree(&obj->programs);
|
|
|
|
|
2015-07-01 02:14:10 +00:00
|
|
|
list_del(&obj->list);
|
2015-07-01 02:13:53 +00:00
|
|
|
free(obj);
|
|
|
|
}
|
2015-07-01 02:14:08 +00:00
|
|
|
|
2015-07-01 02:14:10 +00:00
|
|
|
struct bpf_object *
|
|
|
|
bpf_object__next(struct bpf_object *prev)
|
|
|
|
{
|
|
|
|
struct bpf_object *next;
|
|
|
|
|
|
|
|
if (!prev)
|
|
|
|
next = list_first_entry(&bpf_objects_list,
|
|
|
|
struct bpf_object,
|
|
|
|
list);
|
|
|
|
else
|
|
|
|
next = list_next_entry(prev, list);
|
|
|
|
|
|
|
|
/* Empty list is noticed here so don't need checking on entry. */
|
|
|
|
if (&next->list == &bpf_objects_list)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
return next;
|
|
|
|
}
|
|
|
|
|
2016-06-03 15:22:51 +00:00
|
|
|
const char *bpf_object__name(struct bpf_object *obj)
|
2015-08-27 02:30:55 +00:00
|
|
|
{
|
2016-06-03 15:22:51 +00:00
|
|
|
return obj ? obj->path : ERR_PTR(-EINVAL);
|
2015-08-27 02:30:55 +00:00
|
|
|
}
|
|
|
|
|
2016-06-03 15:22:51 +00:00
|
|
|
unsigned int bpf_object__kversion(struct bpf_object *obj)
|
2015-11-06 13:49:38 +00:00
|
|
|
{
|
2016-06-03 15:22:51 +00:00
|
|
|
return obj ? obj->kern_version : 0;
|
2015-11-06 13:49:38 +00:00
|
|
|
}
|
|
|
|
|
2018-04-18 22:56:05 +00:00
|
|
|
int bpf_object__btf_fd(const struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
return obj->btf ? btf__fd(obj->btf) : -1;
|
|
|
|
}
|
|
|
|
|
2016-11-26 07:03:26 +00:00
|
|
|
int bpf_object__set_priv(struct bpf_object *obj, void *priv,
|
|
|
|
bpf_object_clear_priv_t clear_priv)
|
|
|
|
{
|
|
|
|
if (obj->priv && obj->clear_priv)
|
|
|
|
obj->clear_priv(obj, obj->priv);
|
|
|
|
|
|
|
|
obj->priv = priv;
|
|
|
|
obj->clear_priv = clear_priv;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *bpf_object__priv(struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
return obj ? obj->priv : ERR_PTR(-EINVAL);
|
|
|
|
}
|
|
|
|
|
2015-07-01 02:14:08 +00:00
|
|
|
struct bpf_program *
|
|
|
|
bpf_program__next(struct bpf_program *prev, struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
size_t idx;
|
|
|
|
|
|
|
|
if (!obj->programs)
|
|
|
|
return NULL;
|
|
|
|
/* First handler */
|
|
|
|
if (prev == NULL)
|
|
|
|
return &obj->programs[0];
|
|
|
|
|
|
|
|
if (prev->obj != obj) {
|
|
|
|
pr_warning("error: program handler doesn't match object\n");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = (prev - obj->programs) + 1;
|
|
|
|
if (idx >= obj->nr_programs)
|
|
|
|
return NULL;
|
|
|
|
return &obj->programs[idx];
|
|
|
|
}
|
|
|
|
|
2016-06-03 15:38:21 +00:00
|
|
|
int bpf_program__set_priv(struct bpf_program *prog, void *priv,
|
|
|
|
bpf_program_clear_priv_t clear_priv)
|
2015-07-01 02:14:08 +00:00
|
|
|
{
|
|
|
|
if (prog->priv && prog->clear_priv)
|
|
|
|
prog->clear_priv(prog, prog->priv);
|
|
|
|
|
|
|
|
prog->priv = priv;
|
|
|
|
prog->clear_priv = clear_priv;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-03 15:36:39 +00:00
|
|
|
void *bpf_program__priv(struct bpf_program *prog)
|
2015-07-01 02:14:08 +00:00
|
|
|
{
|
2016-06-03 15:36:39 +00:00
|
|
|
return prog ? prog->priv : ERR_PTR(-EINVAL);
|
2015-07-01 02:14:08 +00:00
|
|
|
}
|
|
|
|
|
2015-11-03 11:21:05 +00:00
|
|
|
const char *bpf_program__title(struct bpf_program *prog, bool needs_copy)
|
2015-07-01 02:14:08 +00:00
|
|
|
{
|
|
|
|
const char *title;
|
|
|
|
|
|
|
|
title = prog->section_name;
|
2015-11-03 11:21:05 +00:00
|
|
|
if (needs_copy) {
|
2015-07-01 02:14:08 +00:00
|
|
|
title = strdup(title);
|
|
|
|
if (!title) {
|
|
|
|
pr_warning("failed to strdup program title\n");
|
2015-11-06 13:49:37 +00:00
|
|
|
return ERR_PTR(-ENOMEM);
|
2015-07-01 02:14:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return title;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_program__fd(struct bpf_program *prog)
|
|
|
|
{
|
2015-11-16 12:10:09 +00:00
|
|
|
return bpf_program__nth_fd(prog, 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_program__set_prep(struct bpf_program *prog, int nr_instances,
|
|
|
|
bpf_program_prep_t prep)
|
|
|
|
{
|
|
|
|
int *instances_fds;
|
|
|
|
|
|
|
|
if (nr_instances <= 0 || !prep)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (prog->instances.nr > 0 || prog->instances.fds) {
|
|
|
|
pr_warning("Can't set pre-processor after loading\n");
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
instances_fds = malloc(sizeof(int) * nr_instances);
|
|
|
|
if (!instances_fds) {
|
|
|
|
pr_warning("alloc memory failed for fds\n");
|
|
|
|
return -ENOMEM;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* fill all fd with -1 */
|
|
|
|
memset(instances_fds, -1, sizeof(int) * nr_instances);
|
|
|
|
|
|
|
|
prog->instances.nr = nr_instances;
|
|
|
|
prog->instances.fds = instances_fds;
|
|
|
|
prog->preprocessor = prep;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_program__nth_fd(struct bpf_program *prog, int n)
|
|
|
|
{
|
|
|
|
int fd;
|
|
|
|
|
|
|
|
if (n >= prog->instances.nr || n < 0) {
|
|
|
|
pr_warning("Can't get the %dth fd from program %s: only %d instances\n",
|
|
|
|
n, prog->section_name, prog->instances.nr);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
fd = prog->instances.fds[n];
|
|
|
|
if (fd < 0) {
|
|
|
|
pr_warning("%dth instance of program '%s' is invalid\n",
|
|
|
|
n, prog->section_name);
|
|
|
|
return -ENOENT;
|
|
|
|
}
|
|
|
|
|
|
|
|
return fd;
|
2015-07-01 02:14:08 +00:00
|
|
|
}
|
2015-11-27 08:47:35 +00:00
|
|
|
|
2017-03-31 04:45:40 +00:00
|
|
|
void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
|
2016-07-13 10:44:01 +00:00
|
|
|
{
|
|
|
|
prog->type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool bpf_program__is_type(struct bpf_program *prog,
|
|
|
|
enum bpf_prog_type type)
|
|
|
|
{
|
|
|
|
return prog ? (prog->type == type) : false;
|
|
|
|
}
|
|
|
|
|
2017-01-23 01:11:23 +00:00
|
|
|
#define BPF_PROG_TYPE_FNS(NAME, TYPE) \
|
|
|
|
int bpf_program__set_##NAME(struct bpf_program *prog) \
|
|
|
|
{ \
|
|
|
|
if (!prog) \
|
|
|
|
return -EINVAL; \
|
|
|
|
bpf_program__set_type(prog, TYPE); \
|
|
|
|
return 0; \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
bool bpf_program__is_##NAME(struct bpf_program *prog) \
|
|
|
|
{ \
|
|
|
|
return bpf_program__is_type(prog, TYPE); \
|
|
|
|
} \
|
|
|
|
|
2017-01-23 01:11:24 +00:00
|
|
|
BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
|
2017-01-23 01:11:23 +00:00
|
|
|
BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
|
2017-01-23 01:11:24 +00:00
|
|
|
BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
|
|
|
|
BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
|
2017-01-23 01:11:23 +00:00
|
|
|
BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
|
2018-04-17 17:28:46 +00:00
|
|
|
BPF_PROG_TYPE_FNS(raw_tracepoint, BPF_PROG_TYPE_RAW_TRACEPOINT);
|
2017-01-23 01:11:24 +00:00
|
|
|
BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
|
|
|
|
BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
|
2016-07-13 10:44:01 +00:00
|
|
|
|
2018-04-23 21:30:38 +00:00
|
|
|
void bpf_program__set_expected_attach_type(struct bpf_program *prog,
|
|
|
|
enum bpf_attach_type type)
|
2018-03-30 22:08:01 +00:00
|
|
|
{
|
|
|
|
prog->expected_attach_type = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define BPF_PROG_SEC_FULL(string, ptype, atype) \
|
|
|
|
{ string, sizeof(string) - 1, ptype, atype }
|
|
|
|
|
|
|
|
#define BPF_PROG_SEC(string, ptype) BPF_PROG_SEC_FULL(string, ptype, 0)
|
|
|
|
|
2018-04-17 17:28:45 +00:00
|
|
|
#define BPF_S_PROG_SEC(string, ptype) \
|
|
|
|
BPF_PROG_SEC_FULL(string, BPF_PROG_TYPE_CGROUP_SOCK, ptype)
|
|
|
|
|
selftests/bpf: Selftest for sys_bind hooks
Add selftest to work with bpf_sock_addr context from
`BPF_PROG_TYPE_CGROUP_SOCK_ADDR` programs.
Try to bind(2) on IP:port and apply:
* loads to make sure context can be read correctly, including narrow
loads (byte, half) for IP and full-size loads (word) for all fields;
* stores to those fields allowed by verifier.
All combination from IPv4/IPv6 and TCP/UDP are tested.
Both scenarios are tested:
* valid programs can be loaded and attached;
* invalid programs can be neither loaded nor attached.
Test passes when expected data can be read from context in the
BPF-program, and after the call to bind(2) socket is bound to IP:port
pair that was written by BPF-program to the context.
Example:
# ./test_sock_addr
Attached bind4 program.
Test case #1 (IPv4/TCP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Test case #2 (IPv4/UDP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Attached bind6 program.
Test case #3 (IPv6/TCP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
Test case #4 (IPv6/UDP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
### SUCCESS
Signed-off-by: Andrey Ignatov <rdna@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-03-30 22:08:03 +00:00
|
|
|
#define BPF_SA_PROG_SEC(string, ptype) \
|
|
|
|
BPF_PROG_SEC_FULL(string, BPF_PROG_TYPE_CGROUP_SOCK_ADDR, ptype)
|
|
|
|
|
2017-12-13 15:18:51 +00:00
|
|
|
static const struct {
|
|
|
|
const char *sec;
|
|
|
|
size_t len;
|
|
|
|
enum bpf_prog_type prog_type;
|
2018-03-30 22:08:01 +00:00
|
|
|
enum bpf_attach_type expected_attach_type;
|
2017-12-13 15:18:51 +00:00
|
|
|
} section_names[] = {
|
|
|
|
BPF_PROG_SEC("socket", BPF_PROG_TYPE_SOCKET_FILTER),
|
|
|
|
BPF_PROG_SEC("kprobe/", BPF_PROG_TYPE_KPROBE),
|
|
|
|
BPF_PROG_SEC("kretprobe/", BPF_PROG_TYPE_KPROBE),
|
2018-02-08 04:27:13 +00:00
|
|
|
BPF_PROG_SEC("classifier", BPF_PROG_TYPE_SCHED_CLS),
|
|
|
|
BPF_PROG_SEC("action", BPF_PROG_TYPE_SCHED_ACT),
|
2017-12-13 15:18:51 +00:00
|
|
|
BPF_PROG_SEC("tracepoint/", BPF_PROG_TYPE_TRACEPOINT),
|
2018-04-17 17:28:46 +00:00
|
|
|
BPF_PROG_SEC("raw_tracepoint/", BPF_PROG_TYPE_RAW_TRACEPOINT),
|
2017-12-13 15:18:51 +00:00
|
|
|
BPF_PROG_SEC("xdp", BPF_PROG_TYPE_XDP),
|
|
|
|
BPF_PROG_SEC("perf_event", BPF_PROG_TYPE_PERF_EVENT),
|
|
|
|
BPF_PROG_SEC("cgroup/skb", BPF_PROG_TYPE_CGROUP_SKB),
|
|
|
|
BPF_PROG_SEC("cgroup/sock", BPF_PROG_TYPE_CGROUP_SOCK),
|
|
|
|
BPF_PROG_SEC("cgroup/dev", BPF_PROG_TYPE_CGROUP_DEVICE),
|
2018-02-08 04:27:13 +00:00
|
|
|
BPF_PROG_SEC("lwt_in", BPF_PROG_TYPE_LWT_IN),
|
|
|
|
BPF_PROG_SEC("lwt_out", BPF_PROG_TYPE_LWT_OUT),
|
|
|
|
BPF_PROG_SEC("lwt_xmit", BPF_PROG_TYPE_LWT_XMIT),
|
2017-12-13 15:18:51 +00:00
|
|
|
BPF_PROG_SEC("sockops", BPF_PROG_TYPE_SOCK_OPS),
|
|
|
|
BPF_PROG_SEC("sk_skb", BPF_PROG_TYPE_SK_SKB),
|
2018-03-18 19:57:41 +00:00
|
|
|
BPF_PROG_SEC("sk_msg", BPF_PROG_TYPE_SK_MSG),
|
selftests/bpf: Selftest for sys_bind hooks
Add selftest to work with bpf_sock_addr context from
`BPF_PROG_TYPE_CGROUP_SOCK_ADDR` programs.
Try to bind(2) on IP:port and apply:
* loads to make sure context can be read correctly, including narrow
loads (byte, half) for IP and full-size loads (word) for all fields;
* stores to those fields allowed by verifier.
All combination from IPv4/IPv6 and TCP/UDP are tested.
Both scenarios are tested:
* valid programs can be loaded and attached;
* invalid programs can be neither loaded nor attached.
Test passes when expected data can be read from context in the
BPF-program, and after the call to bind(2) socket is bound to IP:port
pair that was written by BPF-program to the context.
Example:
# ./test_sock_addr
Attached bind4 program.
Test case #1 (IPv4/TCP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Test case #2 (IPv4/UDP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Attached bind6 program.
Test case #3 (IPv6/TCP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
Test case #4 (IPv6/UDP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
### SUCCESS
Signed-off-by: Andrey Ignatov <rdna@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-03-30 22:08:03 +00:00
|
|
|
BPF_SA_PROG_SEC("cgroup/bind4", BPF_CGROUP_INET4_BIND),
|
|
|
|
BPF_SA_PROG_SEC("cgroup/bind6", BPF_CGROUP_INET6_BIND),
|
selftests/bpf: Selftest for sys_connect hooks
Add selftest for BPF_CGROUP_INET4_CONNECT and BPF_CGROUP_INET6_CONNECT
attach types.
Try to connect(2) to specified IP:port and test that:
* remote IP:port pair is overridden;
* local end of connection is bound to specified IP.
All combinations of IPv4/IPv6 and TCP/UDP are tested.
Example:
# tcpdump -pn -i lo -w connect.pcap 2>/dev/null &
[1] 478
# strace -qqf -e connect -o connect.trace ./test_sock_addr.sh
Wait for testing IPv4/IPv6 to become available ... OK
Load bind4 with invalid type (can pollute stderr) ... REJECTED
Load bind4 with valid type ... OK
Attach bind4 with invalid type ... REJECTED
Attach bind4 with valid type ... OK
Load connect4 with invalid type (can pollute stderr) libbpf: load bpf \
program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
0: (b7) r2 = 23569
1: (63) *(u32 *)(r1 +24) = r2
2: (b7) r2 = 16777343
3: (63) *(u32 *)(r1 +4) = r2
invalid bpf_context access off=4 size=4
[ 1518.404609] random: crng init done
libbpf: -- END LOG --
libbpf: failed to load program 'cgroup/connect4'
libbpf: failed to load object './connect4_prog.o'
... REJECTED
Load connect4 with valid type ... OK
Attach connect4 with invalid type ... REJECTED
Attach connect4 with valid type ... OK
Test case #1 (IPv4/TCP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Requested: connect(192.168.1.254, 4040) from (*, *) ..
Actual: connect(127.0.0.1, 4444) from (127.0.0.4, 56068)
Test case #2 (IPv4/UDP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Requested: connect(192.168.1.254, 4040) from (*, *) ..
Actual: connect(127.0.0.1, 4444) from (127.0.0.4, 56447)
Load bind6 with invalid type (can pollute stderr) ... REJECTED
Load bind6 with valid type ... OK
Attach bind6 with invalid type ... REJECTED
Attach bind6 with valid type ... OK
Load connect6 with invalid type (can pollute stderr) libbpf: load bpf \
program failed: Permission denied
libbpf: -- BEGIN DUMP LOG ---
libbpf:
0: (b7) r6 = 0
1: (63) *(u32 *)(r1 +12) = r6
invalid bpf_context access off=12 size=4
libbpf: -- END LOG --
libbpf: failed to load program 'cgroup/connect6'
libbpf: failed to load object './connect6_prog.o'
... REJECTED
Load connect6 with valid type ... OK
Attach connect6 with invalid type ... REJECTED
Attach connect6 with valid type ... OK
Test case #3 (IPv6/TCP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
Requested: connect(face:b00c:1234:5678::abcd, 6060) from (*, *)
Actual: connect(::1, 6666) from (::6, 37458)
Test case #4 (IPv6/UDP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
Requested: connect(face:b00c:1234:5678::abcd, 6060) from (*, *)
Actual: connect(::1, 6666) from (::6, 39315)
### SUCCESS
# egrep 'connect\(.*AF_INET' connect.trace | \
> egrep -vw 'htons\(1025\)' | fold -b -s -w 72
502 connect(7, {sa_family=AF_INET, sin_port=htons(4040),
sin_addr=inet_addr("192.168.1.254")}, 128) = 0
502 connect(8, {sa_family=AF_INET, sin_port=htons(4040),
sin_addr=inet_addr("192.168.1.254")}, 128) = 0
502 connect(9, {sa_family=AF_INET6, sin6_port=htons(6060),
inet_pton(AF_INET6, "face:b00c:1234:5678::abcd", &sin6_addr),
sin6_flowinfo=0, sin6_scope_id=0}, 128) = 0
502 connect(10, {sa_family=AF_INET6, sin6_port=htons(6060),
inet_pton(AF_INET6, "face:b00c:1234:5678::abcd", &sin6_addr),
sin6_flowinfo=0, sin6_scope_id=0}, 128) = 0
# fg
tcpdump -pn -i lo -w connect.pcap 2> /dev/null
# tcpdump -r connect.pcap -n tcp | cut -c 1-72
reading from file connect.pcap, link-type EN10MB (Ethernet)
17:57:40.383533 IP 127.0.0.4.56068 > 127.0.0.1.4444: Flags [S], seq 1333
17:57:40.383566 IP 127.0.0.1.4444 > 127.0.0.4.56068: Flags [S.], seq 112
17:57:40.383589 IP 127.0.0.4.56068 > 127.0.0.1.4444: Flags [.], ack 1, w
17:57:40.384578 IP 127.0.0.1.4444 > 127.0.0.4.56068: Flags [R.], seq 1,
17:57:40.403327 IP6 ::6.37458 > ::1.6666: Flags [S], seq 406513443, win
17:57:40.403357 IP6 ::1.6666 > ::6.37458: Flags [S.], seq 2448389240, ac
17:57:40.403376 IP6 ::6.37458 > ::1.6666: Flags [.], ack 1, win 342, opt
17:57:40.404263 IP6 ::1.6666 > ::6.37458: Flags [R.], seq 1, ack 1, win
Signed-off-by: Andrey Ignatov <rdna@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-03-30 22:08:06 +00:00
|
|
|
BPF_SA_PROG_SEC("cgroup/connect4", BPF_CGROUP_INET4_CONNECT),
|
|
|
|
BPF_SA_PROG_SEC("cgroup/connect6", BPF_CGROUP_INET6_CONNECT),
|
2018-04-17 17:28:45 +00:00
|
|
|
BPF_S_PROG_SEC("cgroup/post_bind4", BPF_CGROUP_INET4_POST_BIND),
|
|
|
|
BPF_S_PROG_SEC("cgroup/post_bind6", BPF_CGROUP_INET6_POST_BIND),
|
2017-12-13 15:18:51 +00:00
|
|
|
};
|
2018-03-30 22:08:01 +00:00
|
|
|
|
2017-12-13 15:18:51 +00:00
|
|
|
#undef BPF_PROG_SEC
|
2018-03-30 22:08:01 +00:00
|
|
|
#undef BPF_PROG_SEC_FULL
|
2018-04-17 17:28:45 +00:00
|
|
|
#undef BPF_S_PROG_SEC
|
selftests/bpf: Selftest for sys_bind hooks
Add selftest to work with bpf_sock_addr context from
`BPF_PROG_TYPE_CGROUP_SOCK_ADDR` programs.
Try to bind(2) on IP:port and apply:
* loads to make sure context can be read correctly, including narrow
loads (byte, half) for IP and full-size loads (word) for all fields;
* stores to those fields allowed by verifier.
All combination from IPv4/IPv6 and TCP/UDP are tested.
Both scenarios are tested:
* valid programs can be loaded and attached;
* invalid programs can be neither loaded nor attached.
Test passes when expected data can be read from context in the
BPF-program, and after the call to bind(2) socket is bound to IP:port
pair that was written by BPF-program to the context.
Example:
# ./test_sock_addr
Attached bind4 program.
Test case #1 (IPv4/TCP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Test case #2 (IPv4/UDP):
Requested: bind(192.168.1.254, 4040) ..
Actual: bind(127.0.0.1, 4444)
Attached bind6 program.
Test case #3 (IPv6/TCP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
Test case #4 (IPv6/UDP):
Requested: bind(face:b00c:1234:5678::abcd, 6060) ..
Actual: bind(::1, 6666)
### SUCCESS
Signed-off-by: Andrey Ignatov <rdna@fb.com>
Signed-off-by: Alexei Starovoitov <ast@kernel.org>
Signed-off-by: Daniel Borkmann <daniel@iogearbox.net>
2018-03-30 22:08:03 +00:00
|
|
|
#undef BPF_SA_PROG_SEC
|
2017-12-13 15:18:51 +00:00
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
static int bpf_program__identify_section(struct bpf_program *prog)
|
2017-12-13 15:18:51 +00:00
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (!prog->section_name)
|
|
|
|
goto err;
|
|
|
|
|
|
|
|
for (i = 0; i < ARRAY_SIZE(section_names); i++)
|
|
|
|
if (strncmp(prog->section_name, section_names[i].sec,
|
|
|
|
section_names[i].len) == 0)
|
2018-03-30 22:08:01 +00:00
|
|
|
return i;
|
2017-12-13 15:18:51 +00:00
|
|
|
|
|
|
|
err:
|
|
|
|
pr_warning("failed to guess program type based on section name %s\n",
|
|
|
|
prog->section_name);
|
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
return -1;
|
2017-12-13 15:18:51 +00:00
|
|
|
}
|
|
|
|
|
2016-06-03 15:15:52 +00:00
|
|
|
int bpf_map__fd(struct bpf_map *map)
|
2015-11-27 08:47:35 +00:00
|
|
|
{
|
2016-06-03 15:15:52 +00:00
|
|
|
return map ? map->fd : -EINVAL;
|
2015-11-27 08:47:35 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 17:21:06 +00:00
|
|
|
const struct bpf_map_def *bpf_map__def(struct bpf_map *map)
|
2015-11-27 08:47:35 +00:00
|
|
|
{
|
2016-06-02 17:21:06 +00:00
|
|
|
return map ? &map->def : ERR_PTR(-EINVAL);
|
2015-11-27 08:47:35 +00:00
|
|
|
}
|
|
|
|
|
2016-06-02 14:02:05 +00:00
|
|
|
const char *bpf_map__name(struct bpf_map *map)
|
2015-11-27 08:47:36 +00:00
|
|
|
{
|
2016-06-02 14:02:05 +00:00
|
|
|
return map ? map->name : NULL;
|
2015-11-27 08:47:36 +00:00
|
|
|
}
|
|
|
|
|
2018-05-22 22:04:24 +00:00
|
|
|
uint32_t bpf_map__btf_key_type_id(const struct bpf_map *map)
|
2018-04-18 22:56:05 +00:00
|
|
|
{
|
2018-05-22 22:04:24 +00:00
|
|
|
return map ? map->btf_key_type_id : 0;
|
2018-04-18 22:56:05 +00:00
|
|
|
}
|
|
|
|
|
2018-05-22 22:04:24 +00:00
|
|
|
uint32_t bpf_map__btf_value_type_id(const struct bpf_map *map)
|
2018-04-18 22:56:05 +00:00
|
|
|
{
|
2018-05-22 22:04:24 +00:00
|
|
|
return map ? map->btf_value_type_id : 0;
|
2018-04-18 22:56:05 +00:00
|
|
|
}
|
|
|
|
|
2016-06-03 15:38:21 +00:00
|
|
|
int bpf_map__set_priv(struct bpf_map *map, void *priv,
|
|
|
|
bpf_map_clear_priv_t clear_priv)
|
2015-11-27 08:47:35 +00:00
|
|
|
{
|
|
|
|
if (!map)
|
|
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
if (map->priv) {
|
|
|
|
if (map->clear_priv)
|
|
|
|
map->clear_priv(map, map->priv);
|
|
|
|
}
|
|
|
|
|
|
|
|
map->priv = priv;
|
|
|
|
map->clear_priv = clear_priv;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-06-02 13:51:59 +00:00
|
|
|
void *bpf_map__priv(struct bpf_map *map)
|
2015-11-27 08:47:35 +00:00
|
|
|
{
|
2016-06-02 13:51:59 +00:00
|
|
|
return map ? map->priv : ERR_PTR(-EINVAL);
|
2015-11-27 08:47:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
struct bpf_map *
|
|
|
|
bpf_map__next(struct bpf_map *prev, struct bpf_object *obj)
|
|
|
|
{
|
|
|
|
size_t idx;
|
|
|
|
struct bpf_map *s, *e;
|
|
|
|
|
|
|
|
if (!obj || !obj->maps)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
s = obj->maps;
|
|
|
|
e = obj->maps + obj->nr_maps;
|
|
|
|
|
|
|
|
if (prev == NULL)
|
|
|
|
return s;
|
|
|
|
|
|
|
|
if ((prev < s) || (prev >= e)) {
|
|
|
|
pr_warning("error in %s: map handler doesn't belong to object\n",
|
|
|
|
__func__);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
idx = (prev - obj->maps) + 1;
|
|
|
|
if (idx >= obj->nr_maps)
|
|
|
|
return NULL;
|
|
|
|
return &obj->maps[idx];
|
|
|
|
}
|
2015-11-27 08:47:36 +00:00
|
|
|
|
|
|
|
struct bpf_map *
|
2016-06-03 15:22:51 +00:00
|
|
|
bpf_object__find_map_by_name(struct bpf_object *obj, const char *name)
|
2015-11-27 08:47:36 +00:00
|
|
|
{
|
|
|
|
struct bpf_map *pos;
|
|
|
|
|
|
|
|
bpf_map__for_each(pos, obj) {
|
2015-12-08 02:25:29 +00:00
|
|
|
if (pos->name && !strcmp(pos->name, name))
|
2015-11-27 08:47:36 +00:00
|
|
|
return pos;
|
|
|
|
}
|
|
|
|
return NULL;
|
|
|
|
}
|
2016-11-26 07:03:27 +00:00
|
|
|
|
|
|
|
struct bpf_map *
|
|
|
|
bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < obj->nr_maps; i++) {
|
|
|
|
if (obj->maps[i].offset == offset)
|
|
|
|
return &obj->maps[i];
|
|
|
|
}
|
|
|
|
return ERR_PTR(-ENOENT);
|
|
|
|
}
|
2017-01-23 01:11:25 +00:00
|
|
|
|
|
|
|
long libbpf_get_error(const void *ptr)
|
|
|
|
{
|
|
|
|
if (IS_ERR(ptr))
|
|
|
|
return PTR_ERR(ptr);
|
|
|
|
return 0;
|
|
|
|
}
|
2017-08-16 05:34:22 +00:00
|
|
|
|
|
|
|
int bpf_prog_load(const char *file, enum bpf_prog_type type,
|
|
|
|
struct bpf_object **pobj, int *prog_fd)
|
2018-03-30 22:08:01 +00:00
|
|
|
{
|
|
|
|
struct bpf_prog_load_attr attr;
|
|
|
|
|
|
|
|
memset(&attr, 0, sizeof(struct bpf_prog_load_attr));
|
|
|
|
attr.file = file;
|
|
|
|
attr.prog_type = type;
|
|
|
|
attr.expected_attach_type = 0;
|
|
|
|
|
|
|
|
return bpf_prog_load_xattr(&attr, pobj, prog_fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
|
|
|
|
struct bpf_object **pobj, int *prog_fd)
|
2017-08-16 05:34:22 +00:00
|
|
|
{
|
2017-12-15 01:55:10 +00:00
|
|
|
struct bpf_program *prog, *first_prog = NULL;
|
2018-03-30 22:08:01 +00:00
|
|
|
enum bpf_attach_type expected_attach_type;
|
|
|
|
enum bpf_prog_type prog_type;
|
2017-08-16 05:34:22 +00:00
|
|
|
struct bpf_object *obj;
|
2018-05-16 21:02:49 +00:00
|
|
|
struct bpf_map *map;
|
2018-03-30 22:08:01 +00:00
|
|
|
int section_idx;
|
2017-08-16 05:34:22 +00:00
|
|
|
int err;
|
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
if (!attr)
|
|
|
|
return -EINVAL;
|
2018-05-10 17:24:42 +00:00
|
|
|
if (!attr->file)
|
|
|
|
return -EINVAL;
|
2018-03-30 22:08:01 +00:00
|
|
|
|
2018-05-10 17:24:42 +00:00
|
|
|
obj = __bpf_object__open(attr->file, NULL, 0,
|
|
|
|
bpf_prog_type__needs_kver(attr->prog_type));
|
2017-08-16 05:34:22 +00:00
|
|
|
if (IS_ERR(obj))
|
|
|
|
return -ENOENT;
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
bpf_object__for_each_program(prog, obj) {
|
|
|
|
/*
|
|
|
|
* If type is not specified, try to guess it based on
|
|
|
|
* section name.
|
|
|
|
*/
|
2018-03-30 22:08:01 +00:00
|
|
|
prog_type = attr->prog_type;
|
2018-05-16 21:02:49 +00:00
|
|
|
prog->prog_ifindex = attr->ifindex;
|
2018-03-30 22:08:01 +00:00
|
|
|
expected_attach_type = attr->expected_attach_type;
|
|
|
|
if (prog_type == BPF_PROG_TYPE_UNSPEC) {
|
|
|
|
section_idx = bpf_program__identify_section(prog);
|
|
|
|
if (section_idx < 0) {
|
2017-12-15 01:55:10 +00:00
|
|
|
bpf_object__close(obj);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
2018-03-30 22:08:01 +00:00
|
|
|
prog_type = section_names[section_idx].prog_type;
|
|
|
|
expected_attach_type =
|
|
|
|
section_names[section_idx].expected_attach_type;
|
2017-12-13 15:18:51 +00:00
|
|
|
}
|
2017-12-15 01:55:10 +00:00
|
|
|
|
2018-03-30 22:08:01 +00:00
|
|
|
bpf_program__set_type(prog, prog_type);
|
|
|
|
bpf_program__set_expected_attach_type(prog,
|
|
|
|
expected_attach_type);
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
if (prog->idx != obj->efile.text_shndx && !first_prog)
|
|
|
|
first_prog = prog;
|
|
|
|
}
|
|
|
|
|
2018-05-16 21:02:49 +00:00
|
|
|
bpf_map__for_each(map, obj) {
|
|
|
|
map->map_ifindex = attr->ifindex;
|
|
|
|
}
|
|
|
|
|
2017-12-15 01:55:10 +00:00
|
|
|
if (!first_prog) {
|
|
|
|
pr_warning("object file doesn't contain bpf program\n");
|
|
|
|
bpf_object__close(obj);
|
|
|
|
return -ENOENT;
|
2017-12-13 15:18:51 +00:00
|
|
|
}
|
|
|
|
|
2017-08-16 05:34:22 +00:00
|
|
|
err = bpf_object__load(obj);
|
|
|
|
if (err) {
|
|
|
|
bpf_object__close(obj);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
*pobj = obj;
|
2017-12-15 01:55:10 +00:00
|
|
|
*prog_fd = bpf_program__fd(first_prog);
|
2017-08-16 05:34:22 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2018-05-10 17:24:40 +00:00
|
|
|
|
|
|
|
enum bpf_perf_event_ret
|
|
|
|
bpf_perf_event_read_simple(void *mem, unsigned long size,
|
|
|
|
unsigned long page_size, void **buf, size_t *buf_len,
|
|
|
|
bpf_perf_event_print_t fn, void *priv)
|
|
|
|
{
|
|
|
|
volatile struct perf_event_mmap_page *header = mem;
|
|
|
|
__u64 data_tail = header->data_tail;
|
|
|
|
__u64 data_head = header->data_head;
|
|
|
|
void *base, *begin, *end;
|
|
|
|
int ret;
|
|
|
|
|
|
|
|
asm volatile("" ::: "memory"); /* in real code it should be smp_rmb() */
|
|
|
|
if (data_head == data_tail)
|
|
|
|
return LIBBPF_PERF_EVENT_CONT;
|
|
|
|
|
|
|
|
base = ((char *)header) + page_size;
|
|
|
|
|
|
|
|
begin = base + data_tail % size;
|
|
|
|
end = base + data_head % size;
|
|
|
|
|
|
|
|
while (begin != end) {
|
|
|
|
struct perf_event_header *ehdr;
|
|
|
|
|
|
|
|
ehdr = begin;
|
|
|
|
if (begin + ehdr->size > base + size) {
|
|
|
|
long len = base + size - begin;
|
|
|
|
|
|
|
|
if (*buf_len < ehdr->size) {
|
|
|
|
free(*buf);
|
|
|
|
*buf = malloc(ehdr->size);
|
|
|
|
if (!*buf) {
|
|
|
|
ret = LIBBPF_PERF_EVENT_ERROR;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
*buf_len = ehdr->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
memcpy(*buf, begin, len);
|
|
|
|
memcpy(*buf + len, base, ehdr->size - len);
|
|
|
|
ehdr = (void *)*buf;
|
|
|
|
begin = base + ehdr->size - len;
|
|
|
|
} else if (begin + ehdr->size == base + size) {
|
|
|
|
begin = base;
|
|
|
|
} else {
|
|
|
|
begin += ehdr->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
ret = fn(ehdr, priv);
|
|
|
|
if (ret != LIBBPF_PERF_EVENT_CONT)
|
|
|
|
break;
|
|
|
|
|
|
|
|
data_tail += ehdr->size;
|
|
|
|
}
|
|
|
|
|
|
|
|
__sync_synchronize(); /* smp_mb() */
|
|
|
|
header->data_tail = data_tail;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|