Merge 'v0.24.0' into tizen 76/271276/1
authorAdrian Szyndela <adrian.s@samsung.com>
Thu, 17 Feb 2022 12:08:00 +0000 (13:08 +0100)
committerAdrian Szyndela <adrian.s@samsung.com>
Thu, 17 Feb 2022 15:07:47 +0000 (16:07 +0100)
Change-Id: I7826c3d82ddea6b3918020acd53c85f8995335be

21 files changed:
1  2 
packaging/bcc-tools.spec
src/cc/CMakeLists.txt
src/cc/export/helpers.h
src/cc/frontends/clang/b_frontend_action.cc
src/cc/frontends/clang/loader.cc
src/cc/libbpf.c
src/cc/libbpf/BPF-CHECKPOINT-COMMIT
src/cc/libbpf/CHECKPOINT-COMMIT
src/cc/libbpf/docs/api.rst
src/cc/libbpf/src/bpf.c
src/cc/libbpf/src/bpf.h
src/cc/libbpf/src/bpf_tracing.h
src/cc/libbpf/src/btf.h
src/cc/libbpf/src/libbpf.c
src/cc/libbpf/src/libbpf.h
src/cc/libbpf/src/libbpf_internal.h
src/cc/libbpf/src/libbpf_probes.c
src/cc/libbpf/travis-ci/rootfs/mkrootfs_tweak.sh
src/cc/libbpf/travis-ci/vmtest/configs/config-latest.s390x
src/cc/libbpf/travis-ci/vmtest/configs/config-latest.x86_64
src/python/bcc/libbcc.py

index c8fd7b4,0000000..11ae0c4
mode 100644,000000..100644
--- /dev/null
@@@ -1,85 -1,0 +1,80 @@@
- BuildRequires: cmake python
 +Name: bcc-tools
 +Version: 0.21.0
 +Release: 0
 +Summary: Tools for BPF-based Linux IO analysis, networking, monitoring
 +License: Apache-2.0
 +Vendor: None (open source)
 +Group: Development/Compilers
 +URL: https://github.com/iovisor/bcc
 +Source0: %{name}-%{version}.tar.gz
 +ExcludeArch: aarch64
 +Requires: /sbin/ldconfig
 +BuildRequires: gcc >= 3.4
- # workaround for an issue with dependencies for python(abi) = 2.7, provided
- # by multiple packages: python and python3-base
- %filter_from_requires /^python(abi)/d
- %filter_setup
++BuildRequires: cmake python3
 +BuildRequires: bison flex llvm-devel clang-devel libelf-devel
 +BuildRequires: llvm-static-devel
 +BuildRequires: pkgconfig(zlib)
 +
- %{_prefix}/lib/python2.7/site-packages/bcc*
 +%description
 +BCC is a toolkit for creating efficient kernel tracing and manipulation
 +programs, and includes several useful tools and examples. It makes use
 +of extended BPF (Berkeley Packet Filters), formally known as eBPF, a new
 +feature that was first added to Linux 3.15. Much of what BCC uses
 +requires Linux 4.1 and above.
 +
 +%package devel
 +Summary: Developer package for BCC
 +Requires: %{name} = %{version}-%{release}
 +
 +%description devel
 +bcc tools - devel
 +This package provides development libraries.
 +
 +%package dbus
 +Summary: Tools for monitoring dbus
 +
 +%description dbus
 +bcc tools - dbus
 +This package provides tools for tracking dbus.
 +
 +%package examples
 +Summary: Example codes for BCC
 +License: Apache-2.0
 +
 +%description examples
 +This package provides example codes and documents.
 +
 +%prep
 +%setup -q -n bcc-tools-%{version}
 +
 +%build
 +#DREVISION - number which will be put at back of the package name if repo isn't available
 +cmake -DCMAKE_INSTALL_PREFIX=/usr -DINSTALL_CPP_EXAMPLES=ON -DREVISION="%{version}" .
 +make %{?jobs:-j%jobs}
 +
 +%install
 +%make_install
 +
 +%clean
 +rm -rf %{buildroot}
 +
 +%post -p /sbin/ldconfig
 +
 +%postun -p /sbin/ldconfig
 +
 +%files
 +%{_libdir}/libbcc_bpf.so*
 +%{_libdir}/libbcc.so.*
++%{_prefix}/lib/python*/site-packages/bcc*
 +%{_includedir}/bcc/hostcompat/*
 +
 +%files devel
 +%{_includedir}/*
 +%{_libdir}/libbcc.so
 +%{_libdir}/pkgconfig/libbcc.pc
 +
 +%files dbus
 +%{_datadir}/bcc/tools/dbus-*
 +
 +%files examples
 +%{_datadir}/bcc/*
Simple merge
Simple merge
Simple merge
diff --cc src/cc/libbpf.c
Simple merge
index 417cd69,0000000..4854ec6
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- 819d11507f6637731947836e6308f5966d64cf9d
++d6d86830705f173fca6087a3e67ceaf68db80523
index b364dc6,0000000..3b9b714
mode 100644,000000..100644
--- /dev/null
@@@ -1,1 -1,0 +1,1 @@@
- e967a20a8fabc6442a78e2e2059e63a4bb6aed08
++44bab87d8ca6f0544a9f8fc97bdf33aa5b3c899e
index 4ad9f34,0000000..7a8e709
mode 100644,000000..100644
--- /dev/null
@@@ -1,51 -1,0 +1,93 @@@
- ==================
 +.. SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 +
 +.. _api:
 +
 +.. toctree:: Table of Contents
 +
 +
 +LIBBPF API
++==========
++
++Error Handling
++--------------
++
++When libbpf is used in "libbpf 1.0 mode", API functions can return errors in one of two ways.
++
++You can set "libbpf 1.0" mode with the following line:
++
++.. code-block::
++
++    libbpf_set_strict_mode(LIBBPF_STRICT_DIRECT_ERRS | LIBBPF_STRICT_CLEAN_PTRS);
++
++If the function returns an error code directly, it uses 0 to indicate success
++and a negative error code to indicate what caused the error. In this case the
++error code should be checked directly from the return, you do not need to check
++errno.
++
++For example:
++
++.. code-block::
++
++    err = some_libbpf_api_with_error_return(...);
++    if (err < 0) {
++        /* Handle error accordingly */
++    }
++
++If the function returns a pointer, it will return NULL to indicate there was
++an error. In this case errno should be checked for the error code.
++
++For example:
++
++.. code-block::
++
++    ptr = some_libbpf_api_returning_ptr();
++    if (!ptr) {
++        /* note no minus sign for EINVAL and E2BIG below */
++        if (errno == EINVAL) {
++           /* handle EINVAL error */
++        } else if (errno == E2BIG) {
++           /* handle E2BIG error */
++        }
++    }
 +
 +libbpf.h
 +--------
 +.. doxygenfile:: libbpf.h
 +   :project: libbpf
 +   :sections: func define public-type enum
 +
 +bpf.h
 +-----
 +.. doxygenfile:: bpf.h
 +   :project: libbpf
 +   :sections: func define public-type enum
 +
 +btf.h
 +-----
 +.. doxygenfile:: btf.h
 +   :project: libbpf
 +   :sections: func define public-type enum
 +
 +xsk.h
 +-----
 +.. doxygenfile:: xsk.h
 +   :project: libbpf
 +   :sections: func define public-type enum
 +
 +bpf_tracing.h
 +-------------
 +.. doxygenfile:: bpf_tracing.h
 +   :project: libbpf
 +   :sections: func define public-type enum
 +
 +bpf_core_read.h
 +---------------
 +.. doxygenfile:: bpf_core_read.h
 +   :project: libbpf
 +   :sections: func define public-type enum
 +
 +bpf_endian.h
 +------------
 +.. doxygenfile:: bpf_endian.h
 +   :project: libbpf
 +   :sections: func define public-type enum
index 1d367db,0000000..5df7d22
mode 100644,000000..100644
--- /dev/null
@@@ -1,1254 -1,0 +1,1254 @@@
- int bpf_map_delete_batch(int fd, void *keys, __u32 *count,
 +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 +
 +/*
 + * common eBPF ELF operations.
 + *
 + * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
 + * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
 + * Copyright (C) 2015 Huawei Inc.
 + *
 + * 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>
 + */
 +
 +#include <stdlib.h>
 +#include <string.h>
 +#include <memory.h>
 +#include <unistd.h>
 +#include <asm/unistd.h>
 +#include <errno.h>
 +#include <linux/bpf.h>
 +#include <linux/filter.h>
 +#include <limits.h>
 +#include <sys/resource.h>
 +#include "bpf.h"
 +#include "libbpf.h"
 +#include "libbpf_internal.h"
 +
 +/*
 + * When building perf, unistd.h is overridden. __NR_bpf is
 + * required to be defined explicitly.
 + */
 +#ifndef __NR_bpf
 +# if defined(__i386__)
 +#  define __NR_bpf 357
 +# elif defined(__x86_64__)
 +#  define __NR_bpf 321
 +# elif defined(__aarch64__)
 +#  define __NR_bpf 280
 +# elif defined(__sparc__)
 +#  define __NR_bpf 349
 +# elif defined(__s390__)
 +#  define __NR_bpf 351
 +# elif defined(__arc__)
 +#  define __NR_bpf 280
 +# elif defined(__mips__) && defined(_ABIO32)
 +#  define __NR_bpf 4355
 +# elif defined(__mips__) && defined(_ABIN32)
 +#  define __NR_bpf 6319
 +# elif defined(__mips__) && defined(_ABI64)
 +#  define __NR_bpf 5315
 +# elif defined(__aarch64__)
 +#  define __NR_bpf 280
 +# elif defined(__arm__)
 +#  define __NR_bpf 386
 +# else
 +#  error __NR_bpf not defined. libbpf does not support your arch.
 +# endif
 +#endif
 +
 +static inline __u64 ptr_to_u64(const void *ptr)
 +{
 +      return (__u64) (unsigned long) ptr;
 +}
 +
 +static inline int sys_bpf(enum bpf_cmd cmd, union bpf_attr *attr,
 +                        unsigned int size)
 +{
 +      return syscall(__NR_bpf, cmd, attr, size);
 +}
 +
 +static inline int sys_bpf_fd(enum bpf_cmd cmd, union bpf_attr *attr,
 +                           unsigned int size)
 +{
 +      int fd;
 +
 +      fd = sys_bpf(cmd, attr, size);
 +      return ensure_good_fd(fd);
 +}
 +
 +#define PROG_LOAD_ATTEMPTS 5
 +
 +static inline int sys_bpf_prog_load(union bpf_attr *attr, unsigned int size, int attempts)
 +{
 +      int fd;
 +
 +      do {
 +              fd = sys_bpf_fd(BPF_PROG_LOAD, attr, size);
 +      } while (fd < 0 && errno == EAGAIN && --attempts > 0);
 +
 +      return fd;
 +}
 +
 +/* Probe whether kernel switched from memlock-based (RLIMIT_MEMLOCK) to
 + * memcg-based memory accounting for BPF maps and progs. This was done in [0].
 + * We use the support for bpf_ktime_get_coarse_ns() helper, which was added in
 + * the same 5.11 Linux release ([1]), to detect memcg-based accounting for BPF.
 + *
 + *   [0] https://lore.kernel.org/bpf/20201201215900.3569844-1-guro@fb.com/
 + *   [1] d05512618056 ("bpf: Add bpf_ktime_get_coarse_ns helper")
 + */
 +int probe_memcg_account(void)
 +{
 +      const size_t prog_load_attr_sz = offsetofend(union bpf_attr, attach_btf_obj_fd);
 +      struct bpf_insn insns[] = {
 +              BPF_EMIT_CALL(BPF_FUNC_ktime_get_coarse_ns),
 +              BPF_EXIT_INSN(),
 +      };
 +      size_t insn_cnt = sizeof(insns) / sizeof(insns[0]);
 +      union bpf_attr attr;
 +      int prog_fd;
 +
 +      /* attempt loading freplace trying to use custom BTF */
 +      memset(&attr, 0, prog_load_attr_sz);
 +      attr.prog_type = BPF_PROG_TYPE_SOCKET_FILTER;
 +      attr.insns = ptr_to_u64(insns);
 +      attr.insn_cnt = insn_cnt;
 +      attr.license = ptr_to_u64("GPL");
 +
 +      prog_fd = sys_bpf_fd(BPF_PROG_LOAD, &attr, prog_load_attr_sz);
 +      if (prog_fd >= 0) {
 +              close(prog_fd);
 +              return 1;
 +      }
 +      return 0;
 +}
 +
 +static bool memlock_bumped;
 +static rlim_t memlock_rlim = RLIM_INFINITY;
 +
 +int libbpf_set_memlock_rlim(size_t memlock_bytes)
 +{
 +      if (memlock_bumped)
 +              return libbpf_err(-EBUSY);
 +
 +      memlock_rlim = memlock_bytes;
 +      return 0;
 +}
 +
 +int bump_rlimit_memlock(void)
 +{
 +      struct rlimit rlim;
 +
 +      /* this the default in libbpf 1.0, but for now user has to opt-in explicitly */
 +      if (!(libbpf_mode & LIBBPF_STRICT_AUTO_RLIMIT_MEMLOCK))
 +              return 0;
 +
 +      /* if kernel supports memcg-based accounting, skip bumping RLIMIT_MEMLOCK */
 +      if (memlock_bumped || kernel_supports(NULL, FEAT_MEMCG_ACCOUNT))
 +              return 0;
 +
 +      memlock_bumped = true;
 +
 +      /* zero memlock_rlim_max disables auto-bumping RLIMIT_MEMLOCK */
 +      if (memlock_rlim == 0)
 +              return 0;
 +
 +      rlim.rlim_cur = rlim.rlim_max = memlock_rlim;
 +      if (setrlimit(RLIMIT_MEMLOCK, &rlim))
 +              return -errno;
 +
 +      return 0;
 +}
 +
 +int bpf_map_create(enum bpf_map_type map_type,
 +                 const char *map_name,
 +                 __u32 key_size,
 +                 __u32 value_size,
 +                 __u32 max_entries,
 +                 const struct bpf_map_create_opts *opts)
 +{
 +      const size_t attr_sz = offsetofend(union bpf_attr, map_extra);
 +      union bpf_attr attr;
 +      int fd;
 +
 +      bump_rlimit_memlock();
 +
 +      memset(&attr, 0, attr_sz);
 +
 +      if (!OPTS_VALID(opts, bpf_map_create_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      attr.map_type = map_type;
 +      if (map_name)
 +              libbpf_strlcpy(attr.map_name, map_name, sizeof(attr.map_name));
 +      attr.key_size = key_size;
 +      attr.value_size = value_size;
 +      attr.max_entries = max_entries;
 +
 +      attr.btf_fd = OPTS_GET(opts, btf_fd, 0);
 +      attr.btf_key_type_id = OPTS_GET(opts, btf_key_type_id, 0);
 +      attr.btf_value_type_id = OPTS_GET(opts, btf_value_type_id, 0);
 +      attr.btf_vmlinux_value_type_id = OPTS_GET(opts, btf_vmlinux_value_type_id, 0);
 +
 +      attr.inner_map_fd = OPTS_GET(opts, inner_map_fd, 0);
 +      attr.map_flags = OPTS_GET(opts, map_flags, 0);
 +      attr.map_extra = OPTS_GET(opts, map_extra, 0);
 +      attr.numa_node = OPTS_GET(opts, numa_node, 0);
 +      attr.map_ifindex = OPTS_GET(opts, map_ifindex, 0);
 +
 +      fd = sys_bpf_fd(BPF_MAP_CREATE, &attr, attr_sz);
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, p);
 +
 +      p.map_flags = create_attr->map_flags;
 +      p.numa_node = create_attr->numa_node;
 +      p.btf_fd = create_attr->btf_fd;
 +      p.btf_key_type_id = create_attr->btf_key_type_id;
 +      p.btf_value_type_id = create_attr->btf_value_type_id;
 +      p.map_ifindex = create_attr->map_ifindex;
 +      if (create_attr->map_type == BPF_MAP_TYPE_STRUCT_OPS)
 +              p.btf_vmlinux_value_type_id = create_attr->btf_vmlinux_value_type_id;
 +      else
 +              p.inner_map_fd = create_attr->inner_map_fd;
 +
 +      return bpf_map_create(create_attr->map_type, create_attr->name,
 +                            create_attr->key_size, create_attr->value_size,
 +                            create_attr->max_entries, &p);
 +}
 +
 +int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
 +                      int key_size, int value_size, int max_entries,
 +                      __u32 map_flags, int node)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts);
 +
 +      opts.map_flags = map_flags;
 +      if (node >= 0) {
 +              opts.numa_node = node;
 +              opts.map_flags |= BPF_F_NUMA_NODE;
 +      }
 +
 +      return bpf_map_create(map_type, name, key_size, value_size, max_entries, &opts);
 +}
 +
 +int bpf_create_map(enum bpf_map_type map_type, int key_size,
 +                 int value_size, int max_entries, __u32 map_flags)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = map_flags);
 +
 +      return bpf_map_create(map_type, NULL, key_size, value_size, max_entries, &opts);
 +}
 +
 +int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
 +                      int key_size, int value_size, int max_entries,
 +                      __u32 map_flags)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = map_flags);
 +
 +      return bpf_map_create(map_type, name, key_size, value_size, max_entries, &opts);
 +}
 +
 +int bpf_create_map_in_map_node(enum bpf_map_type map_type, const char *name,
 +                             int key_size, int inner_map_fd, int max_entries,
 +                             __u32 map_flags, int node)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts);
 +
 +      opts.inner_map_fd = inner_map_fd;
 +      opts.map_flags = map_flags;
 +      if (node >= 0) {
 +              opts.map_flags |= BPF_F_NUMA_NODE;
 +              opts.numa_node = node;
 +      }
 +
 +      return bpf_map_create(map_type, name, key_size, 4, max_entries, &opts);
 +}
 +
 +int bpf_create_map_in_map(enum bpf_map_type map_type, const char *name,
 +                        int key_size, int inner_map_fd, int max_entries,
 +                        __u32 map_flags)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts,
 +              .inner_map_fd = inner_map_fd,
 +              .map_flags = map_flags,
 +      );
 +
 +      return bpf_map_create(map_type, name, key_size, 4, max_entries, &opts);
 +}
 +
 +static void *
 +alloc_zero_tailing_info(const void *orecord, __u32 cnt,
 +                      __u32 actual_rec_size, __u32 expected_rec_size)
 +{
 +      __u64 info_len = (__u64)actual_rec_size * cnt;
 +      void *info, *nrecord;
 +      int i;
 +
 +      info = malloc(info_len);
 +      if (!info)
 +              return NULL;
 +
 +      /* zero out bytes kernel does not understand */
 +      nrecord = info;
 +      for (i = 0; i < cnt; i++) {
 +              memcpy(nrecord, orecord, expected_rec_size);
 +              memset(nrecord + expected_rec_size, 0,
 +                     actual_rec_size - expected_rec_size);
 +              orecord += actual_rec_size;
 +              nrecord += actual_rec_size;
 +      }
 +
 +      return info;
 +}
 +
 +DEFAULT_VERSION(bpf_prog_load_v0_6_0, bpf_prog_load, LIBBPF_0.6.0)
 +int bpf_prog_load_v0_6_0(enum bpf_prog_type prog_type,
 +                       const char *prog_name, const char *license,
 +                       const struct bpf_insn *insns, size_t insn_cnt,
 +                       const struct bpf_prog_load_opts *opts)
 +{
 +      void *finfo = NULL, *linfo = NULL;
 +      const char *func_info, *line_info;
 +      __u32 log_size, log_level, attach_prog_fd, attach_btf_obj_fd;
 +      __u32 func_info_rec_size, line_info_rec_size;
 +      int fd, attempts;
 +      union bpf_attr attr;
 +      char *log_buf;
 +
 +      bump_rlimit_memlock();
 +
 +      if (!OPTS_VALID(opts, bpf_prog_load_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      attempts = OPTS_GET(opts, attempts, 0);
 +      if (attempts < 0)
 +              return libbpf_err(-EINVAL);
 +      if (attempts == 0)
 +              attempts = PROG_LOAD_ATTEMPTS;
 +
 +      memset(&attr, 0, sizeof(attr));
 +
 +      attr.prog_type = prog_type;
 +      attr.expected_attach_type = OPTS_GET(opts, expected_attach_type, 0);
 +
 +      attr.prog_btf_fd = OPTS_GET(opts, prog_btf_fd, 0);
 +      attr.prog_flags = OPTS_GET(opts, prog_flags, 0);
 +      attr.prog_ifindex = OPTS_GET(opts, prog_ifindex, 0);
 +      attr.kern_version = OPTS_GET(opts, kern_version, 0);
 +
 +      if (prog_name)
 +              libbpf_strlcpy(attr.prog_name, prog_name, sizeof(attr.prog_name));
 +      attr.license = ptr_to_u64(license);
 +
 +      if (insn_cnt > UINT_MAX)
 +              return libbpf_err(-E2BIG);
 +
 +      attr.insns = ptr_to_u64(insns);
 +      attr.insn_cnt = (__u32)insn_cnt;
 +
 +      attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
 +      attach_btf_obj_fd = OPTS_GET(opts, attach_btf_obj_fd, 0);
 +
 +      if (attach_prog_fd && attach_btf_obj_fd)
 +              return libbpf_err(-EINVAL);
 +
 +      attr.attach_btf_id = OPTS_GET(opts, attach_btf_id, 0);
 +      if (attach_prog_fd)
 +              attr.attach_prog_fd = attach_prog_fd;
 +      else
 +              attr.attach_btf_obj_fd = attach_btf_obj_fd;
 +
 +      log_buf = OPTS_GET(opts, log_buf, NULL);
 +      log_size = OPTS_GET(opts, log_size, 0);
 +      log_level = OPTS_GET(opts, log_level, 0);
 +
 +      if (!!log_buf != !!log_size)
 +              return libbpf_err(-EINVAL);
 +      if (log_level > (4 | 2 | 1))
 +              return libbpf_err(-EINVAL);
 +      if (log_level && !log_buf)
 +              return libbpf_err(-EINVAL);
 +
 +      func_info_rec_size = OPTS_GET(opts, func_info_rec_size, 0);
 +      func_info = OPTS_GET(opts, func_info, NULL);
 +      attr.func_info_rec_size = func_info_rec_size;
 +      attr.func_info = ptr_to_u64(func_info);
 +      attr.func_info_cnt = OPTS_GET(opts, func_info_cnt, 0);
 +
 +      line_info_rec_size = OPTS_GET(opts, line_info_rec_size, 0);
 +      line_info = OPTS_GET(opts, line_info, NULL);
 +      attr.line_info_rec_size = line_info_rec_size;
 +      attr.line_info = ptr_to_u64(line_info);
 +      attr.line_info_cnt = OPTS_GET(opts, line_info_cnt, 0);
 +
 +      attr.fd_array = ptr_to_u64(OPTS_GET(opts, fd_array, NULL));
 +
 +      if (log_level) {
 +              attr.log_buf = ptr_to_u64(log_buf);
 +              attr.log_size = log_size;
 +              attr.log_level = log_level;
 +      }
 +
 +      fd = sys_bpf_prog_load(&attr, sizeof(attr), attempts);
 +      if (fd >= 0)
 +              return fd;
 +
 +      /* After bpf_prog_load, the kernel may modify certain attributes
 +       * to give user space a hint how to deal with loading failure.
 +       * Check to see whether we can make some changes and load again.
 +       */
 +      while (errno == E2BIG && (!finfo || !linfo)) {
 +              if (!finfo && attr.func_info_cnt &&
 +                  attr.func_info_rec_size < func_info_rec_size) {
 +                      /* try with corrected func info records */
 +                      finfo = alloc_zero_tailing_info(func_info,
 +                                                      attr.func_info_cnt,
 +                                                      func_info_rec_size,
 +                                                      attr.func_info_rec_size);
 +                      if (!finfo) {
 +                              errno = E2BIG;
 +                              goto done;
 +                      }
 +
 +                      attr.func_info = ptr_to_u64(finfo);
 +                      attr.func_info_rec_size = func_info_rec_size;
 +              } else if (!linfo && attr.line_info_cnt &&
 +                         attr.line_info_rec_size < line_info_rec_size) {
 +                      linfo = alloc_zero_tailing_info(line_info,
 +                                                      attr.line_info_cnt,
 +                                                      line_info_rec_size,
 +                                                      attr.line_info_rec_size);
 +                      if (!linfo) {
 +                              errno = E2BIG;
 +                              goto done;
 +                      }
 +
 +                      attr.line_info = ptr_to_u64(linfo);
 +                      attr.line_info_rec_size = line_info_rec_size;
 +              } else {
 +                      break;
 +              }
 +
 +              fd = sys_bpf_prog_load(&attr, sizeof(attr), attempts);
 +              if (fd >= 0)
 +                      goto done;
 +      }
 +
 +      if (log_level == 0 && log_buf) {
 +              /* log_level == 0 with non-NULL log_buf requires retrying on error
 +               * with log_level == 1 and log_buf/log_buf_size set, to get details of
 +               * failure
 +               */
 +              attr.log_buf = ptr_to_u64(log_buf);
 +              attr.log_size = log_size;
 +              attr.log_level = 1;
 +
 +              fd = sys_bpf_prog_load(&attr, sizeof(attr), attempts);
 +      }
 +done:
 +      /* free() doesn't affect errno, so we don't need to restore it */
 +      free(finfo);
 +      free(linfo);
 +      return libbpf_err_errno(fd);
 +}
 +
 +__attribute__((alias("bpf_load_program_xattr2")))
 +int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
 +                         char *log_buf, size_t log_buf_sz);
 +
 +static int bpf_load_program_xattr2(const struct bpf_load_program_attr *load_attr,
 +                                 char *log_buf, size_t log_buf_sz)
 +{
 +      LIBBPF_OPTS(bpf_prog_load_opts, p);
 +
 +      if (!load_attr || !log_buf != !log_buf_sz)
 +              return libbpf_err(-EINVAL);
 +
 +      p.expected_attach_type = load_attr->expected_attach_type;
 +      switch (load_attr->prog_type) {
 +      case BPF_PROG_TYPE_STRUCT_OPS:
 +      case BPF_PROG_TYPE_LSM:
 +              p.attach_btf_id = load_attr->attach_btf_id;
 +              break;
 +      case BPF_PROG_TYPE_TRACING:
 +      case BPF_PROG_TYPE_EXT:
 +              p.attach_btf_id = load_attr->attach_btf_id;
 +              p.attach_prog_fd = load_attr->attach_prog_fd;
 +              break;
 +      default:
 +              p.prog_ifindex = load_attr->prog_ifindex;
 +              p.kern_version = load_attr->kern_version;
 +      }
 +      p.log_level = load_attr->log_level;
 +      p.log_buf = log_buf;
 +      p.log_size = log_buf_sz;
 +      p.prog_btf_fd = load_attr->prog_btf_fd;
 +      p.func_info_rec_size = load_attr->func_info_rec_size;
 +      p.func_info_cnt = load_attr->func_info_cnt;
 +      p.func_info = load_attr->func_info;
 +      p.line_info_rec_size = load_attr->line_info_rec_size;
 +      p.line_info_cnt = load_attr->line_info_cnt;
 +      p.line_info = load_attr->line_info;
 +      p.prog_flags = load_attr->prog_flags;
 +
 +      return bpf_prog_load(load_attr->prog_type, load_attr->name, load_attr->license,
 +                           load_attr->insns, load_attr->insns_cnt, &p);
 +}
 +
 +int bpf_load_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 +                   size_t insns_cnt, const char *license,
 +                   __u32 kern_version, char *log_buf,
 +                   size_t log_buf_sz)
 +{
 +      struct bpf_load_program_attr load_attr;
 +
 +      memset(&load_attr, 0, sizeof(struct bpf_load_program_attr));
 +      load_attr.prog_type = type;
 +      load_attr.expected_attach_type = 0;
 +      load_attr.name = NULL;
 +      load_attr.insns = insns;
 +      load_attr.insns_cnt = insns_cnt;
 +      load_attr.license = license;
 +      load_attr.kern_version = kern_version;
 +
 +      return bpf_load_program_xattr2(&load_attr, log_buf, log_buf_sz);
 +}
 +
 +int bpf_verify_program(enum bpf_prog_type type, const struct bpf_insn *insns,
 +                     size_t insns_cnt, __u32 prog_flags, const char *license,
 +                     __u32 kern_version, char *log_buf, size_t log_buf_sz,
 +                     int log_level)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      bump_rlimit_memlock();
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.prog_type = type;
 +      attr.insn_cnt = (__u32)insns_cnt;
 +      attr.insns = ptr_to_u64(insns);
 +      attr.license = ptr_to_u64(license);
 +      attr.log_buf = ptr_to_u64(log_buf);
 +      attr.log_size = log_buf_sz;
 +      attr.log_level = log_level;
 +      log_buf[0] = 0;
 +      attr.kern_version = kern_version;
 +      attr.prog_flags = prog_flags;
 +
 +      fd = sys_bpf_prog_load(&attr, sizeof(attr), PROG_LOAD_ATTEMPTS);
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_map_update_elem(int fd, const void *key, const void *value,
 +                      __u64 flags)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +      attr.value = ptr_to_u64(value);
 +      attr.flags = flags;
 +
 +      ret = sys_bpf(BPF_MAP_UPDATE_ELEM, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_lookup_elem(int fd, const void *key, void *value)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +      attr.value = ptr_to_u64(value);
 +
 +      ret = sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_lookup_elem_flags(int fd, const void *key, void *value, __u64 flags)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +      attr.value = ptr_to_u64(value);
 +      attr.flags = flags;
 +
 +      ret = sys_bpf(BPF_MAP_LOOKUP_ELEM, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_lookup_and_delete_elem(int fd, const void *key, void *value)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +      attr.value = ptr_to_u64(value);
 +
 +      ret = sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key, void *value, __u64 flags)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +      attr.value = ptr_to_u64(value);
 +      attr.flags = flags;
 +
 +      ret = sys_bpf(BPF_MAP_LOOKUP_AND_DELETE_ELEM, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_delete_elem(int fd, const void *key)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +
 +      ret = sys_bpf(BPF_MAP_DELETE_ELEM, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_get_next_key(int fd, const void *key, void *next_key)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +      attr.key = ptr_to_u64(key);
 +      attr.next_key = ptr_to_u64(next_key);
 +
 +      ret = sys_bpf(BPF_MAP_GET_NEXT_KEY, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_map_freeze(int fd)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_fd = fd;
 +
 +      ret = sys_bpf(BPF_MAP_FREEZE, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +static int bpf_map_batch_common(int cmd, int fd, void  *in_batch,
 +                              void *out_batch, void *keys, void *values,
 +                              __u32 *count,
 +                              const struct bpf_map_batch_opts *opts)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      if (!OPTS_VALID(opts, bpf_map_batch_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.batch.map_fd = fd;
 +      attr.batch.in_batch = ptr_to_u64(in_batch);
 +      attr.batch.out_batch = ptr_to_u64(out_batch);
 +      attr.batch.keys = ptr_to_u64(keys);
 +      attr.batch.values = ptr_to_u64(values);
 +      attr.batch.count = *count;
 +      attr.batch.elem_flags  = OPTS_GET(opts, elem_flags, 0);
 +      attr.batch.flags = OPTS_GET(opts, flags, 0);
 +
 +      ret = sys_bpf(cmd, &attr, sizeof(attr));
 +      *count = attr.batch.count;
 +
 +      return libbpf_err_errno(ret);
 +}
 +
-                                   NULL, keys, NULL, count, opts);
++int bpf_map_delete_batch(int fd, const void *keys, __u32 *count,
 +                       const struct bpf_map_batch_opts *opts)
 +{
 +      return bpf_map_batch_common(BPF_MAP_DELETE_BATCH, fd, NULL,
- int bpf_map_update_batch(int fd, void *keys, void *values, __u32 *count,
++                                  NULL, (void *)keys, NULL, count, opts);
 +}
 +
 +int bpf_map_lookup_batch(int fd, void *in_batch, void *out_batch, void *keys,
 +                       void *values, __u32 *count,
 +                       const struct bpf_map_batch_opts *opts)
 +{
 +      return bpf_map_batch_common(BPF_MAP_LOOKUP_BATCH, fd, in_batch,
 +                                  out_batch, keys, values, count, opts);
 +}
 +
 +int bpf_map_lookup_and_delete_batch(int fd, void *in_batch, void *out_batch,
 +                                  void *keys, void *values, __u32 *count,
 +                                  const struct bpf_map_batch_opts *opts)
 +{
 +      return bpf_map_batch_common(BPF_MAP_LOOKUP_AND_DELETE_BATCH,
 +                                  fd, in_batch, out_batch, keys, values,
 +                                  count, opts);
 +}
 +
-                                   keys, values, count, opts);
++int bpf_map_update_batch(int fd, const void *keys, const void *values, __u32 *count,
 +                       const struct bpf_map_batch_opts *opts)
 +{
 +      return bpf_map_batch_common(BPF_MAP_UPDATE_BATCH, fd, NULL, NULL,
++                                  (void *)keys, (void *)values, count, opts);
 +}
 +
 +int bpf_obj_pin(int fd, const char *pathname)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.pathname = ptr_to_u64((void *)pathname);
 +      attr.bpf_fd = fd;
 +
 +      ret = sys_bpf(BPF_OBJ_PIN, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_obj_get(const char *pathname)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.pathname = ptr_to_u64((void *)pathname);
 +
 +      fd = sys_bpf_fd(BPF_OBJ_GET, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_prog_attach(int prog_fd, int target_fd, enum bpf_attach_type type,
 +                  unsigned int flags)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_prog_attach_opts, opts,
 +              .flags = flags,
 +      );
 +
 +      return bpf_prog_attach_xattr(prog_fd, target_fd, type, &opts);
 +}
 +
 +int bpf_prog_attach_xattr(int prog_fd, int target_fd,
 +                        enum bpf_attach_type type,
 +                        const struct bpf_prog_attach_opts *opts)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      if (!OPTS_VALID(opts, bpf_prog_attach_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.target_fd     = target_fd;
 +      attr.attach_bpf_fd = prog_fd;
 +      attr.attach_type   = type;
 +      attr.attach_flags  = OPTS_GET(opts, flags, 0);
 +      attr.replace_bpf_fd = OPTS_GET(opts, replace_prog_fd, 0);
 +
 +      ret = sys_bpf(BPF_PROG_ATTACH, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_prog_detach(int target_fd, enum bpf_attach_type type)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.target_fd   = target_fd;
 +      attr.attach_type = type;
 +
 +      ret = sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_prog_detach2(int prog_fd, int target_fd, enum bpf_attach_type type)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.target_fd   = target_fd;
 +      attr.attach_bpf_fd = prog_fd;
 +      attr.attach_type = type;
 +
 +      ret = sys_bpf(BPF_PROG_DETACH, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_link_create(int prog_fd, int target_fd,
 +                  enum bpf_attach_type attach_type,
 +                  const struct bpf_link_create_opts *opts)
 +{
 +      __u32 target_btf_id, iter_info_len;
 +      union bpf_attr attr;
 +      int fd;
 +
 +      if (!OPTS_VALID(opts, bpf_link_create_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      iter_info_len = OPTS_GET(opts, iter_info_len, 0);
 +      target_btf_id = OPTS_GET(opts, target_btf_id, 0);
 +
 +      /* validate we don't have unexpected combinations of non-zero fields */
 +      if (iter_info_len || target_btf_id) {
 +              if (iter_info_len && target_btf_id)
 +                      return libbpf_err(-EINVAL);
 +              if (!OPTS_ZEROED(opts, target_btf_id))
 +                      return libbpf_err(-EINVAL);
 +      }
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.link_create.prog_fd = prog_fd;
 +      attr.link_create.target_fd = target_fd;
 +      attr.link_create.attach_type = attach_type;
 +      attr.link_create.flags = OPTS_GET(opts, flags, 0);
 +
 +      if (target_btf_id) {
 +              attr.link_create.target_btf_id = target_btf_id;
 +              goto proceed;
 +      }
 +
 +      switch (attach_type) {
 +      case BPF_TRACE_ITER:
 +              attr.link_create.iter_info = ptr_to_u64(OPTS_GET(opts, iter_info, (void *)0));
 +              attr.link_create.iter_info_len = iter_info_len;
 +              break;
 +      case BPF_PERF_EVENT:
 +              attr.link_create.perf_event.bpf_cookie = OPTS_GET(opts, perf_event.bpf_cookie, 0);
 +              if (!OPTS_ZEROED(opts, perf_event))
 +                      return libbpf_err(-EINVAL);
 +              break;
 +      default:
 +              if (!OPTS_ZEROED(opts, flags))
 +                      return libbpf_err(-EINVAL);
 +              break;
 +      }
 +proceed:
 +      fd = sys_bpf_fd(BPF_LINK_CREATE, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_link_detach(int link_fd)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.link_detach.link_fd = link_fd;
 +
 +      ret = sys_bpf(BPF_LINK_DETACH, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_link_update(int link_fd, int new_prog_fd,
 +                  const struct bpf_link_update_opts *opts)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      if (!OPTS_VALID(opts, bpf_link_update_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.link_update.link_fd = link_fd;
 +      attr.link_update.new_prog_fd = new_prog_fd;
 +      attr.link_update.flags = OPTS_GET(opts, flags, 0);
 +      attr.link_update.old_prog_fd = OPTS_GET(opts, old_prog_fd, 0);
 +
 +      ret = sys_bpf(BPF_LINK_UPDATE, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_iter_create(int link_fd)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.iter_create.link_fd = link_fd;
 +
 +      fd = sys_bpf_fd(BPF_ITER_CREATE, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_prog_query(int target_fd, enum bpf_attach_type type, __u32 query_flags,
 +                 __u32 *attach_flags, __u32 *prog_ids, __u32 *prog_cnt)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.query.target_fd    = target_fd;
 +      attr.query.attach_type  = type;
 +      attr.query.query_flags  = query_flags;
 +      attr.query.prog_cnt     = *prog_cnt;
 +      attr.query.prog_ids     = ptr_to_u64(prog_ids);
 +
 +      ret = sys_bpf(BPF_PROG_QUERY, &attr, sizeof(attr));
 +
 +      if (attach_flags)
 +              *attach_flags = attr.query.attach_flags;
 +      *prog_cnt = attr.query.prog_cnt;
 +
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_prog_test_run(int prog_fd, int repeat, void *data, __u32 size,
 +                    void *data_out, __u32 *size_out, __u32 *retval,
 +                    __u32 *duration)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.test.prog_fd = prog_fd;
 +      attr.test.data_in = ptr_to_u64(data);
 +      attr.test.data_out = ptr_to_u64(data_out);
 +      attr.test.data_size_in = size;
 +      attr.test.repeat = repeat;
 +
 +      ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
 +
 +      if (size_out)
 +              *size_out = attr.test.data_size_out;
 +      if (retval)
 +              *retval = attr.test.retval;
 +      if (duration)
 +              *duration = attr.test.duration;
 +
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      if (!test_attr->data_out && test_attr->data_size_out > 0)
 +              return libbpf_err(-EINVAL);
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.test.prog_fd = test_attr->prog_fd;
 +      attr.test.data_in = ptr_to_u64(test_attr->data_in);
 +      attr.test.data_out = ptr_to_u64(test_attr->data_out);
 +      attr.test.data_size_in = test_attr->data_size_in;
 +      attr.test.data_size_out = test_attr->data_size_out;
 +      attr.test.ctx_in = ptr_to_u64(test_attr->ctx_in);
 +      attr.test.ctx_out = ptr_to_u64(test_attr->ctx_out);
 +      attr.test.ctx_size_in = test_attr->ctx_size_in;
 +      attr.test.ctx_size_out = test_attr->ctx_size_out;
 +      attr.test.repeat = test_attr->repeat;
 +
 +      ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
 +
 +      test_attr->data_size_out = attr.test.data_size_out;
 +      test_attr->ctx_size_out = attr.test.ctx_size_out;
 +      test_attr->retval = attr.test.retval;
 +      test_attr->duration = attr.test.duration;
 +
 +      return libbpf_err_errno(ret);
 +}
 +
 +int bpf_prog_test_run_opts(int prog_fd, struct bpf_test_run_opts *opts)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      if (!OPTS_VALID(opts, bpf_test_run_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.test.prog_fd = prog_fd;
 +      attr.test.cpu = OPTS_GET(opts, cpu, 0);
 +      attr.test.flags = OPTS_GET(opts, flags, 0);
 +      attr.test.repeat = OPTS_GET(opts, repeat, 0);
 +      attr.test.duration = OPTS_GET(opts, duration, 0);
 +      attr.test.ctx_size_in = OPTS_GET(opts, ctx_size_in, 0);
 +      attr.test.ctx_size_out = OPTS_GET(opts, ctx_size_out, 0);
 +      attr.test.data_size_in = OPTS_GET(opts, data_size_in, 0);
 +      attr.test.data_size_out = OPTS_GET(opts, data_size_out, 0);
 +      attr.test.ctx_in = ptr_to_u64(OPTS_GET(opts, ctx_in, NULL));
 +      attr.test.ctx_out = ptr_to_u64(OPTS_GET(opts, ctx_out, NULL));
 +      attr.test.data_in = ptr_to_u64(OPTS_GET(opts, data_in, NULL));
 +      attr.test.data_out = ptr_to_u64(OPTS_GET(opts, data_out, NULL));
 +
 +      ret = sys_bpf(BPF_PROG_TEST_RUN, &attr, sizeof(attr));
 +
 +      OPTS_SET(opts, data_size_out, attr.test.data_size_out);
 +      OPTS_SET(opts, ctx_size_out, attr.test.ctx_size_out);
 +      OPTS_SET(opts, duration, attr.test.duration);
 +      OPTS_SET(opts, retval, attr.test.retval);
 +
 +      return libbpf_err_errno(ret);
 +}
 +
 +static int bpf_obj_get_next_id(__u32 start_id, __u32 *next_id, int cmd)
 +{
 +      union bpf_attr attr;
 +      int err;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.start_id = start_id;
 +
 +      err = sys_bpf(cmd, &attr, sizeof(attr));
 +      if (!err)
 +              *next_id = attr.next_id;
 +
 +      return libbpf_err_errno(err);
 +}
 +
 +int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id)
 +{
 +      return bpf_obj_get_next_id(start_id, next_id, BPF_PROG_GET_NEXT_ID);
 +}
 +
 +int bpf_map_get_next_id(__u32 start_id, __u32 *next_id)
 +{
 +      return bpf_obj_get_next_id(start_id, next_id, BPF_MAP_GET_NEXT_ID);
 +}
 +
 +int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id)
 +{
 +      return bpf_obj_get_next_id(start_id, next_id, BPF_BTF_GET_NEXT_ID);
 +}
 +
 +int bpf_link_get_next_id(__u32 start_id, __u32 *next_id)
 +{
 +      return bpf_obj_get_next_id(start_id, next_id, BPF_LINK_GET_NEXT_ID);
 +}
 +
 +int bpf_prog_get_fd_by_id(__u32 id)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.prog_id = id;
 +
 +      fd = sys_bpf_fd(BPF_PROG_GET_FD_BY_ID, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_map_get_fd_by_id(__u32 id)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.map_id = id;
 +
 +      fd = sys_bpf_fd(BPF_MAP_GET_FD_BY_ID, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_btf_get_fd_by_id(__u32 id)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.btf_id = id;
 +
 +      fd = sys_bpf_fd(BPF_BTF_GET_FD_BY_ID, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_link_get_fd_by_id(__u32 id)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.link_id = id;
 +
 +      fd = sys_bpf_fd(BPF_LINK_GET_FD_BY_ID, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len)
 +{
 +      union bpf_attr attr;
 +      int err;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.info.bpf_fd = bpf_fd;
 +      attr.info.info_len = *info_len;
 +      attr.info.info = ptr_to_u64(info);
 +
 +      err = sys_bpf(BPF_OBJ_GET_INFO_BY_FD, &attr, sizeof(attr));
 +
 +      if (!err)
 +              *info_len = attr.info.info_len;
 +
 +      return libbpf_err_errno(err);
 +}
 +
 +int bpf_raw_tracepoint_open(const char *name, int prog_fd)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.raw_tracepoint.name = ptr_to_u64(name);
 +      attr.raw_tracepoint.prog_fd = prog_fd;
 +
 +      fd = sys_bpf_fd(BPF_RAW_TRACEPOINT_OPEN, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_btf_load(const void *btf_data, size_t btf_size, const struct bpf_btf_load_opts *opts)
 +{
 +      const size_t attr_sz = offsetofend(union bpf_attr, btf_log_level);
 +      union bpf_attr attr;
 +      char *log_buf;
 +      size_t log_size;
 +      __u32 log_level;
 +      int fd;
 +
 +      bump_rlimit_memlock();
 +
 +      memset(&attr, 0, attr_sz);
 +
 +      if (!OPTS_VALID(opts, bpf_btf_load_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      log_buf = OPTS_GET(opts, log_buf, NULL);
 +      log_size = OPTS_GET(opts, log_size, 0);
 +      log_level = OPTS_GET(opts, log_level, 0);
 +
 +      if (log_size > UINT_MAX)
 +              return libbpf_err(-EINVAL);
 +      if (log_size && !log_buf)
 +              return libbpf_err(-EINVAL);
 +
 +      attr.btf = ptr_to_u64(btf_data);
 +      attr.btf_size = btf_size;
 +      /* log_level == 0 and log_buf != NULL means "try loading without
 +       * log_buf, but retry with log_buf and log_level=1 on error", which is
 +       * consistent across low-level and high-level BTF and program loading
 +       * APIs within libbpf and provides a sensible behavior in practice
 +       */
 +      if (log_level) {
 +              attr.btf_log_buf = ptr_to_u64(log_buf);
 +              attr.btf_log_size = (__u32)log_size;
 +              attr.btf_log_level = log_level;
 +      }
 +
 +      fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, attr_sz);
 +      if (fd < 0 && log_buf && log_level == 0) {
 +              attr.btf_log_buf = ptr_to_u64(log_buf);
 +              attr.btf_log_size = (__u32)log_size;
 +              attr.btf_log_level = 1;
 +              fd = sys_bpf_fd(BPF_BTF_LOAD, &attr, attr_sz);
 +      }
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf, __u32 log_buf_size, bool do_log)
 +{
 +      LIBBPF_OPTS(bpf_btf_load_opts, opts);
 +      int fd;
 +
 +retry:
 +      if (do_log && log_buf && log_buf_size) {
 +              opts.log_buf = log_buf;
 +              opts.log_size = log_buf_size;
 +              opts.log_level = 1;
 +      }
 +
 +      fd = bpf_btf_load(btf, btf_size, &opts);
 +      if (fd < 0 && !do_log && log_buf && log_buf_size) {
 +              do_log = true;
 +              goto retry;
 +      }
 +
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf, __u32 *buf_len,
 +                    __u32 *prog_id, __u32 *fd_type, __u64 *probe_offset,
 +                    __u64 *probe_addr)
 +{
 +      union bpf_attr attr = {};
 +      int err;
 +
 +      attr.task_fd_query.pid = pid;
 +      attr.task_fd_query.fd = fd;
 +      attr.task_fd_query.flags = flags;
 +      attr.task_fd_query.buf = ptr_to_u64(buf);
 +      attr.task_fd_query.buf_len = *buf_len;
 +
 +      err = sys_bpf(BPF_TASK_FD_QUERY, &attr, sizeof(attr));
 +
 +      *buf_len = attr.task_fd_query.buf_len;
 +      *prog_id = attr.task_fd_query.prog_id;
 +      *fd_type = attr.task_fd_query.fd_type;
 +      *probe_offset = attr.task_fd_query.probe_offset;
 +      *probe_addr = attr.task_fd_query.probe_addr;
 +
 +      return libbpf_err_errno(err);
 +}
 +
 +int bpf_enable_stats(enum bpf_stats_type type)
 +{
 +      union bpf_attr attr;
 +      int fd;
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.enable_stats.type = type;
 +
 +      fd = sys_bpf_fd(BPF_ENABLE_STATS, &attr, sizeof(attr));
 +      return libbpf_err_errno(fd);
 +}
 +
 +int bpf_prog_bind_map(int prog_fd, int map_fd,
 +                    const struct bpf_prog_bind_opts *opts)
 +{
 +      union bpf_attr attr;
 +      int ret;
 +
 +      if (!OPTS_VALID(opts, bpf_prog_bind_opts))
 +              return libbpf_err(-EINVAL);
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.prog_bind_map.prog_fd = prog_fd;
 +      attr.prog_bind_map.map_fd = map_fd;
 +      attr.prog_bind_map.flags = OPTS_GET(opts, flags, 0);
 +
 +      ret = sys_bpf(BPF_PROG_BIND_MAP, &attr, sizeof(attr));
 +      return libbpf_err_errno(ret);
 +}
index 00619f6,0000000..14e0d97
mode 100644,000000..100644
--- /dev/null
@@@ -1,408 -1,0 +1,519 @@@
- LIBBPF_API int bpf_map_delete_batch(int fd, void *keys,
 +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
 +
 +/*
 + * common eBPF ELF operations.
 + *
 + * Copyright (C) 2013-2015 Alexei Starovoitov <ast@kernel.org>
 + * Copyright (C) 2015 Wang Nan <wangnan0@huawei.com>
 + * Copyright (C) 2015 Huawei Inc.
 + *
 + * 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>
 + */
 +#ifndef __LIBBPF_BPF_H
 +#define __LIBBPF_BPF_H
 +
 +#include <linux/bpf.h>
 +#include <stdbool.h>
 +#include <stddef.h>
 +#include <stdint.h>
 +
 +#include "libbpf_common.h"
 +#include "libbpf_legacy.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +int libbpf_set_memlock_rlim(size_t memlock_bytes);
 +
 +struct bpf_map_create_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +
 +      __u32 btf_fd;
 +      __u32 btf_key_type_id;
 +      __u32 btf_value_type_id;
 +      __u32 btf_vmlinux_value_type_id;
 +
 +      __u32 inner_map_fd;
 +      __u32 map_flags;
 +      __u64 map_extra;
 +
 +      __u32 numa_node;
 +      __u32 map_ifindex;
 +};
 +#define bpf_map_create_opts__last_field map_ifindex
 +
 +LIBBPF_API int bpf_map_create(enum bpf_map_type map_type,
 +                            const char *map_name,
 +                            __u32 key_size,
 +                            __u32 value_size,
 +                            __u32 max_entries,
 +                            const struct bpf_map_create_opts *opts);
 +
 +struct bpf_create_map_attr {
 +      const char *name;
 +      enum bpf_map_type map_type;
 +      __u32 map_flags;
 +      __u32 key_size;
 +      __u32 value_size;
 +      __u32 max_entries;
 +      __u32 numa_node;
 +      __u32 btf_fd;
 +      __u32 btf_key_type_id;
 +      __u32 btf_value_type_id;
 +      __u32 map_ifindex;
 +      union {
 +              __u32 inner_map_fd;
 +              __u32 btf_vmlinux_value_type_id;
 +      };
 +};
 +
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_map_create() instead")
 +LIBBPF_API int bpf_create_map_xattr(const struct bpf_create_map_attr *create_attr);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_map_create() instead")
 +LIBBPF_API int bpf_create_map_node(enum bpf_map_type map_type, const char *name,
 +                                 int key_size, int value_size,
 +                                 int max_entries, __u32 map_flags, int node);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_map_create() instead")
 +LIBBPF_API int bpf_create_map_name(enum bpf_map_type map_type, const char *name,
 +                                 int key_size, int value_size,
 +                                 int max_entries, __u32 map_flags);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_map_create() instead")
 +LIBBPF_API int bpf_create_map(enum bpf_map_type map_type, int key_size,
 +                            int value_size, int max_entries, __u32 map_flags);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_map_create() instead")
 +LIBBPF_API int bpf_create_map_in_map_node(enum bpf_map_type map_type,
 +                                        const char *name, int key_size,
 +                                        int inner_map_fd, int max_entries,
 +                                        __u32 map_flags, int node);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_map_create() instead")
 +LIBBPF_API int bpf_create_map_in_map(enum bpf_map_type map_type,
 +                                   const char *name, int key_size,
 +                                   int inner_map_fd, int max_entries,
 +                                   __u32 map_flags);
 +
 +struct bpf_prog_load_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +
 +      /* libbpf can retry BPF_PROG_LOAD command if bpf() syscall returns
 +       * -EAGAIN. This field determines how many attempts libbpf has to
 +       *  make. If not specified, libbpf will use default value of 5.
 +       */
 +      int attempts;
 +
 +      enum bpf_attach_type expected_attach_type;
 +      __u32 prog_btf_fd;
 +      __u32 prog_flags;
 +      __u32 prog_ifindex;
 +      __u32 kern_version;
 +
 +      __u32 attach_btf_id;
 +      __u32 attach_prog_fd;
 +      __u32 attach_btf_obj_fd;
 +
 +      const int *fd_array;
 +
 +      /* .BTF.ext func info data */
 +      const void *func_info;
 +      __u32 func_info_cnt;
 +      __u32 func_info_rec_size;
 +
 +      /* .BTF.ext line info data */
 +      const void *line_info;
 +      __u32 line_info_cnt;
 +      __u32 line_info_rec_size;
 +
 +      /* verifier log options */
 +      __u32 log_level;
 +      __u32 log_size;
 +      char *log_buf;
 +};
 +#define bpf_prog_load_opts__last_field log_buf
 +
 +LIBBPF_API int bpf_prog_load(enum bpf_prog_type prog_type,
 +                           const char *prog_name, const char *license,
 +                           const struct bpf_insn *insns, size_t insn_cnt,
 +                           const struct bpf_prog_load_opts *opts);
 +/* this "specialization" should go away in libbpf 1.0 */
 +LIBBPF_API int bpf_prog_load_v0_6_0(enum bpf_prog_type prog_type,
 +                                  const char *prog_name, const char *license,
 +                                  const struct bpf_insn *insns, size_t insn_cnt,
 +                                  const struct bpf_prog_load_opts *opts);
 +
 +/* This is an elaborate way to not conflict with deprecated bpf_prog_load()
 + * API, defined in libbpf.h. Once we hit libbpf 1.0, all this will be gone.
 + * With this approach, if someone is calling bpf_prog_load() with
 + * 4 arguments, they will use the deprecated API, which keeps backwards
 + * compatibility (both source code and binary). If bpf_prog_load() is called
 + * with 6 arguments, though, it gets redirected to __bpf_prog_load.
 + * So looking forward to libbpf 1.0 when this hack will be gone and
 + * __bpf_prog_load() will be called just bpf_prog_load().
 + */
 +#ifndef bpf_prog_load
 +#define bpf_prog_load(...) ___libbpf_overload(___bpf_prog_load, __VA_ARGS__)
 +#define ___bpf_prog_load4(file, type, pobj, prog_fd) \
 +      bpf_prog_load_deprecated(file, type, pobj, prog_fd)
 +#define ___bpf_prog_load6(prog_type, prog_name, license, insns, insn_cnt, opts) \
 +      bpf_prog_load(prog_type, prog_name, license, insns, insn_cnt, opts)
 +#endif /* bpf_prog_load */
 +
 +struct bpf_load_program_attr {
 +      enum bpf_prog_type prog_type;
 +      enum bpf_attach_type expected_attach_type;
 +      const char *name;
 +      const struct bpf_insn *insns;
 +      size_t insns_cnt;
 +      const char *license;
 +      union {
 +              __u32 kern_version;
 +              __u32 attach_prog_fd;
 +      };
 +      union {
 +              __u32 prog_ifindex;
 +              __u32 attach_btf_id;
 +      };
 +      __u32 prog_btf_fd;
 +      __u32 func_info_rec_size;
 +      const void *func_info;
 +      __u32 func_info_cnt;
 +      __u32 line_info_rec_size;
 +      const void *line_info;
 +      __u32 line_info_cnt;
 +      __u32 log_level;
 +      __u32 prog_flags;
 +};
 +
 +/* Flags to direct loading requirements */
 +#define MAPS_RELAX_COMPAT     0x01
 +
 +/* Recommended log buffer size */
 +#define BPF_LOG_BUF_SIZE (UINT32_MAX >> 8) /* verifier maximum in kernels <= 5.1 */
 +
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_load() instead")
 +LIBBPF_API int bpf_load_program_xattr(const struct bpf_load_program_attr *load_attr,
 +                                    char *log_buf, size_t log_buf_sz);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_load() instead")
 +LIBBPF_API int bpf_load_program(enum bpf_prog_type type,
 +                              const struct bpf_insn *insns, size_t insns_cnt,
 +                              const char *license, __u32 kern_version,
 +                              char *log_buf, size_t log_buf_sz);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_prog_load() instead")
 +LIBBPF_API int bpf_verify_program(enum bpf_prog_type type,
 +                                const struct bpf_insn *insns,
 +                                size_t insns_cnt, __u32 prog_flags,
 +                                const char *license, __u32 kern_version,
 +                                char *log_buf, size_t log_buf_sz,
 +                                int log_level);
 +
 +struct bpf_btf_load_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +
 +      /* kernel log options */
 +      char *log_buf;
 +      __u32 log_level;
 +      __u32 log_size;
 +};
 +#define bpf_btf_load_opts__last_field log_size
 +
 +LIBBPF_API int bpf_btf_load(const void *btf_data, size_t btf_size,
 +                          const struct bpf_btf_load_opts *opts);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_btf_load() instead")
 +LIBBPF_API int bpf_load_btf(const void *btf, __u32 btf_size, char *log_buf,
 +                          __u32 log_buf_size, bool do_log);
 +
 +LIBBPF_API int bpf_map_update_elem(int fd, const void *key, const void *value,
 +                                 __u64 flags);
 +
 +LIBBPF_API int bpf_map_lookup_elem(int fd, const void *key, void *value);
 +LIBBPF_API int bpf_map_lookup_elem_flags(int fd, const void *key, void *value,
 +                                       __u64 flags);
 +LIBBPF_API int bpf_map_lookup_and_delete_elem(int fd, const void *key,
 +                                            void *value);
 +LIBBPF_API int bpf_map_lookup_and_delete_elem_flags(int fd, const void *key,
 +                                                  void *value, __u64 flags);
 +LIBBPF_API int bpf_map_delete_elem(int fd, const void *key);
 +LIBBPF_API int bpf_map_get_next_key(int fd, const void *key, void *next_key);
 +LIBBPF_API int bpf_map_freeze(int fd);
 +
 +struct bpf_map_batch_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      __u64 elem_flags;
 +      __u64 flags;
 +};
 +#define bpf_map_batch_opts__last_field flags
 +
- LIBBPF_API int bpf_map_update_batch(int fd, void *keys, void *values,
++
++/**
++ * @brief **bpf_map_delete_batch()** allows for batch deletion of multiple
++ * elements in a BPF map.
++ *
++ * @param fd BPF map file descriptor
++ * @param keys pointer to an array of *count* keys
++ * @param count input and output parameter; on input **count** represents the
++ * number of  elements in the map to delete in batch;
++ * on output if a non-EFAULT error is returned, **count** represents the number of deleted
++ * elements if the output **count** value is not equal to the input **count** value
++ * If EFAULT is returned, **count** should not be trusted to be correct.
++ * @param opts options for configuring the way the batch deletion works
++ * @return 0, on success; negative error code, otherwise (errno is also set to
++ * the error code)
++ */
++LIBBPF_API int bpf_map_delete_batch(int fd, const void *keys,
 +                                  __u32 *count,
 +                                  const struct bpf_map_batch_opts *opts);
++
++/**
++ * @brief **bpf_map_lookup_batch()** allows for batch lookup of BPF map elements.
++ *
++ * The parameter *in_batch* is the address of the first element in the batch to read.
++ * *out_batch* is an output parameter that should be passed as *in_batch* to subsequent
++ * calls to **bpf_map_lookup_batch()**. NULL can be passed for *in_batch* to indicate
++ * that the batched lookup starts from the beginning of the map.
++ *
++ * The *keys* and *values* are output parameters which must point to memory large enough to
++ * hold *count* items based on the key and value size of the map *map_fd*. The *keys*
++ * buffer must be of *key_size* * *count*. The *values* buffer must be of
++ * *value_size* * *count*.
++ *
++ * @param fd BPF map file descriptor
++ * @param in_batch address of the first element in batch to read, can pass NULL to
++ * indicate that the batched lookup starts from the beginning of the map.
++ * @param out_batch output parameter that should be passed to next call as *in_batch*
++ * @param keys pointer to an array large enough for *count* keys
++ * @param values pointer to an array large enough for *count* values
++ * @param count input and output parameter; on input it's the number of elements
++ * in the map to read in batch; on output it's the number of elements that were
++ * successfully read.
++ * If a non-EFAULT error is returned, count will be set as the number of elements
++ * that were read before the error occurred.
++ * If EFAULT is returned, **count** should not be trusted to be correct.
++ * @param opts options for configuring the way the batch lookup works
++ * @return 0, on success; negative error code, otherwise (errno is also set to
++ * the error code)
++ */
 +LIBBPF_API int bpf_map_lookup_batch(int fd, void *in_batch, void *out_batch,
 +                                  void *keys, void *values, __u32 *count,
 +                                  const struct bpf_map_batch_opts *opts);
++
++/**
++ * @brief **bpf_map_lookup_and_delete_batch()** allows for batch lookup and deletion
++ * of BPF map elements where each element is deleted after being retrieved.
++ *
++ * @param fd BPF map file descriptor
++ * @param in_batch address of the first element in batch to read, can pass NULL to
++ * get address of the first element in *out_batch*
++ * @param out_batch output parameter that should be passed to next call as *in_batch*
++ * @param keys pointer to an array of *count* keys
++ * @param values pointer to an array large enough for *count* values
++ * @param count input and output parameter; on input it's the number of elements
++ * in the map to read and delete in batch; on output it represents the number of
++ * elements that were successfully read and deleted
++ * If a non-**EFAULT** error code is returned and if the output **count** value
++ * is not equal to the input **count** value, up to **count** elements may
++ * have been deleted.
++ * if **EFAULT** is returned up to *count* elements may have been deleted without
++ * being returned via the *keys* and *values* output parameters.
++ * @param opts options for configuring the way the batch lookup and delete works
++ * @return 0, on success; negative error code, otherwise (errno is also set to
++ * the error code)
++ */
 +LIBBPF_API int bpf_map_lookup_and_delete_batch(int fd, void *in_batch,
 +                                      void *out_batch, void *keys,
 +                                      void *values, __u32 *count,
 +                                      const struct bpf_map_batch_opts *opts);
++
++/**
++ * @brief **bpf_map_update_batch()** updates multiple elements in a map
++ * by specifying keys and their corresponding values.
++ *
++ * The *keys* and *values* parameters must point to memory large enough
++ * to hold *count* items based on the key and value size of the map.
++ *
++ * The *opts* parameter can be used to control how *bpf_map_update_batch()*
++ * should handle keys that either do or do not already exist in the map.
++ * In particular the *flags* parameter of *bpf_map_batch_opts* can be
++ * one of the following:
++ *
++ * Note that *count* is an input and output parameter, where on output it
++ * represents how many elements were successfully updated. Also note that if
++ * **EFAULT** then *count* should not be trusted to be correct.
++ *
++ * **BPF_ANY**
++ *    Create new elements or update existing.
++ *
++ * **BPF_NOEXIST**
++ *    Create new elements only if they do not exist.
++ *
++ * **BPF_EXIST**
++ *    Update existing elements.
++ *
++ * **BPF_F_LOCK**
++ *    Update spin_lock-ed map elements. This must be
++ *    specified if the map value contains a spinlock.
++ *
++ * @param fd BPF map file descriptor
++ * @param keys pointer to an array of *count* keys
++ * @param values pointer to an array of *count* values
++ * @param count input and output parameter; on input it's the number of elements
++ * in the map to update in batch; on output if a non-EFAULT error is returned,
++ * **count** represents the number of updated elements if the output **count**
++ * value is not equal to the input **count** value.
++ * If EFAULT is returned, **count** should not be trusted to be correct.
++ * @param opts options for configuring the way the batch update works
++ * @return 0, on success; negative error code, otherwise (errno is also set to
++ * the error code)
++ */
++LIBBPF_API int bpf_map_update_batch(int fd, const void *keys, const void *values,
 +                                  __u32 *count,
 +                                  const struct bpf_map_batch_opts *opts);
 +
 +LIBBPF_API int bpf_obj_pin(int fd, const char *pathname);
 +LIBBPF_API int bpf_obj_get(const char *pathname);
 +
 +struct bpf_prog_attach_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      unsigned int flags;
 +      int replace_prog_fd;
 +};
 +#define bpf_prog_attach_opts__last_field replace_prog_fd
 +
 +LIBBPF_API int bpf_prog_attach(int prog_fd, int attachable_fd,
 +                             enum bpf_attach_type type, unsigned int flags);
 +LIBBPF_API int bpf_prog_attach_xattr(int prog_fd, int attachable_fd,
 +                                   enum bpf_attach_type type,
 +                                   const struct bpf_prog_attach_opts *opts);
 +LIBBPF_API int bpf_prog_detach(int attachable_fd, enum bpf_attach_type type);
 +LIBBPF_API int bpf_prog_detach2(int prog_fd, int attachable_fd,
 +                              enum bpf_attach_type type);
 +
 +union bpf_iter_link_info; /* defined in up-to-date linux/bpf.h */
 +struct bpf_link_create_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      __u32 flags;
 +      union bpf_iter_link_info *iter_info;
 +      __u32 iter_info_len;
 +      __u32 target_btf_id;
 +      union {
 +              struct {
 +                      __u64 bpf_cookie;
 +              } perf_event;
 +      };
 +      size_t :0;
 +};
 +#define bpf_link_create_opts__last_field perf_event
 +
 +LIBBPF_API int bpf_link_create(int prog_fd, int target_fd,
 +                             enum bpf_attach_type attach_type,
 +                             const struct bpf_link_create_opts *opts);
 +
 +LIBBPF_API int bpf_link_detach(int link_fd);
 +
 +struct bpf_link_update_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      __u32 flags;       /* extra flags */
 +      __u32 old_prog_fd; /* expected old program FD */
 +};
 +#define bpf_link_update_opts__last_field old_prog_fd
 +
 +LIBBPF_API int bpf_link_update(int link_fd, int new_prog_fd,
 +                             const struct bpf_link_update_opts *opts);
 +
 +LIBBPF_API int bpf_iter_create(int link_fd);
 +
 +struct bpf_prog_test_run_attr {
 +      int prog_fd;
 +      int repeat;
 +      const void *data_in;
 +      __u32 data_size_in;
 +      void *data_out;      /* optional */
 +      __u32 data_size_out; /* in: max length of data_out
 +                            * out: length of data_out */
 +      __u32 retval;        /* out: return code of the BPF program */
 +      __u32 duration;      /* out: average per repetition in ns */
 +      const void *ctx_in; /* optional */
 +      __u32 ctx_size_in;
 +      void *ctx_out;      /* optional */
 +      __u32 ctx_size_out; /* in: max length of ctx_out
 +                           * out: length of cxt_out */
 +};
 +
 +LIBBPF_API int bpf_prog_test_run_xattr(struct bpf_prog_test_run_attr *test_attr);
 +
 +/*
 + * bpf_prog_test_run does not check that data_out is large enough. Consider
 + * using bpf_prog_test_run_xattr instead.
 + */
 +LIBBPF_API int bpf_prog_test_run(int prog_fd, int repeat, void *data,
 +                               __u32 size, void *data_out, __u32 *size_out,
 +                               __u32 *retval, __u32 *duration);
 +LIBBPF_API int bpf_prog_get_next_id(__u32 start_id, __u32 *next_id);
 +LIBBPF_API int bpf_map_get_next_id(__u32 start_id, __u32 *next_id);
 +LIBBPF_API int bpf_btf_get_next_id(__u32 start_id, __u32 *next_id);
 +LIBBPF_API int bpf_link_get_next_id(__u32 start_id, __u32 *next_id);
 +LIBBPF_API int bpf_prog_get_fd_by_id(__u32 id);
 +LIBBPF_API int bpf_map_get_fd_by_id(__u32 id);
 +LIBBPF_API int bpf_btf_get_fd_by_id(__u32 id);
 +LIBBPF_API int bpf_link_get_fd_by_id(__u32 id);
 +LIBBPF_API int bpf_obj_get_info_by_fd(int bpf_fd, void *info, __u32 *info_len);
 +LIBBPF_API int bpf_prog_query(int target_fd, enum bpf_attach_type type,
 +                            __u32 query_flags, __u32 *attach_flags,
 +                            __u32 *prog_ids, __u32 *prog_cnt);
 +LIBBPF_API int bpf_raw_tracepoint_open(const char *name, int prog_fd);
 +LIBBPF_API int bpf_task_fd_query(int pid, int fd, __u32 flags, char *buf,
 +                               __u32 *buf_len, __u32 *prog_id, __u32 *fd_type,
 +                               __u64 *probe_offset, __u64 *probe_addr);
 +
 +enum bpf_stats_type; /* defined in up-to-date linux/bpf.h */
 +LIBBPF_API int bpf_enable_stats(enum bpf_stats_type type);
 +
 +struct bpf_prog_bind_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      __u32 flags;
 +};
 +#define bpf_prog_bind_opts__last_field flags
 +
 +LIBBPF_API int bpf_prog_bind_map(int prog_fd, int map_fd,
 +                               const struct bpf_prog_bind_opts *opts);
 +
 +struct bpf_test_run_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      const void *data_in; /* optional */
 +      void *data_out;      /* optional */
 +      __u32 data_size_in;
 +      __u32 data_size_out; /* in: max length of data_out
 +                            * out: length of data_out
 +                            */
 +      const void *ctx_in; /* optional */
 +      void *ctx_out;      /* optional */
 +      __u32 ctx_size_in;
 +      __u32 ctx_size_out; /* in: max length of ctx_out
 +                           * out: length of cxt_out
 +                           */
 +      __u32 retval;        /* out: return code of the BPF program */
 +      int repeat;
 +      __u32 duration;      /* out: average per repetition in ns */
 +      __u32 flags;
 +      __u32 cpu;
 +};
 +#define bpf_test_run_opts__last_field cpu
 +
 +LIBBPF_API int bpf_prog_test_run_opts(int prog_fd,
 +                                    struct bpf_test_run_opts *opts);
 +
 +#ifdef __cplusplus
 +} /* extern "C" */
 +#endif
 +
 +#endif /* __LIBBPF_BPF_H */
index db05a59,0000000..90f56b0
mode 100644,000000..100644
--- /dev/null
@@@ -1,492 -1,0 +1,409 @@@
- #define PT_REGS_PARM1(x) ((x)->di)
- #define PT_REGS_PARM2(x) ((x)->si)
- #define PT_REGS_PARM3(x) ((x)->dx)
- #define PT_REGS_PARM4(x) ((x)->cx)
- #define PT_REGS_PARM5(x) ((x)->r8)
- #define PT_REGS_RET(x) ((x)->sp)
- #define PT_REGS_FP(x) ((x)->bp)
- #define PT_REGS_RC(x) ((x)->ax)
- #define PT_REGS_SP(x) ((x)->sp)
- #define PT_REGS_IP(x) ((x)->ip)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), di)
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), si)
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), dx)
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), cx)
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), r8)
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), sp)
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), bp)
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), ax)
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), sp)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), ip)
 +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
 +#ifndef __BPF_TRACING_H__
 +#define __BPF_TRACING_H__
 +
 +/* Scan the ARCH passed in from ARCH env variable (see Makefile) */
 +#if defined(__TARGET_ARCH_x86)
 +      #define bpf_target_x86
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_s390)
 +      #define bpf_target_s390
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_arm)
 +      #define bpf_target_arm
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_arm64)
 +      #define bpf_target_arm64
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_mips)
 +      #define bpf_target_mips
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_powerpc)
 +      #define bpf_target_powerpc
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_sparc)
 +      #define bpf_target_sparc
 +      #define bpf_target_defined
 +#elif defined(__TARGET_ARCH_riscv)
 +      #define bpf_target_riscv
 +      #define bpf_target_defined
 +#else
 +
 +/* Fall back to what the compiler says */
 +#if defined(__x86_64__)
 +      #define bpf_target_x86
 +      #define bpf_target_defined
 +#elif defined(__s390__)
 +      #define bpf_target_s390
 +      #define bpf_target_defined
 +#elif defined(__arm__)
 +      #define bpf_target_arm
 +      #define bpf_target_defined
 +#elif defined(__aarch64__)
 +      #define bpf_target_arm64
 +      #define bpf_target_defined
 +#elif defined(__mips__)
 +      #define bpf_target_mips
 +      #define bpf_target_defined
 +#elif defined(__powerpc__)
 +      #define bpf_target_powerpc
 +      #define bpf_target_defined
 +#elif defined(__sparc__)
 +      #define bpf_target_sparc
 +      #define bpf_target_defined
 +#elif defined(__riscv) && __riscv_xlen == 64
 +      #define bpf_target_riscv
 +      #define bpf_target_defined
 +#endif /* no compiler target */
 +
 +#endif
 +
 +#ifndef __BPF_TARGET_MISSING
 +#define __BPF_TARGET_MISSING "GCC error \"Must specify a BPF target arch via __TARGET_ARCH_xxx\""
 +#endif
 +
 +#if defined(bpf_target_x86)
 +
 +#if defined(__KERNEL__) || defined(__VMLINUX_H__)
 +
- /* i386 kernel is built with -mregparm=3 */
- #define PT_REGS_PARM1(x) ((x)->eax)
- #define PT_REGS_PARM2(x) ((x)->edx)
- #define PT_REGS_PARM3(x) ((x)->ecx)
- #define PT_REGS_PARM4(x) 0
- #define PT_REGS_PARM5(x) 0
- #define PT_REGS_RET(x) ((x)->esp)
- #define PT_REGS_FP(x) ((x)->ebp)
- #define PT_REGS_RC(x) ((x)->eax)
- #define PT_REGS_SP(x) ((x)->esp)
- #define PT_REGS_IP(x) ((x)->eip)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), eax)
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), edx)
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), ecx)
- #define PT_REGS_PARM4_CORE(x) 0
- #define PT_REGS_PARM5_CORE(x) 0
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), esp)
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), ebp)
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), eax)
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), esp)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), eip)
- #else
++#define __PT_PARM1_REG di
++#define __PT_PARM2_REG si
++#define __PT_PARM3_REG dx
++#define __PT_PARM4_REG cx
++#define __PT_PARM5_REG r8
++#define __PT_RET_REG sp
++#define __PT_FP_REG bp
++#define __PT_RC_REG ax
++#define __PT_SP_REG sp
++#define __PT_IP_REG ip
 +
 +#else
 +
 +#ifdef __i386__
- #define PT_REGS_PARM1(x) ((x)->rdi)
- #define PT_REGS_PARM2(x) ((x)->rsi)
- #define PT_REGS_PARM3(x) ((x)->rdx)
- #define PT_REGS_PARM4(x) ((x)->rcx)
- #define PT_REGS_PARM5(x) ((x)->r8)
- #define PT_REGS_RET(x) ((x)->rsp)
- #define PT_REGS_FP(x) ((x)->rbp)
- #define PT_REGS_RC(x) ((x)->rax)
- #define PT_REGS_SP(x) ((x)->rsp)
- #define PT_REGS_IP(x) ((x)->rip)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), rdi)
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), rsi)
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), rdx)
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), rcx)
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), r8)
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), rsp)
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), rbp)
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), rax)
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), rsp)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), rip)
- #endif
- #endif
 +
- struct pt_regs;
- #define PT_REGS_S390 const volatile user_pt_regs
- #define PT_REGS_PARM1(x) (((PT_REGS_S390 *)(x))->gprs[2])
- #define PT_REGS_PARM2(x) (((PT_REGS_S390 *)(x))->gprs[3])
- #define PT_REGS_PARM3(x) (((PT_REGS_S390 *)(x))->gprs[4])
- #define PT_REGS_PARM4(x) (((PT_REGS_S390 *)(x))->gprs[5])
- #define PT_REGS_PARM5(x) (((PT_REGS_S390 *)(x))->gprs[6])
- #define PT_REGS_RET(x) (((PT_REGS_S390 *)(x))->gprs[14])
- /* Works only with CONFIG_FRAME_POINTER */
- #define PT_REGS_FP(x) (((PT_REGS_S390 *)(x))->gprs[11])
- #define PT_REGS_RC(x) (((PT_REGS_S390 *)(x))->gprs[2])
- #define PT_REGS_SP(x) (((PT_REGS_S390 *)(x))->gprs[15])
- #define PT_REGS_IP(x) (((PT_REGS_S390 *)(x))->psw.addr)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[2])
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[3])
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[4])
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[5])
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[6])
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[14])
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[11])
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[2])
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), gprs[15])
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_S390 *)(x), psw.addr)
++#define __PT_PARM1_REG eax
++#define __PT_PARM2_REG edx
++#define __PT_PARM3_REG ecx
++/* i386 kernel is built with -mregparm=3 */
++#define __PT_PARM4_REG __unsupported__
++#define __PT_PARM5_REG __unsupported__
++#define __PT_RET_REG esp
++#define __PT_FP_REG ebp
++#define __PT_RC_REG eax
++#define __PT_SP_REG esp
++#define __PT_IP_REG eip
++
++#else /* __i386__ */
++
++#define __PT_PARM1_REG rdi
++#define __PT_PARM2_REG rsi
++#define __PT_PARM3_REG rdx
++#define __PT_PARM4_REG rcx
++#define __PT_PARM5_REG r8
++#define __PT_RET_REG rsp
++#define __PT_FP_REG rbp
++#define __PT_RC_REG rax
++#define __PT_SP_REG rsp
++#define __PT_IP_REG rip
++
++#endif /* __i386__ */
++
++#endif /* __KERNEL__ || __VMLINUX_H__ */
 +
 +#elif defined(bpf_target_s390)
 +
 +/* s390 provides user_pt_regs instead of struct pt_regs to userspace */
- #define PT_REGS_PARM1(x) ((x)->uregs[0])
- #define PT_REGS_PARM2(x) ((x)->uregs[1])
- #define PT_REGS_PARM3(x) ((x)->uregs[2])
- #define PT_REGS_PARM4(x) ((x)->uregs[3])
- #define PT_REGS_PARM5(x) ((x)->uregs[4])
- #define PT_REGS_RET(x) ((x)->uregs[14])
- #define PT_REGS_FP(x) ((x)->uregs[11]) /* Works only with CONFIG_FRAME_POINTER */
- #define PT_REGS_RC(x) ((x)->uregs[0])
- #define PT_REGS_SP(x) ((x)->uregs[13])
- #define PT_REGS_IP(x) ((x)->uregs[12])
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), uregs[0])
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), uregs[1])
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), uregs[2])
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), uregs[3])
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), uregs[4])
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), uregs[14])
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), uregs[11])
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), uregs[0])
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), uregs[13])
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), uregs[12])
++#define __PT_REGS_CAST(x) ((const user_pt_regs *)(x))
++#define __PT_PARM1_REG gprs[2]
++#define __PT_PARM2_REG gprs[3]
++#define __PT_PARM3_REG gprs[4]
++#define __PT_PARM4_REG gprs[5]
++#define __PT_PARM5_REG gprs[6]
++#define __PT_RET_REG grps[14]
++#define __PT_FP_REG gprs[11]  /* Works only with CONFIG_FRAME_POINTER */
++#define __PT_RC_REG gprs[2]
++#define __PT_SP_REG gprs[15]
++#define __PT_IP_REG psw.addr
 +
 +#elif defined(bpf_target_arm)
 +
- struct pt_regs;
- #define PT_REGS_ARM64 const volatile struct user_pt_regs
- #define PT_REGS_PARM1(x) (((PT_REGS_ARM64 *)(x))->regs[0])
- #define PT_REGS_PARM2(x) (((PT_REGS_ARM64 *)(x))->regs[1])
- #define PT_REGS_PARM3(x) (((PT_REGS_ARM64 *)(x))->regs[2])
- #define PT_REGS_PARM4(x) (((PT_REGS_ARM64 *)(x))->regs[3])
- #define PT_REGS_PARM5(x) (((PT_REGS_ARM64 *)(x))->regs[4])
- #define PT_REGS_RET(x) (((PT_REGS_ARM64 *)(x))->regs[30])
- /* Works only with CONFIG_FRAME_POINTER */
- #define PT_REGS_FP(x) (((PT_REGS_ARM64 *)(x))->regs[29])
- #define PT_REGS_RC(x) (((PT_REGS_ARM64 *)(x))->regs[0])
- #define PT_REGS_SP(x) (((PT_REGS_ARM64 *)(x))->sp)
- #define PT_REGS_IP(x) (((PT_REGS_ARM64 *)(x))->pc)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[0])
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[1])
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[2])
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[3])
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[4])
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[30])
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[29])
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), regs[0])
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), sp)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_ARM64 *)(x), pc)
++#define __PT_PARM1_REG uregs[0]
++#define __PT_PARM2_REG uregs[1]
++#define __PT_PARM3_REG uregs[2]
++#define __PT_PARM4_REG uregs[3]
++#define __PT_PARM5_REG uregs[4]
++#define __PT_RET_REG uregs[14]
++#define __PT_FP_REG uregs[11] /* Works only with CONFIG_FRAME_POINTER */
++#define __PT_RC_REG uregs[0]
++#define __PT_SP_REG uregs[13]
++#define __PT_IP_REG uregs[12]
 +
 +#elif defined(bpf_target_arm64)
 +
 +/* arm64 provides struct user_pt_regs instead of struct pt_regs to userspace */
- #define PT_REGS_PARM1(x) ((x)->regs[4])
- #define PT_REGS_PARM2(x) ((x)->regs[5])
- #define PT_REGS_PARM3(x) ((x)->regs[6])
- #define PT_REGS_PARM4(x) ((x)->regs[7])
- #define PT_REGS_PARM5(x) ((x)->regs[8])
- #define PT_REGS_RET(x) ((x)->regs[31])
- #define PT_REGS_FP(x) ((x)->regs[30]) /* Works only with CONFIG_FRAME_POINTER */
- #define PT_REGS_RC(x) ((x)->regs[2])
- #define PT_REGS_SP(x) ((x)->regs[29])
- #define PT_REGS_IP(x) ((x)->cp0_epc)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), regs[4])
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), regs[5])
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), regs[6])
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), regs[7])
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), regs[8])
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), regs[31])
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((x), regs[30])
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), regs[2])
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), regs[29])
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), cp0_epc)
++#define __PT_REGS_CAST(x) ((const struct user_pt_regs *)(x))
++#define __PT_PARM1_REG regs[0]
++#define __PT_PARM2_REG regs[1]
++#define __PT_PARM3_REG regs[2]
++#define __PT_PARM4_REG regs[3]
++#define __PT_PARM5_REG regs[4]
++#define __PT_RET_REG regs[30]
++#define __PT_FP_REG regs[29]  /* Works only with CONFIG_FRAME_POINTER */
++#define __PT_RC_REG regs[0]
++#define __PT_SP_REG sp
++#define __PT_IP_REG pc
 +
 +#elif defined(bpf_target_mips)
 +
- #define PT_REGS_PARM1(x) ((x)->gpr[3])
- #define PT_REGS_PARM2(x) ((x)->gpr[4])
- #define PT_REGS_PARM3(x) ((x)->gpr[5])
- #define PT_REGS_PARM4(x) ((x)->gpr[6])
- #define PT_REGS_PARM5(x) ((x)->gpr[7])
- #define PT_REGS_RC(x) ((x)->gpr[3])
- #define PT_REGS_SP(x) ((x)->sp)
- #define PT_REGS_IP(x) ((x)->nip)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), gpr[3])
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), gpr[4])
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), gpr[5])
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), gpr[6])
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), gpr[7])
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), gpr[3])
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), sp)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), nip)
++#define __PT_PARM1_REG regs[4]
++#define __PT_PARM2_REG regs[5]
++#define __PT_PARM3_REG regs[6]
++#define __PT_PARM4_REG regs[7]
++#define __PT_PARM5_REG regs[8]
++#define __PT_RET_REG regs[31]
++#define __PT_FP_REG regs[30]  /* Works only with CONFIG_FRAME_POINTER */
++#define __PT_RC_REG regs[2]
++#define __PT_SP_REG regs[29]
++#define __PT_IP_REG cp0_epc
 +
 +#elif defined(bpf_target_powerpc)
 +
- #define PT_REGS_PARM1(x) ((x)->u_regs[UREG_I0])
- #define PT_REGS_PARM2(x) ((x)->u_regs[UREG_I1])
- #define PT_REGS_PARM3(x) ((x)->u_regs[UREG_I2])
- #define PT_REGS_PARM4(x) ((x)->u_regs[UREG_I3])
- #define PT_REGS_PARM5(x) ((x)->u_regs[UREG_I4])
- #define PT_REGS_RET(x) ((x)->u_regs[UREG_I7])
- #define PT_REGS_RC(x) ((x)->u_regs[UREG_I0])
- #define PT_REGS_SP(x) ((x)->u_regs[UREG_FP])
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I0])
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I1])
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I2])
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I3])
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I4])
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I7])
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((x), u_regs[UREG_I0])
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((x), u_regs[UREG_FP])
++#define __PT_PARM1_REG gpr[3]
++#define __PT_PARM2_REG gpr[4]
++#define __PT_PARM3_REG gpr[5]
++#define __PT_PARM4_REG gpr[6]
++#define __PT_PARM5_REG gpr[7]
++#define __PT_RET_REG regs[31]
++#define __PT_FP_REG __unsupported__
++#define __PT_RC_REG gpr[3]
++#define __PT_SP_REG sp
++#define __PT_IP_REG nip
 +
 +#elif defined(bpf_target_sparc)
 +
- #define PT_REGS_IP(x) ((x)->tpc)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), tpc)
++#define __PT_PARM1_REG u_regs[UREG_I0]
++#define __PT_PARM2_REG u_regs[UREG_I1]
++#define __PT_PARM3_REG u_regs[UREG_I2]
++#define __PT_PARM4_REG u_regs[UREG_I3]
++#define __PT_PARM5_REG u_regs[UREG_I4]
++#define __PT_RET_REG u_regs[UREG_I7]
++#define __PT_FP_REG __unsupported__
++#define __PT_RC_REG u_regs[UREG_I0]
++#define __PT_SP_REG u_regs[UREG_FP]
 +/* Should this also be a bpf_target check for the sparc case? */
 +#if defined(__arch64__)
- #define PT_REGS_IP(x) ((x)->pc)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((x), pc)
++#define __PT_IP_REG tpc
 +#else
- #define PT_REGS_RV const volatile struct user_regs_struct
- #define PT_REGS_PARM1(x) (((PT_REGS_RV *)(x))->a0)
- #define PT_REGS_PARM2(x) (((PT_REGS_RV *)(x))->a1)
- #define PT_REGS_PARM3(x) (((PT_REGS_RV *)(x))->a2)
- #define PT_REGS_PARM4(x) (((PT_REGS_RV *)(x))->a3)
- #define PT_REGS_PARM5(x) (((PT_REGS_RV *)(x))->a4)
- #define PT_REGS_RET(x) (((PT_REGS_RV *)(x))->ra)
- #define PT_REGS_FP(x) (((PT_REGS_RV *)(x))->s5)
- #define PT_REGS_RC(x) (((PT_REGS_RV *)(x))->a5)
- #define PT_REGS_SP(x) (((PT_REGS_RV *)(x))->sp)
- #define PT_REGS_IP(x) (((PT_REGS_RV *)(x))->epc)
- #define PT_REGS_PARM1_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a0)
- #define PT_REGS_PARM2_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a1)
- #define PT_REGS_PARM3_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a2)
- #define PT_REGS_PARM4_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a3)
- #define PT_REGS_PARM5_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a4)
- #define PT_REGS_RET_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), ra)
- #define PT_REGS_FP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), fp)
- #define PT_REGS_RC_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), a5)
- #define PT_REGS_SP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), sp)
- #define PT_REGS_IP_CORE(x) BPF_CORE_READ((PT_REGS_RV *)(x), epc)
++#define __PT_IP_REG pc
 +#endif
 +
 +#elif defined(bpf_target_riscv)
 +
++#define __PT_REGS_CAST(x) ((const struct user_regs_struct *)(x))
++#define __PT_PARM1_REG a0
++#define __PT_PARM2_REG a1
++#define __PT_PARM3_REG a2
++#define __PT_PARM4_REG a3
++#define __PT_PARM5_REG a4
++#define __PT_RET_REG ra
++#define __PT_FP_REG fp
++#define __PT_RC_REG a5
++#define __PT_SP_REG sp
++#define __PT_IP_REG epc
++
++#endif
++
++#if defined(bpf_target_defined)
++
 +struct pt_regs;
- #elif defined(bpf_target_defined)
 +
++/* allow some architecutres to override `struct pt_regs` */
++#ifndef __PT_REGS_CAST
++#define __PT_REGS_CAST(x) (x)
 +#endif
 +
++#define PT_REGS_PARM1(x) (__PT_REGS_CAST(x)->__PT_PARM1_REG)
++#define PT_REGS_PARM2(x) (__PT_REGS_CAST(x)->__PT_PARM2_REG)
++#define PT_REGS_PARM3(x) (__PT_REGS_CAST(x)->__PT_PARM3_REG)
++#define PT_REGS_PARM4(x) (__PT_REGS_CAST(x)->__PT_PARM4_REG)
++#define PT_REGS_PARM5(x) (__PT_REGS_CAST(x)->__PT_PARM5_REG)
++#define PT_REGS_RET(x) (__PT_REGS_CAST(x)->__PT_RET_REG)
++#define PT_REGS_FP(x) (__PT_REGS_CAST(x)->__PT_FP_REG)
++#define PT_REGS_RC(x) (__PT_REGS_CAST(x)->__PT_RC_REG)
++#define PT_REGS_SP(x) (__PT_REGS_CAST(x)->__PT_SP_REG)
++#define PT_REGS_IP(x) (__PT_REGS_CAST(x)->__PT_IP_REG)
++
++#define PT_REGS_PARM1_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM1_REG)
++#define PT_REGS_PARM2_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM2_REG)
++#define PT_REGS_PARM3_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM3_REG)
++#define PT_REGS_PARM4_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM4_REG)
++#define PT_REGS_PARM5_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_PARM5_REG)
++#define PT_REGS_RET_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RET_REG)
++#define PT_REGS_FP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_FP_REG)
++#define PT_REGS_RC_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_RC_REG)
++#define PT_REGS_SP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_SP_REG)
++#define PT_REGS_IP_CORE(x) BPF_CORE_READ(__PT_REGS_CAST(x), __PT_IP_REG)
++
 +#if defined(bpf_target_powerpc)
++
 +#define BPF_KPROBE_READ_RET_IP(ip, ctx)               ({ (ip) = (ctx)->link; })
 +#define BPF_KRETPROBE_READ_RET_IP             BPF_KPROBE_READ_RET_IP
++
 +#elif defined(bpf_target_sparc)
++
 +#define BPF_KPROBE_READ_RET_IP(ip, ctx)               ({ (ip) = PT_REGS_RET(ctx); })
 +#define BPF_KRETPROBE_READ_RET_IP             BPF_KPROBE_READ_RET_IP
-       ({ bpf_probe_read_kernel(&(ip), sizeof(ip),                         \
-                         (void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
++
++#else
++
 +#define BPF_KPROBE_READ_RET_IP(ip, ctx)                                           \
 +      ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)PT_REGS_RET(ctx)); })
 +#define BPF_KRETPROBE_READ_RET_IP(ip, ctx)                                \
- #if !defined(bpf_target_defined)
++      ({ bpf_probe_read_kernel(&(ip), sizeof(ip), (void *)(PT_REGS_FP(ctx) + sizeof(ip))); })
++
 +#endif
 +
- #endif /* !defined(bpf_target_defined) */
++#else /* defined(bpf_target_defined) */
 +
 +#define PT_REGS_PARM1(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM2(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM3(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM4(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM5(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_RET(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_FP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_RC(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_SP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_IP(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +
 +#define PT_REGS_PARM1_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM2_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM3_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM4_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_PARM5_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_RET_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_FP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_RC_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_SP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define PT_REGS_IP_CORE(x) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +
 +#define BPF_KPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +#define BPF_KRETPROBE_READ_RET_IP(ip, ctx) ({ _Pragma(__BPF_TARGET_MISSING); 0l; })
 +
- #define ___bpf_narg(...) \
-       ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
++#endif /* defined(bpf_target_defined) */
 +
 +#ifndef ___bpf_concat
 +#define ___bpf_concat(a, b) a ## b
 +#endif
 +#ifndef ___bpf_apply
 +#define ___bpf_apply(fn, n) ___bpf_concat(fn, n)
 +#endif
 +#ifndef ___bpf_nth
 +#define ___bpf_nth(_, _1, _2, _3, _4, _5, _6, _7, _8, _9, _a, _b, _c, N, ...) N
 +#endif
 +#ifndef ___bpf_narg
- #define ___bpf_ctx_cast0() ctx
- #define ___bpf_ctx_cast1(x) ___bpf_ctx_cast0(), (void *)ctx[0]
- #define ___bpf_ctx_cast2(x, args...) ___bpf_ctx_cast1(args), (void *)ctx[1]
- #define ___bpf_ctx_cast3(x, args...) ___bpf_ctx_cast2(args), (void *)ctx[2]
- #define ___bpf_ctx_cast4(x, args...) ___bpf_ctx_cast3(args), (void *)ctx[3]
- #define ___bpf_ctx_cast5(x, args...) ___bpf_ctx_cast4(args), (void *)ctx[4]
- #define ___bpf_ctx_cast6(x, args...) ___bpf_ctx_cast5(args), (void *)ctx[5]
- #define ___bpf_ctx_cast7(x, args...) ___bpf_ctx_cast6(args), (void *)ctx[6]
- #define ___bpf_ctx_cast8(x, args...) ___bpf_ctx_cast7(args), (void *)ctx[7]
- #define ___bpf_ctx_cast9(x, args...) ___bpf_ctx_cast8(args), (void *)ctx[8]
++#define ___bpf_narg(...) ___bpf_nth(_, ##__VA_ARGS__, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
 +#endif
 +
- #define ___bpf_ctx_cast(args...) \
-       ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
++#define ___bpf_ctx_cast0()            ctx
++#define ___bpf_ctx_cast1(x)           ___bpf_ctx_cast0(), (void *)ctx[0]
++#define ___bpf_ctx_cast2(x, args...)  ___bpf_ctx_cast1(args), (void *)ctx[1]
++#define ___bpf_ctx_cast3(x, args...)  ___bpf_ctx_cast2(args), (void *)ctx[2]
++#define ___bpf_ctx_cast4(x, args...)  ___bpf_ctx_cast3(args), (void *)ctx[3]
++#define ___bpf_ctx_cast5(x, args...)  ___bpf_ctx_cast4(args), (void *)ctx[4]
++#define ___bpf_ctx_cast6(x, args...)  ___bpf_ctx_cast5(args), (void *)ctx[5]
++#define ___bpf_ctx_cast7(x, args...)  ___bpf_ctx_cast6(args), (void *)ctx[6]
++#define ___bpf_ctx_cast8(x, args...)  ___bpf_ctx_cast7(args), (void *)ctx[7]
++#define ___bpf_ctx_cast9(x, args...)  ___bpf_ctx_cast8(args), (void *)ctx[8]
 +#define ___bpf_ctx_cast10(x, args...) ___bpf_ctx_cast9(args), (void *)ctx[9]
 +#define ___bpf_ctx_cast11(x, args...) ___bpf_ctx_cast10(args), (void *)ctx[10]
 +#define ___bpf_ctx_cast12(x, args...) ___bpf_ctx_cast11(args), (void *)ctx[11]
- #define ___bpf_kprobe_args0() ctx
- #define ___bpf_kprobe_args1(x) \
-       ___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)
- #define ___bpf_kprobe_args2(x, args...) \
-       ___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)
- #define ___bpf_kprobe_args3(x, args...) \
-       ___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)
- #define ___bpf_kprobe_args4(x, args...) \
-       ___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)
- #define ___bpf_kprobe_args5(x, args...) \
-       ___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)
- #define ___bpf_kprobe_args(args...) \
-       ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)
++#define ___bpf_ctx_cast(args...)      ___bpf_apply(___bpf_ctx_cast, ___bpf_narg(args))(args)
 +
 +/*
 + * BPF_PROG is a convenience wrapper for generic tp_btf/fentry/fexit and
 + * similar kinds of BPF programs, that accept input arguments as a single
 + * pointer to untyped u64 array, where each u64 can actually be a typed
 + * pointer or integer of different size. Instead of requring user to write
 + * manual casts and work with array elements by index, BPF_PROG macro
 + * allows user to declare a list of named and typed input arguments in the
 + * same syntax as for normal C function. All the casting is hidden and
 + * performed transparently, while user code can just assume working with
 + * function arguments of specified type and name.
 + *
 + * Original raw context argument is preserved as well as 'ctx' argument.
 + * This is useful when using BPF helpers that expect original context
 + * as one of the parameters (e.g., for bpf_perf_event_output()).
 + */
 +#define BPF_PROG(name, args...)                                                   \
 +name(unsigned long long *ctx);                                                    \
 +static __attribute__((always_inline)) typeof(name(0))                     \
 +____##name(unsigned long long *ctx, ##args);                              \
 +typeof(name(0)) name(unsigned long long *ctx)                             \
 +{                                                                         \
 +      _Pragma("GCC diagnostic push")                                      \
 +      _Pragma("GCC diagnostic ignored \"-Wint-conversion\"")              \
 +      return ____##name(___bpf_ctx_cast(args));                           \
 +      _Pragma("GCC diagnostic pop")                                       \
 +}                                                                         \
 +static __attribute__((always_inline)) typeof(name(0))                     \
 +____##name(unsigned long long *ctx, ##args)
 +
 +struct pt_regs;
 +
- #define ___bpf_kretprobe_args0() ctx
- #define ___bpf_kretprobe_args1(x) \
-       ___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx)
- #define ___bpf_kretprobe_args(args...) \
-       ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args)
++#define ___bpf_kprobe_args0()           ctx
++#define ___bpf_kprobe_args1(x)          ___bpf_kprobe_args0(), (void *)PT_REGS_PARM1(ctx)
++#define ___bpf_kprobe_args2(x, args...) ___bpf_kprobe_args1(args), (void *)PT_REGS_PARM2(ctx)
++#define ___bpf_kprobe_args3(x, args...) ___bpf_kprobe_args2(args), (void *)PT_REGS_PARM3(ctx)
++#define ___bpf_kprobe_args4(x, args...) ___bpf_kprobe_args3(args), (void *)PT_REGS_PARM4(ctx)
++#define ___bpf_kprobe_args5(x, args...) ___bpf_kprobe_args4(args), (void *)PT_REGS_PARM5(ctx)
++#define ___bpf_kprobe_args(args...)     ___bpf_apply(___bpf_kprobe_args, ___bpf_narg(args))(args)
 +
 +/*
 + * BPF_KPROBE serves the same purpose for kprobes as BPF_PROG for
 + * tp_btf/fentry/fexit BPF programs. It hides the underlying platform-specific
 + * low-level way of getting kprobe input arguments from struct pt_regs, and
 + * provides a familiar typed and named function arguments syntax and
 + * semantics of accessing kprobe input paremeters.
 + *
 + * Original struct pt_regs* context is preserved as 'ctx' argument. This might
 + * be necessary when using BPF helpers like bpf_perf_event_output().
 + */
 +#define BPF_KPROBE(name, args...)                                         \
 +name(struct pt_regs *ctx);                                                \
 +static __attribute__((always_inline)) typeof(name(0))                     \
 +____##name(struct pt_regs *ctx, ##args);                                  \
 +typeof(name(0)) name(struct pt_regs *ctx)                                 \
 +{                                                                         \
 +      _Pragma("GCC diagnostic push")                                      \
 +      _Pragma("GCC diagnostic ignored \"-Wint-conversion\"")              \
 +      return ____##name(___bpf_kprobe_args(args));                        \
 +      _Pragma("GCC diagnostic pop")                                       \
 +}                                                                         \
 +static __attribute__((always_inline)) typeof(name(0))                     \
 +____##name(struct pt_regs *ctx, ##args)
 +
++#define ___bpf_kretprobe_args0()       ctx
++#define ___bpf_kretprobe_args1(x)      ___bpf_kretprobe_args0(), (void *)PT_REGS_RC(ctx)
++#define ___bpf_kretprobe_args(args...) ___bpf_apply(___bpf_kretprobe_args, ___bpf_narg(args))(args)
 +
 +/*
 + * BPF_KRETPROBE is similar to BPF_KPROBE, except, it only provides optional
 + * return value (in addition to `struct pt_regs *ctx`), but no input
 + * arguments, because they will be clobbered by the time probed function
 + * returns.
 + */
 +#define BPF_KRETPROBE(name, args...)                                      \
 +name(struct pt_regs *ctx);                                                \
 +static __attribute__((always_inline)) typeof(name(0))                     \
 +____##name(struct pt_regs *ctx, ##args);                                  \
 +typeof(name(0)) name(struct pt_regs *ctx)                                 \
 +{                                                                         \
 +      _Pragma("GCC diagnostic push")                                      \
 +      _Pragma("GCC diagnostic ignored \"-Wint-conversion\"")              \
 +      return ____##name(___bpf_kretprobe_args(args));                     \
 +      _Pragma("GCC diagnostic pop")                                       \
 +}                                                                         \
 +static __always_inline typeof(name(0)) ____##name(struct pt_regs *ctx, ##args)
 +
 +#endif
index 742a2bf,0000000..061839f
mode 100644,000000..100644
--- /dev/null
@@@ -1,579 -1,0 +1,585 @@@
 +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
 +/* Copyright (c) 2018 Facebook */
 +/*! \file */
 +
 +#ifndef __LIBBPF_BTF_H
 +#define __LIBBPF_BTF_H
 +
 +#include <stdarg.h>
 +#include <stdbool.h>
 +#include <linux/btf.h>
 +#include <linux/types.h>
 +
 +#include "libbpf_common.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +#define BTF_ELF_SEC ".BTF"
 +#define BTF_EXT_ELF_SEC ".BTF.ext"
 +#define MAPS_ELF_SEC ".maps"
 +
 +struct btf;
 +struct btf_ext;
 +struct btf_type;
 +
 +struct bpf_object;
 +
 +enum btf_endianness {
 +      BTF_LITTLE_ENDIAN = 0,
 +      BTF_BIG_ENDIAN = 1,
 +};
 +
 +/**
 + * @brief **btf__free()** frees all data of a BTF object
 + * @param btf BTF object to free
 + */
 +LIBBPF_API void btf__free(struct btf *btf);
 +
 +/**
 + * @brief **btf__new()** creates a new instance of a BTF object from the raw
 + * bytes of an ELF's BTF section
 + * @param data raw bytes
 + * @param size number of bytes passed in `data`
 + * @return new BTF object instance which has to be eventually freed with
 + * **btf__free()**
 + *
 + * On error, error-code-encoded-as-pointer is returned, not a NULL. To extract
 + * error code from such a pointer `libbpf_get_error()` should be used. If
 + * `libbpf_set_strict_mode(LIBBPF_STRICT_CLEAN_PTRS)` is enabled, NULL is
 + * returned on error instead. In both cases thread-local `errno` variable is
 + * always set to error code as well.
 + */
 +LIBBPF_API struct btf *btf__new(const void *data, __u32 size);
 +
 +/**
 + * @brief **btf__new_split()** create a new instance of a BTF object from the
 + * provided raw data bytes. It takes another BTF instance, **base_btf**, which
 + * serves as a base BTF, which is extended by types in a newly created BTF
 + * instance
 + * @param data raw bytes
 + * @param size length of raw bytes
 + * @param base_btf the base BTF object
 + * @return new BTF object instance which has to be eventually freed with
 + * **btf__free()**
 + *
 + * If *base_btf* is NULL, `btf__new_split()` is equivalent to `btf__new()` and
 + * creates non-split BTF.
 + *
 + * On error, error-code-encoded-as-pointer is returned, not a NULL. To extract
 + * error code from such a pointer `libbpf_get_error()` should be used. If
 + * `libbpf_set_strict_mode(LIBBPF_STRICT_CLEAN_PTRS)` is enabled, NULL is
 + * returned on error instead. In both cases thread-local `errno` variable is
 + * always set to error code as well.
 + */
 +LIBBPF_API struct btf *btf__new_split(const void *data, __u32 size, struct btf *base_btf);
 +
 +/**
 + * @brief **btf__new_empty()** creates an empty BTF object.  Use
 + * `btf__add_*()` to populate such BTF object.
 + * @return new BTF object instance which has to be eventually freed with
 + * **btf__free()**
 + *
 + * On error, error-code-encoded-as-pointer is returned, not a NULL. To extract
 + * error code from such a pointer `libbpf_get_error()` should be used. If
 + * `libbpf_set_strict_mode(LIBBPF_STRICT_CLEAN_PTRS)` is enabled, NULL is
 + * returned on error instead. In both cases thread-local `errno` variable is
 + * always set to error code as well.
 + */
 +LIBBPF_API struct btf *btf__new_empty(void);
 +
 +/**
 + * @brief **btf__new_empty_split()** creates an unpopulated BTF object from an
 + * ELF BTF section except with a base BTF on top of which split BTF should be
 + * based
 + * @return new BTF object instance which has to be eventually freed with
 + * **btf__free()**
 + *
 + * If *base_btf* is NULL, `btf__new_empty_split()` is equivalent to
 + * `btf__new_empty()` and creates non-split BTF.
 + *
 + * On error, error-code-encoded-as-pointer is returned, not a NULL. To extract
 + * error code from such a pointer `libbpf_get_error()` should be used. If
 + * `libbpf_set_strict_mode(LIBBPF_STRICT_CLEAN_PTRS)` is enabled, NULL is
 + * returned on error instead. In both cases thread-local `errno` variable is
 + * always set to error code as well.
 + */
 +LIBBPF_API struct btf *btf__new_empty_split(struct btf *base_btf);
 +
 +LIBBPF_API struct btf *btf__parse(const char *path, struct btf_ext **btf_ext);
 +LIBBPF_API struct btf *btf__parse_split(const char *path, struct btf *base_btf);
 +LIBBPF_API struct btf *btf__parse_elf(const char *path, struct btf_ext **btf_ext);
 +LIBBPF_API struct btf *btf__parse_elf_split(const char *path, struct btf *base_btf);
 +LIBBPF_API struct btf *btf__parse_raw(const char *path);
 +LIBBPF_API struct btf *btf__parse_raw_split(const char *path, struct btf *base_btf);
 +
 +LIBBPF_API struct btf *btf__load_vmlinux_btf(void);
 +LIBBPF_API struct btf *btf__load_module_btf(const char *module_name, struct btf *vmlinux_btf);
 +LIBBPF_API struct btf *libbpf_find_kernel_btf(void);
 +
 +LIBBPF_API struct btf *btf__load_from_kernel_by_id(__u32 id);
 +LIBBPF_API struct btf *btf__load_from_kernel_by_id_split(__u32 id, struct btf *base_btf);
 +LIBBPF_DEPRECATED_SINCE(0, 6, "use btf__load_from_kernel_by_id instead")
 +LIBBPF_API int btf__get_from_id(__u32 id, struct btf **btf);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 6, "intended for internal libbpf use only")
 +LIBBPF_API int btf__finalize_data(struct bpf_object *obj, struct btf *btf);
 +LIBBPF_DEPRECATED_SINCE(0, 6, "use btf__load_into_kernel instead")
 +LIBBPF_API int btf__load(struct btf *btf);
 +LIBBPF_API int btf__load_into_kernel(struct btf *btf);
 +LIBBPF_API __s32 btf__find_by_name(const struct btf *btf,
 +                                 const char *type_name);
 +LIBBPF_API __s32 btf__find_by_name_kind(const struct btf *btf,
 +                                      const char *type_name, __u32 kind);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use btf__type_cnt() instead; note that btf__get_nr_types() == btf__type_cnt() - 1")
 +LIBBPF_API __u32 btf__get_nr_types(const struct btf *btf);
 +LIBBPF_API __u32 btf__type_cnt(const struct btf *btf);
 +LIBBPF_API const struct btf *btf__base_btf(const struct btf *btf);
 +LIBBPF_API const struct btf_type *btf__type_by_id(const struct btf *btf,
 +                                                __u32 id);
 +LIBBPF_API size_t btf__pointer_size(const struct btf *btf);
 +LIBBPF_API int btf__set_pointer_size(struct btf *btf, size_t ptr_sz);
 +LIBBPF_API enum btf_endianness btf__endianness(const struct btf *btf);
 +LIBBPF_API int btf__set_endianness(struct btf *btf, enum btf_endianness endian);
 +LIBBPF_API __s64 btf__resolve_size(const struct btf *btf, __u32 type_id);
 +LIBBPF_API int btf__resolve_type(const struct btf *btf, __u32 type_id);
 +LIBBPF_API int btf__align_of(const struct btf *btf, __u32 id);
 +LIBBPF_API int btf__fd(const struct btf *btf);
 +LIBBPF_API void btf__set_fd(struct btf *btf, int fd);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use btf__raw_data() instead")
 +LIBBPF_API const void *btf__get_raw_data(const struct btf *btf, __u32 *size);
 +LIBBPF_API const void *btf__raw_data(const struct btf *btf, __u32 *size);
 +LIBBPF_API const char *btf__name_by_offset(const struct btf *btf, __u32 offset);
 +LIBBPF_API const char *btf__str_by_offset(const struct btf *btf, __u32 offset);
 +LIBBPF_API int btf__get_map_kv_tids(const struct btf *btf, const char *map_name,
 +                                  __u32 expected_key_size,
 +                                  __u32 expected_value_size,
 +                                  __u32 *key_type_id, __u32 *value_type_id);
 +
 +LIBBPF_API struct btf_ext *btf_ext__new(const __u8 *data, __u32 size);
 +LIBBPF_API void btf_ext__free(struct btf_ext *btf_ext);
 +LIBBPF_API const void *btf_ext__get_raw_data(const struct btf_ext *btf_ext,
 +                                           __u32 *size);
 +LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_func_info was never meant as a public API and has wrong assumptions embedded in it; it will be removed in the future libbpf versions")
 +int btf_ext__reloc_func_info(const struct btf *btf,
 +                           const struct btf_ext *btf_ext,
 +                           const char *sec_name, __u32 insns_cnt,
 +                           void **func_info, __u32 *cnt);
 +LIBBPF_API LIBBPF_DEPRECATED("btf_ext__reloc_line_info was never meant as a public API and has wrong assumptions embedded in it; it will be removed in the future libbpf versions")
 +int btf_ext__reloc_line_info(const struct btf *btf,
 +                           const struct btf_ext *btf_ext,
 +                           const char *sec_name, __u32 insns_cnt,
 +                           void **line_info, __u32 *cnt);
 +LIBBPF_API __u32 btf_ext__func_info_rec_size(const struct btf_ext *btf_ext);
 +LIBBPF_API __u32 btf_ext__line_info_rec_size(const struct btf_ext *btf_ext);
 +
 +LIBBPF_API int btf__find_str(struct btf *btf, const char *s);
 +LIBBPF_API int btf__add_str(struct btf *btf, const char *s);
 +LIBBPF_API int btf__add_type(struct btf *btf, const struct btf *src_btf,
 +                           const struct btf_type *src_type);
 +/**
 + * @brief **btf__add_btf()** appends all the BTF types from *src_btf* into *btf*
 + * @param btf BTF object which all the BTF types and strings are added to
 + * @param src_btf BTF object which all BTF types and referenced strings are copied from
 + * @return BTF type ID of the first appended BTF type, or negative error code
 + *
 + * **btf__add_btf()** can be used to simply and efficiently append the entire
 + * contents of one BTF object to another one. All the BTF type data is copied
 + * over, all referenced type IDs are adjusted by adding a necessary ID offset.
 + * Only strings referenced from BTF types are copied over and deduplicated, so
 + * if there were some unused strings in *src_btf*, those won't be copied over,
 + * which is consistent with the general string deduplication semantics of BTF
 + * writing APIs.
 + *
 + * If any error is encountered during this process, the contents of *btf* is
 + * left intact, which means that **btf__add_btf()** follows the transactional
 + * semantics and the operation as a whole is all-or-nothing.
 + *
 + * *src_btf* has to be non-split BTF, as of now copying types from split BTF
 + * is not supported and will result in -ENOTSUP error code returned.
 + */
 +LIBBPF_API int btf__add_btf(struct btf *btf, const struct btf *src_btf);
 +
 +LIBBPF_API int btf__add_int(struct btf *btf, const char *name, size_t byte_sz, int encoding);
 +LIBBPF_API int btf__add_float(struct btf *btf, const char *name, size_t byte_sz);
 +LIBBPF_API int btf__add_ptr(struct btf *btf, int ref_type_id);
 +LIBBPF_API int btf__add_array(struct btf *btf,
 +                            int index_type_id, int elem_type_id, __u32 nr_elems);
 +/* struct/union construction APIs */
 +LIBBPF_API int btf__add_struct(struct btf *btf, const char *name, __u32 sz);
 +LIBBPF_API int btf__add_union(struct btf *btf, const char *name, __u32 sz);
 +LIBBPF_API int btf__add_field(struct btf *btf, const char *name, int field_type_id,
 +                            __u32 bit_offset, __u32 bit_size);
 +
 +/* enum construction APIs */
 +LIBBPF_API int btf__add_enum(struct btf *btf, const char *name, __u32 bytes_sz);
 +LIBBPF_API int btf__add_enum_value(struct btf *btf, const char *name, __s64 value);
 +
 +enum btf_fwd_kind {
 +      BTF_FWD_STRUCT = 0,
 +      BTF_FWD_UNION = 1,
 +      BTF_FWD_ENUM = 2,
 +};
 +
 +LIBBPF_API int btf__add_fwd(struct btf *btf, const char *name, enum btf_fwd_kind fwd_kind);
 +LIBBPF_API int btf__add_typedef(struct btf *btf, const char *name, int ref_type_id);
 +LIBBPF_API int btf__add_volatile(struct btf *btf, int ref_type_id);
 +LIBBPF_API int btf__add_const(struct btf *btf, int ref_type_id);
 +LIBBPF_API int btf__add_restrict(struct btf *btf, int ref_type_id);
 +LIBBPF_API int btf__add_type_tag(struct btf *btf, const char *value, int ref_type_id);
 +
 +/* func and func_proto construction APIs */
 +LIBBPF_API int btf__add_func(struct btf *btf, const char *name,
 +                           enum btf_func_linkage linkage, int proto_type_id);
 +LIBBPF_API int btf__add_func_proto(struct btf *btf, int ret_type_id);
 +LIBBPF_API int btf__add_func_param(struct btf *btf, const char *name, int type_id);
 +
 +/* var & datasec construction APIs */
 +LIBBPF_API int btf__add_var(struct btf *btf, const char *name, int linkage, int type_id);
 +LIBBPF_API int btf__add_datasec(struct btf *btf, const char *name, __u32 byte_sz);
 +LIBBPF_API int btf__add_datasec_var_info(struct btf *btf, int var_type_id,
 +                                       __u32 offset, __u32 byte_sz);
 +
 +/* tag construction API */
 +LIBBPF_API int btf__add_decl_tag(struct btf *btf, const char *value, int ref_type_id,
 +                          int component_idx);
 +
 +struct btf_dedup_opts {
 +      size_t sz;
 +      /* optional .BTF.ext info to dedup along the main BTF info */
 +      struct btf_ext *btf_ext;
 +      /* force hash collisions (used for testing) */
 +      bool force_collisions;
 +      size_t :0;
 +};
 +#define btf_dedup_opts__last_field force_collisions
 +
 +LIBBPF_API int btf__dedup(struct btf *btf, const struct btf_dedup_opts *opts);
 +
 +LIBBPF_API int btf__dedup_v0_6_0(struct btf *btf, const struct btf_dedup_opts *opts);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use btf__dedup() instead")
 +LIBBPF_API int btf__dedup_deprecated(struct btf *btf, struct btf_ext *btf_ext, const void *opts);
 +#define btf__dedup(...) ___libbpf_overload(___btf_dedup, __VA_ARGS__)
 +#define ___btf_dedup3(btf, btf_ext, opts) btf__dedup_deprecated(btf, btf_ext, opts)
 +#define ___btf_dedup2(btf, opts) btf__dedup(btf, opts)
 +
 +struct btf_dump;
 +
 +struct btf_dump_opts {
 +      union {
 +              size_t sz;
 +              void *ctx; /* DEPRECATED: will be gone in v1.0 */
 +      };
 +};
 +
 +typedef void (*btf_dump_printf_fn_t)(void *ctx, const char *fmt, va_list args);
 +
 +LIBBPF_API struct btf_dump *btf_dump__new(const struct btf *btf,
 +                                        btf_dump_printf_fn_t printf_fn,
 +                                        void *ctx,
 +                                        const struct btf_dump_opts *opts);
 +
 +LIBBPF_API struct btf_dump *btf_dump__new_v0_6_0(const struct btf *btf,
 +                                               btf_dump_printf_fn_t printf_fn,
 +                                               void *ctx,
 +                                               const struct btf_dump_opts *opts);
 +
 +LIBBPF_API struct btf_dump *btf_dump__new_deprecated(const struct btf *btf,
 +                                                   const struct btf_ext *btf_ext,
 +                                                   const struct btf_dump_opts *opts,
 +                                                   btf_dump_printf_fn_t printf_fn);
 +
 +/* Choose either btf_dump__new() or btf_dump__new_deprecated() based on the
 + * type of 4th argument. If it's btf_dump's print callback, use deprecated
 + * API; otherwise, choose the new btf_dump__new(). ___libbpf_override()
 + * doesn't work here because both variants have 4 input arguments.
 + *
 + * (void *) casts are necessary to avoid compilation warnings about type
 + * mismatches, because even though __builtin_choose_expr() only ever evaluates
 + * one side the other side still has to satisfy type constraints (this is
 + * compiler implementation limitation which might be lifted eventually,
 + * according to the documentation). So passing struct btf_ext in place of
 + * btf_dump_printf_fn_t would be generating compilation warning.  Casting to
 + * void * avoids this issue.
 + *
 + * Also, two type compatibility checks for a function and function pointer are
 + * required because passing function reference into btf_dump__new() as
 + * btf_dump__new(..., my_callback, ...) and as btf_dump__new(...,
 + * &my_callback, ...) (not explicit ampersand in the latter case) actually
 + * differs as far as __builtin_types_compatible_p() is concerned. Thus two
 + * checks are combined to detect callback argument.
 + *
 + * The rest works just like in case of ___libbpf_override() usage with symbol
 + * versioning.
++ *
++ * C++ compilers don't support __builtin_types_compatible_p(), so at least
++ * don't screw up compilation for them and let C++ users pick btf_dump__new
++ * vs btf_dump__new_deprecated explicitly.
 + */
++#ifndef __cplusplus
 +#define btf_dump__new(a1, a2, a3, a4) __builtin_choose_expr(                          \
 +      __builtin_types_compatible_p(typeof(a4), btf_dump_printf_fn_t) ||               \
 +      __builtin_types_compatible_p(typeof(a4), void(void *, const char *, va_list)),  \
 +      btf_dump__new_deprecated((void *)a1, (void *)a2, (void *)a3, (void *)a4),       \
 +      btf_dump__new((void *)a1, (void *)a2, (void *)a3, (void *)a4))
++#endif
 +
 +LIBBPF_API void btf_dump__free(struct btf_dump *d);
 +
 +LIBBPF_API int btf_dump__dump_type(struct btf_dump *d, __u32 id);
 +
 +struct btf_dump_emit_type_decl_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +      /* optional field name for type declaration, e.g.:
 +       * - struct my_struct <FNAME>
 +       * - void (*<FNAME>)(int)
 +       * - char (*<FNAME>)[123]
 +       */
 +      const char *field_name;
 +      /* extra indentation level (in number of tabs) to emit for multi-line
 +       * type declarations (e.g., anonymous struct); applies for lines
 +       * starting from the second one (first line is assumed to have
 +       * necessary indentation already
 +       */
 +      int indent_level;
 +      /* strip all the const/volatile/restrict mods */
 +      bool strip_mods;
 +      size_t :0;
 +};
 +#define btf_dump_emit_type_decl_opts__last_field strip_mods
 +
 +LIBBPF_API int
 +btf_dump__emit_type_decl(struct btf_dump *d, __u32 id,
 +                       const struct btf_dump_emit_type_decl_opts *opts);
 +
 +
 +struct btf_dump_type_data_opts {
 +      /* size of this struct, for forward/backward compatibility */
 +      size_t sz;
 +      const char *indent_str;
 +      int indent_level;
 +      /* below match "show" flags for bpf_show_snprintf() */
 +      bool compact;           /* no newlines/indentation */
 +      bool skip_names;        /* skip member/type names */
 +      bool emit_zeroes;       /* show 0-valued fields */
 +      size_t :0;
 +};
 +#define btf_dump_type_data_opts__last_field emit_zeroes
 +
 +LIBBPF_API int
 +btf_dump__dump_type_data(struct btf_dump *d, __u32 id,
 +                       const void *data, size_t data_sz,
 +                       const struct btf_dump_type_data_opts *opts);
 +
 +/*
 + * A set of helpers for easier BTF types handling
 + */
 +static inline __u16 btf_kind(const struct btf_type *t)
 +{
 +      return BTF_INFO_KIND(t->info);
 +}
 +
 +static inline __u16 btf_vlen(const struct btf_type *t)
 +{
 +      return BTF_INFO_VLEN(t->info);
 +}
 +
 +static inline bool btf_kflag(const struct btf_type *t)
 +{
 +      return BTF_INFO_KFLAG(t->info);
 +}
 +
 +static inline bool btf_is_void(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_UNKN;
 +}
 +
 +static inline bool btf_is_int(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_INT;
 +}
 +
 +static inline bool btf_is_ptr(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_PTR;
 +}
 +
 +static inline bool btf_is_array(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_ARRAY;
 +}
 +
 +static inline bool btf_is_struct(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_STRUCT;
 +}
 +
 +static inline bool btf_is_union(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_UNION;
 +}
 +
 +static inline bool btf_is_composite(const struct btf_type *t)
 +{
 +      __u16 kind = btf_kind(t);
 +
 +      return kind == BTF_KIND_STRUCT || kind == BTF_KIND_UNION;
 +}
 +
 +static inline bool btf_is_enum(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_ENUM;
 +}
 +
 +static inline bool btf_is_fwd(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_FWD;
 +}
 +
 +static inline bool btf_is_typedef(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_TYPEDEF;
 +}
 +
 +static inline bool btf_is_volatile(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_VOLATILE;
 +}
 +
 +static inline bool btf_is_const(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_CONST;
 +}
 +
 +static inline bool btf_is_restrict(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_RESTRICT;
 +}
 +
 +static inline bool btf_is_mod(const struct btf_type *t)
 +{
 +      __u16 kind = btf_kind(t);
 +
 +      return kind == BTF_KIND_VOLATILE ||
 +             kind == BTF_KIND_CONST ||
 +             kind == BTF_KIND_RESTRICT ||
 +             kind == BTF_KIND_TYPE_TAG;
 +}
 +
 +static inline bool btf_is_func(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_FUNC;
 +}
 +
 +static inline bool btf_is_func_proto(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_FUNC_PROTO;
 +}
 +
 +static inline bool btf_is_var(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_VAR;
 +}
 +
 +static inline bool btf_is_datasec(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_DATASEC;
 +}
 +
 +static inline bool btf_is_float(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_FLOAT;
 +}
 +
 +static inline bool btf_is_decl_tag(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_DECL_TAG;
 +}
 +
 +static inline bool btf_is_type_tag(const struct btf_type *t)
 +{
 +      return btf_kind(t) == BTF_KIND_TYPE_TAG;
 +}
 +
 +static inline __u8 btf_int_encoding(const struct btf_type *t)
 +{
 +      return BTF_INT_ENCODING(*(__u32 *)(t + 1));
 +}
 +
 +static inline __u8 btf_int_offset(const struct btf_type *t)
 +{
 +      return BTF_INT_OFFSET(*(__u32 *)(t + 1));
 +}
 +
 +static inline __u8 btf_int_bits(const struct btf_type *t)
 +{
 +      return BTF_INT_BITS(*(__u32 *)(t + 1));
 +}
 +
 +static inline struct btf_array *btf_array(const struct btf_type *t)
 +{
 +      return (struct btf_array *)(t + 1);
 +}
 +
 +static inline struct btf_enum *btf_enum(const struct btf_type *t)
 +{
 +      return (struct btf_enum *)(t + 1);
 +}
 +
 +static inline struct btf_member *btf_members(const struct btf_type *t)
 +{
 +      return (struct btf_member *)(t + 1);
 +}
 +
 +/* Get bit offset of a member with specified index. */
 +static inline __u32 btf_member_bit_offset(const struct btf_type *t,
 +                                        __u32 member_idx)
 +{
 +      const struct btf_member *m = btf_members(t) + member_idx;
 +      bool kflag = btf_kflag(t);
 +
 +      return kflag ? BTF_MEMBER_BIT_OFFSET(m->offset) : m->offset;
 +}
 +/*
 + * Get bitfield size of a member, assuming t is BTF_KIND_STRUCT or
 + * BTF_KIND_UNION. If member is not a bitfield, zero is returned.
 + */
 +static inline __u32 btf_member_bitfield_size(const struct btf_type *t,
 +                                           __u32 member_idx)
 +{
 +      const struct btf_member *m = btf_members(t) + member_idx;
 +      bool kflag = btf_kflag(t);
 +
 +      return kflag ? BTF_MEMBER_BITFIELD_SIZE(m->offset) : 0;
 +}
 +
 +static inline struct btf_param *btf_params(const struct btf_type *t)
 +{
 +      return (struct btf_param *)(t + 1);
 +}
 +
 +static inline struct btf_var *btf_var(const struct btf_type *t)
 +{
 +      return (struct btf_var *)(t + 1);
 +}
 +
 +static inline struct btf_var_secinfo *
 +btf_var_secinfos(const struct btf_type *t)
 +{
 +      return (struct btf_var_secinfo *)(t + 1);
 +}
 +
 +struct btf_decl_tag;
 +static inline struct btf_decl_tag *btf_decl_tag(const struct btf_type *t)
 +{
 +      return (struct btf_decl_tag *)(t + 1);
 +}
 +
 +#ifdef __cplusplus
 +} /* extern "C" */
 +#endif
 +
 +#endif /* __LIBBPF_BTF_H */
index 45ce409,0000000..182f403
mode 100644,000000..100644
--- /dev/null
@@@ -1,11779 -1,0 +1,11813 @@@
- static __u32 get_kernel_version(void)
 +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 +
 +/*
 + * 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.
 + * Copyright (C) 2017 Nicira, Inc.
 + * Copyright (C) 2019 Isovalent, Inc.
 + */
 +
 +#ifndef _GNU_SOURCE
 +#define _GNU_SOURCE
 +#endif
 +#include <stdlib.h>
 +#include <stdio.h>
 +#include <stdarg.h>
 +#include <libgen.h>
 +#include <inttypes.h>
 +#include <limits.h>
 +#include <string.h>
 +#include <unistd.h>
 +#include <endian.h>
 +#include <fcntl.h>
 +#include <errno.h>
 +#include <ctype.h>
 +#include <asm/unistd.h>
 +#include <linux/err.h>
 +#include <linux/kernel.h>
 +#include <linux/bpf.h>
 +#include <linux/btf.h>
 +#include <linux/filter.h>
 +#include <linux/list.h>
 +#include <linux/limits.h>
 +#include <linux/perf_event.h>
 +#include <linux/ring_buffer.h>
 +#include <linux/version.h>
 +#include <sys/epoll.h>
 +#include <sys/ioctl.h>
 +#include <sys/mman.h>
 +#include <sys/stat.h>
 +#include <sys/types.h>
 +#include <sys/vfs.h>
 +#include <sys/utsname.h>
 +#include <sys/resource.h>
 +#include <libelf.h>
 +#include <gelf.h>
 +#include <zlib.h>
 +
 +#include "libbpf.h"
 +#include "bpf.h"
 +#include "btf.h"
 +#include "str_error.h"
 +#include "libbpf_internal.h"
 +#include "hashmap.h"
 +#include "bpf_gen_internal.h"
 +
 +#ifndef PERF_EVENT_IOC_SET_BPF
 +#define PERF_EVENT_IOC_SET_BPF                       _IOW('$', 8, __u32)
 +#define PERF_COUNT_SW_BPF_OUTPUT 10
 +#endif
 +#ifndef PERF_FLAG_FD_CLOEXEC
 +#define PERF_FLAG_FD_CLOEXEC         (1UL << 3) /* O_CLOEXEC */
 +#endif
 +
 +#ifndef BPF_FS_MAGIC
 +#define BPF_FS_MAGIC          0xcafe4a11
 +#endif
 +
 +#define BPF_INSN_SZ (sizeof(struct bpf_insn))
 +
 +/* vsprintf() in __base_pr() uses nonliteral format string. It may break
 + * compilation if user enables corresponding warning. Disable it explicitly.
 + */
 +#pragma GCC diagnostic ignored "-Wformat-nonliteral"
 +
 +#define __printf(a, b)        __attribute__((format(printf, a, b)))
 +
 +static struct bpf_map *bpf_object__add_map(struct bpf_object *obj);
 +static bool prog_is_subprog(const struct bpf_object *obj, const struct bpf_program *prog);
 +
 +static int __base_pr(enum libbpf_print_level level, const char *format,
 +                   va_list args)
 +{
 +      if (level == LIBBPF_DEBUG)
 +              return 0;
 +
 +      return vfprintf(stderr, format, args);
 +}
 +
 +static libbpf_print_fn_t __libbpf_pr = __base_pr;
 +
 +libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn)
 +{
 +      libbpf_print_fn_t old_print_fn = __libbpf_pr;
 +
 +      __libbpf_pr = fn;
 +      return old_print_fn;
 +}
 +
 +__printf(2, 3)
 +void libbpf_print(enum libbpf_print_level level, const char *format, ...)
 +{
 +      va_list args;
 +
 +      if (!__libbpf_pr)
 +              return;
 +
 +      va_start(args, format);
 +      __libbpf_pr(level, format, args);
 +      va_end(args);
 +}
 +
 +static void pr_perm_msg(int err)
 +{
 +      struct rlimit limit;
 +      char buf[100];
 +
 +      if (err != -EPERM || geteuid() != 0)
 +              return;
 +
 +      err = getrlimit(RLIMIT_MEMLOCK, &limit);
 +      if (err)
 +              return;
 +
 +      if (limit.rlim_cur == RLIM_INFINITY)
 +              return;
 +
 +      if (limit.rlim_cur < 1024)
 +              snprintf(buf, sizeof(buf), "%zu bytes", (size_t)limit.rlim_cur);
 +      else if (limit.rlim_cur < 1024*1024)
 +              snprintf(buf, sizeof(buf), "%.1f KiB", (double)limit.rlim_cur / 1024);
 +      else
 +              snprintf(buf, sizeof(buf), "%.1f MiB", (double)limit.rlim_cur / (1024*1024));
 +
 +      pr_warn("permission error while running as root; try raising 'ulimit -l'? current value: %s\n",
 +              buf);
 +}
 +
 +#define STRERR_BUFSIZE  128
 +
 +/* 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
 +
 +static inline __u64 ptr_to_u64(const void *ptr)
 +{
 +      return (__u64) (unsigned long) ptr;
 +}
 +
 +/* this goes away in libbpf 1.0 */
 +enum libbpf_strict_mode libbpf_mode = LIBBPF_STRICT_NONE;
 +
 +int libbpf_set_strict_mode(enum libbpf_strict_mode mode)
 +{
 +      /* __LIBBPF_STRICT_LAST is the last power-of-2 value used + 1, so to
 +       * get all possible values we compensate last +1, and then (2*x - 1)
 +       * to get the bit mask
 +       */
 +      if (mode != LIBBPF_STRICT_ALL
 +          && (mode & ~((__LIBBPF_STRICT_LAST - 1) * 2 - 1)))
 +              return errno = EINVAL, -EINVAL;
 +
 +      libbpf_mode = mode;
 +      return 0;
 +}
 +
 +__u32 libbpf_major_version(void)
 +{
 +      return LIBBPF_MAJOR_VERSION;
 +}
 +
 +__u32 libbpf_minor_version(void)
 +{
 +      return LIBBPF_MINOR_VERSION;
 +}
 +
 +const char *libbpf_version_string(void)
 +{
 +#define __S(X) #X
 +#define _S(X) __S(X)
 +      return  "v" _S(LIBBPF_MAJOR_VERSION) "." _S(LIBBPF_MINOR_VERSION);
 +#undef _S
 +#undef __S
 +}
 +
 +enum reloc_type {
 +      RELO_LD64,
 +      RELO_CALL,
 +      RELO_DATA,
 +      RELO_EXTERN_VAR,
 +      RELO_EXTERN_FUNC,
 +      RELO_SUBPROG_ADDR,
 +      RELO_CORE,
 +};
 +
 +struct reloc_desc {
 +      enum reloc_type type;
 +      int insn_idx;
 +      union {
 +              const struct bpf_core_relo *core_relo; /* used when type == RELO_CORE */
 +              struct {
 +                      int map_idx;
 +                      int sym_off;
 +              };
 +      };
 +};
 +
 +struct bpf_sec_def;
 +
 +typedef int (*init_fn_t)(struct bpf_program *prog, long cookie);
 +typedef int (*preload_fn_t)(struct bpf_program *prog, struct bpf_prog_load_opts *opts, long cookie);
 +typedef struct bpf_link *(*attach_fn_t)(const struct bpf_program *prog, long cookie);
 +
 +/* stored as sec_def->cookie for all libbpf-supported SEC()s */
 +enum sec_def_flags {
 +      SEC_NONE = 0,
 +      /* expected_attach_type is optional, if kernel doesn't support that */
 +      SEC_EXP_ATTACH_OPT = 1,
 +      /* legacy, only used by libbpf_get_type_names() and
 +       * libbpf_attach_type_by_name(), not used by libbpf itself at all.
 +       * This used to be associated with cgroup (and few other) BPF programs
 +       * that were attachable through BPF_PROG_ATTACH command. Pretty
 +       * meaningless nowadays, though.
 +       */
 +      SEC_ATTACHABLE = 2,
 +      SEC_ATTACHABLE_OPT = SEC_ATTACHABLE | SEC_EXP_ATTACH_OPT,
 +      /* attachment target is specified through BTF ID in either kernel or
 +       * other BPF program's BTF object */
 +      SEC_ATTACH_BTF = 4,
 +      /* BPF program type allows sleeping/blocking in kernel */
 +      SEC_SLEEPABLE = 8,
 +      /* allow non-strict prefix matching */
 +      SEC_SLOPPY_PFX = 16,
 +};
 +
 +struct bpf_sec_def {
 +      const char *sec;
 +      enum bpf_prog_type prog_type;
 +      enum bpf_attach_type expected_attach_type;
 +      long cookie;
 +
 +      init_fn_t init_fn;
 +      preload_fn_t preload_fn;
 +      attach_fn_t attach_fn;
 +};
 +
 +/*
 + * bpf_prog should be a better name but it has been used in
 + * linux/filter.h.
 + */
 +struct bpf_program {
 +      const struct bpf_sec_def *sec_def;
 +      char *sec_name;
 +      size_t sec_idx;
 +      /* this program's instruction offset (in number of instructions)
 +       * within its containing ELF section
 +       */
 +      size_t sec_insn_off;
 +      /* number of original instructions in ELF section belonging to this
 +       * program, not taking into account subprogram instructions possible
 +       * appended later during relocation
 +       */
 +      size_t sec_insn_cnt;
 +      /* Offset (in number of instructions) of the start of instruction
 +       * belonging to this BPF program  within its containing main BPF
 +       * program. For the entry-point (main) BPF program, this is always
 +       * zero. For a sub-program, this gets reset before each of main BPF
 +       * programs are processed and relocated and is used to determined
 +       * whether sub-program was already appended to the main program, and
 +       * if yes, at which instruction offset.
 +       */
 +      size_t sub_insn_off;
 +
 +      char *name;
 +      /* name with / replaced by _; makes recursive pinning
 +       * in bpf_object__pin_programs easier
 +       */
 +      char *pin_name;
 +
 +      /* instructions that belong to BPF program; insns[0] is located at
 +       * sec_insn_off instruction within its ELF section in ELF file, so
 +       * when mapping ELF file instruction index to the local instruction,
 +       * one needs to subtract sec_insn_off; and vice versa.
 +       */
 +      struct bpf_insn *insns;
 +      /* actual number of instruction in this BPF program's image; for
 +       * entry-point BPF programs this includes the size of main program
 +       * itself plus all the used sub-programs, appended at the end
 +       */
 +      size_t insns_cnt;
 +
 +      struct reloc_desc *reloc_desc;
 +      int nr_reloc;
 +
 +      /* BPF verifier log settings */
 +      char *log_buf;
 +      size_t log_size;
 +      __u32 log_level;
 +
 +      struct {
 +              int nr;
 +              int *fds;
 +      } instances;
 +      bpf_program_prep_t preprocessor;
 +
 +      struct bpf_object *obj;
 +      void *priv;
 +      bpf_program_clear_priv_t clear_priv;
 +
 +      bool load;
 +      bool mark_btf_static;
 +      enum bpf_prog_type type;
 +      enum bpf_attach_type expected_attach_type;
 +      int prog_ifindex;
 +      __u32 attach_btf_obj_fd;
 +      __u32 attach_btf_id;
 +      __u32 attach_prog_fd;
 +      void *func_info;
 +      __u32 func_info_rec_size;
 +      __u32 func_info_cnt;
 +
 +      void *line_info;
 +      __u32 line_info_rec_size;
 +      __u32 line_info_cnt;
 +      __u32 prog_flags;
 +};
 +
 +struct bpf_struct_ops {
 +      const char *tname;
 +      const struct btf_type *type;
 +      struct bpf_program **progs;
 +      __u32 *kern_func_off;
 +      /* e.g. struct tcp_congestion_ops in bpf_prog's btf format */
 +      void *data;
 +      /* e.g. struct bpf_struct_ops_tcp_congestion_ops in
 +       *      btf_vmlinux's format.
 +       * struct bpf_struct_ops_tcp_congestion_ops {
 +       *      [... some other kernel fields ...]
 +       *      struct tcp_congestion_ops data;
 +       * }
 +       * kern_vdata-size == sizeof(struct bpf_struct_ops_tcp_congestion_ops)
 +       * bpf_map__init_kern_struct_ops() will populate the "kern_vdata"
 +       * from "data".
 +       */
 +      void *kern_vdata;
 +      __u32 type_id;
 +};
 +
 +#define DATA_SEC ".data"
 +#define BSS_SEC ".bss"
 +#define RODATA_SEC ".rodata"
 +#define KCONFIG_SEC ".kconfig"
 +#define KSYMS_SEC ".ksyms"
 +#define STRUCT_OPS_SEC ".struct_ops"
 +
 +enum libbpf_map_type {
 +      LIBBPF_MAP_UNSPEC,
 +      LIBBPF_MAP_DATA,
 +      LIBBPF_MAP_BSS,
 +      LIBBPF_MAP_RODATA,
 +      LIBBPF_MAP_KCONFIG,
 +};
 +
 +struct bpf_map {
 +      char *name;
 +      /* real_name is defined for special internal maps (.rodata*,
 +       * .data*, .bss, .kconfig) and preserves their original ELF section
 +       * name. This is important to be be able to find corresponding BTF
 +       * DATASEC information.
 +       */
 +      char *real_name;
 +      int fd;
 +      int sec_idx;
 +      size_t sec_offset;
 +      int map_ifindex;
 +      int inner_map_fd;
 +      struct bpf_map_def def;
 +      __u32 numa_node;
 +      __u32 btf_var_idx;
 +      __u32 btf_key_type_id;
 +      __u32 btf_value_type_id;
 +      __u32 btf_vmlinux_value_type_id;
 +      void *priv;
 +      bpf_map_clear_priv_t clear_priv;
 +      enum libbpf_map_type libbpf_type;
 +      void *mmaped;
 +      struct bpf_struct_ops *st_ops;
 +      struct bpf_map *inner_map;
 +      void **init_slots;
 +      int init_slots_sz;
 +      char *pin_path;
 +      bool pinned;
 +      bool reused;
 +      bool skipped;
 +      __u64 map_extra;
 +};
 +
 +enum extern_type {
 +      EXT_UNKNOWN,
 +      EXT_KCFG,
 +      EXT_KSYM,
 +};
 +
 +enum kcfg_type {
 +      KCFG_UNKNOWN,
 +      KCFG_CHAR,
 +      KCFG_BOOL,
 +      KCFG_INT,
 +      KCFG_TRISTATE,
 +      KCFG_CHAR_ARR,
 +};
 +
 +struct extern_desc {
 +      enum extern_type type;
 +      int sym_idx;
 +      int btf_id;
 +      int sec_btf_id;
 +      const char *name;
 +      bool is_set;
 +      bool is_weak;
 +      union {
 +              struct {
 +                      enum kcfg_type type;
 +                      int sz;
 +                      int align;
 +                      int data_off;
 +                      bool is_signed;
 +              } kcfg;
 +              struct {
 +                      unsigned long long addr;
 +
 +                      /* target btf_id of the corresponding kernel var. */
 +                      int kernel_btf_obj_fd;
 +                      int kernel_btf_id;
 +
 +                      /* local btf_id of the ksym extern's type. */
 +                      __u32 type_id;
 +                      /* BTF fd index to be patched in for insn->off, this is
 +                       * 0 for vmlinux BTF, index in obj->fd_array for module
 +                       * BTF
 +                       */
 +                      __s16 btf_fd_idx;
 +              } ksym;
 +      };
 +};
 +
 +static LIST_HEAD(bpf_objects_list);
 +
 +struct module_btf {
 +      struct btf *btf;
 +      char *name;
 +      __u32 id;
 +      int fd;
 +      int fd_array_idx;
 +};
 +
 +enum sec_type {
 +      SEC_UNUSED = 0,
 +      SEC_RELO,
 +      SEC_BSS,
 +      SEC_DATA,
 +      SEC_RODATA,
 +};
 +
 +struct elf_sec_desc {
 +      enum sec_type sec_type;
 +      Elf64_Shdr *shdr;
 +      Elf_Data *data;
 +};
 +
 +struct elf_state {
 +      int fd;
 +      const void *obj_buf;
 +      size_t obj_buf_sz;
 +      Elf *elf;
 +      Elf64_Ehdr *ehdr;
 +      Elf_Data *symbols;
 +      Elf_Data *st_ops_data;
 +      size_t shstrndx; /* section index for section name strings */
 +      size_t strtabidx;
 +      struct elf_sec_desc *secs;
 +      int sec_cnt;
 +      int maps_shndx;
 +      int btf_maps_shndx;
 +      __u32 btf_maps_sec_btf_id;
 +      int text_shndx;
 +      int symbols_shndx;
 +      int st_ops_shndx;
 +};
 +
 +struct bpf_object {
 +      char name[BPF_OBJ_NAME_LEN];
 +      char license[64];
 +      __u32 kern_version;
 +
 +      struct bpf_program *programs;
 +      size_t nr_programs;
 +      struct bpf_map *maps;
 +      size_t nr_maps;
 +      size_t maps_cap;
 +
 +      char *kconfig;
 +      struct extern_desc *externs;
 +      int nr_extern;
 +      int kconfig_map_idx;
 +
 +      bool loaded;
 +      bool has_subcalls;
 +      bool has_rodata;
 +
 +      struct bpf_gen *gen_loader;
 +
 +      /* Information when doing ELF related work. Only valid if efile.elf is not NULL */
 +      struct elf_state efile;
 +      /*
 +       * All loaded bpf_object are linked in a list, which is
 +       * hidden to caller. bpf_objects__<func> handlers deal with
 +       * all objects.
 +       */
 +      struct list_head list;
 +
 +      struct btf *btf;
 +      struct btf_ext *btf_ext;
 +
 +      /* Parse and load BTF vmlinux if any of the programs in the object need
 +       * it at load time.
 +       */
 +      struct btf *btf_vmlinux;
 +      /* Path to the custom BTF to be used for BPF CO-RE relocations as an
 +       * override for vmlinux BTF.
 +       */
 +      char *btf_custom_path;
 +      /* vmlinux BTF override for CO-RE relocations */
 +      struct btf *btf_vmlinux_override;
 +      /* Lazily initialized kernel module BTFs */
 +      struct module_btf *btf_modules;
 +      bool btf_modules_loaded;
 +      size_t btf_module_cnt;
 +      size_t btf_module_cap;
 +
 +      /* optional log settings passed to BPF_BTF_LOAD and BPF_PROG_LOAD commands */
 +      char *log_buf;
 +      size_t log_size;
 +      __u32 log_level;
 +
 +      void *priv;
 +      bpf_object_clear_priv_t clear_priv;
 +
 +      int *fd_array;
 +      size_t fd_array_cap;
 +      size_t fd_array_cnt;
 +
 +      char path[];
 +};
 +
 +static const char *elf_sym_str(const struct bpf_object *obj, size_t off);
 +static const char *elf_sec_str(const struct bpf_object *obj, size_t off);
 +static Elf_Scn *elf_sec_by_idx(const struct bpf_object *obj, size_t idx);
 +static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name);
 +static Elf64_Shdr *elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn);
 +static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn);
 +static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn);
 +static Elf64_Sym *elf_sym_by_idx(const struct bpf_object *obj, size_t idx);
 +static Elf64_Rel *elf_rel_by_idx(Elf_Data *data, size_t idx);
 +
 +void bpf_program__unload(struct bpf_program *prog)
 +{
 +      int i;
 +
 +      if (!prog)
 +              return;
 +
 +      /*
 +       * 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_warn("Internal error: instances.nr is %d\n",
 +                      prog->instances.nr);
 +      }
 +
 +      prog->instances.nr = -1;
 +      zfree(&prog->instances.fds);
 +
 +      zfree(&prog->func_info);
 +      zfree(&prog->line_info);
 +}
 +
 +static void bpf_program__exit(struct bpf_program *prog)
 +{
 +      if (!prog)
 +              return;
 +
 +      if (prog->clear_priv)
 +              prog->clear_priv(prog, prog->priv);
 +
 +      prog->priv = NULL;
 +      prog->clear_priv = NULL;
 +
 +      bpf_program__unload(prog);
 +      zfree(&prog->name);
 +      zfree(&prog->sec_name);
 +      zfree(&prog->pin_name);
 +      zfree(&prog->insns);
 +      zfree(&prog->reloc_desc);
 +
 +      prog->nr_reloc = 0;
 +      prog->insns_cnt = 0;
 +      prog->sec_idx = -1;
 +}
 +
 +static char *__bpf_program__pin_name(struct bpf_program *prog)
 +{
 +      char *name, *p;
 +
 +      if (libbpf_mode & LIBBPF_STRICT_SEC_NAME)
 +              name = strdup(prog->name);
 +      else
 +              name = strdup(prog->sec_name);
 +
 +      if (!name)
 +              return NULL;
 +
 +      p = name;
 +
 +      while ((p = strchr(p, '/')))
 +              *p = '_';
 +
 +      return name;
 +}
 +
 +static bool insn_is_subprog_call(const struct bpf_insn *insn)
 +{
 +      return BPF_CLASS(insn->code) == BPF_JMP &&
 +             BPF_OP(insn->code) == BPF_CALL &&
 +             BPF_SRC(insn->code) == BPF_K &&
 +             insn->src_reg == BPF_PSEUDO_CALL &&
 +             insn->dst_reg == 0 &&
 +             insn->off == 0;
 +}
 +
 +static bool is_call_insn(const struct bpf_insn *insn)
 +{
 +      return insn->code == (BPF_JMP | BPF_CALL);
 +}
 +
 +static bool insn_is_pseudo_func(struct bpf_insn *insn)
 +{
 +      return is_ldimm64_insn(insn) && insn->src_reg == BPF_PSEUDO_FUNC;
 +}
 +
 +static int
 +bpf_object__init_prog(struct bpf_object *obj, struct bpf_program *prog,
 +                    const char *name, size_t sec_idx, const char *sec_name,
 +                    size_t sec_off, void *insn_data, size_t insn_data_sz)
 +{
 +      if (insn_data_sz == 0 || insn_data_sz % BPF_INSN_SZ || sec_off % BPF_INSN_SZ) {
 +              pr_warn("sec '%s': corrupted program '%s', offset %zu, size %zu\n",
 +                      sec_name, name, sec_off, insn_data_sz);
 +              return -EINVAL;
 +      }
 +
 +      memset(prog, 0, sizeof(*prog));
 +      prog->obj = obj;
 +
 +      prog->sec_idx = sec_idx;
 +      prog->sec_insn_off = sec_off / BPF_INSN_SZ;
 +      prog->sec_insn_cnt = insn_data_sz / BPF_INSN_SZ;
 +      /* insns_cnt can later be increased by appending used subprograms */
 +      prog->insns_cnt = prog->sec_insn_cnt;
 +
 +      prog->type = BPF_PROG_TYPE_UNSPEC;
 +      prog->load = true;
 +
 +      prog->instances.fds = NULL;
 +      prog->instances.nr = -1;
 +
 +      /* inherit object's log_level */
 +      prog->log_level = obj->log_level;
 +
 +      prog->sec_name = strdup(sec_name);
 +      if (!prog->sec_name)
 +              goto errout;
 +
 +      prog->name = strdup(name);
 +      if (!prog->name)
 +              goto errout;
 +
 +      prog->pin_name = __bpf_program__pin_name(prog);
 +      if (!prog->pin_name)
 +              goto errout;
 +
 +      prog->insns = malloc(insn_data_sz);
 +      if (!prog->insns)
 +              goto errout;
 +      memcpy(prog->insns, insn_data, insn_data_sz);
 +
 +      return 0;
 +errout:
 +      pr_warn("sec '%s': failed to allocate memory for prog '%s'\n", sec_name, name);
 +      bpf_program__exit(prog);
 +      return -ENOMEM;
 +}
 +
 +static int
 +bpf_object__add_programs(struct bpf_object *obj, Elf_Data *sec_data,
 +                       const char *sec_name, int sec_idx)
 +{
 +      Elf_Data *symbols = obj->efile.symbols;
 +      struct bpf_program *prog, *progs;
 +      void *data = sec_data->d_buf;
 +      size_t sec_sz = sec_data->d_size, sec_off, prog_sz, nr_syms;
 +      int nr_progs, err, i;
 +      const char *name;
 +      Elf64_Sym *sym;
 +
 +      progs = obj->programs;
 +      nr_progs = obj->nr_programs;
 +      nr_syms = symbols->d_size / sizeof(Elf64_Sym);
 +      sec_off = 0;
 +
 +      for (i = 0; i < nr_syms; i++) {
 +              sym = elf_sym_by_idx(obj, i);
 +
 +              if (sym->st_shndx != sec_idx)
 +                      continue;
 +              if (ELF64_ST_TYPE(sym->st_info) != STT_FUNC)
 +                      continue;
 +
 +              prog_sz = sym->st_size;
 +              sec_off = sym->st_value;
 +
 +              name = elf_sym_str(obj, sym->st_name);
 +              if (!name) {
 +                      pr_warn("sec '%s': failed to get symbol name for offset %zu\n",
 +                              sec_name, sec_off);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              if (sec_off + prog_sz > sec_sz) {
 +                      pr_warn("sec '%s': program at offset %zu crosses section boundary\n",
 +                              sec_name, sec_off);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              if (sec_idx != obj->efile.text_shndx && ELF64_ST_BIND(sym->st_info) == STB_LOCAL) {
 +                      pr_warn("sec '%s': program '%s' is static and not supported\n", sec_name, name);
 +                      return -ENOTSUP;
 +              }
 +
 +              pr_debug("sec '%s': found program '%s' at insn offset %zu (%zu bytes), code size %zu insns (%zu bytes)\n",
 +                       sec_name, name, sec_off / BPF_INSN_SZ, sec_off, prog_sz / BPF_INSN_SZ, prog_sz);
 +
 +              progs = libbpf_reallocarray(progs, nr_progs + 1, sizeof(*progs));
 +              if (!progs) {
 +                      /*
 +                       * In this case the original obj->programs
 +                       * is still valid, so don't need special treat for
 +                       * bpf_close_object().
 +                       */
 +                      pr_warn("sec '%s': failed to alloc memory for new program '%s'\n",
 +                              sec_name, name);
 +                      return -ENOMEM;
 +              }
 +              obj->programs = progs;
 +
 +              prog = &progs[nr_progs];
 +
 +              err = bpf_object__init_prog(obj, prog, name, sec_idx, sec_name,
 +                                          sec_off, data + sec_off, prog_sz);
 +              if (err)
 +                      return err;
 +
 +              /* if function is a global/weak symbol, but has restricted
 +               * (STV_HIDDEN or STV_INTERNAL) visibility, mark its BTF FUNC
 +               * as static to enable more permissive BPF verification mode
 +               * with more outside context available to BPF verifier
 +               */
 +              if (ELF64_ST_BIND(sym->st_info) != STB_LOCAL
 +                  && (ELF64_ST_VISIBILITY(sym->st_other) == STV_HIDDEN
 +                      || ELF64_ST_VISIBILITY(sym->st_other) == STV_INTERNAL))
 +                      prog->mark_btf_static = true;
 +
 +              nr_progs++;
 +              obj->nr_programs = nr_progs;
 +      }
 +
 +      return 0;
 +}
 +
-       snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx", getpid(), kfunc_name, offset);
++__u32 get_kernel_version(void)
 +{
++      /* On Ubuntu LINUX_VERSION_CODE doesn't correspond to info.release,
++       * but Ubuntu provides /proc/version_signature file, as described at
++       * https://ubuntu.com/kernel, with an example contents below, which we
++       * can use to get a proper LINUX_VERSION_CODE.
++       *
++       *   Ubuntu 5.4.0-12.15-generic 5.4.8
++       *
++       * In the above, 5.4.8 is what kernel is actually expecting, while
++       * uname() call will return 5.4.0 in info.release.
++       */
++      const char *ubuntu_kver_file = "/proc/version_signature";
 +      __u32 major, minor, patch;
 +      struct utsname info;
 +
++      if (access(ubuntu_kver_file, R_OK) == 0) {
++              FILE *f;
++
++              f = fopen(ubuntu_kver_file, "r");
++              if (f) {
++                      if (fscanf(f, "%*s %*s %d.%d.%d\n", &major, &minor, &patch) == 3) {
++                              fclose(f);
++                              return KERNEL_VERSION(major, minor, patch);
++                      }
++                      fclose(f);
++              }
++              /* something went wrong, fall back to uname() approach */
++      }
++
 +      uname(&info);
 +      if (sscanf(info.release, "%u.%u.%u", &major, &minor, &patch) != 3)
 +              return 0;
 +      return KERNEL_VERSION(major, minor, patch);
 +}
 +
 +static const struct btf_member *
 +find_member_by_offset(const struct btf_type *t, __u32 bit_offset)
 +{
 +      struct btf_member *m;
 +      int i;
 +
 +      for (i = 0, m = btf_members(t); i < btf_vlen(t); i++, m++) {
 +              if (btf_member_bit_offset(t, i) == bit_offset)
 +                      return m;
 +      }
 +
 +      return NULL;
 +}
 +
 +static const struct btf_member *
 +find_member_by_name(const struct btf *btf, const struct btf_type *t,
 +                  const char *name)
 +{
 +      struct btf_member *m;
 +      int i;
 +
 +      for (i = 0, m = btf_members(t); i < btf_vlen(t); i++, m++) {
 +              if (!strcmp(btf__name_by_offset(btf, m->name_off), name))
 +                      return m;
 +      }
 +
 +      return NULL;
 +}
 +
 +#define STRUCT_OPS_VALUE_PREFIX "bpf_struct_ops_"
 +static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
 +                                 const char *name, __u32 kind);
 +
 +static int
 +find_struct_ops_kern_types(const struct btf *btf, const char *tname,
 +                         const struct btf_type **type, __u32 *type_id,
 +                         const struct btf_type **vtype, __u32 *vtype_id,
 +                         const struct btf_member **data_member)
 +{
 +      const struct btf_type *kern_type, *kern_vtype;
 +      const struct btf_member *kern_data_member;
 +      __s32 kern_vtype_id, kern_type_id;
 +      __u32 i;
 +
 +      kern_type_id = btf__find_by_name_kind(btf, tname, BTF_KIND_STRUCT);
 +      if (kern_type_id < 0) {
 +              pr_warn("struct_ops init_kern: struct %s is not found in kernel BTF\n",
 +                      tname);
 +              return kern_type_id;
 +      }
 +      kern_type = btf__type_by_id(btf, kern_type_id);
 +
 +      /* Find the corresponding "map_value" type that will be used
 +       * in map_update(BPF_MAP_TYPE_STRUCT_OPS).  For example,
 +       * find "struct bpf_struct_ops_tcp_congestion_ops" from the
 +       * btf_vmlinux.
 +       */
 +      kern_vtype_id = find_btf_by_prefix_kind(btf, STRUCT_OPS_VALUE_PREFIX,
 +                                              tname, BTF_KIND_STRUCT);
 +      if (kern_vtype_id < 0) {
 +              pr_warn("struct_ops init_kern: struct %s%s is not found in kernel BTF\n",
 +                      STRUCT_OPS_VALUE_PREFIX, tname);
 +              return kern_vtype_id;
 +      }
 +      kern_vtype = btf__type_by_id(btf, kern_vtype_id);
 +
 +      /* Find "struct tcp_congestion_ops" from
 +       * struct bpf_struct_ops_tcp_congestion_ops {
 +       *      [ ... ]
 +       *      struct tcp_congestion_ops data;
 +       * }
 +       */
 +      kern_data_member = btf_members(kern_vtype);
 +      for (i = 0; i < btf_vlen(kern_vtype); i++, kern_data_member++) {
 +              if (kern_data_member->type == kern_type_id)
 +                      break;
 +      }
 +      if (i == btf_vlen(kern_vtype)) {
 +              pr_warn("struct_ops init_kern: struct %s data is not found in struct %s%s\n",
 +                      tname, STRUCT_OPS_VALUE_PREFIX, tname);
 +              return -EINVAL;
 +      }
 +
 +      *type = kern_type;
 +      *type_id = kern_type_id;
 +      *vtype = kern_vtype;
 +      *vtype_id = kern_vtype_id;
 +      *data_member = kern_data_member;
 +
 +      return 0;
 +}
 +
 +static bool bpf_map__is_struct_ops(const struct bpf_map *map)
 +{
 +      return map->def.type == BPF_MAP_TYPE_STRUCT_OPS;
 +}
 +
 +/* Init the map's fields that depend on kern_btf */
 +static int bpf_map__init_kern_struct_ops(struct bpf_map *map,
 +                                       const struct btf *btf,
 +                                       const struct btf *kern_btf)
 +{
 +      const struct btf_member *member, *kern_member, *kern_data_member;
 +      const struct btf_type *type, *kern_type, *kern_vtype;
 +      __u32 i, kern_type_id, kern_vtype_id, kern_data_off;
 +      struct bpf_struct_ops *st_ops;
 +      void *data, *kern_data;
 +      const char *tname;
 +      int err;
 +
 +      st_ops = map->st_ops;
 +      type = st_ops->type;
 +      tname = st_ops->tname;
 +      err = find_struct_ops_kern_types(kern_btf, tname,
 +                                       &kern_type, &kern_type_id,
 +                                       &kern_vtype, &kern_vtype_id,
 +                                       &kern_data_member);
 +      if (err)
 +              return err;
 +
 +      pr_debug("struct_ops init_kern %s: type_id:%u kern_type_id:%u kern_vtype_id:%u\n",
 +               map->name, st_ops->type_id, kern_type_id, kern_vtype_id);
 +
 +      map->def.value_size = kern_vtype->size;
 +      map->btf_vmlinux_value_type_id = kern_vtype_id;
 +
 +      st_ops->kern_vdata = calloc(1, kern_vtype->size);
 +      if (!st_ops->kern_vdata)
 +              return -ENOMEM;
 +
 +      data = st_ops->data;
 +      kern_data_off = kern_data_member->offset / 8;
 +      kern_data = st_ops->kern_vdata + kern_data_off;
 +
 +      member = btf_members(type);
 +      for (i = 0; i < btf_vlen(type); i++, member++) {
 +              const struct btf_type *mtype, *kern_mtype;
 +              __u32 mtype_id, kern_mtype_id;
 +              void *mdata, *kern_mdata;
 +              __s64 msize, kern_msize;
 +              __u32 moff, kern_moff;
 +              __u32 kern_member_idx;
 +              const char *mname;
 +
 +              mname = btf__name_by_offset(btf, member->name_off);
 +              kern_member = find_member_by_name(kern_btf, kern_type, mname);
 +              if (!kern_member) {
 +                      pr_warn("struct_ops init_kern %s: Cannot find member %s in kernel BTF\n",
 +                              map->name, mname);
 +                      return -ENOTSUP;
 +              }
 +
 +              kern_member_idx = kern_member - btf_members(kern_type);
 +              if (btf_member_bitfield_size(type, i) ||
 +                  btf_member_bitfield_size(kern_type, kern_member_idx)) {
 +                      pr_warn("struct_ops init_kern %s: bitfield %s is not supported\n",
 +                              map->name, mname);
 +                      return -ENOTSUP;
 +              }
 +
 +              moff = member->offset / 8;
 +              kern_moff = kern_member->offset / 8;
 +
 +              mdata = data + moff;
 +              kern_mdata = kern_data + kern_moff;
 +
 +              mtype = skip_mods_and_typedefs(btf, member->type, &mtype_id);
 +              kern_mtype = skip_mods_and_typedefs(kern_btf, kern_member->type,
 +                                                  &kern_mtype_id);
 +              if (BTF_INFO_KIND(mtype->info) !=
 +                  BTF_INFO_KIND(kern_mtype->info)) {
 +                      pr_warn("struct_ops init_kern %s: Unmatched member type %s %u != %u(kernel)\n",
 +                              map->name, mname, BTF_INFO_KIND(mtype->info),
 +                              BTF_INFO_KIND(kern_mtype->info));
 +                      return -ENOTSUP;
 +              }
 +
 +              if (btf_is_ptr(mtype)) {
 +                      struct bpf_program *prog;
 +
 +                      prog = st_ops->progs[i];
 +                      if (!prog)
 +                              continue;
 +
 +                      kern_mtype = skip_mods_and_typedefs(kern_btf,
 +                                                          kern_mtype->type,
 +                                                          &kern_mtype_id);
 +
 +                      /* mtype->type must be a func_proto which was
 +                       * guaranteed in bpf_object__collect_st_ops_relos(),
 +                       * so only check kern_mtype for func_proto here.
 +                       */
 +                      if (!btf_is_func_proto(kern_mtype)) {
 +                              pr_warn("struct_ops init_kern %s: kernel member %s is not a func ptr\n",
 +                                      map->name, mname);
 +                              return -ENOTSUP;
 +                      }
 +
 +                      prog->attach_btf_id = kern_type_id;
 +                      prog->expected_attach_type = kern_member_idx;
 +
 +                      st_ops->kern_func_off[i] = kern_data_off + kern_moff;
 +
 +                      pr_debug("struct_ops init_kern %s: func ptr %s is set to prog %s from data(+%u) to kern_data(+%u)\n",
 +                               map->name, mname, prog->name, moff,
 +                               kern_moff);
 +
 +                      continue;
 +              }
 +
 +              msize = btf__resolve_size(btf, mtype_id);
 +              kern_msize = btf__resolve_size(kern_btf, kern_mtype_id);
 +              if (msize < 0 || kern_msize < 0 || msize != kern_msize) {
 +                      pr_warn("struct_ops init_kern %s: Error in size of member %s: %zd != %zd(kernel)\n",
 +                              map->name, mname, (ssize_t)msize,
 +                              (ssize_t)kern_msize);
 +                      return -ENOTSUP;
 +              }
 +
 +              pr_debug("struct_ops init_kern %s: copy %s %u bytes from data(+%u) to kern_data(+%u)\n",
 +                       map->name, mname, (unsigned int)msize,
 +                       moff, kern_moff);
 +              memcpy(kern_mdata, mdata, msize);
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object__init_kern_struct_ops_maps(struct bpf_object *obj)
 +{
 +      struct bpf_map *map;
 +      size_t i;
 +      int err;
 +
 +      for (i = 0; i < obj->nr_maps; i++) {
 +              map = &obj->maps[i];
 +
 +              if (!bpf_map__is_struct_ops(map))
 +                      continue;
 +
 +              err = bpf_map__init_kern_struct_ops(map, obj->btf,
 +                                                  obj->btf_vmlinux);
 +              if (err)
 +                      return err;
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object__init_struct_ops_maps(struct bpf_object *obj)
 +{
 +      const struct btf_type *type, *datasec;
 +      const struct btf_var_secinfo *vsi;
 +      struct bpf_struct_ops *st_ops;
 +      const char *tname, *var_name;
 +      __s32 type_id, datasec_id;
 +      const struct btf *btf;
 +      struct bpf_map *map;
 +      __u32 i;
 +
 +      if (obj->efile.st_ops_shndx == -1)
 +              return 0;
 +
 +      btf = obj->btf;
 +      datasec_id = btf__find_by_name_kind(btf, STRUCT_OPS_SEC,
 +                                          BTF_KIND_DATASEC);
 +      if (datasec_id < 0) {
 +              pr_warn("struct_ops init: DATASEC %s not found\n",
 +                      STRUCT_OPS_SEC);
 +              return -EINVAL;
 +      }
 +
 +      datasec = btf__type_by_id(btf, datasec_id);
 +      vsi = btf_var_secinfos(datasec);
 +      for (i = 0; i < btf_vlen(datasec); i++, vsi++) {
 +              type = btf__type_by_id(obj->btf, vsi->type);
 +              var_name = btf__name_by_offset(obj->btf, type->name_off);
 +
 +              type_id = btf__resolve_type(obj->btf, vsi->type);
 +              if (type_id < 0) {
 +                      pr_warn("struct_ops init: Cannot resolve var type_id %u in DATASEC %s\n",
 +                              vsi->type, STRUCT_OPS_SEC);
 +                      return -EINVAL;
 +              }
 +
 +              type = btf__type_by_id(obj->btf, type_id);
 +              tname = btf__name_by_offset(obj->btf, type->name_off);
 +              if (!tname[0]) {
 +                      pr_warn("struct_ops init: anonymous type is not supported\n");
 +                      return -ENOTSUP;
 +              }
 +              if (!btf_is_struct(type)) {
 +                      pr_warn("struct_ops init: %s is not a struct\n", tname);
 +                      return -EINVAL;
 +              }
 +
 +              map = bpf_object__add_map(obj);
 +              if (IS_ERR(map))
 +                      return PTR_ERR(map);
 +
 +              map->sec_idx = obj->efile.st_ops_shndx;
 +              map->sec_offset = vsi->offset;
 +              map->name = strdup(var_name);
 +              if (!map->name)
 +                      return -ENOMEM;
 +
 +              map->def.type = BPF_MAP_TYPE_STRUCT_OPS;
 +              map->def.key_size = sizeof(int);
 +              map->def.value_size = type->size;
 +              map->def.max_entries = 1;
 +
 +              map->st_ops = calloc(1, sizeof(*map->st_ops));
 +              if (!map->st_ops)
 +                      return -ENOMEM;
 +              st_ops = map->st_ops;
 +              st_ops->data = malloc(type->size);
 +              st_ops->progs = calloc(btf_vlen(type), sizeof(*st_ops->progs));
 +              st_ops->kern_func_off = malloc(btf_vlen(type) *
 +                                             sizeof(*st_ops->kern_func_off));
 +              if (!st_ops->data || !st_ops->progs || !st_ops->kern_func_off)
 +                      return -ENOMEM;
 +
 +              if (vsi->offset + type->size > obj->efile.st_ops_data->d_size) {
 +                      pr_warn("struct_ops init: var %s is beyond the end of DATASEC %s\n",
 +                              var_name, STRUCT_OPS_SEC);
 +                      return -EINVAL;
 +              }
 +
 +              memcpy(st_ops->data,
 +                     obj->efile.st_ops_data->d_buf + vsi->offset,
 +                     type->size);
 +              st_ops->tname = tname;
 +              st_ops->type = type;
 +              st_ops->type_id = type_id;
 +
 +              pr_debug("struct_ops init: struct %s(type_id=%u) %s found at offset %u\n",
 +                       tname, type_id, var_name, vsi->offset);
 +      }
 +
 +      return 0;
 +}
 +
 +static struct bpf_object *bpf_object__new(const char *path,
 +                                        const void *obj_buf,
 +                                        size_t obj_buf_sz,
 +                                        const char *obj_name)
 +{
 +      bool strict = (libbpf_mode & LIBBPF_STRICT_NO_OBJECT_LIST);
 +      struct bpf_object *obj;
 +      char *end;
 +
 +      obj = calloc(1, sizeof(struct bpf_object) + strlen(path) + 1);
 +      if (!obj) {
 +              pr_warn("alloc memory failed for %s\n", path);
 +              return ERR_PTR(-ENOMEM);
 +      }
 +
 +      strcpy(obj->path, path);
 +      if (obj_name) {
 +              libbpf_strlcpy(obj->name, obj_name, sizeof(obj->name));
 +      } else {
 +              /* Using basename() GNU version which doesn't modify arg. */
 +              libbpf_strlcpy(obj->name, basename((void *)path), sizeof(obj->name));
 +              end = strchr(obj->name, '.');
 +              if (end)
 +                      *end = 0;
 +      }
 +
 +      obj->efile.fd = -1;
 +      /*
 +       * Caller of this function should also call
 +       * 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;
 +      obj->efile.maps_shndx = -1;
 +      obj->efile.btf_maps_shndx = -1;
 +      obj->efile.st_ops_shndx = -1;
 +      obj->kconfig_map_idx = -1;
 +
 +      obj->kern_version = get_kernel_version();
 +      obj->loaded = false;
 +
 +      INIT_LIST_HEAD(&obj->list);
 +      if (!strict)
 +              list_add(&obj->list, &bpf_objects_list);
 +      return obj;
 +}
 +
 +static void bpf_object__elf_finish(struct bpf_object *obj)
 +{
 +      if (!obj->efile.elf)
 +              return;
 +
 +      if (obj->efile.elf) {
 +              elf_end(obj->efile.elf);
 +              obj->efile.elf = NULL;
 +      }
 +      obj->efile.symbols = NULL;
 +      obj->efile.st_ops_data = NULL;
 +
 +      zfree(&obj->efile.secs);
 +      obj->efile.sec_cnt = 0;
 +      zclose(obj->efile.fd);
 +      obj->efile.obj_buf = NULL;
 +      obj->efile.obj_buf_sz = 0;
 +}
 +
 +static int bpf_object__elf_init(struct bpf_object *obj)
 +{
 +      Elf64_Ehdr *ehdr;
 +      int err = 0;
 +      Elf *elf;
 +
 +      if (obj->efile.elf) {
 +              pr_warn("elf: init internal error\n");
 +              return -LIBBPF_ERRNO__LIBELF;
 +      }
 +
 +      if (obj->efile.obj_buf_sz > 0) {
 +              /*
 +               * obj_buf should have been validated by
 +               * bpf_object__open_buffer().
 +               */
 +              elf = elf_memory((char *)obj->efile.obj_buf, obj->efile.obj_buf_sz);
 +      } else {
 +              obj->efile.fd = open(obj->path, O_RDONLY | O_CLOEXEC);
 +              if (obj->efile.fd < 0) {
 +                      char errmsg[STRERR_BUFSIZE], *cp;
 +
 +                      err = -errno;
 +                      cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 +                      pr_warn("elf: failed to open %s: %s\n", obj->path, cp);
 +                      return err;
 +              }
 +
 +              elf = elf_begin(obj->efile.fd, ELF_C_READ_MMAP, NULL);
 +      }
 +
 +      if (!elf) {
 +              pr_warn("elf: failed to open %s as ELF file: %s\n", obj->path, elf_errmsg(-1));
 +              err = -LIBBPF_ERRNO__LIBELF;
 +              goto errout;
 +      }
 +
 +      obj->efile.elf = elf;
 +
 +      if (elf_kind(elf) != ELF_K_ELF) {
 +              err = -LIBBPF_ERRNO__FORMAT;
 +              pr_warn("elf: '%s' is not a proper ELF object\n", obj->path);
 +              goto errout;
 +      }
 +
 +      if (gelf_getclass(elf) != ELFCLASS64) {
 +              err = -LIBBPF_ERRNO__FORMAT;
 +              pr_warn("elf: '%s' is not a 64-bit ELF object\n", obj->path);
 +              goto errout;
 +      }
 +
 +      obj->efile.ehdr = ehdr = elf64_getehdr(elf);
 +      if (!obj->efile.ehdr) {
 +              pr_warn("elf: failed to get ELF header from %s: %s\n", obj->path, elf_errmsg(-1));
 +              err = -LIBBPF_ERRNO__FORMAT;
 +              goto errout;
 +      }
 +
 +      if (elf_getshdrstrndx(elf, &obj->efile.shstrndx)) {
 +              pr_warn("elf: failed to get section names section index for %s: %s\n",
 +                      obj->path, elf_errmsg(-1));
 +              err = -LIBBPF_ERRNO__FORMAT;
 +              goto errout;
 +      }
 +
 +      /* Elf is corrupted/truncated, avoid calling elf_strptr. */
 +      if (!elf_rawdata(elf_getscn(elf, obj->efile.shstrndx), NULL)) {
 +              pr_warn("elf: failed to get section names strings from %s: %s\n",
 +                      obj->path, elf_errmsg(-1));
 +              err = -LIBBPF_ERRNO__FORMAT;
 +              goto errout;
 +      }
 +
 +      /* Old LLVM set e_machine to EM_NONE */
 +      if (ehdr->e_type != ET_REL || (ehdr->e_machine && ehdr->e_machine != EM_BPF)) {
 +              pr_warn("elf: %s is not a valid eBPF object file\n", obj->path);
 +              err = -LIBBPF_ERRNO__FORMAT;
 +              goto errout;
 +      }
 +
 +      return 0;
 +errout:
 +      bpf_object__elf_finish(obj);
 +      return err;
 +}
 +
 +static int bpf_object__check_endianness(struct bpf_object *obj)
 +{
 +#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
 +      if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2LSB)
 +              return 0;
 +#elif __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__
 +      if (obj->efile.ehdr->e_ident[EI_DATA] == ELFDATA2MSB)
 +              return 0;
 +#else
 +# error "Unrecognized __BYTE_ORDER__"
 +#endif
 +      pr_warn("elf: endianness mismatch in %s.\n", obj->path);
 +      return -LIBBPF_ERRNO__ENDIAN;
 +}
 +
 +static int
 +bpf_object__init_license(struct bpf_object *obj, void *data, size_t size)
 +{
 +      /* libbpf_strlcpy() only copies first N - 1 bytes, so size + 1 won't
 +       * go over allowed ELF data section buffer
 +       */
 +      libbpf_strlcpy(obj->license, data, min(size + 1, sizeof(obj->license)));
 +      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_warn("invalid kver section in %s\n", obj->path);
 +              return -LIBBPF_ERRNO__FORMAT;
 +      }
 +      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;
 +}
 +
 +static bool bpf_map_type__is_map_in_map(enum bpf_map_type type)
 +{
 +      if (type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
 +          type == BPF_MAP_TYPE_HASH_OF_MAPS)
 +              return true;
 +      return false;
 +}
 +
 +static int find_elf_sec_sz(const struct bpf_object *obj, const char *name, __u32 *size)
 +{
 +      int ret = -ENOENT;
 +      Elf_Data *data;
 +      Elf_Scn *scn;
 +
 +      *size = 0;
 +      if (!name)
 +              return -EINVAL;
 +
 +      scn = elf_sec_by_name(obj, name);
 +      data = elf_sec_data(obj, scn);
 +      if (data) {
 +              ret = 0; /* found it */
 +              *size = data->d_size;
 +      }
 +
 +      return *size ? 0 : ret;
 +}
 +
 +static int find_elf_var_offset(const struct bpf_object *obj, const char *name, __u32 *off)
 +{
 +      Elf_Data *symbols = obj->efile.symbols;
 +      const char *sname;
 +      size_t si;
 +
 +      if (!name || !off)
 +              return -EINVAL;
 +
 +      for (si = 0; si < symbols->d_size / sizeof(Elf64_Sym); si++) {
 +              Elf64_Sym *sym = elf_sym_by_idx(obj, si);
 +
 +              if (ELF64_ST_BIND(sym->st_info) != STB_GLOBAL ||
 +                  ELF64_ST_TYPE(sym->st_info) != STT_OBJECT)
 +                      continue;
 +
 +              sname = elf_sym_str(obj, sym->st_name);
 +              if (!sname) {
 +                      pr_warn("failed to get sym name string for var %s\n", name);
 +                      return -EIO;
 +              }
 +              if (strcmp(name, sname) == 0) {
 +                      *off = sym->st_value;
 +                      return 0;
 +              }
 +      }
 +
 +      return -ENOENT;
 +}
 +
 +static struct bpf_map *bpf_object__add_map(struct bpf_object *obj)
 +{
 +      struct bpf_map *new_maps;
 +      size_t new_cap;
 +      int i;
 +
 +      if (obj->nr_maps < obj->maps_cap)
 +              return &obj->maps[obj->nr_maps++];
 +
 +      new_cap = max((size_t)4, obj->maps_cap * 3 / 2);
 +      new_maps = libbpf_reallocarray(obj->maps, new_cap, sizeof(*obj->maps));
 +      if (!new_maps) {
 +              pr_warn("alloc maps for object failed\n");
 +              return ERR_PTR(-ENOMEM);
 +      }
 +
 +      obj->maps_cap = new_cap;
 +      obj->maps = new_maps;
 +
 +      /* zero out new maps */
 +      memset(obj->maps + obj->nr_maps, 0,
 +             (obj->maps_cap - obj->nr_maps) * sizeof(*obj->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 = obj->nr_maps; i < obj->maps_cap; i++) {
 +              obj->maps[i].fd = -1;
 +              obj->maps[i].inner_map_fd = -1;
 +      }
 +
 +      return &obj->maps[obj->nr_maps++];
 +}
 +
 +static size_t bpf_map_mmap_sz(const struct bpf_map *map)
 +{
 +      long page_sz = sysconf(_SC_PAGE_SIZE);
 +      size_t map_sz;
 +
 +      map_sz = (size_t)roundup(map->def.value_size, 8) * map->def.max_entries;
 +      map_sz = roundup(map_sz, page_sz);
 +      return map_sz;
 +}
 +
 +static char *internal_map_name(struct bpf_object *obj, const char *real_name)
 +{
 +      char map_name[BPF_OBJ_NAME_LEN], *p;
 +      int pfx_len, sfx_len = max((size_t)7, strlen(real_name));
 +
 +      /* This is one of the more confusing parts of libbpf for various
 +       * reasons, some of which are historical. The original idea for naming
 +       * internal names was to include as much of BPF object name prefix as
 +       * possible, so that it can be distinguished from similar internal
 +       * maps of a different BPF object.
 +       * As an example, let's say we have bpf_object named 'my_object_name'
 +       * and internal map corresponding to '.rodata' ELF section. The final
 +       * map name advertised to user and to the kernel will be
 +       * 'my_objec.rodata', taking first 8 characters of object name and
 +       * entire 7 characters of '.rodata'.
 +       * Somewhat confusingly, if internal map ELF section name is shorter
 +       * than 7 characters, e.g., '.bss', we still reserve 7 characters
 +       * for the suffix, even though we only have 4 actual characters, and
 +       * resulting map will be called 'my_objec.bss', not even using all 15
 +       * characters allowed by the kernel. Oh well, at least the truncated
 +       * object name is somewhat consistent in this case. But if the map
 +       * name is '.kconfig', we'll still have entirety of '.kconfig' added
 +       * (8 chars) and thus will be left with only first 7 characters of the
 +       * object name ('my_obje'). Happy guessing, user, that the final map
 +       * name will be "my_obje.kconfig".
 +       * Now, with libbpf starting to support arbitrarily named .rodata.*
 +       * and .data.* data sections, it's possible that ELF section name is
 +       * longer than allowed 15 chars, so we now need to be careful to take
 +       * only up to 15 first characters of ELF name, taking no BPF object
 +       * name characters at all. So '.rodata.abracadabra' will result in
 +       * '.rodata.abracad' kernel and user-visible name.
 +       * We need to keep this convoluted logic intact for .data, .bss and
 +       * .rodata maps, but for new custom .data.custom and .rodata.custom
 +       * maps we use their ELF names as is, not prepending bpf_object name
 +       * in front. We still need to truncate them to 15 characters for the
 +       * kernel. Full name can be recovered for such maps by using DATASEC
 +       * BTF type associated with such map's value type, though.
 +       */
 +      if (sfx_len >= BPF_OBJ_NAME_LEN)
 +              sfx_len = BPF_OBJ_NAME_LEN - 1;
 +
 +      /* if there are two or more dots in map name, it's a custom dot map */
 +      if (strchr(real_name + 1, '.') != NULL)
 +              pfx_len = 0;
 +      else
 +              pfx_len = min((size_t)BPF_OBJ_NAME_LEN - sfx_len - 1, strlen(obj->name));
 +
 +      snprintf(map_name, sizeof(map_name), "%.*s%.*s", pfx_len, obj->name,
 +               sfx_len, real_name);
 +
 +      /* sanitise map name to characters allowed by kernel */
 +      for (p = map_name; *p && p < map_name + sizeof(map_name); p++)
 +              if (!isalnum(*p) && *p != '_' && *p != '.')
 +                      *p = '_';
 +
 +      return strdup(map_name);
 +}
 +
 +static int
 +bpf_object__init_internal_map(struct bpf_object *obj, enum libbpf_map_type type,
 +                            const char *real_name, int sec_idx, void *data, size_t data_sz)
 +{
 +      struct bpf_map_def *def;
 +      struct bpf_map *map;
 +      int err;
 +
 +      map = bpf_object__add_map(obj);
 +      if (IS_ERR(map))
 +              return PTR_ERR(map);
 +
 +      map->libbpf_type = type;
 +      map->sec_idx = sec_idx;
 +      map->sec_offset = 0;
 +      map->real_name = strdup(real_name);
 +      map->name = internal_map_name(obj, real_name);
 +      if (!map->real_name || !map->name) {
 +              zfree(&map->real_name);
 +              zfree(&map->name);
 +              return -ENOMEM;
 +      }
 +
 +      def = &map->def;
 +      def->type = BPF_MAP_TYPE_ARRAY;
 +      def->key_size = sizeof(int);
 +      def->value_size = data_sz;
 +      def->max_entries = 1;
 +      def->map_flags = type == LIBBPF_MAP_RODATA || type == LIBBPF_MAP_KCONFIG
 +                       ? BPF_F_RDONLY_PROG : 0;
 +      def->map_flags |= BPF_F_MMAPABLE;
 +
 +      pr_debug("map '%s' (global data): at sec_idx %d, offset %zu, flags %x.\n",
 +               map->name, map->sec_idx, map->sec_offset, def->map_flags);
 +
 +      map->mmaped = mmap(NULL, bpf_map_mmap_sz(map), PROT_READ | PROT_WRITE,
 +                         MAP_SHARED | MAP_ANONYMOUS, -1, 0);
 +      if (map->mmaped == MAP_FAILED) {
 +              err = -errno;
 +              map->mmaped = NULL;
 +              pr_warn("failed to alloc map '%s' content buffer: %d\n",
 +                      map->name, err);
 +              zfree(&map->real_name);
 +              zfree(&map->name);
 +              return err;
 +      }
 +
 +      if (data)
 +              memcpy(map->mmaped, data, data_sz);
 +
 +      pr_debug("map %td is \"%s\"\n", map - obj->maps, map->name);
 +      return 0;
 +}
 +
 +static int bpf_object__init_global_data_maps(struct bpf_object *obj)
 +{
 +      struct elf_sec_desc *sec_desc;
 +      const char *sec_name;
 +      int err = 0, sec_idx;
 +
 +      /*
 +       * Populate obj->maps with libbpf internal maps.
 +       */
 +      for (sec_idx = 1; sec_idx < obj->efile.sec_cnt; sec_idx++) {
 +              sec_desc = &obj->efile.secs[sec_idx];
 +
 +              switch (sec_desc->sec_type) {
 +              case SEC_DATA:
 +                      sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
 +                      err = bpf_object__init_internal_map(obj, LIBBPF_MAP_DATA,
 +                                                          sec_name, sec_idx,
 +                                                          sec_desc->data->d_buf,
 +                                                          sec_desc->data->d_size);
 +                      break;
 +              case SEC_RODATA:
 +                      obj->has_rodata = true;
 +                      sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
 +                      err = bpf_object__init_internal_map(obj, LIBBPF_MAP_RODATA,
 +                                                          sec_name, sec_idx,
 +                                                          sec_desc->data->d_buf,
 +                                                          sec_desc->data->d_size);
 +                      break;
 +              case SEC_BSS:
 +                      sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, sec_idx));
 +                      err = bpf_object__init_internal_map(obj, LIBBPF_MAP_BSS,
 +                                                          sec_name, sec_idx,
 +                                                          NULL,
 +                                                          sec_desc->data->d_size);
 +                      break;
 +              default:
 +                      /* skip */
 +                      break;
 +              }
 +              if (err)
 +                      return err;
 +      }
 +      return 0;
 +}
 +
 +
 +static struct extern_desc *find_extern_by_name(const struct bpf_object *obj,
 +                                             const void *name)
 +{
 +      int i;
 +
 +      for (i = 0; i < obj->nr_extern; i++) {
 +              if (strcmp(obj->externs[i].name, name) == 0)
 +                      return &obj->externs[i];
 +      }
 +      return NULL;
 +}
 +
 +static int set_kcfg_value_tri(struct extern_desc *ext, void *ext_val,
 +                            char value)
 +{
 +      switch (ext->kcfg.type) {
 +      case KCFG_BOOL:
 +              if (value == 'm') {
 +                      pr_warn("extern (kcfg) %s=%c should be tristate or char\n",
 +                              ext->name, value);
 +                      return -EINVAL;
 +              }
 +              *(bool *)ext_val = value == 'y' ? true : false;
 +              break;
 +      case KCFG_TRISTATE:
 +              if (value == 'y')
 +                      *(enum libbpf_tristate *)ext_val = TRI_YES;
 +              else if (value == 'm')
 +                      *(enum libbpf_tristate *)ext_val = TRI_MODULE;
 +              else /* value == 'n' */
 +                      *(enum libbpf_tristate *)ext_val = TRI_NO;
 +              break;
 +      case KCFG_CHAR:
 +              *(char *)ext_val = value;
 +              break;
 +      case KCFG_UNKNOWN:
 +      case KCFG_INT:
 +      case KCFG_CHAR_ARR:
 +      default:
 +              pr_warn("extern (kcfg) %s=%c should be bool, tristate, or char\n",
 +                      ext->name, value);
 +              return -EINVAL;
 +      }
 +      ext->is_set = true;
 +      return 0;
 +}
 +
 +static int set_kcfg_value_str(struct extern_desc *ext, char *ext_val,
 +                            const char *value)
 +{
 +      size_t len;
 +
 +      if (ext->kcfg.type != KCFG_CHAR_ARR) {
 +              pr_warn("extern (kcfg) %s=%s should be char array\n", ext->name, value);
 +              return -EINVAL;
 +      }
 +
 +      len = strlen(value);
 +      if (value[len - 1] != '"') {
 +              pr_warn("extern (kcfg) '%s': invalid string config '%s'\n",
 +                      ext->name, value);
 +              return -EINVAL;
 +      }
 +
 +      /* strip quotes */
 +      len -= 2;
 +      if (len >= ext->kcfg.sz) {
 +              pr_warn("extern (kcfg) '%s': long string config %s of (%zu bytes) truncated to %d bytes\n",
 +                      ext->name, value, len, ext->kcfg.sz - 1);
 +              len = ext->kcfg.sz - 1;
 +      }
 +      memcpy(ext_val, value + 1, len);
 +      ext_val[len] = '\0';
 +      ext->is_set = true;
 +      return 0;
 +}
 +
 +static int parse_u64(const char *value, __u64 *res)
 +{
 +      char *value_end;
 +      int err;
 +
 +      errno = 0;
 +      *res = strtoull(value, &value_end, 0);
 +      if (errno) {
 +              err = -errno;
 +              pr_warn("failed to parse '%s' as integer: %d\n", value, err);
 +              return err;
 +      }
 +      if (*value_end) {
 +              pr_warn("failed to parse '%s' as integer completely\n", value);
 +              return -EINVAL;
 +      }
 +      return 0;
 +}
 +
 +static bool is_kcfg_value_in_range(const struct extern_desc *ext, __u64 v)
 +{
 +      int bit_sz = ext->kcfg.sz * 8;
 +
 +      if (ext->kcfg.sz == 8)
 +              return true;
 +
 +      /* Validate that value stored in u64 fits in integer of `ext->sz`
 +       * bytes size without any loss of information. If the target integer
 +       * is signed, we rely on the following limits of integer type of
 +       * Y bits and subsequent transformation:
 +       *
 +       *     -2^(Y-1) <= X           <= 2^(Y-1) - 1
 +       *            0 <= X + 2^(Y-1) <= 2^Y - 1
 +       *            0 <= X + 2^(Y-1) <  2^Y
 +       *
 +       *  For unsigned target integer, check that all the (64 - Y) bits are
 +       *  zero.
 +       */
 +      if (ext->kcfg.is_signed)
 +              return v + (1ULL << (bit_sz - 1)) < (1ULL << bit_sz);
 +      else
 +              return (v >> bit_sz) == 0;
 +}
 +
 +static int set_kcfg_value_num(struct extern_desc *ext, void *ext_val,
 +                            __u64 value)
 +{
 +      if (ext->kcfg.type != KCFG_INT && ext->kcfg.type != KCFG_CHAR) {
 +              pr_warn("extern (kcfg) %s=%llu should be integer\n",
 +                      ext->name, (unsigned long long)value);
 +              return -EINVAL;
 +      }
 +      if (!is_kcfg_value_in_range(ext, value)) {
 +              pr_warn("extern (kcfg) %s=%llu value doesn't fit in %d bytes\n",
 +                      ext->name, (unsigned long long)value, ext->kcfg.sz);
 +              return -ERANGE;
 +      }
 +      switch (ext->kcfg.sz) {
 +              case 1: *(__u8 *)ext_val = value; break;
 +              case 2: *(__u16 *)ext_val = value; break;
 +              case 4: *(__u32 *)ext_val = value; break;
 +              case 8: *(__u64 *)ext_val = value; break;
 +              default:
 +                      return -EINVAL;
 +      }
 +      ext->is_set = true;
 +      return 0;
 +}
 +
 +static int bpf_object__process_kconfig_line(struct bpf_object *obj,
 +                                          char *buf, void *data)
 +{
 +      struct extern_desc *ext;
 +      char *sep, *value;
 +      int len, err = 0;
 +      void *ext_val;
 +      __u64 num;
 +
 +      if (!str_has_pfx(buf, "CONFIG_"))
 +              return 0;
 +
 +      sep = strchr(buf, '=');
 +      if (!sep) {
 +              pr_warn("failed to parse '%s': no separator\n", buf);
 +              return -EINVAL;
 +      }
 +
 +      /* Trim ending '\n' */
 +      len = strlen(buf);
 +      if (buf[len - 1] == '\n')
 +              buf[len - 1] = '\0';
 +      /* Split on '=' and ensure that a value is present. */
 +      *sep = '\0';
 +      if (!sep[1]) {
 +              *sep = '=';
 +              pr_warn("failed to parse '%s': no value\n", buf);
 +              return -EINVAL;
 +      }
 +
 +      ext = find_extern_by_name(obj, buf);
 +      if (!ext || ext->is_set)
 +              return 0;
 +
 +      ext_val = data + ext->kcfg.data_off;
 +      value = sep + 1;
 +
 +      switch (*value) {
 +      case 'y': case 'n': case 'm':
 +              err = set_kcfg_value_tri(ext, ext_val, *value);
 +              break;
 +      case '"':
 +              err = set_kcfg_value_str(ext, ext_val, value);
 +              break;
 +      default:
 +              /* assume integer */
 +              err = parse_u64(value, &num);
 +              if (err) {
 +                      pr_warn("extern (kcfg) %s=%s should be integer\n",
 +                              ext->name, value);
 +                      return err;
 +              }
 +              err = set_kcfg_value_num(ext, ext_val, num);
 +              break;
 +      }
 +      if (err)
 +              return err;
 +      pr_debug("extern (kcfg) %s=%s\n", ext->name, value);
 +      return 0;
 +}
 +
 +static int bpf_object__read_kconfig_file(struct bpf_object *obj, void *data)
 +{
 +      char buf[PATH_MAX];
 +      struct utsname uts;
 +      int len, err = 0;
 +      gzFile file;
 +
 +      uname(&uts);
 +      len = snprintf(buf, PATH_MAX, "/boot/config-%s", uts.release);
 +      if (len < 0)
 +              return -EINVAL;
 +      else if (len >= PATH_MAX)
 +              return -ENAMETOOLONG;
 +
 +      /* gzopen also accepts uncompressed files. */
 +      file = gzopen(buf, "r");
 +      if (!file)
 +              file = gzopen("/proc/config.gz", "r");
 +
 +      if (!file) {
 +              pr_warn("failed to open system Kconfig\n");
 +              return -ENOENT;
 +      }
 +
 +      while (gzgets(file, buf, sizeof(buf))) {
 +              err = bpf_object__process_kconfig_line(obj, buf, data);
 +              if (err) {
 +                      pr_warn("error parsing system Kconfig line '%s': %d\n",
 +                              buf, err);
 +                      goto out;
 +              }
 +      }
 +
 +out:
 +      gzclose(file);
 +      return err;
 +}
 +
 +static int bpf_object__read_kconfig_mem(struct bpf_object *obj,
 +                                      const char *config, void *data)
 +{
 +      char buf[PATH_MAX];
 +      int err = 0;
 +      FILE *file;
 +
 +      file = fmemopen((void *)config, strlen(config), "r");
 +      if (!file) {
 +              err = -errno;
 +              pr_warn("failed to open in-memory Kconfig: %d\n", err);
 +              return err;
 +      }
 +
 +      while (fgets(buf, sizeof(buf), file)) {
 +              err = bpf_object__process_kconfig_line(obj, buf, data);
 +              if (err) {
 +                      pr_warn("error parsing in-memory Kconfig line '%s': %d\n",
 +                              buf, err);
 +                      break;
 +              }
 +      }
 +
 +      fclose(file);
 +      return err;
 +}
 +
 +static int bpf_object__init_kconfig_map(struct bpf_object *obj)
 +{
 +      struct extern_desc *last_ext = NULL, *ext;
 +      size_t map_sz;
 +      int i, err;
 +
 +      for (i = 0; i < obj->nr_extern; i++) {
 +              ext = &obj->externs[i];
 +              if (ext->type == EXT_KCFG)
 +                      last_ext = ext;
 +      }
 +
 +      if (!last_ext)
 +              return 0;
 +
 +      map_sz = last_ext->kcfg.data_off + last_ext->kcfg.sz;
 +      err = bpf_object__init_internal_map(obj, LIBBPF_MAP_KCONFIG,
 +                                          ".kconfig", obj->efile.symbols_shndx,
 +                                          NULL, map_sz);
 +      if (err)
 +              return err;
 +
 +      obj->kconfig_map_idx = obj->nr_maps - 1;
 +
 +      return 0;
 +}
 +
 +static int bpf_object__init_user_maps(struct bpf_object *obj, bool strict)
 +{
 +      Elf_Data *symbols = obj->efile.symbols;
 +      int i, map_def_sz = 0, nr_maps = 0, nr_syms;
 +      Elf_Data *data = NULL;
 +      Elf_Scn *scn;
 +
 +      if (obj->efile.maps_shndx < 0)
 +              return 0;
 +
 +      if (!symbols)
 +              return -EINVAL;
 +
 +      scn = elf_sec_by_idx(obj, obj->efile.maps_shndx);
 +      data = elf_sec_data(obj, scn);
 +      if (!scn || !data) {
 +              pr_warn("elf: failed to get legacy map definitions for %s\n",
 +                      obj->path);
 +              return -EINVAL;
 +      }
 +
 +      /*
 +       * 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.
 +       */
 +      nr_syms = symbols->d_size / sizeof(Elf64_Sym);
 +      for (i = 0; i < nr_syms; i++) {
 +              Elf64_Sym *sym = elf_sym_by_idx(obj, i);
 +
 +              if (sym->st_shndx != obj->efile.maps_shndx)
 +                      continue;
 +              if (ELF64_ST_TYPE(sym->st_info) == STT_SECTION)
 +                      continue;
 +              nr_maps++;
 +      }
 +      /* Assume equally sized map definitions */
 +      pr_debug("elf: found %d legacy map definitions (%zd bytes) in %s\n",
 +               nr_maps, data->d_size, obj->path);
 +
 +      if (!data->d_size || nr_maps == 0 || (data->d_size % nr_maps) != 0) {
 +              pr_warn("elf: unable to determine legacy map definition size in %s\n",
 +                      obj->path);
 +              return -EINVAL;
 +      }
 +      map_def_sz = data->d_size / nr_maps;
 +
 +      /* Fill obj->maps using data in "maps" section.  */
 +      for (i = 0; i < nr_syms; i++) {
 +              Elf64_Sym *sym = elf_sym_by_idx(obj, i);
 +              const char *map_name;
 +              struct bpf_map_def *def;
 +              struct bpf_map *map;
 +
 +              if (sym->st_shndx != obj->efile.maps_shndx)
 +                      continue;
 +              if (ELF64_ST_TYPE(sym->st_info) == STT_SECTION)
 +                      continue;
 +
 +              map = bpf_object__add_map(obj);
 +              if (IS_ERR(map))
 +                      return PTR_ERR(map);
 +
 +              map_name = elf_sym_str(obj, sym->st_name);
 +              if (!map_name) {
 +                      pr_warn("failed to get map #%d name sym string for obj %s\n",
 +                              i, obj->path);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              if (ELF64_ST_BIND(sym->st_info) == STB_LOCAL) {
 +                      pr_warn("map '%s' (legacy): static maps are not supported\n", map_name);
 +                      return -ENOTSUP;
 +              }
 +
 +              map->libbpf_type = LIBBPF_MAP_UNSPEC;
 +              map->sec_idx = sym->st_shndx;
 +              map->sec_offset = sym->st_value;
 +              pr_debug("map '%s' (legacy): at sec_idx %d, offset %zu.\n",
 +                       map_name, map->sec_idx, map->sec_offset);
 +              if (sym->st_value + map_def_sz > data->d_size) {
 +                      pr_warn("corrupted maps section in %s: last map \"%s\" too small\n",
 +                              obj->path, map_name);
 +                      return -EINVAL;
 +              }
 +
 +              map->name = strdup(map_name);
 +              if (!map->name) {
 +                      pr_warn("map '%s': failed to alloc map name\n", map_name);
 +                      return -ENOMEM;
 +              }
 +              pr_debug("map %d is \"%s\"\n", i, map->name);
 +              def = (struct bpf_map_def *)(data->d_buf + sym->st_value);
 +              /*
 +               * 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(&map->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_warn("maps section in %s: \"%s\" has unrecognized, non-zero options\n",
 +                                              obj->path, map_name);
 +                                      if (strict)
 +                                              return -EINVAL;
 +                              }
 +                      }
 +                      memcpy(&map->def, def, sizeof(struct bpf_map_def));
 +              }
 +      }
 +      return 0;
 +}
 +
 +const struct btf_type *
 +skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id)
 +{
 +      const struct btf_type *t = btf__type_by_id(btf, id);
 +
 +      if (res_id)
 +              *res_id = id;
 +
 +      while (btf_is_mod(t) || btf_is_typedef(t)) {
 +              if (res_id)
 +                      *res_id = t->type;
 +              t = btf__type_by_id(btf, t->type);
 +      }
 +
 +      return t;
 +}
 +
 +static const struct btf_type *
 +resolve_func_ptr(const struct btf *btf, __u32 id, __u32 *res_id)
 +{
 +      const struct btf_type *t;
 +
 +      t = skip_mods_and_typedefs(btf, id, NULL);
 +      if (!btf_is_ptr(t))
 +              return NULL;
 +
 +      t = skip_mods_and_typedefs(btf, t->type, res_id);
 +
 +      return btf_is_func_proto(t) ? t : NULL;
 +}
 +
 +static const char *__btf_kind_str(__u16 kind)
 +{
 +      switch (kind) {
 +      case BTF_KIND_UNKN: return "void";
 +      case BTF_KIND_INT: return "int";
 +      case BTF_KIND_PTR: return "ptr";
 +      case BTF_KIND_ARRAY: return "array";
 +      case BTF_KIND_STRUCT: return "struct";
 +      case BTF_KIND_UNION: return "union";
 +      case BTF_KIND_ENUM: return "enum";
 +      case BTF_KIND_FWD: return "fwd";
 +      case BTF_KIND_TYPEDEF: return "typedef";
 +      case BTF_KIND_VOLATILE: return "volatile";
 +      case BTF_KIND_CONST: return "const";
 +      case BTF_KIND_RESTRICT: return "restrict";
 +      case BTF_KIND_FUNC: return "func";
 +      case BTF_KIND_FUNC_PROTO: return "func_proto";
 +      case BTF_KIND_VAR: return "var";
 +      case BTF_KIND_DATASEC: return "datasec";
 +      case BTF_KIND_FLOAT: return "float";
 +      case BTF_KIND_DECL_TAG: return "decl_tag";
 +      case BTF_KIND_TYPE_TAG: return "type_tag";
 +      default: return "unknown";
 +      }
 +}
 +
 +const char *btf_kind_str(const struct btf_type *t)
 +{
 +      return __btf_kind_str(btf_kind(t));
 +}
 +
 +/*
 + * Fetch integer attribute of BTF map definition. Such attributes are
 + * represented using a pointer to an array, in which dimensionality of array
 + * encodes specified integer value. E.g., int (*type)[BPF_MAP_TYPE_ARRAY];
 + * encodes `type => BPF_MAP_TYPE_ARRAY` key/value pair completely using BTF
 + * type definition, while using only sizeof(void *) space in ELF data section.
 + */
 +static bool get_map_field_int(const char *map_name, const struct btf *btf,
 +                            const struct btf_member *m, __u32 *res)
 +{
 +      const struct btf_type *t = skip_mods_and_typedefs(btf, m->type, NULL);
 +      const char *name = btf__name_by_offset(btf, m->name_off);
 +      const struct btf_array *arr_info;
 +      const struct btf_type *arr_t;
 +
 +      if (!btf_is_ptr(t)) {
 +              pr_warn("map '%s': attr '%s': expected PTR, got %s.\n",
 +                      map_name, name, btf_kind_str(t));
 +              return false;
 +      }
 +
 +      arr_t = btf__type_by_id(btf, t->type);
 +      if (!arr_t) {
 +              pr_warn("map '%s': attr '%s': type [%u] not found.\n",
 +                      map_name, name, t->type);
 +              return false;
 +      }
 +      if (!btf_is_array(arr_t)) {
 +              pr_warn("map '%s': attr '%s': expected ARRAY, got %s.\n",
 +                      map_name, name, btf_kind_str(arr_t));
 +              return false;
 +      }
 +      arr_info = btf_array(arr_t);
 +      *res = arr_info->nelems;
 +      return true;
 +}
 +
 +static int build_map_pin_path(struct bpf_map *map, const char *path)
 +{
 +      char buf[PATH_MAX];
 +      int len;
 +
 +      if (!path)
 +              path = "/sys/fs/bpf";
 +
 +      len = snprintf(buf, PATH_MAX, "%s/%s", path, bpf_map__name(map));
 +      if (len < 0)
 +              return -EINVAL;
 +      else if (len >= PATH_MAX)
 +              return -ENAMETOOLONG;
 +
 +      return bpf_map__set_pin_path(map, buf);
 +}
 +
 +int parse_btf_map_def(const char *map_name, struct btf *btf,
 +                    const struct btf_type *def_t, bool strict,
 +                    struct btf_map_def *map_def, struct btf_map_def *inner_def)
 +{
 +      const struct btf_type *t;
 +      const struct btf_member *m;
 +      bool is_inner = inner_def == NULL;
 +      int vlen, i;
 +
 +      vlen = btf_vlen(def_t);
 +      m = btf_members(def_t);
 +      for (i = 0; i < vlen; i++, m++) {
 +              const char *name = btf__name_by_offset(btf, m->name_off);
 +
 +              if (!name) {
 +                      pr_warn("map '%s': invalid field #%d.\n", map_name, i);
 +                      return -EINVAL;
 +              }
 +              if (strcmp(name, "type") == 0) {
 +                      if (!get_map_field_int(map_name, btf, m, &map_def->map_type))
 +                              return -EINVAL;
 +                      map_def->parts |= MAP_DEF_MAP_TYPE;
 +              } else if (strcmp(name, "max_entries") == 0) {
 +                      if (!get_map_field_int(map_name, btf, m, &map_def->max_entries))
 +                              return -EINVAL;
 +                      map_def->parts |= MAP_DEF_MAX_ENTRIES;
 +              } else if (strcmp(name, "map_flags") == 0) {
 +                      if (!get_map_field_int(map_name, btf, m, &map_def->map_flags))
 +                              return -EINVAL;
 +                      map_def->parts |= MAP_DEF_MAP_FLAGS;
 +              } else if (strcmp(name, "numa_node") == 0) {
 +                      if (!get_map_field_int(map_name, btf, m, &map_def->numa_node))
 +                              return -EINVAL;
 +                      map_def->parts |= MAP_DEF_NUMA_NODE;
 +              } else if (strcmp(name, "key_size") == 0) {
 +                      __u32 sz;
 +
 +                      if (!get_map_field_int(map_name, btf, m, &sz))
 +                              return -EINVAL;
 +                      if (map_def->key_size && map_def->key_size != sz) {
 +                              pr_warn("map '%s': conflicting key size %u != %u.\n",
 +                                      map_name, map_def->key_size, sz);
 +                              return -EINVAL;
 +                      }
 +                      map_def->key_size = sz;
 +                      map_def->parts |= MAP_DEF_KEY_SIZE;
 +              } else if (strcmp(name, "key") == 0) {
 +                      __s64 sz;
 +
 +                      t = btf__type_by_id(btf, m->type);
 +                      if (!t) {
 +                              pr_warn("map '%s': key type [%d] not found.\n",
 +                                      map_name, m->type);
 +                              return -EINVAL;
 +                      }
 +                      if (!btf_is_ptr(t)) {
 +                              pr_warn("map '%s': key spec is not PTR: %s.\n",
 +                                      map_name, btf_kind_str(t));
 +                              return -EINVAL;
 +                      }
 +                      sz = btf__resolve_size(btf, t->type);
 +                      if (sz < 0) {
 +                              pr_warn("map '%s': can't determine key size for type [%u]: %zd.\n",
 +                                      map_name, t->type, (ssize_t)sz);
 +                              return sz;
 +                      }
 +                      if (map_def->key_size && map_def->key_size != sz) {
 +                              pr_warn("map '%s': conflicting key size %u != %zd.\n",
 +                                      map_name, map_def->key_size, (ssize_t)sz);
 +                              return -EINVAL;
 +                      }
 +                      map_def->key_size = sz;
 +                      map_def->key_type_id = t->type;
 +                      map_def->parts |= MAP_DEF_KEY_SIZE | MAP_DEF_KEY_TYPE;
 +              } else if (strcmp(name, "value_size") == 0) {
 +                      __u32 sz;
 +
 +                      if (!get_map_field_int(map_name, btf, m, &sz))
 +                              return -EINVAL;
 +                      if (map_def->value_size && map_def->value_size != sz) {
 +                              pr_warn("map '%s': conflicting value size %u != %u.\n",
 +                                      map_name, map_def->value_size, sz);
 +                              return -EINVAL;
 +                      }
 +                      map_def->value_size = sz;
 +                      map_def->parts |= MAP_DEF_VALUE_SIZE;
 +              } else if (strcmp(name, "value") == 0) {
 +                      __s64 sz;
 +
 +                      t = btf__type_by_id(btf, m->type);
 +                      if (!t) {
 +                              pr_warn("map '%s': value type [%d] not found.\n",
 +                                      map_name, m->type);
 +                              return -EINVAL;
 +                      }
 +                      if (!btf_is_ptr(t)) {
 +                              pr_warn("map '%s': value spec is not PTR: %s.\n",
 +                                      map_name, btf_kind_str(t));
 +                              return -EINVAL;
 +                      }
 +                      sz = btf__resolve_size(btf, t->type);
 +                      if (sz < 0) {
 +                              pr_warn("map '%s': can't determine value size for type [%u]: %zd.\n",
 +                                      map_name, t->type, (ssize_t)sz);
 +                              return sz;
 +                      }
 +                      if (map_def->value_size && map_def->value_size != sz) {
 +                              pr_warn("map '%s': conflicting value size %u != %zd.\n",
 +                                      map_name, map_def->value_size, (ssize_t)sz);
 +                              return -EINVAL;
 +                      }
 +                      map_def->value_size = sz;
 +                      map_def->value_type_id = t->type;
 +                      map_def->parts |= MAP_DEF_VALUE_SIZE | MAP_DEF_VALUE_TYPE;
 +              }
 +              else if (strcmp(name, "values") == 0) {
 +                      bool is_map_in_map = bpf_map_type__is_map_in_map(map_def->map_type);
 +                      bool is_prog_array = map_def->map_type == BPF_MAP_TYPE_PROG_ARRAY;
 +                      const char *desc = is_map_in_map ? "map-in-map inner" : "prog-array value";
 +                      char inner_map_name[128];
 +                      int err;
 +
 +                      if (is_inner) {
 +                              pr_warn("map '%s': multi-level inner maps not supported.\n",
 +                                      map_name);
 +                              return -ENOTSUP;
 +                      }
 +                      if (i != vlen - 1) {
 +                              pr_warn("map '%s': '%s' member should be last.\n",
 +                                      map_name, name);
 +                              return -EINVAL;
 +                      }
 +                      if (!is_map_in_map && !is_prog_array) {
 +                              pr_warn("map '%s': should be map-in-map or prog-array.\n",
 +                                      map_name);
 +                              return -ENOTSUP;
 +                      }
 +                      if (map_def->value_size && map_def->value_size != 4) {
 +                              pr_warn("map '%s': conflicting value size %u != 4.\n",
 +                                      map_name, map_def->value_size);
 +                              return -EINVAL;
 +                      }
 +                      map_def->value_size = 4;
 +                      t = btf__type_by_id(btf, m->type);
 +                      if (!t) {
 +                              pr_warn("map '%s': %s type [%d] not found.\n",
 +                                      map_name, desc, m->type);
 +                              return -EINVAL;
 +                      }
 +                      if (!btf_is_array(t) || btf_array(t)->nelems) {
 +                              pr_warn("map '%s': %s spec is not a zero-sized array.\n",
 +                                      map_name, desc);
 +                              return -EINVAL;
 +                      }
 +                      t = skip_mods_and_typedefs(btf, btf_array(t)->type, NULL);
 +                      if (!btf_is_ptr(t)) {
 +                              pr_warn("map '%s': %s def is of unexpected kind %s.\n",
 +                                      map_name, desc, btf_kind_str(t));
 +                              return -EINVAL;
 +                      }
 +                      t = skip_mods_and_typedefs(btf, t->type, NULL);
 +                      if (is_prog_array) {
 +                              if (!btf_is_func_proto(t)) {
 +                                      pr_warn("map '%s': prog-array value def is of unexpected kind %s.\n",
 +                                              map_name, btf_kind_str(t));
 +                                      return -EINVAL;
 +                              }
 +                              continue;
 +                      }
 +                      if (!btf_is_struct(t)) {
 +                              pr_warn("map '%s': map-in-map inner def is of unexpected kind %s.\n",
 +                                      map_name, btf_kind_str(t));
 +                              return -EINVAL;
 +                      }
 +
 +                      snprintf(inner_map_name, sizeof(inner_map_name), "%s.inner", map_name);
 +                      err = parse_btf_map_def(inner_map_name, btf, t, strict, inner_def, NULL);
 +                      if (err)
 +                              return err;
 +
 +                      map_def->parts |= MAP_DEF_INNER_MAP;
 +              } else if (strcmp(name, "pinning") == 0) {
 +                      __u32 val;
 +
 +                      if (is_inner) {
 +                              pr_warn("map '%s': inner def can't be pinned.\n", map_name);
 +                              return -EINVAL;
 +                      }
 +                      if (!get_map_field_int(map_name, btf, m, &val))
 +                              return -EINVAL;
 +                      if (val != LIBBPF_PIN_NONE && val != LIBBPF_PIN_BY_NAME) {
 +                              pr_warn("map '%s': invalid pinning value %u.\n",
 +                                      map_name, val);
 +                              return -EINVAL;
 +                      }
 +                      map_def->pinning = val;
 +                      map_def->parts |= MAP_DEF_PINNING;
 +              } else if (strcmp(name, "map_extra") == 0) {
 +                      __u32 map_extra;
 +
 +                      if (!get_map_field_int(map_name, btf, m, &map_extra))
 +                              return -EINVAL;
 +                      map_def->map_extra = map_extra;
 +                      map_def->parts |= MAP_DEF_MAP_EXTRA;
 +              } else {
 +                      if (strict) {
 +                              pr_warn("map '%s': unknown field '%s'.\n", map_name, name);
 +                              return -ENOTSUP;
 +                      }
 +                      pr_debug("map '%s': ignoring unknown field '%s'.\n", map_name, name);
 +              }
 +      }
 +
 +      if (map_def->map_type == BPF_MAP_TYPE_UNSPEC) {
 +              pr_warn("map '%s': map type isn't specified.\n", map_name);
 +              return -EINVAL;
 +      }
 +
 +      return 0;
 +}
 +
 +static void fill_map_from_def(struct bpf_map *map, const struct btf_map_def *def)
 +{
 +      map->def.type = def->map_type;
 +      map->def.key_size = def->key_size;
 +      map->def.value_size = def->value_size;
 +      map->def.max_entries = def->max_entries;
 +      map->def.map_flags = def->map_flags;
 +      map->map_extra = def->map_extra;
 +
 +      map->numa_node = def->numa_node;
 +      map->btf_key_type_id = def->key_type_id;
 +      map->btf_value_type_id = def->value_type_id;
 +
 +      if (def->parts & MAP_DEF_MAP_TYPE)
 +              pr_debug("map '%s': found type = %u.\n", map->name, def->map_type);
 +
 +      if (def->parts & MAP_DEF_KEY_TYPE)
 +              pr_debug("map '%s': found key [%u], sz = %u.\n",
 +                       map->name, def->key_type_id, def->key_size);
 +      else if (def->parts & MAP_DEF_KEY_SIZE)
 +              pr_debug("map '%s': found key_size = %u.\n", map->name, def->key_size);
 +
 +      if (def->parts & MAP_DEF_VALUE_TYPE)
 +              pr_debug("map '%s': found value [%u], sz = %u.\n",
 +                       map->name, def->value_type_id, def->value_size);
 +      else if (def->parts & MAP_DEF_VALUE_SIZE)
 +              pr_debug("map '%s': found value_size = %u.\n", map->name, def->value_size);
 +
 +      if (def->parts & MAP_DEF_MAX_ENTRIES)
 +              pr_debug("map '%s': found max_entries = %u.\n", map->name, def->max_entries);
 +      if (def->parts & MAP_DEF_MAP_FLAGS)
 +              pr_debug("map '%s': found map_flags = 0x%x.\n", map->name, def->map_flags);
 +      if (def->parts & MAP_DEF_MAP_EXTRA)
 +              pr_debug("map '%s': found map_extra = 0x%llx.\n", map->name,
 +                       (unsigned long long)def->map_extra);
 +      if (def->parts & MAP_DEF_PINNING)
 +              pr_debug("map '%s': found pinning = %u.\n", map->name, def->pinning);
 +      if (def->parts & MAP_DEF_NUMA_NODE)
 +              pr_debug("map '%s': found numa_node = %u.\n", map->name, def->numa_node);
 +
 +      if (def->parts & MAP_DEF_INNER_MAP)
 +              pr_debug("map '%s': found inner map definition.\n", map->name);
 +}
 +
 +static const char *btf_var_linkage_str(__u32 linkage)
 +{
 +      switch (linkage) {
 +      case BTF_VAR_STATIC: return "static";
 +      case BTF_VAR_GLOBAL_ALLOCATED: return "global";
 +      case BTF_VAR_GLOBAL_EXTERN: return "extern";
 +      default: return "unknown";
 +      }
 +}
 +
 +static int bpf_object__init_user_btf_map(struct bpf_object *obj,
 +                                       const struct btf_type *sec,
 +                                       int var_idx, int sec_idx,
 +                                       const Elf_Data *data, bool strict,
 +                                       const char *pin_root_path)
 +{
 +      struct btf_map_def map_def = {}, inner_def = {};
 +      const struct btf_type *var, *def;
 +      const struct btf_var_secinfo *vi;
 +      const struct btf_var *var_extra;
 +      const char *map_name;
 +      struct bpf_map *map;
 +      int err;
 +
 +      vi = btf_var_secinfos(sec) + var_idx;
 +      var = btf__type_by_id(obj->btf, vi->type);
 +      var_extra = btf_var(var);
 +      map_name = btf__name_by_offset(obj->btf, var->name_off);
 +
 +      if (map_name == NULL || map_name[0] == '\0') {
 +              pr_warn("map #%d: empty name.\n", var_idx);
 +              return -EINVAL;
 +      }
 +      if ((__u64)vi->offset + vi->size > data->d_size) {
 +              pr_warn("map '%s' BTF data is corrupted.\n", map_name);
 +              return -EINVAL;
 +      }
 +      if (!btf_is_var(var)) {
 +              pr_warn("map '%s': unexpected var kind %s.\n",
 +                      map_name, btf_kind_str(var));
 +              return -EINVAL;
 +      }
 +      if (var_extra->linkage != BTF_VAR_GLOBAL_ALLOCATED) {
 +              pr_warn("map '%s': unsupported map linkage %s.\n",
 +                      map_name, btf_var_linkage_str(var_extra->linkage));
 +              return -EOPNOTSUPP;
 +      }
 +
 +      def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
 +      if (!btf_is_struct(def)) {
 +              pr_warn("map '%s': unexpected def kind %s.\n",
 +                      map_name, btf_kind_str(var));
 +              return -EINVAL;
 +      }
 +      if (def->size > vi->size) {
 +              pr_warn("map '%s': invalid def size.\n", map_name);
 +              return -EINVAL;
 +      }
 +
 +      map = bpf_object__add_map(obj);
 +      if (IS_ERR(map))
 +              return PTR_ERR(map);
 +      map->name = strdup(map_name);
 +      if (!map->name) {
 +              pr_warn("map '%s': failed to alloc map name.\n", map_name);
 +              return -ENOMEM;
 +      }
 +      map->libbpf_type = LIBBPF_MAP_UNSPEC;
 +      map->def.type = BPF_MAP_TYPE_UNSPEC;
 +      map->sec_idx = sec_idx;
 +      map->sec_offset = vi->offset;
 +      map->btf_var_idx = var_idx;
 +      pr_debug("map '%s': at sec_idx %d, offset %zu.\n",
 +               map_name, map->sec_idx, map->sec_offset);
 +
 +      err = parse_btf_map_def(map->name, obj->btf, def, strict, &map_def, &inner_def);
 +      if (err)
 +              return err;
 +
 +      fill_map_from_def(map, &map_def);
 +
 +      if (map_def.pinning == LIBBPF_PIN_BY_NAME) {
 +              err = build_map_pin_path(map, pin_root_path);
 +              if (err) {
 +                      pr_warn("map '%s': couldn't build pin path.\n", map->name);
 +                      return err;
 +              }
 +      }
 +
 +      if (map_def.parts & MAP_DEF_INNER_MAP) {
 +              map->inner_map = calloc(1, sizeof(*map->inner_map));
 +              if (!map->inner_map)
 +                      return -ENOMEM;
 +              map->inner_map->fd = -1;
 +              map->inner_map->sec_idx = sec_idx;
 +              map->inner_map->name = malloc(strlen(map_name) + sizeof(".inner") + 1);
 +              if (!map->inner_map->name)
 +                      return -ENOMEM;
 +              sprintf(map->inner_map->name, "%s.inner", map_name);
 +
 +              fill_map_from_def(map->inner_map, &inner_def);
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object__init_user_btf_maps(struct bpf_object *obj, bool strict,
 +                                        const char *pin_root_path)
 +{
 +      const struct btf_type *sec = NULL;
 +      int nr_types, i, vlen, err;
 +      const struct btf_type *t;
 +      const char *name;
 +      Elf_Data *data;
 +      Elf_Scn *scn;
 +
 +      if (obj->efile.btf_maps_shndx < 0)
 +              return 0;
 +
 +      scn = elf_sec_by_idx(obj, obj->efile.btf_maps_shndx);
 +      data = elf_sec_data(obj, scn);
 +      if (!scn || !data) {
 +              pr_warn("elf: failed to get %s map definitions for %s\n",
 +                      MAPS_ELF_SEC, obj->path);
 +              return -EINVAL;
 +      }
 +
 +      nr_types = btf__type_cnt(obj->btf);
 +      for (i = 1; i < nr_types; i++) {
 +              t = btf__type_by_id(obj->btf, i);
 +              if (!btf_is_datasec(t))
 +                      continue;
 +              name = btf__name_by_offset(obj->btf, t->name_off);
 +              if (strcmp(name, MAPS_ELF_SEC) == 0) {
 +                      sec = t;
 +                      obj->efile.btf_maps_sec_btf_id = i;
 +                      break;
 +              }
 +      }
 +
 +      if (!sec) {
 +              pr_warn("DATASEC '%s' not found.\n", MAPS_ELF_SEC);
 +              return -ENOENT;
 +      }
 +
 +      vlen = btf_vlen(sec);
 +      for (i = 0; i < vlen; i++) {
 +              err = bpf_object__init_user_btf_map(obj, sec, i,
 +                                                  obj->efile.btf_maps_shndx,
 +                                                  data, strict,
 +                                                  pin_root_path);
 +              if (err)
 +                      return err;
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object__init_maps(struct bpf_object *obj,
 +                               const struct bpf_object_open_opts *opts)
 +{
 +      const char *pin_root_path;
 +      bool strict;
 +      int err;
 +
 +      strict = !OPTS_GET(opts, relaxed_maps, false);
 +      pin_root_path = OPTS_GET(opts, pin_root_path, NULL);
 +
 +      err = bpf_object__init_user_maps(obj, strict);
 +      err = err ?: bpf_object__init_user_btf_maps(obj, strict, pin_root_path);
 +      err = err ?: bpf_object__init_global_data_maps(obj);
 +      err = err ?: bpf_object__init_kconfig_map(obj);
 +      err = err ?: bpf_object__init_struct_ops_maps(obj);
 +
 +      return err;
 +}
 +
 +static bool section_have_execinstr(struct bpf_object *obj, int idx)
 +{
 +      Elf64_Shdr *sh;
 +
 +      sh = elf_sec_hdr(obj, elf_sec_by_idx(obj, idx));
 +      if (!sh)
 +              return false;
 +
 +      return sh->sh_flags & SHF_EXECINSTR;
 +}
 +
 +static bool btf_needs_sanitization(struct bpf_object *obj)
 +{
 +      bool has_func_global = kernel_supports(obj, FEAT_BTF_GLOBAL_FUNC);
 +      bool has_datasec = kernel_supports(obj, FEAT_BTF_DATASEC);
 +      bool has_float = kernel_supports(obj, FEAT_BTF_FLOAT);
 +      bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
 +      bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
 +      bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
 +
 +      return !has_func || !has_datasec || !has_func_global || !has_float ||
 +             !has_decl_tag || !has_type_tag;
 +}
 +
 +static void bpf_object__sanitize_btf(struct bpf_object *obj, struct btf *btf)
 +{
 +      bool has_func_global = kernel_supports(obj, FEAT_BTF_GLOBAL_FUNC);
 +      bool has_datasec = kernel_supports(obj, FEAT_BTF_DATASEC);
 +      bool has_float = kernel_supports(obj, FEAT_BTF_FLOAT);
 +      bool has_func = kernel_supports(obj, FEAT_BTF_FUNC);
 +      bool has_decl_tag = kernel_supports(obj, FEAT_BTF_DECL_TAG);
 +      bool has_type_tag = kernel_supports(obj, FEAT_BTF_TYPE_TAG);
 +      struct btf_type *t;
 +      int i, j, vlen;
 +
 +      for (i = 1; i < btf__type_cnt(btf); i++) {
 +              t = (struct btf_type *)btf__type_by_id(btf, i);
 +
 +              if ((!has_datasec && btf_is_var(t)) || (!has_decl_tag && btf_is_decl_tag(t))) {
 +                      /* replace VAR/DECL_TAG with INT */
 +                      t->info = BTF_INFO_ENC(BTF_KIND_INT, 0, 0);
 +                      /*
 +                       * using size = 1 is the safest choice, 4 will be too
 +                       * big and cause kernel BTF validation failure if
 +                       * original variable took less than 4 bytes
 +                       */
 +                      t->size = 1;
 +                      *(int *)(t + 1) = BTF_INT_ENC(0, 0, 8);
 +              } else if (!has_datasec && btf_is_datasec(t)) {
 +                      /* replace DATASEC with STRUCT */
 +                      const struct btf_var_secinfo *v = btf_var_secinfos(t);
 +                      struct btf_member *m = btf_members(t);
 +                      struct btf_type *vt;
 +                      char *name;
 +
 +                      name = (char *)btf__name_by_offset(btf, t->name_off);
 +                      while (*name) {
 +                              if (*name == '.')
 +                                      *name = '_';
 +                              name++;
 +                      }
 +
 +                      vlen = btf_vlen(t);
 +                      t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, vlen);
 +                      for (j = 0; j < vlen; j++, v++, m++) {
 +                              /* order of field assignments is important */
 +                              m->offset = v->offset * 8;
 +                              m->type = v->type;
 +                              /* preserve variable name as member name */
 +                              vt = (void *)btf__type_by_id(btf, v->type);
 +                              m->name_off = vt->name_off;
 +                      }
 +              } else if (!has_func && btf_is_func_proto(t)) {
 +                      /* replace FUNC_PROTO with ENUM */
 +                      vlen = btf_vlen(t);
 +                      t->info = BTF_INFO_ENC(BTF_KIND_ENUM, 0, vlen);
 +                      t->size = sizeof(__u32); /* kernel enforced */
 +              } else if (!has_func && btf_is_func(t)) {
 +                      /* replace FUNC with TYPEDEF */
 +                      t->info = BTF_INFO_ENC(BTF_KIND_TYPEDEF, 0, 0);
 +              } else if (!has_func_global && btf_is_func(t)) {
 +                      /* replace BTF_FUNC_GLOBAL with BTF_FUNC_STATIC */
 +                      t->info = BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0);
 +              } else if (!has_float && btf_is_float(t)) {
 +                      /* replace FLOAT with an equally-sized empty STRUCT;
 +                       * since C compilers do not accept e.g. "float" as a
 +                       * valid struct name, make it anonymous
 +                       */
 +                      t->name_off = 0;
 +                      t->info = BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 0);
 +              } else if (!has_type_tag && btf_is_type_tag(t)) {
 +                      /* replace TYPE_TAG with a CONST */
 +                      t->name_off = 0;
 +                      t->info = BTF_INFO_ENC(BTF_KIND_CONST, 0, 0);
 +              }
 +      }
 +}
 +
 +static bool libbpf_needs_btf(const struct bpf_object *obj)
 +{
 +      return obj->efile.btf_maps_shndx >= 0 ||
 +             obj->efile.st_ops_shndx >= 0 ||
 +             obj->nr_extern > 0;
 +}
 +
 +static bool kernel_needs_btf(const struct bpf_object *obj)
 +{
 +      return obj->efile.st_ops_shndx >= 0;
 +}
 +
 +static int bpf_object__init_btf(struct bpf_object *obj,
 +                              Elf_Data *btf_data,
 +                              Elf_Data *btf_ext_data)
 +{
 +      int err = -ENOENT;
 +
 +      if (btf_data) {
 +              obj->btf = btf__new(btf_data->d_buf, btf_data->d_size);
 +              err = libbpf_get_error(obj->btf);
 +              if (err) {
 +                      obj->btf = NULL;
 +                      pr_warn("Error loading ELF section %s: %d.\n", BTF_ELF_SEC, err);
 +                      goto out;
 +              }
 +              /* enforce 8-byte pointers for BPF-targeted BTFs */
 +              btf__set_pointer_size(obj->btf, 8);
 +      }
 +      if (btf_ext_data) {
 +              if (!obj->btf) {
 +                      pr_debug("Ignore ELF section %s because its depending ELF section %s is not found.\n",
 +                               BTF_EXT_ELF_SEC, BTF_ELF_SEC);
 +                      goto out;
 +              }
 +              obj->btf_ext = btf_ext__new(btf_ext_data->d_buf, btf_ext_data->d_size);
 +              err = libbpf_get_error(obj->btf_ext);
 +              if (err) {
 +                      pr_warn("Error loading ELF section %s: %d. Ignored and continue.\n",
 +                              BTF_EXT_ELF_SEC, err);
 +                      obj->btf_ext = NULL;
 +                      goto out;
 +              }
 +      }
 +out:
 +      if (err && libbpf_needs_btf(obj)) {
 +              pr_warn("BTF is required, but is missing or corrupted.\n");
 +              return err;
 +      }
 +      return 0;
 +}
 +
 +static int compare_vsi_off(const void *_a, const void *_b)
 +{
 +      const struct btf_var_secinfo *a = _a;
 +      const struct btf_var_secinfo *b = _b;
 +
 +      return a->offset - b->offset;
 +}
 +
 +static int btf_fixup_datasec(struct bpf_object *obj, struct btf *btf,
 +                           struct btf_type *t)
 +{
 +      __u32 size = 0, off = 0, i, vars = btf_vlen(t);
 +      const char *name = btf__name_by_offset(btf, t->name_off);
 +      const struct btf_type *t_var;
 +      struct btf_var_secinfo *vsi;
 +      const struct btf_var *var;
 +      int ret;
 +
 +      if (!name) {
 +              pr_debug("No name found in string section for DATASEC kind.\n");
 +              return -ENOENT;
 +      }
 +
 +      /* .extern datasec size and var offsets were set correctly during
 +       * extern collection step, so just skip straight to sorting variables
 +       */
 +      if (t->size)
 +              goto sort_vars;
 +
 +      ret = find_elf_sec_sz(obj, name, &size);
 +      if (ret || !size || (t->size && t->size != size)) {
 +              pr_debug("Invalid size for section %s: %u bytes\n", name, size);
 +              return -ENOENT;
 +      }
 +
 +      t->size = size;
 +
 +      for (i = 0, vsi = btf_var_secinfos(t); i < vars; i++, vsi++) {
 +              t_var = btf__type_by_id(btf, vsi->type);
 +              if (!t_var || !btf_is_var(t_var)) {
 +                      pr_debug("Non-VAR type seen in section %s\n", name);
 +                      return -EINVAL;
 +              }
 +
 +              var = btf_var(t_var);
 +              if (var->linkage == BTF_VAR_STATIC)
 +                      continue;
 +
 +              name = btf__name_by_offset(btf, t_var->name_off);
 +              if (!name) {
 +                      pr_debug("No name found in string section for VAR kind\n");
 +                      return -ENOENT;
 +              }
 +
 +              ret = find_elf_var_offset(obj, name, &off);
 +              if (ret) {
 +                      pr_debug("No offset found in symbol table for VAR %s\n",
 +                               name);
 +                      return -ENOENT;
 +              }
 +
 +              vsi->offset = off;
 +      }
 +
 +sort_vars:
 +      qsort(btf_var_secinfos(t), vars, sizeof(*vsi), compare_vsi_off);
 +      return 0;
 +}
 +
 +static int btf_finalize_data(struct bpf_object *obj, struct btf *btf)
 +{
 +      int err = 0;
 +      __u32 i, n = btf__type_cnt(btf);
 +
 +      for (i = 1; i < n; i++) {
 +              struct btf_type *t = btf_type_by_id(btf, i);
 +
 +              /* Loader needs to fix up some of the things compiler
 +               * couldn't get its hands on while emitting BTF. This
 +               * is section size and global variable offset. We use
 +               * the info from the ELF itself for this purpose.
 +               */
 +              if (btf_is_datasec(t)) {
 +                      err = btf_fixup_datasec(obj, btf, t);
 +                      if (err)
 +                              break;
 +              }
 +      }
 +
 +      return libbpf_err(err);
 +}
 +
 +int btf__finalize_data(struct bpf_object *obj, struct btf *btf)
 +{
 +      return btf_finalize_data(obj, btf);
 +}
 +
 +static int bpf_object__finalize_btf(struct bpf_object *obj)
 +{
 +      int err;
 +
 +      if (!obj->btf)
 +              return 0;
 +
 +      err = btf_finalize_data(obj, obj->btf);
 +      if (err) {
 +              pr_warn("Error finalizing %s: %d.\n", BTF_ELF_SEC, err);
 +              return err;
 +      }
 +
 +      return 0;
 +}
 +
 +static bool prog_needs_vmlinux_btf(struct bpf_program *prog)
 +{
 +      if (prog->type == BPF_PROG_TYPE_STRUCT_OPS ||
 +          prog->type == BPF_PROG_TYPE_LSM)
 +              return true;
 +
 +      /* BPF_PROG_TYPE_TRACING programs which do not attach to other programs
 +       * also need vmlinux BTF
 +       */
 +      if (prog->type == BPF_PROG_TYPE_TRACING && !prog->attach_prog_fd)
 +              return true;
 +
 +      return false;
 +}
 +
 +static bool obj_needs_vmlinux_btf(const struct bpf_object *obj)
 +{
 +      struct bpf_program *prog;
 +      int i;
 +
 +      /* CO-RE relocations need kernel BTF, only when btf_custom_path
 +       * is not specified
 +       */
 +      if (obj->btf_ext && obj->btf_ext->core_relo_info.len && !obj->btf_custom_path)
 +              return true;
 +
 +      /* Support for typed ksyms needs kernel BTF */
 +      for (i = 0; i < obj->nr_extern; i++) {
 +              const struct extern_desc *ext;
 +
 +              ext = &obj->externs[i];
 +              if (ext->type == EXT_KSYM && ext->ksym.type_id)
 +                      return true;
 +      }
 +
 +      bpf_object__for_each_program(prog, obj) {
 +              if (!prog->load)
 +                      continue;
 +              if (prog_needs_vmlinux_btf(prog))
 +                      return true;
 +      }
 +
 +      return false;
 +}
 +
 +static int bpf_object__load_vmlinux_btf(struct bpf_object *obj, bool force)
 +{
 +      int err;
 +
 +      /* btf_vmlinux could be loaded earlier */
 +      if (obj->btf_vmlinux || obj->gen_loader)
 +              return 0;
 +
 +      if (!force && !obj_needs_vmlinux_btf(obj))
 +              return 0;
 +
 +      obj->btf_vmlinux = btf__load_vmlinux_btf();
 +      err = libbpf_get_error(obj->btf_vmlinux);
 +      if (err) {
 +              pr_warn("Error loading vmlinux BTF: %d\n", err);
 +              obj->btf_vmlinux = NULL;
 +              return err;
 +      }
 +      return 0;
 +}
 +
 +static int bpf_object__sanitize_and_load_btf(struct bpf_object *obj)
 +{
 +      struct btf *kern_btf = obj->btf;
 +      bool btf_mandatory, sanitize;
 +      int i, err = 0;
 +
 +      if (!obj->btf)
 +              return 0;
 +
 +      if (!kernel_supports(obj, FEAT_BTF)) {
 +              if (kernel_needs_btf(obj)) {
 +                      err = -EOPNOTSUPP;
 +                      goto report;
 +              }
 +              pr_debug("Kernel doesn't support BTF, skipping uploading it.\n");
 +              return 0;
 +      }
 +
 +      /* Even though some subprogs are global/weak, user might prefer more
 +       * permissive BPF verification process that BPF verifier performs for
 +       * static functions, taking into account more context from the caller
 +       * functions. In such case, they need to mark such subprogs with
 +       * __attribute__((visibility("hidden"))) and libbpf will adjust
 +       * corresponding FUNC BTF type to be marked as static and trigger more
 +       * involved BPF verification process.
 +       */
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              struct bpf_program *prog = &obj->programs[i];
 +              struct btf_type *t;
 +              const char *name;
 +              int j, n;
 +
 +              if (!prog->mark_btf_static || !prog_is_subprog(obj, prog))
 +                      continue;
 +
 +              n = btf__type_cnt(obj->btf);
 +              for (j = 1; j < n; j++) {
 +                      t = btf_type_by_id(obj->btf, j);
 +                      if (!btf_is_func(t) || btf_func_linkage(t) != BTF_FUNC_GLOBAL)
 +                              continue;
 +
 +                      name = btf__str_by_offset(obj->btf, t->name_off);
 +                      if (strcmp(name, prog->name) != 0)
 +                              continue;
 +
 +                      t->info = btf_type_info(BTF_KIND_FUNC, BTF_FUNC_STATIC, 0);
 +                      break;
 +              }
 +      }
 +
 +      sanitize = btf_needs_sanitization(obj);
 +      if (sanitize) {
 +              const void *raw_data;
 +              __u32 sz;
 +
 +              /* clone BTF to sanitize a copy and leave the original intact */
 +              raw_data = btf__raw_data(obj->btf, &sz);
 +              kern_btf = btf__new(raw_data, sz);
 +              err = libbpf_get_error(kern_btf);
 +              if (err)
 +                      return err;
 +
 +              /* enforce 8-byte pointers for BPF-targeted BTFs */
 +              btf__set_pointer_size(obj->btf, 8);
 +              bpf_object__sanitize_btf(obj, kern_btf);
 +      }
 +
 +      if (obj->gen_loader) {
 +              __u32 raw_size = 0;
 +              const void *raw_data = btf__raw_data(kern_btf, &raw_size);
 +
 +              if (!raw_data)
 +                      return -ENOMEM;
 +              bpf_gen__load_btf(obj->gen_loader, raw_data, raw_size);
 +              /* Pretend to have valid FD to pass various fd >= 0 checks.
 +               * This fd == 0 will not be used with any syscall and will be reset to -1 eventually.
 +               */
 +              btf__set_fd(kern_btf, 0);
 +      } else {
 +              /* currently BPF_BTF_LOAD only supports log_level 1 */
 +              err = btf_load_into_kernel(kern_btf, obj->log_buf, obj->log_size,
 +                                         obj->log_level ? 1 : 0);
 +      }
 +      if (sanitize) {
 +              if (!err) {
 +                      /* move fd to libbpf's BTF */
 +                      btf__set_fd(obj->btf, btf__fd(kern_btf));
 +                      btf__set_fd(kern_btf, -1);
 +              }
 +              btf__free(kern_btf);
 +      }
 +report:
 +      if (err) {
 +              btf_mandatory = kernel_needs_btf(obj);
 +              pr_warn("Error loading .BTF into kernel: %d. %s\n", err,
 +                      btf_mandatory ? "BTF is mandatory, can't proceed."
 +                                    : "BTF is optional, ignoring.");
 +              if (!btf_mandatory)
 +                      err = 0;
 +      }
 +      return err;
 +}
 +
 +static const char *elf_sym_str(const struct bpf_object *obj, size_t off)
 +{
 +      const char *name;
 +
 +      name = elf_strptr(obj->efile.elf, obj->efile.strtabidx, off);
 +      if (!name) {
 +              pr_warn("elf: failed to get section name string at offset %zu from %s: %s\n",
 +                      off, obj->path, elf_errmsg(-1));
 +              return NULL;
 +      }
 +
 +      return name;
 +}
 +
 +static const char *elf_sec_str(const struct bpf_object *obj, size_t off)
 +{
 +      const char *name;
 +
 +      name = elf_strptr(obj->efile.elf, obj->efile.shstrndx, off);
 +      if (!name) {
 +              pr_warn("elf: failed to get section name string at offset %zu from %s: %s\n",
 +                      off, obj->path, elf_errmsg(-1));
 +              return NULL;
 +      }
 +
 +      return name;
 +}
 +
 +static Elf_Scn *elf_sec_by_idx(const struct bpf_object *obj, size_t idx)
 +{
 +      Elf_Scn *scn;
 +
 +      scn = elf_getscn(obj->efile.elf, idx);
 +      if (!scn) {
 +              pr_warn("elf: failed to get section(%zu) from %s: %s\n",
 +                      idx, obj->path, elf_errmsg(-1));
 +              return NULL;
 +      }
 +      return scn;
 +}
 +
 +static Elf_Scn *elf_sec_by_name(const struct bpf_object *obj, const char *name)
 +{
 +      Elf_Scn *scn = NULL;
 +      Elf *elf = obj->efile.elf;
 +      const char *sec_name;
 +
 +      while ((scn = elf_nextscn(elf, scn)) != NULL) {
 +              sec_name = elf_sec_name(obj, scn);
 +              if (!sec_name)
 +                      return NULL;
 +
 +              if (strcmp(sec_name, name) != 0)
 +                      continue;
 +
 +              return scn;
 +      }
 +      return NULL;
 +}
 +
 +static Elf64_Shdr *elf_sec_hdr(const struct bpf_object *obj, Elf_Scn *scn)
 +{
 +      Elf64_Shdr *shdr;
 +
 +      if (!scn)
 +              return NULL;
 +
 +      shdr = elf64_getshdr(scn);
 +      if (!shdr) {
 +              pr_warn("elf: failed to get section(%zu) header from %s: %s\n",
 +                      elf_ndxscn(scn), obj->path, elf_errmsg(-1));
 +              return NULL;
 +      }
 +
 +      return shdr;
 +}
 +
 +static const char *elf_sec_name(const struct bpf_object *obj, Elf_Scn *scn)
 +{
 +      const char *name;
 +      Elf64_Shdr *sh;
 +
 +      if (!scn)
 +              return NULL;
 +
 +      sh = elf_sec_hdr(obj, scn);
 +      if (!sh)
 +              return NULL;
 +
 +      name = elf_sec_str(obj, sh->sh_name);
 +      if (!name) {
 +              pr_warn("elf: failed to get section(%zu) name from %s: %s\n",
 +                      elf_ndxscn(scn), obj->path, elf_errmsg(-1));
 +              return NULL;
 +      }
 +
 +      return name;
 +}
 +
 +static Elf_Data *elf_sec_data(const struct bpf_object *obj, Elf_Scn *scn)
 +{
 +      Elf_Data *data;
 +
 +      if (!scn)
 +              return NULL;
 +
 +      data = elf_getdata(scn, 0);
 +      if (!data) {
 +              pr_warn("elf: failed to get section(%zu) %s data from %s: %s\n",
 +                      elf_ndxscn(scn), elf_sec_name(obj, scn) ?: "<?>",
 +                      obj->path, elf_errmsg(-1));
 +              return NULL;
 +      }
 +
 +      return data;
 +}
 +
 +static Elf64_Sym *elf_sym_by_idx(const struct bpf_object *obj, size_t idx)
 +{
 +      if (idx >= obj->efile.symbols->d_size / sizeof(Elf64_Sym))
 +              return NULL;
 +
 +      return (Elf64_Sym *)obj->efile.symbols->d_buf + idx;
 +}
 +
 +static Elf64_Rel *elf_rel_by_idx(Elf_Data *data, size_t idx)
 +{
 +      if (idx >= data->d_size / sizeof(Elf64_Rel))
 +              return NULL;
 +
 +      return (Elf64_Rel *)data->d_buf + idx;
 +}
 +
 +static bool is_sec_name_dwarf(const char *name)
 +{
 +      /* approximation, but the actual list is too long */
 +      return str_has_pfx(name, ".debug_");
 +}
 +
 +static bool ignore_elf_section(Elf64_Shdr *hdr, const char *name)
 +{
 +      /* no special handling of .strtab */
 +      if (hdr->sh_type == SHT_STRTAB)
 +              return true;
 +
 +      /* ignore .llvm_addrsig section as well */
 +      if (hdr->sh_type == SHT_LLVM_ADDRSIG)
 +              return true;
 +
 +      /* no subprograms will lead to an empty .text section, ignore it */
 +      if (hdr->sh_type == SHT_PROGBITS && hdr->sh_size == 0 &&
 +          strcmp(name, ".text") == 0)
 +              return true;
 +
 +      /* DWARF sections */
 +      if (is_sec_name_dwarf(name))
 +              return true;
 +
 +      if (str_has_pfx(name, ".rel")) {
 +              name += sizeof(".rel") - 1;
 +              /* DWARF section relocations */
 +              if (is_sec_name_dwarf(name))
 +                      return true;
 +
 +              /* .BTF and .BTF.ext don't need relocations */
 +              if (strcmp(name, BTF_ELF_SEC) == 0 ||
 +                  strcmp(name, BTF_EXT_ELF_SEC) == 0)
 +                      return true;
 +      }
 +
 +      return false;
 +}
 +
 +static int cmp_progs(const void *_a, const void *_b)
 +{
 +      const struct bpf_program *a = _a;
 +      const struct bpf_program *b = _b;
 +
 +      if (a->sec_idx != b->sec_idx)
 +              return a->sec_idx < b->sec_idx ? -1 : 1;
 +
 +      /* sec_insn_off can't be the same within the section */
 +      return a->sec_insn_off < b->sec_insn_off ? -1 : 1;
 +}
 +
 +static int bpf_object__elf_collect(struct bpf_object *obj)
 +{
 +      struct elf_sec_desc *sec_desc;
 +      Elf *elf = obj->efile.elf;
 +      Elf_Data *btf_ext_data = NULL;
 +      Elf_Data *btf_data = NULL;
 +      int idx = 0, err = 0;
 +      const char *name;
 +      Elf_Data *data;
 +      Elf_Scn *scn;
 +      Elf64_Shdr *sh;
 +
 +      /* ELF section indices are 0-based, but sec #0 is special "invalid"
 +       * section. e_shnum does include sec #0, so e_shnum is the necessary
 +       * size of an array to keep all the sections.
 +       */
 +      obj->efile.sec_cnt = obj->efile.ehdr->e_shnum;
 +      obj->efile.secs = calloc(obj->efile.sec_cnt, sizeof(*obj->efile.secs));
 +      if (!obj->efile.secs)
 +              return -ENOMEM;
 +
 +      /* a bunch of ELF parsing functionality depends on processing symbols,
 +       * so do the first pass and find the symbol table
 +       */
 +      scn = NULL;
 +      while ((scn = elf_nextscn(elf, scn)) != NULL) {
 +              sh = elf_sec_hdr(obj, scn);
 +              if (!sh)
 +                      return -LIBBPF_ERRNO__FORMAT;
 +
 +              if (sh->sh_type == SHT_SYMTAB) {
 +                      if (obj->efile.symbols) {
 +                              pr_warn("elf: multiple symbol tables in %s\n", obj->path);
 +                              return -LIBBPF_ERRNO__FORMAT;
 +                      }
 +
 +                      data = elf_sec_data(obj, scn);
 +                      if (!data)
 +                              return -LIBBPF_ERRNO__FORMAT;
 +
 +                      idx = elf_ndxscn(scn);
 +
 +                      obj->efile.symbols = data;
 +                      obj->efile.symbols_shndx = idx;
 +                      obj->efile.strtabidx = sh->sh_link;
 +              }
 +      }
 +
 +      if (!obj->efile.symbols) {
 +              pr_warn("elf: couldn't find symbol table in %s, stripped object file?\n",
 +                      obj->path);
 +              return -ENOENT;
 +      }
 +
 +      scn = NULL;
 +      while ((scn = elf_nextscn(elf, scn)) != NULL) {
 +              idx = elf_ndxscn(scn);
 +              sec_desc = &obj->efile.secs[idx];
 +
 +              sh = elf_sec_hdr(obj, scn);
 +              if (!sh)
 +                      return -LIBBPF_ERRNO__FORMAT;
 +
 +              name = elf_sec_str(obj, sh->sh_name);
 +              if (!name)
 +                      return -LIBBPF_ERRNO__FORMAT;
 +
 +              if (ignore_elf_section(sh, name))
 +                      continue;
 +
 +              data = elf_sec_data(obj, scn);
 +              if (!data)
 +                      return -LIBBPF_ERRNO__FORMAT;
 +
 +              pr_debug("elf: section(%d) %s, size %ld, link %d, flags %lx, type=%d\n",
 +                       idx, name, (unsigned long)data->d_size,
 +                       (int)sh->sh_link, (unsigned long)sh->sh_flags,
 +                       (int)sh->sh_type);
 +
 +              if (strcmp(name, "license") == 0) {
 +                      err = bpf_object__init_license(obj, data->d_buf, data->d_size);
 +                      if (err)
 +                              return err;
 +              } else if (strcmp(name, "version") == 0) {
 +                      err = bpf_object__init_kversion(obj, data->d_buf, data->d_size);
 +                      if (err)
 +                              return err;
 +              } else if (strcmp(name, "maps") == 0) {
 +                      obj->efile.maps_shndx = idx;
 +              } else if (strcmp(name, MAPS_ELF_SEC) == 0) {
 +                      obj->efile.btf_maps_shndx = idx;
 +              } else if (strcmp(name, BTF_ELF_SEC) == 0) {
 +                      if (sh->sh_type != SHT_PROGBITS)
 +                              return -LIBBPF_ERRNO__FORMAT;
 +                      btf_data = data;
 +              } else if (strcmp(name, BTF_EXT_ELF_SEC) == 0) {
 +                      if (sh->sh_type != SHT_PROGBITS)
 +                              return -LIBBPF_ERRNO__FORMAT;
 +                      btf_ext_data = data;
 +              } else if (sh->sh_type == SHT_SYMTAB) {
 +                      /* already processed during the first pass above */
 +              } else if (sh->sh_type == SHT_PROGBITS && data->d_size > 0) {
 +                      if (sh->sh_flags & SHF_EXECINSTR) {
 +                              if (strcmp(name, ".text") == 0)
 +                                      obj->efile.text_shndx = idx;
 +                              err = bpf_object__add_programs(obj, data, name, idx);
 +                              if (err)
 +                                      return err;
 +                      } else if (strcmp(name, DATA_SEC) == 0 ||
 +                                 str_has_pfx(name, DATA_SEC ".")) {
 +                              sec_desc->sec_type = SEC_DATA;
 +                              sec_desc->shdr = sh;
 +                              sec_desc->data = data;
 +                      } else if (strcmp(name, RODATA_SEC) == 0 ||
 +                                 str_has_pfx(name, RODATA_SEC ".")) {
 +                              sec_desc->sec_type = SEC_RODATA;
 +                              sec_desc->shdr = sh;
 +                              sec_desc->data = data;
 +                      } else if (strcmp(name, STRUCT_OPS_SEC) == 0) {
 +                              obj->efile.st_ops_data = data;
 +                              obj->efile.st_ops_shndx = idx;
 +                      } else {
 +                              pr_info("elf: skipping unrecognized data section(%d) %s\n",
 +                                      idx, name);
 +                      }
 +              } else if (sh->sh_type == SHT_REL) {
 +                      int targ_sec_idx = sh->sh_info; /* points to other section */
 +
 +                      if (sh->sh_entsize != sizeof(Elf64_Rel) ||
 +                          targ_sec_idx >= obj->efile.sec_cnt)
 +                              return -LIBBPF_ERRNO__FORMAT;
 +
 +                      /* Only do relo for section with exec instructions */
 +                      if (!section_have_execinstr(obj, targ_sec_idx) &&
 +                          strcmp(name, ".rel" STRUCT_OPS_SEC) &&
 +                          strcmp(name, ".rel" MAPS_ELF_SEC)) {
 +                              pr_info("elf: skipping relo section(%d) %s for section(%d) %s\n",
 +                                      idx, name, targ_sec_idx,
 +                                      elf_sec_name(obj, elf_sec_by_idx(obj, targ_sec_idx)) ?: "<?>");
 +                              continue;
 +                      }
 +
 +                      sec_desc->sec_type = SEC_RELO;
 +                      sec_desc->shdr = sh;
 +                      sec_desc->data = data;
 +              } else if (sh->sh_type == SHT_NOBITS && strcmp(name, BSS_SEC) == 0) {
 +                      sec_desc->sec_type = SEC_BSS;
 +                      sec_desc->shdr = sh;
 +                      sec_desc->data = data;
 +              } else {
 +                      pr_info("elf: skipping section(%d) %s (size %zu)\n", idx, name,
 +                              (size_t)sh->sh_size);
 +              }
 +      }
 +
 +      if (!obj->efile.strtabidx || obj->efile.strtabidx > idx) {
 +              pr_warn("elf: symbol strings section missing or invalid in %s\n", obj->path);
 +              return -LIBBPF_ERRNO__FORMAT;
 +      }
 +
 +      /* sort BPF programs by section name and in-section instruction offset
 +       * for faster search */
 +      if (obj->nr_programs)
 +              qsort(obj->programs, obj->nr_programs, sizeof(*obj->programs), cmp_progs);
 +
 +      return bpf_object__init_btf(obj, btf_data, btf_ext_data);
 +}
 +
 +static bool sym_is_extern(const Elf64_Sym *sym)
 +{
 +      int bind = ELF64_ST_BIND(sym->st_info);
 +      /* externs are symbols w/ type=NOTYPE, bind=GLOBAL|WEAK, section=UND */
 +      return sym->st_shndx == SHN_UNDEF &&
 +             (bind == STB_GLOBAL || bind == STB_WEAK) &&
 +             ELF64_ST_TYPE(sym->st_info) == STT_NOTYPE;
 +}
 +
 +static bool sym_is_subprog(const Elf64_Sym *sym, int text_shndx)
 +{
 +      int bind = ELF64_ST_BIND(sym->st_info);
 +      int type = ELF64_ST_TYPE(sym->st_info);
 +
 +      /* in .text section */
 +      if (sym->st_shndx != text_shndx)
 +              return false;
 +
 +      /* local function */
 +      if (bind == STB_LOCAL && type == STT_SECTION)
 +              return true;
 +
 +      /* global function */
 +      return bind == STB_GLOBAL && type == STT_FUNC;
 +}
 +
 +static int find_extern_btf_id(const struct btf *btf, const char *ext_name)
 +{
 +      const struct btf_type *t;
 +      const char *tname;
 +      int i, n;
 +
 +      if (!btf)
 +              return -ESRCH;
 +
 +      n = btf__type_cnt(btf);
 +      for (i = 1; i < n; i++) {
 +              t = btf__type_by_id(btf, i);
 +
 +              if (!btf_is_var(t) && !btf_is_func(t))
 +                      continue;
 +
 +              tname = btf__name_by_offset(btf, t->name_off);
 +              if (strcmp(tname, ext_name))
 +                      continue;
 +
 +              if (btf_is_var(t) &&
 +                  btf_var(t)->linkage != BTF_VAR_GLOBAL_EXTERN)
 +                      return -EINVAL;
 +
 +              if (btf_is_func(t) && btf_func_linkage(t) != BTF_FUNC_EXTERN)
 +                      return -EINVAL;
 +
 +              return i;
 +      }
 +
 +      return -ENOENT;
 +}
 +
 +static int find_extern_sec_btf_id(struct btf *btf, int ext_btf_id) {
 +      const struct btf_var_secinfo *vs;
 +      const struct btf_type *t;
 +      int i, j, n;
 +
 +      if (!btf)
 +              return -ESRCH;
 +
 +      n = btf__type_cnt(btf);
 +      for (i = 1; i < n; i++) {
 +              t = btf__type_by_id(btf, i);
 +
 +              if (!btf_is_datasec(t))
 +                      continue;
 +
 +              vs = btf_var_secinfos(t);
 +              for (j = 0; j < btf_vlen(t); j++, vs++) {
 +                      if (vs->type == ext_btf_id)
 +                              return i;
 +              }
 +      }
 +
 +      return -ENOENT;
 +}
 +
 +static enum kcfg_type find_kcfg_type(const struct btf *btf, int id,
 +                                   bool *is_signed)
 +{
 +      const struct btf_type *t;
 +      const char *name;
 +
 +      t = skip_mods_and_typedefs(btf, id, NULL);
 +      name = btf__name_by_offset(btf, t->name_off);
 +
 +      if (is_signed)
 +              *is_signed = false;
 +      switch (btf_kind(t)) {
 +      case BTF_KIND_INT: {
 +              int enc = btf_int_encoding(t);
 +
 +              if (enc & BTF_INT_BOOL)
 +                      return t->size == 1 ? KCFG_BOOL : KCFG_UNKNOWN;
 +              if (is_signed)
 +                      *is_signed = enc & BTF_INT_SIGNED;
 +              if (t->size == 1)
 +                      return KCFG_CHAR;
 +              if (t->size < 1 || t->size > 8 || (t->size & (t->size - 1)))
 +                      return KCFG_UNKNOWN;
 +              return KCFG_INT;
 +      }
 +      case BTF_KIND_ENUM:
 +              if (t->size != 4)
 +                      return KCFG_UNKNOWN;
 +              if (strcmp(name, "libbpf_tristate"))
 +                      return KCFG_UNKNOWN;
 +              return KCFG_TRISTATE;
 +      case BTF_KIND_ARRAY:
 +              if (btf_array(t)->nelems == 0)
 +                      return KCFG_UNKNOWN;
 +              if (find_kcfg_type(btf, btf_array(t)->type, NULL) != KCFG_CHAR)
 +                      return KCFG_UNKNOWN;
 +              return KCFG_CHAR_ARR;
 +      default:
 +              return KCFG_UNKNOWN;
 +      }
 +}
 +
 +static int cmp_externs(const void *_a, const void *_b)
 +{
 +      const struct extern_desc *a = _a;
 +      const struct extern_desc *b = _b;
 +
 +      if (a->type != b->type)
 +              return a->type < b->type ? -1 : 1;
 +
 +      if (a->type == EXT_KCFG) {
 +              /* descending order by alignment requirements */
 +              if (a->kcfg.align != b->kcfg.align)
 +                      return a->kcfg.align > b->kcfg.align ? -1 : 1;
 +              /* ascending order by size, within same alignment class */
 +              if (a->kcfg.sz != b->kcfg.sz)
 +                      return a->kcfg.sz < b->kcfg.sz ? -1 : 1;
 +      }
 +
 +      /* resolve ties by name */
 +      return strcmp(a->name, b->name);
 +}
 +
 +static int find_int_btf_id(const struct btf *btf)
 +{
 +      const struct btf_type *t;
 +      int i, n;
 +
 +      n = btf__type_cnt(btf);
 +      for (i = 1; i < n; i++) {
 +              t = btf__type_by_id(btf, i);
 +
 +              if (btf_is_int(t) && btf_int_bits(t) == 32)
 +                      return i;
 +      }
 +
 +      return 0;
 +}
 +
 +static int add_dummy_ksym_var(struct btf *btf)
 +{
 +      int i, int_btf_id, sec_btf_id, dummy_var_btf_id;
 +      const struct btf_var_secinfo *vs;
 +      const struct btf_type *sec;
 +
 +      if (!btf)
 +              return 0;
 +
 +      sec_btf_id = btf__find_by_name_kind(btf, KSYMS_SEC,
 +                                          BTF_KIND_DATASEC);
 +      if (sec_btf_id < 0)
 +              return 0;
 +
 +      sec = btf__type_by_id(btf, sec_btf_id);
 +      vs = btf_var_secinfos(sec);
 +      for (i = 0; i < btf_vlen(sec); i++, vs++) {
 +              const struct btf_type *vt;
 +
 +              vt = btf__type_by_id(btf, vs->type);
 +              if (btf_is_func(vt))
 +                      break;
 +      }
 +
 +      /* No func in ksyms sec.  No need to add dummy var. */
 +      if (i == btf_vlen(sec))
 +              return 0;
 +
 +      int_btf_id = find_int_btf_id(btf);
 +      dummy_var_btf_id = btf__add_var(btf,
 +                                      "dummy_ksym",
 +                                      BTF_VAR_GLOBAL_ALLOCATED,
 +                                      int_btf_id);
 +      if (dummy_var_btf_id < 0)
 +              pr_warn("cannot create a dummy_ksym var\n");
 +
 +      return dummy_var_btf_id;
 +}
 +
 +static int bpf_object__collect_externs(struct bpf_object *obj)
 +{
 +      struct btf_type *sec, *kcfg_sec = NULL, *ksym_sec = NULL;
 +      const struct btf_type *t;
 +      struct extern_desc *ext;
 +      int i, n, off, dummy_var_btf_id;
 +      const char *ext_name, *sec_name;
 +      Elf_Scn *scn;
 +      Elf64_Shdr *sh;
 +
 +      if (!obj->efile.symbols)
 +              return 0;
 +
 +      scn = elf_sec_by_idx(obj, obj->efile.symbols_shndx);
 +      sh = elf_sec_hdr(obj, scn);
 +      if (!sh || sh->sh_entsize != sizeof(Elf64_Sym))
 +              return -LIBBPF_ERRNO__FORMAT;
 +
 +      dummy_var_btf_id = add_dummy_ksym_var(obj->btf);
 +      if (dummy_var_btf_id < 0)
 +              return dummy_var_btf_id;
 +
 +      n = sh->sh_size / sh->sh_entsize;
 +      pr_debug("looking for externs among %d symbols...\n", n);
 +
 +      for (i = 0; i < n; i++) {
 +              Elf64_Sym *sym = elf_sym_by_idx(obj, i);
 +
 +              if (!sym)
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              if (!sym_is_extern(sym))
 +                      continue;
 +              ext_name = elf_sym_str(obj, sym->st_name);
 +              if (!ext_name || !ext_name[0])
 +                      continue;
 +
 +              ext = obj->externs;
 +              ext = libbpf_reallocarray(ext, obj->nr_extern + 1, sizeof(*ext));
 +              if (!ext)
 +                      return -ENOMEM;
 +              obj->externs = ext;
 +              ext = &ext[obj->nr_extern];
 +              memset(ext, 0, sizeof(*ext));
 +              obj->nr_extern++;
 +
 +              ext->btf_id = find_extern_btf_id(obj->btf, ext_name);
 +              if (ext->btf_id <= 0) {
 +                      pr_warn("failed to find BTF for extern '%s': %d\n",
 +                              ext_name, ext->btf_id);
 +                      return ext->btf_id;
 +              }
 +              t = btf__type_by_id(obj->btf, ext->btf_id);
 +              ext->name = btf__name_by_offset(obj->btf, t->name_off);
 +              ext->sym_idx = i;
 +              ext->is_weak = ELF64_ST_BIND(sym->st_info) == STB_WEAK;
 +
 +              ext->sec_btf_id = find_extern_sec_btf_id(obj->btf, ext->btf_id);
 +              if (ext->sec_btf_id <= 0) {
 +                      pr_warn("failed to find BTF for extern '%s' [%d] section: %d\n",
 +                              ext_name, ext->btf_id, ext->sec_btf_id);
 +                      return ext->sec_btf_id;
 +              }
 +              sec = (void *)btf__type_by_id(obj->btf, ext->sec_btf_id);
 +              sec_name = btf__name_by_offset(obj->btf, sec->name_off);
 +
 +              if (strcmp(sec_name, KCONFIG_SEC) == 0) {
 +                      if (btf_is_func(t)) {
 +                              pr_warn("extern function %s is unsupported under %s section\n",
 +                                      ext->name, KCONFIG_SEC);
 +                              return -ENOTSUP;
 +                      }
 +                      kcfg_sec = sec;
 +                      ext->type = EXT_KCFG;
 +                      ext->kcfg.sz = btf__resolve_size(obj->btf, t->type);
 +                      if (ext->kcfg.sz <= 0) {
 +                              pr_warn("failed to resolve size of extern (kcfg) '%s': %d\n",
 +                                      ext_name, ext->kcfg.sz);
 +                              return ext->kcfg.sz;
 +                      }
 +                      ext->kcfg.align = btf__align_of(obj->btf, t->type);
 +                      if (ext->kcfg.align <= 0) {
 +                              pr_warn("failed to determine alignment of extern (kcfg) '%s': %d\n",
 +                                      ext_name, ext->kcfg.align);
 +                              return -EINVAL;
 +                      }
 +                      ext->kcfg.type = find_kcfg_type(obj->btf, t->type,
 +                                                      &ext->kcfg.is_signed);
 +                      if (ext->kcfg.type == KCFG_UNKNOWN) {
 +                              pr_warn("extern (kcfg) '%s' type is unsupported\n", ext_name);
 +                              return -ENOTSUP;
 +                      }
 +              } else if (strcmp(sec_name, KSYMS_SEC) == 0) {
 +                      ksym_sec = sec;
 +                      ext->type = EXT_KSYM;
 +                      skip_mods_and_typedefs(obj->btf, t->type,
 +                                             &ext->ksym.type_id);
 +              } else {
 +                      pr_warn("unrecognized extern section '%s'\n", sec_name);
 +                      return -ENOTSUP;
 +              }
 +      }
 +      pr_debug("collected %d externs total\n", obj->nr_extern);
 +
 +      if (!obj->nr_extern)
 +              return 0;
 +
 +      /* sort externs by type, for kcfg ones also by (align, size, name) */
 +      qsort(obj->externs, obj->nr_extern, sizeof(*ext), cmp_externs);
 +
 +      /* for .ksyms section, we need to turn all externs into allocated
 +       * variables in BTF to pass kernel verification; we do this by
 +       * pretending that each extern is a 8-byte variable
 +       */
 +      if (ksym_sec) {
 +              /* find existing 4-byte integer type in BTF to use for fake
 +               * extern variables in DATASEC
 +               */
 +              int int_btf_id = find_int_btf_id(obj->btf);
 +              /* For extern function, a dummy_var added earlier
 +               * will be used to replace the vs->type and
 +               * its name string will be used to refill
 +               * the missing param's name.
 +               */
 +              const struct btf_type *dummy_var;
 +
 +              dummy_var = btf__type_by_id(obj->btf, dummy_var_btf_id);
 +              for (i = 0; i < obj->nr_extern; i++) {
 +                      ext = &obj->externs[i];
 +                      if (ext->type != EXT_KSYM)
 +                              continue;
 +                      pr_debug("extern (ksym) #%d: symbol %d, name %s\n",
 +                               i, ext->sym_idx, ext->name);
 +              }
 +
 +              sec = ksym_sec;
 +              n = btf_vlen(sec);
 +              for (i = 0, off = 0; i < n; i++, off += sizeof(int)) {
 +                      struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i;
 +                      struct btf_type *vt;
 +
 +                      vt = (void *)btf__type_by_id(obj->btf, vs->type);
 +                      ext_name = btf__name_by_offset(obj->btf, vt->name_off);
 +                      ext = find_extern_by_name(obj, ext_name);
 +                      if (!ext) {
 +                              pr_warn("failed to find extern definition for BTF %s '%s'\n",
 +                                      btf_kind_str(vt), ext_name);
 +                              return -ESRCH;
 +                      }
 +                      if (btf_is_func(vt)) {
 +                              const struct btf_type *func_proto;
 +                              struct btf_param *param;
 +                              int j;
 +
 +                              func_proto = btf__type_by_id(obj->btf,
 +                                                           vt->type);
 +                              param = btf_params(func_proto);
 +                              /* Reuse the dummy_var string if the
 +                               * func proto does not have param name.
 +                               */
 +                              for (j = 0; j < btf_vlen(func_proto); j++)
 +                                      if (param[j].type && !param[j].name_off)
 +                                              param[j].name_off =
 +                                                      dummy_var->name_off;
 +                              vs->type = dummy_var_btf_id;
 +                              vt->info &= ~0xffff;
 +                              vt->info |= BTF_FUNC_GLOBAL;
 +                      } else {
 +                              btf_var(vt)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
 +                              vt->type = int_btf_id;
 +                      }
 +                      vs->offset = off;
 +                      vs->size = sizeof(int);
 +              }
 +              sec->size = off;
 +      }
 +
 +      if (kcfg_sec) {
 +              sec = kcfg_sec;
 +              /* for kcfg externs calculate their offsets within a .kconfig map */
 +              off = 0;
 +              for (i = 0; i < obj->nr_extern; i++) {
 +                      ext = &obj->externs[i];
 +                      if (ext->type != EXT_KCFG)
 +                              continue;
 +
 +                      ext->kcfg.data_off = roundup(off, ext->kcfg.align);
 +                      off = ext->kcfg.data_off + ext->kcfg.sz;
 +                      pr_debug("extern (kcfg) #%d: symbol %d, off %u, name %s\n",
 +                               i, ext->sym_idx, ext->kcfg.data_off, ext->name);
 +              }
 +              sec->size = off;
 +              n = btf_vlen(sec);
 +              for (i = 0; i < n; i++) {
 +                      struct btf_var_secinfo *vs = btf_var_secinfos(sec) + i;
 +
 +                      t = btf__type_by_id(obj->btf, vs->type);
 +                      ext_name = btf__name_by_offset(obj->btf, t->name_off);
 +                      ext = find_extern_by_name(obj, ext_name);
 +                      if (!ext) {
 +                              pr_warn("failed to find extern definition for BTF var '%s'\n",
 +                                      ext_name);
 +                              return -ESRCH;
 +                      }
 +                      btf_var(t)->linkage = BTF_VAR_GLOBAL_ALLOCATED;
 +                      vs->offset = ext->kcfg.data_off;
 +              }
 +      }
 +      return 0;
 +}
 +
 +struct bpf_program *
 +bpf_object__find_program_by_title(const struct bpf_object *obj,
 +                                const char *title)
 +{
 +      struct bpf_program *pos;
 +
 +      bpf_object__for_each_program(pos, obj) {
 +              if (pos->sec_name && !strcmp(pos->sec_name, title))
 +                      return pos;
 +      }
 +      return errno = ENOENT, NULL;
 +}
 +
 +static bool prog_is_subprog(const struct bpf_object *obj,
 +                          const struct bpf_program *prog)
 +{
 +      /* For legacy reasons, libbpf supports an entry-point BPF programs
 +       * without SEC() attribute, i.e., those in the .text section. But if
 +       * there are 2 or more such programs in the .text section, they all
 +       * must be subprograms called from entry-point BPF programs in
 +       * designated SEC()'tions, otherwise there is no way to distinguish
 +       * which of those programs should be loaded vs which are a subprogram.
 +       * Similarly, if there is a function/program in .text and at least one
 +       * other BPF program with custom SEC() attribute, then we just assume
 +       * .text programs are subprograms (even if they are not called from
 +       * other programs), because libbpf never explicitly supported mixing
 +       * SEC()-designated BPF programs and .text entry-point BPF programs.
 +       */
 +      return prog->sec_idx == obj->efile.text_shndx && obj->nr_programs > 1;
 +}
 +
 +struct bpf_program *
 +bpf_object__find_program_by_name(const struct bpf_object *obj,
 +                               const char *name)
 +{
 +      struct bpf_program *prog;
 +
 +      bpf_object__for_each_program(prog, obj) {
 +              if (prog_is_subprog(obj, prog))
 +                      continue;
 +              if (!strcmp(prog->name, name))
 +                      return prog;
 +      }
 +      return errno = ENOENT, NULL;
 +}
 +
 +static bool bpf_object__shndx_is_data(const struct bpf_object *obj,
 +                                    int shndx)
 +{
 +      switch (obj->efile.secs[shndx].sec_type) {
 +      case SEC_BSS:
 +      case SEC_DATA:
 +      case SEC_RODATA:
 +              return true;
 +      default:
 +              return false;
 +      }
 +}
 +
 +static bool bpf_object__shndx_is_maps(const struct bpf_object *obj,
 +                                    int shndx)
 +{
 +      return shndx == obj->efile.maps_shndx ||
 +             shndx == obj->efile.btf_maps_shndx;
 +}
 +
 +static enum libbpf_map_type
 +bpf_object__section_to_libbpf_map_type(const struct bpf_object *obj, int shndx)
 +{
 +      if (shndx == obj->efile.symbols_shndx)
 +              return LIBBPF_MAP_KCONFIG;
 +
 +      switch (obj->efile.secs[shndx].sec_type) {
 +      case SEC_BSS:
 +              return LIBBPF_MAP_BSS;
 +      case SEC_DATA:
 +              return LIBBPF_MAP_DATA;
 +      case SEC_RODATA:
 +              return LIBBPF_MAP_RODATA;
 +      default:
 +              return LIBBPF_MAP_UNSPEC;
 +      }
 +}
 +
 +static int bpf_program__record_reloc(struct bpf_program *prog,
 +                                   struct reloc_desc *reloc_desc,
 +                                   __u32 insn_idx, const char *sym_name,
 +                                   const Elf64_Sym *sym, const Elf64_Rel *rel)
 +{
 +      struct bpf_insn *insn = &prog->insns[insn_idx];
 +      size_t map_idx, nr_maps = prog->obj->nr_maps;
 +      struct bpf_object *obj = prog->obj;
 +      __u32 shdr_idx = sym->st_shndx;
 +      enum libbpf_map_type type;
 +      const char *sym_sec_name;
 +      struct bpf_map *map;
 +
 +      if (!is_call_insn(insn) && !is_ldimm64_insn(insn)) {
 +              pr_warn("prog '%s': invalid relo against '%s' for insns[%d].code 0x%x\n",
 +                      prog->name, sym_name, insn_idx, insn->code);
 +              return -LIBBPF_ERRNO__RELOC;
 +      }
 +
 +      if (sym_is_extern(sym)) {
 +              int sym_idx = ELF64_R_SYM(rel->r_info);
 +              int i, n = obj->nr_extern;
 +              struct extern_desc *ext;
 +
 +              for (i = 0; i < n; i++) {
 +                      ext = &obj->externs[i];
 +                      if (ext->sym_idx == sym_idx)
 +                              break;
 +              }
 +              if (i >= n) {
 +                      pr_warn("prog '%s': extern relo failed to find extern for '%s' (%d)\n",
 +                              prog->name, sym_name, sym_idx);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              pr_debug("prog '%s': found extern #%d '%s' (sym %d) for insn #%u\n",
 +                       prog->name, i, ext->name, ext->sym_idx, insn_idx);
 +              if (insn->code == (BPF_JMP | BPF_CALL))
 +                      reloc_desc->type = RELO_EXTERN_FUNC;
 +              else
 +                      reloc_desc->type = RELO_EXTERN_VAR;
 +              reloc_desc->insn_idx = insn_idx;
 +              reloc_desc->sym_off = i; /* sym_off stores extern index */
 +              return 0;
 +      }
 +
 +      /* sub-program call relocation */
 +      if (is_call_insn(insn)) {
 +              if (insn->src_reg != BPF_PSEUDO_CALL) {
 +                      pr_warn("prog '%s': incorrect bpf_call opcode\n", prog->name);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              /* text_shndx can be 0, if no default "main" program exists */
 +              if (!shdr_idx || shdr_idx != obj->efile.text_shndx) {
 +                      sym_sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, shdr_idx));
 +                      pr_warn("prog '%s': bad call relo against '%s' in section '%s'\n",
 +                              prog->name, sym_name, sym_sec_name);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              if (sym->st_value % BPF_INSN_SZ) {
 +                      pr_warn("prog '%s': bad call relo against '%s' at offset %zu\n",
 +                              prog->name, sym_name, (size_t)sym->st_value);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              reloc_desc->type = RELO_CALL;
 +              reloc_desc->insn_idx = insn_idx;
 +              reloc_desc->sym_off = sym->st_value;
 +              return 0;
 +      }
 +
 +      if (!shdr_idx || shdr_idx >= SHN_LORESERVE) {
 +              pr_warn("prog '%s': invalid relo against '%s' in special section 0x%x; forgot to initialize global var?..\n",
 +                      prog->name, sym_name, shdr_idx);
 +              return -LIBBPF_ERRNO__RELOC;
 +      }
 +
 +      /* loading subprog addresses */
 +      if (sym_is_subprog(sym, obj->efile.text_shndx)) {
 +              /* global_func: sym->st_value = offset in the section, insn->imm = 0.
 +               * local_func: sym->st_value = 0, insn->imm = offset in the section.
 +               */
 +              if ((sym->st_value % BPF_INSN_SZ) || (insn->imm % BPF_INSN_SZ)) {
 +                      pr_warn("prog '%s': bad subprog addr relo against '%s' at offset %zu+%d\n",
 +                              prog->name, sym_name, (size_t)sym->st_value, insn->imm);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +
 +              reloc_desc->type = RELO_SUBPROG_ADDR;
 +              reloc_desc->insn_idx = insn_idx;
 +              reloc_desc->sym_off = sym->st_value;
 +              return 0;
 +      }
 +
 +      type = bpf_object__section_to_libbpf_map_type(obj, shdr_idx);
 +      sym_sec_name = elf_sec_name(obj, elf_sec_by_idx(obj, shdr_idx));
 +
 +      /* generic map reference relocation */
 +      if (type == LIBBPF_MAP_UNSPEC) {
 +              if (!bpf_object__shndx_is_maps(obj, shdr_idx)) {
 +                      pr_warn("prog '%s': bad map relo against '%s' in section '%s'\n",
 +                              prog->name, sym_name, sym_sec_name);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              for (map_idx = 0; map_idx < nr_maps; map_idx++) {
 +                      map = &obj->maps[map_idx];
 +                      if (map->libbpf_type != type ||
 +                          map->sec_idx != sym->st_shndx ||
 +                          map->sec_offset != sym->st_value)
 +                              continue;
 +                      pr_debug("prog '%s': found map %zd (%s, sec %d, off %zu) for insn #%u\n",
 +                               prog->name, map_idx, map->name, map->sec_idx,
 +                               map->sec_offset, insn_idx);
 +                      break;
 +              }
 +              if (map_idx >= nr_maps) {
 +                      pr_warn("prog '%s': map relo failed to find map for section '%s', off %zu\n",
 +                              prog->name, sym_sec_name, (size_t)sym->st_value);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              reloc_desc->type = RELO_LD64;
 +              reloc_desc->insn_idx = insn_idx;
 +              reloc_desc->map_idx = map_idx;
 +              reloc_desc->sym_off = 0; /* sym->st_value determines map_idx */
 +              return 0;
 +      }
 +
 +      /* global data map relocation */
 +      if (!bpf_object__shndx_is_data(obj, shdr_idx)) {
 +              pr_warn("prog '%s': bad data relo against section '%s'\n",
 +                      prog->name, sym_sec_name);
 +              return -LIBBPF_ERRNO__RELOC;
 +      }
 +      for (map_idx = 0; map_idx < nr_maps; map_idx++) {
 +              map = &obj->maps[map_idx];
 +              if (map->libbpf_type != type || map->sec_idx != sym->st_shndx)
 +                      continue;
 +              pr_debug("prog '%s': found data map %zd (%s, sec %d, off %zu) for insn %u\n",
 +                       prog->name, map_idx, map->name, map->sec_idx,
 +                       map->sec_offset, insn_idx);
 +              break;
 +      }
 +      if (map_idx >= nr_maps) {
 +              pr_warn("prog '%s': data relo failed to find map for section '%s'\n",
 +                      prog->name, sym_sec_name);
 +              return -LIBBPF_ERRNO__RELOC;
 +      }
 +
 +      reloc_desc->type = RELO_DATA;
 +      reloc_desc->insn_idx = insn_idx;
 +      reloc_desc->map_idx = map_idx;
 +      reloc_desc->sym_off = sym->st_value;
 +      return 0;
 +}
 +
 +static bool prog_contains_insn(const struct bpf_program *prog, size_t insn_idx)
 +{
 +      return insn_idx >= prog->sec_insn_off &&
 +             insn_idx < prog->sec_insn_off + prog->sec_insn_cnt;
 +}
 +
 +static struct bpf_program *find_prog_by_sec_insn(const struct bpf_object *obj,
 +                                               size_t sec_idx, size_t insn_idx)
 +{
 +      int l = 0, r = obj->nr_programs - 1, m;
 +      struct bpf_program *prog;
 +
 +      while (l < r) {
 +              m = l + (r - l + 1) / 2;
 +              prog = &obj->programs[m];
 +
 +              if (prog->sec_idx < sec_idx ||
 +                  (prog->sec_idx == sec_idx && prog->sec_insn_off <= insn_idx))
 +                      l = m;
 +              else
 +                      r = m - 1;
 +      }
 +      /* matching program could be at index l, but it still might be the
 +       * wrong one, so we need to double check conditions for the last time
 +       */
 +      prog = &obj->programs[l];
 +      if (prog->sec_idx == sec_idx && prog_contains_insn(prog, insn_idx))
 +              return prog;
 +      return NULL;
 +}
 +
 +static int
 +bpf_object__collect_prog_relos(struct bpf_object *obj, Elf64_Shdr *shdr, Elf_Data *data)
 +{
 +      const char *relo_sec_name, *sec_name;
 +      size_t sec_idx = shdr->sh_info, sym_idx;
 +      struct bpf_program *prog;
 +      struct reloc_desc *relos;
 +      int err, i, nrels;
 +      const char *sym_name;
 +      __u32 insn_idx;
 +      Elf_Scn *scn;
 +      Elf_Data *scn_data;
 +      Elf64_Sym *sym;
 +      Elf64_Rel *rel;
 +
 +      if (sec_idx >= obj->efile.sec_cnt)
 +              return -EINVAL;
 +
 +      scn = elf_sec_by_idx(obj, sec_idx);
 +      scn_data = elf_sec_data(obj, scn);
 +
 +      relo_sec_name = elf_sec_str(obj, shdr->sh_name);
 +      sec_name = elf_sec_name(obj, scn);
 +      if (!relo_sec_name || !sec_name)
 +              return -EINVAL;
 +
 +      pr_debug("sec '%s': collecting relocation for section(%zu) '%s'\n",
 +               relo_sec_name, sec_idx, sec_name);
 +      nrels = shdr->sh_size / shdr->sh_entsize;
 +
 +      for (i = 0; i < nrels; i++) {
 +              rel = elf_rel_by_idx(data, i);
 +              if (!rel) {
 +                      pr_warn("sec '%s': failed to get relo #%d\n", relo_sec_name, i);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              sym_idx = ELF64_R_SYM(rel->r_info);
 +              sym = elf_sym_by_idx(obj, sym_idx);
 +              if (!sym) {
 +                      pr_warn("sec '%s': symbol #%zu not found for relo #%d\n",
 +                              relo_sec_name, sym_idx, i);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              if (sym->st_shndx >= obj->efile.sec_cnt) {
 +                      pr_warn("sec '%s': corrupted symbol #%zu pointing to invalid section #%zu for relo #%d\n",
 +                              relo_sec_name, sym_idx, (size_t)sym->st_shndx, i);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              if (rel->r_offset % BPF_INSN_SZ || rel->r_offset >= scn_data->d_size) {
 +                      pr_warn("sec '%s': invalid offset 0x%zx for relo #%d\n",
 +                              relo_sec_name, (size_t)rel->r_offset, i);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              insn_idx = rel->r_offset / BPF_INSN_SZ;
 +              /* relocations against static functions are recorded as
 +               * relocations against the section that contains a function;
 +               * in such case, symbol will be STT_SECTION and sym.st_name
 +               * will point to empty string (0), so fetch section name
 +               * instead
 +               */
 +              if (ELF64_ST_TYPE(sym->st_info) == STT_SECTION && sym->st_name == 0)
 +                      sym_name = elf_sec_name(obj, elf_sec_by_idx(obj, sym->st_shndx));
 +              else
 +                      sym_name = elf_sym_str(obj, sym->st_name);
 +              sym_name = sym_name ?: "<?";
 +
 +              pr_debug("sec '%s': relo #%d: insn #%u against '%s'\n",
 +                       relo_sec_name, i, insn_idx, sym_name);
 +
 +              prog = find_prog_by_sec_insn(obj, sec_idx, insn_idx);
 +              if (!prog) {
 +                      pr_debug("sec '%s': relo #%d: couldn't find program in section '%s' for insn #%u, probably overridden weak function, skipping...\n",
 +                              relo_sec_name, i, sec_name, insn_idx);
 +                      continue;
 +              }
 +
 +              relos = libbpf_reallocarray(prog->reloc_desc,
 +                                          prog->nr_reloc + 1, sizeof(*relos));
 +              if (!relos)
 +                      return -ENOMEM;
 +              prog->reloc_desc = relos;
 +
 +              /* adjust insn_idx to local BPF program frame of reference */
 +              insn_idx -= prog->sec_insn_off;
 +              err = bpf_program__record_reloc(prog, &relos[prog->nr_reloc],
 +                                              insn_idx, sym_name, sym, rel);
 +              if (err)
 +                      return err;
 +
 +              prog->nr_reloc++;
 +      }
 +      return 0;
 +}
 +
 +static int bpf_map_find_btf_info(struct bpf_object *obj, struct bpf_map *map)
 +{
 +      struct bpf_map_def *def = &map->def;
 +      __u32 key_type_id = 0, value_type_id = 0;
 +      int ret;
 +
 +      /* if it's BTF-defined map, we don't need to search for type IDs.
 +       * For struct_ops map, it does not need btf_key_type_id and
 +       * btf_value_type_id.
 +       */
 +      if (map->sec_idx == obj->efile.btf_maps_shndx ||
 +          bpf_map__is_struct_ops(map))
 +              return 0;
 +
 +      if (!bpf_map__is_internal(map)) {
 +              ret = btf__get_map_kv_tids(obj->btf, map->name, def->key_size,
 +                                         def->value_size, &key_type_id,
 +                                         &value_type_id);
 +      } else {
 +              /*
 +               * LLVM annotates global data differently in BTF, that is,
 +               * only as '.data', '.bss' or '.rodata'.
 +               */
 +              ret = btf__find_by_name(obj->btf, map->real_name);
 +      }
 +      if (ret < 0)
 +              return ret;
 +
 +      map->btf_key_type_id = key_type_id;
 +      map->btf_value_type_id = bpf_map__is_internal(map) ?
 +                               ret : value_type_id;
 +      return 0;
 +}
 +
 +static int bpf_get_map_info_from_fdinfo(int fd, struct bpf_map_info *info)
 +{
 +      char file[PATH_MAX], buff[4096];
 +      FILE *fp;
 +      __u32 val;
 +      int err;
 +
 +      snprintf(file, sizeof(file), "/proc/%d/fdinfo/%d", getpid(), fd);
 +      memset(info, 0, sizeof(*info));
 +
 +      fp = fopen(file, "r");
 +      if (!fp) {
 +              err = -errno;
 +              pr_warn("failed to open %s: %d. No procfs support?\n", file,
 +                      err);
 +              return err;
 +      }
 +
 +      while (fgets(buff, sizeof(buff), fp)) {
 +              if (sscanf(buff, "map_type:\t%u", &val) == 1)
 +                      info->type = val;
 +              else if (sscanf(buff, "key_size:\t%u", &val) == 1)
 +                      info->key_size = val;
 +              else if (sscanf(buff, "value_size:\t%u", &val) == 1)
 +                      info->value_size = val;
 +              else if (sscanf(buff, "max_entries:\t%u", &val) == 1)
 +                      info->max_entries = val;
 +              else if (sscanf(buff, "map_flags:\t%i", &val) == 1)
 +                      info->map_flags = val;
 +      }
 +
 +      fclose(fp);
 +
 +      return 0;
 +}
 +
 +int bpf_map__reuse_fd(struct bpf_map *map, int fd)
 +{
 +      struct bpf_map_info info = {};
 +      __u32 len = sizeof(info);
 +      int new_fd, err;
 +      char *new_name;
 +
 +      err = bpf_obj_get_info_by_fd(fd, &info, &len);
 +      if (err && errno == EINVAL)
 +              err = bpf_get_map_info_from_fdinfo(fd, &info);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      new_name = strdup(info.name);
 +      if (!new_name)
 +              return libbpf_err(-errno);
 +
 +      new_fd = open("/", O_RDONLY | O_CLOEXEC);
 +      if (new_fd < 0) {
 +              err = -errno;
 +              goto err_free_new_name;
 +      }
 +
 +      new_fd = dup3(fd, new_fd, O_CLOEXEC);
 +      if (new_fd < 0) {
 +              err = -errno;
 +              goto err_close_new_fd;
 +      }
 +
 +      err = zclose(map->fd);
 +      if (err) {
 +              err = -errno;
 +              goto err_close_new_fd;
 +      }
 +      free(map->name);
 +
 +      map->fd = new_fd;
 +      map->name = new_name;
 +      map->def.type = info.type;
 +      map->def.key_size = info.key_size;
 +      map->def.value_size = info.value_size;
 +      map->def.max_entries = info.max_entries;
 +      map->def.map_flags = info.map_flags;
 +      map->btf_key_type_id = info.btf_key_type_id;
 +      map->btf_value_type_id = info.btf_value_type_id;
 +      map->reused = true;
 +      map->map_extra = info.map_extra;
 +
 +      return 0;
 +
 +err_close_new_fd:
 +      close(new_fd);
 +err_free_new_name:
 +      free(new_name);
 +      return libbpf_err(err);
 +}
 +
 +__u32 bpf_map__max_entries(const struct bpf_map *map)
 +{
 +      return map->def.max_entries;
 +}
 +
 +struct bpf_map *bpf_map__inner_map(struct bpf_map *map)
 +{
 +      if (!bpf_map_type__is_map_in_map(map->def.type))
 +              return errno = EINVAL, NULL;
 +
 +      return map->inner_map;
 +}
 +
 +int bpf_map__set_max_entries(struct bpf_map *map, __u32 max_entries)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->def.max_entries = max_entries;
 +      return 0;
 +}
 +
 +int bpf_map__resize(struct bpf_map *map, __u32 max_entries)
 +{
 +      if (!map || !max_entries)
 +              return libbpf_err(-EINVAL);
 +
 +      return bpf_map__set_max_entries(map, max_entries);
 +}
 +
 +static int
 +bpf_object__probe_loading(struct bpf_object *obj)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN(),
 +      };
 +      int ret, insn_cnt = ARRAY_SIZE(insns);
 +
 +      if (obj->gen_loader)
 +              return 0;
 +
 +      ret = bump_rlimit_memlock();
 +      if (ret)
 +              pr_warn("Failed to bump RLIMIT_MEMLOCK (err = %d), you might need to do it explicitly!\n", ret);
 +
 +      /* make sure basic loading works */
 +      ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, NULL);
 +      if (ret < 0)
 +              ret = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL", insns, insn_cnt, NULL);
 +      if (ret < 0) {
 +              ret = errno;
 +              cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg));
 +              pr_warn("Error in %s():%s(%d). Couldn't load trivial BPF "
 +                      "program. Make sure your kernel supports BPF "
 +                      "(CONFIG_BPF_SYSCALL=y) and/or that RLIMIT_MEMLOCK is "
 +                      "set to big enough value.\n", __func__, cp, ret);
 +              return -ret;
 +      }
 +      close(ret);
 +
 +      return 0;
 +}
 +
 +static int probe_fd(int fd)
 +{
 +      if (fd >= 0)
 +              close(fd);
 +      return fd >= 0;
 +}
 +
 +static int probe_kern_prog_name(void)
 +{
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN(),
 +      };
 +      int ret, insn_cnt = ARRAY_SIZE(insns);
 +
 +      /* make sure loading with name works */
 +      ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, "test", "GPL", insns, insn_cnt, NULL);
 +      return probe_fd(ret);
 +}
 +
 +static int probe_kern_global_data(void)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      struct bpf_insn insns[] = {
 +              BPF_LD_MAP_VALUE(BPF_REG_1, 0, 16),
 +              BPF_ST_MEM(BPF_DW, BPF_REG_1, 0, 42),
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN(),
 +      };
 +      int ret, map, insn_cnt = ARRAY_SIZE(insns);
 +
 +      map = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), 32, 1, NULL);
 +      if (map < 0) {
 +              ret = -errno;
 +              cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg));
 +              pr_warn("Error in %s():%s(%d). Couldn't create simple array map.\n",
 +                      __func__, cp, -ret);
 +              return ret;
 +      }
 +
 +      insns[0].imm = map;
 +
 +      ret = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, NULL);
 +      close(map);
 +      return probe_fd(ret);
 +}
 +
 +static int probe_kern_btf(void)
 +{
 +      static const char strs[] = "\0int";
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_btf_func(void)
 +{
 +      static const char strs[] = "\0int\0x\0a";
 +      /* void x(int a) {} */
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
 +              /* FUNC_PROTO */                                /* [2] */
 +              BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
 +              BTF_PARAM_ENC(7, 1),
 +              /* FUNC x */                                    /* [3] */
 +              BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, 0), 2),
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_btf_func_global(void)
 +{
 +      static const char strs[] = "\0int\0x\0a";
 +      /* static void x(int a) {} */
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
 +              /* FUNC_PROTO */                                /* [2] */
 +              BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_FUNC_PROTO, 0, 1), 0),
 +              BTF_PARAM_ENC(7, 1),
 +              /* FUNC x BTF_FUNC_GLOBAL */                    /* [3] */
 +              BTF_TYPE_ENC(5, BTF_INFO_ENC(BTF_KIND_FUNC, 0, BTF_FUNC_GLOBAL), 2),
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_btf_datasec(void)
 +{
 +      static const char strs[] = "\0x\0.data";
 +      /* static int a; */
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
 +              /* VAR x */                                     /* [2] */
 +              BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
 +              BTF_VAR_STATIC,
 +              /* DATASEC val */                               /* [3] */
 +              BTF_TYPE_ENC(3, BTF_INFO_ENC(BTF_KIND_DATASEC, 0, 1), 4),
 +              BTF_VAR_SECINFO_ENC(2, 0, 4),
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_btf_float(void)
 +{
 +      static const char strs[] = "\0float";
 +      __u32 types[] = {
 +              /* float */
 +              BTF_TYPE_FLOAT_ENC(1, 4),
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_btf_decl_tag(void)
 +{
 +      static const char strs[] = "\0tag";
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
 +              /* VAR x */                                     /* [2] */
 +              BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_VAR, 0, 0), 1),
 +              BTF_VAR_STATIC,
 +              /* attr */
 +              BTF_TYPE_DECL_TAG_ENC(1, 2, -1),
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_btf_type_tag(void)
 +{
 +      static const char strs[] = "\0tag";
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),          /* [1] */
 +              /* attr */
 +              BTF_TYPE_TYPE_TAG_ENC(1, 1),                            /* [2] */
 +              /* ptr */
 +              BTF_TYPE_ENC(0, BTF_INFO_ENC(BTF_KIND_PTR, 0, 0), 2),   /* [3] */
 +      };
 +
 +      return probe_fd(libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                           strs, sizeof(strs)));
 +}
 +
 +static int probe_kern_array_mmap(void)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts, .map_flags = BPF_F_MMAPABLE);
 +      int fd;
 +
 +      fd = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), sizeof(int), 1, &opts);
 +      return probe_fd(fd);
 +}
 +
 +static int probe_kern_exp_attach_type(void)
 +{
 +      LIBBPF_OPTS(bpf_prog_load_opts, opts, .expected_attach_type = BPF_CGROUP_INET_SOCK_CREATE);
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN(),
 +      };
 +      int fd, insn_cnt = ARRAY_SIZE(insns);
 +
 +      /* use any valid combination of program type and (optional)
 +       * non-zero expected attach type (i.e., not a BPF_CGROUP_INET_INGRESS)
 +       * to see if kernel supports expected_attach_type field for
 +       * BPF_PROG_LOAD command
 +       */
 +      fd = bpf_prog_load(BPF_PROG_TYPE_CGROUP_SOCK, NULL, "GPL", insns, insn_cnt, &opts);
 +      return probe_fd(fd);
 +}
 +
 +static int probe_kern_probe_read_kernel(void)
 +{
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_REG(BPF_REG_1, BPF_REG_10),   /* r1 = r10 (fp) */
 +              BPF_ALU64_IMM(BPF_ADD, BPF_REG_1, -8),  /* r1 += -8 */
 +              BPF_MOV64_IMM(BPF_REG_2, 8),            /* r2 = 8 */
 +              BPF_MOV64_IMM(BPF_REG_3, 0),            /* r3 = 0 */
 +              BPF_RAW_INSN(BPF_JMP | BPF_CALL, 0, 0, 0, BPF_FUNC_probe_read_kernel),
 +              BPF_EXIT_INSN(),
 +      };
 +      int fd, insn_cnt = ARRAY_SIZE(insns);
 +
 +      fd = bpf_prog_load(BPF_PROG_TYPE_KPROBE, NULL, "GPL", insns, insn_cnt, NULL);
 +      return probe_fd(fd);
 +}
 +
 +static int probe_prog_bind_map(void)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN(),
 +      };
 +      int ret, map, prog, insn_cnt = ARRAY_SIZE(insns);
 +
 +      map = bpf_map_create(BPF_MAP_TYPE_ARRAY, NULL, sizeof(int), 32, 1, NULL);
 +      if (map < 0) {
 +              ret = -errno;
 +              cp = libbpf_strerror_r(ret, errmsg, sizeof(errmsg));
 +              pr_warn("Error in %s():%s(%d). Couldn't create simple array map.\n",
 +                      __func__, cp, -ret);
 +              return ret;
 +      }
 +
 +      prog = bpf_prog_load(BPF_PROG_TYPE_SOCKET_FILTER, NULL, "GPL", insns, insn_cnt, NULL);
 +      if (prog < 0) {
 +              close(map);
 +              return 0;
 +      }
 +
 +      ret = bpf_prog_bind_map(prog, map, NULL);
 +
 +      close(map);
 +      close(prog);
 +
 +      return ret >= 0;
 +}
 +
 +static int probe_module_btf(void)
 +{
 +      static const char strs[] = "\0int";
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(1, BTF_INT_SIGNED, 0, 32, 4),
 +      };
 +      struct bpf_btf_info info;
 +      __u32 len = sizeof(info);
 +      char name[16];
 +      int fd, err;
 +
 +      fd = libbpf__load_raw_btf((char *)types, sizeof(types), strs, sizeof(strs));
 +      if (fd < 0)
 +              return 0; /* BTF not supported at all */
 +
 +      memset(&info, 0, sizeof(info));
 +      info.name = ptr_to_u64(name);
 +      info.name_len = sizeof(name);
 +
 +      /* check that BPF_OBJ_GET_INFO_BY_FD supports specifying name pointer;
 +       * kernel's module BTF support coincides with support for
 +       * name/name_len fields in struct bpf_btf_info.
 +       */
 +      err = bpf_obj_get_info_by_fd(fd, &info, &len);
 +      close(fd);
 +      return !err;
 +}
 +
 +static int probe_perf_link(void)
 +{
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN(),
 +      };
 +      int prog_fd, link_fd, err;
 +
 +      prog_fd = bpf_prog_load(BPF_PROG_TYPE_TRACEPOINT, NULL, "GPL",
 +                              insns, ARRAY_SIZE(insns), NULL);
 +      if (prog_fd < 0)
 +              return -errno;
 +
 +      /* use invalid perf_event FD to get EBADF, if link is supported;
 +       * otherwise EINVAL should be returned
 +       */
 +      link_fd = bpf_link_create(prog_fd, -1, BPF_PERF_EVENT, NULL);
 +      err = -errno; /* close() can clobber errno */
 +
 +      if (link_fd >= 0)
 +              close(link_fd);
 +      close(prog_fd);
 +
 +      return link_fd < 0 && err == -EBADF;
 +}
 +
 +enum kern_feature_result {
 +      FEAT_UNKNOWN = 0,
 +      FEAT_SUPPORTED = 1,
 +      FEAT_MISSING = 2,
 +};
 +
 +typedef int (*feature_probe_fn)(void);
 +
 +static struct kern_feature_desc {
 +      const char *desc;
 +      feature_probe_fn probe;
 +      enum kern_feature_result res;
 +} feature_probes[__FEAT_CNT] = {
 +      [FEAT_PROG_NAME] = {
 +              "BPF program name", probe_kern_prog_name,
 +      },
 +      [FEAT_GLOBAL_DATA] = {
 +              "global variables", probe_kern_global_data,
 +      },
 +      [FEAT_BTF] = {
 +              "minimal BTF", probe_kern_btf,
 +      },
 +      [FEAT_BTF_FUNC] = {
 +              "BTF functions", probe_kern_btf_func,
 +      },
 +      [FEAT_BTF_GLOBAL_FUNC] = {
 +              "BTF global function", probe_kern_btf_func_global,
 +      },
 +      [FEAT_BTF_DATASEC] = {
 +              "BTF data section and variable", probe_kern_btf_datasec,
 +      },
 +      [FEAT_ARRAY_MMAP] = {
 +              "ARRAY map mmap()", probe_kern_array_mmap,
 +      },
 +      [FEAT_EXP_ATTACH_TYPE] = {
 +              "BPF_PROG_LOAD expected_attach_type attribute",
 +              probe_kern_exp_attach_type,
 +      },
 +      [FEAT_PROBE_READ_KERN] = {
 +              "bpf_probe_read_kernel() helper", probe_kern_probe_read_kernel,
 +      },
 +      [FEAT_PROG_BIND_MAP] = {
 +              "BPF_PROG_BIND_MAP support", probe_prog_bind_map,
 +      },
 +      [FEAT_MODULE_BTF] = {
 +              "module BTF support", probe_module_btf,
 +      },
 +      [FEAT_BTF_FLOAT] = {
 +              "BTF_KIND_FLOAT support", probe_kern_btf_float,
 +      },
 +      [FEAT_PERF_LINK] = {
 +              "BPF perf link support", probe_perf_link,
 +      },
 +      [FEAT_BTF_DECL_TAG] = {
 +              "BTF_KIND_DECL_TAG support", probe_kern_btf_decl_tag,
 +      },
 +      [FEAT_BTF_TYPE_TAG] = {
 +              "BTF_KIND_TYPE_TAG support", probe_kern_btf_type_tag,
 +      },
 +      [FEAT_MEMCG_ACCOUNT] = {
 +              "memcg-based memory accounting", probe_memcg_account,
 +      },
 +};
 +
 +bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id)
 +{
 +      struct kern_feature_desc *feat = &feature_probes[feat_id];
 +      int ret;
 +
 +      if (obj && obj->gen_loader)
 +              /* To generate loader program assume the latest kernel
 +               * to avoid doing extra prog_load, map_create syscalls.
 +               */
 +              return true;
 +
 +      if (READ_ONCE(feat->res) == FEAT_UNKNOWN) {
 +              ret = feat->probe();
 +              if (ret > 0) {
 +                      WRITE_ONCE(feat->res, FEAT_SUPPORTED);
 +              } else if (ret == 0) {
 +                      WRITE_ONCE(feat->res, FEAT_MISSING);
 +              } else {
 +                      pr_warn("Detection of kernel %s support failed: %d\n", feat->desc, ret);
 +                      WRITE_ONCE(feat->res, FEAT_MISSING);
 +              }
 +      }
 +
 +      return READ_ONCE(feat->res) == FEAT_SUPPORTED;
 +}
 +
 +static bool map_is_reuse_compat(const struct bpf_map *map, int map_fd)
 +{
 +      struct bpf_map_info map_info = {};
 +      char msg[STRERR_BUFSIZE];
 +      __u32 map_info_len;
 +      int err;
 +
 +      map_info_len = sizeof(map_info);
 +
 +      err = bpf_obj_get_info_by_fd(map_fd, &map_info, &map_info_len);
 +      if (err && errno == EINVAL)
 +              err = bpf_get_map_info_from_fdinfo(map_fd, &map_info);
 +      if (err) {
 +              pr_warn("failed to get map info for map FD %d: %s\n", map_fd,
 +                      libbpf_strerror_r(errno, msg, sizeof(msg)));
 +              return false;
 +      }
 +
 +      return (map_info.type == map->def.type &&
 +              map_info.key_size == map->def.key_size &&
 +              map_info.value_size == map->def.value_size &&
 +              map_info.max_entries == map->def.max_entries &&
 +              map_info.map_flags == map->def.map_flags &&
 +              map_info.map_extra == map->map_extra);
 +}
 +
 +static int
 +bpf_object__reuse_map(struct bpf_map *map)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      int err, pin_fd;
 +
 +      pin_fd = bpf_obj_get(map->pin_path);
 +      if (pin_fd < 0) {
 +              err = -errno;
 +              if (err == -ENOENT) {
 +                      pr_debug("found no pinned map to reuse at '%s'\n",
 +                               map->pin_path);
 +                      return 0;
 +              }
 +
 +              cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg));
 +              pr_warn("couldn't retrieve pinned map '%s': %s\n",
 +                      map->pin_path, cp);
 +              return err;
 +      }
 +
 +      if (!map_is_reuse_compat(map, pin_fd)) {
 +              pr_warn("couldn't reuse pinned map at '%s': parameter mismatch\n",
 +                      map->pin_path);
 +              close(pin_fd);
 +              return -EINVAL;
 +      }
 +
 +      err = bpf_map__reuse_fd(map, pin_fd);
 +      if (err) {
 +              close(pin_fd);
 +              return err;
 +      }
 +      map->pinned = true;
 +      pr_debug("reused pinned map at '%s'\n", map->pin_path);
 +
 +      return 0;
 +}
 +
 +static int
 +bpf_object__populate_internal_map(struct bpf_object *obj, struct bpf_map *map)
 +{
 +      enum libbpf_map_type map_type = map->libbpf_type;
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      int err, zero = 0;
 +
 +      if (obj->gen_loader) {
 +              bpf_gen__map_update_elem(obj->gen_loader, map - obj->maps,
 +                                       map->mmaped, map->def.value_size);
 +              if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_KCONFIG)
 +                      bpf_gen__map_freeze(obj->gen_loader, map - obj->maps);
 +              return 0;
 +      }
 +      err = bpf_map_update_elem(map->fd, &zero, map->mmaped, 0);
 +      if (err) {
 +              err = -errno;
 +              cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 +              pr_warn("Error setting initial map(%s) contents: %s\n",
 +                      map->name, cp);
 +              return err;
 +      }
 +
 +      /* Freeze .rodata and .kconfig map as read-only from syscall side. */
 +      if (map_type == LIBBPF_MAP_RODATA || map_type == LIBBPF_MAP_KCONFIG) {
 +              err = bpf_map_freeze(map->fd);
 +              if (err) {
 +                      err = -errno;
 +                      cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 +                      pr_warn("Error freezing map(%s) as read-only: %s\n",
 +                              map->name, cp);
 +                      return err;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static void bpf_map__destroy(struct bpf_map *map);
 +
 +static int bpf_object__create_map(struct bpf_object *obj, struct bpf_map *map, bool is_inner)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, create_attr);
 +      struct bpf_map_def *def = &map->def;
 +      const char *map_name = NULL;
 +      __u32 max_entries;
 +      int err = 0;
 +
 +      if (kernel_supports(obj, FEAT_PROG_NAME))
 +              map_name = map->name;
 +      create_attr.map_ifindex = map->map_ifindex;
 +      create_attr.map_flags = def->map_flags;
 +      create_attr.numa_node = map->numa_node;
 +      create_attr.map_extra = map->map_extra;
 +
 +      if (def->type == BPF_MAP_TYPE_PERF_EVENT_ARRAY && !def->max_entries) {
 +              int nr_cpus;
 +
 +              nr_cpus = libbpf_num_possible_cpus();
 +              if (nr_cpus < 0) {
 +                      pr_warn("map '%s': failed to determine number of system CPUs: %d\n",
 +                              map->name, nr_cpus);
 +                      return nr_cpus;
 +              }
 +              pr_debug("map '%s': setting size to %d\n", map->name, nr_cpus);
 +              max_entries = nr_cpus;
 +      } else {
 +              max_entries = def->max_entries;
 +      }
 +
 +      if (bpf_map__is_struct_ops(map))
 +              create_attr.btf_vmlinux_value_type_id = map->btf_vmlinux_value_type_id;
 +
 +      if (obj->btf && btf__fd(obj->btf) >= 0 && !bpf_map_find_btf_info(obj, map)) {
 +              create_attr.btf_fd = btf__fd(obj->btf);
 +              create_attr.btf_key_type_id = map->btf_key_type_id;
 +              create_attr.btf_value_type_id = map->btf_value_type_id;
 +      }
 +
 +      if (bpf_map_type__is_map_in_map(def->type)) {
 +              if (map->inner_map) {
 +                      err = bpf_object__create_map(obj, map->inner_map, true);
 +                      if (err) {
 +                              pr_warn("map '%s': failed to create inner map: %d\n",
 +                                      map->name, err);
 +                              return err;
 +                      }
 +                      map->inner_map_fd = bpf_map__fd(map->inner_map);
 +              }
 +              if (map->inner_map_fd >= 0)
 +                      create_attr.inner_map_fd = map->inner_map_fd;
 +      }
 +
 +      switch (def->type) {
 +      case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
 +      case BPF_MAP_TYPE_CGROUP_ARRAY:
 +      case BPF_MAP_TYPE_STACK_TRACE:
 +      case BPF_MAP_TYPE_ARRAY_OF_MAPS:
 +      case BPF_MAP_TYPE_HASH_OF_MAPS:
 +      case BPF_MAP_TYPE_DEVMAP:
 +      case BPF_MAP_TYPE_DEVMAP_HASH:
 +      case BPF_MAP_TYPE_CPUMAP:
 +      case BPF_MAP_TYPE_XSKMAP:
 +      case BPF_MAP_TYPE_SOCKMAP:
 +      case BPF_MAP_TYPE_SOCKHASH:
 +      case BPF_MAP_TYPE_QUEUE:
 +      case BPF_MAP_TYPE_STACK:
 +      case BPF_MAP_TYPE_RINGBUF:
 +              create_attr.btf_fd = 0;
 +              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;
 +      default:
 +              break;
 +      }
 +
 +      if (obj->gen_loader) {
 +              bpf_gen__map_create(obj->gen_loader, def->type, map_name,
 +                                  def->key_size, def->value_size, max_entries,
 +                                  &create_attr, is_inner ? -1 : map - obj->maps);
 +              /* Pretend to have valid FD to pass various fd >= 0 checks.
 +               * This fd == 0 will not be used with any syscall and will be reset to -1 eventually.
 +               */
 +              map->fd = 0;
 +      } else {
 +              map->fd = bpf_map_create(def->type, map_name,
 +                                       def->key_size, def->value_size,
 +                                       max_entries, &create_attr);
 +      }
 +      if (map->fd < 0 && (create_attr.btf_key_type_id ||
 +                          create_attr.btf_value_type_id)) {
 +              char *cp, errmsg[STRERR_BUFSIZE];
 +
 +              err = -errno;
 +              cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 +              pr_warn("Error in bpf_create_map_xattr(%s):%s(%d). Retrying without BTF.\n",
 +                      map->name, cp, err);
 +              create_attr.btf_fd = 0;
 +              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;
 +              map->fd = bpf_map_create(def->type, map_name,
 +                                       def->key_size, def->value_size,
 +                                       max_entries, &create_attr);
 +      }
 +
 +      err = map->fd < 0 ? -errno : 0;
 +
 +      if (bpf_map_type__is_map_in_map(def->type) && map->inner_map) {
 +              if (obj->gen_loader)
 +                      map->inner_map->fd = -1;
 +              bpf_map__destroy(map->inner_map);
 +              zfree(&map->inner_map);
 +      }
 +
 +      return err;
 +}
 +
 +static int init_map_in_map_slots(struct bpf_object *obj, struct bpf_map *map)
 +{
 +      const struct bpf_map *targ_map;
 +      unsigned int i;
 +      int fd, err = 0;
 +
 +      for (i = 0; i < map->init_slots_sz; i++) {
 +              if (!map->init_slots[i])
 +                      continue;
 +
 +              targ_map = map->init_slots[i];
 +              fd = bpf_map__fd(targ_map);
 +
 +              if (obj->gen_loader) {
 +                      bpf_gen__populate_outer_map(obj->gen_loader,
 +                                                  map - obj->maps, i,
 +                                                  targ_map - obj->maps);
 +              } else {
 +                      err = bpf_map_update_elem(map->fd, &i, &fd, 0);
 +              }
 +              if (err) {
 +                      err = -errno;
 +                      pr_warn("map '%s': failed to initialize slot [%d] to map '%s' fd=%d: %d\n",
 +                              map->name, i, targ_map->name, fd, err);
 +                      return err;
 +              }
 +              pr_debug("map '%s': slot [%d] set to map '%s' fd=%d\n",
 +                       map->name, i, targ_map->name, fd);
 +      }
 +
 +      zfree(&map->init_slots);
 +      map->init_slots_sz = 0;
 +
 +      return 0;
 +}
 +
 +static int init_prog_array_slots(struct bpf_object *obj, struct bpf_map *map)
 +{
 +      const struct bpf_program *targ_prog;
 +      unsigned int i;
 +      int fd, err;
 +
 +      if (obj->gen_loader)
 +              return -ENOTSUP;
 +
 +      for (i = 0; i < map->init_slots_sz; i++) {
 +              if (!map->init_slots[i])
 +                      continue;
 +
 +              targ_prog = map->init_slots[i];
 +              fd = bpf_program__fd(targ_prog);
 +
 +              err = bpf_map_update_elem(map->fd, &i, &fd, 0);
 +              if (err) {
 +                      err = -errno;
 +                      pr_warn("map '%s': failed to initialize slot [%d] to prog '%s' fd=%d: %d\n",
 +                              map->name, i, targ_prog->name, fd, err);
 +                      return err;
 +              }
 +              pr_debug("map '%s': slot [%d] set to prog '%s' fd=%d\n",
 +                       map->name, i, targ_prog->name, fd);
 +      }
 +
 +      zfree(&map->init_slots);
 +      map->init_slots_sz = 0;
 +
 +      return 0;
 +}
 +
 +static int bpf_object_init_prog_arrays(struct bpf_object *obj)
 +{
 +      struct bpf_map *map;
 +      int i, err;
 +
 +      for (i = 0; i < obj->nr_maps; i++) {
 +              map = &obj->maps[i];
 +
 +              if (!map->init_slots_sz || map->def.type != BPF_MAP_TYPE_PROG_ARRAY)
 +                      continue;
 +
 +              err = init_prog_array_slots(obj, map);
 +              if (err < 0) {
 +                      zclose(map->fd);
 +                      return err;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int
 +bpf_object__create_maps(struct bpf_object *obj)
 +{
 +      struct bpf_map *map;
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      unsigned int i, j;
 +      int err;
 +      bool retried;
 +
 +      for (i = 0; i < obj->nr_maps; i++) {
 +              map = &obj->maps[i];
 +
 +              /* To support old kernels, we skip creating global data maps
 +               * (.rodata, .data, .kconfig, etc); later on, during program
 +               * loading, if we detect that at least one of the to-be-loaded
 +               * programs is referencing any global data map, we'll error
 +               * out with program name and relocation index logged.
 +               * This approach allows to accommodate Clang emitting
 +               * unnecessary .rodata.str1.1 sections for string literals,
 +               * but also it allows to have CO-RE applications that use
 +               * global variables in some of BPF programs, but not others.
 +               * If those global variable-using programs are not loaded at
 +               * runtime due to bpf_program__set_autoload(prog, false),
 +               * bpf_object loading will succeed just fine even on old
 +               * kernels.
 +               */
 +              if (bpf_map__is_internal(map) &&
 +                  !kernel_supports(obj, FEAT_GLOBAL_DATA)) {
 +                      map->skipped = true;
 +                      continue;
 +              }
 +
 +              retried = false;
 +retry:
 +              if (map->pin_path) {
 +                      err = bpf_object__reuse_map(map);
 +                      if (err) {
 +                              pr_warn("map '%s': error reusing pinned map\n",
 +                                      map->name);
 +                              goto err_out;
 +                      }
 +                      if (retried && map->fd < 0) {
 +                              pr_warn("map '%s': cannot find pinned map\n",
 +                                      map->name);
 +                              err = -ENOENT;
 +                              goto err_out;
 +                      }
 +              }
 +
 +              if (map->fd >= 0) {
 +                      pr_debug("map '%s': skipping creation (preset fd=%d)\n",
 +                               map->name, map->fd);
 +              } else {
 +                      err = bpf_object__create_map(obj, map, false);
 +                      if (err)
 +                              goto err_out;
 +
 +                      pr_debug("map '%s': created successfully, fd=%d\n",
 +                               map->name, map->fd);
 +
 +                      if (bpf_map__is_internal(map)) {
 +                              err = bpf_object__populate_internal_map(obj, map);
 +                              if (err < 0) {
 +                                      zclose(map->fd);
 +                                      goto err_out;
 +                              }
 +                      }
 +
 +                      if (map->init_slots_sz && map->def.type != BPF_MAP_TYPE_PROG_ARRAY) {
 +                              err = init_map_in_map_slots(obj, map);
 +                              if (err < 0) {
 +                                      zclose(map->fd);
 +                                      goto err_out;
 +                              }
 +                      }
 +              }
 +
 +              if (map->pin_path && !map->pinned) {
 +                      err = bpf_map__pin(map, NULL);
 +                      if (err) {
 +                              zclose(map->fd);
 +                              if (!retried && err == -EEXIST) {
 +                                      retried = true;
 +                                      goto retry;
 +                              }
 +                              pr_warn("map '%s': failed to auto-pin at '%s': %d\n",
 +                                      map->name, map->pin_path, err);
 +                              goto err_out;
 +                      }
 +              }
 +      }
 +
 +      return 0;
 +
 +err_out:
 +      cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 +      pr_warn("map '%s': failed to create: %s(%d)\n", map->name, cp, err);
 +      pr_perm_msg(err);
 +      for (j = 0; j < i; j++)
 +              zclose(obj->maps[j].fd);
 +      return err;
 +}
 +
 +static bool bpf_core_is_flavor_sep(const char *s)
 +{
 +      /* check X___Y name pattern, where X and Y are not underscores */
 +      return s[0] != '_' &&                                 /* X */
 +             s[1] == '_' && s[2] == '_' && s[3] == '_' &&   /* ___ */
 +             s[4] != '_';                                   /* Y */
 +}
 +
 +/* Given 'some_struct_name___with_flavor' return the length of a name prefix
 + * before last triple underscore. Struct name part after last triple
 + * underscore is ignored by BPF CO-RE relocation during relocation matching.
 + */
 +size_t bpf_core_essential_name_len(const char *name)
 +{
 +      size_t n = strlen(name);
 +      int i;
 +
 +      for (i = n - 5; i >= 0; i--) {
 +              if (bpf_core_is_flavor_sep(name + i))
 +                      return i + 1;
 +      }
 +      return n;
 +}
 +
 +static void bpf_core_free_cands(struct bpf_core_cand_list *cands)
 +{
 +      free(cands->cands);
 +      free(cands);
 +}
 +
 +static int bpf_core_add_cands(struct bpf_core_cand *local_cand,
 +                            size_t local_essent_len,
 +                            const struct btf *targ_btf,
 +                            const char *targ_btf_name,
 +                            int targ_start_id,
 +                            struct bpf_core_cand_list *cands)
 +{
 +      struct bpf_core_cand *new_cands, *cand;
 +      const struct btf_type *t, *local_t;
 +      const char *targ_name, *local_name;
 +      size_t targ_essent_len;
 +      int n, i;
 +
 +      local_t = btf__type_by_id(local_cand->btf, local_cand->id);
 +      local_name = btf__str_by_offset(local_cand->btf, local_t->name_off);
 +
 +      n = btf__type_cnt(targ_btf);
 +      for (i = targ_start_id; i < n; i++) {
 +              t = btf__type_by_id(targ_btf, i);
 +              if (btf_kind(t) != btf_kind(local_t))
 +                      continue;
 +
 +              targ_name = btf__name_by_offset(targ_btf, t->name_off);
 +              if (str_is_empty(targ_name))
 +                      continue;
 +
 +              targ_essent_len = bpf_core_essential_name_len(targ_name);
 +              if (targ_essent_len != local_essent_len)
 +                      continue;
 +
 +              if (strncmp(local_name, targ_name, local_essent_len) != 0)
 +                      continue;
 +
 +              pr_debug("CO-RE relocating [%d] %s %s: found target candidate [%d] %s %s in [%s]\n",
 +                       local_cand->id, btf_kind_str(local_t),
 +                       local_name, i, btf_kind_str(t), targ_name,
 +                       targ_btf_name);
 +              new_cands = libbpf_reallocarray(cands->cands, cands->len + 1,
 +                                            sizeof(*cands->cands));
 +              if (!new_cands)
 +                      return -ENOMEM;
 +
 +              cand = &new_cands[cands->len];
 +              cand->btf = targ_btf;
 +              cand->id = i;
 +
 +              cands->cands = new_cands;
 +              cands->len++;
 +      }
 +      return 0;
 +}
 +
 +static int load_module_btfs(struct bpf_object *obj)
 +{
 +      struct bpf_btf_info info;
 +      struct module_btf *mod_btf;
 +      struct btf *btf;
 +      char name[64];
 +      __u32 id = 0, len;
 +      int err, fd;
 +
 +      if (obj->btf_modules_loaded)
 +              return 0;
 +
 +      if (obj->gen_loader)
 +              return 0;
 +
 +      /* don't do this again, even if we find no module BTFs */
 +      obj->btf_modules_loaded = true;
 +
 +      /* kernel too old to support module BTFs */
 +      if (!kernel_supports(obj, FEAT_MODULE_BTF))
 +              return 0;
 +
 +      while (true) {
 +              err = bpf_btf_get_next_id(id, &id);
 +              if (err && errno == ENOENT)
 +                      return 0;
 +              if (err) {
 +                      err = -errno;
 +                      pr_warn("failed to iterate BTF objects: %d\n", err);
 +                      return err;
 +              }
 +
 +              fd = bpf_btf_get_fd_by_id(id);
 +              if (fd < 0) {
 +                      if (errno == ENOENT)
 +                              continue; /* expected race: BTF was unloaded */
 +                      err = -errno;
 +                      pr_warn("failed to get BTF object #%d FD: %d\n", id, err);
 +                      return err;
 +              }
 +
 +              len = sizeof(info);
 +              memset(&info, 0, sizeof(info));
 +              info.name = ptr_to_u64(name);
 +              info.name_len = sizeof(name);
 +
 +              err = bpf_obj_get_info_by_fd(fd, &info, &len);
 +              if (err) {
 +                      err = -errno;
 +                      pr_warn("failed to get BTF object #%d info: %d\n", id, err);
 +                      goto err_out;
 +              }
 +
 +              /* ignore non-module BTFs */
 +              if (!info.kernel_btf || strcmp(name, "vmlinux") == 0) {
 +                      close(fd);
 +                      continue;
 +              }
 +
 +              btf = btf_get_from_fd(fd, obj->btf_vmlinux);
 +              err = libbpf_get_error(btf);
 +              if (err) {
 +                      pr_warn("failed to load module [%s]'s BTF object #%d: %d\n",
 +                              name, id, err);
 +                      goto err_out;
 +              }
 +
 +              err = libbpf_ensure_mem((void **)&obj->btf_modules, &obj->btf_module_cap,
 +                                      sizeof(*obj->btf_modules), obj->btf_module_cnt + 1);
 +              if (err)
 +                      goto err_out;
 +
 +              mod_btf = &obj->btf_modules[obj->btf_module_cnt++];
 +
 +              mod_btf->btf = btf;
 +              mod_btf->id = id;
 +              mod_btf->fd = fd;
 +              mod_btf->name = strdup(name);
 +              if (!mod_btf->name) {
 +                      err = -ENOMEM;
 +                      goto err_out;
 +              }
 +              continue;
 +
 +err_out:
 +              close(fd);
 +              return err;
 +      }
 +
 +      return 0;
 +}
 +
 +static struct bpf_core_cand_list *
 +bpf_core_find_cands(struct bpf_object *obj, const struct btf *local_btf, __u32 local_type_id)
 +{
 +      struct bpf_core_cand local_cand = {};
 +      struct bpf_core_cand_list *cands;
 +      const struct btf *main_btf;
 +      const struct btf_type *local_t;
 +      const char *local_name;
 +      size_t local_essent_len;
 +      int err, i;
 +
 +      local_cand.btf = local_btf;
 +      local_cand.id = local_type_id;
 +      local_t = btf__type_by_id(local_btf, local_type_id);
 +      if (!local_t)
 +              return ERR_PTR(-EINVAL);
 +
 +      local_name = btf__name_by_offset(local_btf, local_t->name_off);
 +      if (str_is_empty(local_name))
 +              return ERR_PTR(-EINVAL);
 +      local_essent_len = bpf_core_essential_name_len(local_name);
 +
 +      cands = calloc(1, sizeof(*cands));
 +      if (!cands)
 +              return ERR_PTR(-ENOMEM);
 +
 +      /* Attempt to find target candidates in vmlinux BTF first */
 +      main_btf = obj->btf_vmlinux_override ?: obj->btf_vmlinux;
 +      err = bpf_core_add_cands(&local_cand, local_essent_len, main_btf, "vmlinux", 1, cands);
 +      if (err)
 +              goto err_out;
 +
 +      /* if vmlinux BTF has any candidate, don't got for module BTFs */
 +      if (cands->len)
 +              return cands;
 +
 +      /* if vmlinux BTF was overridden, don't attempt to load module BTFs */
 +      if (obj->btf_vmlinux_override)
 +              return cands;
 +
 +      /* now look through module BTFs, trying to still find candidates */
 +      err = load_module_btfs(obj);
 +      if (err)
 +              goto err_out;
 +
 +      for (i = 0; i < obj->btf_module_cnt; i++) {
 +              err = bpf_core_add_cands(&local_cand, local_essent_len,
 +                                       obj->btf_modules[i].btf,
 +                                       obj->btf_modules[i].name,
 +                                       btf__type_cnt(obj->btf_vmlinux),
 +                                       cands);
 +              if (err)
 +                      goto err_out;
 +      }
 +
 +      return cands;
 +err_out:
 +      bpf_core_free_cands(cands);
 +      return ERR_PTR(err);
 +}
 +
 +/* Check local and target types for compatibility. This check is used for
 + * type-based CO-RE relocations and follow slightly different rules than
 + * field-based relocations. This function assumes that root types were already
 + * checked for name match. Beyond that initial root-level name check, names
 + * are completely ignored. Compatibility rules are as follows:
 + *   - any two STRUCTs/UNIONs/FWDs/ENUMs/INTs are considered compatible, but
 + *     kind should match for local and target types (i.e., STRUCT is not
 + *     compatible with UNION);
 + *   - for ENUMs, the size is ignored;
 + *   - for INT, size and signedness are ignored;
 + *   - for ARRAY, dimensionality is ignored, element types are checked for
 + *     compatibility recursively;
 + *   - CONST/VOLATILE/RESTRICT modifiers are ignored;
 + *   - TYPEDEFs/PTRs are compatible if types they pointing to are compatible;
 + *   - FUNC_PROTOs are compatible if they have compatible signature: same
 + *     number of input args and compatible return and argument types.
 + * These rules are not set in stone and probably will be adjusted as we get
 + * more experience with using BPF CO-RE relocations.
 + */
 +int bpf_core_types_are_compat(const struct btf *local_btf, __u32 local_id,
 +                            const struct btf *targ_btf, __u32 targ_id)
 +{
 +      const struct btf_type *local_type, *targ_type;
 +      int depth = 32; /* max recursion depth */
 +
 +      /* caller made sure that names match (ignoring flavor suffix) */
 +      local_type = btf__type_by_id(local_btf, local_id);
 +      targ_type = btf__type_by_id(targ_btf, targ_id);
 +      if (btf_kind(local_type) != btf_kind(targ_type))
 +              return 0;
 +
 +recur:
 +      depth--;
 +      if (depth < 0)
 +              return -EINVAL;
 +
 +      local_type = skip_mods_and_typedefs(local_btf, local_id, &local_id);
 +      targ_type = skip_mods_and_typedefs(targ_btf, targ_id, &targ_id);
 +      if (!local_type || !targ_type)
 +              return -EINVAL;
 +
 +      if (btf_kind(local_type) != btf_kind(targ_type))
 +              return 0;
 +
 +      switch (btf_kind(local_type)) {
 +      case BTF_KIND_UNKN:
 +      case BTF_KIND_STRUCT:
 +      case BTF_KIND_UNION:
 +      case BTF_KIND_ENUM:
 +      case BTF_KIND_FWD:
 +              return 1;
 +      case BTF_KIND_INT:
 +              /* just reject deprecated bitfield-like integers; all other
 +               * integers are by default compatible between each other
 +               */
 +              return btf_int_offset(local_type) == 0 && btf_int_offset(targ_type) == 0;
 +      case BTF_KIND_PTR:
 +              local_id = local_type->type;
 +              targ_id = targ_type->type;
 +              goto recur;
 +      case BTF_KIND_ARRAY:
 +              local_id = btf_array(local_type)->type;
 +              targ_id = btf_array(targ_type)->type;
 +              goto recur;
 +      case BTF_KIND_FUNC_PROTO: {
 +              struct btf_param *local_p = btf_params(local_type);
 +              struct btf_param *targ_p = btf_params(targ_type);
 +              __u16 local_vlen = btf_vlen(local_type);
 +              __u16 targ_vlen = btf_vlen(targ_type);
 +              int i, err;
 +
 +              if (local_vlen != targ_vlen)
 +                      return 0;
 +
 +              for (i = 0; i < local_vlen; i++, local_p++, targ_p++) {
 +                      skip_mods_and_typedefs(local_btf, local_p->type, &local_id);
 +                      skip_mods_and_typedefs(targ_btf, targ_p->type, &targ_id);
 +                      err = bpf_core_types_are_compat(local_btf, local_id, targ_btf, targ_id);
 +                      if (err <= 0)
 +                              return err;
 +              }
 +
 +              /* tail recurse for return type check */
 +              skip_mods_and_typedefs(local_btf, local_type->type, &local_id);
 +              skip_mods_and_typedefs(targ_btf, targ_type->type, &targ_id);
 +              goto recur;
 +      }
 +      default:
 +              pr_warn("unexpected kind %s relocated, local [%d], target [%d]\n",
 +                      btf_kind_str(local_type), local_id, targ_id);
 +              return 0;
 +      }
 +}
 +
 +static size_t bpf_core_hash_fn(const void *key, void *ctx)
 +{
 +      return (size_t)key;
 +}
 +
 +static bool bpf_core_equal_fn(const void *k1, const void *k2, void *ctx)
 +{
 +      return k1 == k2;
 +}
 +
 +static void *u32_as_hash_key(__u32 x)
 +{
 +      return (void *)(uintptr_t)x;
 +}
 +
 +static int record_relo_core(struct bpf_program *prog,
 +                          const struct bpf_core_relo *core_relo, int insn_idx)
 +{
 +      struct reloc_desc *relos, *relo;
 +
 +      relos = libbpf_reallocarray(prog->reloc_desc,
 +                                  prog->nr_reloc + 1, sizeof(*relos));
 +      if (!relos)
 +              return -ENOMEM;
 +      relo = &relos[prog->nr_reloc];
 +      relo->type = RELO_CORE;
 +      relo->insn_idx = insn_idx;
 +      relo->core_relo = core_relo;
 +      prog->reloc_desc = relos;
 +      prog->nr_reloc++;
 +      return 0;
 +}
 +
 +static int bpf_core_apply_relo(struct bpf_program *prog,
 +                             const struct bpf_core_relo *relo,
 +                             int relo_idx,
 +                             const struct btf *local_btf,
 +                             struct hashmap *cand_cache)
 +{
 +      struct bpf_core_spec specs_scratch[3] = {};
 +      const void *type_key = u32_as_hash_key(relo->type_id);
 +      struct bpf_core_cand_list *cands = NULL;
 +      const char *prog_name = prog->name;
 +      const struct btf_type *local_type;
 +      const char *local_name;
 +      __u32 local_id = relo->type_id;
 +      struct bpf_insn *insn;
 +      int insn_idx, err;
 +
 +      if (relo->insn_off % BPF_INSN_SZ)
 +              return -EINVAL;
 +      insn_idx = relo->insn_off / BPF_INSN_SZ;
 +      /* adjust insn_idx from section frame of reference to the local
 +       * program's frame of reference; (sub-)program code is not yet
 +       * relocated, so it's enough to just subtract in-section offset
 +       */
 +      insn_idx = insn_idx - prog->sec_insn_off;
 +      if (insn_idx >= prog->insns_cnt)
 +              return -EINVAL;
 +      insn = &prog->insns[insn_idx];
 +
 +      local_type = btf__type_by_id(local_btf, local_id);
 +      if (!local_type)
 +              return -EINVAL;
 +
 +      local_name = btf__name_by_offset(local_btf, local_type->name_off);
 +      if (!local_name)
 +              return -EINVAL;
 +
 +      if (prog->obj->gen_loader) {
 +              const char *spec_str = btf__name_by_offset(local_btf, relo->access_str_off);
 +
 +              pr_debug("record_relo_core: prog %td insn[%d] %s %s %s final insn_idx %d\n",
 +                      prog - prog->obj->programs, relo->insn_off / 8,
 +                      btf_kind_str(local_type), local_name, spec_str, insn_idx);
 +              return record_relo_core(prog, relo, insn_idx);
 +      }
 +
 +      if (relo->kind != BPF_CORE_TYPE_ID_LOCAL &&
 +          !hashmap__find(cand_cache, type_key, (void **)&cands)) {
 +              cands = bpf_core_find_cands(prog->obj, local_btf, local_id);
 +              if (IS_ERR(cands)) {
 +                      pr_warn("prog '%s': relo #%d: target candidate search failed for [%d] %s %s: %ld\n",
 +                              prog_name, relo_idx, local_id, btf_kind_str(local_type),
 +                              local_name, PTR_ERR(cands));
 +                      return PTR_ERR(cands);
 +              }
 +              err = hashmap__set(cand_cache, type_key, cands, NULL, NULL);
 +              if (err) {
 +                      bpf_core_free_cands(cands);
 +                      return err;
 +              }
 +      }
 +
 +      return bpf_core_apply_relo_insn(prog_name, insn, insn_idx, relo,
 +                                      relo_idx, local_btf, cands, specs_scratch);
 +}
 +
 +static int
 +bpf_object__relocate_core(struct bpf_object *obj, const char *targ_btf_path)
 +{
 +      const struct btf_ext_info_sec *sec;
 +      const struct bpf_core_relo *rec;
 +      const struct btf_ext_info *seg;
 +      struct hashmap_entry *entry;
 +      struct hashmap *cand_cache = NULL;
 +      struct bpf_program *prog;
 +      const char *sec_name;
 +      int i, err = 0, insn_idx, sec_idx;
 +
 +      if (obj->btf_ext->core_relo_info.len == 0)
 +              return 0;
 +
 +      if (targ_btf_path) {
 +              obj->btf_vmlinux_override = btf__parse(targ_btf_path, NULL);
 +              err = libbpf_get_error(obj->btf_vmlinux_override);
 +              if (err) {
 +                      pr_warn("failed to parse target BTF: %d\n", err);
 +                      return err;
 +              }
 +      }
 +
 +      cand_cache = hashmap__new(bpf_core_hash_fn, bpf_core_equal_fn, NULL);
 +      if (IS_ERR(cand_cache)) {
 +              err = PTR_ERR(cand_cache);
 +              goto out;
 +      }
 +
 +      seg = &obj->btf_ext->core_relo_info;
 +      for_each_btf_ext_sec(seg, sec) {
 +              sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off);
 +              if (str_is_empty(sec_name)) {
 +                      err = -EINVAL;
 +                      goto out;
 +              }
 +              /* bpf_object's ELF is gone by now so it's not easy to find
 +               * section index by section name, but we can find *any*
 +               * bpf_program within desired section name and use it's
 +               * prog->sec_idx to do a proper search by section index and
 +               * instruction offset
 +               */
 +              prog = NULL;
 +              for (i = 0; i < obj->nr_programs; i++) {
 +                      prog = &obj->programs[i];
 +                      if (strcmp(prog->sec_name, sec_name) == 0)
 +                              break;
 +              }
 +              if (!prog) {
 +                      pr_warn("sec '%s': failed to find a BPF program\n", sec_name);
 +                      return -ENOENT;
 +              }
 +              sec_idx = prog->sec_idx;
 +
 +              pr_debug("sec '%s': found %d CO-RE relocations\n",
 +                       sec_name, sec->num_info);
 +
 +              for_each_btf_ext_rec(seg, sec, i, rec) {
 +                      insn_idx = rec->insn_off / BPF_INSN_SZ;
 +                      prog = find_prog_by_sec_insn(obj, sec_idx, insn_idx);
 +                      if (!prog) {
 +                              pr_warn("sec '%s': failed to find program at insn #%d for CO-RE offset relocation #%d\n",
 +                                      sec_name, insn_idx, i);
 +                              err = -EINVAL;
 +                              goto out;
 +                      }
 +                      /* no need to apply CO-RE relocation if the program is
 +                       * not going to be loaded
 +                       */
 +                      if (!prog->load)
 +                              continue;
 +
 +                      err = bpf_core_apply_relo(prog, rec, i, obj->btf, cand_cache);
 +                      if (err) {
 +                              pr_warn("prog '%s': relo #%d: failed to relocate: %d\n",
 +                                      prog->name, i, err);
 +                              goto out;
 +                      }
 +              }
 +      }
 +
 +out:
 +      /* obj->btf_vmlinux and module BTFs are freed after object load */
 +      btf__free(obj->btf_vmlinux_override);
 +      obj->btf_vmlinux_override = NULL;
 +
 +      if (!IS_ERR_OR_NULL(cand_cache)) {
 +              hashmap__for_each_entry(cand_cache, entry, i) {
 +                      bpf_core_free_cands(entry->value);
 +              }
 +              hashmap__free(cand_cache);
 +      }
 +      return err;
 +}
 +
 +/* Relocate data references within program code:
 + *  - map references;
 + *  - global variable references;
 + *  - extern references.
 + */
 +static int
 +bpf_object__relocate_data(struct bpf_object *obj, struct bpf_program *prog)
 +{
 +      int i;
 +
 +      for (i = 0; i < prog->nr_reloc; i++) {
 +              struct reloc_desc *relo = &prog->reloc_desc[i];
 +              struct bpf_insn *insn = &prog->insns[relo->insn_idx];
 +              struct extern_desc *ext;
 +
 +              switch (relo->type) {
 +              case RELO_LD64:
 +                      if (obj->gen_loader) {
 +                              insn[0].src_reg = BPF_PSEUDO_MAP_IDX;
 +                              insn[0].imm = relo->map_idx;
 +                      } else {
 +                              insn[0].src_reg = BPF_PSEUDO_MAP_FD;
 +                              insn[0].imm = obj->maps[relo->map_idx].fd;
 +                      }
 +                      break;
 +              case RELO_DATA:
 +                      insn[1].imm = insn[0].imm + relo->sym_off;
 +                      if (obj->gen_loader) {
 +                              insn[0].src_reg = BPF_PSEUDO_MAP_IDX_VALUE;
 +                              insn[0].imm = relo->map_idx;
 +                      } else {
 +                              const struct bpf_map *map = &obj->maps[relo->map_idx];
 +
 +                              if (map->skipped) {
 +                                      pr_warn("prog '%s': relo #%d: kernel doesn't support global data\n",
 +                                              prog->name, i);
 +                                      return -ENOTSUP;
 +                              }
 +                              insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
 +                              insn[0].imm = obj->maps[relo->map_idx].fd;
 +                      }
 +                      break;
 +              case RELO_EXTERN_VAR:
 +                      ext = &obj->externs[relo->sym_off];
 +                      if (ext->type == EXT_KCFG) {
 +                              if (obj->gen_loader) {
 +                                      insn[0].src_reg = BPF_PSEUDO_MAP_IDX_VALUE;
 +                                      insn[0].imm = obj->kconfig_map_idx;
 +                              } else {
 +                                      insn[0].src_reg = BPF_PSEUDO_MAP_VALUE;
 +                                      insn[0].imm = obj->maps[obj->kconfig_map_idx].fd;
 +                              }
 +                              insn[1].imm = ext->kcfg.data_off;
 +                      } else /* EXT_KSYM */ {
 +                              if (ext->ksym.type_id && ext->is_set) { /* typed ksyms */
 +                                      insn[0].src_reg = BPF_PSEUDO_BTF_ID;
 +                                      insn[0].imm = ext->ksym.kernel_btf_id;
 +                                      insn[1].imm = ext->ksym.kernel_btf_obj_fd;
 +                              } else { /* typeless ksyms or unresolved typed ksyms */
 +                                      insn[0].imm = (__u32)ext->ksym.addr;
 +                                      insn[1].imm = ext->ksym.addr >> 32;
 +                              }
 +                      }
 +                      break;
 +              case RELO_EXTERN_FUNC:
 +                      ext = &obj->externs[relo->sym_off];
 +                      insn[0].src_reg = BPF_PSEUDO_KFUNC_CALL;
 +                      if (ext->is_set) {
 +                              insn[0].imm = ext->ksym.kernel_btf_id;
 +                              insn[0].off = ext->ksym.btf_fd_idx;
 +                      } else { /* unresolved weak kfunc */
 +                              insn[0].imm = 0;
 +                              insn[0].off = 0;
 +                      }
 +                      break;
 +              case RELO_SUBPROG_ADDR:
 +                      if (insn[0].src_reg != BPF_PSEUDO_FUNC) {
 +                              pr_warn("prog '%s': relo #%d: bad insn\n",
 +                                      prog->name, i);
 +                              return -EINVAL;
 +                      }
 +                      /* handled already */
 +                      break;
 +              case RELO_CALL:
 +                      /* handled already */
 +                      break;
 +              case RELO_CORE:
 +                      /* will be handled by bpf_program_record_relos() */
 +                      break;
 +              default:
 +                      pr_warn("prog '%s': relo #%d: bad relo type %d\n",
 +                              prog->name, i, relo->type);
 +                      return -EINVAL;
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static int adjust_prog_btf_ext_info(const struct bpf_object *obj,
 +                                  const struct bpf_program *prog,
 +                                  const struct btf_ext_info *ext_info,
 +                                  void **prog_info, __u32 *prog_rec_cnt,
 +                                  __u32 *prog_rec_sz)
 +{
 +      void *copy_start = NULL, *copy_end = NULL;
 +      void *rec, *rec_end, *new_prog_info;
 +      const struct btf_ext_info_sec *sec;
 +      size_t old_sz, new_sz;
 +      const char *sec_name;
 +      int i, off_adj;
 +
 +      for_each_btf_ext_sec(ext_info, sec) {
 +              sec_name = btf__name_by_offset(obj->btf, sec->sec_name_off);
 +              if (!sec_name)
 +                      return -EINVAL;
 +              if (strcmp(sec_name, prog->sec_name) != 0)
 +                      continue;
 +
 +              for_each_btf_ext_rec(ext_info, sec, i, rec) {
 +                      __u32 insn_off = *(__u32 *)rec / BPF_INSN_SZ;
 +
 +                      if (insn_off < prog->sec_insn_off)
 +                              continue;
 +                      if (insn_off >= prog->sec_insn_off + prog->sec_insn_cnt)
 +                              break;
 +
 +                      if (!copy_start)
 +                              copy_start = rec;
 +                      copy_end = rec + ext_info->rec_size;
 +              }
 +
 +              if (!copy_start)
 +                      return -ENOENT;
 +
 +              /* append func/line info of a given (sub-)program to the main
 +               * program func/line info
 +               */
 +              old_sz = (size_t)(*prog_rec_cnt) * ext_info->rec_size;
 +              new_sz = old_sz + (copy_end - copy_start);
 +              new_prog_info = realloc(*prog_info, new_sz);
 +              if (!new_prog_info)
 +                      return -ENOMEM;
 +              *prog_info = new_prog_info;
 +              *prog_rec_cnt = new_sz / ext_info->rec_size;
 +              memcpy(new_prog_info + old_sz, copy_start, copy_end - copy_start);
 +
 +              /* Kernel instruction offsets are in units of 8-byte
 +               * instructions, while .BTF.ext instruction offsets generated
 +               * by Clang are in units of bytes. So convert Clang offsets
 +               * into kernel offsets and adjust offset according to program
 +               * relocated position.
 +               */
 +              off_adj = prog->sub_insn_off - prog->sec_insn_off;
 +              rec = new_prog_info + old_sz;
 +              rec_end = new_prog_info + new_sz;
 +              for (; rec < rec_end; rec += ext_info->rec_size) {
 +                      __u32 *insn_off = rec;
 +
 +                      *insn_off = *insn_off / BPF_INSN_SZ + off_adj;
 +              }
 +              *prog_rec_sz = ext_info->rec_size;
 +              return 0;
 +      }
 +
 +      return -ENOENT;
 +}
 +
 +static int
 +reloc_prog_func_and_line_info(const struct bpf_object *obj,
 +                            struct bpf_program *main_prog,
 +                            const struct bpf_program *prog)
 +{
 +      int err;
 +
 +      /* no .BTF.ext relocation if .BTF.ext is missing or kernel doesn't
 +       * supprot func/line info
 +       */
 +      if (!obj->btf_ext || !kernel_supports(obj, FEAT_BTF_FUNC))
 +              return 0;
 +
 +      /* only attempt func info relocation if main program's func_info
 +       * relocation was successful
 +       */
 +      if (main_prog != prog && !main_prog->func_info)
 +              goto line_info;
 +
 +      err = adjust_prog_btf_ext_info(obj, prog, &obj->btf_ext->func_info,
 +                                     &main_prog->func_info,
 +                                     &main_prog->func_info_cnt,
 +                                     &main_prog->func_info_rec_size);
 +      if (err) {
 +              if (err != -ENOENT) {
 +                      pr_warn("prog '%s': error relocating .BTF.ext function info: %d\n",
 +                              prog->name, err);
 +                      return err;
 +              }
 +              if (main_prog->func_info) {
 +                      /*
 +                       * Some info has already been found but has problem
 +                       * in the last btf_ext reloc. Must have to error out.
 +                       */
 +                      pr_warn("prog '%s': missing .BTF.ext function info.\n", prog->name);
 +                      return err;
 +              }
 +              /* Have problem loading the very first info. Ignore the rest. */
 +              pr_warn("prog '%s': missing .BTF.ext function info for the main program, skipping all of .BTF.ext func info.\n",
 +                      prog->name);
 +      }
 +
 +line_info:
 +      /* don't relocate line info if main program's relocation failed */
 +      if (main_prog != prog && !main_prog->line_info)
 +              return 0;
 +
 +      err = adjust_prog_btf_ext_info(obj, prog, &obj->btf_ext->line_info,
 +                                     &main_prog->line_info,
 +                                     &main_prog->line_info_cnt,
 +                                     &main_prog->line_info_rec_size);
 +      if (err) {
 +              if (err != -ENOENT) {
 +                      pr_warn("prog '%s': error relocating .BTF.ext line info: %d\n",
 +                              prog->name, err);
 +                      return err;
 +              }
 +              if (main_prog->line_info) {
 +                      /*
 +                       * Some info has already been found but has problem
 +                       * in the last btf_ext reloc. Must have to error out.
 +                       */
 +                      pr_warn("prog '%s': missing .BTF.ext line info.\n", prog->name);
 +                      return err;
 +              }
 +              /* Have problem loading the very first info. Ignore the rest. */
 +              pr_warn("prog '%s': missing .BTF.ext line info for the main program, skipping all of .BTF.ext line info.\n",
 +                      prog->name);
 +      }
 +      return 0;
 +}
 +
 +static int cmp_relo_by_insn_idx(const void *key, const void *elem)
 +{
 +      size_t insn_idx = *(const size_t *)key;
 +      const struct reloc_desc *relo = elem;
 +
 +      if (insn_idx == relo->insn_idx)
 +              return 0;
 +      return insn_idx < relo->insn_idx ? -1 : 1;
 +}
 +
 +static struct reloc_desc *find_prog_insn_relo(const struct bpf_program *prog, size_t insn_idx)
 +{
 +      if (!prog->nr_reloc)
 +              return NULL;
 +      return bsearch(&insn_idx, prog->reloc_desc, prog->nr_reloc,
 +                     sizeof(*prog->reloc_desc), cmp_relo_by_insn_idx);
 +}
 +
 +static int append_subprog_relos(struct bpf_program *main_prog, struct bpf_program *subprog)
 +{
 +      int new_cnt = main_prog->nr_reloc + subprog->nr_reloc;
 +      struct reloc_desc *relos;
 +      int i;
 +
 +      if (main_prog == subprog)
 +              return 0;
 +      relos = libbpf_reallocarray(main_prog->reloc_desc, new_cnt, sizeof(*relos));
 +      if (!relos)
 +              return -ENOMEM;
 +      if (subprog->nr_reloc)
 +              memcpy(relos + main_prog->nr_reloc, subprog->reloc_desc,
 +                     sizeof(*relos) * subprog->nr_reloc);
 +
 +      for (i = main_prog->nr_reloc; i < new_cnt; i++)
 +              relos[i].insn_idx += subprog->sub_insn_off;
 +      /* After insn_idx adjustment the 'relos' array is still sorted
 +       * by insn_idx and doesn't break bsearch.
 +       */
 +      main_prog->reloc_desc = relos;
 +      main_prog->nr_reloc = new_cnt;
 +      return 0;
 +}
 +
 +static int
 +bpf_object__reloc_code(struct bpf_object *obj, struct bpf_program *main_prog,
 +                     struct bpf_program *prog)
 +{
 +      size_t sub_insn_idx, insn_idx, new_cnt;
 +      struct bpf_program *subprog;
 +      struct bpf_insn *insns, *insn;
 +      struct reloc_desc *relo;
 +      int err;
 +
 +      err = reloc_prog_func_and_line_info(obj, main_prog, prog);
 +      if (err)
 +              return err;
 +
 +      for (insn_idx = 0; insn_idx < prog->sec_insn_cnt; insn_idx++) {
 +              insn = &main_prog->insns[prog->sub_insn_off + insn_idx];
 +              if (!insn_is_subprog_call(insn) && !insn_is_pseudo_func(insn))
 +                      continue;
 +
 +              relo = find_prog_insn_relo(prog, insn_idx);
 +              if (relo && relo->type == RELO_EXTERN_FUNC)
 +                      /* kfunc relocations will be handled later
 +                       * in bpf_object__relocate_data()
 +                       */
 +                      continue;
 +              if (relo && relo->type != RELO_CALL && relo->type != RELO_SUBPROG_ADDR) {
 +                      pr_warn("prog '%s': unexpected relo for insn #%zu, type %d\n",
 +                              prog->name, insn_idx, relo->type);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              if (relo) {
 +                      /* sub-program instruction index is a combination of
 +                       * an offset of a symbol pointed to by relocation and
 +                       * call instruction's imm field; for global functions,
 +                       * call always has imm = -1, but for static functions
 +                       * relocation is against STT_SECTION and insn->imm
 +                       * points to a start of a static function
 +                       *
 +                       * for subprog addr relocation, the relo->sym_off + insn->imm is
 +                       * the byte offset in the corresponding section.
 +                       */
 +                      if (relo->type == RELO_CALL)
 +                              sub_insn_idx = relo->sym_off / BPF_INSN_SZ + insn->imm + 1;
 +                      else
 +                              sub_insn_idx = (relo->sym_off + insn->imm) / BPF_INSN_SZ;
 +              } else if (insn_is_pseudo_func(insn)) {
 +                      /*
 +                       * RELO_SUBPROG_ADDR relo is always emitted even if both
 +                       * functions are in the same section, so it shouldn't reach here.
 +                       */
 +                      pr_warn("prog '%s': missing subprog addr relo for insn #%zu\n",
 +                              prog->name, insn_idx);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              } else {
 +                      /* if subprogram call is to a static function within
 +                       * the same ELF section, there won't be any relocation
 +                       * emitted, but it also means there is no additional
 +                       * offset necessary, insns->imm is relative to
 +                       * instruction's original position within the section
 +                       */
 +                      sub_insn_idx = prog->sec_insn_off + insn_idx + insn->imm + 1;
 +              }
 +
 +              /* we enforce that sub-programs should be in .text section */
 +              subprog = find_prog_by_sec_insn(obj, obj->efile.text_shndx, sub_insn_idx);
 +              if (!subprog) {
 +                      pr_warn("prog '%s': no .text section found yet sub-program call exists\n",
 +                              prog->name);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +
 +              /* if it's the first call instruction calling into this
 +               * subprogram (meaning this subprog hasn't been processed
 +               * yet) within the context of current main program:
 +               *   - append it at the end of main program's instructions blog;
 +               *   - process is recursively, while current program is put on hold;
 +               *   - if that subprogram calls some other not yet processes
 +               *   subprogram, same thing will happen recursively until
 +               *   there are no more unprocesses subprograms left to append
 +               *   and relocate.
 +               */
 +              if (subprog->sub_insn_off == 0) {
 +                      subprog->sub_insn_off = main_prog->insns_cnt;
 +
 +                      new_cnt = main_prog->insns_cnt + subprog->insns_cnt;
 +                      insns = libbpf_reallocarray(main_prog->insns, new_cnt, sizeof(*insns));
 +                      if (!insns) {
 +                              pr_warn("prog '%s': failed to realloc prog code\n", main_prog->name);
 +                              return -ENOMEM;
 +                      }
 +                      main_prog->insns = insns;
 +                      main_prog->insns_cnt = new_cnt;
 +
 +                      memcpy(main_prog->insns + subprog->sub_insn_off, subprog->insns,
 +                             subprog->insns_cnt * sizeof(*insns));
 +
 +                      pr_debug("prog '%s': added %zu insns from sub-prog '%s'\n",
 +                               main_prog->name, subprog->insns_cnt, subprog->name);
 +
 +                      /* The subprog insns are now appended. Append its relos too. */
 +                      err = append_subprog_relos(main_prog, subprog);
 +                      if (err)
 +                              return err;
 +                      err = bpf_object__reloc_code(obj, main_prog, subprog);
 +                      if (err)
 +                              return err;
 +              }
 +
 +              /* main_prog->insns memory could have been re-allocated, so
 +               * calculate pointer again
 +               */
 +              insn = &main_prog->insns[prog->sub_insn_off + insn_idx];
 +              /* calculate correct instruction position within current main
 +               * prog; each main prog can have a different set of
 +               * subprograms appended (potentially in different order as
 +               * well), so position of any subprog can be different for
 +               * different main programs */
 +              insn->imm = subprog->sub_insn_off - (prog->sub_insn_off + insn_idx) - 1;
 +
 +              pr_debug("prog '%s': insn #%zu relocated, imm %d points to subprog '%s' (now at %zu offset)\n",
 +                       prog->name, insn_idx, insn->imm, subprog->name, subprog->sub_insn_off);
 +      }
 +
 +      return 0;
 +}
 +
 +/*
 + * Relocate sub-program calls.
 + *
 + * Algorithm operates as follows. Each entry-point BPF program (referred to as
 + * main prog) is processed separately. For each subprog (non-entry functions,
 + * that can be called from either entry progs or other subprogs) gets their
 + * sub_insn_off reset to zero. This serves as indicator that this subprogram
 + * hasn't been yet appended and relocated within current main prog. Once its
 + * relocated, sub_insn_off will point at the position within current main prog
 + * where given subprog was appended. This will further be used to relocate all
 + * the call instructions jumping into this subprog.
 + *
 + * We start with main program and process all call instructions. If the call
 + * is into a subprog that hasn't been processed (i.e., subprog->sub_insn_off
 + * is zero), subprog instructions are appended at the end of main program's
 + * instruction array. Then main program is "put on hold" while we recursively
 + * process newly appended subprogram. If that subprogram calls into another
 + * subprogram that hasn't been appended, new subprogram is appended again to
 + * the *main* prog's instructions (subprog's instructions are always left
 + * untouched, as they need to be in unmodified state for subsequent main progs
 + * and subprog instructions are always sent only as part of a main prog) and
 + * the process continues recursively. Once all the subprogs called from a main
 + * prog or any of its subprogs are appended (and relocated), all their
 + * positions within finalized instructions array are known, so it's easy to
 + * rewrite call instructions with correct relative offsets, corresponding to
 + * desired target subprog.
 + *
 + * Its important to realize that some subprogs might not be called from some
 + * main prog and any of its called/used subprogs. Those will keep their
 + * subprog->sub_insn_off as zero at all times and won't be appended to current
 + * main prog and won't be relocated within the context of current main prog.
 + * They might still be used from other main progs later.
 + *
 + * Visually this process can be shown as below. Suppose we have two main
 + * programs mainA and mainB and BPF object contains three subprogs: subA,
 + * subB, and subC. mainA calls only subA, mainB calls only subC, but subA and
 + * subC both call subB:
 + *
 + *        +--------+ +-------+
 + *        |        v v       |
 + *     +--+---+ +--+-+-+ +---+--+
 + *     | subA | | subB | | subC |
 + *     +--+---+ +------+ +---+--+
 + *        ^                  ^
 + *        |                  |
 + *    +---+-------+   +------+----+
 + *    |   mainA   |   |   mainB   |
 + *    +-----------+   +-----------+
 + *
 + * We'll start relocating mainA, will find subA, append it and start
 + * processing sub A recursively:
 + *
 + *    +-----------+------+
 + *    |   mainA   | subA |
 + *    +-----------+------+
 + *
 + * At this point we notice that subB is used from subA, so we append it and
 + * relocate (there are no further subcalls from subB):
 + *
 + *    +-----------+------+------+
 + *    |   mainA   | subA | subB |
 + *    +-----------+------+------+
 + *
 + * At this point, we relocate subA calls, then go one level up and finish with
 + * relocatin mainA calls. mainA is done.
 + *
 + * For mainB process is similar but results in different order. We start with
 + * mainB and skip subA and subB, as mainB never calls them (at least
 + * directly), but we see subC is needed, so we append and start processing it:
 + *
 + *    +-----------+------+
 + *    |   mainB   | subC |
 + *    +-----------+------+
 + * Now we see subC needs subB, so we go back to it, append and relocate it:
 + *
 + *    +-----------+------+------+
 + *    |   mainB   | subC | subB |
 + *    +-----------+------+------+
 + *
 + * At this point we unwind recursion, relocate calls in subC, then in mainB.
 + */
 +static int
 +bpf_object__relocate_calls(struct bpf_object *obj, struct bpf_program *prog)
 +{
 +      struct bpf_program *subprog;
 +      int i, err;
 +
 +      /* mark all subprogs as not relocated (yet) within the context of
 +       * current main program
 +       */
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              subprog = &obj->programs[i];
 +              if (!prog_is_subprog(obj, subprog))
 +                      continue;
 +
 +              subprog->sub_insn_off = 0;
 +      }
 +
 +      err = bpf_object__reloc_code(obj, prog, prog);
 +      if (err)
 +              return err;
 +
 +
 +      return 0;
 +}
 +
 +static void
 +bpf_object__free_relocs(struct bpf_object *obj)
 +{
 +      struct bpf_program *prog;
 +      int i;
 +
 +      /* free up relocation descriptors */
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              prog = &obj->programs[i];
 +              zfree(&prog->reloc_desc);
 +              prog->nr_reloc = 0;
 +      }
 +}
 +
 +static int cmp_relocs(const void *_a, const void *_b)
 +{
 +      const struct reloc_desc *a = _a;
 +      const struct reloc_desc *b = _b;
 +
 +      if (a->insn_idx != b->insn_idx)
 +              return a->insn_idx < b->insn_idx ? -1 : 1;
 +
 +      /* no two relocations should have the same insn_idx, but ... */
 +      if (a->type != b->type)
 +              return a->type < b->type ? -1 : 1;
 +
 +      return 0;
 +}
 +
 +static void bpf_object__sort_relos(struct bpf_object *obj)
 +{
 +      int i;
 +
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              struct bpf_program *p = &obj->programs[i];
 +
 +              if (!p->nr_reloc)
 +                      continue;
 +
 +              qsort(p->reloc_desc, p->nr_reloc, sizeof(*p->reloc_desc), cmp_relocs);
 +      }
 +}
 +
 +static int
 +bpf_object__relocate(struct bpf_object *obj, const char *targ_btf_path)
 +{
 +      struct bpf_program *prog;
 +      size_t i, j;
 +      int err;
 +
 +      if (obj->btf_ext) {
 +              err = bpf_object__relocate_core(obj, targ_btf_path);
 +              if (err) {
 +                      pr_warn("failed to perform CO-RE relocations: %d\n",
 +                              err);
 +                      return err;
 +              }
 +              if (obj->gen_loader)
 +                      bpf_object__sort_relos(obj);
 +      }
 +
 +      /* Before relocating calls pre-process relocations and mark
 +       * few ld_imm64 instructions that points to subprogs.
 +       * Otherwise bpf_object__reloc_code() later would have to consider
 +       * all ld_imm64 insns as relocation candidates. That would
 +       * reduce relocation speed, since amount of find_prog_insn_relo()
 +       * would increase and most of them will fail to find a relo.
 +       */
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              prog = &obj->programs[i];
 +              for (j = 0; j < prog->nr_reloc; j++) {
 +                      struct reloc_desc *relo = &prog->reloc_desc[j];
 +                      struct bpf_insn *insn = &prog->insns[relo->insn_idx];
 +
 +                      /* mark the insn, so it's recognized by insn_is_pseudo_func() */
 +                      if (relo->type == RELO_SUBPROG_ADDR)
 +                              insn[0].src_reg = BPF_PSEUDO_FUNC;
 +              }
 +      }
 +
 +      /* relocate subprogram calls and append used subprograms to main
 +       * programs; each copy of subprogram code needs to be relocated
 +       * differently for each main program, because its code location might
 +       * have changed.
 +       * Append subprog relos to main programs to allow data relos to be
 +       * processed after text is completely relocated.
 +       */
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              prog = &obj->programs[i];
 +              /* sub-program's sub-calls are relocated within the context of
 +               * its main program only
 +               */
 +              if (prog_is_subprog(obj, prog))
 +                      continue;
 +              if (!prog->load)
 +                      continue;
 +
 +              err = bpf_object__relocate_calls(obj, prog);
 +              if (err) {
 +                      pr_warn("prog '%s': failed to relocate calls: %d\n",
 +                              prog->name, err);
 +                      return err;
 +              }
 +      }
 +      /* Process data relos for main programs */
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              prog = &obj->programs[i];
 +              if (prog_is_subprog(obj, prog))
 +                      continue;
 +              if (!prog->load)
 +                      continue;
 +              err = bpf_object__relocate_data(obj, prog);
 +              if (err) {
 +                      pr_warn("prog '%s': failed to relocate data references: %d\n",
 +                              prog->name, err);
 +                      return err;
 +              }
 +      }
 +      if (!obj->gen_loader)
 +              bpf_object__free_relocs(obj);
 +      return 0;
 +}
 +
 +static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
 +                                          Elf64_Shdr *shdr, Elf_Data *data);
 +
 +static int bpf_object__collect_map_relos(struct bpf_object *obj,
 +                                       Elf64_Shdr *shdr, Elf_Data *data)
 +{
 +      const int bpf_ptr_sz = 8, host_ptr_sz = sizeof(void *);
 +      int i, j, nrels, new_sz;
 +      const struct btf_var_secinfo *vi = NULL;
 +      const struct btf_type *sec, *var, *def;
 +      struct bpf_map *map = NULL, *targ_map = NULL;
 +      struct bpf_program *targ_prog = NULL;
 +      bool is_prog_array, is_map_in_map;
 +      const struct btf_member *member;
 +      const char *name, *mname, *type;
 +      unsigned int moff;
 +      Elf64_Sym *sym;
 +      Elf64_Rel *rel;
 +      void *tmp;
 +
 +      if (!obj->efile.btf_maps_sec_btf_id || !obj->btf)
 +              return -EINVAL;
 +      sec = btf__type_by_id(obj->btf, obj->efile.btf_maps_sec_btf_id);
 +      if (!sec)
 +              return -EINVAL;
 +
 +      nrels = shdr->sh_size / shdr->sh_entsize;
 +      for (i = 0; i < nrels; i++) {
 +              rel = elf_rel_by_idx(data, i);
 +              if (!rel) {
 +                      pr_warn(".maps relo #%d: failed to get ELF relo\n", i);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              sym = elf_sym_by_idx(obj, ELF64_R_SYM(rel->r_info));
 +              if (!sym) {
 +                      pr_warn(".maps relo #%d: symbol %zx not found\n",
 +                              i, (size_t)ELF64_R_SYM(rel->r_info));
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +              name = elf_sym_str(obj, sym->st_name) ?: "<?>";
 +
 +              pr_debug(".maps relo #%d: for %zd value %zd rel->r_offset %zu name %d ('%s')\n",
 +                       i, (ssize_t)(rel->r_info >> 32), (size_t)sym->st_value,
 +                       (size_t)rel->r_offset, sym->st_name, name);
 +
 +              for (j = 0; j < obj->nr_maps; j++) {
 +                      map = &obj->maps[j];
 +                      if (map->sec_idx != obj->efile.btf_maps_shndx)
 +                              continue;
 +
 +                      vi = btf_var_secinfos(sec) + map->btf_var_idx;
 +                      if (vi->offset <= rel->r_offset &&
 +                          rel->r_offset + bpf_ptr_sz <= vi->offset + vi->size)
 +                              break;
 +              }
 +              if (j == obj->nr_maps) {
 +                      pr_warn(".maps relo #%d: cannot find map '%s' at rel->r_offset %zu\n",
 +                              i, name, (size_t)rel->r_offset);
 +                      return -EINVAL;
 +              }
 +
 +              is_map_in_map = bpf_map_type__is_map_in_map(map->def.type);
 +              is_prog_array = map->def.type == BPF_MAP_TYPE_PROG_ARRAY;
 +              type = is_map_in_map ? "map" : "prog";
 +              if (is_map_in_map) {
 +                      if (sym->st_shndx != obj->efile.btf_maps_shndx) {
 +                              pr_warn(".maps relo #%d: '%s' isn't a BTF-defined map\n",
 +                                      i, name);
 +                              return -LIBBPF_ERRNO__RELOC;
 +                      }
 +                      if (map->def.type == BPF_MAP_TYPE_HASH_OF_MAPS &&
 +                          map->def.key_size != sizeof(int)) {
 +                              pr_warn(".maps relo #%d: hash-of-maps '%s' should have key size %zu.\n",
 +                                      i, map->name, sizeof(int));
 +                              return -EINVAL;
 +                      }
 +                      targ_map = bpf_object__find_map_by_name(obj, name);
 +                      if (!targ_map) {
 +                              pr_warn(".maps relo #%d: '%s' isn't a valid map reference\n",
 +                                      i, name);
 +                              return -ESRCH;
 +                      }
 +              } else if (is_prog_array) {
 +                      targ_prog = bpf_object__find_program_by_name(obj, name);
 +                      if (!targ_prog) {
 +                              pr_warn(".maps relo #%d: '%s' isn't a valid program reference\n",
 +                                      i, name);
 +                              return -ESRCH;
 +                      }
 +                      if (targ_prog->sec_idx != sym->st_shndx ||
 +                          targ_prog->sec_insn_off * 8 != sym->st_value ||
 +                          prog_is_subprog(obj, targ_prog)) {
 +                              pr_warn(".maps relo #%d: '%s' isn't an entry-point program\n",
 +                                      i, name);
 +                              return -LIBBPF_ERRNO__RELOC;
 +                      }
 +              } else {
 +                      return -EINVAL;
 +              }
 +
 +              var = btf__type_by_id(obj->btf, vi->type);
 +              def = skip_mods_and_typedefs(obj->btf, var->type, NULL);
 +              if (btf_vlen(def) == 0)
 +                      return -EINVAL;
 +              member = btf_members(def) + btf_vlen(def) - 1;
 +              mname = btf__name_by_offset(obj->btf, member->name_off);
 +              if (strcmp(mname, "values"))
 +                      return -EINVAL;
 +
 +              moff = btf_member_bit_offset(def, btf_vlen(def) - 1) / 8;
 +              if (rel->r_offset - vi->offset < moff)
 +                      return -EINVAL;
 +
 +              moff = rel->r_offset - vi->offset - moff;
 +              /* here we use BPF pointer size, which is always 64 bit, as we
 +               * are parsing ELF that was built for BPF target
 +               */
 +              if (moff % bpf_ptr_sz)
 +                      return -EINVAL;
 +              moff /= bpf_ptr_sz;
 +              if (moff >= map->init_slots_sz) {
 +                      new_sz = moff + 1;
 +                      tmp = libbpf_reallocarray(map->init_slots, new_sz, host_ptr_sz);
 +                      if (!tmp)
 +                              return -ENOMEM;
 +                      map->init_slots = tmp;
 +                      memset(map->init_slots + map->init_slots_sz, 0,
 +                             (new_sz - map->init_slots_sz) * host_ptr_sz);
 +                      map->init_slots_sz = new_sz;
 +              }
 +              map->init_slots[moff] = is_map_in_map ? (void *)targ_map : (void *)targ_prog;
 +
 +              pr_debug(".maps relo #%d: map '%s' slot [%d] points to %s '%s'\n",
 +                       i, map->name, moff, type, name);
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object__collect_relos(struct bpf_object *obj)
 +{
 +      int i, err;
 +
 +      for (i = 0; i < obj->efile.sec_cnt; i++) {
 +              struct elf_sec_desc *sec_desc = &obj->efile.secs[i];
 +              Elf64_Shdr *shdr;
 +              Elf_Data *data;
 +              int idx;
 +
 +              if (sec_desc->sec_type != SEC_RELO)
 +                      continue;
 +
 +              shdr = sec_desc->shdr;
 +              data = sec_desc->data;
 +              idx = shdr->sh_info;
 +
 +              if (shdr->sh_type != SHT_REL) {
 +                      pr_warn("internal error at %d\n", __LINE__);
 +                      return -LIBBPF_ERRNO__INTERNAL;
 +              }
 +
 +              if (idx == obj->efile.st_ops_shndx)
 +                      err = bpf_object__collect_st_ops_relos(obj, shdr, data);
 +              else if (idx == obj->efile.btf_maps_shndx)
 +                      err = bpf_object__collect_map_relos(obj, shdr, data);
 +              else
 +                      err = bpf_object__collect_prog_relos(obj, shdr, data);
 +              if (err)
 +                      return err;
 +      }
 +
 +      bpf_object__sort_relos(obj);
 +      return 0;
 +}
 +
 +static bool insn_is_helper_call(struct bpf_insn *insn, enum bpf_func_id *func_id)
 +{
 +      if (BPF_CLASS(insn->code) == BPF_JMP &&
 +          BPF_OP(insn->code) == BPF_CALL &&
 +          BPF_SRC(insn->code) == BPF_K &&
 +          insn->src_reg == 0 &&
 +          insn->dst_reg == 0) {
 +                  *func_id = insn->imm;
 +                  return true;
 +      }
 +      return false;
 +}
 +
 +static int bpf_object__sanitize_prog(struct bpf_object *obj, struct bpf_program *prog)
 +{
 +      struct bpf_insn *insn = prog->insns;
 +      enum bpf_func_id func_id;
 +      int i;
 +
 +      if (obj->gen_loader)
 +              return 0;
 +
 +      for (i = 0; i < prog->insns_cnt; i++, insn++) {
 +              if (!insn_is_helper_call(insn, &func_id))
 +                      continue;
 +
 +              /* on kernels that don't yet support
 +               * bpf_probe_read_{kernel,user}[_str] helpers, fall back
 +               * to bpf_probe_read() which works well for old kernels
 +               */
 +              switch (func_id) {
 +              case BPF_FUNC_probe_read_kernel:
 +              case BPF_FUNC_probe_read_user:
 +                      if (!kernel_supports(obj, FEAT_PROBE_READ_KERN))
 +                              insn->imm = BPF_FUNC_probe_read;
 +                      break;
 +              case BPF_FUNC_probe_read_kernel_str:
 +              case BPF_FUNC_probe_read_user_str:
 +                      if (!kernel_supports(obj, FEAT_PROBE_READ_KERN))
 +                              insn->imm = BPF_FUNC_probe_read_str;
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attach_name,
 +                                   int *btf_obj_fd, int *btf_type_id);
 +
 +/* this is called as prog->sec_def->preload_fn for libbpf-supported sec_defs */
 +static int libbpf_preload_prog(struct bpf_program *prog,
 +                             struct bpf_prog_load_opts *opts, long cookie)
 +{
 +      enum sec_def_flags def = cookie;
 +
 +      /* old kernels might not support specifying expected_attach_type */
 +      if ((def & SEC_EXP_ATTACH_OPT) && !kernel_supports(prog->obj, FEAT_EXP_ATTACH_TYPE))
 +              opts->expected_attach_type = 0;
 +
 +      if (def & SEC_SLEEPABLE)
 +              opts->prog_flags |= BPF_F_SLEEPABLE;
 +
 +      if ((prog->type == BPF_PROG_TYPE_TRACING ||
 +           prog->type == BPF_PROG_TYPE_LSM ||
 +           prog->type == BPF_PROG_TYPE_EXT) && !prog->attach_btf_id) {
 +              int btf_obj_fd = 0, btf_type_id = 0, err;
 +              const char *attach_name;
 +
 +              attach_name = strchr(prog->sec_name, '/') + 1;
 +              err = libbpf_find_attach_btf_id(prog, attach_name, &btf_obj_fd, &btf_type_id);
 +              if (err)
 +                      return err;
 +
 +              /* cache resolved BTF FD and BTF type ID in the prog */
 +              prog->attach_btf_obj_fd = btf_obj_fd;
 +              prog->attach_btf_id = btf_type_id;
 +
 +              /* but by now libbpf common logic is not utilizing
 +               * prog->atach_btf_obj_fd/prog->attach_btf_id anymore because
 +               * this callback is called after opts were populated by
 +               * libbpf, so this callback has to update opts explicitly here
 +               */
 +              opts->attach_btf_obj_fd = btf_obj_fd;
 +              opts->attach_btf_id = btf_type_id;
 +      }
 +      return 0;
 +}
 +
 +static int bpf_object_load_prog_instance(struct bpf_object *obj, struct bpf_program *prog,
 +                                       struct bpf_insn *insns, int insns_cnt,
 +                                       const char *license, __u32 kern_version,
 +                                       int *prog_fd)
 +{
 +      LIBBPF_OPTS(bpf_prog_load_opts, load_attr);
 +      const char *prog_name = NULL;
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      size_t log_buf_size = 0;
 +      char *log_buf = NULL, *tmp;
 +      int btf_fd, ret, err;
 +      bool own_log_buf = true;
 +      __u32 log_level = prog->log_level;
 +
 +      if (prog->type == BPF_PROG_TYPE_UNSPEC) {
 +              /*
 +               * The program type must be set.  Most likely we couldn't find a proper
 +               * section definition at load time, and thus we didn't infer the type.
 +               */
 +              pr_warn("prog '%s': missing BPF prog type, check ELF section name '%s'\n",
 +                      prog->name, prog->sec_name);
 +              return -EINVAL;
 +      }
 +
 +      if (!insns || !insns_cnt)
 +              return -EINVAL;
 +
 +      load_attr.expected_attach_type = prog->expected_attach_type;
 +      if (kernel_supports(obj, FEAT_PROG_NAME))
 +              prog_name = prog->name;
 +      load_attr.attach_prog_fd = prog->attach_prog_fd;
 +      load_attr.attach_btf_obj_fd = prog->attach_btf_obj_fd;
 +      load_attr.attach_btf_id = prog->attach_btf_id;
 +      load_attr.kern_version = kern_version;
 +      load_attr.prog_ifindex = prog->prog_ifindex;
 +
 +      /* specify func_info/line_info only if kernel supports them */
 +      btf_fd = bpf_object__btf_fd(obj);
 +      if (btf_fd >= 0 && kernel_supports(obj, FEAT_BTF_FUNC)) {
 +              load_attr.prog_btf_fd = btf_fd;
 +              load_attr.func_info = prog->func_info;
 +              load_attr.func_info_rec_size = prog->func_info_rec_size;
 +              load_attr.func_info_cnt = prog->func_info_cnt;
 +              load_attr.line_info = prog->line_info;
 +              load_attr.line_info_rec_size = prog->line_info_rec_size;
 +              load_attr.line_info_cnt = prog->line_info_cnt;
 +      }
 +      load_attr.log_level = log_level;
 +      load_attr.prog_flags = prog->prog_flags;
 +      load_attr.fd_array = obj->fd_array;
 +
 +      /* adjust load_attr if sec_def provides custom preload callback */
 +      if (prog->sec_def && prog->sec_def->preload_fn) {
 +              err = prog->sec_def->preload_fn(prog, &load_attr, prog->sec_def->cookie);
 +              if (err < 0) {
 +                      pr_warn("prog '%s': failed to prepare load attributes: %d\n",
 +                              prog->name, err);
 +                      return err;
 +              }
 +      }
 +
 +      if (obj->gen_loader) {
 +              bpf_gen__prog_load(obj->gen_loader, prog->type, prog->name,
 +                                 license, insns, insns_cnt, &load_attr,
 +                                 prog - obj->programs);
 +              *prog_fd = -1;
 +              return 0;
 +      }
 +
 +retry_load:
 +      /* if log_level is zero, we don't request logs initiallly even if
 +       * custom log_buf is specified; if the program load fails, then we'll
 +       * bump log_level to 1 and use either custom log_buf or we'll allocate
 +       * our own and retry the load to get details on what failed
 +       */
 +      if (log_level) {
 +              if (prog->log_buf) {
 +                      log_buf = prog->log_buf;
 +                      log_buf_size = prog->log_size;
 +                      own_log_buf = false;
 +              } else if (obj->log_buf) {
 +                      log_buf = obj->log_buf;
 +                      log_buf_size = obj->log_size;
 +                      own_log_buf = false;
 +              } else {
 +                      log_buf_size = max((size_t)BPF_LOG_BUF_SIZE, log_buf_size * 2);
 +                      tmp = realloc(log_buf, log_buf_size);
 +                      if (!tmp) {
 +                              ret = -ENOMEM;
 +                              goto out;
 +                      }
 +                      log_buf = tmp;
 +                      log_buf[0] = '\0';
 +                      own_log_buf = true;
 +              }
 +      }
 +
 +      load_attr.log_buf = log_buf;
 +      load_attr.log_size = log_buf_size;
 +      load_attr.log_level = log_level;
 +
 +      ret = bpf_prog_load(prog->type, prog_name, license, insns, insns_cnt, &load_attr);
 +      if (ret >= 0) {
 +              if (log_level && own_log_buf) {
 +                      pr_debug("prog '%s': -- BEGIN PROG LOAD LOG --\n%s-- END PROG LOAD LOG --\n",
 +                               prog->name, log_buf);
 +              }
 +
 +              if (obj->has_rodata && kernel_supports(obj, FEAT_PROG_BIND_MAP)) {
 +                      struct bpf_map *map;
 +                      int i;
 +
 +                      for (i = 0; i < obj->nr_maps; i++) {
 +                              map = &prog->obj->maps[i];
 +                              if (map->libbpf_type != LIBBPF_MAP_RODATA)
 +                                      continue;
 +
 +                              if (bpf_prog_bind_map(ret, bpf_map__fd(map), NULL)) {
 +                                      cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
 +                                      pr_warn("prog '%s': failed to bind map '%s': %s\n",
 +                                              prog->name, map->real_name, cp);
 +                                      /* Don't fail hard if can't bind rodata. */
 +                              }
 +                      }
 +              }
 +
 +              *prog_fd = ret;
 +              ret = 0;
 +              goto out;
 +      }
 +
 +      if (log_level == 0) {
 +              log_level = 1;
 +              goto retry_load;
 +      }
 +      /* On ENOSPC, increase log buffer size and retry, unless custom
 +       * log_buf is specified.
 +       * Be careful to not overflow u32, though. Kernel's log buf size limit
 +       * isn't part of UAPI so it can always be bumped to full 4GB. So don't
 +       * multiply by 2 unless we are sure we'll fit within 32 bits.
 +       * Currently, we'll get -EINVAL when we reach (UINT_MAX >> 2).
 +       */
 +      if (own_log_buf && errno == ENOSPC && log_buf_size <= UINT_MAX / 2)
 +              goto retry_load;
 +
 +      ret = -errno;
 +      cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
 +      pr_warn("prog '%s': BPF program load failed: %s\n", prog->name, cp);
 +      pr_perm_msg(ret);
 +
 +      if (own_log_buf && log_buf && log_buf[0] != '\0') {
 +              pr_warn("prog '%s': -- BEGIN PROG LOAD LOG --\n%s-- END PROG LOAD LOG --\n",
 +                      prog->name, log_buf);
 +      }
 +      if (insns_cnt >= BPF_MAXINSNS) {
 +              pr_warn("prog '%s': program too large (%d insns), at most %d insns\n",
 +                      prog->name, insns_cnt, BPF_MAXINSNS);
 +      }
 +
 +out:
 +      if (own_log_buf)
 +              free(log_buf);
 +      return ret;
 +}
 +
 +static int bpf_program_record_relos(struct bpf_program *prog)
 +{
 +      struct bpf_object *obj = prog->obj;
 +      int i;
 +
 +      for (i = 0; i < prog->nr_reloc; i++) {
 +              struct reloc_desc *relo = &prog->reloc_desc[i];
 +              struct extern_desc *ext = &obj->externs[relo->sym_off];
 +
 +              switch (relo->type) {
 +              case RELO_EXTERN_VAR:
 +                      if (ext->type != EXT_KSYM)
 +                              continue;
 +                      bpf_gen__record_extern(obj->gen_loader, ext->name,
 +                                             ext->is_weak, !ext->ksym.type_id,
 +                                             BTF_KIND_VAR, relo->insn_idx);
 +                      break;
 +              case RELO_EXTERN_FUNC:
 +                      bpf_gen__record_extern(obj->gen_loader, ext->name,
 +                                             ext->is_weak, false, BTF_KIND_FUNC,
 +                                             relo->insn_idx);
 +                      break;
 +              case RELO_CORE: {
 +                      struct bpf_core_relo cr = {
 +                              .insn_off = relo->insn_idx * 8,
 +                              .type_id = relo->core_relo->type_id,
 +                              .access_str_off = relo->core_relo->access_str_off,
 +                              .kind = relo->core_relo->kind,
 +                      };
 +
 +                      bpf_gen__record_relo_core(obj->gen_loader, &cr);
 +                      break;
 +              }
 +              default:
 +                      continue;
 +              }
 +      }
 +      return 0;
 +}
 +
 +static int bpf_object_load_prog(struct bpf_object *obj, struct bpf_program *prog,
 +                              const char *license, __u32 kern_ver)
 +{
 +      int err = 0, fd, i;
 +
 +      if (obj->loaded) {
 +              pr_warn("prog '%s': can't load after object was loaded\n", prog->name);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (prog->instances.nr < 0 || !prog->instances.fds) {
 +              if (prog->preprocessor) {
 +                      pr_warn("Internal error: can't load program '%s'\n",
 +                              prog->name);
 +                      return libbpf_err(-LIBBPF_ERRNO__INTERNAL);
 +              }
 +
 +              prog->instances.fds = malloc(sizeof(int));
 +              if (!prog->instances.fds) {
 +                      pr_warn("Not enough memory for BPF fds\n");
 +                      return libbpf_err(-ENOMEM);
 +              }
 +              prog->instances.nr = 1;
 +              prog->instances.fds[0] = -1;
 +      }
 +
 +      if (!prog->preprocessor) {
 +              if (prog->instances.nr != 1) {
 +                      pr_warn("prog '%s': inconsistent nr(%d) != 1\n",
 +                              prog->name, prog->instances.nr);
 +              }
 +              if (obj->gen_loader)
 +                      bpf_program_record_relos(prog);
 +              err = bpf_object_load_prog_instance(obj, prog,
 +                                                  prog->insns, prog->insns_cnt,
 +                                                  license, kern_ver, &fd);
 +              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;
 +
 +              memset(&result, 0, sizeof(result));
 +              err = preprocessor(prog, i, prog->insns,
 +                                 prog->insns_cnt, &result);
 +              if (err) {
 +                      pr_warn("Preprocessing the %dth instance of program '%s' failed\n",
 +                              i, prog->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->name);
 +                      prog->instances.fds[i] = -1;
 +                      if (result.pfd)
 +                              *result.pfd = -1;
 +                      continue;
 +              }
 +
 +              err = bpf_object_load_prog_instance(obj, prog,
 +                                                  result.new_insn_ptr, result.new_insn_cnt,
 +                                                  license, kern_ver, &fd);
 +              if (err) {
 +                      pr_warn("Loading the %dth instance of program '%s' failed\n",
 +                              i, prog->name);
 +                      goto out;
 +              }
 +
 +              if (result.pfd)
 +                      *result.pfd = fd;
 +              prog->instances.fds[i] = fd;
 +      }
 +out:
 +      if (err)
 +              pr_warn("failed to load program '%s'\n", prog->name);
 +      return libbpf_err(err);
 +}
 +
 +int bpf_program__load(struct bpf_program *prog, const char *license, __u32 kern_ver)
 +{
 +      return bpf_object_load_prog(prog->obj, prog, license, kern_ver);
 +}
 +
 +static int
 +bpf_object__load_progs(struct bpf_object *obj, int log_level)
 +{
 +      struct bpf_program *prog;
 +      size_t i;
 +      int err;
 +
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              prog = &obj->programs[i];
 +              err = bpf_object__sanitize_prog(obj, prog);
 +              if (err)
 +                      return err;
 +      }
 +
 +      for (i = 0; i < obj->nr_programs; i++) {
 +              prog = &obj->programs[i];
 +              if (prog_is_subprog(obj, prog))
 +                      continue;
 +              if (!prog->load) {
 +                      pr_debug("prog '%s': skipped loading\n", prog->name);
 +                      continue;
 +              }
 +              prog->log_level |= log_level;
 +              err = bpf_object_load_prog(obj, prog, obj->license, obj->kern_version);
 +              if (err)
 +                      return err;
 +      }
 +      if (obj->gen_loader)
 +              bpf_object__free_relocs(obj);
 +      return 0;
 +}
 +
 +static const struct bpf_sec_def *find_sec_def(const char *sec_name);
 +
 +static int bpf_object_init_progs(struct bpf_object *obj, const struct bpf_object_open_opts *opts)
 +{
 +      struct bpf_program *prog;
 +      int err;
 +
 +      bpf_object__for_each_program(prog, obj) {
 +              prog->sec_def = find_sec_def(prog->sec_name);
 +              if (!prog->sec_def) {
 +                      /* couldn't guess, but user might manually specify */
 +                      pr_debug("prog '%s': unrecognized ELF section name '%s'\n",
 +                              prog->name, prog->sec_name);
 +                      continue;
 +              }
 +
 +              bpf_program__set_type(prog, prog->sec_def->prog_type);
 +              bpf_program__set_expected_attach_type(prog, prog->sec_def->expected_attach_type);
 +
 +#pragma GCC diagnostic push
 +#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
 +              if (prog->sec_def->prog_type == BPF_PROG_TYPE_TRACING ||
 +                  prog->sec_def->prog_type == BPF_PROG_TYPE_EXT)
 +                      prog->attach_prog_fd = OPTS_GET(opts, attach_prog_fd, 0);
 +#pragma GCC diagnostic pop
 +
 +              /* sec_def can have custom callback which should be called
 +               * after bpf_program is initialized to adjust its properties
 +               */
 +              if (prog->sec_def->init_fn) {
 +                      err = prog->sec_def->init_fn(prog, prog->sec_def->cookie);
 +                      if (err < 0) {
 +                              pr_warn("prog '%s': failed to initialize: %d\n",
 +                                      prog->name, err);
 +                              return err;
 +                      }
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static struct bpf_object *bpf_object_open(const char *path, const void *obj_buf, size_t obj_buf_sz,
 +                                        const struct bpf_object_open_opts *opts)
 +{
 +      const char *obj_name, *kconfig, *btf_tmp_path;
 +      struct bpf_object *obj;
 +      char tmp_name[64];
 +      int err;
 +      char *log_buf;
 +      size_t log_size;
 +      __u32 log_level;
 +
 +      if (elf_version(EV_CURRENT) == EV_NONE) {
 +              pr_warn("failed to init libelf for %s\n",
 +                      path ? : "(mem buf)");
 +              return ERR_PTR(-LIBBPF_ERRNO__LIBELF);
 +      }
 +
 +      if (!OPTS_VALID(opts, bpf_object_open_opts))
 +              return ERR_PTR(-EINVAL);
 +
 +      obj_name = OPTS_GET(opts, object_name, NULL);
 +      if (obj_buf) {
 +              if (!obj_name) {
 +                      snprintf(tmp_name, sizeof(tmp_name), "%lx-%lx",
 +                               (unsigned long)obj_buf,
 +                               (unsigned long)obj_buf_sz);
 +                      obj_name = tmp_name;
 +              }
 +              path = obj_name;
 +              pr_debug("loading object '%s' from buffer\n", obj_name);
 +      }
 +
 +      log_buf = OPTS_GET(opts, kernel_log_buf, NULL);
 +      log_size = OPTS_GET(opts, kernel_log_size, 0);
 +      log_level = OPTS_GET(opts, kernel_log_level, 0);
 +      if (log_size > UINT_MAX)
 +              return ERR_PTR(-EINVAL);
 +      if (log_size && !log_buf)
 +              return ERR_PTR(-EINVAL);
 +
 +      obj = bpf_object__new(path, obj_buf, obj_buf_sz, obj_name);
 +      if (IS_ERR(obj))
 +              return obj;
 +
 +      obj->log_buf = log_buf;
 +      obj->log_size = log_size;
 +      obj->log_level = log_level;
 +
 +      btf_tmp_path = OPTS_GET(opts, btf_custom_path, NULL);
 +      if (btf_tmp_path) {
 +              if (strlen(btf_tmp_path) >= PATH_MAX) {
 +                      err = -ENAMETOOLONG;
 +                      goto out;
 +              }
 +              obj->btf_custom_path = strdup(btf_tmp_path);
 +              if (!obj->btf_custom_path) {
 +                      err = -ENOMEM;
 +                      goto out;
 +              }
 +      }
 +
 +      kconfig = OPTS_GET(opts, kconfig, NULL);
 +      if (kconfig) {
 +              obj->kconfig = strdup(kconfig);
 +              if (!obj->kconfig) {
 +                      err = -ENOMEM;
 +                      goto out;
 +              }
 +      }
 +
 +      err = bpf_object__elf_init(obj);
 +      err = err ? : bpf_object__check_endianness(obj);
 +      err = err ? : bpf_object__elf_collect(obj);
 +      err = err ? : bpf_object__collect_externs(obj);
 +      err = err ? : bpf_object__finalize_btf(obj);
 +      err = err ? : bpf_object__init_maps(obj, opts);
 +      err = err ? : bpf_object_init_progs(obj, opts);
 +      err = err ? : bpf_object__collect_relos(obj);
 +      if (err)
 +              goto out;
 +
 +      bpf_object__elf_finish(obj);
 +
 +      return obj;
 +out:
 +      bpf_object__close(obj);
 +      return ERR_PTR(err);
 +}
 +
 +static struct bpf_object *
 +__bpf_object__open_xattr(struct bpf_object_open_attr *attr, int flags)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
 +              .relaxed_maps = flags & MAPS_RELAX_COMPAT,
 +      );
 +
 +      /* param validation */
 +      if (!attr->file)
 +              return NULL;
 +
 +      pr_debug("loading %s\n", attr->file);
 +      return bpf_object_open(attr->file, NULL, 0, &opts);
 +}
 +
 +struct bpf_object *bpf_object__open_xattr(struct bpf_object_open_attr *attr)
 +{
 +      return libbpf_ptr(__bpf_object__open_xattr(attr, 0));
 +}
 +
 +struct bpf_object *bpf_object__open(const char *path)
 +{
 +      struct bpf_object_open_attr attr = {
 +              .file           = path,
 +              .prog_type      = BPF_PROG_TYPE_UNSPEC,
 +      };
 +
 +      return libbpf_ptr(__bpf_object__open_xattr(&attr, 0));
 +}
 +
 +struct bpf_object *
 +bpf_object__open_file(const char *path, const struct bpf_object_open_opts *opts)
 +{
 +      if (!path)
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      pr_debug("loading %s\n", path);
 +
 +      return libbpf_ptr(bpf_object_open(path, NULL, 0, opts));
 +}
 +
 +struct bpf_object *
 +bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
 +                   const struct bpf_object_open_opts *opts)
 +{
 +      if (!obj_buf || obj_buf_sz == 0)
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      return libbpf_ptr(bpf_object_open(NULL, obj_buf, obj_buf_sz, opts));
 +}
 +
 +struct bpf_object *
 +bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
 +                      const char *name)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_object_open_opts, opts,
 +              .object_name = name,
 +              /* wrong default, but backwards-compatible */
 +              .relaxed_maps = true,
 +      );
 +
 +      /* returning NULL is wrong, but backwards-compatible */
 +      if (!obj_buf || obj_buf_sz == 0)
 +              return errno = EINVAL, NULL;
 +
 +      return libbpf_ptr(bpf_object_open(NULL, obj_buf, obj_buf_sz, &opts));
 +}
 +
 +static int bpf_object_unload(struct bpf_object *obj)
 +{
 +      size_t i;
 +
 +      if (!obj)
 +              return libbpf_err(-EINVAL);
 +
 +      for (i = 0; i < obj->nr_maps; i++) {
 +              zclose(obj->maps[i].fd);
 +              if (obj->maps[i].st_ops)
 +                      zfree(&obj->maps[i].st_ops->kern_vdata);
 +      }
 +
 +      for (i = 0; i < obj->nr_programs; i++)
 +              bpf_program__unload(&obj->programs[i]);
 +
 +      return 0;
 +}
 +
 +int bpf_object__unload(struct bpf_object *obj) __attribute__((alias("bpf_object_unload")));
 +
 +static int bpf_object__sanitize_maps(struct bpf_object *obj)
 +{
 +      struct bpf_map *m;
 +
 +      bpf_object__for_each_map(m, obj) {
 +              if (!bpf_map__is_internal(m))
 +                      continue;
 +              if (!kernel_supports(obj, FEAT_ARRAY_MMAP))
 +                      m->def.map_flags ^= BPF_F_MMAPABLE;
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object__read_kallsyms_file(struct bpf_object *obj)
 +{
 +      char sym_type, sym_name[500];
 +      unsigned long long sym_addr;
 +      const struct btf_type *t;
 +      struct extern_desc *ext;
 +      int ret, err = 0;
 +      FILE *f;
 +
 +      f = fopen("/proc/kallsyms", "r");
 +      if (!f) {
 +              err = -errno;
 +              pr_warn("failed to open /proc/kallsyms: %d\n", err);
 +              return err;
 +      }
 +
 +      while (true) {
 +              ret = fscanf(f, "%llx %c %499s%*[^\n]\n",
 +                           &sym_addr, &sym_type, sym_name);
 +              if (ret == EOF && feof(f))
 +                      break;
 +              if (ret != 3) {
 +                      pr_warn("failed to read kallsyms entry: %d\n", ret);
 +                      err = -EINVAL;
 +                      goto out;
 +              }
 +
 +              ext = find_extern_by_name(obj, sym_name);
 +              if (!ext || ext->type != EXT_KSYM)
 +                      continue;
 +
 +              t = btf__type_by_id(obj->btf, ext->btf_id);
 +              if (!btf_is_var(t))
 +                      continue;
 +
 +              if (ext->is_set && ext->ksym.addr != sym_addr) {
 +                      pr_warn("extern (ksym) '%s' resolution is ambiguous: 0x%llx or 0x%llx\n",
 +                              sym_name, ext->ksym.addr, sym_addr);
 +                      err = -EINVAL;
 +                      goto out;
 +              }
 +              if (!ext->is_set) {
 +                      ext->is_set = true;
 +                      ext->ksym.addr = sym_addr;
 +                      pr_debug("extern (ksym) %s=0x%llx\n", sym_name, sym_addr);
 +              }
 +      }
 +
 +out:
 +      fclose(f);
 +      return err;
 +}
 +
 +static int find_ksym_btf_id(struct bpf_object *obj, const char *ksym_name,
 +                          __u16 kind, struct btf **res_btf,
 +                          struct module_btf **res_mod_btf)
 +{
 +      struct module_btf *mod_btf;
 +      struct btf *btf;
 +      int i, id, err;
 +
 +      btf = obj->btf_vmlinux;
 +      mod_btf = NULL;
 +      id = btf__find_by_name_kind(btf, ksym_name, kind);
 +
 +      if (id == -ENOENT) {
 +              err = load_module_btfs(obj);
 +              if (err)
 +                      return err;
 +
 +              for (i = 0; i < obj->btf_module_cnt; i++) {
 +                      /* we assume module_btf's BTF FD is always >0 */
 +                      mod_btf = &obj->btf_modules[i];
 +                      btf = mod_btf->btf;
 +                      id = btf__find_by_name_kind_own(btf, ksym_name, kind);
 +                      if (id != -ENOENT)
 +                              break;
 +              }
 +      }
 +      if (id <= 0)
 +              return -ESRCH;
 +
 +      *res_btf = btf;
 +      *res_mod_btf = mod_btf;
 +      return id;
 +}
 +
 +static int bpf_object__resolve_ksym_var_btf_id(struct bpf_object *obj,
 +                                             struct extern_desc *ext)
 +{
 +      const struct btf_type *targ_var, *targ_type;
 +      __u32 targ_type_id, local_type_id;
 +      struct module_btf *mod_btf = NULL;
 +      const char *targ_var_name;
 +      struct btf *btf = NULL;
 +      int id, err;
 +
 +      id = find_ksym_btf_id(obj, ext->name, BTF_KIND_VAR, &btf, &mod_btf);
 +      if (id < 0) {
 +              if (id == -ESRCH && ext->is_weak)
 +                      return 0;
 +              pr_warn("extern (var ksym) '%s': not found in kernel BTF\n",
 +                      ext->name);
 +              return id;
 +      }
 +
 +      /* find local type_id */
 +      local_type_id = ext->ksym.type_id;
 +
 +      /* find target type_id */
 +      targ_var = btf__type_by_id(btf, id);
 +      targ_var_name = btf__name_by_offset(btf, targ_var->name_off);
 +      targ_type = skip_mods_and_typedefs(btf, targ_var->type, &targ_type_id);
 +
 +      err = bpf_core_types_are_compat(obj->btf, local_type_id,
 +                                      btf, targ_type_id);
 +      if (err <= 0) {
 +              const struct btf_type *local_type;
 +              const char *targ_name, *local_name;
 +
 +              local_type = btf__type_by_id(obj->btf, local_type_id);
 +              local_name = btf__name_by_offset(obj->btf, local_type->name_off);
 +              targ_name = btf__name_by_offset(btf, targ_type->name_off);
 +
 +              pr_warn("extern (var ksym) '%s': incompatible types, expected [%d] %s %s, but kernel has [%d] %s %s\n",
 +                      ext->name, local_type_id,
 +                      btf_kind_str(local_type), local_name, targ_type_id,
 +                      btf_kind_str(targ_type), targ_name);
 +              return -EINVAL;
 +      }
 +
 +      ext->is_set = true;
 +      ext->ksym.kernel_btf_obj_fd = mod_btf ? mod_btf->fd : 0;
 +      ext->ksym.kernel_btf_id = id;
 +      pr_debug("extern (var ksym) '%s': resolved to [%d] %s %s\n",
 +               ext->name, id, btf_kind_str(targ_var), targ_var_name);
 +
 +      return 0;
 +}
 +
 +static int bpf_object__resolve_ksym_func_btf_id(struct bpf_object *obj,
 +                                              struct extern_desc *ext)
 +{
 +      int local_func_proto_id, kfunc_proto_id, kfunc_id;
 +      struct module_btf *mod_btf = NULL;
 +      const struct btf_type *kern_func;
 +      struct btf *kern_btf = NULL;
 +      int ret;
 +
 +      local_func_proto_id = ext->ksym.type_id;
 +
 +      kfunc_id = find_ksym_btf_id(obj, ext->name, BTF_KIND_FUNC, &kern_btf, &mod_btf);
 +      if (kfunc_id < 0) {
 +              if (kfunc_id == -ESRCH && ext->is_weak)
 +                      return 0;
 +              pr_warn("extern (func ksym) '%s': not found in kernel or module BTFs\n",
 +                      ext->name);
 +              return kfunc_id;
 +      }
 +
 +      kern_func = btf__type_by_id(kern_btf, kfunc_id);
 +      kfunc_proto_id = kern_func->type;
 +
 +      ret = bpf_core_types_are_compat(obj->btf, local_func_proto_id,
 +                                      kern_btf, kfunc_proto_id);
 +      if (ret <= 0) {
 +              pr_warn("extern (func ksym) '%s': func_proto [%d] incompatible with kernel [%d]\n",
 +                      ext->name, local_func_proto_id, kfunc_proto_id);
 +              return -EINVAL;
 +      }
 +
 +      /* set index for module BTF fd in fd_array, if unset */
 +      if (mod_btf && !mod_btf->fd_array_idx) {
 +              /* insn->off is s16 */
 +              if (obj->fd_array_cnt == INT16_MAX) {
 +                      pr_warn("extern (func ksym) '%s': module BTF fd index %d too big to fit in bpf_insn offset\n",
 +                              ext->name, mod_btf->fd_array_idx);
 +                      return -E2BIG;
 +              }
 +              /* Cannot use index 0 for module BTF fd */
 +              if (!obj->fd_array_cnt)
 +                      obj->fd_array_cnt = 1;
 +
 +              ret = libbpf_ensure_mem((void **)&obj->fd_array, &obj->fd_array_cap, sizeof(int),
 +                                      obj->fd_array_cnt + 1);
 +              if (ret)
 +                      return ret;
 +              mod_btf->fd_array_idx = obj->fd_array_cnt;
 +              /* we assume module BTF FD is always >0 */
 +              obj->fd_array[obj->fd_array_cnt++] = mod_btf->fd;
 +      }
 +
 +      ext->is_set = true;
 +      ext->ksym.kernel_btf_id = kfunc_id;
 +      ext->ksym.btf_fd_idx = mod_btf ? mod_btf->fd_array_idx : 0;
 +      pr_debug("extern (func ksym) '%s': resolved to kernel [%d]\n",
 +               ext->name, kfunc_id);
 +
 +      return 0;
 +}
 +
 +static int bpf_object__resolve_ksyms_btf_id(struct bpf_object *obj)
 +{
 +      const struct btf_type *t;
 +      struct extern_desc *ext;
 +      int i, err;
 +
 +      for (i = 0; i < obj->nr_extern; i++) {
 +              ext = &obj->externs[i];
 +              if (ext->type != EXT_KSYM || !ext->ksym.type_id)
 +                      continue;
 +
 +              if (obj->gen_loader) {
 +                      ext->is_set = true;
 +                      ext->ksym.kernel_btf_obj_fd = 0;
 +                      ext->ksym.kernel_btf_id = 0;
 +                      continue;
 +              }
 +              t = btf__type_by_id(obj->btf, ext->btf_id);
 +              if (btf_is_var(t))
 +                      err = bpf_object__resolve_ksym_var_btf_id(obj, ext);
 +              else
 +                      err = bpf_object__resolve_ksym_func_btf_id(obj, ext);
 +              if (err)
 +                      return err;
 +      }
 +      return 0;
 +}
 +
 +static int bpf_object__resolve_externs(struct bpf_object *obj,
 +                                     const char *extra_kconfig)
 +{
 +      bool need_config = false, need_kallsyms = false;
 +      bool need_vmlinux_btf = false;
 +      struct extern_desc *ext;
 +      void *kcfg_data = NULL;
 +      int err, i;
 +
 +      if (obj->nr_extern == 0)
 +              return 0;
 +
 +      if (obj->kconfig_map_idx >= 0)
 +              kcfg_data = obj->maps[obj->kconfig_map_idx].mmaped;
 +
 +      for (i = 0; i < obj->nr_extern; i++) {
 +              ext = &obj->externs[i];
 +
 +              if (ext->type == EXT_KCFG &&
 +                  strcmp(ext->name, "LINUX_KERNEL_VERSION") == 0) {
 +                      void *ext_val = kcfg_data + ext->kcfg.data_off;
 +                      __u32 kver = get_kernel_version();
 +
 +                      if (!kver) {
 +                              pr_warn("failed to get kernel version\n");
 +                              return -EINVAL;
 +                      }
 +                      err = set_kcfg_value_num(ext, ext_val, kver);
 +                      if (err)
 +                              return err;
 +                      pr_debug("extern (kcfg) %s=0x%x\n", ext->name, kver);
 +              } else if (ext->type == EXT_KCFG && str_has_pfx(ext->name, "CONFIG_")) {
 +                      need_config = true;
 +              } else if (ext->type == EXT_KSYM) {
 +                      if (ext->ksym.type_id)
 +                              need_vmlinux_btf = true;
 +                      else
 +                              need_kallsyms = true;
 +              } else {
 +                      pr_warn("unrecognized extern '%s'\n", ext->name);
 +                      return -EINVAL;
 +              }
 +      }
 +      if (need_config && extra_kconfig) {
 +              err = bpf_object__read_kconfig_mem(obj, extra_kconfig, kcfg_data);
 +              if (err)
 +                      return -EINVAL;
 +              need_config = false;
 +              for (i = 0; i < obj->nr_extern; i++) {
 +                      ext = &obj->externs[i];
 +                      if (ext->type == EXT_KCFG && !ext->is_set) {
 +                              need_config = true;
 +                              break;
 +                      }
 +              }
 +      }
 +      if (need_config) {
 +              err = bpf_object__read_kconfig_file(obj, kcfg_data);
 +              if (err)
 +                      return -EINVAL;
 +      }
 +      if (need_kallsyms) {
 +              err = bpf_object__read_kallsyms_file(obj);
 +              if (err)
 +                      return -EINVAL;
 +      }
 +      if (need_vmlinux_btf) {
 +              err = bpf_object__resolve_ksyms_btf_id(obj);
 +              if (err)
 +                      return -EINVAL;
 +      }
 +      for (i = 0; i < obj->nr_extern; i++) {
 +              ext = &obj->externs[i];
 +
 +              if (!ext->is_set && !ext->is_weak) {
 +                      pr_warn("extern %s (strong) not resolved\n", ext->name);
 +                      return -ESRCH;
 +              } else if (!ext->is_set) {
 +                      pr_debug("extern %s (weak) not resolved, defaulting to zero\n",
 +                               ext->name);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +static int bpf_object_load(struct bpf_object *obj, int extra_log_level, const char *target_btf_path)
 +{
 +      int err, i;
 +
 +      if (!obj)
 +              return libbpf_err(-EINVAL);
 +
 +      if (obj->loaded) {
 +              pr_warn("object '%s': load can't be attempted twice\n", obj->name);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (obj->gen_loader)
 +              bpf_gen__init(obj->gen_loader, extra_log_level, obj->nr_programs, obj->nr_maps);
 +
 +      err = bpf_object__probe_loading(obj);
 +      err = err ? : bpf_object__load_vmlinux_btf(obj, false);
 +      err = err ? : bpf_object__resolve_externs(obj, obj->kconfig);
 +      err = err ? : bpf_object__sanitize_and_load_btf(obj);
 +      err = err ? : bpf_object__sanitize_maps(obj);
 +      err = err ? : bpf_object__init_kern_struct_ops_maps(obj);
 +      err = err ? : bpf_object__create_maps(obj);
 +      err = err ? : bpf_object__relocate(obj, obj->btf_custom_path ? : target_btf_path);
 +      err = err ? : bpf_object__load_progs(obj, extra_log_level);
 +      err = err ? : bpf_object_init_prog_arrays(obj);
 +
 +      if (obj->gen_loader) {
 +              /* reset FDs */
 +              if (obj->btf)
 +                      btf__set_fd(obj->btf, -1);
 +              for (i = 0; i < obj->nr_maps; i++)
 +                      obj->maps[i].fd = -1;
 +              if (!err)
 +                      err = bpf_gen__finish(obj->gen_loader, obj->nr_programs, obj->nr_maps);
 +      }
 +
 +      /* clean up fd_array */
 +      zfree(&obj->fd_array);
 +
 +      /* clean up module BTFs */
 +      for (i = 0; i < obj->btf_module_cnt; i++) {
 +              close(obj->btf_modules[i].fd);
 +              btf__free(obj->btf_modules[i].btf);
 +              free(obj->btf_modules[i].name);
 +      }
 +      free(obj->btf_modules);
 +
 +      /* clean up vmlinux BTF */
 +      btf__free(obj->btf_vmlinux);
 +      obj->btf_vmlinux = NULL;
 +
 +      obj->loaded = true; /* doesn't matter if successfully or not */
 +
 +      if (err)
 +              goto out;
 +
 +      return 0;
 +out:
 +      /* unpin any maps that were auto-pinned during load */
 +      for (i = 0; i < obj->nr_maps; i++)
 +              if (obj->maps[i].pinned && !obj->maps[i].reused)
 +                      bpf_map__unpin(&obj->maps[i], NULL);
 +
 +      bpf_object_unload(obj);
 +      pr_warn("failed to load object '%s'\n", obj->path);
 +      return libbpf_err(err);
 +}
 +
 +int bpf_object__load_xattr(struct bpf_object_load_attr *attr)
 +{
 +      return bpf_object_load(attr->obj, attr->log_level, attr->target_btf_path);
 +}
 +
 +int bpf_object__load(struct bpf_object *obj)
 +{
 +      return bpf_object_load(obj, 0, NULL);
 +}
 +
 +static int make_parent_dir(const char *path)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      char *dname, *dir;
 +      int err = 0;
 +
 +      dname = strdup(path);
 +      if (dname == NULL)
 +              return -ENOMEM;
 +
 +      dir = dirname(dname);
 +      if (mkdir(dir, 0700) && errno != EEXIST)
 +              err = -errno;
 +
 +      free(dname);
 +      if (err) {
 +              cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg));
 +              pr_warn("failed to mkdir %s: %s\n", path, cp);
 +      }
 +      return err;
 +}
 +
 +static int check_path(const char *path)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      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)) {
 +              cp = libbpf_strerror_r(errno, errmsg, sizeof(errmsg));
 +              pr_warn("failed to statfs %s: %s\n", dir, cp);
 +              err = -errno;
 +      }
 +      free(dname);
 +
 +      if (!err && st_fs.f_type != BPF_FS_MAGIC) {
 +              pr_warn("specified path %s is not on BPF FS\n", path);
 +              err = -EINVAL;
 +      }
 +
 +      return err;
 +}
 +
 +static int bpf_program_pin_instance(struct bpf_program *prog, const char *path, int instance)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      int err;
 +
 +      err = make_parent_dir(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      err = check_path(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      if (prog == NULL) {
 +              pr_warn("invalid program pointer\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (instance < 0 || instance >= prog->instances.nr) {
 +              pr_warn("invalid prog instance %d of prog %s (max %d)\n",
 +                      instance, prog->name, prog->instances.nr);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (bpf_obj_pin(prog->instances.fds[instance], path)) {
 +              err = -errno;
 +              cp = libbpf_strerror_r(err, errmsg, sizeof(errmsg));
 +              pr_warn("failed to pin program: %s\n", cp);
 +              return libbpf_err(err);
 +      }
 +      pr_debug("pinned program '%s'\n", path);
 +
 +      return 0;
 +}
 +
 +static int bpf_program_unpin_instance(struct bpf_program *prog, const char *path, int instance)
 +{
 +      int err;
 +
 +      err = check_path(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      if (prog == NULL) {
 +              pr_warn("invalid program pointer\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (instance < 0 || instance >= prog->instances.nr) {
 +              pr_warn("invalid prog instance %d of prog %s (max %d)\n",
 +                      instance, prog->name, prog->instances.nr);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      err = unlink(path);
 +      if (err != 0)
 +              return libbpf_err(-errno);
 +
 +      pr_debug("unpinned program '%s'\n", path);
 +
 +      return 0;
 +}
 +
 +__attribute__((alias("bpf_program_pin_instance")))
 +int bpf_object__pin_instance(struct bpf_program *prog, const char *path, int instance);
 +
 +__attribute__((alias("bpf_program_unpin_instance")))
 +int bpf_program__unpin_instance(struct bpf_program *prog, const char *path, int instance);
 +
 +int bpf_program__pin(struct bpf_program *prog, const char *path)
 +{
 +      int i, err;
 +
 +      err = make_parent_dir(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      err = check_path(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      if (prog == NULL) {
 +              pr_warn("invalid program pointer\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (prog->instances.nr <= 0) {
 +              pr_warn("no instances of prog %s to pin\n", prog->name);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (prog->instances.nr == 1) {
 +              /* don't create subdirs when pinning single instance */
 +              return bpf_program_pin_instance(prog, path, 0);
 +      }
 +
 +      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) {
 +                      err = -EINVAL;
 +                      goto err_unpin;
 +              } else if (len >= PATH_MAX) {
 +                      err = -ENAMETOOLONG;
 +                      goto err_unpin;
 +              }
 +
 +              err = bpf_program_pin_instance(prog, buf, i);
 +              if (err)
 +                      goto err_unpin;
 +      }
 +
 +      return 0;
 +
 +err_unpin:
 +      for (i = i - 1; i >= 0; i--) {
 +              char buf[PATH_MAX];
 +              int len;
 +
 +              len = snprintf(buf, PATH_MAX, "%s/%d", path, i);
 +              if (len < 0)
 +                      continue;
 +              else if (len >= PATH_MAX)
 +                      continue;
 +
 +              bpf_program_unpin_instance(prog, buf, i);
 +      }
 +
 +      rmdir(path);
 +
 +      return libbpf_err(err);
 +}
 +
 +int bpf_program__unpin(struct bpf_program *prog, const char *path)
 +{
 +      int i, err;
 +
 +      err = check_path(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      if (prog == NULL) {
 +              pr_warn("invalid program pointer\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (prog->instances.nr <= 0) {
 +              pr_warn("no instances of prog %s to pin\n", prog->name);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (prog->instances.nr == 1) {
 +              /* don't create subdirs when pinning single instance */
 +              return bpf_program_unpin_instance(prog, path, 0);
 +      }
 +
 +      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 libbpf_err(-EINVAL);
 +              else if (len >= PATH_MAX)
 +                      return libbpf_err(-ENAMETOOLONG);
 +
 +              err = bpf_program_unpin_instance(prog, buf, i);
 +              if (err)
 +                      return err;
 +      }
 +
 +      err = rmdir(path);
 +      if (err)
 +              return libbpf_err(-errno);
 +
 +      return 0;
 +}
 +
 +int bpf_map__pin(struct bpf_map *map, const char *path)
 +{
 +      char *cp, errmsg[STRERR_BUFSIZE];
 +      int err;
 +
 +      if (map == NULL) {
 +              pr_warn("invalid map pointer\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (map->pin_path) {
 +              if (path && strcmp(path, map->pin_path)) {
 +                      pr_warn("map '%s' already has pin path '%s' different from '%s'\n",
 +                              bpf_map__name(map), map->pin_path, path);
 +                      return libbpf_err(-EINVAL);
 +              } else if (map->pinned) {
 +                      pr_debug("map '%s' already pinned at '%s'; not re-pinning\n",
 +                               bpf_map__name(map), map->pin_path);
 +                      return 0;
 +              }
 +      } else {
 +              if (!path) {
 +                      pr_warn("missing a path to pin map '%s' at\n",
 +                              bpf_map__name(map));
 +                      return libbpf_err(-EINVAL);
 +              } else if (map->pinned) {
 +                      pr_warn("map '%s' already pinned\n", bpf_map__name(map));
 +                      return libbpf_err(-EEXIST);
 +              }
 +
 +              map->pin_path = strdup(path);
 +              if (!map->pin_path) {
 +                      err = -errno;
 +                      goto out_err;
 +              }
 +      }
 +
 +      err = make_parent_dir(map->pin_path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      err = check_path(map->pin_path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      if (bpf_obj_pin(map->fd, map->pin_path)) {
 +              err = -errno;
 +              goto out_err;
 +      }
 +
 +      map->pinned = true;
 +      pr_debug("pinned map '%s'\n", map->pin_path);
 +
 +      return 0;
 +
 +out_err:
 +      cp = libbpf_strerror_r(-err, errmsg, sizeof(errmsg));
 +      pr_warn("failed to pin map: %s\n", cp);
 +      return libbpf_err(err);
 +}
 +
 +int bpf_map__unpin(struct bpf_map *map, const char *path)
 +{
 +      int err;
 +
 +      if (map == NULL) {
 +              pr_warn("invalid map pointer\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (map->pin_path) {
 +              if (path && strcmp(path, map->pin_path)) {
 +                      pr_warn("map '%s' already has pin path '%s' different from '%s'\n",
 +                              bpf_map__name(map), map->pin_path, path);
 +                      return libbpf_err(-EINVAL);
 +              }
 +              path = map->pin_path;
 +      } else if (!path) {
 +              pr_warn("no path to unpin map '%s' from\n",
 +                      bpf_map__name(map));
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      err = check_path(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      err = unlink(path);
 +      if (err != 0)
 +              return libbpf_err(-errno);
 +
 +      map->pinned = false;
 +      pr_debug("unpinned map '%s' from '%s'\n", bpf_map__name(map), path);
 +
 +      return 0;
 +}
 +
 +int bpf_map__set_pin_path(struct bpf_map *map, const char *path)
 +{
 +      char *new = NULL;
 +
 +      if (path) {
 +              new = strdup(path);
 +              if (!new)
 +                      return libbpf_err(-errno);
 +      }
 +
 +      free(map->pin_path);
 +      map->pin_path = new;
 +      return 0;
 +}
 +
 +const char *bpf_map__get_pin_path(const struct bpf_map *map)
 +{
 +      return map->pin_path;
 +}
 +
 +const char *bpf_map__pin_path(const struct bpf_map *map)
 +{
 +      return map->pin_path;
 +}
 +
 +bool bpf_map__is_pinned(const struct bpf_map *map)
 +{
 +      return map->pinned;
 +}
 +
 +static void sanitize_pin_path(char *s)
 +{
 +      /* bpffs disallows periods in path names */
 +      while (*s) {
 +              if (*s == '.')
 +                      *s = '_';
 +              s++;
 +      }
 +}
 +
 +int bpf_object__pin_maps(struct bpf_object *obj, const char *path)
 +{
 +      struct bpf_map *map;
 +      int err;
 +
 +      if (!obj)
 +              return libbpf_err(-ENOENT);
 +
 +      if (!obj->loaded) {
 +              pr_warn("object not yet loaded; load it first\n");
 +              return libbpf_err(-ENOENT);
 +      }
 +
 +      bpf_object__for_each_map(map, obj) {
 +              char *pin_path = NULL;
 +              char buf[PATH_MAX];
 +
 +              if (map->skipped)
 +                      continue;
 +
 +              if (path) {
 +                      int len;
 +
 +                      len = snprintf(buf, PATH_MAX, "%s/%s", path,
 +                                     bpf_map__name(map));
 +                      if (len < 0) {
 +                              err = -EINVAL;
 +                              goto err_unpin_maps;
 +                      } else if (len >= PATH_MAX) {
 +                              err = -ENAMETOOLONG;
 +                              goto err_unpin_maps;
 +                      }
 +                      sanitize_pin_path(buf);
 +                      pin_path = buf;
 +              } else if (!map->pin_path) {
 +                      continue;
 +              }
 +
 +              err = bpf_map__pin(map, pin_path);
 +              if (err)
 +                      goto err_unpin_maps;
 +      }
 +
 +      return 0;
 +
 +err_unpin_maps:
 +      while ((map = bpf_object__prev_map(obj, map))) {
 +              if (!map->pin_path)
 +                      continue;
 +
 +              bpf_map__unpin(map, NULL);
 +      }
 +
 +      return libbpf_err(err);
 +}
 +
 +int bpf_object__unpin_maps(struct bpf_object *obj, const char *path)
 +{
 +      struct bpf_map *map;
 +      int err;
 +
 +      if (!obj)
 +              return libbpf_err(-ENOENT);
 +
 +      bpf_object__for_each_map(map, obj) {
 +              char *pin_path = NULL;
 +              char buf[PATH_MAX];
 +
 +              if (path) {
 +                      int len;
 +
 +                      len = snprintf(buf, PATH_MAX, "%s/%s", path,
 +                                     bpf_map__name(map));
 +                      if (len < 0)
 +                              return libbpf_err(-EINVAL);
 +                      else if (len >= PATH_MAX)
 +                              return libbpf_err(-ENAMETOOLONG);
 +                      sanitize_pin_path(buf);
 +                      pin_path = buf;
 +              } else if (!map->pin_path) {
 +                      continue;
 +              }
 +
 +              err = bpf_map__unpin(map, pin_path);
 +              if (err)
 +                      return libbpf_err(err);
 +      }
 +
 +      return 0;
 +}
 +
 +int bpf_object__pin_programs(struct bpf_object *obj, const char *path)
 +{
 +      struct bpf_program *prog;
 +      int err;
 +
 +      if (!obj)
 +              return libbpf_err(-ENOENT);
 +
 +      if (!obj->loaded) {
 +              pr_warn("object not yet loaded; load it first\n");
 +              return libbpf_err(-ENOENT);
 +      }
 +
 +      bpf_object__for_each_program(prog, obj) {
 +              char buf[PATH_MAX];
 +              int len;
 +
 +              len = snprintf(buf, PATH_MAX, "%s/%s", path,
 +                             prog->pin_name);
 +              if (len < 0) {
 +                      err = -EINVAL;
 +                      goto err_unpin_programs;
 +              } else if (len >= PATH_MAX) {
 +                      err = -ENAMETOOLONG;
 +                      goto err_unpin_programs;
 +              }
 +
 +              err = bpf_program__pin(prog, buf);
 +              if (err)
 +                      goto err_unpin_programs;
 +      }
 +
 +      return 0;
 +
 +err_unpin_programs:
 +      while ((prog = bpf_object__prev_program(obj, prog))) {
 +              char buf[PATH_MAX];
 +              int len;
 +
 +              len = snprintf(buf, PATH_MAX, "%s/%s", path,
 +                             prog->pin_name);
 +              if (len < 0)
 +                      continue;
 +              else if (len >= PATH_MAX)
 +                      continue;
 +
 +              bpf_program__unpin(prog, buf);
 +      }
 +
 +      return libbpf_err(err);
 +}
 +
 +int bpf_object__unpin_programs(struct bpf_object *obj, const char *path)
 +{
 +      struct bpf_program *prog;
 +      int err;
 +
 +      if (!obj)
 +              return libbpf_err(-ENOENT);
 +
 +      bpf_object__for_each_program(prog, obj) {
 +              char buf[PATH_MAX];
 +              int len;
 +
 +              len = snprintf(buf, PATH_MAX, "%s/%s", path,
 +                             prog->pin_name);
 +              if (len < 0)
 +                      return libbpf_err(-EINVAL);
 +              else if (len >= PATH_MAX)
 +                      return libbpf_err(-ENAMETOOLONG);
 +
 +              err = bpf_program__unpin(prog, buf);
 +              if (err)
 +                      return libbpf_err(err);
 +      }
 +
 +      return 0;
 +}
 +
 +int bpf_object__pin(struct bpf_object *obj, const char *path)
 +{
 +      int err;
 +
 +      err = bpf_object__pin_maps(obj, path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      err = bpf_object__pin_programs(obj, path);
 +      if (err) {
 +              bpf_object__unpin_maps(obj, path);
 +              return libbpf_err(err);
 +      }
 +
 +      return 0;
 +}
 +
 +static void bpf_map__destroy(struct bpf_map *map)
 +{
 +      if (map->clear_priv)
 +              map->clear_priv(map, map->priv);
 +      map->priv = NULL;
 +      map->clear_priv = NULL;
 +
 +      if (map->inner_map) {
 +              bpf_map__destroy(map->inner_map);
 +              zfree(&map->inner_map);
 +      }
 +
 +      zfree(&map->init_slots);
 +      map->init_slots_sz = 0;
 +
 +      if (map->mmaped) {
 +              munmap(map->mmaped, bpf_map_mmap_sz(map));
 +              map->mmaped = NULL;
 +      }
 +
 +      if (map->st_ops) {
 +              zfree(&map->st_ops->data);
 +              zfree(&map->st_ops->progs);
 +              zfree(&map->st_ops->kern_func_off);
 +              zfree(&map->st_ops);
 +      }
 +
 +      zfree(&map->name);
 +      zfree(&map->real_name);
 +      zfree(&map->pin_path);
 +
 +      if (map->fd >= 0)
 +              zclose(map->fd);
 +}
 +
 +void bpf_object__close(struct bpf_object *obj)
 +{
 +      size_t i;
 +
 +      if (IS_ERR_OR_NULL(obj))
 +              return;
 +
 +      if (obj->clear_priv)
 +              obj->clear_priv(obj, obj->priv);
 +
 +      bpf_gen__free(obj->gen_loader);
 +      bpf_object__elf_finish(obj);
 +      bpf_object_unload(obj);
 +      btf__free(obj->btf);
 +      btf_ext__free(obj->btf_ext);
 +
 +      for (i = 0; i < obj->nr_maps; i++)
 +              bpf_map__destroy(&obj->maps[i]);
 +
 +      zfree(&obj->btf_custom_path);
 +      zfree(&obj->kconfig);
 +      zfree(&obj->externs);
 +      obj->nr_extern = 0;
 +
 +      zfree(&obj->maps);
 +      obj->nr_maps = 0;
 +
 +      if (obj->programs && obj->nr_programs) {
 +              for (i = 0; i < obj->nr_programs; i++)
 +                      bpf_program__exit(&obj->programs[i]);
 +      }
 +      zfree(&obj->programs);
 +
 +      list_del(&obj->list);
 +      free(obj);
 +}
 +
 +struct bpf_object *
 +bpf_object__next(struct bpf_object *prev)
 +{
 +      struct bpf_object *next;
 +      bool strict = (libbpf_mode & LIBBPF_STRICT_NO_OBJECT_LIST);
 +
 +      if (strict)
 +              return NULL;
 +
 +      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;
 +}
 +
 +const char *bpf_object__name(const struct bpf_object *obj)
 +{
 +      return obj ? obj->name : libbpf_err_ptr(-EINVAL);
 +}
 +
 +unsigned int bpf_object__kversion(const struct bpf_object *obj)
 +{
 +      return obj ? obj->kern_version : 0;
 +}
 +
 +struct btf *bpf_object__btf(const struct bpf_object *obj)
 +{
 +      return obj ? obj->btf : NULL;
 +}
 +
 +int bpf_object__btf_fd(const struct bpf_object *obj)
 +{
 +      return obj->btf ? btf__fd(obj->btf) : -1;
 +}
 +
 +int bpf_object__set_kversion(struct bpf_object *obj, __u32 kern_version)
 +{
 +      if (obj->loaded)
 +              return libbpf_err(-EINVAL);
 +
 +      obj->kern_version = kern_version;
 +
 +      return 0;
 +}
 +
 +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(const struct bpf_object *obj)
 +{
 +      return obj ? obj->priv : libbpf_err_ptr(-EINVAL);
 +}
 +
 +int bpf_object__gen_loader(struct bpf_object *obj, struct gen_loader_opts *opts)
 +{
 +      struct bpf_gen *gen;
 +
 +      if (!opts)
 +              return -EFAULT;
 +      if (!OPTS_VALID(opts, gen_loader_opts))
 +              return -EINVAL;
 +      gen = calloc(sizeof(*gen), 1);
 +      if (!gen)
 +              return -ENOMEM;
 +      gen->opts = opts;
 +      obj->gen_loader = gen;
 +      return 0;
 +}
 +
 +static struct bpf_program *
 +__bpf_program__iter(const struct bpf_program *p, const struct bpf_object *obj,
 +                  bool forward)
 +{
 +      size_t nr_programs = obj->nr_programs;
 +      ssize_t idx;
 +
 +      if (!nr_programs)
 +              return NULL;
 +
 +      if (!p)
 +              /* Iter from the beginning */
 +              return forward ? &obj->programs[0] :
 +                      &obj->programs[nr_programs - 1];
 +
 +      if (p->obj != obj) {
 +              pr_warn("error: program handler doesn't match object\n");
 +              return errno = EINVAL, NULL;
 +      }
 +
 +      idx = (p - obj->programs) + (forward ? 1 : -1);
 +      if (idx >= obj->nr_programs || idx < 0)
 +              return NULL;
 +      return &obj->programs[idx];
 +}
 +
 +struct bpf_program *
 +bpf_program__next(struct bpf_program *prev, const struct bpf_object *obj)
 +{
 +      return bpf_object__next_program(obj, prev);
 +}
 +
 +struct bpf_program *
 +bpf_object__next_program(const struct bpf_object *obj, struct bpf_program *prev)
 +{
 +      struct bpf_program *prog = prev;
 +
 +      do {
 +              prog = __bpf_program__iter(prog, obj, true);
 +      } while (prog && prog_is_subprog(obj, prog));
 +
 +      return prog;
 +}
 +
 +struct bpf_program *
 +bpf_program__prev(struct bpf_program *next, const struct bpf_object *obj)
 +{
 +      return bpf_object__prev_program(obj, next);
 +}
 +
 +struct bpf_program *
 +bpf_object__prev_program(const struct bpf_object *obj, struct bpf_program *next)
 +{
 +      struct bpf_program *prog = next;
 +
 +      do {
 +              prog = __bpf_program__iter(prog, obj, false);
 +      } while (prog && prog_is_subprog(obj, prog));
 +
 +      return prog;
 +}
 +
 +int bpf_program__set_priv(struct bpf_program *prog, void *priv,
 +                        bpf_program_clear_priv_t clear_priv)
 +{
 +      if (prog->priv && prog->clear_priv)
 +              prog->clear_priv(prog, prog->priv);
 +
 +      prog->priv = priv;
 +      prog->clear_priv = clear_priv;
 +      return 0;
 +}
 +
 +void *bpf_program__priv(const struct bpf_program *prog)
 +{
 +      return prog ? prog->priv : libbpf_err_ptr(-EINVAL);
 +}
 +
 +void bpf_program__set_ifindex(struct bpf_program *prog, __u32 ifindex)
 +{
 +      prog->prog_ifindex = ifindex;
 +}
 +
 +const char *bpf_program__name(const struct bpf_program *prog)
 +{
 +      return prog->name;
 +}
 +
 +const char *bpf_program__section_name(const struct bpf_program *prog)
 +{
 +      return prog->sec_name;
 +}
 +
 +const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy)
 +{
 +      const char *title;
 +
 +      title = prog->sec_name;
 +      if (needs_copy) {
 +              title = strdup(title);
 +              if (!title) {
 +                      pr_warn("failed to strdup program title\n");
 +                      return libbpf_err_ptr(-ENOMEM);
 +              }
 +      }
 +
 +      return title;
 +}
 +
 +bool bpf_program__autoload(const struct bpf_program *prog)
 +{
 +      return prog->load;
 +}
 +
 +int bpf_program__set_autoload(struct bpf_program *prog, bool autoload)
 +{
 +      if (prog->obj->loaded)
 +              return libbpf_err(-EINVAL);
 +
 +      prog->load = autoload;
 +      return 0;
 +}
 +
 +static int bpf_program_nth_fd(const struct bpf_program *prog, int n);
 +
 +int bpf_program__fd(const struct bpf_program *prog)
 +{
 +      return bpf_program_nth_fd(prog, 0);
 +}
 +
 +size_t bpf_program__size(const struct bpf_program *prog)
 +{
 +      return prog->insns_cnt * BPF_INSN_SZ;
 +}
 +
 +const struct bpf_insn *bpf_program__insns(const struct bpf_program *prog)
 +{
 +      return prog->insns;
 +}
 +
 +size_t bpf_program__insn_cnt(const struct bpf_program *prog)
 +{
 +      return prog->insns_cnt;
 +}
 +
 +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 libbpf_err(-EINVAL);
 +
 +      if (prog->instances.nr > 0 || prog->instances.fds) {
 +              pr_warn("Can't set pre-processor after loading\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      instances_fds = malloc(sizeof(int) * nr_instances);
 +      if (!instances_fds) {
 +              pr_warn("alloc memory failed for fds\n");
 +              return libbpf_err(-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;
 +}
 +
 +__attribute__((alias("bpf_program_nth_fd")))
 +int bpf_program__nth_fd(const struct bpf_program *prog, int n);
 +
 +static int bpf_program_nth_fd(const struct bpf_program *prog, int n)
 +{
 +      int fd;
 +
 +      if (!prog)
 +              return libbpf_err(-EINVAL);
 +
 +      if (n >= prog->instances.nr || n < 0) {
 +              pr_warn("Can't get the %dth fd from program %s: only %d instances\n",
 +                      n, prog->name, prog->instances.nr);
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      fd = prog->instances.fds[n];
 +      if (fd < 0) {
 +              pr_warn("%dth instance of program '%s' is invalid\n",
 +                      n, prog->name);
 +              return libbpf_err(-ENOENT);
 +      }
 +
 +      return fd;
 +}
 +
 +enum bpf_prog_type bpf_program__get_type(const struct bpf_program *prog)
 +{
 +      return prog->type;
 +}
 +
 +void bpf_program__set_type(struct bpf_program *prog, enum bpf_prog_type type)
 +{
 +      prog->type = type;
 +}
 +
 +static bool bpf_program__is_type(const struct bpf_program *prog,
 +                               enum bpf_prog_type type)
 +{
 +      return prog ? (prog->type == type) : false;
 +}
 +
 +#define BPF_PROG_TYPE_FNS(NAME, TYPE)                         \
 +int bpf_program__set_##NAME(struct bpf_program *prog)         \
 +{                                                             \
 +      if (!prog)                                              \
 +              return libbpf_err(-EINVAL);                     \
 +      bpf_program__set_type(prog, TYPE);                      \
 +      return 0;                                               \
 +}                                                             \
 +                                                              \
 +bool bpf_program__is_##NAME(const struct bpf_program *prog)   \
 +{                                                             \
 +      return bpf_program__is_type(prog, TYPE);                \
 +}                                                             \
 +
 +BPF_PROG_TYPE_FNS(socket_filter, BPF_PROG_TYPE_SOCKET_FILTER);
 +BPF_PROG_TYPE_FNS(lsm, BPF_PROG_TYPE_LSM);
 +BPF_PROG_TYPE_FNS(kprobe, BPF_PROG_TYPE_KPROBE);
 +BPF_PROG_TYPE_FNS(sched_cls, BPF_PROG_TYPE_SCHED_CLS);
 +BPF_PROG_TYPE_FNS(sched_act, BPF_PROG_TYPE_SCHED_ACT);
 +BPF_PROG_TYPE_FNS(tracepoint, BPF_PROG_TYPE_TRACEPOINT);
 +BPF_PROG_TYPE_FNS(raw_tracepoint, BPF_PROG_TYPE_RAW_TRACEPOINT);
 +BPF_PROG_TYPE_FNS(xdp, BPF_PROG_TYPE_XDP);
 +BPF_PROG_TYPE_FNS(perf_event, BPF_PROG_TYPE_PERF_EVENT);
 +BPF_PROG_TYPE_FNS(tracing, BPF_PROG_TYPE_TRACING);
 +BPF_PROG_TYPE_FNS(struct_ops, BPF_PROG_TYPE_STRUCT_OPS);
 +BPF_PROG_TYPE_FNS(extension, BPF_PROG_TYPE_EXT);
 +BPF_PROG_TYPE_FNS(sk_lookup, BPF_PROG_TYPE_SK_LOOKUP);
 +
 +enum bpf_attach_type
 +bpf_program__get_expected_attach_type(const struct bpf_program *prog)
 +{
 +      return prog->expected_attach_type;
 +}
 +
 +void bpf_program__set_expected_attach_type(struct bpf_program *prog,
 +                                         enum bpf_attach_type type)
 +{
 +      prog->expected_attach_type = type;
 +}
 +
 +__u32 bpf_program__flags(const struct bpf_program *prog)
 +{
 +      return prog->prog_flags;
 +}
 +
 +int bpf_program__set_flags(struct bpf_program *prog, __u32 flags)
 +{
 +      if (prog->obj->loaded)
 +              return libbpf_err(-EBUSY);
 +
 +      prog->prog_flags = flags;
 +      return 0;
 +}
 +
 +__u32 bpf_program__log_level(const struct bpf_program *prog)
 +{
 +      return prog->log_level;
 +}
 +
 +int bpf_program__set_log_level(struct bpf_program *prog, __u32 log_level)
 +{
 +      if (prog->obj->loaded)
 +              return libbpf_err(-EBUSY);
 +
 +      prog->log_level = log_level;
 +      return 0;
 +}
 +
 +const char *bpf_program__log_buf(const struct bpf_program *prog, size_t *log_size)
 +{
 +      *log_size = prog->log_size;
 +      return prog->log_buf;
 +}
 +
 +int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log_size)
 +{
 +      if (log_size && !log_buf)
 +              return -EINVAL;
 +      if (prog->log_size > UINT_MAX)
 +              return -EINVAL;
 +      if (prog->obj->loaded)
 +              return -EBUSY;
 +
 +      prog->log_buf = log_buf;
 +      prog->log_size = log_size;
 +      return 0;
 +}
 +
 +#define SEC_DEF(sec_pfx, ptype, atype, flags, ...) {                      \
 +      .sec = sec_pfx,                                                     \
 +      .prog_type = BPF_PROG_TYPE_##ptype,                                 \
 +      .expected_attach_type = atype,                                      \
 +      .cookie = (long)(flags),                                            \
 +      .preload_fn = libbpf_preload_prog,                                  \
 +      __VA_ARGS__                                                         \
 +}
 +
 +static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cookie);
 +static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie);
 +static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cookie);
 +static struct bpf_link *attach_trace(const struct bpf_program *prog, long cookie);
 +static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie);
 +static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie);
 +
 +static const struct bpf_sec_def section_defs[] = {
 +      SEC_DEF("socket",               SOCKET_FILTER, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("sk_reuseport/migrate", SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT_OR_MIGRATE, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("sk_reuseport",         SK_REUSEPORT, BPF_SK_REUSEPORT_SELECT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("kprobe/",              KPROBE, 0, SEC_NONE, attach_kprobe),
 +      SEC_DEF("uprobe/",              KPROBE, 0, SEC_NONE),
 +      SEC_DEF("kretprobe/",           KPROBE, 0, SEC_NONE, attach_kprobe),
 +      SEC_DEF("uretprobe/",           KPROBE, 0, SEC_NONE),
 +      SEC_DEF("tc",                   SCHED_CLS, 0, SEC_NONE),
 +      SEC_DEF("classifier",           SCHED_CLS, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("action",               SCHED_ACT, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("tracepoint/",          TRACEPOINT, 0, SEC_NONE, attach_tp),
 +      SEC_DEF("tp/",                  TRACEPOINT, 0, SEC_NONE, attach_tp),
 +      SEC_DEF("raw_tracepoint/",      RAW_TRACEPOINT, 0, SEC_NONE, attach_raw_tp),
 +      SEC_DEF("raw_tp/",              RAW_TRACEPOINT, 0, SEC_NONE, attach_raw_tp),
 +      SEC_DEF("raw_tracepoint.w/",    RAW_TRACEPOINT_WRITABLE, 0, SEC_NONE, attach_raw_tp),
 +      SEC_DEF("raw_tp.w/",            RAW_TRACEPOINT_WRITABLE, 0, SEC_NONE, attach_raw_tp),
 +      SEC_DEF("tp_btf/",              TRACING, BPF_TRACE_RAW_TP, SEC_ATTACH_BTF, attach_trace),
 +      SEC_DEF("fentry/",              TRACING, BPF_TRACE_FENTRY, SEC_ATTACH_BTF, attach_trace),
 +      SEC_DEF("fmod_ret/",            TRACING, BPF_MODIFY_RETURN, SEC_ATTACH_BTF, attach_trace),
 +      SEC_DEF("fexit/",               TRACING, BPF_TRACE_FEXIT, SEC_ATTACH_BTF, attach_trace),
 +      SEC_DEF("fentry.s/",            TRACING, BPF_TRACE_FENTRY, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_trace),
 +      SEC_DEF("fmod_ret.s/",          TRACING, BPF_MODIFY_RETURN, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_trace),
 +      SEC_DEF("fexit.s/",             TRACING, BPF_TRACE_FEXIT, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_trace),
 +      SEC_DEF("freplace/",            EXT, 0, SEC_ATTACH_BTF, attach_trace),
 +      SEC_DEF("lsm/",                 LSM, BPF_LSM_MAC, SEC_ATTACH_BTF, attach_lsm),
 +      SEC_DEF("lsm.s/",               LSM, BPF_LSM_MAC, SEC_ATTACH_BTF | SEC_SLEEPABLE, attach_lsm),
 +      SEC_DEF("iter/",                TRACING, BPF_TRACE_ITER, SEC_ATTACH_BTF, attach_iter),
 +      SEC_DEF("syscall",              SYSCALL, 0, SEC_SLEEPABLE),
 +      SEC_DEF("xdp_devmap/",          XDP, BPF_XDP_DEVMAP, SEC_ATTACHABLE),
 +      SEC_DEF("xdp_cpumap/",          XDP, BPF_XDP_CPUMAP, SEC_ATTACHABLE),
 +      SEC_DEF("xdp",                  XDP, BPF_XDP, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("perf_event",           PERF_EVENT, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("lwt_in",               LWT_IN, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("lwt_out",              LWT_OUT, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("lwt_xmit",             LWT_XMIT, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("lwt_seg6local",        LWT_SEG6LOCAL, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup_skb/ingress",   CGROUP_SKB, BPF_CGROUP_INET_INGRESS, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup_skb/egress",    CGROUP_SKB, BPF_CGROUP_INET_EGRESS, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/skb",           CGROUP_SKB, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/sock_create",   CGROUP_SOCK, BPF_CGROUP_INET_SOCK_CREATE, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/sock_release",  CGROUP_SOCK, BPF_CGROUP_INET_SOCK_RELEASE, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/sock",          CGROUP_SOCK, BPF_CGROUP_INET_SOCK_CREATE, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/post_bind4",    CGROUP_SOCK, BPF_CGROUP_INET4_POST_BIND, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/post_bind6",    CGROUP_SOCK, BPF_CGROUP_INET6_POST_BIND, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/dev",           CGROUP_DEVICE, BPF_CGROUP_DEVICE, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("sockops",              SOCK_OPS, BPF_CGROUP_SOCK_OPS, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("sk_skb/stream_parser", SK_SKB, BPF_SK_SKB_STREAM_PARSER, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("sk_skb/stream_verdict",SK_SKB, BPF_SK_SKB_STREAM_VERDICT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("sk_skb",               SK_SKB, 0, SEC_NONE | SEC_SLOPPY_PFX),
 +      SEC_DEF("sk_msg",               SK_MSG, BPF_SK_MSG_VERDICT, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("lirc_mode2",           LIRC_MODE2, BPF_LIRC_MODE2, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("flow_dissector",       FLOW_DISSECTOR, BPF_FLOW_DISSECTOR, SEC_ATTACHABLE_OPT | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/bind4",         CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_BIND, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/bind6",         CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_BIND, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/connect4",      CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_CONNECT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/connect6",      CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_CONNECT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/sendmsg4",      CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_SENDMSG, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/sendmsg6",      CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_SENDMSG, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/recvmsg4",      CGROUP_SOCK_ADDR, BPF_CGROUP_UDP4_RECVMSG, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/recvmsg6",      CGROUP_SOCK_ADDR, BPF_CGROUP_UDP6_RECVMSG, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/getpeername4",  CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_GETPEERNAME, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/getpeername6",  CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_GETPEERNAME, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/getsockname4",  CGROUP_SOCK_ADDR, BPF_CGROUP_INET4_GETSOCKNAME, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/getsockname6",  CGROUP_SOCK_ADDR, BPF_CGROUP_INET6_GETSOCKNAME, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/sysctl",        CGROUP_SYSCTL, BPF_CGROUP_SYSCTL, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/getsockopt",    CGROUP_SOCKOPT, BPF_CGROUP_GETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("cgroup/setsockopt",    CGROUP_SOCKOPT, BPF_CGROUP_SETSOCKOPT, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +      SEC_DEF("struct_ops+",          STRUCT_OPS, 0, SEC_NONE),
 +      SEC_DEF("sk_lookup",            SK_LOOKUP, BPF_SK_LOOKUP, SEC_ATTACHABLE | SEC_SLOPPY_PFX),
 +};
 +
 +#define MAX_TYPE_NAME_SIZE 32
 +
 +static const struct bpf_sec_def *find_sec_def(const char *sec_name)
 +{
 +      const struct bpf_sec_def *sec_def;
 +      enum sec_def_flags sec_flags;
 +      int i, n = ARRAY_SIZE(section_defs), len;
 +      bool strict = libbpf_mode & LIBBPF_STRICT_SEC_NAME;
 +
 +      for (i = 0; i < n; i++) {
 +              sec_def = &section_defs[i];
 +              sec_flags = sec_def->cookie;
 +              len = strlen(sec_def->sec);
 +
 +              /* "type/" always has to have proper SEC("type/extras") form */
 +              if (sec_def->sec[len - 1] == '/') {
 +                      if (str_has_pfx(sec_name, sec_def->sec))
 +                              return sec_def;
 +                      continue;
 +              }
 +
 +              /* "type+" means it can be either exact SEC("type") or
 +               * well-formed SEC("type/extras") with proper '/' separator
 +               */
 +              if (sec_def->sec[len - 1] == '+') {
 +                      len--;
 +                      /* not even a prefix */
 +                      if (strncmp(sec_name, sec_def->sec, len) != 0)
 +                              continue;
 +                      /* exact match or has '/' separator */
 +                      if (sec_name[len] == '\0' || sec_name[len] == '/')
 +                              return sec_def;
 +                      continue;
 +              }
 +
 +              /* SEC_SLOPPY_PFX definitions are allowed to be just prefix
 +               * matches, unless strict section name mode
 +               * (LIBBPF_STRICT_SEC_NAME) is enabled, in which case the
 +               * match has to be exact.
 +               */
 +              if ((sec_flags & SEC_SLOPPY_PFX) && !strict)  {
 +                      if (str_has_pfx(sec_name, sec_def->sec))
 +                              return sec_def;
 +                      continue;
 +              }
 +
 +              /* Definitions not marked SEC_SLOPPY_PFX (e.g.,
 +               * SEC("syscall")) are exact matches in both modes.
 +               */
 +              if (strcmp(sec_name, sec_def->sec) == 0)
 +                      return sec_def;
 +      }
 +      return NULL;
 +}
 +
 +static char *libbpf_get_type_names(bool attach_type)
 +{
 +      int i, len = ARRAY_SIZE(section_defs) * MAX_TYPE_NAME_SIZE;
 +      char *buf;
 +
 +      buf = malloc(len);
 +      if (!buf)
 +              return NULL;
 +
 +      buf[0] = '\0';
 +      /* Forge string buf with all available names */
 +      for (i = 0; i < ARRAY_SIZE(section_defs); i++) {
 +              const struct bpf_sec_def *sec_def = &section_defs[i];
 +
 +              if (attach_type) {
 +                      if (sec_def->preload_fn != libbpf_preload_prog)
 +                              continue;
 +
 +                      if (!(sec_def->cookie & SEC_ATTACHABLE))
 +                              continue;
 +              }
 +
 +              if (strlen(buf) + strlen(section_defs[i].sec) + 2 > len) {
 +                      free(buf);
 +                      return NULL;
 +              }
 +              strcat(buf, " ");
 +              strcat(buf, section_defs[i].sec);
 +      }
 +
 +      return buf;
 +}
 +
 +int libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
 +                           enum bpf_attach_type *expected_attach_type)
 +{
 +      const struct bpf_sec_def *sec_def;
 +      char *type_names;
 +
 +      if (!name)
 +              return libbpf_err(-EINVAL);
 +
 +      sec_def = find_sec_def(name);
 +      if (sec_def) {
 +              *prog_type = sec_def->prog_type;
 +              *expected_attach_type = sec_def->expected_attach_type;
 +              return 0;
 +      }
 +
 +      pr_debug("failed to guess program type from ELF section '%s'\n", name);
 +      type_names = libbpf_get_type_names(false);
 +      if (type_names != NULL) {
 +              pr_debug("supported section(type) names are:%s\n", type_names);
 +              free(type_names);
 +      }
 +
 +      return libbpf_err(-ESRCH);
 +}
 +
 +static struct bpf_map *find_struct_ops_map_by_offset(struct bpf_object *obj,
 +                                                   size_t offset)
 +{
 +      struct bpf_map *map;
 +      size_t i;
 +
 +      for (i = 0; i < obj->nr_maps; i++) {
 +              map = &obj->maps[i];
 +              if (!bpf_map__is_struct_ops(map))
 +                      continue;
 +              if (map->sec_offset <= offset &&
 +                  offset - map->sec_offset < map->def.value_size)
 +                      return map;
 +      }
 +
 +      return NULL;
 +}
 +
 +/* Collect the reloc from ELF and populate the st_ops->progs[] */
 +static int bpf_object__collect_st_ops_relos(struct bpf_object *obj,
 +                                          Elf64_Shdr *shdr, Elf_Data *data)
 +{
 +      const struct btf_member *member;
 +      struct bpf_struct_ops *st_ops;
 +      struct bpf_program *prog;
 +      unsigned int shdr_idx;
 +      const struct btf *btf;
 +      struct bpf_map *map;
 +      unsigned int moff, insn_idx;
 +      const char *name;
 +      __u32 member_idx;
 +      Elf64_Sym *sym;
 +      Elf64_Rel *rel;
 +      int i, nrels;
 +
 +      btf = obj->btf;
 +      nrels = shdr->sh_size / shdr->sh_entsize;
 +      for (i = 0; i < nrels; i++) {
 +              rel = elf_rel_by_idx(data, i);
 +              if (!rel) {
 +                      pr_warn("struct_ops reloc: failed to get %d reloc\n", i);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              sym = elf_sym_by_idx(obj, ELF64_R_SYM(rel->r_info));
 +              if (!sym) {
 +                      pr_warn("struct_ops reloc: symbol %zx not found\n",
 +                              (size_t)ELF64_R_SYM(rel->r_info));
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +
 +              name = elf_sym_str(obj, sym->st_name) ?: "<?>";
 +              map = find_struct_ops_map_by_offset(obj, rel->r_offset);
 +              if (!map) {
 +                      pr_warn("struct_ops reloc: cannot find map at rel->r_offset %zu\n",
 +                              (size_t)rel->r_offset);
 +                      return -EINVAL;
 +              }
 +
 +              moff = rel->r_offset - map->sec_offset;
 +              shdr_idx = sym->st_shndx;
 +              st_ops = map->st_ops;
 +              pr_debug("struct_ops reloc %s: for %lld value %lld shdr_idx %u rel->r_offset %zu map->sec_offset %zu name %d (\'%s\')\n",
 +                       map->name,
 +                       (long long)(rel->r_info >> 32),
 +                       (long long)sym->st_value,
 +                       shdr_idx, (size_t)rel->r_offset,
 +                       map->sec_offset, sym->st_name, name);
 +
 +              if (shdr_idx >= SHN_LORESERVE) {
 +                      pr_warn("struct_ops reloc %s: rel->r_offset %zu shdr_idx %u unsupported non-static function\n",
 +                              map->name, (size_t)rel->r_offset, shdr_idx);
 +                      return -LIBBPF_ERRNO__RELOC;
 +              }
 +              if (sym->st_value % BPF_INSN_SZ) {
 +                      pr_warn("struct_ops reloc %s: invalid target program offset %llu\n",
 +                              map->name, (unsigned long long)sym->st_value);
 +                      return -LIBBPF_ERRNO__FORMAT;
 +              }
 +              insn_idx = sym->st_value / BPF_INSN_SZ;
 +
 +              member = find_member_by_offset(st_ops->type, moff * 8);
 +              if (!member) {
 +                      pr_warn("struct_ops reloc %s: cannot find member at moff %u\n",
 +                              map->name, moff);
 +                      return -EINVAL;
 +              }
 +              member_idx = member - btf_members(st_ops->type);
 +              name = btf__name_by_offset(btf, member->name_off);
 +
 +              if (!resolve_func_ptr(btf, member->type, NULL)) {
 +                      pr_warn("struct_ops reloc %s: cannot relocate non func ptr %s\n",
 +                              map->name, name);
 +                      return -EINVAL;
 +              }
 +
 +              prog = find_prog_by_sec_insn(obj, shdr_idx, insn_idx);
 +              if (!prog) {
 +                      pr_warn("struct_ops reloc %s: cannot find prog at shdr_idx %u to relocate func ptr %s\n",
 +                              map->name, shdr_idx, name);
 +                      return -EINVAL;
 +              }
 +
 +              /* prevent the use of BPF prog with invalid type */
 +              if (prog->type != BPF_PROG_TYPE_STRUCT_OPS) {
 +                      pr_warn("struct_ops reloc %s: prog %s is not struct_ops BPF program\n",
 +                              map->name, prog->name);
 +                      return -EINVAL;
 +              }
 +
 +              /* if we haven't yet processed this BPF program, record proper
 +               * attach_btf_id and member_idx
 +               */
 +              if (!prog->attach_btf_id) {
 +                      prog->attach_btf_id = st_ops->type_id;
 +                      prog->expected_attach_type = member_idx;
 +              }
 +
 +              /* struct_ops BPF prog can be re-used between multiple
 +               * .struct_ops as long as it's the same struct_ops struct
 +               * definition and the same function pointer field
 +               */
 +              if (prog->attach_btf_id != st_ops->type_id ||
 +                  prog->expected_attach_type != member_idx) {
 +                      pr_warn("struct_ops reloc %s: cannot use prog %s in sec %s with type %u attach_btf_id %u expected_attach_type %u for func ptr %s\n",
 +                              map->name, prog->name, prog->sec_name, prog->type,
 +                              prog->attach_btf_id, prog->expected_attach_type, name);
 +                      return -EINVAL;
 +              }
 +
 +              st_ops->progs[member_idx] = prog;
 +      }
 +
 +      return 0;
 +}
 +
 +#define BTF_TRACE_PREFIX "btf_trace_"
 +#define BTF_LSM_PREFIX "bpf_lsm_"
 +#define BTF_ITER_PREFIX "bpf_iter_"
 +#define BTF_MAX_NAME_SIZE 128
 +
 +void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
 +                              const char **prefix, int *kind)
 +{
 +      switch (attach_type) {
 +      case BPF_TRACE_RAW_TP:
 +              *prefix = BTF_TRACE_PREFIX;
 +              *kind = BTF_KIND_TYPEDEF;
 +              break;
 +      case BPF_LSM_MAC:
 +              *prefix = BTF_LSM_PREFIX;
 +              *kind = BTF_KIND_FUNC;
 +              break;
 +      case BPF_TRACE_ITER:
 +              *prefix = BTF_ITER_PREFIX;
 +              *kind = BTF_KIND_FUNC;
 +              break;
 +      default:
 +              *prefix = "";
 +              *kind = BTF_KIND_FUNC;
 +      }
 +}
 +
 +static int find_btf_by_prefix_kind(const struct btf *btf, const char *prefix,
 +                                 const char *name, __u32 kind)
 +{
 +      char btf_type_name[BTF_MAX_NAME_SIZE];
 +      int ret;
 +
 +      ret = snprintf(btf_type_name, sizeof(btf_type_name),
 +                     "%s%s", prefix, name);
 +      /* snprintf returns the number of characters written excluding the
 +       * terminating null. So, if >= BTF_MAX_NAME_SIZE are written, it
 +       * indicates truncation.
 +       */
 +      if (ret < 0 || ret >= sizeof(btf_type_name))
 +              return -ENAMETOOLONG;
 +      return btf__find_by_name_kind(btf, btf_type_name, kind);
 +}
 +
 +static inline int find_attach_btf_id(struct btf *btf, const char *name,
 +                                   enum bpf_attach_type attach_type)
 +{
 +      const char *prefix;
 +      int kind;
 +
 +      btf_get_kernel_prefix_kind(attach_type, &prefix, &kind);
 +      return find_btf_by_prefix_kind(btf, prefix, name, kind);
 +}
 +
 +int libbpf_find_vmlinux_btf_id(const char *name,
 +                             enum bpf_attach_type attach_type)
 +{
 +      struct btf *btf;
 +      int err;
 +
 +      btf = btf__load_vmlinux_btf();
 +      err = libbpf_get_error(btf);
 +      if (err) {
 +              pr_warn("vmlinux BTF is not found\n");
 +              return libbpf_err(err);
 +      }
 +
 +      err = find_attach_btf_id(btf, name, attach_type);
 +      if (err <= 0)
 +              pr_warn("%s is not found in vmlinux BTF\n", name);
 +
 +      btf__free(btf);
 +      return libbpf_err(err);
 +}
 +
 +static int libbpf_find_prog_btf_id(const char *name, __u32 attach_prog_fd)
 +{
 +      struct bpf_prog_info info = {};
 +      __u32 info_len = sizeof(info);
 +      struct btf *btf;
 +      int err;
 +
 +      err = bpf_obj_get_info_by_fd(attach_prog_fd, &info, &info_len);
 +      if (err) {
 +              pr_warn("failed bpf_obj_get_info_by_fd for FD %d: %d\n",
 +                      attach_prog_fd, err);
 +              return err;
 +      }
 +
 +      err = -EINVAL;
 +      if (!info.btf_id) {
 +              pr_warn("The target program doesn't have BTF\n");
 +              goto out;
 +      }
 +      btf = btf__load_from_kernel_by_id(info.btf_id);
 +      err = libbpf_get_error(btf);
 +      if (err) {
 +              pr_warn("Failed to get BTF %d of the program: %d\n", info.btf_id, err);
 +              goto out;
 +      }
 +      err = btf__find_by_name_kind(btf, name, BTF_KIND_FUNC);
 +      btf__free(btf);
 +      if (err <= 0) {
 +              pr_warn("%s is not found in prog's BTF\n", name);
 +              goto out;
 +      }
 +out:
 +      return err;
 +}
 +
 +static int find_kernel_btf_id(struct bpf_object *obj, const char *attach_name,
 +                            enum bpf_attach_type attach_type,
 +                            int *btf_obj_fd, int *btf_type_id)
 +{
 +      int ret, i;
 +
 +      ret = find_attach_btf_id(obj->btf_vmlinux, attach_name, attach_type);
 +      if (ret > 0) {
 +              *btf_obj_fd = 0; /* vmlinux BTF */
 +              *btf_type_id = ret;
 +              return 0;
 +      }
 +      if (ret != -ENOENT)
 +              return ret;
 +
 +      ret = load_module_btfs(obj);
 +      if (ret)
 +              return ret;
 +
 +      for (i = 0; i < obj->btf_module_cnt; i++) {
 +              const struct module_btf *mod = &obj->btf_modules[i];
 +
 +              ret = find_attach_btf_id(mod->btf, attach_name, attach_type);
 +              if (ret > 0) {
 +                      *btf_obj_fd = mod->fd;
 +                      *btf_type_id = ret;
 +                      return 0;
 +              }
 +              if (ret == -ENOENT)
 +                      continue;
 +
 +              return ret;
 +      }
 +
 +      return -ESRCH;
 +}
 +
 +static int libbpf_find_attach_btf_id(struct bpf_program *prog, const char *attach_name,
 +                                   int *btf_obj_fd, int *btf_type_id)
 +{
 +      enum bpf_attach_type attach_type = prog->expected_attach_type;
 +      __u32 attach_prog_fd = prog->attach_prog_fd;
 +      int err = 0;
 +
 +      /* BPF program's BTF ID */
 +      if (attach_prog_fd) {
 +              err = libbpf_find_prog_btf_id(attach_name, attach_prog_fd);
 +              if (err < 0) {
 +                      pr_warn("failed to find BPF program (FD %d) BTF ID for '%s': %d\n",
 +                               attach_prog_fd, attach_name, err);
 +                      return err;
 +              }
 +              *btf_obj_fd = 0;
 +              *btf_type_id = err;
 +              return 0;
 +      }
 +
 +      /* kernel/module BTF ID */
 +      if (prog->obj->gen_loader) {
 +              bpf_gen__record_attach_target(prog->obj->gen_loader, attach_name, attach_type);
 +              *btf_obj_fd = 0;
 +              *btf_type_id = 1;
 +      } else {
 +              err = find_kernel_btf_id(prog->obj, attach_name, attach_type, btf_obj_fd, btf_type_id);
 +      }
 +      if (err) {
 +              pr_warn("failed to find kernel BTF type ID of '%s': %d\n", attach_name, err);
 +              return err;
 +      }
 +      return 0;
 +}
 +
 +int libbpf_attach_type_by_name(const char *name,
 +                             enum bpf_attach_type *attach_type)
 +{
 +      char *type_names;
 +      const struct bpf_sec_def *sec_def;
 +
 +      if (!name)
 +              return libbpf_err(-EINVAL);
 +
 +      sec_def = find_sec_def(name);
 +      if (!sec_def) {
 +              pr_debug("failed to guess attach type based on ELF section name '%s'\n", name);
 +              type_names = libbpf_get_type_names(true);
 +              if (type_names != NULL) {
 +                      pr_debug("attachable section(type) names are:%s\n", type_names);
 +                      free(type_names);
 +              }
 +
 +              return libbpf_err(-EINVAL);
 +      }
 +
 +      if (sec_def->preload_fn != libbpf_preload_prog)
 +              return libbpf_err(-EINVAL);
 +      if (!(sec_def->cookie & SEC_ATTACHABLE))
 +              return libbpf_err(-EINVAL);
 +
 +      *attach_type = sec_def->expected_attach_type;
 +      return 0;
 +}
 +
 +int bpf_map__fd(const struct bpf_map *map)
 +{
 +      return map ? map->fd : libbpf_err(-EINVAL);
 +}
 +
 +const struct bpf_map_def *bpf_map__def(const struct bpf_map *map)
 +{
 +      return map ? &map->def : libbpf_err_ptr(-EINVAL);
 +}
 +
 +static bool map_uses_real_name(const struct bpf_map *map)
 +{
 +      /* Since libbpf started to support custom .data.* and .rodata.* maps,
 +       * their user-visible name differs from kernel-visible name. Users see
 +       * such map's corresponding ELF section name as a map name.
 +       * This check distinguishes .data/.rodata from .data.* and .rodata.*
 +       * maps to know which name has to be returned to the user.
 +       */
 +      if (map->libbpf_type == LIBBPF_MAP_DATA && strcmp(map->real_name, DATA_SEC) != 0)
 +              return true;
 +      if (map->libbpf_type == LIBBPF_MAP_RODATA && strcmp(map->real_name, RODATA_SEC) != 0)
 +              return true;
 +      return false;
 +}
 +
 +const char *bpf_map__name(const struct bpf_map *map)
 +{
 +      if (!map)
 +              return NULL;
 +
 +      if (map_uses_real_name(map))
 +              return map->real_name;
 +
 +      return map->name;
 +}
 +
 +enum bpf_map_type bpf_map__type(const struct bpf_map *map)
 +{
 +      return map->def.type;
 +}
 +
 +int bpf_map__set_type(struct bpf_map *map, enum bpf_map_type type)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->def.type = type;
 +      return 0;
 +}
 +
 +__u32 bpf_map__map_flags(const struct bpf_map *map)
 +{
 +      return map->def.map_flags;
 +}
 +
 +int bpf_map__set_map_flags(struct bpf_map *map, __u32 flags)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->def.map_flags = flags;
 +      return 0;
 +}
 +
 +__u64 bpf_map__map_extra(const struct bpf_map *map)
 +{
 +      return map->map_extra;
 +}
 +
 +int bpf_map__set_map_extra(struct bpf_map *map, __u64 map_extra)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->map_extra = map_extra;
 +      return 0;
 +}
 +
 +__u32 bpf_map__numa_node(const struct bpf_map *map)
 +{
 +      return map->numa_node;
 +}
 +
 +int bpf_map__set_numa_node(struct bpf_map *map, __u32 numa_node)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->numa_node = numa_node;
 +      return 0;
 +}
 +
 +__u32 bpf_map__key_size(const struct bpf_map *map)
 +{
 +      return map->def.key_size;
 +}
 +
 +int bpf_map__set_key_size(struct bpf_map *map, __u32 size)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->def.key_size = size;
 +      return 0;
 +}
 +
 +__u32 bpf_map__value_size(const struct bpf_map *map)
 +{
 +      return map->def.value_size;
 +}
 +
 +int bpf_map__set_value_size(struct bpf_map *map, __u32 size)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->def.value_size = size;
 +      return 0;
 +}
 +
 +__u32 bpf_map__btf_key_type_id(const struct bpf_map *map)
 +{
 +      return map ? map->btf_key_type_id : 0;
 +}
 +
 +__u32 bpf_map__btf_value_type_id(const struct bpf_map *map)
 +{
 +      return map ? map->btf_value_type_id : 0;
 +}
 +
 +int bpf_map__set_priv(struct bpf_map *map, void *priv,
 +                   bpf_map_clear_priv_t clear_priv)
 +{
 +      if (!map)
 +              return libbpf_err(-EINVAL);
 +
 +      if (map->priv) {
 +              if (map->clear_priv)
 +                      map->clear_priv(map, map->priv);
 +      }
 +
 +      map->priv = priv;
 +      map->clear_priv = clear_priv;
 +      return 0;
 +}
 +
 +void *bpf_map__priv(const struct bpf_map *map)
 +{
 +      return map ? map->priv : libbpf_err_ptr(-EINVAL);
 +}
 +
 +int bpf_map__set_initial_value(struct bpf_map *map,
 +                             const void *data, size_t size)
 +{
 +      if (!map->mmaped || map->libbpf_type == LIBBPF_MAP_KCONFIG ||
 +          size != map->def.value_size || map->fd >= 0)
 +              return libbpf_err(-EINVAL);
 +
 +      memcpy(map->mmaped, data, size);
 +      return 0;
 +}
 +
 +const void *bpf_map__initial_value(struct bpf_map *map, size_t *psize)
 +{
 +      if (!map->mmaped)
 +              return NULL;
 +      *psize = map->def.value_size;
 +      return map->mmaped;
 +}
 +
 +bool bpf_map__is_offload_neutral(const struct bpf_map *map)
 +{
 +      return map->def.type == BPF_MAP_TYPE_PERF_EVENT_ARRAY;
 +}
 +
 +bool bpf_map__is_internal(const struct bpf_map *map)
 +{
 +      return map->libbpf_type != LIBBPF_MAP_UNSPEC;
 +}
 +
 +__u32 bpf_map__ifindex(const struct bpf_map *map)
 +{
 +      return map->map_ifindex;
 +}
 +
 +int bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex)
 +{
 +      if (map->fd >= 0)
 +              return libbpf_err(-EBUSY);
 +      map->map_ifindex = ifindex;
 +      return 0;
 +}
 +
 +int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd)
 +{
 +      if (!bpf_map_type__is_map_in_map(map->def.type)) {
 +              pr_warn("error: unsupported map type\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +      if (map->inner_map_fd != -1) {
 +              pr_warn("error: inner_map_fd already specified\n");
 +              return libbpf_err(-EINVAL);
 +      }
 +      if (map->inner_map) {
 +              bpf_map__destroy(map->inner_map);
 +              zfree(&map->inner_map);
 +      }
 +      map->inner_map_fd = fd;
 +      return 0;
 +}
 +
 +static struct bpf_map *
 +__bpf_map__iter(const struct bpf_map *m, const struct bpf_object *obj, int i)
 +{
 +      ssize_t idx;
 +      struct bpf_map *s, *e;
 +
 +      if (!obj || !obj->maps)
 +              return errno = EINVAL, NULL;
 +
 +      s = obj->maps;
 +      e = obj->maps + obj->nr_maps;
 +
 +      if ((m < s) || (m >= e)) {
 +              pr_warn("error in %s: map handler doesn't belong to object\n",
 +                       __func__);
 +              return errno = EINVAL, NULL;
 +      }
 +
 +      idx = (m - obj->maps) + i;
 +      if (idx >= obj->nr_maps || idx < 0)
 +              return NULL;
 +      return &obj->maps[idx];
 +}
 +
 +struct bpf_map *
 +bpf_map__next(const struct bpf_map *prev, const struct bpf_object *obj)
 +{
 +      return bpf_object__next_map(obj, prev);
 +}
 +
 +struct bpf_map *
 +bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *prev)
 +{
 +      if (prev == NULL)
 +              return obj->maps;
 +
 +      return __bpf_map__iter(prev, obj, 1);
 +}
 +
 +struct bpf_map *
 +bpf_map__prev(const struct bpf_map *next, const struct bpf_object *obj)
 +{
 +      return bpf_object__prev_map(obj, next);
 +}
 +
 +struct bpf_map *
 +bpf_object__prev_map(const struct bpf_object *obj, const struct bpf_map *next)
 +{
 +      if (next == NULL) {
 +              if (!obj->nr_maps)
 +                      return NULL;
 +              return obj->maps + obj->nr_maps - 1;
 +      }
 +
 +      return __bpf_map__iter(next, obj, -1);
 +}
 +
 +struct bpf_map *
 +bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name)
 +{
 +      struct bpf_map *pos;
 +
 +      bpf_object__for_each_map(pos, obj) {
 +              /* if it's a special internal map name (which always starts
 +               * with dot) then check if that special name matches the
 +               * real map name (ELF section name)
 +               */
 +              if (name[0] == '.') {
 +                      if (pos->real_name && strcmp(pos->real_name, name) == 0)
 +                              return pos;
 +                      continue;
 +              }
 +              /* otherwise map name has to be an exact match */
 +              if (map_uses_real_name(pos)) {
 +                      if (strcmp(pos->real_name, name) == 0)
 +                              return pos;
 +                      continue;
 +              }
 +              if (strcmp(pos->name, name) == 0)
 +                      return pos;
 +      }
 +      return errno = ENOENT, NULL;
 +}
 +
 +int
 +bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name)
 +{
 +      return bpf_map__fd(bpf_object__find_map_by_name(obj, name));
 +}
 +
 +struct bpf_map *
 +bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset)
 +{
 +      return libbpf_err_ptr(-ENOTSUP);
 +}
 +
 +long libbpf_get_error(const void *ptr)
 +{
 +      if (!IS_ERR_OR_NULL(ptr))
 +              return 0;
 +
 +      if (IS_ERR(ptr))
 +              errno = -PTR_ERR(ptr);
 +
 +      /* If ptr == NULL, then errno should be already set by the failing
 +       * API, because libbpf never returns NULL on success and it now always
 +       * sets errno on error. So no extra errno handling for ptr == NULL
 +       * case.
 +       */
 +      return -errno;
 +}
 +
 +__attribute__((alias("bpf_prog_load_xattr2")))
 +int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 +                      struct bpf_object **pobj, int *prog_fd);
 +
 +static int bpf_prog_load_xattr2(const struct bpf_prog_load_attr *attr,
 +                              struct bpf_object **pobj, int *prog_fd)
 +{
 +      struct bpf_object_open_attr open_attr = {};
 +      struct bpf_program *prog, *first_prog = NULL;
 +      struct bpf_object *obj;
 +      struct bpf_map *map;
 +      int err;
 +
 +      if (!attr)
 +              return libbpf_err(-EINVAL);
 +      if (!attr->file)
 +              return libbpf_err(-EINVAL);
 +
 +      open_attr.file = attr->file;
 +      open_attr.prog_type = attr->prog_type;
 +
 +      obj = bpf_object__open_xattr(&open_attr);
 +      err = libbpf_get_error(obj);
 +      if (err)
 +              return libbpf_err(-ENOENT);
 +
 +      bpf_object__for_each_program(prog, obj) {
 +              enum bpf_attach_type attach_type = attr->expected_attach_type;
 +              /*
 +               * to preserve backwards compatibility, bpf_prog_load treats
 +               * attr->prog_type, if specified, as an override to whatever
 +               * bpf_object__open guessed
 +               */
 +              if (attr->prog_type != BPF_PROG_TYPE_UNSPEC) {
 +                      bpf_program__set_type(prog, attr->prog_type);
 +                      bpf_program__set_expected_attach_type(prog,
 +                                                            attach_type);
 +              }
 +              if (bpf_program__get_type(prog) == BPF_PROG_TYPE_UNSPEC) {
 +                      /*
 +                       * we haven't guessed from section name and user
 +                       * didn't provide a fallback type, too bad...
 +                       */
 +                      bpf_object__close(obj);
 +                      return libbpf_err(-EINVAL);
 +              }
 +
 +              prog->prog_ifindex = attr->ifindex;
 +              prog->log_level = attr->log_level;
 +              prog->prog_flags |= attr->prog_flags;
 +              if (!first_prog)
 +                      first_prog = prog;
 +      }
 +
 +      bpf_object__for_each_map(map, obj) {
 +              if (!bpf_map__is_offload_neutral(map))
 +                      map->map_ifindex = attr->ifindex;
 +      }
 +
 +      if (!first_prog) {
 +              pr_warn("object file doesn't contain bpf program\n");
 +              bpf_object__close(obj);
 +              return libbpf_err(-ENOENT);
 +      }
 +
 +      err = bpf_object__load(obj);
 +      if (err) {
 +              bpf_object__close(obj);
 +              return libbpf_err(err);
 +      }
 +
 +      *pobj = obj;
 +      *prog_fd = bpf_program__fd(first_prog);
 +      return 0;
 +}
 +
 +COMPAT_VERSION(bpf_prog_load_deprecated, bpf_prog_load, LIBBPF_0.0.1)
 +int bpf_prog_load_deprecated(const char *file, enum bpf_prog_type type,
 +                           struct bpf_object **pobj, int *prog_fd)
 +{
 +      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_xattr2(&attr, pobj, prog_fd);
 +}
 +
 +struct bpf_link {
 +      int (*detach)(struct bpf_link *link);
 +      void (*dealloc)(struct bpf_link *link);
 +      char *pin_path;         /* NULL, if not pinned */
 +      int fd;                 /* hook FD, -1 if not applicable */
 +      bool disconnected;
 +};
 +
 +/* Replace link's underlying BPF program with the new one */
 +int bpf_link__update_program(struct bpf_link *link, struct bpf_program *prog)
 +{
 +      int ret;
 +
 +      ret = bpf_link_update(bpf_link__fd(link), bpf_program__fd(prog), NULL);
 +      return libbpf_err_errno(ret);
 +}
 +
 +/* Release "ownership" of underlying BPF resource (typically, BPF program
 + * attached to some BPF hook, e.g., tracepoint, kprobe, etc). Disconnected
 + * link, when destructed through bpf_link__destroy() call won't attempt to
 + * detach/unregisted that BPF resource. This is useful in situations where,
 + * say, attached BPF program has to outlive userspace program that attached it
 + * in the system. Depending on type of BPF program, though, there might be
 + * additional steps (like pinning BPF program in BPF FS) necessary to ensure
 + * exit of userspace program doesn't trigger automatic detachment and clean up
 + * inside the kernel.
 + */
 +void bpf_link__disconnect(struct bpf_link *link)
 +{
 +      link->disconnected = true;
 +}
 +
 +int bpf_link__destroy(struct bpf_link *link)
 +{
 +      int err = 0;
 +
 +      if (IS_ERR_OR_NULL(link))
 +              return 0;
 +
 +      if (!link->disconnected && link->detach)
 +              err = link->detach(link);
 +      if (link->pin_path)
 +              free(link->pin_path);
 +      if (link->dealloc)
 +              link->dealloc(link);
 +      else
 +              free(link);
 +
 +      return libbpf_err(err);
 +}
 +
 +int bpf_link__fd(const struct bpf_link *link)
 +{
 +      return link->fd;
 +}
 +
 +const char *bpf_link__pin_path(const struct bpf_link *link)
 +{
 +      return link->pin_path;
 +}
 +
 +static int bpf_link__detach_fd(struct bpf_link *link)
 +{
 +      return libbpf_err_errno(close(link->fd));
 +}
 +
 +struct bpf_link *bpf_link__open(const char *path)
 +{
 +      struct bpf_link *link;
 +      int fd;
 +
 +      fd = bpf_obj_get(path);
 +      if (fd < 0) {
 +              fd = -errno;
 +              pr_warn("failed to open link at %s: %d\n", path, fd);
 +              return libbpf_err_ptr(fd);
 +      }
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link) {
 +              close(fd);
 +              return libbpf_err_ptr(-ENOMEM);
 +      }
 +      link->detach = &bpf_link__detach_fd;
 +      link->fd = fd;
 +
 +      link->pin_path = strdup(path);
 +      if (!link->pin_path) {
 +              bpf_link__destroy(link);
 +              return libbpf_err_ptr(-ENOMEM);
 +      }
 +
 +      return link;
 +}
 +
 +int bpf_link__detach(struct bpf_link *link)
 +{
 +      return bpf_link_detach(link->fd) ? -errno : 0;
 +}
 +
 +int bpf_link__pin(struct bpf_link *link, const char *path)
 +{
 +      int err;
 +
 +      if (link->pin_path)
 +              return libbpf_err(-EBUSY);
 +      err = make_parent_dir(path);
 +      if (err)
 +              return libbpf_err(err);
 +      err = check_path(path);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      link->pin_path = strdup(path);
 +      if (!link->pin_path)
 +              return libbpf_err(-ENOMEM);
 +
 +      if (bpf_obj_pin(link->fd, link->pin_path)) {
 +              err = -errno;
 +              zfree(&link->pin_path);
 +              return libbpf_err(err);
 +      }
 +
 +      pr_debug("link fd=%d: pinned at %s\n", link->fd, link->pin_path);
 +      return 0;
 +}
 +
 +int bpf_link__unpin(struct bpf_link *link)
 +{
 +      int err;
 +
 +      if (!link->pin_path)
 +              return libbpf_err(-EINVAL);
 +
 +      err = unlink(link->pin_path);
 +      if (err != 0)
 +              return -errno;
 +
 +      pr_debug("link fd=%d: unpinned from %s\n", link->fd, link->pin_path);
 +      zfree(&link->pin_path);
 +      return 0;
 +}
 +
 +struct bpf_link_perf {
 +      struct bpf_link link;
 +      int perf_event_fd;
 +      /* legacy kprobe support: keep track of probe identifier and type */
 +      char *legacy_probe_name;
 +      bool legacy_is_kprobe;
 +      bool legacy_is_retprobe;
 +};
 +
 +static int remove_kprobe_event_legacy(const char *probe_name, bool retprobe);
 +static int remove_uprobe_event_legacy(const char *probe_name, bool retprobe);
 +
 +static int bpf_link_perf_detach(struct bpf_link *link)
 +{
 +      struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
 +      int err = 0;
 +
 +      if (ioctl(perf_link->perf_event_fd, PERF_EVENT_IOC_DISABLE, 0) < 0)
 +              err = -errno;
 +
 +      if (perf_link->perf_event_fd != link->fd)
 +              close(perf_link->perf_event_fd);
 +      close(link->fd);
 +
 +      /* legacy uprobe/kprobe needs to be removed after perf event fd closure */
 +      if (perf_link->legacy_probe_name) {
 +              if (perf_link->legacy_is_kprobe) {
 +                      err = remove_kprobe_event_legacy(perf_link->legacy_probe_name,
 +                                                       perf_link->legacy_is_retprobe);
 +              } else {
 +                      err = remove_uprobe_event_legacy(perf_link->legacy_probe_name,
 +                                                       perf_link->legacy_is_retprobe);
 +              }
 +      }
 +
 +      return err;
 +}
 +
 +static void bpf_link_perf_dealloc(struct bpf_link *link)
 +{
 +      struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
 +
 +      free(perf_link->legacy_probe_name);
 +      free(perf_link);
 +}
 +
 +struct bpf_link *bpf_program__attach_perf_event_opts(const struct bpf_program *prog, int pfd,
 +                                                   const struct bpf_perf_event_opts *opts)
 +{
 +      char errmsg[STRERR_BUFSIZE];
 +      struct bpf_link_perf *link;
 +      int prog_fd, link_fd = -1, err;
 +
 +      if (!OPTS_VALID(opts, bpf_perf_event_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      if (pfd < 0) {
 +              pr_warn("prog '%s': invalid perf event FD %d\n",
 +                      prog->name, pfd);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +      prog_fd = bpf_program__fd(prog);
 +      if (prog_fd < 0) {
 +              pr_warn("prog '%s': can't attach BPF program w/o FD (did you load it?)\n",
 +                      prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link)
 +              return libbpf_err_ptr(-ENOMEM);
 +      link->link.detach = &bpf_link_perf_detach;
 +      link->link.dealloc = &bpf_link_perf_dealloc;
 +      link->perf_event_fd = pfd;
 +
 +      if (kernel_supports(prog->obj, FEAT_PERF_LINK)) {
 +              DECLARE_LIBBPF_OPTS(bpf_link_create_opts, link_opts,
 +                      .perf_event.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0));
 +
 +              link_fd = bpf_link_create(prog_fd, pfd, BPF_PERF_EVENT, &link_opts);
 +              if (link_fd < 0) {
 +                      err = -errno;
 +                      pr_warn("prog '%s': failed to create BPF link for perf_event FD %d: %d (%s)\n",
 +                              prog->name, pfd,
 +                              err, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +                      goto err_out;
 +              }
 +              link->link.fd = link_fd;
 +      } else {
 +              if (OPTS_GET(opts, bpf_cookie, 0)) {
 +                      pr_warn("prog '%s': user context value is not supported\n", prog->name);
 +                      err = -EOPNOTSUPP;
 +                      goto err_out;
 +              }
 +
 +              if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, prog_fd) < 0) {
 +                      err = -errno;
 +                      pr_warn("prog '%s': failed to attach to perf_event FD %d: %s\n",
 +                              prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +                      if (err == -EPROTO)
 +                              pr_warn("prog '%s': try add PERF_SAMPLE_CALLCHAIN to or remove exclude_callchain_[kernel|user] from pfd %d\n",
 +                                      prog->name, pfd);
 +                      goto err_out;
 +              }
 +              link->link.fd = pfd;
 +      }
 +      if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
 +              err = -errno;
 +              pr_warn("prog '%s': failed to enable perf_event FD %d: %s\n",
 +                      prog->name, pfd, libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              goto err_out;
 +      }
 +
 +      return &link->link;
 +err_out:
 +      if (link_fd >= 0)
 +              close(link_fd);
 +      free(link);
 +      return libbpf_err_ptr(err);
 +}
 +
 +struct bpf_link *bpf_program__attach_perf_event(const struct bpf_program *prog, int pfd)
 +{
 +      return bpf_program__attach_perf_event_opts(prog, pfd, NULL);
 +}
 +
 +/*
 + * this function is expected to parse integer in the range of [0, 2^31-1] from
 + * given file using scanf format string fmt. If actual parsed value is
 + * negative, the result might be indistinguishable from error
 + */
 +static int parse_uint_from_file(const char *file, const char *fmt)
 +{
 +      char buf[STRERR_BUFSIZE];
 +      int err, ret;
 +      FILE *f;
 +
 +      f = fopen(file, "r");
 +      if (!f) {
 +              err = -errno;
 +              pr_debug("failed to open '%s': %s\n", file,
 +                       libbpf_strerror_r(err, buf, sizeof(buf)));
 +              return err;
 +      }
 +      err = fscanf(f, fmt, &ret);
 +      if (err != 1) {
 +              err = err == EOF ? -EIO : -errno;
 +              pr_debug("failed to parse '%s': %s\n", file,
 +                      libbpf_strerror_r(err, buf, sizeof(buf)));
 +              fclose(f);
 +              return err;
 +      }
 +      fclose(f);
 +      return ret;
 +}
 +
 +static int determine_kprobe_perf_type(void)
 +{
 +      const char *file = "/sys/bus/event_source/devices/kprobe/type";
 +
 +      return parse_uint_from_file(file, "%d\n");
 +}
 +
 +static int determine_uprobe_perf_type(void)
 +{
 +      const char *file = "/sys/bus/event_source/devices/uprobe/type";
 +
 +      return parse_uint_from_file(file, "%d\n");
 +}
 +
 +static int determine_kprobe_retprobe_bit(void)
 +{
 +      const char *file = "/sys/bus/event_source/devices/kprobe/format/retprobe";
 +
 +      return parse_uint_from_file(file, "config:%d\n");
 +}
 +
 +static int determine_uprobe_retprobe_bit(void)
 +{
 +      const char *file = "/sys/bus/event_source/devices/uprobe/format/retprobe";
 +
 +      return parse_uint_from_file(file, "config:%d\n");
 +}
 +
 +#define PERF_UPROBE_REF_CTR_OFFSET_BITS 32
 +#define PERF_UPROBE_REF_CTR_OFFSET_SHIFT 32
 +
 +static int perf_event_open_probe(bool uprobe, bool retprobe, const char *name,
 +                               uint64_t offset, int pid, size_t ref_ctr_off)
 +{
 +      struct perf_event_attr attr = {};
 +      char errmsg[STRERR_BUFSIZE];
 +      int type, pfd, err;
 +
 +      if (ref_ctr_off >= (1ULL << PERF_UPROBE_REF_CTR_OFFSET_BITS))
 +              return -EINVAL;
 +
 +      type = uprobe ? determine_uprobe_perf_type()
 +                    : determine_kprobe_perf_type();
 +      if (type < 0) {
 +              pr_warn("failed to determine %s perf type: %s\n",
 +                      uprobe ? "uprobe" : "kprobe",
 +                      libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
 +              return type;
 +      }
 +      if (retprobe) {
 +              int bit = uprobe ? determine_uprobe_retprobe_bit()
 +                               : determine_kprobe_retprobe_bit();
 +
 +              if (bit < 0) {
 +                      pr_warn("failed to determine %s retprobe bit: %s\n",
 +                              uprobe ? "uprobe" : "kprobe",
 +                              libbpf_strerror_r(bit, errmsg, sizeof(errmsg)));
 +                      return bit;
 +              }
 +              attr.config |= 1 << bit;
 +      }
 +      attr.size = sizeof(attr);
 +      attr.type = type;
 +      attr.config |= (__u64)ref_ctr_off << PERF_UPROBE_REF_CTR_OFFSET_SHIFT;
 +      attr.config1 = ptr_to_u64(name); /* kprobe_func or uprobe_path */
 +      attr.config2 = offset;           /* kprobe_addr or probe_offset */
 +
 +      /* pid filter is meaningful only for uprobes */
 +      pfd = syscall(__NR_perf_event_open, &attr,
 +                    pid < 0 ? -1 : pid /* pid */,
 +                    pid == -1 ? 0 : -1 /* cpu */,
 +                    -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
 +      if (pfd < 0) {
 +              err = -errno;
 +              pr_warn("%s perf_event_open() failed: %s\n",
 +                      uprobe ? "uprobe" : "kprobe",
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              return err;
 +      }
 +      return pfd;
 +}
 +
 +static int append_to_file(const char *file, const char *fmt, ...)
 +{
 +      int fd, n, err = 0;
 +      va_list ap;
 +
 +      fd = open(file, O_WRONLY | O_APPEND | O_CLOEXEC, 0);
 +      if (fd < 0)
 +              return -errno;
 +
 +      va_start(ap, fmt);
 +      n = vdprintf(fd, fmt, ap);
 +      va_end(ap);
 +
 +      if (n < 0)
 +              err = -errno;
 +
 +      close(fd);
 +      return err;
 +}
 +
 +static void gen_kprobe_legacy_event_name(char *buf, size_t buf_sz,
 +                                       const char *kfunc_name, size_t offset)
 +{
-               legacy_probe = strdup(func_name);
++      static int index = 0;
++
++      snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx_%d", getpid(), kfunc_name, offset,
++               __sync_fetch_and_add(&index, 1));
 +}
 +
 +static int add_kprobe_event_legacy(const char *probe_name, bool retprobe,
 +                                 const char *kfunc_name, size_t offset)
 +{
 +      const char *file = "/sys/kernel/debug/tracing/kprobe_events";
 +
 +      return append_to_file(file, "%c:%s/%s %s+0x%zx",
 +                            retprobe ? 'r' : 'p',
 +                            retprobe ? "kretprobes" : "kprobes",
 +                            probe_name, kfunc_name, offset);
 +}
 +
 +static int remove_kprobe_event_legacy(const char *probe_name, bool retprobe)
 +{
 +      const char *file = "/sys/kernel/debug/tracing/kprobe_events";
 +
 +      return append_to_file(file, "-:%s/%s", retprobe ? "kretprobes" : "kprobes", probe_name);
 +}
 +
 +static int determine_kprobe_perf_type_legacy(const char *probe_name, bool retprobe)
 +{
 +      char file[256];
 +
 +      snprintf(file, sizeof(file),
 +               "/sys/kernel/debug/tracing/events/%s/%s/id",
 +               retprobe ? "kretprobes" : "kprobes", probe_name);
 +
 +      return parse_uint_from_file(file, "%d\n");
 +}
 +
 +static int perf_event_kprobe_open_legacy(const char *probe_name, bool retprobe,
 +                                       const char *kfunc_name, size_t offset, int pid)
 +{
 +      struct perf_event_attr attr = {};
 +      char errmsg[STRERR_BUFSIZE];
 +      int type, pfd, err;
 +
 +      err = add_kprobe_event_legacy(probe_name, retprobe, kfunc_name, offset);
 +      if (err < 0) {
 +              pr_warn("failed to add legacy kprobe event for '%s+0x%zx': %s\n",
 +                      kfunc_name, offset,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              return err;
 +      }
 +      type = determine_kprobe_perf_type_legacy(probe_name, retprobe);
 +      if (type < 0) {
 +              pr_warn("failed to determine legacy kprobe event id for '%s+0x%zx': %s\n",
 +                      kfunc_name, offset,
 +                      libbpf_strerror_r(type, errmsg, sizeof(errmsg)));
 +              return type;
 +      }
 +      attr.size = sizeof(attr);
 +      attr.config = type;
 +      attr.type = PERF_TYPE_TRACEPOINT;
 +
 +      pfd = syscall(__NR_perf_event_open, &attr,
 +                    pid < 0 ? -1 : pid, /* pid */
 +                    pid == -1 ? 0 : -1, /* cpu */
 +                    -1 /* group_fd */,  PERF_FLAG_FD_CLOEXEC);
 +      if (pfd < 0) {
 +              err = -errno;
 +              pr_warn("legacy kprobe perf_event_open() failed: %s\n",
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              return err;
 +      }
 +      return pfd;
 +}
 +
 +struct bpf_link *
 +bpf_program__attach_kprobe_opts(const struct bpf_program *prog,
 +                              const char *func_name,
 +                              const struct bpf_kprobe_opts *opts)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
 +      char errmsg[STRERR_BUFSIZE];
 +      char *legacy_probe = NULL;
 +      struct bpf_link *link;
 +      size_t offset;
 +      bool retprobe, legacy;
 +      int pfd, err;
 +
 +      if (!OPTS_VALID(opts, bpf_kprobe_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      retprobe = OPTS_GET(opts, retprobe, false);
 +      offset = OPTS_GET(opts, offset, 0);
 +      pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);
 +
 +      legacy = determine_kprobe_perf_type() < 0;
 +      if (!legacy) {
 +              pfd = perf_event_open_probe(false /* uprobe */, retprobe,
 +                                          func_name, offset,
 +                                          -1 /* pid */, 0 /* ref_ctr_off */);
 +      } else {
 +              char probe_name[256];
 +
 +              gen_kprobe_legacy_event_name(probe_name, sizeof(probe_name),
 +                                           func_name, offset);
 +
- enum bpf_perf_event_ret
- bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
-                          void **copy_mem, size_t *copy_size,
-                          bpf_perf_event_print_t fn, void *private_data)
++              legacy_probe = strdup(probe_name);
 +              if (!legacy_probe)
 +                      return libbpf_err_ptr(-ENOMEM);
 +
 +              pfd = perf_event_kprobe_open_legacy(legacy_probe, retprobe, func_name,
 +                                                  offset, -1 /* pid */);
 +      }
 +      if (pfd < 0) {
 +              err = -errno;
 +              pr_warn("prog '%s': failed to create %s '%s+0x%zx' perf event: %s\n",
 +                      prog->name, retprobe ? "kretprobe" : "kprobe",
 +                      func_name, offset,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              goto err_out;
 +      }
 +      link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts);
 +      err = libbpf_get_error(link);
 +      if (err) {
 +              close(pfd);
 +              pr_warn("prog '%s': failed to attach to %s '%s+0x%zx': %s\n",
 +                      prog->name, retprobe ? "kretprobe" : "kprobe",
 +                      func_name, offset,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              goto err_out;
 +      }
 +      if (legacy) {
 +              struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
 +
 +              perf_link->legacy_probe_name = legacy_probe;
 +              perf_link->legacy_is_kprobe = true;
 +              perf_link->legacy_is_retprobe = retprobe;
 +      }
 +
 +      return link;
 +err_out:
 +      free(legacy_probe);
 +      return libbpf_err_ptr(err);
 +}
 +
 +struct bpf_link *bpf_program__attach_kprobe(const struct bpf_program *prog,
 +                                          bool retprobe,
 +                                          const char *func_name)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts,
 +              .retprobe = retprobe,
 +      );
 +
 +      return bpf_program__attach_kprobe_opts(prog, func_name, &opts);
 +}
 +
 +static struct bpf_link *attach_kprobe(const struct bpf_program *prog, long cookie)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_kprobe_opts, opts);
 +      unsigned long offset = 0;
 +      struct bpf_link *link;
 +      const char *func_name;
 +      char *func;
 +      int n, err;
 +
 +      opts.retprobe = str_has_pfx(prog->sec_name, "kretprobe/");
 +      if (opts.retprobe)
 +              func_name = prog->sec_name + sizeof("kretprobe/") - 1;
 +      else
 +              func_name = prog->sec_name + sizeof("kprobe/") - 1;
 +
 +      n = sscanf(func_name, "%m[a-zA-Z0-9_.]+%li", &func, &offset);
 +      if (n < 1) {
 +              err = -EINVAL;
 +              pr_warn("kprobe name is invalid: %s\n", func_name);
 +              return libbpf_err_ptr(err);
 +      }
 +      if (opts.retprobe && offset != 0) {
 +              free(func);
 +              err = -EINVAL;
 +              pr_warn("kretprobes do not support offset specification\n");
 +              return libbpf_err_ptr(err);
 +      }
 +
 +      opts.offset = offset;
 +      link = bpf_program__attach_kprobe_opts(prog, func, &opts);
 +      free(func);
 +      return link;
 +}
 +
 +static void gen_uprobe_legacy_event_name(char *buf, size_t buf_sz,
 +                                       const char *binary_path, uint64_t offset)
 +{
 +      int i;
 +
 +      snprintf(buf, buf_sz, "libbpf_%u_%s_0x%zx", getpid(), binary_path, (size_t)offset);
 +
 +      /* sanitize binary_path in the probe name */
 +      for (i = 0; buf[i]; i++) {
 +              if (!isalnum(buf[i]))
 +                      buf[i] = '_';
 +      }
 +}
 +
 +static inline int add_uprobe_event_legacy(const char *probe_name, bool retprobe,
 +                                        const char *binary_path, size_t offset)
 +{
 +      const char *file = "/sys/kernel/debug/tracing/uprobe_events";
 +
 +      return append_to_file(file, "%c:%s/%s %s:0x%zx",
 +                            retprobe ? 'r' : 'p',
 +                            retprobe ? "uretprobes" : "uprobes",
 +                            probe_name, binary_path, offset);
 +}
 +
 +static inline int remove_uprobe_event_legacy(const char *probe_name, bool retprobe)
 +{
 +      const char *file = "/sys/kernel/debug/tracing/uprobe_events";
 +
 +      return append_to_file(file, "-:%s/%s", retprobe ? "uretprobes" : "uprobes", probe_name);
 +}
 +
 +static int determine_uprobe_perf_type_legacy(const char *probe_name, bool retprobe)
 +{
 +      char file[512];
 +
 +      snprintf(file, sizeof(file),
 +               "/sys/kernel/debug/tracing/events/%s/%s/id",
 +               retprobe ? "uretprobes" : "uprobes", probe_name);
 +
 +      return parse_uint_from_file(file, "%d\n");
 +}
 +
 +static int perf_event_uprobe_open_legacy(const char *probe_name, bool retprobe,
 +                                       const char *binary_path, size_t offset, int pid)
 +{
 +      struct perf_event_attr attr;
 +      int type, pfd, err;
 +
 +      err = add_uprobe_event_legacy(probe_name, retprobe, binary_path, offset);
 +      if (err < 0) {
 +              pr_warn("failed to add legacy uprobe event for %s:0x%zx: %d\n",
 +                      binary_path, (size_t)offset, err);
 +              return err;
 +      }
 +      type = determine_uprobe_perf_type_legacy(probe_name, retprobe);
 +      if (type < 0) {
 +              pr_warn("failed to determine legacy uprobe event id for %s:0x%zx: %d\n",
 +                      binary_path, offset, err);
 +              return type;
 +      }
 +
 +      memset(&attr, 0, sizeof(attr));
 +      attr.size = sizeof(attr);
 +      attr.config = type;
 +      attr.type = PERF_TYPE_TRACEPOINT;
 +
 +      pfd = syscall(__NR_perf_event_open, &attr,
 +                    pid < 0 ? -1 : pid, /* pid */
 +                    pid == -1 ? 0 : -1, /* cpu */
 +                    -1 /* group_fd */,  PERF_FLAG_FD_CLOEXEC);
 +      if (pfd < 0) {
 +              err = -errno;
 +              pr_warn("legacy uprobe perf_event_open() failed: %d\n", err);
 +              return err;
 +      }
 +      return pfd;
 +}
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
 +                              const char *binary_path, size_t func_offset,
 +                              const struct bpf_uprobe_opts *opts)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
 +      char errmsg[STRERR_BUFSIZE], *legacy_probe = NULL;
 +      struct bpf_link *link;
 +      size_t ref_ctr_off;
 +      int pfd, err;
 +      bool retprobe, legacy;
 +
 +      if (!OPTS_VALID(opts, bpf_uprobe_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      retprobe = OPTS_GET(opts, retprobe, false);
 +      ref_ctr_off = OPTS_GET(opts, ref_ctr_offset, 0);
 +      pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);
 +
 +      legacy = determine_uprobe_perf_type() < 0;
 +      if (!legacy) {
 +              pfd = perf_event_open_probe(true /* uprobe */, retprobe, binary_path,
 +                                          func_offset, pid, ref_ctr_off);
 +      } else {
 +              char probe_name[512];
 +
 +              if (ref_ctr_off)
 +                      return libbpf_err_ptr(-EINVAL);
 +
 +              gen_uprobe_legacy_event_name(probe_name, sizeof(probe_name),
 +                                           binary_path, func_offset);
 +
 +              legacy_probe = strdup(probe_name);
 +              if (!legacy_probe)
 +                      return libbpf_err_ptr(-ENOMEM);
 +
 +              pfd = perf_event_uprobe_open_legacy(legacy_probe, retprobe,
 +                                                  binary_path, func_offset, pid);
 +      }
 +      if (pfd < 0) {
 +              err = -errno;
 +              pr_warn("prog '%s': failed to create %s '%s:0x%zx' perf event: %s\n",
 +                      prog->name, retprobe ? "uretprobe" : "uprobe",
 +                      binary_path, func_offset,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              goto err_out;
 +      }
 +
 +      link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts);
 +      err = libbpf_get_error(link);
 +      if (err) {
 +              close(pfd);
 +              pr_warn("prog '%s': failed to attach to %s '%s:0x%zx': %s\n",
 +                      prog->name, retprobe ? "uretprobe" : "uprobe",
 +                      binary_path, func_offset,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              goto err_out;
 +      }
 +      if (legacy) {
 +              struct bpf_link_perf *perf_link = container_of(link, struct bpf_link_perf, link);
 +
 +              perf_link->legacy_probe_name = legacy_probe;
 +              perf_link->legacy_is_kprobe = false;
 +              perf_link->legacy_is_retprobe = retprobe;
 +      }
 +      return link;
 +err_out:
 +      free(legacy_probe);
 +      return libbpf_err_ptr(err);
 +
 +}
 +
 +struct bpf_link *bpf_program__attach_uprobe(const struct bpf_program *prog,
 +                                          bool retprobe, pid_t pid,
 +                                          const char *binary_path,
 +                                          size_t func_offset)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_uprobe_opts, opts, .retprobe = retprobe);
 +
 +      return bpf_program__attach_uprobe_opts(prog, pid, binary_path, func_offset, &opts);
 +}
 +
 +static int determine_tracepoint_id(const char *tp_category,
 +                                 const char *tp_name)
 +{
 +      char file[PATH_MAX];
 +      int ret;
 +
 +      ret = snprintf(file, sizeof(file),
 +                     "/sys/kernel/debug/tracing/events/%s/%s/id",
 +                     tp_category, tp_name);
 +      if (ret < 0)
 +              return -errno;
 +      if (ret >= sizeof(file)) {
 +              pr_debug("tracepoint %s/%s path is too long\n",
 +                       tp_category, tp_name);
 +              return -E2BIG;
 +      }
 +      return parse_uint_from_file(file, "%d\n");
 +}
 +
 +static int perf_event_open_tracepoint(const char *tp_category,
 +                                    const char *tp_name)
 +{
 +      struct perf_event_attr attr = {};
 +      char errmsg[STRERR_BUFSIZE];
 +      int tp_id, pfd, err;
 +
 +      tp_id = determine_tracepoint_id(tp_category, tp_name);
 +      if (tp_id < 0) {
 +              pr_warn("failed to determine tracepoint '%s/%s' perf event ID: %s\n",
 +                      tp_category, tp_name,
 +                      libbpf_strerror_r(tp_id, errmsg, sizeof(errmsg)));
 +              return tp_id;
 +      }
 +
 +      attr.type = PERF_TYPE_TRACEPOINT;
 +      attr.size = sizeof(attr);
 +      attr.config = tp_id;
 +
 +      pfd = syscall(__NR_perf_event_open, &attr, -1 /* pid */, 0 /* cpu */,
 +                    -1 /* group_fd */, PERF_FLAG_FD_CLOEXEC);
 +      if (pfd < 0) {
 +              err = -errno;
 +              pr_warn("tracepoint '%s/%s' perf_event_open() failed: %s\n",
 +                      tp_category, tp_name,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              return err;
 +      }
 +      return pfd;
 +}
 +
 +struct bpf_link *bpf_program__attach_tracepoint_opts(const struct bpf_program *prog,
 +                                                   const char *tp_category,
 +                                                   const char *tp_name,
 +                                                   const struct bpf_tracepoint_opts *opts)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_perf_event_opts, pe_opts);
 +      char errmsg[STRERR_BUFSIZE];
 +      struct bpf_link *link;
 +      int pfd, err;
 +
 +      if (!OPTS_VALID(opts, bpf_tracepoint_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      pe_opts.bpf_cookie = OPTS_GET(opts, bpf_cookie, 0);
 +
 +      pfd = perf_event_open_tracepoint(tp_category, tp_name);
 +      if (pfd < 0) {
 +              pr_warn("prog '%s': failed to create tracepoint '%s/%s' perf event: %s\n",
 +                      prog->name, tp_category, tp_name,
 +                      libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
 +              return libbpf_err_ptr(pfd);
 +      }
 +      link = bpf_program__attach_perf_event_opts(prog, pfd, &pe_opts);
 +      err = libbpf_get_error(link);
 +      if (err) {
 +              close(pfd);
 +              pr_warn("prog '%s': failed to attach to tracepoint '%s/%s': %s\n",
 +                      prog->name, tp_category, tp_name,
 +                      libbpf_strerror_r(err, errmsg, sizeof(errmsg)));
 +              return libbpf_err_ptr(err);
 +      }
 +      return link;
 +}
 +
 +struct bpf_link *bpf_program__attach_tracepoint(const struct bpf_program *prog,
 +                                              const char *tp_category,
 +                                              const char *tp_name)
 +{
 +      return bpf_program__attach_tracepoint_opts(prog, tp_category, tp_name, NULL);
 +}
 +
 +static struct bpf_link *attach_tp(const struct bpf_program *prog, long cookie)
 +{
 +      char *sec_name, *tp_cat, *tp_name;
 +      struct bpf_link *link;
 +
 +      sec_name = strdup(prog->sec_name);
 +      if (!sec_name)
 +              return libbpf_err_ptr(-ENOMEM);
 +
 +      /* extract "tp/<category>/<name>" or "tracepoint/<category>/<name>" */
 +      if (str_has_pfx(prog->sec_name, "tp/"))
 +              tp_cat = sec_name + sizeof("tp/") - 1;
 +      else
 +              tp_cat = sec_name + sizeof("tracepoint/") - 1;
 +      tp_name = strchr(tp_cat, '/');
 +      if (!tp_name) {
 +              free(sec_name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +      *tp_name = '\0';
 +      tp_name++;
 +
 +      link = bpf_program__attach_tracepoint(prog, tp_cat, tp_name);
 +      free(sec_name);
 +      return link;
 +}
 +
 +struct bpf_link *bpf_program__attach_raw_tracepoint(const struct bpf_program *prog,
 +                                                  const char *tp_name)
 +{
 +      char errmsg[STRERR_BUFSIZE];
 +      struct bpf_link *link;
 +      int prog_fd, pfd;
 +
 +      prog_fd = bpf_program__fd(prog);
 +      if (prog_fd < 0) {
 +              pr_warn("prog '%s': can't attach before loaded\n", prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link)
 +              return libbpf_err_ptr(-ENOMEM);
 +      link->detach = &bpf_link__detach_fd;
 +
 +      pfd = bpf_raw_tracepoint_open(tp_name, prog_fd);
 +      if (pfd < 0) {
 +              pfd = -errno;
 +              free(link);
 +              pr_warn("prog '%s': failed to attach to raw tracepoint '%s': %s\n",
 +                      prog->name, tp_name, libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
 +              return libbpf_err_ptr(pfd);
 +      }
 +      link->fd = pfd;
 +      return link;
 +}
 +
 +static struct bpf_link *attach_raw_tp(const struct bpf_program *prog, long cookie)
 +{
 +      static const char *const prefixes[] = {
 +              "raw_tp/",
 +              "raw_tracepoint/",
 +              "raw_tp.w/",
 +              "raw_tracepoint.w/",
 +      };
 +      size_t i;
 +      const char *tp_name = NULL;
 +
 +      for (i = 0; i < ARRAY_SIZE(prefixes); i++) {
 +              if (str_has_pfx(prog->sec_name, prefixes[i])) {
 +                      tp_name = prog->sec_name + strlen(prefixes[i]);
 +                      break;
 +              }
 +      }
 +      if (!tp_name) {
 +              pr_warn("prog '%s': invalid section name '%s'\n",
 +                      prog->name, prog->sec_name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      return bpf_program__attach_raw_tracepoint(prog, tp_name);
 +}
 +
 +/* Common logic for all BPF program types that attach to a btf_id */
 +static struct bpf_link *bpf_program__attach_btf_id(const struct bpf_program *prog)
 +{
 +      char errmsg[STRERR_BUFSIZE];
 +      struct bpf_link *link;
 +      int prog_fd, pfd;
 +
 +      prog_fd = bpf_program__fd(prog);
 +      if (prog_fd < 0) {
 +              pr_warn("prog '%s': can't attach before loaded\n", prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link)
 +              return libbpf_err_ptr(-ENOMEM);
 +      link->detach = &bpf_link__detach_fd;
 +
 +      pfd = bpf_raw_tracepoint_open(NULL, prog_fd);
 +      if (pfd < 0) {
 +              pfd = -errno;
 +              free(link);
 +              pr_warn("prog '%s': failed to attach: %s\n",
 +                      prog->name, libbpf_strerror_r(pfd, errmsg, sizeof(errmsg)));
 +              return libbpf_err_ptr(pfd);
 +      }
 +      link->fd = pfd;
 +      return (struct bpf_link *)link;
 +}
 +
 +struct bpf_link *bpf_program__attach_trace(const struct bpf_program *prog)
 +{
 +      return bpf_program__attach_btf_id(prog);
 +}
 +
 +struct bpf_link *bpf_program__attach_lsm(const struct bpf_program *prog)
 +{
 +      return bpf_program__attach_btf_id(prog);
 +}
 +
 +static struct bpf_link *attach_trace(const struct bpf_program *prog, long cookie)
 +{
 +      return bpf_program__attach_trace(prog);
 +}
 +
 +static struct bpf_link *attach_lsm(const struct bpf_program *prog, long cookie)
 +{
 +      return bpf_program__attach_lsm(prog);
 +}
 +
 +static struct bpf_link *
 +bpf_program__attach_fd(const struct bpf_program *prog, int target_fd, int btf_id,
 +                     const char *target_name)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_link_create_opts, opts,
 +                          .target_btf_id = btf_id);
 +      enum bpf_attach_type attach_type;
 +      char errmsg[STRERR_BUFSIZE];
 +      struct bpf_link *link;
 +      int prog_fd, link_fd;
 +
 +      prog_fd = bpf_program__fd(prog);
 +      if (prog_fd < 0) {
 +              pr_warn("prog '%s': can't attach before loaded\n", prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link)
 +              return libbpf_err_ptr(-ENOMEM);
 +      link->detach = &bpf_link__detach_fd;
 +
 +      attach_type = bpf_program__get_expected_attach_type(prog);
 +      link_fd = bpf_link_create(prog_fd, target_fd, attach_type, &opts);
 +      if (link_fd < 0) {
 +              link_fd = -errno;
 +              free(link);
 +              pr_warn("prog '%s': failed to attach to %s: %s\n",
 +                      prog->name, target_name,
 +                      libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg)));
 +              return libbpf_err_ptr(link_fd);
 +      }
 +      link->fd = link_fd;
 +      return link;
 +}
 +
 +struct bpf_link *
 +bpf_program__attach_cgroup(const struct bpf_program *prog, int cgroup_fd)
 +{
 +      return bpf_program__attach_fd(prog, cgroup_fd, 0, "cgroup");
 +}
 +
 +struct bpf_link *
 +bpf_program__attach_netns(const struct bpf_program *prog, int netns_fd)
 +{
 +      return bpf_program__attach_fd(prog, netns_fd, 0, "netns");
 +}
 +
 +struct bpf_link *bpf_program__attach_xdp(const struct bpf_program *prog, int ifindex)
 +{
 +      /* target_fd/target_ifindex use the same field in LINK_CREATE */
 +      return bpf_program__attach_fd(prog, ifindex, 0, "xdp");
 +}
 +
 +struct bpf_link *bpf_program__attach_freplace(const struct bpf_program *prog,
 +                                            int target_fd,
 +                                            const char *attach_func_name)
 +{
 +      int btf_id;
 +
 +      if (!!target_fd != !!attach_func_name) {
 +              pr_warn("prog '%s': supply none or both of target_fd and attach_func_name\n",
 +                      prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      if (prog->type != BPF_PROG_TYPE_EXT) {
 +              pr_warn("prog '%s': only BPF_PROG_TYPE_EXT can attach as freplace",
 +                      prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      if (target_fd) {
 +              btf_id = libbpf_find_prog_btf_id(attach_func_name, target_fd);
 +              if (btf_id < 0)
 +                      return libbpf_err_ptr(btf_id);
 +
 +              return bpf_program__attach_fd(prog, target_fd, btf_id, "freplace");
 +      } else {
 +              /* no target, so use raw_tracepoint_open for compatibility
 +               * with old kernels
 +               */
 +              return bpf_program__attach_trace(prog);
 +      }
 +}
 +
 +struct bpf_link *
 +bpf_program__attach_iter(const struct bpf_program *prog,
 +                       const struct bpf_iter_attach_opts *opts)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_link_create_opts, link_create_opts);
 +      char errmsg[STRERR_BUFSIZE];
 +      struct bpf_link *link;
 +      int prog_fd, link_fd;
 +      __u32 target_fd = 0;
 +
 +      if (!OPTS_VALID(opts, bpf_iter_attach_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      link_create_opts.iter_info = OPTS_GET(opts, link_info, (void *)0);
 +      link_create_opts.iter_info_len = OPTS_GET(opts, link_info_len, 0);
 +
 +      prog_fd = bpf_program__fd(prog);
 +      if (prog_fd < 0) {
 +              pr_warn("prog '%s': can't attach before loaded\n", prog->name);
 +              return libbpf_err_ptr(-EINVAL);
 +      }
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link)
 +              return libbpf_err_ptr(-ENOMEM);
 +      link->detach = &bpf_link__detach_fd;
 +
 +      link_fd = bpf_link_create(prog_fd, target_fd, BPF_TRACE_ITER,
 +                                &link_create_opts);
 +      if (link_fd < 0) {
 +              link_fd = -errno;
 +              free(link);
 +              pr_warn("prog '%s': failed to attach to iterator: %s\n",
 +                      prog->name, libbpf_strerror_r(link_fd, errmsg, sizeof(errmsg)));
 +              return libbpf_err_ptr(link_fd);
 +      }
 +      link->fd = link_fd;
 +      return link;
 +}
 +
 +static struct bpf_link *attach_iter(const struct bpf_program *prog, long cookie)
 +{
 +      return bpf_program__attach_iter(prog, NULL);
 +}
 +
 +struct bpf_link *bpf_program__attach(const struct bpf_program *prog)
 +{
 +      if (!prog->sec_def || !prog->sec_def->attach_fn)
 +              return libbpf_err_ptr(-ESRCH);
 +
 +      return prog->sec_def->attach_fn(prog, prog->sec_def->cookie);
 +}
 +
 +static int bpf_link__detach_struct_ops(struct bpf_link *link)
 +{
 +      __u32 zero = 0;
 +
 +      if (bpf_map_delete_elem(link->fd, &zero))
 +              return -errno;
 +
 +      return 0;
 +}
 +
 +struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map)
 +{
 +      struct bpf_struct_ops *st_ops;
 +      struct bpf_link *link;
 +      __u32 i, zero = 0;
 +      int err;
 +
 +      if (!bpf_map__is_struct_ops(map) || map->fd == -1)
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      link = calloc(1, sizeof(*link));
 +      if (!link)
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      st_ops = map->st_ops;
 +      for (i = 0; i < btf_vlen(st_ops->type); i++) {
 +              struct bpf_program *prog = st_ops->progs[i];
 +              void *kern_data;
 +              int prog_fd;
 +
 +              if (!prog)
 +                      continue;
 +
 +              prog_fd = bpf_program__fd(prog);
 +              kern_data = st_ops->kern_vdata + st_ops->kern_func_off[i];
 +              *(unsigned long *)kern_data = prog_fd;
 +      }
 +
 +      err = bpf_map_update_elem(map->fd, &zero, st_ops->kern_vdata, 0);
 +      if (err) {
 +              err = -errno;
 +              free(link);
 +              return libbpf_err_ptr(err);
 +      }
 +
 +      link->detach = bpf_link__detach_struct_ops;
 +      link->fd = map->fd;
 +
 +      return link;
 +}
 +
-       ret = bpf_perf_event_read_simple(cpu_buf->base, pb->mmap_size,
-                                        pb->page_size, &cpu_buf->buf,
-                                        &cpu_buf->buf_size,
-                                        perf_buffer__process_record, cpu_buf);
++static enum bpf_perf_event_ret
++perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
++                     void **copy_mem, size_t *copy_size,
++                     bpf_perf_event_print_t fn, void *private_data)
 +{
 +      struct perf_event_mmap_page *header = mmap_mem;
 +      __u64 data_head = ring_buffer_read_head(header);
 +      __u64 data_tail = header->data_tail;
 +      void *base = ((__u8 *)header) + page_size;
 +      int ret = LIBBPF_PERF_EVENT_CONT;
 +      struct perf_event_header *ehdr;
 +      size_t ehdr_size;
 +
 +      while (data_head != data_tail) {
 +              ehdr = base + (data_tail & (mmap_size - 1));
 +              ehdr_size = ehdr->size;
 +
 +              if (((void *)ehdr) + ehdr_size > base + mmap_size) {
 +                      void *copy_start = ehdr;
 +                      size_t len_first = base + mmap_size - copy_start;
 +                      size_t len_secnd = ehdr_size - len_first;
 +
 +                      if (*copy_size < ehdr_size) {
 +                              free(*copy_mem);
 +                              *copy_mem = malloc(ehdr_size);
 +                              if (!*copy_mem) {
 +                                      *copy_size = 0;
 +                                      ret = LIBBPF_PERF_EVENT_ERROR;
 +                                      break;
 +                              }
 +                              *copy_size = ehdr_size;
 +                      }
 +
 +                      memcpy(*copy_mem, copy_start, len_first);
 +                      memcpy(*copy_mem + len_first, base, len_secnd);
 +                      ehdr = *copy_mem;
 +              }
 +
 +              ret = fn(ehdr, private_data);
 +              data_tail += ehdr_size;
 +              if (ret != LIBBPF_PERF_EVENT_CONT)
 +                      break;
 +      }
 +
 +      ring_buffer_write_tail(header, data_tail);
 +      return libbpf_err(ret);
 +}
 +
++__attribute__((alias("perf_event_read_simple")))
++enum bpf_perf_event_ret
++bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
++                         void **copy_mem, size_t *copy_size,
++                         bpf_perf_event_print_t fn, void *private_data);
++
 +struct perf_buffer;
 +
 +struct perf_buffer_params {
 +      struct perf_event_attr *attr;
 +      /* if event_cb is specified, it takes precendence */
 +      perf_buffer_event_fn event_cb;
 +      /* sample_cb and lost_cb are higher-level common-case callbacks */
 +      perf_buffer_sample_fn sample_cb;
 +      perf_buffer_lost_fn lost_cb;
 +      void *ctx;
 +      int cpu_cnt;
 +      int *cpus;
 +      int *map_keys;
 +};
 +
 +struct perf_cpu_buf {
 +      struct perf_buffer *pb;
 +      void *base; /* mmap()'ed memory */
 +      void *buf; /* for reconstructing segmented data */
 +      size_t buf_size;
 +      int fd;
 +      int cpu;
 +      int map_key;
 +};
 +
 +struct perf_buffer {
 +      perf_buffer_event_fn event_cb;
 +      perf_buffer_sample_fn sample_cb;
 +      perf_buffer_lost_fn lost_cb;
 +      void *ctx; /* passed into callbacks */
 +
 +      size_t page_size;
 +      size_t mmap_size;
 +      struct perf_cpu_buf **cpu_bufs;
 +      struct epoll_event *events;
 +      int cpu_cnt; /* number of allocated CPU buffers */
 +      int epoll_fd; /* perf event FD */
 +      int map_fd; /* BPF_MAP_TYPE_PERF_EVENT_ARRAY BPF map FD */
 +};
 +
 +static void perf_buffer__free_cpu_buf(struct perf_buffer *pb,
 +                                    struct perf_cpu_buf *cpu_buf)
 +{
 +      if (!cpu_buf)
 +              return;
 +      if (cpu_buf->base &&
 +          munmap(cpu_buf->base, pb->mmap_size + pb->page_size))
 +              pr_warn("failed to munmap cpu_buf #%d\n", cpu_buf->cpu);
 +      if (cpu_buf->fd >= 0) {
 +              ioctl(cpu_buf->fd, PERF_EVENT_IOC_DISABLE, 0);
 +              close(cpu_buf->fd);
 +      }
 +      free(cpu_buf->buf);
 +      free(cpu_buf);
 +}
 +
 +void perf_buffer__free(struct perf_buffer *pb)
 +{
 +      int i;
 +
 +      if (IS_ERR_OR_NULL(pb))
 +              return;
 +      if (pb->cpu_bufs) {
 +              for (i = 0; i < pb->cpu_cnt; i++) {
 +                      struct perf_cpu_buf *cpu_buf = pb->cpu_bufs[i];
 +
 +                      if (!cpu_buf)
 +                              continue;
 +
 +                      bpf_map_delete_elem(pb->map_fd, &cpu_buf->map_key);
 +                      perf_buffer__free_cpu_buf(pb, cpu_buf);
 +              }
 +              free(pb->cpu_bufs);
 +      }
 +      if (pb->epoll_fd >= 0)
 +              close(pb->epoll_fd);
 +      free(pb->events);
 +      free(pb);
 +}
 +
 +static struct perf_cpu_buf *
 +perf_buffer__open_cpu_buf(struct perf_buffer *pb, struct perf_event_attr *attr,
 +                        int cpu, int map_key)
 +{
 +      struct perf_cpu_buf *cpu_buf;
 +      char msg[STRERR_BUFSIZE];
 +      int err;
 +
 +      cpu_buf = calloc(1, sizeof(*cpu_buf));
 +      if (!cpu_buf)
 +              return ERR_PTR(-ENOMEM);
 +
 +      cpu_buf->pb = pb;
 +      cpu_buf->cpu = cpu;
 +      cpu_buf->map_key = map_key;
 +
 +      cpu_buf->fd = syscall(__NR_perf_event_open, attr, -1 /* pid */, cpu,
 +                            -1, PERF_FLAG_FD_CLOEXEC);
 +      if (cpu_buf->fd < 0) {
 +              err = -errno;
 +              pr_warn("failed to open perf buffer event on cpu #%d: %s\n",
 +                      cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
 +              goto error;
 +      }
 +
 +      cpu_buf->base = mmap(NULL, pb->mmap_size + pb->page_size,
 +                           PROT_READ | PROT_WRITE, MAP_SHARED,
 +                           cpu_buf->fd, 0);
 +      if (cpu_buf->base == MAP_FAILED) {
 +              cpu_buf->base = NULL;
 +              err = -errno;
 +              pr_warn("failed to mmap perf buffer on cpu #%d: %s\n",
 +                      cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
 +              goto error;
 +      }
 +
 +      if (ioctl(cpu_buf->fd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
 +              err = -errno;
 +              pr_warn("failed to enable perf buffer event on cpu #%d: %s\n",
 +                      cpu, libbpf_strerror_r(err, msg, sizeof(msg)));
 +              goto error;
 +      }
 +
 +      return cpu_buf;
 +
 +error:
 +      perf_buffer__free_cpu_buf(pb, cpu_buf);
 +      return (struct perf_cpu_buf *)ERR_PTR(err);
 +}
 +
 +static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
 +                                            struct perf_buffer_params *p);
 +
 +DEFAULT_VERSION(perf_buffer__new_v0_6_0, perf_buffer__new, LIBBPF_0.6.0)
 +struct perf_buffer *perf_buffer__new_v0_6_0(int map_fd, size_t page_cnt,
 +                                          perf_buffer_sample_fn sample_cb,
 +                                          perf_buffer_lost_fn lost_cb,
 +                                          void *ctx,
 +                                          const struct perf_buffer_opts *opts)
 +{
 +      struct perf_buffer_params p = {};
 +      struct perf_event_attr attr = {};
 +
 +      if (!OPTS_VALID(opts, perf_buffer_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      attr.config = PERF_COUNT_SW_BPF_OUTPUT;
 +      attr.type = PERF_TYPE_SOFTWARE;
 +      attr.sample_type = PERF_SAMPLE_RAW;
 +      attr.sample_period = 1;
 +      attr.wakeup_events = 1;
 +
 +      p.attr = &attr;
 +      p.sample_cb = sample_cb;
 +      p.lost_cb = lost_cb;
 +      p.ctx = ctx;
 +
 +      return libbpf_ptr(__perf_buffer__new(map_fd, page_cnt, &p));
 +}
 +
 +COMPAT_VERSION(perf_buffer__new_deprecated, perf_buffer__new, LIBBPF_0.0.4)
 +struct perf_buffer *perf_buffer__new_deprecated(int map_fd, size_t page_cnt,
 +                                              const struct perf_buffer_opts *opts)
 +{
 +      return perf_buffer__new_v0_6_0(map_fd, page_cnt,
 +                                     opts ? opts->sample_cb : NULL,
 +                                     opts ? opts->lost_cb : NULL,
 +                                     opts ? opts->ctx : NULL,
 +                                     NULL);
 +}
 +
 +DEFAULT_VERSION(perf_buffer__new_raw_v0_6_0, perf_buffer__new_raw, LIBBPF_0.6.0)
 +struct perf_buffer *perf_buffer__new_raw_v0_6_0(int map_fd, size_t page_cnt,
 +                                              struct perf_event_attr *attr,
 +                                              perf_buffer_event_fn event_cb, void *ctx,
 +                                              const struct perf_buffer_raw_opts *opts)
 +{
 +      struct perf_buffer_params p = {};
 +
 +      if (page_cnt == 0 || !attr)
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      if (!OPTS_VALID(opts, perf_buffer_raw_opts))
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      p.attr = attr;
 +      p.event_cb = event_cb;
 +      p.ctx = ctx;
 +      p.cpu_cnt = OPTS_GET(opts, cpu_cnt, 0);
 +      p.cpus = OPTS_GET(opts, cpus, NULL);
 +      p.map_keys = OPTS_GET(opts, map_keys, NULL);
 +
 +      return libbpf_ptr(__perf_buffer__new(map_fd, page_cnt, &p));
 +}
 +
 +COMPAT_VERSION(perf_buffer__new_raw_deprecated, perf_buffer__new_raw, LIBBPF_0.0.4)
 +struct perf_buffer *perf_buffer__new_raw_deprecated(int map_fd, size_t page_cnt,
 +                                                  const struct perf_buffer_raw_opts *opts)
 +{
 +      LIBBPF_OPTS(perf_buffer_raw_opts, inner_opts,
 +              .cpu_cnt = opts->cpu_cnt,
 +              .cpus = opts->cpus,
 +              .map_keys = opts->map_keys,
 +      );
 +
 +      return perf_buffer__new_raw_v0_6_0(map_fd, page_cnt, opts->attr,
 +                                         opts->event_cb, opts->ctx, &inner_opts);
 +}
 +
 +static struct perf_buffer *__perf_buffer__new(int map_fd, size_t page_cnt,
 +                                            struct perf_buffer_params *p)
 +{
 +      const char *online_cpus_file = "/sys/devices/system/cpu/online";
 +      struct bpf_map_info map;
 +      char msg[STRERR_BUFSIZE];
 +      struct perf_buffer *pb;
 +      bool *online = NULL;
 +      __u32 map_info_len;
 +      int err, i, j, n;
 +
 +      if (page_cnt & (page_cnt - 1)) {
 +              pr_warn("page count should be power of two, but is %zu\n",
 +                      page_cnt);
 +              return ERR_PTR(-EINVAL);
 +      }
 +
 +      /* best-effort sanity checks */
 +      memset(&map, 0, sizeof(map));
 +      map_info_len = sizeof(map);
 +      err = bpf_obj_get_info_by_fd(map_fd, &map, &map_info_len);
 +      if (err) {
 +              err = -errno;
 +              /* if BPF_OBJ_GET_INFO_BY_FD is supported, will return
 +               * -EBADFD, -EFAULT, or -E2BIG on real error
 +               */
 +              if (err != -EINVAL) {
 +                      pr_warn("failed to get map info for map FD %d: %s\n",
 +                              map_fd, libbpf_strerror_r(err, msg, sizeof(msg)));
 +                      return ERR_PTR(err);
 +              }
 +              pr_debug("failed to get map info for FD %d; API not supported? Ignoring...\n",
 +                       map_fd);
 +      } else {
 +              if (map.type != BPF_MAP_TYPE_PERF_EVENT_ARRAY) {
 +                      pr_warn("map '%s' should be BPF_MAP_TYPE_PERF_EVENT_ARRAY\n",
 +                              map.name);
 +                      return ERR_PTR(-EINVAL);
 +              }
 +      }
 +
 +      pb = calloc(1, sizeof(*pb));
 +      if (!pb)
 +              return ERR_PTR(-ENOMEM);
 +
 +      pb->event_cb = p->event_cb;
 +      pb->sample_cb = p->sample_cb;
 +      pb->lost_cb = p->lost_cb;
 +      pb->ctx = p->ctx;
 +
 +      pb->page_size = getpagesize();
 +      pb->mmap_size = pb->page_size * page_cnt;
 +      pb->map_fd = map_fd;
 +
 +      pb->epoll_fd = epoll_create1(EPOLL_CLOEXEC);
 +      if (pb->epoll_fd < 0) {
 +              err = -errno;
 +              pr_warn("failed to create epoll instance: %s\n",
 +                      libbpf_strerror_r(err, msg, sizeof(msg)));
 +              goto error;
 +      }
 +
 +      if (p->cpu_cnt > 0) {
 +              pb->cpu_cnt = p->cpu_cnt;
 +      } else {
 +              pb->cpu_cnt = libbpf_num_possible_cpus();
 +              if (pb->cpu_cnt < 0) {
 +                      err = pb->cpu_cnt;
 +                      goto error;
 +              }
 +              if (map.max_entries && map.max_entries < pb->cpu_cnt)
 +                      pb->cpu_cnt = map.max_entries;
 +      }
 +
 +      pb->events = calloc(pb->cpu_cnt, sizeof(*pb->events));
 +      if (!pb->events) {
 +              err = -ENOMEM;
 +              pr_warn("failed to allocate events: out of memory\n");
 +              goto error;
 +      }
 +      pb->cpu_bufs = calloc(pb->cpu_cnt, sizeof(*pb->cpu_bufs));
 +      if (!pb->cpu_bufs) {
 +              err = -ENOMEM;
 +              pr_warn("failed to allocate buffers: out of memory\n");
 +              goto error;
 +      }
 +
 +      err = parse_cpu_mask_file(online_cpus_file, &online, &n);
 +      if (err) {
 +              pr_warn("failed to get online CPU mask: %d\n", err);
 +              goto error;
 +      }
 +
 +      for (i = 0, j = 0; i < pb->cpu_cnt; i++) {
 +              struct perf_cpu_buf *cpu_buf;
 +              int cpu, map_key;
 +
 +              cpu = p->cpu_cnt > 0 ? p->cpus[i] : i;
 +              map_key = p->cpu_cnt > 0 ? p->map_keys[i] : i;
 +
 +              /* in case user didn't explicitly requested particular CPUs to
 +               * be attached to, skip offline/not present CPUs
 +               */
 +              if (p->cpu_cnt <= 0 && (cpu >= n || !online[cpu]))
 +                      continue;
 +
 +              cpu_buf = perf_buffer__open_cpu_buf(pb, p->attr, cpu, map_key);
 +              if (IS_ERR(cpu_buf)) {
 +                      err = PTR_ERR(cpu_buf);
 +                      goto error;
 +              }
 +
 +              pb->cpu_bufs[j] = cpu_buf;
 +
 +              err = bpf_map_update_elem(pb->map_fd, &map_key,
 +                                        &cpu_buf->fd, 0);
 +              if (err) {
 +                      err = -errno;
 +                      pr_warn("failed to set cpu #%d, key %d -> perf FD %d: %s\n",
 +                              cpu, map_key, cpu_buf->fd,
 +                              libbpf_strerror_r(err, msg, sizeof(msg)));
 +                      goto error;
 +              }
 +
 +              pb->events[j].events = EPOLLIN;
 +              pb->events[j].data.ptr = cpu_buf;
 +              if (epoll_ctl(pb->epoll_fd, EPOLL_CTL_ADD, cpu_buf->fd,
 +                            &pb->events[j]) < 0) {
 +                      err = -errno;
 +                      pr_warn("failed to epoll_ctl cpu #%d perf FD %d: %s\n",
 +                              cpu, cpu_buf->fd,
 +                              libbpf_strerror_r(err, msg, sizeof(msg)));
 +                      goto error;
 +              }
 +              j++;
 +      }
 +      pb->cpu_cnt = j;
 +      free(online);
 +
 +      return pb;
 +
 +error:
 +      free(online);
 +      if (pb)
 +              perf_buffer__free(pb);
 +      return ERR_PTR(err);
 +}
 +
 +struct perf_sample_raw {
 +      struct perf_event_header header;
 +      uint32_t size;
 +      char data[];
 +};
 +
 +struct perf_sample_lost {
 +      struct perf_event_header header;
 +      uint64_t id;
 +      uint64_t lost;
 +      uint64_t sample_id;
 +};
 +
 +static enum bpf_perf_event_ret
 +perf_buffer__process_record(struct perf_event_header *e, void *ctx)
 +{
 +      struct perf_cpu_buf *cpu_buf = ctx;
 +      struct perf_buffer *pb = cpu_buf->pb;
 +      void *data = e;
 +
 +      /* user wants full control over parsing perf event */
 +      if (pb->event_cb)
 +              return pb->event_cb(pb->ctx, cpu_buf->cpu, e);
 +
 +      switch (e->type) {
 +      case PERF_RECORD_SAMPLE: {
 +              struct perf_sample_raw *s = data;
 +
 +              if (pb->sample_cb)
 +                      pb->sample_cb(pb->ctx, cpu_buf->cpu, s->data, s->size);
 +              break;
 +      }
 +      case PERF_RECORD_LOST: {
 +              struct perf_sample_lost *s = data;
 +
 +              if (pb->lost_cb)
 +                      pb->lost_cb(pb->ctx, cpu_buf->cpu, s->lost);
 +              break;
 +      }
 +      default:
 +              pr_warn("unknown perf sample type %d\n", e->type);
 +              return LIBBPF_PERF_EVENT_ERROR;
 +      }
 +      return LIBBPF_PERF_EVENT_CONT;
 +}
 +
 +static int perf_buffer__process_records(struct perf_buffer *pb,
 +                                      struct perf_cpu_buf *cpu_buf)
 +{
 +      enum bpf_perf_event_ret ret;
 +
++      ret = perf_event_read_simple(cpu_buf->base, pb->mmap_size,
++                                   pb->page_size, &cpu_buf->buf,
++                                   &cpu_buf->buf_size,
++                                   perf_buffer__process_record, cpu_buf);
 +      if (ret != LIBBPF_PERF_EVENT_CONT)
 +              return ret;
 +      return 0;
 +}
 +
 +int perf_buffer__epoll_fd(const struct perf_buffer *pb)
 +{
 +      return pb->epoll_fd;
 +}
 +
 +int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms)
 +{
 +      int i, cnt, err;
 +
 +      cnt = epoll_wait(pb->epoll_fd, pb->events, pb->cpu_cnt, timeout_ms);
 +      if (cnt < 0)
 +              return -errno;
 +
 +      for (i = 0; i < cnt; i++) {
 +              struct perf_cpu_buf *cpu_buf = pb->events[i].data.ptr;
 +
 +              err = perf_buffer__process_records(pb, cpu_buf);
 +              if (err) {
 +                      pr_warn("error while processing records: %d\n", err);
 +                      return libbpf_err(err);
 +              }
 +      }
 +      return cnt;
 +}
 +
 +/* Return number of PERF_EVENT_ARRAY map slots set up by this perf_buffer
 + * manager.
 + */
 +size_t perf_buffer__buffer_cnt(const struct perf_buffer *pb)
 +{
 +      return pb->cpu_cnt;
 +}
 +
 +/*
 + * Return perf_event FD of a ring buffer in *buf_idx* slot of
 + * PERF_EVENT_ARRAY BPF map. This FD can be polled for new data using
 + * select()/poll()/epoll() Linux syscalls.
 + */
 +int perf_buffer__buffer_fd(const struct perf_buffer *pb, size_t buf_idx)
 +{
 +      struct perf_cpu_buf *cpu_buf;
 +
 +      if (buf_idx >= pb->cpu_cnt)
 +              return libbpf_err(-EINVAL);
 +
 +      cpu_buf = pb->cpu_bufs[buf_idx];
 +      if (!cpu_buf)
 +              return libbpf_err(-ENOENT);
 +
 +      return cpu_buf->fd;
 +}
 +
 +/*
 + * Consume data from perf ring buffer corresponding to slot *buf_idx* in
 + * PERF_EVENT_ARRAY BPF map without waiting/polling. If there is no data to
 + * consume, do nothing and return success.
 + * Returns:
 + *   - 0 on success;
 + *   - <0 on failure.
 + */
 +int perf_buffer__consume_buffer(struct perf_buffer *pb, size_t buf_idx)
 +{
 +      struct perf_cpu_buf *cpu_buf;
 +
 +      if (buf_idx >= pb->cpu_cnt)
 +              return libbpf_err(-EINVAL);
 +
 +      cpu_buf = pb->cpu_bufs[buf_idx];
 +      if (!cpu_buf)
 +              return libbpf_err(-ENOENT);
 +
 +      return perf_buffer__process_records(pb, cpu_buf);
 +}
 +
 +int perf_buffer__consume(struct perf_buffer *pb)
 +{
 +      int i, err;
 +
 +      for (i = 0; i < pb->cpu_cnt; i++) {
 +              struct perf_cpu_buf *cpu_buf = pb->cpu_bufs[i];
 +
 +              if (!cpu_buf)
 +                      continue;
 +
 +              err = perf_buffer__process_records(pb, cpu_buf);
 +              if (err) {
 +                      pr_warn("perf_buffer: failed to process records in buffer #%d: %d\n", i, err);
 +                      return libbpf_err(err);
 +              }
 +      }
 +      return 0;
 +}
 +
 +struct bpf_prog_info_array_desc {
 +      int     array_offset;   /* e.g. offset of jited_prog_insns */
 +      int     count_offset;   /* e.g. offset of jited_prog_len */
 +      int     size_offset;    /* > 0: offset of rec size,
 +                               * < 0: fix size of -size_offset
 +                               */
 +};
 +
 +static struct bpf_prog_info_array_desc bpf_prog_info_array_desc[] = {
 +      [BPF_PROG_INFO_JITED_INSNS] = {
 +              offsetof(struct bpf_prog_info, jited_prog_insns),
 +              offsetof(struct bpf_prog_info, jited_prog_len),
 +              -1,
 +      },
 +      [BPF_PROG_INFO_XLATED_INSNS] = {
 +              offsetof(struct bpf_prog_info, xlated_prog_insns),
 +              offsetof(struct bpf_prog_info, xlated_prog_len),
 +              -1,
 +      },
 +      [BPF_PROG_INFO_MAP_IDS] = {
 +              offsetof(struct bpf_prog_info, map_ids),
 +              offsetof(struct bpf_prog_info, nr_map_ids),
 +              -(int)sizeof(__u32),
 +      },
 +      [BPF_PROG_INFO_JITED_KSYMS] = {
 +              offsetof(struct bpf_prog_info, jited_ksyms),
 +              offsetof(struct bpf_prog_info, nr_jited_ksyms),
 +              -(int)sizeof(__u64),
 +      },
 +      [BPF_PROG_INFO_JITED_FUNC_LENS] = {
 +              offsetof(struct bpf_prog_info, jited_func_lens),
 +              offsetof(struct bpf_prog_info, nr_jited_func_lens),
 +              -(int)sizeof(__u32),
 +      },
 +      [BPF_PROG_INFO_FUNC_INFO] = {
 +              offsetof(struct bpf_prog_info, func_info),
 +              offsetof(struct bpf_prog_info, nr_func_info),
 +              offsetof(struct bpf_prog_info, func_info_rec_size),
 +      },
 +      [BPF_PROG_INFO_LINE_INFO] = {
 +              offsetof(struct bpf_prog_info, line_info),
 +              offsetof(struct bpf_prog_info, nr_line_info),
 +              offsetof(struct bpf_prog_info, line_info_rec_size),
 +      },
 +      [BPF_PROG_INFO_JITED_LINE_INFO] = {
 +              offsetof(struct bpf_prog_info, jited_line_info),
 +              offsetof(struct bpf_prog_info, nr_jited_line_info),
 +              offsetof(struct bpf_prog_info, jited_line_info_rec_size),
 +      },
 +      [BPF_PROG_INFO_PROG_TAGS] = {
 +              offsetof(struct bpf_prog_info, prog_tags),
 +              offsetof(struct bpf_prog_info, nr_prog_tags),
 +              -(int)sizeof(__u8) * BPF_TAG_SIZE,
 +      },
 +
 +};
 +
 +static __u32 bpf_prog_info_read_offset_u32(struct bpf_prog_info *info,
 +                                         int offset)
 +{
 +      __u32 *array = (__u32 *)info;
 +
 +      if (offset >= 0)
 +              return array[offset / sizeof(__u32)];
 +      return -(int)offset;
 +}
 +
 +static __u64 bpf_prog_info_read_offset_u64(struct bpf_prog_info *info,
 +                                         int offset)
 +{
 +      __u64 *array = (__u64 *)info;
 +
 +      if (offset >= 0)
 +              return array[offset / sizeof(__u64)];
 +      return -(int)offset;
 +}
 +
 +static void bpf_prog_info_set_offset_u32(struct bpf_prog_info *info, int offset,
 +                                       __u32 val)
 +{
 +      __u32 *array = (__u32 *)info;
 +
 +      if (offset >= 0)
 +              array[offset / sizeof(__u32)] = val;
 +}
 +
 +static void bpf_prog_info_set_offset_u64(struct bpf_prog_info *info, int offset,
 +                                       __u64 val)
 +{
 +      __u64 *array = (__u64 *)info;
 +
 +      if (offset >= 0)
 +              array[offset / sizeof(__u64)] = val;
 +}
 +
 +struct bpf_prog_info_linear *
 +bpf_program__get_prog_info_linear(int fd, __u64 arrays)
 +{
 +      struct bpf_prog_info_linear *info_linear;
 +      struct bpf_prog_info info = {};
 +      __u32 info_len = sizeof(info);
 +      __u32 data_len = 0;
 +      int i, err;
 +      void *ptr;
 +
 +      if (arrays >> BPF_PROG_INFO_LAST_ARRAY)
 +              return libbpf_err_ptr(-EINVAL);
 +
 +      /* step 1: get array dimensions */
 +      err = bpf_obj_get_info_by_fd(fd, &info, &info_len);
 +      if (err) {
 +              pr_debug("can't get prog info: %s", strerror(errno));
 +              return libbpf_err_ptr(-EFAULT);
 +      }
 +
 +      /* step 2: calculate total size of all arrays */
 +      for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
 +              bool include_array = (arrays & (1UL << i)) > 0;
 +              struct bpf_prog_info_array_desc *desc;
 +              __u32 count, size;
 +
 +              desc = bpf_prog_info_array_desc + i;
 +
 +              /* kernel is too old to support this field */
 +              if (info_len < desc->array_offset + sizeof(__u32) ||
 +                  info_len < desc->count_offset + sizeof(__u32) ||
 +                  (desc->size_offset > 0 && info_len < desc->size_offset))
 +                      include_array = false;
 +
 +              if (!include_array) {
 +                      arrays &= ~(1UL << i);  /* clear the bit */
 +                      continue;
 +              }
 +
 +              count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
 +              size  = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
 +
 +              data_len += count * size;
 +      }
 +
 +      /* step 3: allocate continuous memory */
 +      data_len = roundup(data_len, sizeof(__u64));
 +      info_linear = malloc(sizeof(struct bpf_prog_info_linear) + data_len);
 +      if (!info_linear)
 +              return libbpf_err_ptr(-ENOMEM);
 +
 +      /* step 4: fill data to info_linear->info */
 +      info_linear->arrays = arrays;
 +      memset(&info_linear->info, 0, sizeof(info));
 +      ptr = info_linear->data;
 +
 +      for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
 +              struct bpf_prog_info_array_desc *desc;
 +              __u32 count, size;
 +
 +              if ((arrays & (1UL << i)) == 0)
 +                      continue;
 +
 +              desc  = bpf_prog_info_array_desc + i;
 +              count = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
 +              size  = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
 +              bpf_prog_info_set_offset_u32(&info_linear->info,
 +                                           desc->count_offset, count);
 +              bpf_prog_info_set_offset_u32(&info_linear->info,
 +                                           desc->size_offset, size);
 +              bpf_prog_info_set_offset_u64(&info_linear->info,
 +                                           desc->array_offset,
 +                                           ptr_to_u64(ptr));
 +              ptr += count * size;
 +      }
 +
 +      /* step 5: call syscall again to get required arrays */
 +      err = bpf_obj_get_info_by_fd(fd, &info_linear->info, &info_len);
 +      if (err) {
 +              pr_debug("can't get prog info: %s", strerror(errno));
 +              free(info_linear);
 +              return libbpf_err_ptr(-EFAULT);
 +      }
 +
 +      /* step 6: verify the data */
 +      for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
 +              struct bpf_prog_info_array_desc *desc;
 +              __u32 v1, v2;
 +
 +              if ((arrays & (1UL << i)) == 0)
 +                      continue;
 +
 +              desc = bpf_prog_info_array_desc + i;
 +              v1 = bpf_prog_info_read_offset_u32(&info, desc->count_offset);
 +              v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
 +                                                 desc->count_offset);
 +              if (v1 != v2)
 +                      pr_warn("%s: mismatch in element count\n", __func__);
 +
 +              v1 = bpf_prog_info_read_offset_u32(&info, desc->size_offset);
 +              v2 = bpf_prog_info_read_offset_u32(&info_linear->info,
 +                                                 desc->size_offset);
 +              if (v1 != v2)
 +                      pr_warn("%s: mismatch in rec size\n", __func__);
 +      }
 +
 +      /* step 7: update info_len and data_len */
 +      info_linear->info_len = sizeof(struct bpf_prog_info);
 +      info_linear->data_len = data_len;
 +
 +      return info_linear;
 +}
 +
 +void bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear)
 +{
 +      int i;
 +
 +      for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
 +              struct bpf_prog_info_array_desc *desc;
 +              __u64 addr, offs;
 +
 +              if ((info_linear->arrays & (1UL << i)) == 0)
 +                      continue;
 +
 +              desc = bpf_prog_info_array_desc + i;
 +              addr = bpf_prog_info_read_offset_u64(&info_linear->info,
 +                                                   desc->array_offset);
 +              offs = addr - ptr_to_u64(info_linear->data);
 +              bpf_prog_info_set_offset_u64(&info_linear->info,
 +                                           desc->array_offset, offs);
 +      }
 +}
 +
 +void bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear)
 +{
 +      int i;
 +
 +      for (i = BPF_PROG_INFO_FIRST_ARRAY; i < BPF_PROG_INFO_LAST_ARRAY; ++i) {
 +              struct bpf_prog_info_array_desc *desc;
 +              __u64 addr, offs;
 +
 +              if ((info_linear->arrays & (1UL << i)) == 0)
 +                      continue;
 +
 +              desc = bpf_prog_info_array_desc + i;
 +              offs = bpf_prog_info_read_offset_u64(&info_linear->info,
 +                                                   desc->array_offset);
 +              addr = offs + ptr_to_u64(info_linear->data);
 +              bpf_prog_info_set_offset_u64(&info_linear->info,
 +                                           desc->array_offset, addr);
 +      }
 +}
 +
 +int bpf_program__set_attach_target(struct bpf_program *prog,
 +                                 int attach_prog_fd,
 +                                 const char *attach_func_name)
 +{
 +      int btf_obj_fd = 0, btf_id = 0, err;
 +
 +      if (!prog || attach_prog_fd < 0)
 +              return libbpf_err(-EINVAL);
 +
 +      if (prog->obj->loaded)
 +              return libbpf_err(-EINVAL);
 +
 +      if (attach_prog_fd && !attach_func_name) {
 +              /* remember attach_prog_fd and let bpf_program__load() find
 +               * BTF ID during the program load
 +               */
 +              prog->attach_prog_fd = attach_prog_fd;
 +              return 0;
 +      }
 +
 +      if (attach_prog_fd) {
 +              btf_id = libbpf_find_prog_btf_id(attach_func_name,
 +                                               attach_prog_fd);
 +              if (btf_id < 0)
 +                      return libbpf_err(btf_id);
 +      } else {
 +              if (!attach_func_name)
 +                      return libbpf_err(-EINVAL);
 +
 +              /* load btf_vmlinux, if not yet */
 +              err = bpf_object__load_vmlinux_btf(prog->obj, true);
 +              if (err)
 +                      return libbpf_err(err);
 +              err = find_kernel_btf_id(prog->obj, attach_func_name,
 +                                       prog->expected_attach_type,
 +                                       &btf_obj_fd, &btf_id);
 +              if (err)
 +                      return libbpf_err(err);
 +      }
 +
 +      prog->attach_btf_id = btf_id;
 +      prog->attach_btf_obj_fd = btf_obj_fd;
 +      prog->attach_prog_fd = attach_prog_fd;
 +      return 0;
 +}
 +
 +int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz)
 +{
 +      int err = 0, n, len, start, end = -1;
 +      bool *tmp;
 +
 +      *mask = NULL;
 +      *mask_sz = 0;
 +
 +      /* Each sub string separated by ',' has format \d+-\d+ or \d+ */
 +      while (*s) {
 +              if (*s == ',' || *s == '\n') {
 +                      s++;
 +                      continue;
 +              }
 +              n = sscanf(s, "%d%n-%d%n", &start, &len, &end, &len);
 +              if (n <= 0 || n > 2) {
 +                      pr_warn("Failed to get CPU range %s: %d\n", s, n);
 +                      err = -EINVAL;
 +                      goto cleanup;
 +              } else if (n == 1) {
 +                      end = start;
 +              }
 +              if (start < 0 || start > end) {
 +                      pr_warn("Invalid CPU range [%d,%d] in %s\n",
 +                              start, end, s);
 +                      err = -EINVAL;
 +                      goto cleanup;
 +              }
 +              tmp = realloc(*mask, end + 1);
 +              if (!tmp) {
 +                      err = -ENOMEM;
 +                      goto cleanup;
 +              }
 +              *mask = tmp;
 +              memset(tmp + *mask_sz, 0, start - *mask_sz);
 +              memset(tmp + start, 1, end - start + 1);
 +              *mask_sz = end + 1;
 +              s += len;
 +      }
 +      if (!*mask_sz) {
 +              pr_warn("Empty CPU range\n");
 +              return -EINVAL;
 +      }
 +      return 0;
 +cleanup:
 +      free(*mask);
 +      *mask = NULL;
 +      return err;
 +}
 +
 +int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz)
 +{
 +      int fd, err = 0, len;
 +      char buf[128];
 +
 +      fd = open(fcpu, O_RDONLY | O_CLOEXEC);
 +      if (fd < 0) {
 +              err = -errno;
 +              pr_warn("Failed to open cpu mask file %s: %d\n", fcpu, err);
 +              return err;
 +      }
 +      len = read(fd, buf, sizeof(buf));
 +      close(fd);
 +      if (len <= 0) {
 +              err = len ? -errno : -EINVAL;
 +              pr_warn("Failed to read cpu mask from %s: %d\n", fcpu, err);
 +              return err;
 +      }
 +      if (len >= sizeof(buf)) {
 +              pr_warn("CPU mask is too big in file %s\n", fcpu);
 +              return -E2BIG;
 +      }
 +      buf[len] = '\0';
 +
 +      return parse_cpu_mask_str(buf, mask, mask_sz);
 +}
 +
 +int libbpf_num_possible_cpus(void)
 +{
 +      static const char *fcpu = "/sys/devices/system/cpu/possible";
 +      static int cpus;
 +      int err, n, i, tmp_cpus;
 +      bool *mask;
 +
 +      tmp_cpus = READ_ONCE(cpus);
 +      if (tmp_cpus > 0)
 +              return tmp_cpus;
 +
 +      err = parse_cpu_mask_file(fcpu, &mask, &n);
 +      if (err)
 +              return libbpf_err(err);
 +
 +      tmp_cpus = 0;
 +      for (i = 0; i < n; i++) {
 +              if (mask[i])
 +                      tmp_cpus++;
 +      }
 +      free(mask);
 +
 +      WRITE_ONCE(cpus, tmp_cpus);
 +      return tmp_cpus;
 +}
 +
 +int bpf_object__open_skeleton(struct bpf_object_skeleton *s,
 +                            const struct bpf_object_open_opts *opts)
 +{
 +      DECLARE_LIBBPF_OPTS(bpf_object_open_opts, skel_opts,
 +              .object_name = s->name,
 +      );
 +      struct bpf_object *obj;
 +      int i, err;
 +
 +      /* Attempt to preserve opts->object_name, unless overriden by user
 +       * explicitly. Overwriting object name for skeletons is discouraged,
 +       * as it breaks global data maps, because they contain object name
 +       * prefix as their own map name prefix. When skeleton is generated,
 +       * bpftool is making an assumption that this name will stay the same.
 +       */
 +      if (opts) {
 +              memcpy(&skel_opts, opts, sizeof(*opts));
 +              if (!opts->object_name)
 +                      skel_opts.object_name = s->name;
 +      }
 +
 +      obj = bpf_object__open_mem(s->data, s->data_sz, &skel_opts);
 +      err = libbpf_get_error(obj);
 +      if (err) {
 +              pr_warn("failed to initialize skeleton BPF object '%s': %d\n",
 +                      s->name, err);
 +              return libbpf_err(err);
 +      }
 +
 +      *s->obj = obj;
 +
 +      for (i = 0; i < s->map_cnt; i++) {
 +              struct bpf_map **map = s->maps[i].map;
 +              const char *name = s->maps[i].name;
 +              void **mmaped = s->maps[i].mmaped;
 +
 +              *map = bpf_object__find_map_by_name(obj, name);
 +              if (!*map) {
 +                      pr_warn("failed to find skeleton map '%s'\n", name);
 +                      return libbpf_err(-ESRCH);
 +              }
 +
 +              /* externs shouldn't be pre-setup from user code */
 +              if (mmaped && (*map)->libbpf_type != LIBBPF_MAP_KCONFIG)
 +                      *mmaped = (*map)->mmaped;
 +      }
 +
 +      for (i = 0; i < s->prog_cnt; i++) {
 +              struct bpf_program **prog = s->progs[i].prog;
 +              const char *name = s->progs[i].name;
 +
 +              *prog = bpf_object__find_program_by_name(obj, name);
 +              if (!*prog) {
 +                      pr_warn("failed to find skeleton program '%s'\n", name);
 +                      return libbpf_err(-ESRCH);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +int bpf_object__load_skeleton(struct bpf_object_skeleton *s)
 +{
 +      int i, err;
 +
 +      err = bpf_object__load(*s->obj);
 +      if (err) {
 +              pr_warn("failed to load BPF skeleton '%s': %d\n", s->name, err);
 +              return libbpf_err(err);
 +      }
 +
 +      for (i = 0; i < s->map_cnt; i++) {
 +              struct bpf_map *map = *s->maps[i].map;
 +              size_t mmap_sz = bpf_map_mmap_sz(map);
 +              int prot, map_fd = bpf_map__fd(map);
 +              void **mmaped = s->maps[i].mmaped;
 +
 +              if (!mmaped)
 +                      continue;
 +
 +              if (!(map->def.map_flags & BPF_F_MMAPABLE)) {
 +                      *mmaped = NULL;
 +                      continue;
 +              }
 +
 +              if (map->def.map_flags & BPF_F_RDONLY_PROG)
 +                      prot = PROT_READ;
 +              else
 +                      prot = PROT_READ | PROT_WRITE;
 +
 +              /* Remap anonymous mmap()-ed "map initialization image" as
 +               * a BPF map-backed mmap()-ed memory, but preserving the same
 +               * memory address. This will cause kernel to change process'
 +               * page table to point to a different piece of kernel memory,
 +               * but from userspace point of view memory address (and its
 +               * contents, being identical at this point) will stay the
 +               * same. This mapping will be released by bpf_object__close()
 +               * as per normal clean up procedure, so we don't need to worry
 +               * about it from skeleton's clean up perspective.
 +               */
 +              *mmaped = mmap(map->mmaped, mmap_sz, prot,
 +                              MAP_SHARED | MAP_FIXED, map_fd, 0);
 +              if (*mmaped == MAP_FAILED) {
 +                      err = -errno;
 +                      *mmaped = NULL;
 +                      pr_warn("failed to re-mmap() map '%s': %d\n",
 +                               bpf_map__name(map), err);
 +                      return libbpf_err(err);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +int bpf_object__attach_skeleton(struct bpf_object_skeleton *s)
 +{
 +      int i, err;
 +
 +      for (i = 0; i < s->prog_cnt; i++) {
 +              struct bpf_program *prog = *s->progs[i].prog;
 +              struct bpf_link **link = s->progs[i].link;
 +
 +              if (!prog->load)
 +                      continue;
 +
 +              /* auto-attaching not supported for this program */
 +              if (!prog->sec_def || !prog->sec_def->attach_fn)
 +                      continue;
 +
 +              *link = bpf_program__attach(prog);
 +              err = libbpf_get_error(*link);
 +              if (err) {
 +                      pr_warn("failed to auto-attach program '%s': %d\n",
 +                              bpf_program__name(prog), err);
 +                      return libbpf_err(err);
 +              }
 +      }
 +
 +      return 0;
 +}
 +
 +void bpf_object__detach_skeleton(struct bpf_object_skeleton *s)
 +{
 +      int i;
 +
 +      for (i = 0; i < s->prog_cnt; i++) {
 +              struct bpf_link **link = s->progs[i].link;
 +
 +              bpf_link__destroy(*link);
 +              *link = NULL;
 +      }
 +}
 +
 +void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s)
 +{
 +      if (s->progs)
 +              bpf_object__detach_skeleton(s);
 +      if (s->obj)
 +              bpf_object__close(*s->obj);
 +      free(s->maps);
 +      free(s->progs);
 +      free(s);
 +}
index 85dfef8,0000000..8b9bc5e
mode 100644,000000..100644
--- /dev/null
@@@ -1,1269 -1,0 +1,1272 @@@
- LIBBPF_API struct bpf_map *
 +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
 +
 +/*
 + * 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.
 + */
 +#ifndef __LIBBPF_LIBBPF_H
 +#define __LIBBPF_LIBBPF_H
 +
 +#include <stdarg.h>
 +#include <stdio.h>
 +#include <stdint.h>
 +#include <stdbool.h>
 +#include <sys/types.h>  // for size_t
 +#include <linux/bpf.h>
 +
 +#include "libbpf_common.h"
 +#include "libbpf_legacy.h"
 +
 +#ifdef __cplusplus
 +extern "C" {
 +#endif
 +
 +LIBBPF_API __u32 libbpf_major_version(void);
 +LIBBPF_API __u32 libbpf_minor_version(void);
 +LIBBPF_API const char *libbpf_version_string(void);
 +
 +enum libbpf_errno {
 +      __LIBBPF_ERRNO__START = 4000,
 +
 +      /* Something wrong in libelf */
 +      LIBBPF_ERRNO__LIBELF = __LIBBPF_ERRNO__START,
 +      LIBBPF_ERRNO__FORMAT,   /* BPF object format invalid */
 +      LIBBPF_ERRNO__KVERSION, /* Incorrect or no 'version' section */
 +      LIBBPF_ERRNO__ENDIAN,   /* Endian mismatch */
 +      LIBBPF_ERRNO__INTERNAL, /* Internal error in libbpf */
 +      LIBBPF_ERRNO__RELOC,    /* Relocation failed */
 +      LIBBPF_ERRNO__LOAD,     /* Load program failure for unknown reason */
 +      LIBBPF_ERRNO__VERIFY,   /* Kernel verifier blocks program loading */
 +      LIBBPF_ERRNO__PROG2BIG, /* Program too big */
 +      LIBBPF_ERRNO__KVER,     /* Incorrect kernel version */
 +      LIBBPF_ERRNO__PROGTYPE, /* Kernel doesn't support this program type */
 +      LIBBPF_ERRNO__WRNGPID,  /* Wrong pid in netlink message */
 +      LIBBPF_ERRNO__INVSEQ,   /* Invalid netlink sequence */
 +      LIBBPF_ERRNO__NLPARSE,  /* netlink parsing error */
 +      __LIBBPF_ERRNO__END,
 +};
 +
 +LIBBPF_API int libbpf_strerror(int err, char *buf, size_t size);
 +
 +enum libbpf_print_level {
 +        LIBBPF_WARN,
 +        LIBBPF_INFO,
 +        LIBBPF_DEBUG,
 +};
 +
 +typedef int (*libbpf_print_fn_t)(enum libbpf_print_level level,
 +                               const char *, va_list ap);
 +
 +LIBBPF_API libbpf_print_fn_t libbpf_set_print(libbpf_print_fn_t fn);
 +
 +/* Hide internal to user */
 +struct bpf_object;
 +
 +struct bpf_object_open_attr {
 +      const char *file;
 +      enum bpf_prog_type prog_type;
 +};
 +
 +struct bpf_object_open_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +      /* object name override, if provided:
 +       * - for object open from file, this will override setting object
 +       *   name from file path's base name;
 +       * - for object open from memory buffer, this will specify an object
 +       *   name and will override default "<addr>-<buf-size>" name;
 +       */
 +      const char *object_name;
 +      /* parse map definitions non-strictly, allowing extra attributes/data */
 +      bool relaxed_maps;
 +      /* DEPRECATED: handle CO-RE relocations non-strictly, allowing failures.
 +       * Value is ignored. Relocations always are processed non-strictly.
 +       * Non-relocatable instructions are replaced with invalid ones to
 +       * prevent accidental errors.
 +       * */
 +      LIBBPF_DEPRECATED_SINCE(0, 6, "field has no effect")
 +      bool relaxed_core_relocs;
 +      /* maps that set the 'pinning' attribute in their definition will have
 +       * their pin_path attribute set to a file in this directory, and be
 +       * auto-pinned to that path on load; defaults to "/sys/fs/bpf".
 +       */
 +      const char *pin_root_path;
 +
 +      LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_program__set_attach_target() on each individual bpf_program")
 +      __u32 attach_prog_fd;
 +      /* Additional kernel config content that augments and overrides
 +       * system Kconfig for CONFIG_xxx externs.
 +       */
 +      const char *kconfig;
 +      /* Path to the custom BTF to be used for BPF CO-RE relocations.
 +       * This custom BTF completely replaces the use of vmlinux BTF
 +       * for the purpose of CO-RE relocations.
 +       * NOTE: any other BPF feature (e.g., fentry/fexit programs,
 +       * struct_ops, etc) will need actual kernel BTF at /sys/kernel/btf/vmlinux.
 +       */
 +      const char *btf_custom_path;
 +      /* Pointer to a buffer for storing kernel logs for applicable BPF
 +       * commands. Valid kernel_log_size has to be specified as well and are
 +       * passed-through to bpf() syscall. Keep in mind that kernel might
 +       * fail operation with -ENOSPC error if provided buffer is too small
 +       * to contain entire log output.
 +       * See the comment below for kernel_log_level for interaction between
 +       * log_buf and log_level settings.
 +       *
 +       * If specified, this log buffer will be passed for:
 +       *   - each BPF progral load (BPF_PROG_LOAD) attempt, unless overriden
 +       *     with bpf_program__set_log() on per-program level, to get
 +       *     BPF verifier log output.
 +       *   - during BPF object's BTF load into kernel (BPF_BTF_LOAD) to get
 +       *     BTF sanity checking log.
 +       *
 +       * Each BPF command (BPF_BTF_LOAD or BPF_PROG_LOAD) will overwrite
 +       * previous contents, so if you need more fine-grained control, set
 +       * per-program buffer with bpf_program__set_log_buf() to preserve each
 +       * individual program's verification log. Keep using kernel_log_buf
 +       * for BTF verification log, if necessary.
 +       */
 +      char *kernel_log_buf;
 +      size_t kernel_log_size;
 +      /*
 +       * Log level can be set independently from log buffer. Log_level=0
 +       * means that libbpf will attempt loading BTF or program without any
 +       * logging requested, but will retry with either its own or custom log
 +       * buffer, if provided, and log_level=1 on any error.
 +       * And vice versa, setting log_level>0 will request BTF or prog
 +       * loading with verbose log from the first attempt (and as such also
 +       * for successfully loaded BTF or program), and the actual log buffer
 +       * could be either libbpf's own auto-allocated log buffer, if
 +       * kernel_log_buffer is NULL, or user-provided custom kernel_log_buf.
 +       * If user didn't provide custom log buffer, libbpf will emit captured
 +       * logs through its print callback.
 +       */
 +      __u32 kernel_log_level;
 +
 +      size_t :0;
 +};
 +#define bpf_object_open_opts__last_field kernel_log_level
 +
 +LIBBPF_API struct bpf_object *bpf_object__open(const char *path);
 +
 +/**
 + * @brief **bpf_object__open_file()** creates a bpf_object by opening
 + * the BPF ELF object file pointed to by the passed path and loading it
 + * into memory.
 + * @param path BPF object file path
 + * @param opts options for how to load the bpf object, this parameter is
 + * optional and can be set to NULL
 + * @return pointer to the new bpf_object; or NULL is returned on error,
 + * error code is stored in errno
 + */
 +LIBBPF_API struct bpf_object *
 +bpf_object__open_file(const char *path, const struct bpf_object_open_opts *opts);
 +
 +/**
 + * @brief **bpf_object__open_mem()** creates a bpf_object by reading
 + * the BPF objects raw bytes from a memory buffer containing a valid
 + * BPF ELF object file.
 + * @param obj_buf pointer to the buffer containing ELF file bytes
 + * @param obj_buf_sz number of bytes in the buffer
 + * @param opts options for how to load the bpf object
 + * @return pointer to the new bpf_object; or NULL is returned on error,
 + * error code is stored in errno
 + */
 +LIBBPF_API struct bpf_object *
 +bpf_object__open_mem(const void *obj_buf, size_t obj_buf_sz,
 +                   const struct bpf_object_open_opts *opts);
 +
 +/* deprecated bpf_object__open variants */
 +LIBBPF_API struct bpf_object *
 +bpf_object__open_buffer(const void *obj_buf, size_t obj_buf_sz,
 +                      const char *name);
 +LIBBPF_API struct bpf_object *
 +bpf_object__open_xattr(struct bpf_object_open_attr *attr);
 +
 +enum libbpf_pin_type {
 +      LIBBPF_PIN_NONE,
 +      /* PIN_BY_NAME: pin maps by name (in /sys/fs/bpf by default) */
 +      LIBBPF_PIN_BY_NAME,
 +};
 +
 +/* pin_maps and unpin_maps can both be called with a NULL path, in which case
 + * they will use the pin_path attribute of each map (and ignore all maps that
 + * don't have a pin_path set).
 + */
 +LIBBPF_API int bpf_object__pin_maps(struct bpf_object *obj, const char *path);
 +LIBBPF_API int bpf_object__unpin_maps(struct bpf_object *obj,
 +                                    const char *path);
 +LIBBPF_API int bpf_object__pin_programs(struct bpf_object *obj,
 +                                      const char *path);
 +LIBBPF_API int bpf_object__unpin_programs(struct bpf_object *obj,
 +                                        const char *path);
 +LIBBPF_API int bpf_object__pin(struct bpf_object *object, const char *path);
 +LIBBPF_API void bpf_object__close(struct bpf_object *object);
 +
 +struct bpf_object_load_attr {
 +      struct bpf_object *obj;
 +      int log_level;
 +      const char *target_btf_path;
 +};
 +
 +/* Load/unload object into/from kernel */
 +LIBBPF_API int bpf_object__load(struct bpf_object *obj);
 +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_object__load() instead")
 +LIBBPF_API int bpf_object__load_xattr(struct bpf_object_load_attr *attr);
 +LIBBPF_DEPRECATED_SINCE(0, 6, "bpf_object__unload() is deprecated, use bpf_object__close() instead")
 +LIBBPF_API int bpf_object__unload(struct bpf_object *obj);
 +
 +LIBBPF_API const char *bpf_object__name(const struct bpf_object *obj);
 +LIBBPF_API unsigned int bpf_object__kversion(const struct bpf_object *obj);
 +LIBBPF_API int bpf_object__set_kversion(struct bpf_object *obj, __u32 kern_version);
 +
 +struct btf;
 +LIBBPF_API struct btf *bpf_object__btf(const struct bpf_object *obj);
 +LIBBPF_API int bpf_object__btf_fd(const struct bpf_object *obj);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__find_program_by_name() instead")
 +LIBBPF_API struct bpf_program *
 +bpf_object__find_program_by_title(const struct bpf_object *obj,
 +                                const char *title);
 +LIBBPF_API struct bpf_program *
 +bpf_object__find_program_by_name(const struct bpf_object *obj,
 +                               const char *name);
 +
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "track bpf_objects in application code instead")
 +struct bpf_object *bpf_object__next(struct bpf_object *prev);
 +#define bpf_object__for_each_safe(pos, tmp)                   \
 +      for ((pos) = bpf_object__next(NULL),            \
 +              (tmp) = bpf_object__next(pos);          \
 +           (pos) != NULL;                             \
 +           (pos) = (tmp), (tmp) = bpf_object__next(tmp))
 +
 +typedef void (*bpf_object_clear_priv_t)(struct bpf_object *, void *);
 +LIBBPF_API int bpf_object__set_priv(struct bpf_object *obj, void *priv,
 +                                  bpf_object_clear_priv_t clear_priv);
 +LIBBPF_API void *bpf_object__priv(const struct bpf_object *prog);
 +
 +LIBBPF_API int
 +libbpf_prog_type_by_name(const char *name, enum bpf_prog_type *prog_type,
 +                       enum bpf_attach_type *expected_attach_type);
 +LIBBPF_API int libbpf_attach_type_by_name(const char *name,
 +                                        enum bpf_attach_type *attach_type);
 +LIBBPF_API int libbpf_find_vmlinux_btf_id(const char *name,
 +                                        enum bpf_attach_type attach_type);
 +
 +/* Accessors of bpf_program */
 +struct bpf_program;
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__next_program() instead")
 +struct bpf_program *bpf_program__next(struct bpf_program *prog,
 +                                    const struct bpf_object *obj);
 +LIBBPF_API struct bpf_program *
 +bpf_object__next_program(const struct bpf_object *obj, struct bpf_program *prog);
 +
 +#define bpf_object__for_each_program(pos, obj)                        \
 +      for ((pos) = bpf_object__next_program((obj), NULL);     \
 +           (pos) != NULL;                                     \
 +           (pos) = bpf_object__next_program((obj), (pos)))
 +
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__prev_program() instead")
 +struct bpf_program *bpf_program__prev(struct bpf_program *prog,
 +                                    const struct bpf_object *obj);
 +LIBBPF_API struct bpf_program *
 +bpf_object__prev_program(const struct bpf_object *obj, struct bpf_program *prog);
 +
 +typedef void (*bpf_program_clear_priv_t)(struct bpf_program *, void *);
 +
 +LIBBPF_API int bpf_program__set_priv(struct bpf_program *prog, void *priv,
 +                                   bpf_program_clear_priv_t clear_priv);
 +
 +LIBBPF_API void *bpf_program__priv(const struct bpf_program *prog);
 +LIBBPF_API void bpf_program__set_ifindex(struct bpf_program *prog,
 +                                       __u32 ifindex);
 +
 +LIBBPF_API const char *bpf_program__name(const struct bpf_program *prog);
 +LIBBPF_API const char *bpf_program__section_name(const struct bpf_program *prog);
 +LIBBPF_API LIBBPF_DEPRECATED("BPF program title is confusing term; please use bpf_program__section_name() instead")
 +const char *bpf_program__title(const struct bpf_program *prog, bool needs_copy);
 +LIBBPF_API bool bpf_program__autoload(const struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_autoload(struct bpf_program *prog, bool autoload);
 +
 +/* returns program size in bytes */
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_program__insn_cnt() instead")
 +LIBBPF_API size_t bpf_program__size(const struct bpf_program *prog);
 +
 +struct bpf_insn;
 +
 +/**
 + * @brief **bpf_program__insns()** gives read-only access to BPF program's
 + * underlying BPF instructions.
 + * @param prog BPF program for which to return instructions
 + * @return a pointer to an array of BPF instructions that belong to the
 + * specified BPF program
 + *
 + * Returned pointer is always valid and not NULL. Number of `struct bpf_insn`
 + * pointed to can be fetched using **bpf_program__insn_cnt()** API.
 + *
 + * Keep in mind, libbpf can modify and append/delete BPF program's
 + * instructions as it processes BPF object file and prepares everything for
 + * uploading into the kernel. So depending on the point in BPF object
 + * lifetime, **bpf_program__insns()** can return different sets of
 + * instructions. As an example, during BPF object load phase BPF program
 + * instructions will be CO-RE-relocated, BPF subprograms instructions will be
 + * appended, ldimm64 instructions will have FDs embedded, etc. So instructions
 + * returned before **bpf_object__load()** and after it might be quite
 + * different.
 + */
 +LIBBPF_API const struct bpf_insn *bpf_program__insns(const struct bpf_program *prog);
 +/**
 + * @brief **bpf_program__insn_cnt()** returns number of `struct bpf_insn`'s
 + * that form specified BPF program.
 + * @param prog BPF program for which to return number of BPF instructions
 + *
 + * See **bpf_program__insns()** documentation for notes on how libbpf can
 + * change instructions and their count during different phases of
 + * **bpf_object** lifetime.
 + */
 +LIBBPF_API size_t bpf_program__insn_cnt(const struct bpf_program *prog);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 6, "use bpf_object__load() instead")
 +LIBBPF_API int bpf_program__load(struct bpf_program *prog, const char *license, __u32 kern_version);
 +LIBBPF_API int bpf_program__fd(const struct bpf_program *prog);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "multi-instance bpf_program support is deprecated")
 +LIBBPF_API int bpf_program__pin_instance(struct bpf_program *prog,
 +                                       const char *path,
 +                                       int instance);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "multi-instance bpf_program support is deprecated")
 +LIBBPF_API int bpf_program__unpin_instance(struct bpf_program *prog,
 +                                         const char *path,
 +                                         int instance);
 +
 +/**
 + * @brief **bpf_program__pin()** pins the BPF program to a file
 + * in the BPF FS specified by a path. This increments the programs
 + * reference count, allowing it to stay loaded after the process
 + * which loaded it has exited.
 + *
 + * @param prog BPF program to pin, must already be loaded
 + * @param path file path in a BPF file system
 + * @return 0, on success; negative error code, otherwise
 + */
 +LIBBPF_API int bpf_program__pin(struct bpf_program *prog, const char *path);
 +
 +/**
 + * @brief **bpf_program__unpin()** unpins the BPF program from a file
 + * in the BPFFS specified by a path. This decrements the programs
 + * reference count.
 + *
 + * The file pinning the BPF program can also be unlinked by a different
 + * process in which case this function will return an error.
 + *
 + * @param prog BPF program to unpin
 + * @param path file path to the pin in a BPF file system
 + * @return 0, on success; negative error code, otherwise
 + */
 +LIBBPF_API int bpf_program__unpin(struct bpf_program *prog, const char *path);
 +LIBBPF_API void bpf_program__unload(struct bpf_program *prog);
 +
 +struct bpf_link;
 +
 +LIBBPF_API struct bpf_link *bpf_link__open(const char *path);
 +LIBBPF_API int bpf_link__fd(const struct bpf_link *link);
 +LIBBPF_API const char *bpf_link__pin_path(const struct bpf_link *link);
 +LIBBPF_API int bpf_link__pin(struct bpf_link *link, const char *path);
 +LIBBPF_API int bpf_link__unpin(struct bpf_link *link);
 +LIBBPF_API int bpf_link__update_program(struct bpf_link *link,
 +                                      struct bpf_program *prog);
 +LIBBPF_API void bpf_link__disconnect(struct bpf_link *link);
 +LIBBPF_API int bpf_link__detach(struct bpf_link *link);
 +LIBBPF_API int bpf_link__destroy(struct bpf_link *link);
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach(const struct bpf_program *prog);
 +
 +struct bpf_perf_event_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +      /* custom user-provided value fetchable through bpf_get_attach_cookie() */
 +      __u64 bpf_cookie;
 +};
 +#define bpf_perf_event_opts__last_field bpf_cookie
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_perf_event(const struct bpf_program *prog, int pfd);
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_perf_event_opts(const struct bpf_program *prog, int pfd,
 +                                  const struct bpf_perf_event_opts *opts);
 +
 +struct bpf_kprobe_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +      /* custom user-provided value fetchable through bpf_get_attach_cookie() */
 +      __u64 bpf_cookie;
 +      /* function's offset to install kprobe to */
 +      size_t offset;
 +      /* kprobe is return probe */
 +      bool retprobe;
 +      size_t :0;
 +};
 +#define bpf_kprobe_opts__last_field retprobe
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_kprobe(const struct bpf_program *prog, bool retprobe,
 +                         const char *func_name);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_kprobe_opts(const struct bpf_program *prog,
 +                                const char *func_name,
 +                                const struct bpf_kprobe_opts *opts);
 +
 +struct bpf_uprobe_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +      /* offset of kernel reference counted USDT semaphore, added in
 +       * a6ca88b241d5 ("trace_uprobe: support reference counter in fd-based uprobe")
 +       */
 +      size_t ref_ctr_offset;
 +      /* custom user-provided value fetchable through bpf_get_attach_cookie() */
 +      __u64 bpf_cookie;
 +      /* uprobe is return probe, invoked at function return time */
 +      bool retprobe;
 +      size_t :0;
 +};
 +#define bpf_uprobe_opts__last_field retprobe
 +
 +/**
 + * @brief **bpf_program__attach_uprobe()** attaches a BPF program
 + * to the userspace function which is found by binary path and
 + * offset. You can optionally specify a particular proccess to attach
 + * to. You can also optionally attach the program to the function
 + * exit instead of entry.
 + *
 + * @param prog BPF program to attach
 + * @param retprobe Attach to function exit
 + * @param pid Process ID to attach the uprobe to, 0 for self (own process),
 + * -1 for all processes
 + * @param binary_path Path to binary that contains the function symbol
 + * @param func_offset Offset within the binary of the function symbol
 + * @return Reference to the newly created BPF link; or NULL is returned on error,
 + * error code is stored in errno
 + */
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_uprobe(const struct bpf_program *prog, bool retprobe,
 +                         pid_t pid, const char *binary_path,
 +                         size_t func_offset);
 +
 +/**
 + * @brief **bpf_program__attach_uprobe_opts()** is just like
 + * bpf_program__attach_uprobe() except with a options struct
 + * for various configurations.
 + *
 + * @param prog BPF program to attach
 + * @param pid Process ID to attach the uprobe to, 0 for self (own process),
 + * -1 for all processes
 + * @param binary_path Path to binary that contains the function symbol
 + * @param func_offset Offset within the binary of the function symbol
 + * @param opts Options for altering program attachment
 + * @return Reference to the newly created BPF link; or NULL is returned on error,
 + * error code is stored in errno
 + */
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_uprobe_opts(const struct bpf_program *prog, pid_t pid,
 +                              const char *binary_path, size_t func_offset,
 +                              const struct bpf_uprobe_opts *opts);
 +
 +struct bpf_tracepoint_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +      /* custom user-provided value fetchable through bpf_get_attach_cookie() */
 +      __u64 bpf_cookie;
 +};
 +#define bpf_tracepoint_opts__last_field bpf_cookie
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_tracepoint(const struct bpf_program *prog,
 +                             const char *tp_category,
 +                             const char *tp_name);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_tracepoint_opts(const struct bpf_program *prog,
 +                                  const char *tp_category,
 +                                  const char *tp_name,
 +                                  const struct bpf_tracepoint_opts *opts);
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_raw_tracepoint(const struct bpf_program *prog,
 +                                 const char *tp_name);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_trace(const struct bpf_program *prog);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_lsm(const struct bpf_program *prog);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_cgroup(const struct bpf_program *prog, int cgroup_fd);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_netns(const struct bpf_program *prog, int netns_fd);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_xdp(const struct bpf_program *prog, int ifindex);
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_freplace(const struct bpf_program *prog,
 +                           int target_fd, const char *attach_func_name);
 +
 +struct bpf_map;
 +
 +LIBBPF_API struct bpf_link *bpf_map__attach_struct_ops(const struct bpf_map *map);
 +
 +struct bpf_iter_attach_opts {
 +      size_t sz; /* size of this struct for forward/backward compatibility */
 +      union bpf_iter_link_info *link_info;
 +      __u32 link_info_len;
 +};
 +#define bpf_iter_attach_opts__last_field link_info_len
 +
 +LIBBPF_API struct bpf_link *
 +bpf_program__attach_iter(const struct bpf_program *prog,
 +                       const struct bpf_iter_attach_opts *opts);
 +
 +/*
 + * Libbpf allows callers to adjust BPF programs before being loaded
 + * into kernel. One program in an object file can be transformed into
 + * multiple variants to be attached to different hooks.
 + *
 + * bpf_program_prep_t, bpf_program__set_prep and bpf_program__nth_fd
 + * form an API for this purpose.
 + *
 + * - bpf_program_prep_t:
 + *   Defines a 'preprocessor', which is a caller defined function
 + *   passed to libbpf through bpf_program__set_prep(), and will be
 + *   called before program is loaded. The processor should adjust
 + *   the program one time for each instance according to the instance id
 + *   passed to it.
 + *
 + * - bpf_program__set_prep:
 + *   Attaches a preprocessor to a BPF program. The number of instances
 + *   that should be created is also passed through this function.
 + *
 + * - bpf_program__nth_fd:
 + *   After the program is loaded, get resulting FD of a given instance
 + *   of the BPF program.
 + *
 + * If bpf_program__set_prep() is not used, the program would be loaded
 + * without adjustment during bpf_object__load(). The program has only
 + * one instance. In this case bpf_program__fd(prog) is equal to
 + * bpf_program__nth_fd(prog, 0).
 + */
 +struct bpf_prog_prep_result {
 +      /*
 +       * If not NULL, load new instruction array.
 +       * If set to NULL, don't load this instance.
 +       */
 +      struct bpf_insn *new_insn_ptr;
 +      int new_insn_cnt;
 +
 +      /* If not NULL, result FD is written to it. */
 +      int *pfd;
 +};
 +
 +/*
 + * Parameters of bpf_program_prep_t:
 + *  - prog:   The bpf_program being loaded.
 + *  - n:      Index of instance being generated.
 + *  - insns:  BPF instructions array.
 + *  - insns_cnt:Number of instructions in insns.
 + *  - res:    Output parameter, result of transformation.
 + *
 + * Return value:
 + *  - Zero:   pre-processing success.
 + *  - Non-zero:       pre-processing error, stop loading.
 + */
 +typedef int (*bpf_program_prep_t)(struct bpf_program *prog, int n,
 +                                struct bpf_insn *insns, int insns_cnt,
 +                                struct bpf_prog_prep_result *res);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_program__insns() for getting bpf_program instructions")
 +LIBBPF_API int bpf_program__set_prep(struct bpf_program *prog, int nr_instance,
 +                                   bpf_program_prep_t prep);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 7, "multi-instance bpf_program support is deprecated")
 +LIBBPF_API int bpf_program__nth_fd(const struct bpf_program *prog, int n);
 +
 +/*
 + * Adjust type of BPF program. Default is kprobe.
 + */
 +LIBBPF_API int bpf_program__set_socket_filter(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_tracepoint(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_raw_tracepoint(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_kprobe(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_lsm(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_sched_cls(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_sched_act(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_xdp(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_perf_event(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_tracing(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_struct_ops(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_extension(struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_sk_lookup(struct bpf_program *prog);
 +
 +LIBBPF_API enum bpf_prog_type bpf_program__get_type(const struct bpf_program *prog);
 +LIBBPF_API void bpf_program__set_type(struct bpf_program *prog,
 +                                    enum bpf_prog_type type);
 +
 +LIBBPF_API enum bpf_attach_type
 +bpf_program__get_expected_attach_type(const struct bpf_program *prog);
 +LIBBPF_API void
 +bpf_program__set_expected_attach_type(struct bpf_program *prog,
 +                                    enum bpf_attach_type type);
 +
 +LIBBPF_API __u32 bpf_program__flags(const struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_flags(struct bpf_program *prog, __u32 flags);
 +
 +/* Per-program log level and log buffer getters/setters.
 + * See bpf_object_open_opts comments regarding log_level and log_buf
 + * interactions.
 + */
 +LIBBPF_API __u32 bpf_program__log_level(const struct bpf_program *prog);
 +LIBBPF_API int bpf_program__set_log_level(struct bpf_program *prog, __u32 log_level);
 +LIBBPF_API const char *bpf_program__log_buf(const struct bpf_program *prog, size_t *log_size);
 +LIBBPF_API int bpf_program__set_log_buf(struct bpf_program *prog, char *log_buf, size_t log_size);
 +
 +LIBBPF_API int
 +bpf_program__set_attach_target(struct bpf_program *prog, int attach_prog_fd,
 +                             const char *attach_func_name);
 +
 +LIBBPF_API bool bpf_program__is_socket_filter(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_tracepoint(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_raw_tracepoint(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_kprobe(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_lsm(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_sched_cls(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_sched_act(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_xdp(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_perf_event(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_tracing(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_struct_ops(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_extension(const struct bpf_program *prog);
 +LIBBPF_API bool bpf_program__is_sk_lookup(const struct bpf_program *prog);
 +
 +/*
 + * No need for __attribute__((packed)), all members of 'bpf_map_def'
 + * are all aligned.  In addition, using __attribute__((packed))
 + * would trigger a -Wpacked warning message, and lead to an error
 + * if -Werror is set.
 + */
 +struct bpf_map_def {
 +      unsigned int type;
 +      unsigned int key_size;
 +      unsigned int value_size;
 +      unsigned int max_entries;
 +      unsigned int map_flags;
 +};
 +
 +/**
 + * @brief **bpf_object__find_map_by_name()** returns BPF map of
 + * the given name, if it exists within the passed BPF object
 + * @param obj BPF object
 + * @param name name of the BPF map
 + * @return BPF map instance, if such map exists within the BPF object;
 + * or NULL otherwise.
 + */
 +LIBBPF_API struct bpf_map *
 +bpf_object__find_map_by_name(const struct bpf_object *obj, const char *name);
 +
 +LIBBPF_API int
 +bpf_object__find_map_fd_by_name(const struct bpf_object *obj, const char *name);
 +
 +/*
 + * Get bpf_map through the offset of corresponding struct bpf_map_def
 + * in the BPF object file.
 + */
++LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_object__find_map_by_name() instead")
++struct bpf_map *
 +bpf_object__find_map_by_offset(struct bpf_object *obj, size_t offset);
 +
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__next_map() instead")
 +struct bpf_map *bpf_map__next(const struct bpf_map *map, const struct bpf_object *obj);
 +LIBBPF_API struct bpf_map *
 +bpf_object__next_map(const struct bpf_object *obj, const struct bpf_map *map);
 +
 +#define bpf_object__for_each_map(pos, obj)            \
 +      for ((pos) = bpf_object__next_map((obj), NULL); \
 +           (pos) != NULL;                             \
 +           (pos) = bpf_object__next_map((obj), (pos)))
 +#define bpf_map__for_each bpf_object__for_each_map
 +
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__prev_map() instead")
 +struct bpf_map *bpf_map__prev(const struct bpf_map *map, const struct bpf_object *obj);
 +LIBBPF_API struct bpf_map *
 +bpf_object__prev_map(const struct bpf_object *obj, const struct bpf_map *map);
 +
 +/**
 + * @brief **bpf_map__fd()** gets the file descriptor of the passed
 + * BPF map
 + * @param map the BPF map instance
 + * @return the file descriptor; or -EINVAL in case of an error
 + */
 +LIBBPF_API int bpf_map__fd(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__reuse_fd(struct bpf_map *map, int fd);
 +/* get map definition */
 +LIBBPF_API const struct bpf_map_def *bpf_map__def(const struct bpf_map *map);
 +/* get map name */
 +LIBBPF_API const char *bpf_map__name(const struct bpf_map *map);
 +/* get/set map type */
 +LIBBPF_API enum bpf_map_type bpf_map__type(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_type(struct bpf_map *map, enum bpf_map_type type);
 +/* get/set map size (max_entries) */
 +LIBBPF_API __u32 bpf_map__max_entries(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_max_entries(struct bpf_map *map, __u32 max_entries);
 +LIBBPF_API int bpf_map__resize(struct bpf_map *map, __u32 max_entries);
 +/* get/set map flags */
 +LIBBPF_API __u32 bpf_map__map_flags(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_map_flags(struct bpf_map *map, __u32 flags);
 +/* get/set map NUMA node */
 +LIBBPF_API __u32 bpf_map__numa_node(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_numa_node(struct bpf_map *map, __u32 numa_node);
 +/* get/set map key size */
 +LIBBPF_API __u32 bpf_map__key_size(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_key_size(struct bpf_map *map, __u32 size);
 +/* get/set map value size */
 +LIBBPF_API __u32 bpf_map__value_size(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_value_size(struct bpf_map *map, __u32 size);
 +/* get map key/value BTF type IDs */
 +LIBBPF_API __u32 bpf_map__btf_key_type_id(const struct bpf_map *map);
 +LIBBPF_API __u32 bpf_map__btf_value_type_id(const struct bpf_map *map);
 +/* get/set map if_index */
 +LIBBPF_API __u32 bpf_map__ifindex(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_ifindex(struct bpf_map *map, __u32 ifindex);
 +/* get/set map map_extra flags */
 +LIBBPF_API __u64 bpf_map__map_extra(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_map_extra(struct bpf_map *map, __u64 map_extra);
 +
 +typedef void (*bpf_map_clear_priv_t)(struct bpf_map *, void *);
 +LIBBPF_API int bpf_map__set_priv(struct bpf_map *map, void *priv,
 +                               bpf_map_clear_priv_t clear_priv);
 +LIBBPF_API void *bpf_map__priv(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_initial_value(struct bpf_map *map,
 +                                        const void *data, size_t size);
 +LIBBPF_API const void *bpf_map__initial_value(struct bpf_map *map, size_t *psize);
++LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_map__type() instead")
 +LIBBPF_API bool bpf_map__is_offload_neutral(const struct bpf_map *map);
 +
 +/**
 + * @brief **bpf_map__is_internal()** tells the caller whether or not the
 + * passed map is a special map created by libbpf automatically for things like
 + * global variables, __ksym externs, Kconfig values, etc
 + * @param map the bpf_map
 + * @return true, if the map is an internal map; false, otherwise
 + */
 +LIBBPF_API bool bpf_map__is_internal(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__set_pin_path(struct bpf_map *map, const char *path);
 +LIBBPF_API const char *bpf_map__get_pin_path(const struct bpf_map *map);
 +LIBBPF_API const char *bpf_map__pin_path(const struct bpf_map *map);
 +LIBBPF_API bool bpf_map__is_pinned(const struct bpf_map *map);
 +LIBBPF_API int bpf_map__pin(struct bpf_map *map, const char *path);
 +LIBBPF_API int bpf_map__unpin(struct bpf_map *map, const char *path);
 +
 +LIBBPF_API int bpf_map__set_inner_map_fd(struct bpf_map *map, int fd);
 +LIBBPF_API struct bpf_map *bpf_map__inner_map(struct bpf_map *map);
 +
 +/**
 + * @brief **libbpf_get_error()** extracts the error code from the passed
 + * pointer
 + * @param ptr pointer returned from libbpf API function
 + * @return error code; or 0 if no error occured
 + *
 + * Many libbpf API functions which return pointers have logic to encode error
 + * codes as pointers, and do not return NULL. Meaning **libbpf_get_error()**
 + * should be used on the return value from these functions immediately after
 + * calling the API function, with no intervening calls that could clobber the
 + * `errno` variable. Consult the individual functions documentation to verify
 + * if this logic applies should be used.
 + *
 + * For these API functions, if `libbpf_set_strict_mode(LIBBPF_STRICT_CLEAN_PTRS)`
 + * is enabled, NULL is returned on error instead.
 + *
 + * If ptr is NULL, then errno should be already set by the failing
 + * API, because libbpf never returns NULL on success and it now always
 + * sets errno on error.
 + *
 + * Example usage:
 + *
 + *   struct perf_buffer *pb;
 + *
 + *   pb = perf_buffer__new(bpf_map__fd(obj->maps.events), PERF_BUFFER_PAGES, &opts);
 + *   err = libbpf_get_error(pb);
 + *   if (err) {
 + *      pb = NULL;
 + *      fprintf(stderr, "failed to open perf buffer: %d\n", err);
 + *      goto cleanup;
 + *   }
 + */
 +LIBBPF_API long libbpf_get_error(const void *ptr);
 +
 +struct bpf_prog_load_attr {
 +      const char *file;
 +      enum bpf_prog_type prog_type;
 +      enum bpf_attach_type expected_attach_type;
 +      int ifindex;
 +      int log_level;
 +      int prog_flags;
 +};
 +
 +LIBBPF_DEPRECATED_SINCE(0, 8, "use bpf_object__open() and bpf_object__load() instead")
 +LIBBPF_API int bpf_prog_load_xattr(const struct bpf_prog_load_attr *attr,
 +                                 struct bpf_object **pobj, int *prog_fd);
 +LIBBPF_DEPRECATED_SINCE(0, 7, "use bpf_object__open() and bpf_object__load() instead")
 +LIBBPF_API int bpf_prog_load_deprecated(const char *file, enum bpf_prog_type type,
 +                                      struct bpf_object **pobj, int *prog_fd);
 +
 +/* XDP related API */
 +struct xdp_link_info {
 +      __u32 prog_id;
 +      __u32 drv_prog_id;
 +      __u32 hw_prog_id;
 +      __u32 skb_prog_id;
 +      __u8 attach_mode;
 +};
 +
 +struct bpf_xdp_set_link_opts {
 +      size_t sz;
 +      int old_fd;
 +      size_t :0;
 +};
 +#define bpf_xdp_set_link_opts__last_field old_fd
 +
 +LIBBPF_API int bpf_set_link_xdp_fd(int ifindex, int fd, __u32 flags);
 +LIBBPF_API int bpf_set_link_xdp_fd_opts(int ifindex, int fd, __u32 flags,
 +                                      const struct bpf_xdp_set_link_opts *opts);
 +LIBBPF_API int bpf_get_link_xdp_id(int ifindex, __u32 *prog_id, __u32 flags);
 +LIBBPF_API int bpf_get_link_xdp_info(int ifindex, struct xdp_link_info *info,
 +                                   size_t info_size, __u32 flags);
 +
 +/* TC related API */
 +enum bpf_tc_attach_point {
 +      BPF_TC_INGRESS = 1 << 0,
 +      BPF_TC_EGRESS  = 1 << 1,
 +      BPF_TC_CUSTOM  = 1 << 2,
 +};
 +
 +#define BPF_TC_PARENT(a, b)   \
 +      ((((a) << 16) & 0xFFFF0000U) | ((b) & 0x0000FFFFU))
 +
 +enum bpf_tc_flags {
 +      BPF_TC_F_REPLACE = 1 << 0,
 +};
 +
 +struct bpf_tc_hook {
 +      size_t sz;
 +      int ifindex;
 +      enum bpf_tc_attach_point attach_point;
 +      __u32 parent;
 +      size_t :0;
 +};
 +#define bpf_tc_hook__last_field parent
 +
 +struct bpf_tc_opts {
 +      size_t sz;
 +      int prog_fd;
 +      __u32 flags;
 +      __u32 prog_id;
 +      __u32 handle;
 +      __u32 priority;
 +      size_t :0;
 +};
 +#define bpf_tc_opts__last_field priority
 +
 +LIBBPF_API int bpf_tc_hook_create(struct bpf_tc_hook *hook);
 +LIBBPF_API int bpf_tc_hook_destroy(struct bpf_tc_hook *hook);
 +LIBBPF_API int bpf_tc_attach(const struct bpf_tc_hook *hook,
 +                           struct bpf_tc_opts *opts);
 +LIBBPF_API int bpf_tc_detach(const struct bpf_tc_hook *hook,
 +                           const struct bpf_tc_opts *opts);
 +LIBBPF_API int bpf_tc_query(const struct bpf_tc_hook *hook,
 +                          struct bpf_tc_opts *opts);
 +
 +/* Ring buffer APIs */
 +struct ring_buffer;
 +
 +typedef int (*ring_buffer_sample_fn)(void *ctx, void *data, size_t size);
 +
 +struct ring_buffer_opts {
 +      size_t sz; /* size of this struct, for forward/backward compatiblity */
 +};
 +
 +#define ring_buffer_opts__last_field sz
 +
 +LIBBPF_API struct ring_buffer *
 +ring_buffer__new(int map_fd, ring_buffer_sample_fn sample_cb, void *ctx,
 +               const struct ring_buffer_opts *opts);
 +LIBBPF_API void ring_buffer__free(struct ring_buffer *rb);
 +LIBBPF_API int ring_buffer__add(struct ring_buffer *rb, int map_fd,
 +                              ring_buffer_sample_fn sample_cb, void *ctx);
 +LIBBPF_API int ring_buffer__poll(struct ring_buffer *rb, int timeout_ms);
 +LIBBPF_API int ring_buffer__consume(struct ring_buffer *rb);
 +LIBBPF_API int ring_buffer__epoll_fd(const struct ring_buffer *rb);
 +
 +/* Perf buffer APIs */
 +struct perf_buffer;
 +
 +typedef void (*perf_buffer_sample_fn)(void *ctx, int cpu,
 +                                    void *data, __u32 size);
 +typedef void (*perf_buffer_lost_fn)(void *ctx, int cpu, __u64 cnt);
 +
 +/* common use perf buffer options */
 +struct perf_buffer_opts {
 +      union {
 +              size_t sz;
 +              struct { /* DEPRECATED: will be removed in v1.0 */
 +                      /* if specified, sample_cb is called for each sample */
 +                      perf_buffer_sample_fn sample_cb;
 +                      /* if specified, lost_cb is called for each batch of lost samples */
 +                      perf_buffer_lost_fn lost_cb;
 +                      /* ctx is provided to sample_cb and lost_cb */
 +                      void *ctx;
 +              };
 +      };
 +};
 +#define perf_buffer_opts__last_field sz
 +
 +/**
 + * @brief **perf_buffer__new()** creates BPF perfbuf manager for a specified
 + * BPF_PERF_EVENT_ARRAY map
 + * @param map_fd FD of BPF_PERF_EVENT_ARRAY BPF map that will be used by BPF
 + * code to send data over to user-space
 + * @param page_cnt number of memory pages allocated for each per-CPU buffer
 + * @param sample_cb function called on each received data record
 + * @param lost_cb function called when record loss has occurred
 + * @param ctx user-provided extra context passed into *sample_cb* and *lost_cb*
 + * @return a new instance of struct perf_buffer on success, NULL on error with
 + * *errno* containing an error code
 + */
 +LIBBPF_API struct perf_buffer *
 +perf_buffer__new(int map_fd, size_t page_cnt,
 +               perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx,
 +               const struct perf_buffer_opts *opts);
 +
 +LIBBPF_API struct perf_buffer *
 +perf_buffer__new_v0_6_0(int map_fd, size_t page_cnt,
 +                      perf_buffer_sample_fn sample_cb, perf_buffer_lost_fn lost_cb, void *ctx,
 +                      const struct perf_buffer_opts *opts);
 +
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use new variant of perf_buffer__new() instead")
 +struct perf_buffer *perf_buffer__new_deprecated(int map_fd, size_t page_cnt,
 +                                              const struct perf_buffer_opts *opts);
 +
 +#define perf_buffer__new(...) ___libbpf_overload(___perf_buffer_new, __VA_ARGS__)
 +#define ___perf_buffer_new6(map_fd, page_cnt, sample_cb, lost_cb, ctx, opts) \
 +      perf_buffer__new(map_fd, page_cnt, sample_cb, lost_cb, ctx, opts)
 +#define ___perf_buffer_new3(map_fd, page_cnt, opts) \
 +      perf_buffer__new_deprecated(map_fd, page_cnt, opts)
 +
 +enum bpf_perf_event_ret {
 +      LIBBPF_PERF_EVENT_DONE  = 0,
 +      LIBBPF_PERF_EVENT_ERROR = -1,
 +      LIBBPF_PERF_EVENT_CONT  = -2,
 +};
 +
 +struct perf_event_header;
 +
 +typedef enum bpf_perf_event_ret
 +(*perf_buffer_event_fn)(void *ctx, int cpu, struct perf_event_header *event);
 +
 +/* raw perf buffer options, giving most power and control */
 +struct perf_buffer_raw_opts {
 +      union {
 +              struct {
 +                      size_t sz;
 +                      long :0;
 +                      long :0;
 +              };
 +              struct { /* DEPRECATED: will be removed in v1.0 */
 +                      /* perf event attrs passed directly into perf_event_open() */
 +                      struct perf_event_attr *attr;
 +                      /* raw event callback */
 +                      perf_buffer_event_fn event_cb;
 +                      /* ctx is provided to event_cb */
 +                      void *ctx;
 +              };
 +      };
 +      /* if cpu_cnt == 0, open all on all possible CPUs (up to the number of
 +       * max_entries of given PERF_EVENT_ARRAY map)
 +       */
 +      int cpu_cnt;
 +      /* if cpu_cnt > 0, cpus is an array of CPUs to open ring buffers on */
 +      int *cpus;
 +      /* if cpu_cnt > 0, map_keys specify map keys to set per-CPU FDs for */
 +      int *map_keys;
 +};
 +#define perf_buffer_raw_opts__last_field map_keys
 +
 +LIBBPF_API struct perf_buffer *
 +perf_buffer__new_raw(int map_fd, size_t page_cnt, struct perf_event_attr *attr,
 +                   perf_buffer_event_fn event_cb, void *ctx,
 +                   const struct perf_buffer_raw_opts *opts);
 +
 +LIBBPF_API struct perf_buffer *
 +perf_buffer__new_raw_v0_6_0(int map_fd, size_t page_cnt, struct perf_event_attr *attr,
 +                          perf_buffer_event_fn event_cb, void *ctx,
 +                          const struct perf_buffer_raw_opts *opts);
 +
 +LIBBPF_API LIBBPF_DEPRECATED_SINCE(0, 7, "use new variant of perf_buffer__new_raw() instead")
 +struct perf_buffer *perf_buffer__new_raw_deprecated(int map_fd, size_t page_cnt,
 +                                                  const struct perf_buffer_raw_opts *opts);
 +
 +#define perf_buffer__new_raw(...) ___libbpf_overload(___perf_buffer_new_raw, __VA_ARGS__)
 +#define ___perf_buffer_new_raw6(map_fd, page_cnt, attr, event_cb, ctx, opts) \
 +      perf_buffer__new_raw(map_fd, page_cnt, attr, event_cb, ctx, opts)
 +#define ___perf_buffer_new_raw3(map_fd, page_cnt, opts) \
 +      perf_buffer__new_raw_deprecated(map_fd, page_cnt, opts)
 +
 +LIBBPF_API void perf_buffer__free(struct perf_buffer *pb);
 +LIBBPF_API int perf_buffer__epoll_fd(const struct perf_buffer *pb);
 +LIBBPF_API int perf_buffer__poll(struct perf_buffer *pb, int timeout_ms);
 +LIBBPF_API int perf_buffer__consume(struct perf_buffer *pb);
 +LIBBPF_API int perf_buffer__consume_buffer(struct perf_buffer *pb, size_t buf_idx);
 +LIBBPF_API size_t perf_buffer__buffer_cnt(const struct perf_buffer *pb);
 +LIBBPF_API int perf_buffer__buffer_fd(const struct perf_buffer *pb, size_t buf_idx);
 +
 +typedef enum bpf_perf_event_ret
 +      (*bpf_perf_event_print_t)(struct perf_event_header *hdr,
 +                                void *private_data);
++LIBBPF_DEPRECATED_SINCE(0, 8, "use perf_buffer__poll() or  perf_buffer__consume() instead")
 +LIBBPF_API enum bpf_perf_event_ret
 +bpf_perf_event_read_simple(void *mmap_mem, size_t mmap_size, size_t page_size,
 +                         void **copy_mem, size_t *copy_size,
 +                         bpf_perf_event_print_t fn, void *private_data);
 +
 +struct bpf_prog_linfo;
 +struct bpf_prog_info;
 +
 +LIBBPF_API void bpf_prog_linfo__free(struct bpf_prog_linfo *prog_linfo);
 +LIBBPF_API struct bpf_prog_linfo *
 +bpf_prog_linfo__new(const struct bpf_prog_info *info);
 +LIBBPF_API const struct bpf_line_info *
 +bpf_prog_linfo__lfind_addr_func(const struct bpf_prog_linfo *prog_linfo,
 +                              __u64 addr, __u32 func_idx, __u32 nr_skip);
 +LIBBPF_API const struct bpf_line_info *
 +bpf_prog_linfo__lfind(const struct bpf_prog_linfo *prog_linfo,
 +                    __u32 insn_off, __u32 nr_skip);
 +
 +/*
 + * Probe for supported system features
 + *
 + * Note that running many of these probes in a short amount of time can cause
 + * the kernel to reach the maximal size of lockable memory allowed for the
 + * user, causing subsequent probes to fail. In this case, the caller may want
 + * to adjust that limit with setrlimit().
 + */
 +LIBBPF_DEPRECATED_SINCE(0, 8, "use libbpf_probe_bpf_prog_type() instead")
 +LIBBPF_API bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex);
 +LIBBPF_DEPRECATED_SINCE(0, 8, "use libbpf_probe_bpf_map_type() instead")
 +LIBBPF_API bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex);
 +LIBBPF_DEPRECATED_SINCE(0, 8, "use libbpf_probe_bpf_helper() instead")
 +LIBBPF_API bool bpf_probe_helper(enum bpf_func_id id, enum bpf_prog_type prog_type, __u32 ifindex);
 +LIBBPF_DEPRECATED_SINCE(0, 8, "implement your own or use bpftool for feature detection")
 +LIBBPF_API bool bpf_probe_large_insn_limit(__u32 ifindex);
 +
 +/**
 + * @brief **libbpf_probe_bpf_prog_type()** detects if host kernel supports
 + * BPF programs of a given type.
 + * @param prog_type BPF program type to detect kernel support for
 + * @param opts reserved for future extensibility, should be NULL
 + * @return 1, if given program type is supported; 0, if given program type is
 + * not supported; negative error code if feature detection failed or can't be
 + * performed
 + *
 + * Make sure the process has required set of CAP_* permissions (or runs as
 + * root) when performing feature checking.
 + */
 +LIBBPF_API int libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts);
 +/**
 + * @brief **libbpf_probe_bpf_map_type()** detects if host kernel supports
 + * BPF maps of a given type.
 + * @param map_type BPF map type to detect kernel support for
 + * @param opts reserved for future extensibility, should be NULL
 + * @return 1, if given map type is supported; 0, if given map type is
 + * not supported; negative error code if feature detection failed or can't be
 + * performed
 + *
 + * Make sure the process has required set of CAP_* permissions (or runs as
 + * root) when performing feature checking.
 + */
 +LIBBPF_API int libbpf_probe_bpf_map_type(enum bpf_map_type map_type, const void *opts);
 +/**
 + * @brief **libbpf_probe_bpf_helper()** detects if host kernel supports the
 + * use of a given BPF helper from specified BPF program type.
 + * @param prog_type BPF program type used to check the support of BPF helper
 + * @param helper_id BPF helper ID (enum bpf_func_id) to check support for
 + * @param opts reserved for future extensibility, should be NULL
 + * @return 1, if given combination of program type and helper is supported; 0,
 + * if the combination is not supported; negative error code if feature
 + * detection for provided input arguments failed or can't be performed
 + *
 + * Make sure the process has required set of CAP_* permissions (or runs as
 + * root) when performing feature checking.
 + */
 +LIBBPF_API int libbpf_probe_bpf_helper(enum bpf_prog_type prog_type,
 +                                     enum bpf_func_id helper_id, const void *opts);
 +
 +/*
 + * Get bpf_prog_info in continuous memory
 + *
 + * struct bpf_prog_info has multiple arrays. The user has option to choose
 + * arrays to fetch from kernel. The following APIs provide an uniform way to
 + * fetch these data. All arrays in bpf_prog_info are stored in a single
 + * continuous memory region. This makes it easy to store the info in a
 + * file.
 + *
 + * Before writing bpf_prog_info_linear to files, it is necessary to
 + * translate pointers in bpf_prog_info to offsets. Helper functions
 + * bpf_program__bpil_addr_to_offs() and bpf_program__bpil_offs_to_addr()
 + * are introduced to switch between pointers and offsets.
 + *
 + * Examples:
 + *   # To fetch map_ids and prog_tags:
 + *   __u64 arrays = (1UL << BPF_PROG_INFO_MAP_IDS) |
 + *           (1UL << BPF_PROG_INFO_PROG_TAGS);
 + *   struct bpf_prog_info_linear *info_linear =
 + *           bpf_program__get_prog_info_linear(fd, arrays);
 + *
 + *   # To save data in file
 + *   bpf_program__bpil_addr_to_offs(info_linear);
 + *   write(f, info_linear, sizeof(*info_linear) + info_linear->data_len);
 + *
 + *   # To read data from file
 + *   read(f, info_linear, <proper_size>);
 + *   bpf_program__bpil_offs_to_addr(info_linear);
 + */
 +enum bpf_prog_info_array {
 +      BPF_PROG_INFO_FIRST_ARRAY = 0,
 +      BPF_PROG_INFO_JITED_INSNS = 0,
 +      BPF_PROG_INFO_XLATED_INSNS,
 +      BPF_PROG_INFO_MAP_IDS,
 +      BPF_PROG_INFO_JITED_KSYMS,
 +      BPF_PROG_INFO_JITED_FUNC_LENS,
 +      BPF_PROG_INFO_FUNC_INFO,
 +      BPF_PROG_INFO_LINE_INFO,
 +      BPF_PROG_INFO_JITED_LINE_INFO,
 +      BPF_PROG_INFO_PROG_TAGS,
 +      BPF_PROG_INFO_LAST_ARRAY,
 +};
 +
 +struct bpf_prog_info_linear {
 +      /* size of struct bpf_prog_info, when the tool is compiled */
 +      __u32                   info_len;
 +      /* total bytes allocated for data, round up to 8 bytes */
 +      __u32                   data_len;
 +      /* which arrays are included in data */
 +      __u64                   arrays;
 +      struct bpf_prog_info    info;
 +      __u8                    data[];
 +};
 +
 +LIBBPF_DEPRECATED_SINCE(0, 6, "use a custom linear prog_info wrapper")
 +LIBBPF_API struct bpf_prog_info_linear *
 +bpf_program__get_prog_info_linear(int fd, __u64 arrays);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 6, "use a custom linear prog_info wrapper")
 +LIBBPF_API void
 +bpf_program__bpil_addr_to_offs(struct bpf_prog_info_linear *info_linear);
 +
 +LIBBPF_DEPRECATED_SINCE(0, 6, "use a custom linear prog_info wrapper")
 +LIBBPF_API void
 +bpf_program__bpil_offs_to_addr(struct bpf_prog_info_linear *info_linear);
 +
 +/**
 + * @brief **libbpf_num_possible_cpus()** is a helper function to get the
 + * number of possible CPUs that the host kernel supports and expects.
 + * @return number of possible CPUs; or error code on failure
 + *
 + * Example usage:
 + *
 + *     int ncpus = libbpf_num_possible_cpus();
 + *     if (ncpus < 0) {
 + *          // error handling
 + *     }
 + *     long values[ncpus];
 + *     bpf_map_lookup_elem(per_cpu_map_fd, key, values);
 + */
 +LIBBPF_API int libbpf_num_possible_cpus(void);
 +
 +struct bpf_map_skeleton {
 +      const char *name;
 +      struct bpf_map **map;
 +      void **mmaped;
 +};
 +
 +struct bpf_prog_skeleton {
 +      const char *name;
 +      struct bpf_program **prog;
 +      struct bpf_link **link;
 +};
 +
 +struct bpf_object_skeleton {
 +      size_t sz; /* size of this struct, for forward/backward compatibility */
 +
 +      const char *name;
 +      const void *data;
 +      size_t data_sz;
 +
 +      struct bpf_object **obj;
 +
 +      int map_cnt;
 +      int map_skel_sz; /* sizeof(struct bpf_map_skeleton) */
 +      struct bpf_map_skeleton *maps;
 +
 +      int prog_cnt;
 +      int prog_skel_sz; /* sizeof(struct bpf_prog_skeleton) */
 +      struct bpf_prog_skeleton *progs;
 +};
 +
 +LIBBPF_API int
 +bpf_object__open_skeleton(struct bpf_object_skeleton *s,
 +                        const struct bpf_object_open_opts *opts);
 +LIBBPF_API int bpf_object__load_skeleton(struct bpf_object_skeleton *s);
 +LIBBPF_API int bpf_object__attach_skeleton(struct bpf_object_skeleton *s);
 +LIBBPF_API void bpf_object__detach_skeleton(struct bpf_object_skeleton *s);
 +LIBBPF_API void bpf_object__destroy_skeleton(struct bpf_object_skeleton *s);
 +
 +struct gen_loader_opts {
 +      size_t sz; /* size of this struct, for forward/backward compatiblity */
 +      const char *data;
 +      const char *insns;
 +      __u32 data_sz;
 +      __u32 insns_sz;
 +};
 +
 +#define gen_loader_opts__last_field insns_sz
 +LIBBPF_API int bpf_object__gen_loader(struct bpf_object *obj,
 +                                    struct gen_loader_opts *opts);
 +
 +enum libbpf_tristate {
 +      TRI_NO = 0,
 +      TRI_YES = 1,
 +      TRI_MODULE = 2,
 +};
 +
 +struct bpf_linker_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +};
 +#define bpf_linker_opts__last_field sz
 +
 +struct bpf_linker_file_opts {
 +      /* size of this struct, for forward/backward compatiblity */
 +      size_t sz;
 +};
 +#define bpf_linker_file_opts__last_field sz
 +
 +struct bpf_linker;
 +
 +LIBBPF_API struct bpf_linker *bpf_linker__new(const char *filename, struct bpf_linker_opts *opts);
 +LIBBPF_API int bpf_linker__add_file(struct bpf_linker *linker,
 +                                  const char *filename,
 +                                  const struct bpf_linker_file_opts *opts);
 +LIBBPF_API int bpf_linker__finalize(struct bpf_linker *linker);
 +LIBBPF_API void bpf_linker__free(struct bpf_linker *linker);
 +
 +#ifdef __cplusplus
 +} /* extern "C" */
 +#endif
 +
 +#endif /* __LIBBPF_LIBBPF_H */
index 5dbe4f4,0000000..1565679
mode 100644,000000..100644
--- /dev/null
@@@ -1,527 -1,0 +1,529 @@@
 +/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
 +
 +/*
 + * Internal libbpf helpers.
 + *
 + * Copyright (c) 2019 Facebook
 + */
 +
 +#ifndef __LIBBPF_LIBBPF_INTERNAL_H
 +#define __LIBBPF_LIBBPF_INTERNAL_H
 +
 +#include <stdlib.h>
 +#include <limits.h>
 +#include <errno.h>
 +#include <linux/err.h>
 +#include <fcntl.h>
 +#include <unistd.h>
 +#include "libbpf_legacy.h"
 +#include "relo_core.h"
 +
 +/* make sure libbpf doesn't use kernel-only integer typedefs */
 +#pragma GCC poison u8 u16 u32 u64 s8 s16 s32 s64
 +
 +/* prevent accidental re-addition of reallocarray() */
 +#pragma GCC poison reallocarray
 +
 +#include "libbpf.h"
 +#include "btf.h"
 +
 +#ifndef EM_BPF
 +#define EM_BPF 247
 +#endif
 +
 +#ifndef R_BPF_64_64
 +#define R_BPF_64_64 1
 +#endif
 +#ifndef R_BPF_64_ABS64
 +#define R_BPF_64_ABS64 2
 +#endif
 +#ifndef R_BPF_64_ABS32
 +#define R_BPF_64_ABS32 3
 +#endif
 +#ifndef R_BPF_64_32
 +#define R_BPF_64_32 10
 +#endif
 +
 +#ifndef SHT_LLVM_ADDRSIG
 +#define SHT_LLVM_ADDRSIG 0x6FFF4C03
 +#endif
 +
 +/* if libelf is old and doesn't support mmap(), fall back to read() */
 +#ifndef ELF_C_READ_MMAP
 +#define ELF_C_READ_MMAP ELF_C_READ
 +#endif
 +
 +/* Older libelf all end up in this expression, for both 32 and 64 bit */
 +#ifndef ELF64_ST_VISIBILITY
 +#define ELF64_ST_VISIBILITY(o) ((o) & 0x03)
 +#endif
 +
 +#define BTF_INFO_ENC(kind, kind_flag, vlen) \
 +      ((!!(kind_flag) << 31) | ((kind) << 24) | ((vlen) & BTF_MAX_VLEN))
 +#define BTF_TYPE_ENC(name, info, size_or_type) (name), (info), (size_or_type)
 +#define BTF_INT_ENC(encoding, bits_offset, nr_bits) \
 +      ((encoding) << 24 | (bits_offset) << 16 | (nr_bits))
 +#define BTF_TYPE_INT_ENC(name, encoding, bits_offset, bits, sz) \
 +      BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_INT, 0, 0), sz), \
 +      BTF_INT_ENC(encoding, bits_offset, bits)
 +#define BTF_MEMBER_ENC(name, type, bits_offset) (name), (type), (bits_offset)
 +#define BTF_PARAM_ENC(name, type) (name), (type)
 +#define BTF_VAR_SECINFO_ENC(type, offset, size) (type), (offset), (size)
 +#define BTF_TYPE_FLOAT_ENC(name, sz) \
 +      BTF_TYPE_ENC(name, BTF_INFO_ENC(BTF_KIND_FLOAT, 0, 0), sz)
 +#define BTF_TYPE_DECL_TAG_ENC(value, type, component_idx) \
 +      BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_DECL_TAG, 0, 0), type), (component_idx)
 +#define BTF_TYPE_TYPE_TAG_ENC(value, type) \
 +      BTF_TYPE_ENC(value, BTF_INFO_ENC(BTF_KIND_TYPE_TAG, 0, 0), type)
 +
 +#ifndef likely
 +#define likely(x) __builtin_expect(!!(x), 1)
 +#endif
 +#ifndef unlikely
 +#define unlikely(x) __builtin_expect(!!(x), 0)
 +#endif
 +#ifndef min
 +# define min(x, y) ((x) < (y) ? (x) : (y))
 +#endif
 +#ifndef max
 +# define max(x, y) ((x) < (y) ? (y) : (x))
 +#endif
 +#ifndef offsetofend
 +# define offsetofend(TYPE, FIELD) \
 +      (offsetof(TYPE, FIELD) + sizeof(((TYPE *)0)->FIELD))
 +#endif
 +
 +/* Check whether a string `str` has prefix `pfx`, regardless if `pfx` is
 + * a string literal known at compilation time or char * pointer known only at
 + * runtime.
 + */
 +#define str_has_pfx(str, pfx) \
 +      (strncmp(str, pfx, __builtin_constant_p(pfx) ? sizeof(pfx) - 1 : strlen(pfx)) == 0)
 +
 +/* Symbol versioning is different between static and shared library.
 + * Properly versioned symbols are needed for shared library, but
 + * only the symbol of the new version is needed for static library.
 + * Starting with GNU C 10, use symver attribute instead of .symver assembler
 + * directive, which works better with GCC LTO builds.
 + */
 +#if defined(SHARED) && defined(__GNUC__) && __GNUC__ >= 10
 +
 +#define DEFAULT_VERSION(internal_name, api_name, version) \
 +      __attribute__((symver(#api_name "@@" #version)))
 +#define COMPAT_VERSION(internal_name, api_name, version) \
 +      __attribute__((symver(#api_name "@" #version)))
 +
 +#elif defined(SHARED)
 +
 +#define COMPAT_VERSION(internal_name, api_name, version) \
 +      asm(".symver " #internal_name "," #api_name "@" #version);
 +#define DEFAULT_VERSION(internal_name, api_name, version) \
 +      asm(".symver " #internal_name "," #api_name "@@" #version);
 +
 +#else /* !SHARED */
 +
 +#define COMPAT_VERSION(internal_name, api_name, version)
 +#define DEFAULT_VERSION(internal_name, api_name, version) \
 +      extern typeof(internal_name) api_name \
 +      __attribute__((alias(#internal_name)));
 +
 +#endif
 +
 +extern void libbpf_print(enum libbpf_print_level level,
 +                       const char *format, ...)
 +      __attribute__((format(printf, 2, 3)));
 +
 +#define __pr(level, fmt, ...) \
 +do {                          \
 +      libbpf_print(level, "libbpf: " fmt, ##__VA_ARGS__);     \
 +} while (0)
 +
 +#define pr_warn(fmt, ...)     __pr(LIBBPF_WARN, fmt, ##__VA_ARGS__)
 +#define pr_info(fmt, ...)     __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
 +#define pr_debug(fmt, ...)    __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
 +
 +#ifndef __has_builtin
 +#define __has_builtin(x) 0
 +#endif
 +/*
 + * Re-implement glibc's reallocarray() for libbpf internal-only use.
 + * reallocarray(), unfortunately, is not available in all versions of glibc,
 + * so requires extra feature detection and using reallocarray() stub from
 + * <tools/libc_compat.h> and COMPAT_NEED_REALLOCARRAY. All this complicates
 + * build of libbpf unnecessarily and is just a maintenance burden. Instead,
 + * it's trivial to implement libbpf-specific internal version and use it
 + * throughout libbpf.
 + */
 +static inline void *libbpf_reallocarray(void *ptr, size_t nmemb, size_t size)
 +{
 +      size_t total;
 +
 +#if __has_builtin(__builtin_mul_overflow)
 +      if (unlikely(__builtin_mul_overflow(nmemb, size, &total)))
 +              return NULL;
 +#else
 +      if (size == 0 || nmemb > ULONG_MAX / size)
 +              return NULL;
 +      total = nmemb * size;
 +#endif
 +      return realloc(ptr, total);
 +}
 +
 +/* Copy up to sz - 1 bytes from zero-terminated src string and ensure that dst
 + * is zero-terminated string no matter what (unless sz == 0, in which case
 + * it's a no-op). It's conceptually close to FreeBSD's strlcpy(), but differs
 + * in what is returned. Given this is internal helper, it's trivial to extend
 + * this, when necessary. Use this instead of strncpy inside libbpf source code.
 + */
 +static inline void libbpf_strlcpy(char *dst, const char *src, size_t sz)
 +{
 +      size_t i;
 +
 +      if (sz == 0)
 +              return;
 +
 +      sz--;
 +      for (i = 0; i < sz && src[i]; i++)
 +              dst[i] = src[i];
 +      dst[i] = '\0';
 +}
 +
++__u32 get_kernel_version(void);
++
 +struct btf;
 +struct btf_type;
 +
 +struct btf_type *btf_type_by_id(const struct btf *btf, __u32 type_id);
 +const char *btf_kind_str(const struct btf_type *t);
 +const struct btf_type *skip_mods_and_typedefs(const struct btf *btf, __u32 id, __u32 *res_id);
 +
 +static inline enum btf_func_linkage btf_func_linkage(const struct btf_type *t)
 +{
 +      return (enum btf_func_linkage)(int)btf_vlen(t);
 +}
 +
 +static inline __u32 btf_type_info(int kind, int vlen, int kflag)
 +{
 +      return (kflag << 31) | (kind << 24) | vlen;
 +}
 +
 +enum map_def_parts {
 +      MAP_DEF_MAP_TYPE        = 0x001,
 +      MAP_DEF_KEY_TYPE        = 0x002,
 +      MAP_DEF_KEY_SIZE        = 0x004,
 +      MAP_DEF_VALUE_TYPE      = 0x008,
 +      MAP_DEF_VALUE_SIZE      = 0x010,
 +      MAP_DEF_MAX_ENTRIES     = 0x020,
 +      MAP_DEF_MAP_FLAGS       = 0x040,
 +      MAP_DEF_NUMA_NODE       = 0x080,
 +      MAP_DEF_PINNING         = 0x100,
 +      MAP_DEF_INNER_MAP       = 0x200,
 +      MAP_DEF_MAP_EXTRA       = 0x400,
 +
 +      MAP_DEF_ALL             = 0x7ff, /* combination of all above */
 +};
 +
 +struct btf_map_def {
 +      enum map_def_parts parts;
 +      __u32 map_type;
 +      __u32 key_type_id;
 +      __u32 key_size;
 +      __u32 value_type_id;
 +      __u32 value_size;
 +      __u32 max_entries;
 +      __u32 map_flags;
 +      __u32 numa_node;
 +      __u32 pinning;
 +      __u64 map_extra;
 +};
 +
 +int parse_btf_map_def(const char *map_name, struct btf *btf,
 +                    const struct btf_type *def_t, bool strict,
 +                    struct btf_map_def *map_def, struct btf_map_def *inner_def);
 +
 +void *libbpf_add_mem(void **data, size_t *cap_cnt, size_t elem_sz,
 +                   size_t cur_cnt, size_t max_cnt, size_t add_cnt);
 +int libbpf_ensure_mem(void **data, size_t *cap_cnt, size_t elem_sz, size_t need_cnt);
 +
 +static inline bool libbpf_is_mem_zeroed(const char *p, ssize_t len)
 +{
 +      while (len > 0) {
 +              if (*p)
 +                      return false;
 +              p++;
 +              len--;
 +      }
 +      return true;
 +}
 +
 +static inline bool libbpf_validate_opts(const char *opts,
 +                                      size_t opts_sz, size_t user_sz,
 +                                      const char *type_name)
 +{
 +      if (user_sz < sizeof(size_t)) {
 +              pr_warn("%s size (%zu) is too small\n", type_name, user_sz);
 +              return false;
 +      }
 +      if (!libbpf_is_mem_zeroed(opts + opts_sz, (ssize_t)user_sz - opts_sz)) {
 +              pr_warn("%s has non-zero extra bytes\n", type_name);
 +              return false;
 +      }
 +      return true;
 +}
 +
 +#define OPTS_VALID(opts, type)                                                      \
 +      (!(opts) || libbpf_validate_opts((const char *)opts,                  \
 +                                       offsetofend(struct type,             \
 +                                                   type##__last_field),     \
 +                                       (opts)->sz, #type))
 +#define OPTS_HAS(opts, field) \
 +      ((opts) && opts->sz >= offsetofend(typeof(*(opts)), field))
 +#define OPTS_GET(opts, field, fallback_value) \
 +      (OPTS_HAS(opts, field) ? (opts)->field : fallback_value)
 +#define OPTS_SET(opts, field, value)          \
 +      do {                                    \
 +              if (OPTS_HAS(opts, field))      \
 +                      (opts)->field = value;  \
 +      } while (0)
 +
 +#define OPTS_ZEROED(opts, last_nonzero_field)                               \
 +({                                                                          \
 +      ssize_t __off = offsetofend(typeof(*(opts)), last_nonzero_field);     \
 +      !(opts) || libbpf_is_mem_zeroed((const void *)opts + __off,           \
 +                                      (opts)->sz - __off);                  \
 +})
 +
 +enum kern_feature_id {
 +      /* v4.14: kernel support for program & map names. */
 +      FEAT_PROG_NAME,
 +      /* v5.2: kernel support for global data sections. */
 +      FEAT_GLOBAL_DATA,
 +      /* BTF support */
 +      FEAT_BTF,
 +      /* BTF_KIND_FUNC and BTF_KIND_FUNC_PROTO support */
 +      FEAT_BTF_FUNC,
 +      /* BTF_KIND_VAR and BTF_KIND_DATASEC support */
 +      FEAT_BTF_DATASEC,
 +      /* BTF_FUNC_GLOBAL is supported */
 +      FEAT_BTF_GLOBAL_FUNC,
 +      /* BPF_F_MMAPABLE is supported for arrays */
 +      FEAT_ARRAY_MMAP,
 +      /* kernel support for expected_attach_type in BPF_PROG_LOAD */
 +      FEAT_EXP_ATTACH_TYPE,
 +      /* bpf_probe_read_{kernel,user}[_str] helpers */
 +      FEAT_PROBE_READ_KERN,
 +      /* BPF_PROG_BIND_MAP is supported */
 +      FEAT_PROG_BIND_MAP,
 +      /* Kernel support for module BTFs */
 +      FEAT_MODULE_BTF,
 +      /* BTF_KIND_FLOAT support */
 +      FEAT_BTF_FLOAT,
 +      /* BPF perf link support */
 +      FEAT_PERF_LINK,
 +      /* BTF_KIND_DECL_TAG support */
 +      FEAT_BTF_DECL_TAG,
 +      /* BTF_KIND_TYPE_TAG support */
 +      FEAT_BTF_TYPE_TAG,
 +      /* memcg-based accounting for BPF maps and progs */
 +      FEAT_MEMCG_ACCOUNT,
 +      __FEAT_CNT,
 +};
 +
 +int probe_memcg_account(void);
 +bool kernel_supports(const struct bpf_object *obj, enum kern_feature_id feat_id);
 +int bump_rlimit_memlock(void);
 +
 +int parse_cpu_mask_str(const char *s, bool **mask, int *mask_sz);
 +int parse_cpu_mask_file(const char *fcpu, bool **mask, int *mask_sz);
 +int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
 +                       const char *str_sec, size_t str_len);
 +int btf_load_into_kernel(struct btf *btf, char *log_buf, size_t log_sz, __u32 log_level);
 +
 +struct btf *btf_get_from_fd(int btf_fd, struct btf *base_btf);
 +void btf_get_kernel_prefix_kind(enum bpf_attach_type attach_type,
 +                              const char **prefix, int *kind);
 +
 +struct btf_ext_info {
 +      /*
 +       * info points to the individual info section (e.g. func_info and
 +       * line_info) from the .BTF.ext. It does not include the __u32 rec_size.
 +       */
 +      void *info;
 +      __u32 rec_size;
 +      __u32 len;
 +};
 +
 +#define for_each_btf_ext_sec(seg, sec)                                        \
 +      for (sec = (seg)->info;                                         \
 +           (void *)sec < (seg)->info + (seg)->len;                    \
 +           sec = (void *)sec + sizeof(struct btf_ext_info_sec) +      \
 +                 (seg)->rec_size * sec->num_info)
 +
 +#define for_each_btf_ext_rec(seg, sec, i, rec)                                \
 +      for (i = 0, rec = (void *)&(sec)->data;                         \
 +           i < (sec)->num_info;                                       \
 +           i++, rec = (void *)rec + (seg)->rec_size)
 +
 +/*
 + * The .BTF.ext ELF section layout defined as
 + *   struct btf_ext_header
 + *   func_info subsection
 + *
 + * The func_info subsection layout:
 + *   record size for struct bpf_func_info in the func_info subsection
 + *   struct btf_sec_func_info for section #1
 + *   a list of bpf_func_info records for section #1
 + *     where struct bpf_func_info mimics one in include/uapi/linux/bpf.h
 + *     but may not be identical
 + *   struct btf_sec_func_info for section #2
 + *   a list of bpf_func_info records for section #2
 + *   ......
 + *
 + * Note that the bpf_func_info record size in .BTF.ext may not
 + * be the same as the one defined in include/uapi/linux/bpf.h.
 + * The loader should ensure that record_size meets minimum
 + * requirement and pass the record as is to the kernel. The
 + * kernel will handle the func_info properly based on its contents.
 + */
 +struct btf_ext_header {
 +      __u16   magic;
 +      __u8    version;
 +      __u8    flags;
 +      __u32   hdr_len;
 +
 +      /* All offsets are in bytes relative to the end of this header */
 +      __u32   func_info_off;
 +      __u32   func_info_len;
 +      __u32   line_info_off;
 +      __u32   line_info_len;
 +
 +      /* optional part of .BTF.ext header */
 +      __u32   core_relo_off;
 +      __u32   core_relo_len;
 +};
 +
 +struct btf_ext {
 +      union {
 +              struct btf_ext_header *hdr;
 +              void *data;
 +      };
 +      struct btf_ext_info func_info;
 +      struct btf_ext_info line_info;
 +      struct btf_ext_info core_relo_info;
 +      __u32 data_size;
 +};
 +
 +struct btf_ext_info_sec {
 +      __u32   sec_name_off;
 +      __u32   num_info;
 +      /* Followed by num_info * record_size number of bytes */
 +      __u8    data[];
 +};
 +
 +/* The minimum bpf_func_info checked by the loader */
 +struct bpf_func_info_min {
 +      __u32   insn_off;
 +      __u32   type_id;
 +};
 +
 +/* The minimum bpf_line_info checked by the loader */
 +struct bpf_line_info_min {
 +      __u32   insn_off;
 +      __u32   file_name_off;
 +      __u32   line_off;
 +      __u32   line_col;
 +};
 +
 +
 +typedef int (*type_id_visit_fn)(__u32 *type_id, void *ctx);
 +typedef int (*str_off_visit_fn)(__u32 *str_off, void *ctx);
 +int btf_type_visit_type_ids(struct btf_type *t, type_id_visit_fn visit, void *ctx);
 +int btf_type_visit_str_offs(struct btf_type *t, str_off_visit_fn visit, void *ctx);
 +int btf_ext_visit_type_ids(struct btf_ext *btf_ext, type_id_visit_fn visit, void *ctx);
 +int btf_ext_visit_str_offs(struct btf_ext *btf_ext, str_off_visit_fn visit, void *ctx);
 +__s32 btf__find_by_name_kind_own(const struct btf *btf, const char *type_name,
 +                               __u32 kind);
 +
 +extern enum libbpf_strict_mode libbpf_mode;
 +
 +/* handle direct returned errors */
 +static inline int libbpf_err(int ret)
 +{
 +      if (ret < 0)
 +              errno = -ret;
 +      return ret;
 +}
 +
 +/* handle errno-based (e.g., syscall or libc) errors according to libbpf's
 + * strict mode settings
 + */
 +static inline int libbpf_err_errno(int ret)
 +{
 +      if (libbpf_mode & LIBBPF_STRICT_DIRECT_ERRS)
 +              /* errno is already assumed to be set on error */
 +              return ret < 0 ? -errno : ret;
 +
 +      /* legacy: on error return -1 directly and don't touch errno */
 +      return ret;
 +}
 +
 +/* handle error for pointer-returning APIs, err is assumed to be < 0 always */
 +static inline void *libbpf_err_ptr(int err)
 +{
 +      /* set errno on error, this doesn't break anything */
 +      errno = -err;
 +
 +      if (libbpf_mode & LIBBPF_STRICT_CLEAN_PTRS)
 +              return NULL;
 +
 +      /* legacy: encode err as ptr */
 +      return ERR_PTR(err);
 +}
 +
 +/* handle pointer-returning APIs' error handling */
 +static inline void *libbpf_ptr(void *ret)
 +{
 +      /* set errno on error, this doesn't break anything */
 +      if (IS_ERR(ret))
 +              errno = -PTR_ERR(ret);
 +
 +      if (libbpf_mode & LIBBPF_STRICT_CLEAN_PTRS)
 +              return IS_ERR(ret) ? NULL : ret;
 +
 +      /* legacy: pass-through original pointer */
 +      return ret;
 +}
 +
 +static inline bool str_is_empty(const char *s)
 +{
 +      return !s || !s[0];
 +}
 +
 +static inline bool is_ldimm64_insn(struct bpf_insn *insn)
 +{
 +      return insn->code == (BPF_LD | BPF_IMM | BPF_DW);
 +}
 +
 +/* if fd is stdin, stdout, or stderr, dup to a fd greater than 2
 + * Takes ownership of the fd passed in, and closes it if calling
 + * fcntl(fd, F_DUPFD_CLOEXEC, 3).
 + */
 +static inline int ensure_good_fd(int fd)
 +{
 +      int old_fd = fd, saved_errno;
 +
 +      if (fd < 0)
 +              return fd;
 +      if (fd < 3) {
 +              fd = fcntl(fd, F_DUPFD_CLOEXEC, 3);
 +              saved_errno = errno;
 +              close(old_fd);
 +              if (fd < 0) {
 +                      pr_warn("failed to dup FD %d to FD > 2: %d\n", old_fd, -saved_errno);
 +                      errno = saved_errno;
 +              }
 +      }
 +      return fd;
 +}
 +
 +#endif /* __LIBBPF_LIBBPF_INTERNAL_H */
index 9d39852,0000000..97b06ce
mode 100644,000000..100644
--- /dev/null
@@@ -1,493 -1,0 +1,477 @@@
- static int get_kernel_version(void)
- {
-       int version, subversion, patchlevel;
-       struct utsname utsn;
-       /* Return 0 on failure, and attempt to probe with empty kversion */
-       if (uname(&utsn))
-               return 0;
-       if (sscanf(utsn.release, "%d.%d.%d",
-                  &version, &subversion, &patchlevel) != 3)
-               return 0;
-       return (version << 16) + (subversion << 8) + patchlevel;
- }
 +// SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause)
 +/* Copyright (c) 2019 Netronome Systems, Inc. */
 +
 +#include <errno.h>
 +#include <fcntl.h>
 +#include <string.h>
 +#include <stdlib.h>
 +#include <unistd.h>
 +#include <net/if.h>
 +#include <sys/utsname.h>
 +
 +#include <linux/btf.h>
 +#include <linux/filter.h>
 +#include <linux/kernel.h>
 +
 +#include "bpf.h"
 +#include "libbpf.h"
 +#include "libbpf_internal.h"
 +
 +static bool grep(const char *buffer, const char *pattern)
 +{
 +      return !!strstr(buffer, pattern);
 +}
 +
 +static int get_vendor_id(int ifindex)
 +{
 +      char ifname[IF_NAMESIZE], path[64], buf[8];
 +      ssize_t len;
 +      int fd;
 +
 +      if (!if_indextoname(ifindex, ifname))
 +              return -1;
 +
 +      snprintf(path, sizeof(path), "/sys/class/net/%s/device/vendor", ifname);
 +
 +      fd = open(path, O_RDONLY | O_CLOEXEC);
 +      if (fd < 0)
 +              return -1;
 +
 +      len = read(fd, buf, sizeof(buf));
 +      close(fd);
 +      if (len < 0)
 +              return -1;
 +      if (len >= (ssize_t)sizeof(buf))
 +              return -1;
 +      buf[len] = '\0';
 +
 +      return strtol(buf, NULL, 0);
 +}
 +
 +static int probe_prog_load(enum bpf_prog_type prog_type,
 +                         const struct bpf_insn *insns, size_t insns_cnt,
 +                         char *log_buf, size_t log_buf_sz,
 +                         __u32 ifindex)
 +{
 +      LIBBPF_OPTS(bpf_prog_load_opts, opts,
 +              .log_buf = log_buf,
 +              .log_size = log_buf_sz,
 +              .log_level = log_buf ? 1 : 0,
 +              .prog_ifindex = ifindex,
 +      );
 +      int fd, err, exp_err = 0;
 +      const char *exp_msg = NULL;
 +      char buf[4096];
 +
 +      switch (prog_type) {
 +      case BPF_PROG_TYPE_CGROUP_SOCK_ADDR:
 +              opts.expected_attach_type = BPF_CGROUP_INET4_CONNECT;
 +              break;
 +      case BPF_PROG_TYPE_CGROUP_SOCKOPT:
 +              opts.expected_attach_type = BPF_CGROUP_GETSOCKOPT;
 +              break;
 +      case BPF_PROG_TYPE_SK_LOOKUP:
 +              opts.expected_attach_type = BPF_SK_LOOKUP;
 +              break;
 +      case BPF_PROG_TYPE_KPROBE:
 +              opts.kern_version = get_kernel_version();
 +              break;
 +      case BPF_PROG_TYPE_LIRC_MODE2:
 +              opts.expected_attach_type = BPF_LIRC_MODE2;
 +              break;
 +      case BPF_PROG_TYPE_TRACING:
 +      case BPF_PROG_TYPE_LSM:
 +              opts.log_buf = buf;
 +              opts.log_size = sizeof(buf);
 +              opts.log_level = 1;
 +              if (prog_type == BPF_PROG_TYPE_TRACING)
 +                      opts.expected_attach_type = BPF_TRACE_FENTRY;
 +              else
 +                      opts.expected_attach_type = BPF_MODIFY_RETURN;
 +              opts.attach_btf_id = 1;
 +
 +              exp_err = -EINVAL;
 +              exp_msg = "attach_btf_id 1 is not a function";
 +              break;
 +      case BPF_PROG_TYPE_EXT:
 +              opts.log_buf = buf;
 +              opts.log_size = sizeof(buf);
 +              opts.log_level = 1;
 +              opts.attach_btf_id = 1;
 +
 +              exp_err = -EINVAL;
 +              exp_msg = "Cannot replace kernel functions";
 +              break;
 +      case BPF_PROG_TYPE_SYSCALL:
 +              opts.prog_flags = BPF_F_SLEEPABLE;
 +              break;
 +      case BPF_PROG_TYPE_STRUCT_OPS:
 +              exp_err = -524; /* -ENOTSUPP */
 +              break;
 +      case BPF_PROG_TYPE_UNSPEC:
 +      case BPF_PROG_TYPE_SOCKET_FILTER:
 +      case BPF_PROG_TYPE_SCHED_CLS:
 +      case BPF_PROG_TYPE_SCHED_ACT:
 +      case BPF_PROG_TYPE_TRACEPOINT:
 +      case BPF_PROG_TYPE_XDP:
 +      case BPF_PROG_TYPE_PERF_EVENT:
 +      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_RAW_TRACEPOINT:
 +      case BPF_PROG_TYPE_RAW_TRACEPOINT_WRITABLE:
 +      case BPF_PROG_TYPE_LWT_SEG6LOCAL:
 +      case BPF_PROG_TYPE_SK_REUSEPORT:
 +      case BPF_PROG_TYPE_FLOW_DISSECTOR:
 +      case BPF_PROG_TYPE_CGROUP_SYSCTL:
 +              break;
 +      default:
 +              return -EOPNOTSUPP;
 +      }
 +
 +      fd = bpf_prog_load(prog_type, NULL, "GPL", insns, insns_cnt, &opts);
 +      err = -errno;
 +      if (fd >= 0)
 +              close(fd);
 +      if (exp_err) {
 +              if (fd >= 0 || err != exp_err)
 +                      return 0;
 +              if (exp_msg && !strstr(buf, exp_msg))
 +                      return 0;
 +              return 1;
 +      }
 +      return fd >= 0 ? 1 : 0;
 +}
 +
 +int libbpf_probe_bpf_prog_type(enum bpf_prog_type prog_type, const void *opts)
 +{
 +      struct bpf_insn insns[] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN()
 +      };
 +      const size_t insn_cnt = ARRAY_SIZE(insns);
 +      int ret;
 +
 +      if (opts)
 +              return libbpf_err(-EINVAL);
 +
 +      ret = probe_prog_load(prog_type, insns, insn_cnt, NULL, 0, 0);
 +      return libbpf_err(ret);
 +}
 +
 +bool bpf_probe_prog_type(enum bpf_prog_type prog_type, __u32 ifindex)
 +{
 +      struct bpf_insn insns[2] = {
 +              BPF_MOV64_IMM(BPF_REG_0, 0),
 +              BPF_EXIT_INSN()
 +      };
 +
 +      /* prefer libbpf_probe_bpf_prog_type() unless offload is requested */
 +      if (ifindex == 0)
 +              return libbpf_probe_bpf_prog_type(prog_type, NULL) == 1;
 +
 +      if (ifindex && prog_type == BPF_PROG_TYPE_SCHED_CLS)
 +              /* nfp returns -EINVAL on exit(0) with TC offload */
 +              insns[0].imm = 2;
 +
 +      errno = 0;
 +      probe_prog_load(prog_type, insns, ARRAY_SIZE(insns), NULL, 0, ifindex);
 +
 +      return errno != EINVAL && errno != EOPNOTSUPP;
 +}
 +
 +int libbpf__load_raw_btf(const char *raw_types, size_t types_len,
 +                       const char *str_sec, size_t str_len)
 +{
 +      struct btf_header hdr = {
 +              .magic = BTF_MAGIC,
 +              .version = BTF_VERSION,
 +              .hdr_len = sizeof(struct btf_header),
 +              .type_len = types_len,
 +              .str_off = types_len,
 +              .str_len = str_len,
 +      };
 +      int btf_fd, btf_len;
 +      __u8 *raw_btf;
 +
 +      btf_len = hdr.hdr_len + hdr.type_len + hdr.str_len;
 +      raw_btf = malloc(btf_len);
 +      if (!raw_btf)
 +              return -ENOMEM;
 +
 +      memcpy(raw_btf, &hdr, sizeof(hdr));
 +      memcpy(raw_btf + hdr.hdr_len, raw_types, hdr.type_len);
 +      memcpy(raw_btf + hdr.hdr_len + hdr.type_len, str_sec, hdr.str_len);
 +
 +      btf_fd = bpf_btf_load(raw_btf, btf_len, NULL);
 +
 +      free(raw_btf);
 +      return btf_fd;
 +}
 +
 +static int load_local_storage_btf(void)
 +{
 +      const char strs[] = "\0bpf_spin_lock\0val\0cnt\0l";
 +      /* struct bpf_spin_lock {
 +       *   int val;
 +       * };
 +       * struct val {
 +       *   int cnt;
 +       *   struct bpf_spin_lock l;
 +       * };
 +       */
 +      __u32 types[] = {
 +              /* int */
 +              BTF_TYPE_INT_ENC(0, BTF_INT_SIGNED, 0, 32, 4),  /* [1] */
 +              /* struct bpf_spin_lock */                      /* [2] */
 +              BTF_TYPE_ENC(1, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 1), 4),
 +              BTF_MEMBER_ENC(15, 1, 0), /* int val; */
 +              /* struct val */                                /* [3] */
 +              BTF_TYPE_ENC(15, BTF_INFO_ENC(BTF_KIND_STRUCT, 0, 2), 8),
 +              BTF_MEMBER_ENC(19, 1, 0), /* int cnt; */
 +              BTF_MEMBER_ENC(23, 2, 32),/* struct bpf_spin_lock l; */
 +      };
 +
 +      return libbpf__load_raw_btf((char *)types, sizeof(types),
 +                                   strs, sizeof(strs));
 +}
 +
 +static int probe_map_create(enum bpf_map_type map_type, __u32 ifindex)
 +{
 +      LIBBPF_OPTS(bpf_map_create_opts, opts);
 +      int key_size, value_size, max_entries;
 +      __u32 btf_key_type_id = 0, btf_value_type_id = 0;
 +      int fd = -1, btf_fd = -1, fd_inner = -1, exp_err = 0, err;
 +
 +      opts.map_ifindex = ifindex;
 +
 +      key_size        = sizeof(__u32);
 +      value_size      = sizeof(__u32);
 +      max_entries     = 1;
 +
 +      switch (map_type) {
 +      case BPF_MAP_TYPE_STACK_TRACE:
 +              value_size      = sizeof(__u64);
 +              break;
 +      case BPF_MAP_TYPE_LPM_TRIE:
 +              key_size        = sizeof(__u64);
 +              value_size      = sizeof(__u64);
 +              opts.map_flags  = BPF_F_NO_PREALLOC;
 +              break;
 +      case BPF_MAP_TYPE_CGROUP_STORAGE:
 +      case BPF_MAP_TYPE_PERCPU_CGROUP_STORAGE:
 +              key_size        = sizeof(struct bpf_cgroup_storage_key);
 +              value_size      = sizeof(__u64);
 +              max_entries     = 0;
 +              break;
 +      case BPF_MAP_TYPE_QUEUE:
 +      case BPF_MAP_TYPE_STACK:
 +              key_size        = 0;
 +              break;
 +      case BPF_MAP_TYPE_SK_STORAGE:
 +      case BPF_MAP_TYPE_INODE_STORAGE:
 +      case BPF_MAP_TYPE_TASK_STORAGE:
 +              btf_key_type_id = 1;
 +              btf_value_type_id = 3;
 +              value_size = 8;
 +              max_entries = 0;
 +              opts.map_flags = BPF_F_NO_PREALLOC;
 +              btf_fd = load_local_storage_btf();
 +              if (btf_fd < 0)
 +                      return btf_fd;
 +              break;
 +      case BPF_MAP_TYPE_RINGBUF:
 +              key_size = 0;
 +              value_size = 0;
 +              max_entries = 4096;
 +              break;
 +      case BPF_MAP_TYPE_STRUCT_OPS:
 +              /* we'll get -ENOTSUPP for invalid BTF type ID for struct_ops */
 +              opts.btf_vmlinux_value_type_id = 1;
 +              exp_err = -524; /* -ENOTSUPP */
 +              break;
 +      case BPF_MAP_TYPE_BLOOM_FILTER:
 +              key_size = 0;
 +              max_entries = 1;
 +              break;
 +      case BPF_MAP_TYPE_HASH:
 +      case BPF_MAP_TYPE_ARRAY:
 +      case BPF_MAP_TYPE_PROG_ARRAY:
 +      case BPF_MAP_TYPE_PERF_EVENT_ARRAY:
 +      case BPF_MAP_TYPE_PERCPU_HASH:
 +      case BPF_MAP_TYPE_PERCPU_ARRAY:
 +      case BPF_MAP_TYPE_CGROUP_ARRAY:
 +      case BPF_MAP_TYPE_LRU_HASH:
 +      case BPF_MAP_TYPE_LRU_PERCPU_HASH:
 +      case BPF_MAP_TYPE_ARRAY_OF_MAPS:
 +      case BPF_MAP_TYPE_HASH_OF_MAPS:
 +      case BPF_MAP_TYPE_DEVMAP:
 +      case BPF_MAP_TYPE_DEVMAP_HASH:
 +      case BPF_MAP_TYPE_SOCKMAP:
 +      case BPF_MAP_TYPE_CPUMAP:
 +      case BPF_MAP_TYPE_XSKMAP:
 +      case BPF_MAP_TYPE_SOCKHASH:
 +      case BPF_MAP_TYPE_REUSEPORT_SOCKARRAY:
 +              break;
 +      case BPF_MAP_TYPE_UNSPEC:
 +      default:
 +              return -EOPNOTSUPP;
 +      }
 +
 +      if (map_type == BPF_MAP_TYPE_ARRAY_OF_MAPS ||
 +          map_type == BPF_MAP_TYPE_HASH_OF_MAPS) {
 +              /* TODO: probe for device, once libbpf has a function to create
 +               * map-in-map for offload
 +               */
 +              if (ifindex)
 +                      goto cleanup;
 +
 +              fd_inner = bpf_map_create(BPF_MAP_TYPE_HASH, NULL,
 +                                        sizeof(__u32), sizeof(__u32), 1, NULL);
 +              if (fd_inner < 0)
 +                      goto cleanup;
 +
 +              opts.inner_map_fd = fd_inner;
 +      }
 +
 +      if (btf_fd >= 0) {
 +              opts.btf_fd = btf_fd;
 +              opts.btf_key_type_id = btf_key_type_id;
 +              opts.btf_value_type_id = btf_value_type_id;
 +      }
 +
 +      fd = bpf_map_create(map_type, NULL, key_size, value_size, max_entries, &opts);
 +      err = -errno;
 +
 +cleanup:
 +      if (fd >= 0)
 +              close(fd);
 +      if (fd_inner >= 0)
 +              close(fd_inner);
 +      if (btf_fd >= 0)
 +              close(btf_fd);
 +
 +      if (exp_err)
 +              return fd < 0 && err == exp_err ? 1 : 0;
 +      else
 +              return fd >= 0 ? 1 : 0;
 +}
 +
 +int libbpf_probe_bpf_map_type(enum bpf_map_type map_type, const void *opts)
 +{
 +      int ret;
 +
 +      if (opts)
 +              return libbpf_err(-EINVAL);
 +
 +      ret = probe_map_create(map_type, 0);
 +      return libbpf_err(ret);
 +}
 +
 +bool bpf_probe_map_type(enum bpf_map_type map_type, __u32 ifindex)
 +{
 +      return probe_map_create(map_type, ifindex) == 1;
 +}
 +
 +int libbpf_probe_bpf_helper(enum bpf_prog_type prog_type, enum bpf_func_id helper_id,
 +                          const void *opts)
 +{
 +      struct bpf_insn insns[] = {
 +              BPF_EMIT_CALL((__u32)helper_id),
 +              BPF_EXIT_INSN(),
 +      };
 +      const size_t insn_cnt = ARRAY_SIZE(insns);
 +      char buf[4096];
 +      int ret;
 +
 +      if (opts)
 +              return libbpf_err(-EINVAL);
 +
 +      /* we can't successfully load all prog types to check for BPF helper
 +       * support, so bail out with -EOPNOTSUPP error
 +       */
 +      switch (prog_type) {
 +      case BPF_PROG_TYPE_TRACING:
 +      case BPF_PROG_TYPE_EXT:
 +      case BPF_PROG_TYPE_LSM:
 +      case BPF_PROG_TYPE_STRUCT_OPS:
 +              return -EOPNOTSUPP;
 +      default:
 +              break;
 +      }
 +
 +      buf[0] = '\0';
 +      ret = probe_prog_load(prog_type, insns, insn_cnt, buf, sizeof(buf), 0);
 +      if (ret < 0)
 +              return libbpf_err(ret);
 +
 +      /* If BPF verifier doesn't recognize BPF helper ID (enum bpf_func_id)
 +       * at all, it will emit something like "invalid func unknown#181".
 +       * If BPF verifier recognizes BPF helper but it's not supported for
 +       * given BPF program type, it will emit "unknown func bpf_sys_bpf#166".
 +       * In both cases, provided combination of BPF program type and BPF
 +       * helper is not supported by the kernel.
 +       * In all other cases, probe_prog_load() above will either succeed (e.g.,
 +       * because BPF helper happens to accept no input arguments or it
 +       * accepts one input argument and initial PTR_TO_CTX is fine for
 +       * that), or we'll get some more specific BPF verifier error about
 +       * some unsatisfied conditions.
 +       */
 +      if (ret == 0 && (strstr(buf, "invalid func ") || strstr(buf, "unknown func ")))
 +              return 0;
 +      return 1; /* assume supported */
 +}
 +
 +bool bpf_probe_helper(enum bpf_func_id id, enum bpf_prog_type prog_type,
 +                    __u32 ifindex)
 +{
 +      struct bpf_insn insns[2] = {
 +              BPF_EMIT_CALL(id),
 +              BPF_EXIT_INSN()
 +      };
 +      char buf[4096] = {};
 +      bool res;
 +
 +      probe_prog_load(prog_type, insns, ARRAY_SIZE(insns), buf, sizeof(buf), ifindex);
 +      res = !grep(buf, "invalid func ") && !grep(buf, "unknown func ");
 +
 +      if (ifindex) {
 +              switch (get_vendor_id(ifindex)) {
 +              case 0x19ee: /* Netronome specific */
 +                      res = res && !grep(buf, "not supported by FW") &&
 +                              !grep(buf, "unsupported function id");
 +                      break;
 +              default:
 +                      break;
 +              }
 +      }
 +
 +      return res;
 +}
 +
 +/*
 + * Probe for availability of kernel commit (5.3):
 + *
 + * c04c0d2b968a ("bpf: increase complexity limit and maximum program size")
 + */
 +bool bpf_probe_large_insn_limit(__u32 ifindex)
 +{
 +      struct bpf_insn insns[BPF_MAXINSNS + 1];
 +      int i;
 +
 +      for (i = 0; i < BPF_MAXINSNS; i++)
 +              insns[i] = BPF_MOV64_IMM(BPF_REG_0, 1);
 +      insns[BPF_MAXINSNS] = BPF_EXIT_INSN();
 +
 +      errno = 0;
 +      probe_prog_load(BPF_PROG_TYPE_SCHED_CLS, insns, ARRAY_SIZE(insns), NULL, 0,
 +                      ifindex);
 +
 +      return errno != E2BIG && errno != EINVAL;
 +}
index b0632d8,0000000..2aafca1
mode 100755,000000..100755
--- /dev/null
@@@ -1,61 -1,0 +1,61 @@@
- if [[ -z $(/bin/mount -l -t devtmpfs) ]]; then
 +#!/bin/bash
 +# This script prepares a mounted root filesystem for testing libbpf in a virtual
 +# machine.
 +set -e -u -x -o pipefail
 +root=$1
 +shift
 +
 +chroot "${root}" /bin/busybox --install
 +
 +cat > "$root/etc/inittab" << "EOF"
 +::sysinit:/etc/init.d/rcS
 +::ctrlaltdel:/sbin/reboot
 +::shutdown:/sbin/swapoff -a
 +::shutdown:/bin/umount -a -r
 +::restart:/sbin/init
 +EOF
 +chmod 644 "$root/etc/inittab"
 +
 +mkdir -m 755 -p "$root/etc/init.d" "$root/etc/rcS.d"
 +cat > "$root/etc/rcS.d/S10-mount" << "EOF"
 +#!/bin/sh
 +
 +set -eux
 +
 +/bin/mount proc /proc -t proc
 +
 +# Mount devtmpfs if not mounted
++if [[ -z $(/bin/mount -t devtmpfs) ]]; then
 +      /bin/mount devtmpfs /dev -t devtmpfs
 +fi
 +
 +/bin/mount sysfs /sys -t sysfs
 +/bin/mount bpffs /sys/fs/bpf -t bpf
 +/bin/mount debugfs /sys/kernel/debug -t debugfs
 +
 +echo 'Listing currently mounted file systems'
 +/bin/mount
 +EOF
 +chmod 755 "$root/etc/rcS.d/S10-mount"
 +
 +cat > "$root/etc/rcS.d/S40-network" << "EOF"
 +#!/bin/sh
 +
 +set -eux
 +
 +ip link set lo up
 +EOF
 +chmod 755 "$root/etc/rcS.d/S40-network"
 +
 +cat > "$root/etc/init.d/rcS" << "EOF"
 +#!/bin/sh
 +
 +set -eux
 +
 +for path in /etc/rcS.d/S*; do
 +      [ -x "$path" ] && "$path"
 +done
 +EOF
 +chmod 755 "$root/etc/init.d/rcS"
 +
 +chmod 755 "$root"
index aa4411d,0000000..ea6de71
mode 100644,000000..100644
--- /dev/null
@@@ -1,2703 -1,0 +1,2705 @@@
- # CONFIG_NF_CONNTRACK is not set
 +#
 +# Automatically generated file; DO NOT EDIT.
 +# Linux/s390 5.15.0 Kernel Configuration
 +#
 +CONFIG_CC_VERSION_TEXT="gcc (Debian 10.2.1-6) 10.2.1 20210110"
 +CONFIG_CC_IS_GCC=y
 +CONFIG_GCC_VERSION=100201
 +CONFIG_CLANG_VERSION=0
 +CONFIG_AS_IS_GNU=y
 +CONFIG_AS_VERSION=23502
 +CONFIG_LD_IS_BFD=y
 +CONFIG_LD_VERSION=23502
 +CONFIG_LLD_VERSION=0
 +CONFIG_CC_CAN_LINK=y
 +CONFIG_CC_CAN_LINK_STATIC=y
 +CONFIG_CC_HAS_ASM_GOTO=y
 +CONFIG_CC_HAS_ASM_INLINE=y
 +CONFIG_CC_HAS_NO_PROFILE_FN_ATTR=y
 +CONFIG_IRQ_WORK=y
 +CONFIG_BUILDTIME_TABLE_SORT=y
 +CONFIG_THREAD_INFO_IN_TASK=y
 +
 +#
 +# General setup
 +#
 +CONFIG_INIT_ENV_ARG_LIMIT=32
 +# CONFIG_COMPILE_TEST is not set
 +# CONFIG_WERROR is not set
 +# CONFIG_UAPI_HEADER_TEST is not set
 +CONFIG_LOCALVERSION=""
 +CONFIG_LOCALVERSION_AUTO=y
 +CONFIG_BUILD_SALT=""
 +CONFIG_HAVE_KERNEL_GZIP=y
 +CONFIG_HAVE_KERNEL_BZIP2=y
 +CONFIG_HAVE_KERNEL_LZMA=y
 +CONFIG_HAVE_KERNEL_XZ=y
 +CONFIG_HAVE_KERNEL_LZO=y
 +CONFIG_HAVE_KERNEL_LZ4=y
 +CONFIG_HAVE_KERNEL_ZSTD=y
 +CONFIG_HAVE_KERNEL_UNCOMPRESSED=y
 +# CONFIG_KERNEL_GZIP is not set
 +# CONFIG_KERNEL_BZIP2 is not set
 +# CONFIG_KERNEL_LZMA is not set
 +# CONFIG_KERNEL_XZ is not set
 +# CONFIG_KERNEL_LZO is not set
 +# CONFIG_KERNEL_LZ4 is not set
 +# CONFIG_KERNEL_ZSTD is not set
 +CONFIG_KERNEL_UNCOMPRESSED=y
 +CONFIG_DEFAULT_INIT=""
 +CONFIG_DEFAULT_HOSTNAME="(none)"
 +CONFIG_SWAP=y
 +CONFIG_SYSVIPC=y
 +CONFIG_SYSVIPC_SYSCTL=y
 +CONFIG_POSIX_MQUEUE=y
 +CONFIG_POSIX_MQUEUE_SYSCTL=y
 +# CONFIG_WATCH_QUEUE is not set
 +CONFIG_CROSS_MEMORY_ATTACH=y
 +CONFIG_USELIB=y
 +CONFIG_AUDIT=y
 +CONFIG_HAVE_ARCH_AUDITSYSCALL=y
 +CONFIG_AUDITSYSCALL=y
 +
 +#
 +# IRQ subsystem
 +#
 +CONFIG_IRQ_DOMAIN=y
 +CONFIG_IRQ_DOMAIN_HIERARCHY=y
 +CONFIG_GENERIC_MSI_IRQ=y
 +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
 +CONFIG_SPARSE_IRQ=y
 +# CONFIG_GENERIC_IRQ_DEBUGFS is not set
 +# end of IRQ subsystem
 +
 +CONFIG_GENERIC_TIME_VSYSCALL=y
 +CONFIG_GENERIC_CLOCKEVENTS=y
 +
 +#
 +# Timers subsystem
 +#
 +CONFIG_TICK_ONESHOT=y
 +CONFIG_NO_HZ_COMMON=y
 +# CONFIG_HZ_PERIODIC is not set
 +CONFIG_NO_HZ_IDLE=y
 +# CONFIG_NO_HZ is not set
 +CONFIG_HIGH_RES_TIMERS=y
 +# end of Timers subsystem
 +
 +CONFIG_BPF=y
 +CONFIG_HAVE_EBPF_JIT=y
 +CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
 +
 +#
 +# BPF subsystem
 +#
 +CONFIG_BPF_SYSCALL=y
 +CONFIG_BPF_JIT=y
 +CONFIG_BPF_JIT_ALWAYS_ON=y
 +CONFIG_BPF_JIT_DEFAULT_ON=y
 +CONFIG_BPF_UNPRIV_DEFAULT_OFF=y
 +CONFIG_USERMODE_DRIVER=y
 +CONFIG_BPF_PRELOAD=y
 +CONFIG_BPF_PRELOAD_UMD=y
 +CONFIG_BPF_LSM=y
 +# end of BPF subsystem
 +
 +CONFIG_PREEMPT_NONE_BEHAVIOUR=y
 +# CONFIG_PREEMPT_VOLUNTARY_BEHAVIOUR is not set
 +# CONFIG_PREEMPT_BEHAVIOUR is not set
 +CONFIG_PREEMPT_NONE=y
 +CONFIG_PREEMPT_COUNT=y
 +# CONFIG_SCHED_CORE is not set
 +
 +#
 +# CPU/Task time and stats accounting
 +#
 +CONFIG_VIRT_CPU_ACCOUNTING=y
 +CONFIG_VIRT_CPU_ACCOUNTING_NATIVE=y
 +# CONFIG_BSD_PROCESS_ACCT is not set
 +CONFIG_TASKSTATS=y
 +CONFIG_TASK_DELAY_ACCT=y
 +CONFIG_TASK_XACCT=y
 +CONFIG_TASK_IO_ACCOUNTING=y
 +# CONFIG_PSI is not set
 +# end of CPU/Task time and stats accounting
 +
 +# CONFIG_CPU_ISOLATION is not set
 +
 +#
 +# RCU Subsystem
 +#
 +CONFIG_TREE_RCU=y
 +# CONFIG_RCU_EXPERT is not set
 +CONFIG_SRCU=y
 +CONFIG_TREE_SRCU=y
 +CONFIG_TASKS_RCU_GENERIC=y
 +CONFIG_TASKS_RUDE_RCU=y
 +CONFIG_TASKS_TRACE_RCU=y
 +CONFIG_RCU_STALL_COMMON=y
 +CONFIG_RCU_NEED_SEGCBLIST=y
 +# end of RCU Subsystem
 +
 +CONFIG_IKCONFIG=y
 +CONFIG_IKCONFIG_PROC=y
 +CONFIG_IKHEADERS=y
 +CONFIG_LOG_BUF_SHIFT=17
 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=12
 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
 +# CONFIG_PRINTK_INDEX is not set
 +
 +#
 +# Scheduler features
 +#
 +# end of Scheduler features
 +
 +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
 +CONFIG_CC_HAS_INT128=y
 +# CONFIG_NUMA_BALANCING is not set
 +CONFIG_CGROUPS=y
 +CONFIG_PAGE_COUNTER=y
 +CONFIG_MEMCG=y
 +CONFIG_MEMCG_SWAP=y
 +CONFIG_MEMCG_KMEM=y
 +CONFIG_BLK_CGROUP=y
 +CONFIG_CGROUP_WRITEBACK=y
 +CONFIG_CGROUP_SCHED=y
 +CONFIG_FAIR_GROUP_SCHED=y
 +# CONFIG_CFS_BANDWIDTH is not set
 +CONFIG_RT_GROUP_SCHED=y
 +CONFIG_CGROUP_PIDS=y
 +# CONFIG_CGROUP_RDMA is not set
 +CONFIG_CGROUP_FREEZER=y
 +CONFIG_CGROUP_HUGETLB=y
 +CONFIG_CPUSETS=y
 +CONFIG_PROC_PID_CPUSET=y
 +CONFIG_CGROUP_DEVICE=y
 +CONFIG_CGROUP_CPUACCT=y
 +CONFIG_CGROUP_PERF=y
 +CONFIG_CGROUP_BPF=y
 +# CONFIG_CGROUP_MISC is not set
 +# CONFIG_CGROUP_DEBUG is not set
 +CONFIG_SOCK_CGROUP_DATA=y
 +CONFIG_NAMESPACES=y
 +CONFIG_UTS_NS=y
 +CONFIG_TIME_NS=y
 +CONFIG_IPC_NS=y
 +CONFIG_USER_NS=y
 +CONFIG_PID_NS=y
 +CONFIG_NET_NS=y
 +CONFIG_CHECKPOINT_RESTORE=y
 +# CONFIG_SCHED_AUTOGROUP is not set
 +# CONFIG_SYSFS_DEPRECATED is not set
 +CONFIG_RELAY=y
 +CONFIG_BLK_DEV_INITRD=y
 +CONFIG_INITRAMFS_SOURCE=""
 +# CONFIG_RD_GZIP is not set
 +# CONFIG_RD_BZIP2 is not set
 +# CONFIG_RD_LZMA is not set
 +# CONFIG_RD_XZ is not set
 +# CONFIG_RD_LZO is not set
 +# CONFIG_RD_LZ4 is not set
 +CONFIG_RD_ZSTD=y
 +# CONFIG_BOOT_CONFIG is not set
 +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
 +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 +CONFIG_SYSCTL=y
 +CONFIG_HAVE_UID16=y
 +CONFIG_SYSCTL_EXCEPTION_TRACE=y
 +CONFIG_EXPERT=y
 +CONFIG_UID16=y
 +CONFIG_MULTIUSER=y
 +# CONFIG_SGETMASK_SYSCALL is not set
 +# CONFIG_SYSFS_SYSCALL is not set
 +CONFIG_FHANDLE=y
 +CONFIG_POSIX_TIMERS=y
 +CONFIG_PRINTK=y
 +CONFIG_BUG=y
 +CONFIG_ELF_CORE=y
 +CONFIG_BASE_FULL=y
 +CONFIG_FUTEX=y
 +CONFIG_FUTEX_PI=y
 +CONFIG_HAVE_FUTEX_CMPXCHG=y
 +CONFIG_EPOLL=y
 +CONFIG_SIGNALFD=y
 +CONFIG_TIMERFD=y
 +CONFIG_EVENTFD=y
 +CONFIG_SHMEM=y
 +CONFIG_AIO=y
 +CONFIG_IO_URING=y
 +CONFIG_ADVISE_SYSCALLS=y
 +CONFIG_MEMBARRIER=y
 +CONFIG_KALLSYMS=y
 +CONFIG_KALLSYMS_ALL=y
 +CONFIG_KALLSYMS_BASE_RELATIVE=y
 +CONFIG_USERFAULTFD=y
 +CONFIG_KCMP=y
 +CONFIG_RSEQ=y
 +# CONFIG_DEBUG_RSEQ is not set
 +# CONFIG_EMBEDDED is not set
 +CONFIG_HAVE_PERF_EVENTS=y
 +# CONFIG_PC104 is not set
 +
 +#
 +# Kernel Performance Events And Counters
 +#
 +CONFIG_PERF_EVENTS=y
 +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
 +# end of Kernel Performance Events And Counters
 +
 +CONFIG_VM_EVENT_COUNTERS=y
 +CONFIG_SLUB_DEBUG=y
 +# CONFIG_COMPAT_BRK is not set
 +# CONFIG_SLAB is not set
 +CONFIG_SLUB=y
 +# CONFIG_SLOB is not set
 +CONFIG_SLAB_MERGE_DEFAULT=y
 +# CONFIG_SLAB_FREELIST_RANDOM is not set
 +# CONFIG_SLAB_FREELIST_HARDENED is not set
 +# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
 +CONFIG_SLUB_CPU_PARTIAL=y
 +CONFIG_PROFILING=y
 +CONFIG_TRACEPOINTS=y
 +# end of General setup
 +
 +CONFIG_MMU=y
 +CONFIG_CPU_BIG_ENDIAN=y
 +CONFIG_LOCKDEP_SUPPORT=y
 +CONFIG_STACKTRACE_SUPPORT=y
 +CONFIG_GENERIC_HWEIGHT=y
 +CONFIG_GENERIC_BUG=y
 +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
 +CONFIG_PGSTE=y
 +CONFIG_AUDIT_ARCH=y
 +CONFIG_NO_IOPORT_MAP=y
 +# CONFIG_PCI_QUIRKS is not set
 +CONFIG_ARCH_SUPPORTS_UPROBES=y
 +CONFIG_S390=y
 +CONFIG_SCHED_OMIT_FRAME_POINTER=y
 +CONFIG_PGTABLE_LEVELS=5
 +CONFIG_HAVE_LIVEPATCH=y
 +CONFIG_LIVEPATCH=y
 +
 +#
 +# Processor type and features
 +#
 +CONFIG_HAVE_MARCH_Z900_FEATURES=y
 +CONFIG_HAVE_MARCH_Z990_FEATURES=y
 +CONFIG_HAVE_MARCH_Z9_109_FEATURES=y
 +CONFIG_HAVE_MARCH_Z10_FEATURES=y
 +CONFIG_HAVE_MARCH_Z196_FEATURES=y
 +# CONFIG_MARCH_Z900 is not set
 +# CONFIG_MARCH_Z990 is not set
 +# CONFIG_MARCH_Z9_109 is not set
 +# CONFIG_MARCH_Z10 is not set
 +CONFIG_MARCH_Z196=y
 +# CONFIG_MARCH_ZEC12 is not set
 +# CONFIG_MARCH_Z13 is not set
 +# CONFIG_MARCH_Z14 is not set
 +# CONFIG_MARCH_Z15 is not set
 +CONFIG_MARCH_Z196_TUNE=y
 +CONFIG_TUNE_DEFAULT=y
 +# CONFIG_TUNE_Z900 is not set
 +# CONFIG_TUNE_Z990 is not set
 +# CONFIG_TUNE_Z9_109 is not set
 +# CONFIG_TUNE_Z10 is not set
 +# CONFIG_TUNE_Z196 is not set
 +# CONFIG_TUNE_ZEC12 is not set
 +# CONFIG_TUNE_Z13 is not set
 +# CONFIG_TUNE_Z14 is not set
 +# CONFIG_TUNE_Z15 is not set
 +CONFIG_64BIT=y
 +CONFIG_COMPAT=y
 +CONFIG_SYSVIPC_COMPAT=y
 +CONFIG_SMP=y
 +CONFIG_NR_CPUS=256
 +CONFIG_HOTPLUG_CPU=y
 +CONFIG_NUMA=y
 +CONFIG_NODES_SHIFT=1
 +CONFIG_SCHED_SMT=y
 +CONFIG_SCHED_MC=y
 +CONFIG_SCHED_BOOK=y
 +CONFIG_SCHED_DRAWER=y
 +CONFIG_SCHED_TOPOLOGY=y
 +CONFIG_HZ_100=y
 +# CONFIG_HZ_250 is not set
 +# CONFIG_HZ_300 is not set
 +# CONFIG_HZ_1000 is not set
 +CONFIG_HZ=100
 +CONFIG_SCHED_HRTICK=y
 +CONFIG_KEXEC=y
 +CONFIG_ARCH_RANDOM=y
 +# CONFIG_KERNEL_NOBP is not set
 +# CONFIG_EXPOLINE is not set
 +CONFIG_RELOCATABLE=y
 +CONFIG_RANDOMIZE_BASE=y
 +# end of Processor type and features
 +
 +#
 +# Memory setup
 +#
 +CONFIG_ARCH_SPARSEMEM_ENABLE=y
 +CONFIG_ARCH_SPARSEMEM_DEFAULT=y
 +CONFIG_MAX_PHYSMEM_BITS=46
 +CONFIG_PACK_STACK=y
 +# end of Memory setup
 +
 +#
 +# I/O subsystem
 +#
 +CONFIG_QDIO=y
 +CONFIG_PCI_NR_FUNCTIONS=512
 +CONFIG_HAS_IOMEM=y
 +CONFIG_CHSC_SCH=m
 +CONFIG_SCM_BUS=y
 +# CONFIG_EADM_SCH is not set
 +# end of I/O subsystem
 +
 +#
 +# Dump support
 +#
 +CONFIG_CRASH_DUMP=y
 +# end of Dump support
 +
 +CONFIG_CCW=y
 +
 +#
 +# Virtualization
 +#
 +# CONFIG_PROTECTED_VIRTUALIZATION_GUEST is not set
 +CONFIG_PFAULT=y
 +# CONFIG_CMM is not set
 +# CONFIG_APPLDATA_BASE is not set
 +CONFIG_S390_HYPFS_FS=y
 +CONFIG_HAVE_KVM=y
 +CONFIG_HAVE_KVM_IRQCHIP=y
 +CONFIG_HAVE_KVM_IRQFD=y
 +CONFIG_HAVE_KVM_IRQ_ROUTING=y
 +CONFIG_HAVE_KVM_EVENTFD=y
 +CONFIG_KVM_ASYNC_PF=y
 +CONFIG_KVM_ASYNC_PF_SYNC=y
 +CONFIG_HAVE_KVM_CPU_RELAX_INTERCEPT=y
 +CONFIG_KVM_VFIO=y
 +CONFIG_HAVE_KVM_INVALID_WAKEUPS=y
 +CONFIG_HAVE_KVM_VCPU_ASYNC_IOCTL=y
 +CONFIG_HAVE_KVM_NO_POLL=y
 +CONFIG_VIRTUALIZATION=y
 +CONFIG_KVM=y
 +# CONFIG_KVM_S390_UCONTROL is not set
 +CONFIG_S390_GUEST=y
 +# end of Virtualization
 +
 +#
 +# Selftests
 +#
 +# CONFIG_S390_UNWIND_SELFTEST is not set
 +# end of Selftests
 +
 +#
 +# General architecture-dependent options
 +#
 +CONFIG_CRASH_CORE=y
 +CONFIG_KEXEC_CORE=y
 +CONFIG_GENERIC_ENTRY=y
 +CONFIG_KPROBES=y
 +CONFIG_JUMP_LABEL=y
 +CONFIG_STATIC_KEYS_SELFTEST=y
 +CONFIG_KPROBES_ON_FTRACE=y
 +CONFIG_UPROBES=y
 +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
 +CONFIG_ARCH_USE_BUILTIN_BSWAP=y
 +CONFIG_KRETPROBES=y
 +CONFIG_HAVE_IOREMAP_PROT=y
 +CONFIG_HAVE_KPROBES=y
 +CONFIG_HAVE_KRETPROBES=y
 +CONFIG_HAVE_KPROBES_ON_FTRACE=y
 +CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y
 +CONFIG_HAVE_NMI=y
 +CONFIG_TRACE_IRQFLAGS_SUPPORT=y
 +CONFIG_HAVE_ARCH_TRACEHOOK=y
 +CONFIG_HAVE_DMA_CONTIGUOUS=y
 +CONFIG_GENERIC_SMP_IDLE_THREAD=y
 +CONFIG_ARCH_HAS_FORTIFY_SOURCE=y
 +CONFIG_ARCH_HAS_SET_MEMORY=y
 +CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y
 +CONFIG_ARCH_WANTS_NO_INSTR=y
 +CONFIG_ARCH_32BIT_USTAT_F_TINODE=y
 +CONFIG_HAVE_ASM_MODVERSIONS=y
 +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
 +CONFIG_HAVE_RSEQ=y
 +CONFIG_HAVE_PERF_REGS=y
 +CONFIG_HAVE_PERF_USER_STACK_DUMP=y
 +CONFIG_HAVE_ARCH_JUMP_LABEL=y
 +CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y
 +CONFIG_MMU_GATHER_TABLE_FREE=y
 +CONFIG_MMU_GATHER_RCU_TABLE_FREE=y
 +CONFIG_MMU_GATHER_NO_GATHER=y
 +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
 +CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
 +CONFIG_HAVE_CMPXCHG_LOCAL=y
 +CONFIG_HAVE_CMPXCHG_DOUBLE=y
 +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y
 +CONFIG_ARCH_WANT_COMPAT_IPC_PARSE_VERSION=y
 +CONFIG_ARCH_WANT_OLD_COMPAT_IPC=y
 +CONFIG_HAVE_ARCH_SECCOMP=y
 +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
 +CONFIG_SECCOMP=y
 +CONFIG_SECCOMP_FILTER=y
 +# CONFIG_SECCOMP_CACHE_DEBUG is not set
 +CONFIG_LTO_NONE=y
 +CONFIG_HAVE_VIRT_CPU_ACCOUNTING=y
 +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_IDLE=y
 +CONFIG_ARCH_HAS_SCALED_CPUTIME=y
 +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
 +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
 +CONFIG_HAVE_ARCH_SOFT_DIRTY=y
 +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
 +CONFIG_MODULES_USE_ELF_RELA=y
 +CONFIG_HAVE_SOFTIRQ_ON_OWN_STACK=y
 +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
 +CONFIG_HAVE_RELIABLE_STACKTRACE=y
 +CONFIG_CLONE_BACKWARDS2=y
 +CONFIG_OLD_SIGSUSPEND3=y
 +CONFIG_OLD_SIGACTION=y
 +CONFIG_COMPAT_OLD_SIGACTION=y
 +CONFIG_COMPAT_32BIT_TIME=y
 +CONFIG_HAVE_ARCH_VMAP_STACK=y
 +CONFIG_VMAP_STACK=y
 +CONFIG_HAVE_ARCH_RANDOMIZE_KSTACK_OFFSET=y
 +# CONFIG_RANDOMIZE_KSTACK_OFFSET_DEFAULT is not set
 +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
 +CONFIG_STRICT_KERNEL_RWX=y
 +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
 +CONFIG_STRICT_MODULE_RWX=y
 +# CONFIG_LOCK_EVENT_COUNTS is not set
 +CONFIG_ARCH_HAS_MEM_ENCRYPT=y
 +CONFIG_ARCH_HAS_VDSO_DATA=y
 +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
 +
 +#
 +# GCOV-based kernel profiling
 +#
 +# CONFIG_GCOV_KERNEL is not set
 +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
 +# end of GCOV-based kernel profiling
 +
 +CONFIG_HAVE_GCC_PLUGINS=y
 +# end of General architecture-dependent options
 +
 +CONFIG_RT_MUTEXES=y
 +CONFIG_BASE_SMALL=0
 +CONFIG_MODULES=y
 +# CONFIG_MODULE_FORCE_LOAD is not set
 +CONFIG_MODULE_UNLOAD=y
 +# CONFIG_MODULE_FORCE_UNLOAD is not set
 +# CONFIG_MODVERSIONS is not set
 +# CONFIG_MODULE_SRCVERSION_ALL is not set
 +# CONFIG_MODULE_SIG is not set
 +CONFIG_MODULE_COMPRESS_NONE=y
 +# CONFIG_MODULE_COMPRESS_GZIP is not set
 +# CONFIG_MODULE_COMPRESS_XZ is not set
 +# CONFIG_MODULE_COMPRESS_ZSTD is not set
 +# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
 +CONFIG_MODPROBE_PATH="/sbin/modprobe"
 +# CONFIG_TRIM_UNUSED_KSYMS is not set
 +CONFIG_MODULES_TREE_LOOKUP=y
 +CONFIG_BLOCK=y
 +# CONFIG_BLK_DEV_BSGLIB is not set
 +# CONFIG_BLK_DEV_INTEGRITY is not set
 +# CONFIG_BLK_DEV_ZONED is not set
 +# CONFIG_BLK_DEV_THROTTLING is not set
 +# CONFIG_BLK_WBT is not set
 +# CONFIG_BLK_CGROUP_IOLATENCY is not set
 +# CONFIG_BLK_CGROUP_IOCOST is not set
 +# CONFIG_BLK_CGROUP_IOPRIO is not set
 +# CONFIG_BLK_DEBUG_FS is not set
 +# CONFIG_BLK_SED_OPAL is not set
 +# CONFIG_BLK_INLINE_ENCRYPTION is not set
 +
 +#
 +# Partition Types
 +#
 +CONFIG_PARTITION_ADVANCED=y
 +# CONFIG_ACORN_PARTITION is not set
 +# CONFIG_AIX_PARTITION is not set
 +# CONFIG_OSF_PARTITION is not set
 +# CONFIG_AMIGA_PARTITION is not set
 +# CONFIG_ATARI_PARTITION is not set
 +# CONFIG_IBM_PARTITION is not set
 +# CONFIG_MAC_PARTITION is not set
 +# CONFIG_MSDOS_PARTITION is not set
 +# CONFIG_LDM_PARTITION is not set
 +# CONFIG_SGI_PARTITION is not set
 +# CONFIG_ULTRIX_PARTITION is not set
 +# CONFIG_SUN_PARTITION is not set
 +# CONFIG_KARMA_PARTITION is not set
 +CONFIG_EFI_PARTITION=y
 +# CONFIG_SYSV68_PARTITION is not set
 +# CONFIG_CMDLINE_PARTITION is not set
 +# end of Partition Types
 +
 +CONFIG_BLOCK_COMPAT=y
 +CONFIG_BLK_MQ_PCI=y
 +CONFIG_BLK_MQ_VIRTIO=y
 +
 +#
 +# IO Schedulers
 +#
 +CONFIG_MQ_IOSCHED_DEADLINE=y
 +CONFIG_MQ_IOSCHED_KYBER=y
 +# CONFIG_IOSCHED_BFQ is not set
 +# end of IO Schedulers
 +
 +CONFIG_PREEMPT_NOTIFIERS=y
 +CONFIG_ARCH_INLINE_SPIN_TRYLOCK=y
 +CONFIG_ARCH_INLINE_SPIN_TRYLOCK_BH=y
 +CONFIG_ARCH_INLINE_SPIN_LOCK=y
 +CONFIG_ARCH_INLINE_SPIN_LOCK_BH=y
 +CONFIG_ARCH_INLINE_SPIN_LOCK_IRQ=y
 +CONFIG_ARCH_INLINE_SPIN_LOCK_IRQSAVE=y
 +CONFIG_ARCH_INLINE_SPIN_UNLOCK=y
 +CONFIG_ARCH_INLINE_SPIN_UNLOCK_BH=y
 +CONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQ=y
 +CONFIG_ARCH_INLINE_SPIN_UNLOCK_IRQRESTORE=y
 +CONFIG_ARCH_INLINE_READ_TRYLOCK=y
 +CONFIG_ARCH_INLINE_READ_LOCK=y
 +CONFIG_ARCH_INLINE_READ_LOCK_BH=y
 +CONFIG_ARCH_INLINE_READ_LOCK_IRQ=y
 +CONFIG_ARCH_INLINE_READ_LOCK_IRQSAVE=y
 +CONFIG_ARCH_INLINE_READ_UNLOCK=y
 +CONFIG_ARCH_INLINE_READ_UNLOCK_BH=y
 +CONFIG_ARCH_INLINE_READ_UNLOCK_IRQ=y
 +CONFIG_ARCH_INLINE_READ_UNLOCK_IRQRESTORE=y
 +CONFIG_ARCH_INLINE_WRITE_TRYLOCK=y
 +CONFIG_ARCH_INLINE_WRITE_LOCK=y
 +CONFIG_ARCH_INLINE_WRITE_LOCK_BH=y
 +CONFIG_ARCH_INLINE_WRITE_LOCK_IRQ=y
 +CONFIG_ARCH_INLINE_WRITE_LOCK_IRQSAVE=y
 +CONFIG_ARCH_INLINE_WRITE_UNLOCK=y
 +CONFIG_ARCH_INLINE_WRITE_UNLOCK_BH=y
 +CONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQ=y
 +CONFIG_ARCH_INLINE_WRITE_UNLOCK_IRQRESTORE=y
 +CONFIG_UNINLINE_SPIN_UNLOCK=y
 +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
 +CONFIG_MUTEX_SPIN_ON_OWNER=y
 +CONFIG_RWSEM_SPIN_ON_OWNER=y
 +CONFIG_LOCK_SPIN_ON_OWNER=y
 +CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y
 +CONFIG_FREEZER=y
 +
 +#
 +# Executable file formats
 +#
 +CONFIG_BINFMT_ELF=y
 +CONFIG_COMPAT_BINFMT_ELF=y
 +CONFIG_ARCH_BINFMT_ELF_STATE=y
 +CONFIG_ELFCORE=y
 +CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS=y
 +CONFIG_BINFMT_SCRIPT=y
 +# CONFIG_BINFMT_MISC is not set
 +CONFIG_COREDUMP=y
 +# end of Executable file formats
 +
 +#
 +# Memory Management options
 +#
 +CONFIG_SPARSEMEM=y
 +CONFIG_SPARSEMEM_EXTREME=y
 +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
 +CONFIG_SPARSEMEM_VMEMMAP=y
 +CONFIG_HAVE_MEMBLOCK_PHYS_MAP=y
 +CONFIG_HAVE_FAST_GUP=y
 +CONFIG_NUMA_KEEP_MEMINFO=y
 +CONFIG_MEMORY_ISOLATION=y
 +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
 +CONFIG_MEMORY_HOTPLUG=y
 +CONFIG_MEMORY_HOTPLUG_SPARSE=y
 +# CONFIG_MEMORY_HOTPLUG_DEFAULT_ONLINE is not set
 +CONFIG_ARCH_ENABLE_MEMORY_HOTREMOVE=y
 +CONFIG_MEMORY_HOTREMOVE=y
 +CONFIG_SPLIT_PTLOCK_CPUS=4
 +CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y
 +CONFIG_MEMORY_BALLOON=y
 +CONFIG_BALLOON_COMPACTION=y
 +CONFIG_COMPACTION=y
 +CONFIG_PAGE_REPORTING=y
 +CONFIG_MIGRATION=y
 +CONFIG_CONTIG_ALLOC=y
 +CONFIG_PHYS_ADDR_T_64BIT=y
 +CONFIG_KSM=y
 +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
 +CONFIG_TRANSPARENT_HUGEPAGE=y
 +CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS=y
 +# CONFIG_TRANSPARENT_HUGEPAGE_MADVISE is not set
 +CONFIG_CLEANCACHE=y
 +CONFIG_FRONTSWAP=y
 +# CONFIG_CMA is not set
 +# CONFIG_MEM_SOFT_DIRTY is not set
 +# CONFIG_ZSWAP is not set
 +# CONFIG_ZPOOL is not set
 +# CONFIG_ZSMALLOC is not set
 +# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
 +CONFIG_PAGE_IDLE_FLAG=y
 +CONFIG_IDLE_PAGE_TRACKING=y
 +CONFIG_ZONE_DMA=y
 +# CONFIG_PERCPU_STATS is not set
 +# CONFIG_GUP_TEST is not set
 +# CONFIG_READ_ONLY_THP_FOR_FS is not set
 +CONFIG_ARCH_HAS_PTE_SPECIAL=y
 +
 +#
 +# Data Access Monitoring
 +#
 +# CONFIG_DAMON is not set
 +# end of Data Access Monitoring
 +# end of Memory Management options
 +
 +CONFIG_NET=y
 +CONFIG_NET_INGRESS=y
 +CONFIG_NET_EGRESS=y
 +CONFIG_SKB_EXTENSIONS=y
 +
 +#
 +# Networking options
 +#
 +CONFIG_PACKET=y
 +# CONFIG_PACKET_DIAG is not set
 +CONFIG_UNIX=y
 +CONFIG_UNIX_SCM=y
 +CONFIG_AF_UNIX_OOB=y
 +# CONFIG_UNIX_DIAG is not set
 +CONFIG_TLS=y
 +# CONFIG_TLS_DEVICE is not set
 +# CONFIG_TLS_TOE is not set
 +CONFIG_XFRM=y
 +CONFIG_XFRM_ALGO=y
 +CONFIG_XFRM_USER=y
 +# CONFIG_XFRM_INTERFACE is not set
 +# CONFIG_XFRM_SUB_POLICY is not set
 +# CONFIG_XFRM_MIGRATE is not set
 +# CONFIG_XFRM_STATISTICS is not set
 +CONFIG_XFRM_ESP=y
 +CONFIG_NET_KEY=y
 +# CONFIG_NET_KEY_MIGRATE is not set
 +# CONFIG_IUCV is not set
 +# CONFIG_AFIUCV is not set
 +CONFIG_XDP_SOCKETS=y
 +# CONFIG_XDP_SOCKETS_DIAG is not set
 +CONFIG_INET=y
 +CONFIG_IP_MULTICAST=y
 +CONFIG_IP_ADVANCED_ROUTER=y
 +# CONFIG_IP_FIB_TRIE_STATS is not set
 +CONFIG_IP_MULTIPLE_TABLES=y
 +# CONFIG_IP_ROUTE_MULTIPATH is not set
 +# CONFIG_IP_ROUTE_VERBOSE is not set
 +# CONFIG_IP_PNP is not set
 +CONFIG_NET_IPIP=y
 +CONFIG_NET_IPGRE_DEMUX=y
 +CONFIG_NET_IP_TUNNEL=y
 +CONFIG_NET_IPGRE=y
 +# CONFIG_NET_IPGRE_BROADCAST is not set
 +# CONFIG_IP_MROUTE is not set
 +CONFIG_SYN_COOKIES=y
 +# CONFIG_NET_IPVTI is not set
 +CONFIG_NET_UDP_TUNNEL=y
 +CONFIG_NET_FOU=y
 +CONFIG_NET_FOU_IP_TUNNELS=y
 +# CONFIG_INET_AH is not set
 +CONFIG_INET_ESP=y
 +# CONFIG_INET_ESP_OFFLOAD is not set
 +# CONFIG_INET_ESPINTCP is not set
 +# CONFIG_INET_IPCOMP is not set
 +CONFIG_INET_TUNNEL=y
 +CONFIG_INET_DIAG=y
 +CONFIG_INET_TCP_DIAG=y
 +# CONFIG_INET_UDP_DIAG is not set
 +# CONFIG_INET_RAW_DIAG is not set
 +# CONFIG_INET_DIAG_DESTROY is not set
 +CONFIG_TCP_CONG_ADVANCED=y
 +# CONFIG_TCP_CONG_BIC is not set
 +CONFIG_TCP_CONG_CUBIC=y
 +# CONFIG_TCP_CONG_WESTWOOD is not set
 +# CONFIG_TCP_CONG_HTCP is not set
 +# CONFIG_TCP_CONG_HSTCP is not set
 +# CONFIG_TCP_CONG_HYBLA is not set
 +# CONFIG_TCP_CONG_VEGAS is not set
 +# CONFIG_TCP_CONG_NV is not set
 +# CONFIG_TCP_CONG_SCALABLE is not set
 +# CONFIG_TCP_CONG_LP is not set
 +# CONFIG_TCP_CONG_VENO is not set
 +# CONFIG_TCP_CONG_YEAH is not set
 +# CONFIG_TCP_CONG_ILLINOIS is not set
 +CONFIG_TCP_CONG_DCTCP=y
 +# CONFIG_TCP_CONG_CDG is not set
 +# CONFIG_TCP_CONG_BBR is not set
 +CONFIG_DEFAULT_CUBIC=y
 +# CONFIG_DEFAULT_DCTCP is not set
 +# CONFIG_DEFAULT_RENO is not set
 +CONFIG_DEFAULT_TCP_CONG="cubic"
 +# CONFIG_TCP_MD5SIG is not set
 +CONFIG_IPV6=y
 +# CONFIG_IPV6_ROUTER_PREF is not set
 +# CONFIG_IPV6_OPTIMISTIC_DAD is not set
 +# CONFIG_INET6_AH is not set
 +CONFIG_INET6_ESP=y
 +# CONFIG_INET6_ESP_OFFLOAD is not set
 +# CONFIG_INET6_ESPINTCP is not set
 +# CONFIG_INET6_IPCOMP is not set
 +# CONFIG_IPV6_MIP6 is not set
 +# CONFIG_IPV6_ILA is not set
 +CONFIG_INET6_TUNNEL=y
 +# CONFIG_IPV6_VTI is not set
 +CONFIG_IPV6_SIT=y
 +# CONFIG_IPV6_SIT_6RD is not set
 +CONFIG_IPV6_NDISC_NODETYPE=y
 +CONFIG_IPV6_TUNNEL=y
 +CONFIG_IPV6_GRE=y
 +CONFIG_IPV6_FOU=y
 +CONFIG_IPV6_FOU_TUNNEL=y
 +CONFIG_IPV6_MULTIPLE_TABLES=y
 +# CONFIG_IPV6_SUBTREES is not set
 +# CONFIG_IPV6_MROUTE is not set
 +CONFIG_IPV6_SEG6_LWTUNNEL=y
 +# CONFIG_IPV6_SEG6_HMAC is not set
 +CONFIG_IPV6_SEG6_BPF=y
 +# CONFIG_IPV6_RPL_LWTUNNEL is not set
 +# CONFIG_IPV6_IOAM6_LWTUNNEL is not set
 +# CONFIG_NETLABEL is not set
 +# CONFIG_MPTCP is not set
 +# CONFIG_NETWORK_SECMARK is not set
 +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
 +CONFIG_NETFILTER=y
 +CONFIG_NETFILTER_ADVANCED=y
 +
 +#
 +# Core Netfilter Configuration
 +#
 +CONFIG_NETFILTER_INGRESS=y
 +CONFIG_NETFILTER_EGRESS=y
 +CONFIG_NETFILTER_SKIP_EGRESS=y
 +CONFIG_NETFILTER_NETLINK=y
 +# CONFIG_NETFILTER_NETLINK_HOOK is not set
 +# CONFIG_NETFILTER_NETLINK_ACCT is not set
 +# CONFIG_NETFILTER_NETLINK_QUEUE is not set
 +# CONFIG_NETFILTER_NETLINK_LOG is not set
 +# CONFIG_NETFILTER_NETLINK_OSF is not set
++CONFIG_NF_CONNTRACK=y
 +# CONFIG_NF_LOG_SYSLOG is not set
 +CONFIG_NF_TABLES=y
 +# CONFIG_NF_TABLES_INET is not set
 +# CONFIG_NF_TABLES_NETDEV is not set
 +# CONFIG_NFT_NUMGEN is not set
 +# CONFIG_NFT_COUNTER is not set
 +# CONFIG_NFT_LOG is not set
 +# CONFIG_NFT_LIMIT is not set
 +# CONFIG_NFT_TUNNEL is not set
 +# CONFIG_NFT_OBJREF is not set
 +# CONFIG_NFT_QUOTA is not set
 +# CONFIG_NFT_REJECT is not set
 +# CONFIG_NFT_COMPAT is not set
 +# CONFIG_NFT_HASH is not set
 +# CONFIG_NFT_XFRM is not set
 +# CONFIG_NFT_SOCKET is not set
 +# CONFIG_NFT_OSF is not set
 +# CONFIG_NFT_TPROXY is not set
 +CONFIG_NETFILTER_XTABLES=y
 +CONFIG_NETFILTER_XTABLES_COMPAT=y
 +
 +#
 +# Xtables combined modules
 +#
 +CONFIG_NETFILTER_XT_MARK=y
 +
 +#
 +# Xtables targets
 +#
 +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
 +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
 +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
 +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
 +# CONFIG_NETFILTER_XT_TARGET_LOG is not set
 +CONFIG_NETFILTER_XT_TARGET_MARK=y
 +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
 +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
 +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
 +# CONFIG_NETFILTER_XT_TARGET_TEE is not set
 +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
 +
 +#
 +# Xtables matches
 +#
 +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
 +CONFIG_NETFILTER_XT_MATCH_BPF=y
 +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set
 +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
 +# CONFIG_NETFILTER_XT_MATCH_CPU is not set
 +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
 +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
 +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
 +# CONFIG_NETFILTER_XT_MATCH_ECN is not set
 +# CONFIG_NETFILTER_XT_MATCH_ESP is not set
 +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
 +# CONFIG_NETFILTER_XT_MATCH_HL is not set
 +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set
 +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
 +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
 +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
 +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
 +# CONFIG_NETFILTER_XT_MATCH_MAC is not set
 +# CONFIG_NETFILTER_XT_MATCH_MARK is not set
 +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
 +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
 +# CONFIG_NETFILTER_XT_MATCH_OSF is not set
 +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
 +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
 +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
 +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
 +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
 +# CONFIG_NETFILTER_XT_MATCH_REALM is not set
 +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
 +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
 +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
 +# CONFIG_NETFILTER_XT_MATCH_STATISTIC is not set
 +# CONFIG_NETFILTER_XT_MATCH_STRING is not set
 +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
 +# CONFIG_NETFILTER_XT_MATCH_TIME is not set
 +# CONFIG_NETFILTER_XT_MATCH_U32 is not set
 +# end of Core Netfilter Configuration
 +
 +# CONFIG_IP_SET is not set
 +# CONFIG_IP_VS is not set
 +
 +#
 +# IP: Netfilter Configuration
 +#
++CONFIG_NF_DEFRAG_IPV4=y
 +# CONFIG_NF_SOCKET_IPV4 is not set
 +# CONFIG_NF_TPROXY_IPV4 is not set
 +# CONFIG_NF_TABLES_IPV4 is not set
 +# CONFIG_NF_TABLES_ARP is not set
 +# CONFIG_NF_DUP_IPV4 is not set
 +# CONFIG_NF_LOG_ARP is not set
 +# CONFIG_NF_LOG_IPV4 is not set
 +# CONFIG_NF_REJECT_IPV4 is not set
 +CONFIG_IP_NF_IPTABLES=y
 +# CONFIG_IP_NF_MATCH_AH is not set
 +# CONFIG_IP_NF_MATCH_ECN is not set
 +# CONFIG_IP_NF_MATCH_TTL is not set
 +CONFIG_IP_NF_FILTER=y
 +# CONFIG_IP_NF_TARGET_REJECT is not set
 +# CONFIG_IP_NF_MANGLE is not set
 +# CONFIG_IP_NF_RAW is not set
 +# CONFIG_IP_NF_SECURITY is not set
 +# CONFIG_IP_NF_ARPTABLES is not set
 +# end of IP: Netfilter Configuration
 +
 +#
 +# IPv6: Netfilter Configuration
 +#
 +# CONFIG_NF_SOCKET_IPV6 is not set
 +# CONFIG_NF_TPROXY_IPV6 is not set
 +# CONFIG_NF_TABLES_IPV6 is not set
 +# CONFIG_NF_DUP_IPV6 is not set
 +# CONFIG_NF_REJECT_IPV6 is not set
 +# CONFIG_NF_LOG_IPV6 is not set
 +# CONFIG_IP6_NF_IPTABLES is not set
 +# end of IPv6: Netfilter Configuration
 +
++CONFIG_NF_DEFRAG_IPV6=y
 +CONFIG_BPFILTER=y
 +CONFIG_BPFILTER_UMH=y
 +# CONFIG_IP_DCCP is not set
 +# CONFIG_IP_SCTP is not set
 +# CONFIG_RDS is not set
 +# CONFIG_TIPC is not set
 +# CONFIG_ATM is not set
 +# CONFIG_L2TP is not set
 +# CONFIG_BRIDGE is not set
 +# CONFIG_NET_DSA is not set
 +CONFIG_VLAN_8021Q=y
 +# CONFIG_VLAN_8021Q_GVRP is not set
 +# CONFIG_VLAN_8021Q_MVRP is not set
 +# CONFIG_DECNET is not set
 +# CONFIG_LLC2 is not set
 +# CONFIG_ATALK is not set
 +# CONFIG_X25 is not set
 +# CONFIG_LAPB is not set
 +# CONFIG_PHONET is not set
 +# CONFIG_6LOWPAN is not set
 +# CONFIG_IEEE802154 is not set
 +CONFIG_NET_SCHED=y
 +
 +#
 +# Queueing/Scheduling
 +#
 +# CONFIG_NET_SCH_CBQ is not set
 +# CONFIG_NET_SCH_HTB is not set
 +# CONFIG_NET_SCH_HFSC is not set
 +# CONFIG_NET_SCH_PRIO is not set
 +# CONFIG_NET_SCH_MULTIQ is not set
 +# CONFIG_NET_SCH_RED is not set
 +# CONFIG_NET_SCH_SFB is not set
 +# CONFIG_NET_SCH_SFQ is not set
 +# CONFIG_NET_SCH_TEQL is not set
 +# CONFIG_NET_SCH_TBF is not set
 +# CONFIG_NET_SCH_CBS is not set
 +# CONFIG_NET_SCH_ETF is not set
 +# CONFIG_NET_SCH_TAPRIO is not set
 +# CONFIG_NET_SCH_GRED is not set
 +# CONFIG_NET_SCH_DSMARK is not set
 +# CONFIG_NET_SCH_NETEM is not set
 +# CONFIG_NET_SCH_DRR is not set
 +# CONFIG_NET_SCH_MQPRIO is not set
 +# CONFIG_NET_SCH_SKBPRIO is not set
 +# CONFIG_NET_SCH_CHOKE is not set
 +# CONFIG_NET_SCH_QFQ is not set
 +# CONFIG_NET_SCH_CODEL is not set
 +# CONFIG_NET_SCH_FQ_CODEL is not set
 +# CONFIG_NET_SCH_CAKE is not set
 +CONFIG_NET_SCH_FQ=y
 +# CONFIG_NET_SCH_HHF is not set
 +# CONFIG_NET_SCH_PIE is not set
 +CONFIG_NET_SCH_INGRESS=y
 +# CONFIG_NET_SCH_PLUG is not set
 +# CONFIG_NET_SCH_ETS is not set
 +# CONFIG_NET_SCH_DEFAULT is not set
 +
 +#
 +# Classification
 +#
 +CONFIG_NET_CLS=y
 +# CONFIG_NET_CLS_BASIC is not set
 +# CONFIG_NET_CLS_TCINDEX is not set
 +# CONFIG_NET_CLS_ROUTE4 is not set
 +# CONFIG_NET_CLS_FW is not set
 +# CONFIG_NET_CLS_U32 is not set
 +# CONFIG_NET_CLS_RSVP is not set
 +# CONFIG_NET_CLS_RSVP6 is not set
 +# CONFIG_NET_CLS_FLOW is not set
 +# CONFIG_NET_CLS_CGROUP is not set
 +CONFIG_NET_CLS_BPF=y
 +CONFIG_NET_CLS_FLOWER=y
 +# CONFIG_NET_CLS_MATCHALL is not set
 +# CONFIG_NET_EMATCH is not set
 +CONFIG_NET_CLS_ACT=y
 +# CONFIG_NET_ACT_POLICE is not set
 +CONFIG_NET_ACT_GACT=y
 +# CONFIG_GACT_PROB is not set
 +# CONFIG_NET_ACT_MIRRED is not set
 +# CONFIG_NET_ACT_SAMPLE is not set
 +# CONFIG_NET_ACT_IPT is not set
 +# CONFIG_NET_ACT_NAT is not set
 +# CONFIG_NET_ACT_PEDIT is not set
 +# CONFIG_NET_ACT_SIMP is not set
 +# CONFIG_NET_ACT_SKBEDIT is not set
 +# CONFIG_NET_ACT_CSUM is not set
 +# CONFIG_NET_ACT_MPLS is not set
 +# CONFIG_NET_ACT_VLAN is not set
 +CONFIG_NET_ACT_BPF=y
 +# CONFIG_NET_ACT_SKBMOD is not set
 +# CONFIG_NET_ACT_IFE is not set
 +# CONFIG_NET_ACT_TUNNEL_KEY is not set
 +# CONFIG_NET_ACT_GATE is not set
 +# CONFIG_NET_TC_SKB_EXT is not set
 +CONFIG_NET_SCH_FIFO=y
 +# CONFIG_DCB is not set
 +# CONFIG_DNS_RESOLVER is not set
 +# CONFIG_BATMAN_ADV is not set
 +# CONFIG_OPENVSWITCH is not set
 +# CONFIG_VSOCKETS is not set
 +# CONFIG_NETLINK_DIAG is not set
 +CONFIG_MPLS=y
 +CONFIG_NET_MPLS_GSO=y
 +CONFIG_MPLS_ROUTING=y
 +CONFIG_MPLS_IPTUNNEL=y
 +# CONFIG_NET_NSH is not set
 +# CONFIG_HSR is not set
 +# CONFIG_NET_SWITCHDEV is not set
 +CONFIG_NET_L3_MASTER_DEV=y
 +# CONFIG_QRTR is not set
 +# CONFIG_NET_NCSI is not set
 +CONFIG_PCPU_DEV_REFCNT=y
 +CONFIG_RPS=y
 +CONFIG_RFS_ACCEL=y
 +CONFIG_SOCK_RX_QUEUE_MAPPING=y
 +CONFIG_XPS=y
 +# CONFIG_CGROUP_NET_PRIO is not set
 +CONFIG_CGROUP_NET_CLASSID=y
 +CONFIG_NET_RX_BUSY_POLL=y
 +CONFIG_BQL=y
 +CONFIG_BPF_STREAM_PARSER=y
 +CONFIG_NET_FLOW_LIMIT=y
 +
 +#
 +# Network testing
 +#
 +# CONFIG_NET_PKTGEN is not set
 +# CONFIG_NET_DROP_MONITOR is not set
 +# end of Network testing
 +# end of Networking options
 +
 +# CONFIG_CAN is not set
 +# CONFIG_AF_RXRPC is not set
 +# CONFIG_AF_KCM is not set
 +CONFIG_STREAM_PARSER=y
 +# CONFIG_MCTP is not set
 +CONFIG_FIB_RULES=y
 +# CONFIG_RFKILL is not set
 +CONFIG_NET_9P=y
 +CONFIG_NET_9P_VIRTIO=y
 +# CONFIG_NET_9P_DEBUG is not set
 +# CONFIG_CAIF is not set
 +# CONFIG_CEPH_LIB is not set
 +# CONFIG_NFC is not set
 +# CONFIG_PSAMPLE is not set
 +# CONFIG_NET_IFE is not set
 +CONFIG_LWTUNNEL=y
 +CONFIG_LWTUNNEL_BPF=y
 +CONFIG_DST_CACHE=y
 +CONFIG_GRO_CELLS=y
 +CONFIG_NET_SOCK_MSG=y
 +CONFIG_NET_DEVLINK=y
 +CONFIG_FAILOVER=y
 +CONFIG_ETHTOOL_NETLINK=y
 +
 +#
 +# Device Drivers
 +#
 +CONFIG_HAVE_PCI=y
 +CONFIG_PCI=y
 +CONFIG_PCI_DOMAINS=y
 +# CONFIG_PCIEPORTBUS is not set
 +CONFIG_PCIEASPM=y
 +CONFIG_PCIEASPM_DEFAULT=y
 +# CONFIG_PCIEASPM_POWERSAVE is not set
 +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
 +# CONFIG_PCIEASPM_PERFORMANCE is not set
 +# CONFIG_PCIE_PTM is not set
 +CONFIG_PCI_MSI=y
 +CONFIG_PCI_MSI_IRQ_DOMAIN=y
 +CONFIG_PCI_MSI_ARCH_FALLBACKS=y
 +# CONFIG_PCI_DEBUG is not set
 +# CONFIG_PCI_STUB is not set
 +# CONFIG_PCI_IOV is not set
 +# CONFIG_PCI_PRI is not set
 +# CONFIG_PCI_PASID is not set
 +# CONFIG_PCIE_BUS_TUNE_OFF is not set
 +CONFIG_PCIE_BUS_DEFAULT=y
 +# CONFIG_PCIE_BUS_SAFE is not set
 +# CONFIG_PCIE_BUS_PERFORMANCE is not set
 +# CONFIG_PCIE_BUS_PEER2PEER is not set
 +# CONFIG_HOTPLUG_PCI is not set
 +
 +#
 +# PCI controller drivers
 +#
 +
 +#
 +# DesignWare PCI Core Support
 +#
 +# CONFIG_PCIE_DW_PLAT_HOST is not set
 +# CONFIG_PCI_MESON is not set
 +# end of DesignWare PCI Core Support
 +
 +#
 +# Mobiveil PCIe Core Support
 +#
 +# end of Mobiveil PCIe Core Support
 +
 +#
 +# Cadence PCIe controllers support
 +#
 +# end of Cadence PCIe controllers support
 +# end of PCI controller drivers
 +
 +#
 +# PCI Endpoint
 +#
 +# CONFIG_PCI_ENDPOINT is not set
 +# end of PCI Endpoint
 +
 +#
 +# PCI switch controller drivers
 +#
 +# CONFIG_PCI_SW_SWITCHTEC is not set
 +# end of PCI switch controller drivers
 +
 +# CONFIG_CXL_BUS is not set
 +# CONFIG_PCCARD is not set
 +# CONFIG_RAPIDIO is not set
 +
 +#
 +# Generic Driver Options
 +#
 +# CONFIG_UEVENT_HELPER is not set
 +CONFIG_DEVTMPFS=y
 +# CONFIG_DEVTMPFS_MOUNT is not set
 +CONFIG_STANDALONE=y
 +CONFIG_PREVENT_FIRMWARE_BUILD=y
 +
 +#
 +# Firmware loader
 +#
 +CONFIG_FW_LOADER=y
 +CONFIG_EXTRA_FIRMWARE=""
 +# CONFIG_FW_LOADER_USER_HELPER is not set
 +# CONFIG_FW_LOADER_COMPRESS is not set
 +# end of Firmware loader
 +
 +CONFIG_ALLOW_DEV_COREDUMP=y
 +# CONFIG_DEBUG_DRIVER is not set
 +# CONFIG_DEBUG_DEVRES is not set
 +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
 +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
 +CONFIG_SYS_HYPERVISOR=y
 +CONFIG_GENERIC_CPU_AUTOPROBE=y
 +CONFIG_GENERIC_CPU_VULNERABILITIES=y
 +# end of Generic Driver Options
 +
 +#
 +# Bus devices
 +#
 +# CONFIG_MHI_BUS is not set
 +# end of Bus devices
 +
 +# CONFIG_CONNECTOR is not set
 +
 +#
 +# Firmware Drivers
 +#
 +
 +#
 +# ARM System Control and Management Interface Protocol
 +#
 +# end of ARM System Control and Management Interface Protocol
 +
 +# CONFIG_FIRMWARE_MEMMAP is not set
 +# CONFIG_GOOGLE_FIRMWARE is not set
 +
 +#
 +# Tegra firmware driver
 +#
 +# end of Tegra firmware driver
 +# end of Firmware Drivers
 +
 +# CONFIG_GNSS is not set
 +# CONFIG_MTD is not set
 +# CONFIG_OF is not set
 +# CONFIG_PARPORT is not set
 +CONFIG_BLK_DEV=y
 +# CONFIG_BLK_DEV_NULL_BLK is not set
 +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
 +CONFIG_BLK_DEV_LOOP=y
 +CONFIG_BLK_DEV_LOOP_MIN_COUNT=8
 +# CONFIG_BLK_DEV_DRBD is not set
 +# CONFIG_BLK_DEV_NBD is not set
 +# CONFIG_BLK_DEV_SX8 is not set
 +CONFIG_BLK_DEV_RAM=y
 +CONFIG_BLK_DEV_RAM_COUNT=16
 +CONFIG_BLK_DEV_RAM_SIZE=4096
 +# CONFIG_CDROM_PKTCDVD is not set
 +# CONFIG_ATA_OVER_ETH is not set
 +
 +#
 +# S/390 block device drivers
 +#
 +# CONFIG_DCSSBLK is not set
 +# CONFIG_DASD is not set
 +CONFIG_VIRTIO_BLK=y
 +# CONFIG_BLK_DEV_RBD is not set
 +# CONFIG_BLK_DEV_RSXX is not set
 +
 +#
 +# NVME Support
 +#
 +# CONFIG_BLK_DEV_NVME is not set
 +# CONFIG_NVME_FC is not set
 +# CONFIG_NVME_TCP is not set
 +# end of NVME Support
 +
 +#
 +# Misc devices
 +#
 +# CONFIG_DUMMY_IRQ is not set
 +# CONFIG_PHANTOM is not set
 +# CONFIG_TIFM_CORE is not set
 +# CONFIG_ENCLOSURE_SERVICES is not set
 +# CONFIG_HP_ILO is not set
 +# CONFIG_SRAM is not set
 +# CONFIG_DW_XDATA_PCIE is not set
 +# CONFIG_PCI_ENDPOINT_TEST is not set
 +# CONFIG_XILINX_SDFEC is not set
 +# CONFIG_C2PORT is not set
 +
 +#
 +# EEPROM support
 +#
 +# CONFIG_EEPROM_93CX6 is not set
 +# end of EEPROM support
 +
 +# CONFIG_CB710_CORE is not set
 +
 +#
 +# Texas Instruments shared transport line discipline
 +#
 +# end of Texas Instruments shared transport line discipline
 +
 +#
 +# Altera FPGA firmware download module (requires I2C)
 +#
 +# CONFIG_GENWQE is not set
 +# CONFIG_ECHO is not set
 +# CONFIG_BCM_VK is not set
 +# CONFIG_MISC_ALCOR_PCI is not set
 +# CONFIG_MISC_RTSX_PCI is not set
 +# CONFIG_HABANA_AI is not set
 +# CONFIG_UACCE is not set
 +# CONFIG_PVPANIC is not set
 +# end of Misc devices
 +
 +#
 +# SCSI device support
 +#
 +CONFIG_SCSI_MOD=y
 +# CONFIG_RAID_ATTRS is not set
 +CONFIG_SCSI_COMMON=y
 +CONFIG_SCSI=y
 +CONFIG_SCSI_DMA=y
 +# CONFIG_SCSI_PROC_FS is not set
 +
 +#
 +# SCSI support type (disk, tape, CD-ROM)
 +#
 +# CONFIG_BLK_DEV_SD is not set
 +# CONFIG_CHR_DEV_ST is not set
 +# CONFIG_BLK_DEV_SR is not set
 +# CONFIG_CHR_DEV_SG is not set
 +# CONFIG_BLK_DEV_BSG is not set
 +# CONFIG_CHR_DEV_SCH is not set
 +# CONFIG_SCSI_CONSTANTS is not set
 +# CONFIG_SCSI_LOGGING is not set
 +# CONFIG_SCSI_SCAN_ASYNC is not set
 +
 +#
 +# SCSI Transports
 +#
 +# CONFIG_SCSI_SPI_ATTRS is not set
 +# CONFIG_SCSI_FC_ATTRS is not set
 +# CONFIG_SCSI_ISCSI_ATTRS is not set
 +# CONFIG_SCSI_SAS_ATTRS is not set
 +# CONFIG_SCSI_SAS_LIBSAS is not set
 +# CONFIG_SCSI_SRP_ATTRS is not set
 +# end of SCSI Transports
 +
 +CONFIG_SCSI_LOWLEVEL=y
 +# CONFIG_ISCSI_TCP is not set
 +# CONFIG_ISCSI_BOOT_SYSFS is not set
 +# CONFIG_SCSI_CXGB3_ISCSI is not set
 +# CONFIG_SCSI_BNX2_ISCSI is not set
 +# CONFIG_BE2ISCSI is not set
 +# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
 +# CONFIG_SCSI_HPSA is not set
 +# CONFIG_SCSI_3W_9XXX is not set
 +# CONFIG_SCSI_3W_SAS is not set
 +# CONFIG_SCSI_ACARD is not set
 +# CONFIG_SCSI_AACRAID is not set
 +# CONFIG_SCSI_AIC7XXX is not set
 +# CONFIG_SCSI_AIC79XX is not set
 +# CONFIG_SCSI_AIC94XX is not set
 +# CONFIG_SCSI_MVSAS is not set
 +# CONFIG_SCSI_MVUMI is not set
 +# CONFIG_SCSI_ADVANSYS is not set
 +# CONFIG_SCSI_ARCMSR is not set
 +# CONFIG_SCSI_ESAS2R is not set
 +# CONFIG_MEGARAID_NEWGEN is not set
 +# CONFIG_MEGARAID_LEGACY is not set
 +# CONFIG_MEGARAID_SAS is not set
 +# CONFIG_SCSI_MPT3SAS is not set
 +# CONFIG_SCSI_MPT2SAS is not set
 +# CONFIG_SCSI_MPI3MR is not set
 +# CONFIG_SCSI_UFSHCD is not set
 +# CONFIG_SCSI_HPTIOP is not set
 +# CONFIG_SCSI_MYRB is not set
 +# CONFIG_SCSI_SNIC is not set
 +# CONFIG_SCSI_DMX3191D is not set
 +# CONFIG_SCSI_FDOMAIN_PCI is not set
 +# CONFIG_SCSI_IPS is not set
 +# CONFIG_SCSI_INITIO is not set
 +# CONFIG_SCSI_INIA100 is not set
 +# CONFIG_SCSI_STEX is not set
 +# CONFIG_SCSI_SYM53C8XX_2 is not set
 +# CONFIG_SCSI_QLOGIC_1280 is not set
 +# CONFIG_SCSI_QLA_ISCSI is not set
 +# CONFIG_SCSI_DC395x is not set
 +# CONFIG_SCSI_AM53C974 is not set
 +# CONFIG_SCSI_WD719X is not set
 +# CONFIG_SCSI_DEBUG is not set
 +# CONFIG_SCSI_PMCRAID is not set
 +# CONFIG_SCSI_PM8001 is not set
 +CONFIG_SCSI_VIRTIO=y
 +# CONFIG_SCSI_DH is not set
 +# end of SCSI device support
 +
 +# CONFIG_ATA is not set
 +# CONFIG_MD is not set
 +# CONFIG_TARGET_CORE is not set
 +# CONFIG_FUSION is not set
 +
 +#
 +# IEEE 1394 (FireWire) support
 +#
 +# CONFIG_FIREWIRE is not set
 +# CONFIG_FIREWIRE_NOSY is not set
 +# end of IEEE 1394 (FireWire) support
 +
 +CONFIG_NETDEVICES=y
 +CONFIG_NET_CORE=y
 +CONFIG_BONDING=y
 +# CONFIG_DUMMY is not set
 +# CONFIG_WIREGUARD is not set
 +# CONFIG_EQUALIZER is not set
 +# CONFIG_NET_FC is not set
 +# CONFIG_NET_TEAM is not set
 +CONFIG_MACVLAN=y
 +CONFIG_MACVTAP=y
 +CONFIG_IPVLAN_L3S=y
 +CONFIG_IPVLAN=y
 +# CONFIG_IPVTAP is not set
 +CONFIG_VXLAN=y
 +CONFIG_GENEVE=y
 +# CONFIG_BAREUDP is not set
 +# CONFIG_GTP is not set
 +# CONFIG_AMT is not set
 +# CONFIG_MACSEC is not set
 +# CONFIG_NETCONSOLE is not set
 +CONFIG_TUN=y
 +CONFIG_TAP=y
 +# CONFIG_TUN_VNET_CROSS_LE is not set
 +CONFIG_VETH=y
 +CONFIG_VIRTIO_NET=y
 +# CONFIG_NLMON is not set
 +CONFIG_NET_VRF=y
 +# CONFIG_ARCNET is not set
 +# CONFIG_ETHERNET is not set
 +# CONFIG_FDDI is not set
 +# CONFIG_HIPPI is not set
 +# CONFIG_PHYLIB is not set
 +# CONFIG_MDIO_DEVICE is not set
 +
 +#
 +# PCS device drivers
 +#
 +# end of PCS device drivers
 +
 +# CONFIG_PPP is not set
 +# CONFIG_SLIP is not set
 +
 +#
 +# S/390 network device drivers
 +#
 +# CONFIG_CTCM is not set
 +# end of S/390 network device drivers
 +
 +#
 +# Host-side USB support is needed for USB Network Adapter support
 +#
 +# CONFIG_WAN is not set
 +
 +#
 +# Wireless WAN
 +#
 +# CONFIG_WWAN is not set
 +# end of Wireless WAN
 +
 +# CONFIG_VMXNET3 is not set
 +CONFIG_NETDEVSIM=y
 +CONFIG_NET_FAILOVER=y
 +
 +#
 +# Input device support
 +#
 +CONFIG_INPUT=y
 +# CONFIG_INPUT_FF_MEMLESS is not set
 +# CONFIG_INPUT_SPARSEKMAP is not set
 +# CONFIG_INPUT_MATRIXKMAP is not set
 +
 +#
 +# Userland interfaces
 +#
 +# CONFIG_INPUT_MOUSEDEV is not set
 +# CONFIG_INPUT_JOYDEV is not set
 +# CONFIG_INPUT_EVDEV is not set
 +# CONFIG_INPUT_EVBUG is not set
 +
 +#
 +# Input Device Drivers
 +#
 +CONFIG_INPUT_KEYBOARD=y
 +CONFIG_KEYBOARD_ATKBD=y
 +# CONFIG_KEYBOARD_LKKBD is not set
 +# CONFIG_KEYBOARD_NEWTON is not set
 +# CONFIG_KEYBOARD_OPENCORES is not set
 +# CONFIG_KEYBOARD_STOWAWAY is not set
 +# CONFIG_KEYBOARD_SUNKBD is not set
 +# CONFIG_KEYBOARD_XTKBD is not set
 +CONFIG_INPUT_MOUSE=y
 +CONFIG_MOUSE_PS2=y
 +CONFIG_MOUSE_PS2_ALPS=y
 +CONFIG_MOUSE_PS2_BYD=y
 +CONFIG_MOUSE_PS2_LOGIPS2PP=y
 +CONFIG_MOUSE_PS2_SYNAPTICS=y
 +CONFIG_MOUSE_PS2_CYPRESS=y
 +CONFIG_MOUSE_PS2_TRACKPOINT=y
 +# CONFIG_MOUSE_PS2_ELANTECH is not set
 +# CONFIG_MOUSE_PS2_SENTELIC is not set
 +# CONFIG_MOUSE_PS2_TOUCHKIT is not set
 +CONFIG_MOUSE_PS2_FOCALTECH=y
 +# CONFIG_MOUSE_SERIAL is not set
 +# CONFIG_MOUSE_APPLETOUCH is not set
 +# CONFIG_MOUSE_BCM5974 is not set
 +# CONFIG_MOUSE_VSXXXAA is not set
 +# CONFIG_MOUSE_SYNAPTICS_USB is not set
 +# CONFIG_INPUT_JOYSTICK is not set
 +# CONFIG_INPUT_TABLET is not set
 +# CONFIG_INPUT_TOUCHSCREEN is not set
 +# CONFIG_INPUT_MISC is not set
 +# CONFIG_RMI4_CORE is not set
 +
 +#
 +# Hardware I/O ports
 +#
 +CONFIG_SERIO=y
 +CONFIG_SERIO_SERPORT=y
 +# CONFIG_SERIO_PCIPS2 is not set
 +CONFIG_SERIO_LIBPS2=y
 +# CONFIG_SERIO_RAW is not set
 +# CONFIG_SERIO_ALTERA_PS2 is not set
 +# CONFIG_SERIO_PS2MULT is not set
 +# CONFIG_SERIO_ARC_PS2 is not set
 +# CONFIG_USERIO is not set
 +# CONFIG_GAMEPORT is not set
 +# end of Hardware I/O ports
 +# end of Input device support
 +
 +#
 +# Character devices
 +#
 +CONFIG_TTY=y
 +# CONFIG_VT is not set
 +CONFIG_UNIX98_PTYS=y
 +CONFIG_LEGACY_PTYS=y
 +CONFIG_LEGACY_PTY_COUNT=256
 +CONFIG_LDISC_AUTOLOAD=y
 +
 +#
 +# Serial drivers
 +#
 +
 +#
 +# Non-8250 serial port support
 +#
 +# CONFIG_SERIAL_UARTLITE is not set
 +# CONFIG_SERIAL_JSM is not set
 +# CONFIG_SERIAL_SCCNXP is not set
 +# CONFIG_SERIAL_ALTERA_JTAGUART is not set
 +# CONFIG_SERIAL_ALTERA_UART is not set
 +# CONFIG_SERIAL_ARC is not set
 +# CONFIG_SERIAL_RP2 is not set
 +# CONFIG_SERIAL_FSL_LPUART is not set
 +# CONFIG_SERIAL_FSL_LINFLEXUART is not set
 +# end of Serial drivers
 +
 +# CONFIG_SERIAL_NONSTANDARD is not set
 +# CONFIG_N_GSM is not set
 +# CONFIG_NOZOMI is not set
 +# CONFIG_NULL_TTY is not set
 +# CONFIG_HVC_IUCV is not set
 +# CONFIG_SERIAL_DEV_BUS is not set
 +# CONFIG_TTY_PRINTK is not set
 +# CONFIG_VIRTIO_CONSOLE is not set
 +# CONFIG_IPMI_HANDLER is not set
 +CONFIG_HW_RANDOM=y
 +# CONFIG_HW_RANDOM_TIMERIOMEM is not set
 +# CONFIG_HW_RANDOM_BA431 is not set
 +# CONFIG_HW_RANDOM_VIRTIO is not set
 +CONFIG_HW_RANDOM_S390=y
 +# CONFIG_HW_RANDOM_XIPHERA is not set
 +# CONFIG_APPLICOM is not set
 +CONFIG_DEVMEM=y
 +CONFIG_DEVPORT=y
 +# CONFIG_HANGCHECK_TIMER is not set
 +CONFIG_TCG_TPM=y
 +CONFIG_HW_RANDOM_TPM=y
 +# CONFIG_TCG_VTPM_PROXY is not set
 +
 +#
 +# S/390 character device drivers
 +#
 +CONFIG_TN3270=y
 +CONFIG_TN3270_TTY=y
 +# CONFIG_TN3270_FS is not set
 +CONFIG_TN3270_CONSOLE=y
 +# CONFIG_TN3215 is not set
 +CONFIG_CCW_CONSOLE=y
 +# CONFIG_SCLP_TTY is not set
 +CONFIG_SCLP_VT220_TTY=y
 +CONFIG_SCLP_VT220_CONSOLE=y
 +# CONFIG_HMC_DRV is not set
 +# CONFIG_SCLP_OFB is not set
 +# CONFIG_S390_TAPE is not set
 +# CONFIG_VMCP is not set
 +# CONFIG_MONWRITER is not set
 +# CONFIG_S390_VMUR is not set
 +# CONFIG_XILLYBUS is not set
 +# CONFIG_RANDOM_TRUST_CPU is not set
 +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set
 +# end of Character devices
 +
 +#
 +# I2C support
 +#
 +# CONFIG_I2C is not set
 +# end of I2C support
 +
 +# CONFIG_I3C is not set
 +# CONFIG_SPI is not set
 +# CONFIG_SPMI is not set
 +# CONFIG_HSI is not set
 +# CONFIG_PPS is not set
 +
 +#
 +# PTP clock support
 +#
 +# CONFIG_PTP_1588_CLOCK is not set
 +CONFIG_PTP_1588_CLOCK_OPTIONAL=y
 +
 +#
 +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
 +#
 +# end of PTP clock support
 +
 +# CONFIG_PINCTRL is not set
 +# CONFIG_GPIOLIB is not set
 +# CONFIG_W1 is not set
 +# CONFIG_POWER_RESET is not set
 +# CONFIG_POWER_SUPPLY is not set
 +CONFIG_HWMON=y
 +# CONFIG_HWMON_DEBUG_CHIP is not set
 +
 +#
 +# Native drivers
 +#
 +# CONFIG_SENSORS_AS370 is not set
 +# CONFIG_SENSORS_AXI_FAN_CONTROL is not set
 +# CONFIG_SENSORS_ASPEED is not set
 +# CONFIG_SENSORS_CORSAIR_CPRO is not set
 +# CONFIG_SENSORS_CORSAIR_PSU is not set
 +# CONFIG_SENSORS_I5K_AMB is not set
 +# CONFIG_SENSORS_F71805F is not set
 +# CONFIG_SENSORS_F71882FG is not set
 +# CONFIG_SENSORS_IT87 is not set
 +# CONFIG_SENSORS_MAX197 is not set
 +# CONFIG_SENSORS_MR75203 is not set
 +# CONFIG_SENSORS_PC87360 is not set
 +# CONFIG_SENSORS_PC87427 is not set
 +# CONFIG_SENSORS_NTC_THERMISTOR is not set
 +# CONFIG_SENSORS_NCT6683 is not set
 +# CONFIG_SENSORS_NCT6775 is not set
 +# CONFIG_SENSORS_NPCM7XX is not set
 +# CONFIG_SENSORS_SIS5595 is not set
 +# CONFIG_SENSORS_SMSC47M1 is not set
 +# CONFIG_SENSORS_SMSC47B397 is not set
 +# CONFIG_SENSORS_VIA686A is not set
 +# CONFIG_SENSORS_VT1211 is not set
 +# CONFIG_SENSORS_VT8231 is not set
 +# CONFIG_SENSORS_W83627HF is not set
 +# CONFIG_SENSORS_W83627EHF is not set
 +# CONFIG_THERMAL is not set
 +# CONFIG_WATCHDOG is not set
 +CONFIG_SSB_POSSIBLE=y
 +# CONFIG_SSB is not set
 +CONFIG_BCMA_POSSIBLE=y
 +# CONFIG_BCMA is not set
 +
 +#
 +# Multifunction device drivers
 +#
 +# CONFIG_MFD_MADERA is not set
 +# CONFIG_HTC_PASIC3 is not set
 +# CONFIG_LPC_ICH is not set
 +# CONFIG_LPC_SCH is not set
 +# CONFIG_MFD_INTEL_PMT is not set
 +# CONFIG_MFD_JANZ_CMODIO is not set
 +# CONFIG_MFD_KEMPLD is not set
 +# CONFIG_MFD_MT6397 is not set
 +# CONFIG_MFD_RDC321X is not set
 +# CONFIG_MFD_SM501 is not set
 +# CONFIG_MFD_SYSCON is not set
 +# CONFIG_MFD_TI_AM335X_TSCADC is not set
 +# CONFIG_MFD_TQMX86 is not set
 +# CONFIG_MFD_VX855 is not set
 +# end of Multifunction device drivers
 +
 +# CONFIG_REGULATOR is not set
 +CONFIG_RC_CORE=y
 +# CONFIG_RC_MAP is not set
 +CONFIG_LIRC=y
 +CONFIG_BPF_LIRC_MODE2=y
 +# CONFIG_RC_DECODERS is not set
 +CONFIG_RC_DEVICES=y
 +CONFIG_RC_LOOPBACK=y
 +# CONFIG_IR_SERIAL is not set
 +
 +#
 +# CEC support
 +#
 +# CONFIG_MEDIA_CEC_SUPPORT is not set
 +# end of CEC support
 +
 +# CONFIG_MEDIA_SUPPORT is not set
 +
 +#
 +# Graphics support
 +#
 +# CONFIG_DRM is not set
 +
 +#
 +# ARM devices
 +#
 +# end of ARM devices
 +
 +#
 +# Frame buffer Devices
 +#
 +# CONFIG_FB is not set
 +# end of Frame buffer Devices
 +
 +#
 +# Backlight & LCD device support
 +#
 +# CONFIG_LCD_CLASS_DEVICE is not set
 +# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
 +# end of Backlight & LCD device support
 +# end of Graphics support
 +
 +# CONFIG_SOUND is not set
 +
 +#
 +# HID support
 +#
 +CONFIG_HID=y
 +# CONFIG_HID_BATTERY_STRENGTH is not set
 +# CONFIG_HIDRAW is not set
 +# CONFIG_UHID is not set
 +CONFIG_HID_GENERIC=y
 +
 +#
 +# Special HID drivers
 +#
 +# CONFIG_HID_A4TECH is not set
 +# CONFIG_HID_ACRUX is not set
 +# CONFIG_HID_APPLE is not set
 +# CONFIG_HID_AUREAL is not set
 +# CONFIG_HID_BELKIN is not set
 +# CONFIG_HID_CHERRY is not set
 +# CONFIG_HID_CHICONY is not set
 +# CONFIG_HID_COUGAR is not set
 +# CONFIG_HID_MACALLY is not set
 +# CONFIG_HID_CMEDIA is not set
 +# CONFIG_HID_CYPRESS is not set
 +# CONFIG_HID_DRAGONRISE is not set
 +# CONFIG_HID_EMS_FF is not set
 +# CONFIG_HID_ELECOM is not set
 +# CONFIG_HID_EZKEY is not set
 +# CONFIG_HID_GEMBIRD is not set
 +# CONFIG_HID_GFRM is not set
 +# CONFIG_HID_GLORIOUS is not set
 +# CONFIG_HID_VIVALDI is not set
 +# CONFIG_HID_KEYTOUCH is not set
 +# CONFIG_HID_KYE is not set
 +# CONFIG_HID_WALTOP is not set
 +# CONFIG_HID_VIEWSONIC is not set
 +# CONFIG_HID_GYRATION is not set
 +# CONFIG_HID_ICADE is not set
 +# CONFIG_HID_ITE is not set
 +# CONFIG_HID_JABRA is not set
 +# CONFIG_HID_TWINHAN is not set
 +# CONFIG_HID_KENSINGTON is not set
 +# CONFIG_HID_LCPOWER is not set
 +# CONFIG_HID_LENOVO is not set
 +# CONFIG_HID_MAGICMOUSE is not set
 +# CONFIG_HID_MALTRON is not set
 +# CONFIG_HID_MAYFLASH is not set
 +# CONFIG_HID_REDRAGON is not set
 +# CONFIG_HID_MICROSOFT is not set
 +# CONFIG_HID_MONTEREY is not set
 +# CONFIG_HID_MULTITOUCH is not set
 +# CONFIG_HID_NTI is not set
 +# CONFIG_HID_ORTEK is not set
 +# CONFIG_HID_PANTHERLORD is not set
 +# CONFIG_HID_PETALYNX is not set
 +# CONFIG_HID_PICOLCD is not set
 +# CONFIG_HID_PLANTRONICS is not set
 +# CONFIG_HID_PLAYSTATION is not set
 +# CONFIG_HID_PRIMAX is not set
 +# CONFIG_HID_SAITEK is not set
 +# CONFIG_HID_SAMSUNG is not set
 +# CONFIG_HID_SEMITEK is not set
 +# CONFIG_HID_SPEEDLINK is not set
 +# CONFIG_HID_STEAM is not set
 +# CONFIG_HID_STEELSERIES is not set
 +# CONFIG_HID_SUNPLUS is not set
 +# CONFIG_HID_RMI is not set
 +# CONFIG_HID_GREENASIA is not set
 +# CONFIG_HID_SMARTJOYPLUS is not set
 +# CONFIG_HID_TIVO is not set
 +# CONFIG_HID_TOPSEED is not set
 +# CONFIG_HID_UDRAW_PS3 is not set
 +# CONFIG_HID_XINMO is not set
 +# CONFIG_HID_ZEROPLUS is not set
 +# CONFIG_HID_ZYDACRON is not set
 +# CONFIG_HID_SENSOR_HUB is not set
 +# CONFIG_HID_ALPS is not set
 +# end of Special HID drivers
 +# end of HID support
 +
 +CONFIG_USB_OHCI_LITTLE_ENDIAN=y
 +CONFIG_USB_SUPPORT=y
 +# CONFIG_USB_ULPI_BUS is not set
 +CONFIG_USB_ARCH_HAS_HCD=y
 +# CONFIG_USB is not set
 +CONFIG_USB_PCI=y
 +
 +#
 +# USB port drivers
 +#
 +
 +#
 +# USB Physical Layer drivers
 +#
 +# CONFIG_NOP_USB_XCEIV is not set
 +# end of USB Physical Layer drivers
 +
 +# CONFIG_USB_GADGET is not set
 +# CONFIG_TYPEC is not set
 +# CONFIG_USB_ROLE_SWITCH is not set
 +# CONFIG_MMC is not set
 +# CONFIG_MEMSTICK is not set
 +# CONFIG_NEW_LEDS is not set
 +# CONFIG_ACCESSIBILITY is not set
 +# CONFIG_INFINIBAND is not set
 +# CONFIG_DMADEVICES is not set
 +
 +#
 +# DMABUF options
 +#
 +# CONFIG_SYNC_FILE is not set
 +# CONFIG_DMABUF_HEAPS is not set
 +# end of DMABUF options
 +
 +# CONFIG_AUXDISPLAY is not set
 +# CONFIG_UIO is not set
 +# CONFIG_VFIO is not set
 +# CONFIG_VIRT_DRIVERS is not set
 +CONFIG_VIRTIO=y
 +CONFIG_VIRTIO_PCI_LIB=y
 +CONFIG_VIRTIO_MENU=y
 +CONFIG_VIRTIO_PCI=y
 +CONFIG_VIRTIO_PCI_LEGACY=y
 +CONFIG_VIRTIO_BALLOON=y
 +# CONFIG_VIRTIO_INPUT is not set
 +# CONFIG_VIRTIO_MMIO is not set
 +# CONFIG_VDPA is not set
 +CONFIG_VHOST_MENU=y
 +# CONFIG_VHOST_NET is not set
 +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
 +
 +#
 +# Microsoft Hyper-V guest support
 +#
 +# end of Microsoft Hyper-V guest support
 +
 +# CONFIG_GREYBUS is not set
 +# CONFIG_COMEDI is not set
 +# CONFIG_STAGING is not set
 +# CONFIG_GOLDFISH is not set
 +# CONFIG_COMMON_CLK is not set
 +# CONFIG_HWSPINLOCK is not set
 +
 +#
 +# Clock Source drivers
 +#
 +# end of Clock Source drivers
 +
 +# CONFIG_MAILBOX is not set
 +CONFIG_IOMMU_API=y
 +CONFIG_IOMMU_SUPPORT=y
 +
 +#
 +# Generic IOMMU Pagetable Support
 +#
 +# end of Generic IOMMU Pagetable Support
 +
 +# CONFIG_IOMMU_DEBUGFS is not set
 +CONFIG_IOMMU_DEFAULT_DMA_STRICT=y
 +# CONFIG_IOMMU_DEFAULT_DMA_LAZY is not set
 +# CONFIG_IOMMU_DEFAULT_PASSTHROUGH is not set
 +CONFIG_S390_IOMMU=y
 +# CONFIG_S390_CCW_IOMMU is not set
 +
 +#
 +# Remoteproc drivers
 +#
 +# CONFIG_REMOTEPROC is not set
 +# end of Remoteproc drivers
 +
 +#
 +# Rpmsg drivers
 +#
 +# CONFIG_RPMSG_VIRTIO is not set
 +# end of Rpmsg drivers
 +
 +#
 +# SOC (System On Chip) specific Drivers
 +#
 +
 +#
 +# Amlogic SoC drivers
 +#
 +# end of Amlogic SoC drivers
 +
 +#
 +# Broadcom SoC drivers
 +#
 +# end of Broadcom SoC drivers
 +
 +#
 +# NXP/Freescale QorIQ SoC drivers
 +#
 +# end of NXP/Freescale QorIQ SoC drivers
 +
 +#
 +# i.MX SoC drivers
 +#
 +# end of i.MX SoC drivers
 +
 +#
 +# Enable LiteX SoC Builder specific drivers
 +#
 +# end of Enable LiteX SoC Builder specific drivers
 +
 +#
 +# Qualcomm SoC drivers
 +#
 +# end of Qualcomm SoC drivers
 +
 +# CONFIG_SOC_TI is not set
 +
 +#
 +# Xilinx SoC drivers
 +#
 +# end of Xilinx SoC drivers
 +# end of SOC (System On Chip) specific Drivers
 +
 +# CONFIG_PM_DEVFREQ is not set
 +# CONFIG_EXTCON is not set
 +# CONFIG_MEMORY is not set
 +# CONFIG_IIO is not set
 +# CONFIG_NTB is not set
 +# CONFIG_VME_BUS is not set
 +# CONFIG_PWM is not set
 +
 +#
 +# IRQ chip support
 +#
 +# end of IRQ chip support
 +
 +# CONFIG_IPACK_BUS is not set
 +# CONFIG_RESET_CONTROLLER is not set
 +
 +#
 +# PHY Subsystem
 +#
 +# CONFIG_GENERIC_PHY is not set
 +# CONFIG_PHY_CAN_TRANSCEIVER is not set
 +# CONFIG_BCM_KONA_USB2_PHY is not set
 +# CONFIG_PHY_PXA_28NM_HSIC is not set
 +# CONFIG_PHY_PXA_28NM_USB2 is not set
 +# end of PHY Subsystem
 +
 +# CONFIG_POWERCAP is not set
 +# CONFIG_MCB is not set
 +
 +#
 +# Performance monitor support
 +#
 +# end of Performance monitor support
 +
 +# CONFIG_RAS is not set
 +# CONFIG_USB4 is not set
 +
 +#
 +# Android
 +#
 +# CONFIG_ANDROID is not set
 +# end of Android
 +
 +# CONFIG_LIBNVDIMM is not set
 +# CONFIG_DAX is not set
 +# CONFIG_NVMEM is not set
 +
 +#
 +# HW tracing support
 +#
 +# CONFIG_STM is not set
 +# CONFIG_INTEL_TH is not set
 +# end of HW tracing support
 +
 +# CONFIG_FPGA is not set
 +# CONFIG_SIOX is not set
 +# CONFIG_SLIMBUS is not set
 +# CONFIG_INTERCONNECT is not set
 +# CONFIG_COUNTER is not set
 +# end of Device Drivers
 +
 +#
 +# File systems
 +#
 +# CONFIG_VALIDATE_FS_PARSER is not set
 +CONFIG_FS_IOMAP=y
 +# CONFIG_EXT2_FS is not set
 +# CONFIG_EXT3_FS is not set
 +CONFIG_EXT4_FS=y
 +CONFIG_EXT4_USE_FOR_EXT2=y
 +CONFIG_EXT4_FS_POSIX_ACL=y
 +CONFIG_EXT4_FS_SECURITY=y
 +# CONFIG_EXT4_DEBUG is not set
 +CONFIG_JBD2=y
 +# CONFIG_JBD2_DEBUG is not set
 +CONFIG_FS_MBCACHE=y
 +# CONFIG_REISERFS_FS is not set
 +# CONFIG_JFS_FS is not set
 +# CONFIG_XFS_FS is not set
 +# CONFIG_GFS2_FS is not set
 +# CONFIG_BTRFS_FS is not set
 +# CONFIG_NILFS2_FS is not set
 +# CONFIG_F2FS_FS is not set
 +# CONFIG_FS_DAX is not set
 +CONFIG_FS_POSIX_ACL=y
 +CONFIG_EXPORTFS=y
 +# CONFIG_EXPORTFS_BLOCK_OPS is not set
 +CONFIG_FILE_LOCKING=y
 +# CONFIG_FS_ENCRYPTION is not set
 +# CONFIG_FS_VERITY is not set
 +CONFIG_FSNOTIFY=y
 +CONFIG_DNOTIFY=y
 +CONFIG_INOTIFY_USER=y
 +CONFIG_FANOTIFY=y
 +# CONFIG_FANOTIFY_ACCESS_PERMISSIONS is not set
 +# CONFIG_QUOTA is not set
 +# CONFIG_AUTOFS4_FS is not set
 +# CONFIG_AUTOFS_FS is not set
 +# CONFIG_FUSE_FS is not set
 +# CONFIG_OVERLAY_FS is not set
 +
 +#
 +# Caches
 +#
 +# CONFIG_FSCACHE is not set
 +# end of Caches
 +
 +#
 +# CD-ROM/DVD Filesystems
 +#
 +# CONFIG_ISO9660_FS is not set
 +# CONFIG_UDF_FS is not set
 +# end of CD-ROM/DVD Filesystems
 +
 +#
 +# DOS/FAT/EXFAT/NT Filesystems
 +#
 +# CONFIG_MSDOS_FS is not set
 +# CONFIG_VFAT_FS is not set
 +# CONFIG_EXFAT_FS is not set
 +# CONFIG_NTFS_FS is not set
 +# CONFIG_NTFS3_FS is not set
 +# end of DOS/FAT/EXFAT/NT Filesystems
 +
 +#
 +# Pseudo filesystems
 +#
 +CONFIG_PROC_FS=y
 +CONFIG_PROC_KCORE=y
 +CONFIG_PROC_VMCORE=y
 +# CONFIG_PROC_VMCORE_DEVICE_DUMP is not set
 +CONFIG_PROC_SYSCTL=y
 +CONFIG_PROC_PAGE_MONITOR=y
 +CONFIG_PROC_CHILDREN=y
 +CONFIG_KERNFS=y
 +CONFIG_SYSFS=y
 +CONFIG_TMPFS=y
 +CONFIG_TMPFS_POSIX_ACL=y
 +CONFIG_TMPFS_XATTR=y
 +# CONFIG_TMPFS_INODE64 is not set
 +CONFIG_ARCH_SUPPORTS_HUGETLBFS=y
 +CONFIG_HUGETLBFS=y
 +CONFIG_HUGETLB_PAGE=y
 +CONFIG_MEMFD_CREATE=y
 +CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
 +# CONFIG_CONFIGFS_FS is not set
 +# end of Pseudo filesystems
 +
 +# CONFIG_MISC_FILESYSTEMS is not set
 +CONFIG_NETWORK_FILESYSTEMS=y
 +# CONFIG_NFS_FS is not set
 +# CONFIG_NFSD is not set
 +# CONFIG_CEPH_FS is not set
 +# CONFIG_CIFS is not set
 +# CONFIG_SMB_SERVER is not set
 +# CONFIG_CODA_FS is not set
 +# CONFIG_AFS_FS is not set
 +CONFIG_9P_FS=y
 +# CONFIG_9P_FS_POSIX_ACL is not set
 +# CONFIG_9P_FS_SECURITY is not set
 +# CONFIG_NLS is not set
 +# CONFIG_UNICODE is not set
 +CONFIG_IO_WQ=y
 +# end of File systems
 +
 +#
 +# Security options
 +#
 +CONFIG_KEYS=y
 +# CONFIG_KEYS_REQUEST_CACHE is not set
 +# CONFIG_PERSISTENT_KEYRINGS is not set
 +# CONFIG_TRUSTED_KEYS is not set
 +# CONFIG_ENCRYPTED_KEYS is not set
 +# CONFIG_KEY_DH_OPERATIONS is not set
 +# CONFIG_SECURITY_DMESG_RESTRICT is not set
 +CONFIG_SECURITY=y
 +CONFIG_SECURITYFS=y
 +CONFIG_SECURITY_NETWORK=y
 +# CONFIG_SECURITY_NETWORK_XFRM is not set
 +# CONFIG_SECURITY_PATH is not set
 +CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
 +# CONFIG_HARDENED_USERCOPY is not set
 +# CONFIG_FORTIFY_SOURCE is not set
 +# CONFIG_STATIC_USERMODEHELPER is not set
 +# CONFIG_SECURITY_SELINUX is not set
 +# CONFIG_SECURITY_SMACK is not set
 +# CONFIG_SECURITY_TOMOYO is not set
 +# CONFIG_SECURITY_APPARMOR is not set
 +# CONFIG_SECURITY_LOADPIN is not set
 +# CONFIG_SECURITY_YAMA is not set
 +# CONFIG_SECURITY_SAFESETID is not set
 +# CONFIG_SECURITY_LOCKDOWN_LSM is not set
 +# CONFIG_SECURITY_LANDLOCK is not set
 +CONFIG_INTEGRITY=y
 +# CONFIG_INTEGRITY_SIGNATURE is not set
 +CONFIG_INTEGRITY_AUDIT=y
 +CONFIG_IMA=y
 +CONFIG_IMA_MEASURE_PCR_IDX=10
 +# CONFIG_IMA_TEMPLATE is not set
 +CONFIG_IMA_NG_TEMPLATE=y
 +# CONFIG_IMA_SIG_TEMPLATE is not set
 +CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng"
 +CONFIG_IMA_DEFAULT_HASH_SHA1=y
 +# CONFIG_IMA_DEFAULT_HASH_SHA256 is not set
 +# CONFIG_IMA_DEFAULT_HASH_SHA512 is not set
 +CONFIG_IMA_DEFAULT_HASH="sha1"
 +# CONFIG_IMA_WRITE_POLICY is not set
 +# CONFIG_IMA_READ_POLICY is not set
 +# CONFIG_IMA_APPRAISE is not set
 +# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set
 +# CONFIG_IMA_DISABLE_HTABLE is not set
 +# CONFIG_EVM is not set
 +CONFIG_DEFAULT_SECURITY_DAC=y
 +CONFIG_LSM="landlock,lockdown,yama,loadpin,safesetid,integrity,bpf"
 +
 +#
 +# Kernel hardening options
 +#
 +
 +#
 +# Memory initialization
 +#
 +CONFIG_INIT_STACK_NONE=y
 +# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
 +# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
 +# end of Memory initialization
 +# end of Kernel hardening options
 +# end of Security options
 +
 +CONFIG_CRYPTO=y
 +
 +#
 +# Crypto core or helper
 +#
 +CONFIG_CRYPTO_ALGAPI=y
 +CONFIG_CRYPTO_ALGAPI2=y
 +CONFIG_CRYPTO_AEAD=y
 +CONFIG_CRYPTO_AEAD2=y
 +CONFIG_CRYPTO_SKCIPHER=y
 +CONFIG_CRYPTO_SKCIPHER2=y
 +CONFIG_CRYPTO_HASH=y
 +CONFIG_CRYPTO_HASH2=y
 +CONFIG_CRYPTO_RNG=y
 +CONFIG_CRYPTO_RNG2=y
 +CONFIG_CRYPTO_RNG_DEFAULT=y
 +CONFIG_CRYPTO_AKCIPHER2=y
 +CONFIG_CRYPTO_KPP2=y
 +CONFIG_CRYPTO_ACOMP2=y
 +CONFIG_CRYPTO_MANAGER=y
 +CONFIG_CRYPTO_MANAGER2=y
 +# CONFIG_CRYPTO_USER is not set
 +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
 +CONFIG_CRYPTO_GF128MUL=y
 +CONFIG_CRYPTO_NULL=y
 +CONFIG_CRYPTO_NULL2=y
 +# CONFIG_CRYPTO_PCRYPT is not set
 +# CONFIG_CRYPTO_CRYPTD is not set
 +CONFIG_CRYPTO_AUTHENC=y
 +# CONFIG_CRYPTO_TEST is not set
 +
 +#
 +# Public-key cryptography
 +#
 +# CONFIG_CRYPTO_RSA is not set
 +# CONFIG_CRYPTO_DH is not set
 +# CONFIG_CRYPTO_ECDH is not set
 +# CONFIG_CRYPTO_ECDSA is not set
 +# CONFIG_CRYPTO_ECRDSA is not set
 +# CONFIG_CRYPTO_SM2 is not set
 +# CONFIG_CRYPTO_CURVE25519 is not set
 +
 +#
 +# Authenticated Encryption with Associated Data
 +#
 +# CONFIG_CRYPTO_CCM is not set
 +CONFIG_CRYPTO_GCM=y
 +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
 +# CONFIG_CRYPTO_AEGIS128 is not set
 +CONFIG_CRYPTO_SEQIV=y
 +CONFIG_CRYPTO_ECHAINIV=y
 +
 +#
 +# Block modes
 +#
 +CONFIG_CRYPTO_CBC=y
 +# CONFIG_CRYPTO_CFB is not set
 +CONFIG_CRYPTO_CTR=y
 +# CONFIG_CRYPTO_CTS is not set
 +# CONFIG_CRYPTO_ECB is not set
 +# CONFIG_CRYPTO_LRW is not set
 +# CONFIG_CRYPTO_OFB is not set
 +# CONFIG_CRYPTO_PCBC is not set
 +# CONFIG_CRYPTO_XTS is not set
 +# CONFIG_CRYPTO_KEYWRAP is not set
 +# CONFIG_CRYPTO_ADIANTUM is not set
 +# CONFIG_CRYPTO_ESSIV is not set
 +
 +#
 +# Hash modes
 +#
 +# CONFIG_CRYPTO_CMAC is not set
 +CONFIG_CRYPTO_HMAC=y
 +# CONFIG_CRYPTO_XCBC is not set
 +# CONFIG_CRYPTO_VMAC is not set
 +
 +#
 +# Digest
 +#
 +CONFIG_CRYPTO_CRC32C=y
 +# CONFIG_CRYPTO_CRC32 is not set
 +# CONFIG_CRYPTO_XXHASH is not set
 +# CONFIG_CRYPTO_BLAKE2B is not set
 +# CONFIG_CRYPTO_BLAKE2S is not set
 +# CONFIG_CRYPTO_CRCT10DIF is not set
 +CONFIG_CRYPTO_GHASH=y
 +# CONFIG_CRYPTO_POLY1305 is not set
 +# CONFIG_CRYPTO_MD4 is not set
 +# CONFIG_CRYPTO_MD5 is not set
 +# CONFIG_CRYPTO_MICHAEL_MIC is not set
 +# CONFIG_CRYPTO_RMD160 is not set
 +CONFIG_CRYPTO_SHA1=y
 +CONFIG_CRYPTO_SHA256=y
 +CONFIG_CRYPTO_SHA512=y
 +# CONFIG_CRYPTO_SHA3 is not set
 +# CONFIG_CRYPTO_SM3 is not set
 +# CONFIG_CRYPTO_STREEBOG is not set
 +# CONFIG_CRYPTO_WP512 is not set
 +
 +#
 +# Ciphers
 +#
 +CONFIG_CRYPTO_AES=y
 +# CONFIG_CRYPTO_AES_TI is not set
 +# CONFIG_CRYPTO_ANUBIS is not set
 +# CONFIG_CRYPTO_ARC4 is not set
 +# CONFIG_CRYPTO_BLOWFISH is not set
 +# CONFIG_CRYPTO_CAMELLIA is not set
 +# CONFIG_CRYPTO_CAST5 is not set
 +# CONFIG_CRYPTO_CAST6 is not set
 +# CONFIG_CRYPTO_DES is not set
 +# CONFIG_CRYPTO_FCRYPT is not set
 +# CONFIG_CRYPTO_KHAZAD is not set
 +# CONFIG_CRYPTO_CHACHA20 is not set
 +# CONFIG_CRYPTO_SEED is not set
 +# CONFIG_CRYPTO_SERPENT is not set
 +# CONFIG_CRYPTO_SM4 is not set
 +# CONFIG_CRYPTO_TEA is not set
 +# CONFIG_CRYPTO_TWOFISH is not set
 +
 +#
 +# Compression
 +#
 +# CONFIG_CRYPTO_DEFLATE is not set
 +# CONFIG_CRYPTO_LZO is not set
 +# CONFIG_CRYPTO_842 is not set
 +# CONFIG_CRYPTO_LZ4 is not set
 +# CONFIG_CRYPTO_LZ4HC is not set
 +# CONFIG_CRYPTO_ZSTD is not set
 +
 +#
 +# Random Number Generation
 +#
 +# CONFIG_CRYPTO_ANSI_CPRNG is not set
 +CONFIG_CRYPTO_DRBG_MENU=y
 +CONFIG_CRYPTO_DRBG_HMAC=y
 +# CONFIG_CRYPTO_DRBG_HASH is not set
 +# CONFIG_CRYPTO_DRBG_CTR is not set
 +CONFIG_CRYPTO_DRBG=y
 +CONFIG_CRYPTO_JITTERENTROPY=y
 +CONFIG_CRYPTO_USER_API=y
 +CONFIG_CRYPTO_USER_API_HASH=y
 +CONFIG_CRYPTO_USER_API_SKCIPHER=y
 +CONFIG_CRYPTO_USER_API_RNG=y
 +# CONFIG_CRYPTO_USER_API_RNG_CAVP is not set
 +# CONFIG_CRYPTO_USER_API_AEAD is not set
 +CONFIG_CRYPTO_USER_API_ENABLE_OBSOLETE=y
 +CONFIG_CRYPTO_HASH_INFO=y
 +
 +#
 +# Crypto library routines
 +#
 +CONFIG_CRYPTO_LIB_AES=y
 +# CONFIG_CRYPTO_LIB_BLAKE2S is not set
 +# CONFIG_CRYPTO_LIB_CHACHA is not set
 +# CONFIG_CRYPTO_LIB_CURVE25519 is not set
 +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=1
 +# CONFIG_CRYPTO_LIB_POLY1305 is not set
 +# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
 +CONFIG_CRYPTO_LIB_SHA256=y
 +CONFIG_CRYPTO_HW=y
 +# CONFIG_ZCRYPT is not set
 +# CONFIG_CRYPTO_SHA1_S390 is not set
 +# CONFIG_CRYPTO_SHA256_S390 is not set
 +# CONFIG_CRYPTO_SHA512_S390 is not set
 +# CONFIG_CRYPTO_SHA3_256_S390 is not set
 +# CONFIG_CRYPTO_SHA3_512_S390 is not set
 +# CONFIG_CRYPTO_DES_S390 is not set
 +# CONFIG_CRYPTO_AES_S390 is not set
 +# CONFIG_S390_PRNG is not set
 +# CONFIG_CRYPTO_GHASH_S390 is not set
 +# CONFIG_CRYPTO_CRC32_S390 is not set
 +# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
 +# CONFIG_CRYPTO_DEV_VIRTIO is not set
 +# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
 +# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
 +# CONFIG_ASYMMETRIC_KEY_TYPE is not set
 +
 +#
 +# Certificates for signature checking
 +#
 +# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
 +# end of Certificates for signature checking
 +
 +CONFIG_BINARY_PRINTF=y
 +
 +#
 +# Library routines
 +#
 +# CONFIG_PACKING is not set
 +CONFIG_BITREVERSE=y
 +CONFIG_GENERIC_STRNCPY_FROM_USER=y
 +CONFIG_GENERIC_STRNLEN_USER=y
 +CONFIG_GENERIC_NET_UTILS=y
 +CONFIG_GENERIC_FIND_FIRST_BIT=y
 +# CONFIG_CORDIC is not set
 +# CONFIG_PRIME_NUMBERS is not set
 +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
 +# CONFIG_CRC_CCITT is not set
 +CONFIG_CRC16=y
 +# CONFIG_CRC_T10DIF is not set
 +# CONFIG_CRC_ITU_T is not set
 +CONFIG_CRC32=y
 +# CONFIG_CRC32_SELFTEST is not set
 +CONFIG_CRC32_SLICEBY8=y
 +# CONFIG_CRC32_SLICEBY4 is not set
 +# CONFIG_CRC32_SARWATE is not set
 +# CONFIG_CRC32_BIT is not set
 +# CONFIG_CRC64 is not set
 +# CONFIG_CRC4 is not set
 +# CONFIG_CRC7 is not set
 +CONFIG_LIBCRC32C=y
 +# CONFIG_CRC8 is not set
 +CONFIG_XXHASH=y
 +# CONFIG_RANDOM32_SELFTEST is not set
 +CONFIG_ZLIB_DFLTCC=y
 +CONFIG_ZSTD_DECOMPRESS=y
 +# CONFIG_XZ_DEC is not set
 +CONFIG_DECOMPRESS_ZSTD=y
 +CONFIG_GENERIC_ALLOCATOR=y
 +CONFIG_XARRAY_MULTI=y
 +CONFIG_ASSOCIATIVE_ARRAY=y
 +CONFIG_HAS_DMA=y
 +CONFIG_DMA_OPS=y
 +CONFIG_NEED_SG_DMA_LENGTH=y
 +CONFIG_NEED_DMA_MAP_STATE=y
 +CONFIG_ARCH_DMA_ADDR_T_64BIT=y
 +CONFIG_ARCH_HAS_FORCE_DMA_UNENCRYPTED=y
 +CONFIG_SWIOTLB=y
 +# CONFIG_DMA_API_DEBUG is not set
 +# CONFIG_DMA_MAP_BENCHMARK is not set
 +CONFIG_SGL_ALLOC=y
 +CONFIG_IOMMU_HELPER=y
 +CONFIG_CPU_RMAP=y
 +CONFIG_DQL=y
 +CONFIG_GLOB=y
 +# CONFIG_GLOB_SELFTEST is not set
 +CONFIG_NLATTR=y
 +# CONFIG_IRQ_POLL is not set
 +CONFIG_HAVE_GENERIC_VDSO=y
 +CONFIG_GENERIC_GETTIMEOFDAY=y
 +CONFIG_GENERIC_VDSO_TIME_NS=y
 +CONFIG_SG_POOL=y
 +CONFIG_ARCH_STACKWALK=y
 +CONFIG_SBITMAP=y
 +# end of Library routines
 +
 +#
 +# Kernel hacking
 +#
 +
 +#
 +# printk and dmesg options
 +#
 +# CONFIG_PRINTK_TIME is not set
 +# CONFIG_PRINTK_CALLER is not set
 +# CONFIG_STACKTRACE_BUILD_ID is not set
 +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4
 +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
 +# CONFIG_DYNAMIC_DEBUG is not set
 +# CONFIG_DYNAMIC_DEBUG_CORE is not set
 +CONFIG_SYMBOLIC_ERRNAME=y
 +CONFIG_DEBUG_BUGVERBOSE=y
 +# end of printk and dmesg options
 +
 +#
 +# Compile-time checks and compiler options
 +#
 +CONFIG_DEBUG_INFO=y
 +# CONFIG_DEBUG_INFO_REDUCED is not set
 +# CONFIG_DEBUG_INFO_COMPRESSED is not set
 +# CONFIG_DEBUG_INFO_SPLIT is not set
 +# CONFIG_DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT is not set
 +CONFIG_DEBUG_INFO_DWARF4=y
 +CONFIG_DEBUG_INFO_BTF=y
 +CONFIG_PAHOLE_HAS_SPLIT_BTF=y
 +CONFIG_DEBUG_INFO_BTF_MODULES=y
 +CONFIG_GDB_SCRIPTS=y
 +CONFIG_FRAME_WARN=2048
 +# CONFIG_STRIP_ASM_SYMS is not set
 +# CONFIG_READABLE_ASM is not set
 +CONFIG_HEADERS_INSTALL=y
 +CONFIG_DEBUG_SECTION_MISMATCH=y
 +CONFIG_SECTION_MISMATCH_WARN_ONLY=y
 +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_64B is not set
 +# CONFIG_VMLINUX_MAP is not set
 +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
 +# end of Compile-time checks and compiler options
 +
 +#
 +# Generic Kernel Debugging Instruments
 +#
 +CONFIG_MAGIC_SYSRQ=y
 +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
 +CONFIG_MAGIC_SYSRQ_SERIAL=y
 +CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
 +CONFIG_DEBUG_FS=y
 +CONFIG_DEBUG_FS_ALLOW_ALL=y
 +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
 +# CONFIG_DEBUG_FS_ALLOW_NONE is not set
 +CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
 +# CONFIG_UBSAN is not set
 +CONFIG_HAVE_ARCH_KCSAN=y
 +# end of Generic Kernel Debugging Instruments
 +
 +CONFIG_DEBUG_KERNEL=y
 +CONFIG_DEBUG_MISC=y
 +
 +#
 +# Memory Debugging
 +#
 +# CONFIG_PAGE_EXTENSION is not set
 +CONFIG_DEBUG_PAGEALLOC=y
 +# CONFIG_DEBUG_PAGEALLOC_ENABLE_DEFAULT is not set
 +# CONFIG_PAGE_OWNER is not set
 +# CONFIG_PAGE_POISONING is not set
 +# CONFIG_DEBUG_PAGE_REF is not set
 +# CONFIG_DEBUG_RODATA_TEST is not set
 +CONFIG_ARCH_HAS_DEBUG_WX=y
 +# CONFIG_DEBUG_WX is not set
 +CONFIG_GENERIC_PTDUMP=y
 +CONFIG_PTDUMP_CORE=y
 +CONFIG_PTDUMP_DEBUGFS=y
 +# CONFIG_DEBUG_OBJECTS is not set
 +# CONFIG_SLUB_DEBUG_ON is not set
 +# CONFIG_SLUB_STATS is not set
 +CONFIG_HAVE_DEBUG_KMEMLEAK=y
 +# CONFIG_DEBUG_KMEMLEAK is not set
 +# CONFIG_DEBUG_STACK_USAGE is not set
 +# CONFIG_SCHED_STACK_END_CHECK is not set
 +CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y
 +# CONFIG_DEBUG_VM is not set
 +# CONFIG_DEBUG_VM_PGTABLE is not set
 +# CONFIG_DEBUG_MEMORY_INIT is not set
 +# CONFIG_DEBUG_PER_CPU_MAPS is not set
 +CONFIG_HAVE_ARCH_KASAN=y
 +CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
 +CONFIG_CC_HAS_KASAN_GENERIC=y
 +CONFIG_CC_HAS_WORKING_NOSANITIZE_ADDRESS=y
 +# CONFIG_KASAN is not set
 +CONFIG_HAVE_ARCH_KFENCE=y
 +# CONFIG_KFENCE is not set
 +# end of Memory Debugging
 +
 +# CONFIG_DEBUG_SHIRQ is not set
 +
 +#
 +# Debug Oops, Lockups and Hangs
 +#
 +CONFIG_PANIC_ON_OOPS=y
 +CONFIG_PANIC_ON_OOPS_VALUE=1
 +CONFIG_PANIC_TIMEOUT=0
 +CONFIG_DETECT_HUNG_TASK=y
 +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
 +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
 +# CONFIG_WQ_WATCHDOG is not set
 +# CONFIG_TEST_LOCKUP is not set
 +# end of Debug Oops, Lockups and Hangs
 +
 +#
 +# Scheduler Debugging
 +#
 +CONFIG_SCHED_DEBUG=y
 +CONFIG_SCHED_INFO=y
 +CONFIG_SCHEDSTATS=y
 +# end of Scheduler Debugging
 +
 +# CONFIG_DEBUG_TIMEKEEPING is not set
 +
 +#
 +# Lock Debugging (spinlocks, mutexes, etc...)
 +#
 +CONFIG_LOCK_DEBUGGING_SUPPORT=y
 +CONFIG_PROVE_LOCKING=y
 +# CONFIG_PROVE_RAW_LOCK_NESTING is not set
 +CONFIG_LOCK_STAT=y
 +CONFIG_DEBUG_RT_MUTEXES=y
 +CONFIG_DEBUG_SPINLOCK=y
 +CONFIG_DEBUG_MUTEXES=y
 +CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
 +CONFIG_DEBUG_RWSEMS=y
 +CONFIG_DEBUG_LOCK_ALLOC=y
 +CONFIG_LOCKDEP=y
 +CONFIG_LOCKDEP_BITS=15
 +CONFIG_LOCKDEP_CHAINS_BITS=16
 +CONFIG_LOCKDEP_STACK_TRACE_BITS=19
 +CONFIG_LOCKDEP_STACK_TRACE_HASH_BITS=14
 +CONFIG_LOCKDEP_CIRCULAR_QUEUE_BITS=12
 +CONFIG_DEBUG_LOCKDEP=y
 +CONFIG_DEBUG_ATOMIC_SLEEP=y
 +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
 +# CONFIG_LOCK_TORTURE_TEST is not set
 +# CONFIG_WW_MUTEX_SELFTEST is not set
 +# CONFIG_SCF_TORTURE_TEST is not set
 +# CONFIG_CSD_LOCK_WAIT_DEBUG is not set
 +# end of Lock Debugging (spinlocks, mutexes, etc...)
 +
 +CONFIG_TRACE_IRQFLAGS=y
 +CONFIG_DEBUG_IRQFLAGS=y
 +CONFIG_STACKTRACE=y
 +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
 +# CONFIG_DEBUG_KOBJECT is not set
 +
 +#
 +# Debug kernel data structures
 +#
 +CONFIG_DEBUG_LIST=y
 +# CONFIG_DEBUG_PLIST is not set
 +CONFIG_DEBUG_SG=y
 +CONFIG_DEBUG_NOTIFIERS=y
 +# CONFIG_BUG_ON_DATA_CORRUPTION is not set
 +# end of Debug kernel data structures
 +
 +# CONFIG_DEBUG_CREDENTIALS is not set
 +
 +#
 +# RCU Debugging
 +#
 +CONFIG_PROVE_RCU=y
 +# CONFIG_RCU_SCALE_TEST is not set
 +# CONFIG_RCU_TORTURE_TEST is not set
 +# CONFIG_RCU_REF_SCALE_TEST is not set
 +CONFIG_RCU_CPU_STALL_TIMEOUT=21
 +# CONFIG_RCU_TRACE is not set
 +# CONFIG_RCU_EQS_DEBUG is not set
 +# end of RCU Debugging
 +
 +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
 +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
 +CONFIG_LATENCYTOP=y
 +CONFIG_NOP_TRACER=y
 +CONFIG_HAVE_FUNCTION_TRACER=y
 +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
 +CONFIG_HAVE_DYNAMIC_FTRACE=y
 +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
 +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
 +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
 +CONFIG_HAVE_FENTRY=y
 +CONFIG_HAVE_NOP_MCOUNT=y
 +CONFIG_TRACER_MAX_TRACE=y
 +CONFIG_TRACE_CLOCK=y
 +CONFIG_RING_BUFFER=y
 +CONFIG_EVENT_TRACING=y
 +CONFIG_CONTEXT_SWITCH_TRACER=y
 +CONFIG_RING_BUFFER_ALLOW_SWAP=y
 +CONFIG_PREEMPTIRQ_TRACEPOINTS=y
 +CONFIG_TRACING=y
 +CONFIG_GENERIC_TRACER=y
 +CONFIG_TRACING_SUPPORT=y
 +CONFIG_FTRACE=y
 +# CONFIG_BOOTTIME_TRACING is not set
 +CONFIG_FUNCTION_TRACER=y
 +CONFIG_FUNCTION_GRAPH_TRACER=y
 +CONFIG_DYNAMIC_FTRACE=y
 +CONFIG_DYNAMIC_FTRACE_WITH_REGS=y
 +CONFIG_FUNCTION_PROFILER=y
 +CONFIG_STACK_TRACER=y
 +# CONFIG_IRQSOFF_TRACER is not set
 +CONFIG_SCHED_TRACER=y
 +# CONFIG_HWLAT_TRACER is not set
 +# CONFIG_OSNOISE_TRACER is not set
 +# CONFIG_TIMERLAT_TRACER is not set
 +CONFIG_FTRACE_SYSCALLS=y
 +CONFIG_TRACER_SNAPSHOT=y
 +CONFIG_TRACER_SNAPSHOT_PER_CPU_SWAP=y
 +CONFIG_BRANCH_PROFILE_NONE=y
 +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
 +# CONFIG_PROFILE_ALL_BRANCHES is not set
 +CONFIG_BLK_DEV_IO_TRACE=y
 +CONFIG_KPROBE_EVENTS=y
 +# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set
 +CONFIG_UPROBE_EVENTS=y
 +CONFIG_BPF_EVENTS=y
 +CONFIG_DYNAMIC_EVENTS=y
 +CONFIG_PROBE_EVENTS=y
 +# CONFIG_BPF_KPROBE_OVERRIDE is not set
 +CONFIG_FTRACE_MCOUNT_RECORD=y
 +CONFIG_FTRACE_MCOUNT_USE_CC=y
 +# CONFIG_SYNTH_EVENTS is not set
 +# CONFIG_HIST_TRIGGERS is not set
 +# CONFIG_TRACE_EVENT_INJECT is not set
 +# CONFIG_TRACEPOINT_BENCHMARK is not set
 +# CONFIG_RING_BUFFER_BENCHMARK is not set
 +# CONFIG_TRACE_EVAL_MAP_FILE is not set
 +# CONFIG_FTRACE_RECORD_RECURSION is not set
 +# CONFIG_FTRACE_STARTUP_TEST is not set
 +# CONFIG_RING_BUFFER_STARTUP_TEST is not set
 +# CONFIG_RING_BUFFER_VALIDATE_TIME_DELTAS is not set
 +# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
 +# CONFIG_KPROBE_EVENT_GEN_TEST is not set
 +CONFIG_SAMPLES=y
 +# CONFIG_SAMPLE_AUXDISPLAY is not set
 +# CONFIG_SAMPLE_TRACE_EVENTS is not set
 +# CONFIG_SAMPLE_TRACE_PRINTK is not set
 +# CONFIG_SAMPLE_TRACE_ARRAY is not set
 +# CONFIG_SAMPLE_KOBJECT is not set
 +# CONFIG_SAMPLE_KPROBES is not set
 +# CONFIG_SAMPLE_KFIFO is not set
 +# CONFIG_SAMPLE_LIVEPATCH is not set
 +# CONFIG_SAMPLE_HIDRAW is not set
 +# CONFIG_SAMPLE_LANDLOCK is not set
 +# CONFIG_SAMPLE_PIDFD is not set
 +CONFIG_SAMPLE_SECCOMP=y
 +# CONFIG_SAMPLE_TIMER is not set
 +# CONFIG_SAMPLE_UHID is not set
 +# CONFIG_SAMPLE_ANDROID_BINDERFS is not set
 +# CONFIG_SAMPLE_VFS is not set
 +# CONFIG_SAMPLE_WATCHDOG is not set
 +# CONFIG_SAMPLE_WATCH_QUEUE is not set
 +CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
 +# CONFIG_STRICT_DEVMEM is not set
 +
 +#
 +# s390 Debugging
 +#
 +CONFIG_EARLY_PRINTK=y
 +# CONFIG_DEBUG_ENTRY is not set
 +# CONFIG_CIO_INJECT is not set
 +# end of s390 Debugging
 +
 +#
 +# Kernel Testing and Coverage
 +#
 +# CONFIG_KUNIT is not set
 +# CONFIG_NOTIFIER_ERROR_INJECTION is not set
 +CONFIG_FUNCTION_ERROR_INJECTION=y
 +# CONFIG_FAULT_INJECTION is not set
 +CONFIG_ARCH_HAS_KCOV=y
 +CONFIG_CC_HAS_SANCOV_TRACE_PC=y
 +# CONFIG_KCOV is not set
 +CONFIG_RUNTIME_TESTING_MENU=y
 +# CONFIG_LKDTM is not set
 +# CONFIG_TEST_MIN_HEAP is not set
 +# CONFIG_TEST_DIV64 is not set
 +# CONFIG_BACKTRACE_SELF_TEST is not set
 +# CONFIG_RBTREE_TEST is not set
 +# CONFIG_REED_SOLOMON_TEST is not set
 +# CONFIG_INTERVAL_TREE_TEST is not set
 +# CONFIG_PERCPU_TEST is not set
 +# CONFIG_ATOMIC64_SELFTEST is not set
 +# CONFIG_TEST_HEXDUMP is not set
 +# CONFIG_STRING_SELFTEST is not set
 +# CONFIG_TEST_STRING_HELPERS is not set
 +# CONFIG_TEST_STRSCPY is not set
 +# CONFIG_TEST_KSTRTOX is not set
 +# CONFIG_TEST_PRINTF is not set
 +# CONFIG_TEST_SCANF is not set
 +# CONFIG_TEST_BITMAP is not set
 +# CONFIG_TEST_UUID is not set
 +# CONFIG_TEST_XARRAY is not set
 +# CONFIG_TEST_OVERFLOW is not set
 +# CONFIG_TEST_RHASHTABLE is not set
 +# CONFIG_TEST_HASH is not set
 +# CONFIG_TEST_IDA is not set
 +# CONFIG_TEST_LKM is not set
 +# CONFIG_TEST_BITOPS is not set
 +# CONFIG_TEST_VMALLOC is not set
 +# CONFIG_TEST_USER_COPY is not set
 +CONFIG_TEST_BPF=m
 +# CONFIG_TEST_BLACKHOLE_DEV is not set
 +# CONFIG_FIND_BIT_BENCHMARK is not set
 +# CONFIG_TEST_FIRMWARE is not set
 +# CONFIG_TEST_SYSCTL is not set
 +# CONFIG_TEST_UDELAY is not set
 +# CONFIG_TEST_STATIC_KEYS is not set
 +# CONFIG_TEST_KMOD is not set
 +# CONFIG_TEST_MEMCAT_P is not set
 +# CONFIG_TEST_STACKINIT is not set
 +# CONFIG_TEST_MEMINIT is not set
 +# CONFIG_TEST_FREE_PAGES is not set
 +# end of Kernel Testing and Coverage
 +# end of Kernel hacking
index a577b17,0000000..d531f1a
mode 100644,000000..100644
--- /dev/null
@@@ -1,3061 -1,0 +1,3065 @@@
- # CONFIG_USERFAULTFD is not set
 +#
 +# Automatically generated file; DO NOT EDIT.
 +# Linux/x86 5.9.0-rc1 Kernel Configuration
 +#
 +CONFIG_CC_VERSION_TEXT="gcc (GCC) 8.2.1 20180801 (Red Hat 8.2.1-2)"
 +CONFIG_CC_IS_GCC=y
 +CONFIG_GCC_VERSION=80201
 +CONFIG_LD_VERSION=230000000
 +CONFIG_CLANG_VERSION=0
 +CONFIG_CC_CAN_LINK=y
 +CONFIG_CC_CAN_LINK_STATIC=y
 +CONFIG_CC_HAS_ASM_GOTO=y
 +CONFIG_IRQ_WORK=y
 +CONFIG_BUILDTIME_TABLE_SORT=y
 +CONFIG_THREAD_INFO_IN_TASK=y
 +
 +#
 +# General setup
 +#
 +CONFIG_INIT_ENV_ARG_LIMIT=32
 +# CONFIG_COMPILE_TEST is not set
 +CONFIG_LOCALVERSION=""
 +CONFIG_LOCALVERSION_AUTO=y
 +CONFIG_BUILD_SALT=""
 +CONFIG_HAVE_KERNEL_GZIP=y
 +CONFIG_HAVE_KERNEL_BZIP2=y
 +CONFIG_HAVE_KERNEL_LZMA=y
 +CONFIG_HAVE_KERNEL_XZ=y
 +CONFIG_HAVE_KERNEL_LZO=y
 +CONFIG_HAVE_KERNEL_LZ4=y
 +CONFIG_HAVE_KERNEL_ZSTD=y
 +CONFIG_KERNEL_GZIP=y
 +# CONFIG_KERNEL_BZIP2 is not set
 +# CONFIG_KERNEL_LZMA is not set
 +# CONFIG_KERNEL_XZ is not set
 +# CONFIG_KERNEL_LZO is not set
 +# CONFIG_KERNEL_LZ4 is not set
 +# CONFIG_KERNEL_ZSTD is not set
 +CONFIG_DEFAULT_INIT=""
 +CONFIG_DEFAULT_HOSTNAME="(none)"
 +CONFIG_SWAP=y
 +CONFIG_SYSVIPC=y
 +CONFIG_SYSVIPC_SYSCTL=y
 +CONFIG_POSIX_MQUEUE=y
 +CONFIG_POSIX_MQUEUE_SYSCTL=y
 +# CONFIG_WATCH_QUEUE is not set
 +CONFIG_CROSS_MEMORY_ATTACH=y
 +# CONFIG_USELIB is not set
 +CONFIG_AUDIT=y
 +CONFIG_HAVE_ARCH_AUDITSYSCALL=y
 +CONFIG_AUDITSYSCALL=y
 +
 +#
 +# IRQ subsystem
 +#
 +CONFIG_GENERIC_IRQ_PROBE=y
 +CONFIG_GENERIC_IRQ_SHOW=y
 +CONFIG_GENERIC_IRQ_EFFECTIVE_AFF_MASK=y
 +CONFIG_GENERIC_PENDING_IRQ=y
 +CONFIG_GENERIC_IRQ_MIGRATION=y
 +CONFIG_HARDIRQS_SW_RESEND=y
 +CONFIG_IRQ_DOMAIN=y
 +CONFIG_IRQ_DOMAIN_HIERARCHY=y
 +CONFIG_GENERIC_MSI_IRQ=y
 +CONFIG_GENERIC_MSI_IRQ_DOMAIN=y
 +CONFIG_GENERIC_IRQ_MATRIX_ALLOCATOR=y
 +CONFIG_GENERIC_IRQ_RESERVATION_MODE=y
 +CONFIG_IRQ_FORCED_THREADING=y
 +CONFIG_SPARSE_IRQ=y
 +# CONFIG_GENERIC_IRQ_DEBUGFS is not set
 +# end of IRQ subsystem
 +
 +CONFIG_CLOCKSOURCE_WATCHDOG=y
 +CONFIG_ARCH_CLOCKSOURCE_INIT=y
 +CONFIG_CLOCKSOURCE_VALIDATE_LAST_CYCLE=y
 +CONFIG_GENERIC_TIME_VSYSCALL=y
 +CONFIG_GENERIC_CLOCKEVENTS=y
 +CONFIG_GENERIC_CLOCKEVENTS_BROADCAST=y
 +CONFIG_GENERIC_CLOCKEVENTS_MIN_ADJUST=y
 +CONFIG_GENERIC_CMOS_UPDATE=y
 +CONFIG_HAVE_POSIX_CPU_TIMERS_TASK_WORK=y
 +CONFIG_POSIX_CPU_TIMERS_TASK_WORK=y
 +
 +#
 +# Timers subsystem
 +#
 +CONFIG_TICK_ONESHOT=y
 +CONFIG_NO_HZ_COMMON=y
 +# CONFIG_HZ_PERIODIC is not set
 +CONFIG_NO_HZ_IDLE=y
 +# CONFIG_NO_HZ_FULL is not set
 +CONFIG_NO_HZ=y
 +CONFIG_HIGH_RES_TIMERS=y
 +# end of Timers subsystem
 +
 +# CONFIG_PREEMPT_NONE is not set
 +# CONFIG_PREEMPT_VOLUNTARY is not set
 +CONFIG_PREEMPT=y
 +CONFIG_PREEMPT_COUNT=y
 +CONFIG_PREEMPTION=y
 +
 +#
 +# CPU/Task time and stats accounting
 +#
 +CONFIG_TICK_CPU_ACCOUNTING=y
 +# CONFIG_VIRT_CPU_ACCOUNTING_GEN is not set
 +# CONFIG_IRQ_TIME_ACCOUNTING is not set
 +CONFIG_BSD_PROCESS_ACCT=y
 +# CONFIG_BSD_PROCESS_ACCT_V3 is not set
 +CONFIG_TASKSTATS=y
 +CONFIG_TASK_DELAY_ACCT=y
 +CONFIG_TASK_XACCT=y
 +CONFIG_TASK_IO_ACCOUNTING=y
 +# CONFIG_PSI is not set
 +# end of CPU/Task time and stats accounting
 +
 +# CONFIG_CPU_ISOLATION is not set
 +
 +#
 +# RCU Subsystem
 +#
 +CONFIG_TREE_RCU=y
 +CONFIG_PREEMPT_RCU=y
 +# CONFIG_RCU_EXPERT is not set
 +CONFIG_SRCU=y
 +CONFIG_TREE_SRCU=y
 +CONFIG_TASKS_RCU_GENERIC=y
 +CONFIG_TASKS_RCU=y
 +CONFIG_TASKS_RUDE_RCU=y
 +CONFIG_RCU_STALL_COMMON=y
 +CONFIG_RCU_NEED_SEGCBLIST=y
 +# end of RCU Subsystem
 +
 +CONFIG_IKCONFIG=y
 +CONFIG_IKCONFIG_PROC=y
 +# CONFIG_IKHEADERS is not set
 +CONFIG_LOG_BUF_SHIFT=21
 +CONFIG_LOG_CPU_MAX_BUF_SHIFT=0
 +CONFIG_PRINTK_SAFE_LOG_BUF_SHIFT=13
 +CONFIG_HAVE_UNSTABLE_SCHED_CLOCK=y
 +
 +#
 +# Scheduler features
 +#
 +# CONFIG_UCLAMP_TASK is not set
 +# end of Scheduler features
 +
 +CONFIG_ARCH_SUPPORTS_NUMA_BALANCING=y
 +CONFIG_ARCH_WANT_BATCHED_UNMAP_TLB_FLUSH=y
 +CONFIG_CC_HAS_INT128=y
 +CONFIG_ARCH_SUPPORTS_INT128=y
 +CONFIG_NUMA_BALANCING=y
 +# CONFIG_NUMA_BALANCING_DEFAULT_ENABLED is not set
 +CONFIG_CGROUPS=y
 +CONFIG_PAGE_COUNTER=y
 +CONFIG_MEMCG=y
 +CONFIG_MEMCG_SWAP=y
 +CONFIG_MEMCG_KMEM=y
 +CONFIG_BLK_CGROUP=y
 +CONFIG_CGROUP_WRITEBACK=y
 +CONFIG_CGROUP_SCHED=y
 +CONFIG_FAIR_GROUP_SCHED=y
 +CONFIG_CFS_BANDWIDTH=y
 +# CONFIG_RT_GROUP_SCHED is not set
 +# CONFIG_CGROUP_PIDS is not set
 +# CONFIG_CGROUP_RDMA is not set
 +CONFIG_CGROUP_FREEZER=y
 +CONFIG_CGROUP_HUGETLB=y
 +CONFIG_CPUSETS=y
 +CONFIG_PROC_PID_CPUSET=y
 +CONFIG_CGROUP_DEVICE=y
 +CONFIG_CGROUP_CPUACCT=y
 +CONFIG_CGROUP_PERF=y
 +CONFIG_CGROUP_BPF=y
 +# CONFIG_CGROUP_DEBUG is not set
 +CONFIG_SOCK_CGROUP_DATA=y
 +CONFIG_NAMESPACES=y
 +CONFIG_UTS_NS=y
 +CONFIG_TIME_NS=y
 +CONFIG_IPC_NS=y
 +CONFIG_USER_NS=y
 +CONFIG_PID_NS=y
 +CONFIG_NET_NS=y
 +# CONFIG_CHECKPOINT_RESTORE is not set
 +# CONFIG_SCHED_AUTOGROUP is not set
 +# CONFIG_SYSFS_DEPRECATED is not set
 +CONFIG_RELAY=y
 +CONFIG_BLK_DEV_INITRD=y
 +CONFIG_INITRAMFS_SOURCE=""
 +CONFIG_RD_GZIP=y
 +CONFIG_RD_BZIP2=y
 +CONFIG_RD_LZMA=y
 +CONFIG_RD_XZ=y
 +CONFIG_RD_LZO=y
 +CONFIG_RD_LZ4=y
 +CONFIG_RD_ZSTD=y
 +CONFIG_BOOT_CONFIG=y
 +CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE=y
 +# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set
 +CONFIG_SYSCTL=y
 +CONFIG_SYSCTL_EXCEPTION_TRACE=y
 +CONFIG_HAVE_PCSPKR_PLATFORM=y
 +CONFIG_BPF=y
 +CONFIG_EXPERT=y
 +CONFIG_MULTIUSER=y
 +CONFIG_SGETMASK_SYSCALL=y
 +# CONFIG_SYSFS_SYSCALL is not set
 +CONFIG_FHANDLE=y
 +CONFIG_POSIX_TIMERS=y
 +CONFIG_PRINTK=y
 +CONFIG_PRINTK_NMI=y
 +CONFIG_BUG=y
 +CONFIG_ELF_CORE=y
 +CONFIG_PCSPKR_PLATFORM=y
 +CONFIG_BASE_FULL=y
 +CONFIG_FUTEX=y
 +CONFIG_FUTEX_PI=y
 +CONFIG_EPOLL=y
 +CONFIG_SIGNALFD=y
 +CONFIG_TIMERFD=y
 +CONFIG_EVENTFD=y
 +CONFIG_SHMEM=y
 +CONFIG_AIO=y
 +CONFIG_IO_URING=y
 +CONFIG_ADVISE_SYSCALLS=y
++CONFIG_HAVE_ARCH_USERFAULTFD_WP=y
++CONFIG_HAVE_ARCH_USERFAULTFD_MINOR=y
 +CONFIG_MEMBARRIER=y
 +CONFIG_KALLSYMS=y
 +CONFIG_KALLSYMS_ALL=y
 +CONFIG_KALLSYMS_ABSOLUTE_PERCPU=y
 +CONFIG_KALLSYMS_BASE_RELATIVE=y
 +CONFIG_BPF_LSM=y
 +CONFIG_BPF_SYSCALL=y
 +CONFIG_ARCH_WANT_DEFAULT_BPF_JIT=y
 +CONFIG_BPF_JIT_ALWAYS_ON=y
 +CONFIG_BPF_JIT_DEFAULT_ON=y
 +CONFIG_USERMODE_DRIVER=y
 +CONFIG_BPF_PRELOAD=y
 +CONFIG_BPF_PRELOAD_UMD=y
- # CONFIG_NF_CONNTRACK is not set
++CONFIG_USERFAULTFD=y
 +CONFIG_ARCH_HAS_MEMBARRIER_SYNC_CORE=y
 +CONFIG_RSEQ=y
 +# CONFIG_DEBUG_RSEQ is not set
 +# CONFIG_EMBEDDED is not set
 +CONFIG_HAVE_PERF_EVENTS=y
 +# CONFIG_PC104 is not set
 +
 +#
 +# Kernel Performance Events And Counters
 +#
 +CONFIG_PERF_EVENTS=y
 +# CONFIG_DEBUG_PERF_USE_VMALLOC is not set
 +# end of Kernel Performance Events And Counters
 +
 +CONFIG_VM_EVENT_COUNTERS=y
 +CONFIG_SLUB_DEBUG=y
 +# CONFIG_SLUB_MEMCG_SYSFS_ON is not set
 +CONFIG_COMPAT_BRK=y
 +# CONFIG_SLAB is not set
 +CONFIG_SLUB=y
 +# CONFIG_SLOB is not set
 +CONFIG_SLAB_MERGE_DEFAULT=y
 +# CONFIG_SLAB_FREELIST_RANDOM is not set
 +# CONFIG_SLAB_FREELIST_HARDENED is not set
 +# CONFIG_SHUFFLE_PAGE_ALLOCATOR is not set
 +CONFIG_SLUB_CPU_PARTIAL=y
 +CONFIG_PROFILING=y
 +CONFIG_TRACEPOINTS=y
 +# end of General setup
 +
 +CONFIG_64BIT=y
 +CONFIG_X86_64=y
 +CONFIG_X86=y
 +CONFIG_INSTRUCTION_DECODER=y
 +CONFIG_OUTPUT_FORMAT="elf64-x86-64"
 +CONFIG_LOCKDEP_SUPPORT=y
 +CONFIG_STACKTRACE_SUPPORT=y
 +CONFIG_MMU=y
 +CONFIG_ARCH_MMAP_RND_BITS_MIN=28
 +CONFIG_ARCH_MMAP_RND_BITS_MAX=32
 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MIN=8
 +CONFIG_ARCH_MMAP_RND_COMPAT_BITS_MAX=16
 +CONFIG_GENERIC_ISA_DMA=y
 +CONFIG_GENERIC_BUG=y
 +CONFIG_GENERIC_BUG_RELATIVE_POINTERS=y
 +CONFIG_ARCH_MAY_HAVE_PC_FDC=y
 +CONFIG_GENERIC_CALIBRATE_DELAY=y
 +CONFIG_ARCH_HAS_CPU_RELAX=y
 +CONFIG_ARCH_HAS_CACHE_LINE_SIZE=y
 +CONFIG_ARCH_HAS_FILTER_PGPROT=y
 +CONFIG_HAVE_SETUP_PER_CPU_AREA=y
 +CONFIG_NEED_PER_CPU_EMBED_FIRST_CHUNK=y
 +CONFIG_NEED_PER_CPU_PAGE_FIRST_CHUNK=y
 +CONFIG_ARCH_HIBERNATION_POSSIBLE=y
 +CONFIG_ARCH_SUSPEND_POSSIBLE=y
 +CONFIG_ARCH_WANT_GENERAL_HUGETLB=y
 +CONFIG_ZONE_DMA32=y
 +CONFIG_AUDIT_ARCH=y
 +CONFIG_ARCH_SUPPORTS_DEBUG_PAGEALLOC=y
 +CONFIG_X86_64_SMP=y
 +CONFIG_ARCH_SUPPORTS_UPROBES=y
 +CONFIG_FIX_EARLYCON_MEM=y
 +CONFIG_PGTABLE_LEVELS=4
 +CONFIG_CC_HAS_SANE_STACKPROTECTOR=y
 +
 +#
 +# Processor type and features
 +#
 +CONFIG_ZONE_DMA=y
 +CONFIG_SMP=y
 +CONFIG_X86_FEATURE_NAMES=y
 +CONFIG_X86_MPPARSE=y
 +# CONFIG_GOLDFISH is not set
 +# CONFIG_RETPOLINE is not set
 +# CONFIG_X86_CPU_RESCTRL is not set
 +CONFIG_X86_EXTENDED_PLATFORM=y
 +# CONFIG_X86_VSMP is not set
 +# CONFIG_X86_GOLDFISH is not set
 +# CONFIG_X86_INTEL_LPSS is not set
 +# CONFIG_X86_AMD_PLATFORM_DEVICE is not set
 +# CONFIG_IOSF_MBI is not set
 +CONFIG_X86_SUPPORTS_MEMORY_FAILURE=y
 +CONFIG_SCHED_OMIT_FRAME_POINTER=y
 +# CONFIG_HYPERVISOR_GUEST is not set
 +# CONFIG_MK8 is not set
 +# CONFIG_MPSC is not set
 +CONFIG_MCORE2=y
 +# CONFIG_MATOM is not set
 +# CONFIG_GENERIC_CPU is not set
 +CONFIG_X86_INTERNODE_CACHE_SHIFT=6
 +CONFIG_X86_L1_CACHE_SHIFT=6
 +CONFIG_X86_INTEL_USERCOPY=y
 +CONFIG_X86_USE_PPRO_CHECKSUM=y
 +CONFIG_X86_P6_NOP=y
 +CONFIG_X86_TSC=y
 +CONFIG_X86_CMPXCHG64=y
 +CONFIG_X86_CMOV=y
 +CONFIG_X86_MINIMUM_CPU_FAMILY=64
 +CONFIG_X86_DEBUGCTLMSR=y
 +CONFIG_IA32_FEAT_CTL=y
 +CONFIG_X86_VMX_FEATURE_NAMES=y
 +# CONFIG_PROCESSOR_SELECT is not set
 +CONFIG_CPU_SUP_INTEL=y
 +CONFIG_CPU_SUP_AMD=y
 +CONFIG_CPU_SUP_HYGON=y
 +CONFIG_CPU_SUP_CENTAUR=y
 +CONFIG_CPU_SUP_ZHAOXIN=y
 +CONFIG_HPET_TIMER=y
 +CONFIG_DMI=y
 +CONFIG_GART_IOMMU=y
 +# CONFIG_MAXSMP is not set
 +CONFIG_NR_CPUS_RANGE_BEGIN=2
 +CONFIG_NR_CPUS_RANGE_END=512
 +CONFIG_NR_CPUS_DEFAULT=64
 +CONFIG_NR_CPUS=128
 +CONFIG_SCHED_SMT=y
 +CONFIG_SCHED_MC=y
 +CONFIG_SCHED_MC_PRIO=y
 +CONFIG_X86_LOCAL_APIC=y
 +CONFIG_X86_IO_APIC=y
 +# CONFIG_X86_REROUTE_FOR_BROKEN_BOOT_IRQS is not set
 +CONFIG_X86_MCE=y
 +# CONFIG_X86_MCELOG_LEGACY is not set
 +CONFIG_X86_MCE_INTEL=y
 +CONFIG_X86_MCE_AMD=y
 +CONFIG_X86_MCE_THRESHOLD=y
 +# CONFIG_X86_MCE_INJECT is not set
 +CONFIG_X86_THERMAL_VECTOR=y
 +
 +#
 +# Performance monitoring
 +#
 +CONFIG_PERF_EVENTS_INTEL_UNCORE=y
 +# CONFIG_PERF_EVENTS_INTEL_RAPL is not set
 +# CONFIG_PERF_EVENTS_INTEL_CSTATE is not set
 +# CONFIG_PERF_EVENTS_AMD_POWER is not set
 +# end of Performance monitoring
 +
 +# CONFIG_X86_16BIT is not set
 +CONFIG_X86_VSYSCALL_EMULATION=y
 +CONFIG_X86_IOPL_IOPERM=y
 +# CONFIG_I8K is not set
 +# CONFIG_MICROCODE is not set
 +CONFIG_X86_MSR=y
 +CONFIG_X86_CPUID=y
 +# CONFIG_X86_5LEVEL is not set
 +CONFIG_X86_DIRECT_GBPAGES=y
 +# CONFIG_X86_CPA_STATISTICS is not set
 +# CONFIG_AMD_MEM_ENCRYPT is not set
 +CONFIG_NUMA=y
 +CONFIG_AMD_NUMA=y
 +CONFIG_X86_64_ACPI_NUMA=y
 +# CONFIG_NUMA_EMU is not set
 +CONFIG_NODES_SHIFT=6
 +CONFIG_ARCH_SPARSEMEM_ENABLE=y
 +CONFIG_ARCH_SPARSEMEM_DEFAULT=y
 +CONFIG_ARCH_SELECT_MEMORY_MODEL=y
 +CONFIG_ARCH_PROC_KCORE_TEXT=y
 +CONFIG_ILLEGAL_POINTER_VALUE=0xdead000000000000
 +# CONFIG_X86_PMEM_LEGACY is not set
 +# CONFIG_X86_CHECK_BIOS_CORRUPTION is not set
 +CONFIG_X86_RESERVE_LOW=64
 +CONFIG_MTRR=y
 +# CONFIG_MTRR_SANITIZER is not set
 +CONFIG_X86_PAT=y
 +CONFIG_ARCH_USES_PG_UNCACHED=y
 +CONFIG_ARCH_RANDOM=y
 +CONFIG_X86_SMAP=y
 +CONFIG_X86_UMIP=y
 +# CONFIG_X86_INTEL_MEMORY_PROTECTION_KEYS is not set
 +CONFIG_X86_INTEL_TSX_MODE_OFF=y
 +# CONFIG_X86_INTEL_TSX_MODE_ON is not set
 +# CONFIG_X86_INTEL_TSX_MODE_AUTO is not set
 +CONFIG_EFI=y
 +CONFIG_EFI_STUB=y
 +# CONFIG_EFI_MIXED is not set
 +CONFIG_SECCOMP=y
 +# CONFIG_HZ_100 is not set
 +# CONFIG_HZ_250 is not set
 +# CONFIG_HZ_300 is not set
 +CONFIG_HZ_1000=y
 +CONFIG_HZ=1000
 +CONFIG_SCHED_HRTICK=y
 +CONFIG_KEXEC=y
 +# CONFIG_KEXEC_FILE is not set
 +# CONFIG_CRASH_DUMP is not set
 +CONFIG_PHYSICAL_START=0x1000000
 +CONFIG_RELOCATABLE=y
 +# CONFIG_RANDOMIZE_BASE is not set
 +CONFIG_PHYSICAL_ALIGN=0x1000000
 +CONFIG_HOTPLUG_CPU=y
 +# CONFIG_BOOTPARAM_HOTPLUG_CPU0 is not set
 +# CONFIG_DEBUG_HOTPLUG_CPU0 is not set
 +# CONFIG_LEGACY_VSYSCALL_EMULATE is not set
 +# CONFIG_LEGACY_VSYSCALL_XONLY is not set
 +CONFIG_LEGACY_VSYSCALL_NONE=y
 +# CONFIG_CMDLINE_BOOL is not set
 +CONFIG_MODIFY_LDT_SYSCALL=y
 +CONFIG_HAVE_LIVEPATCH=y
 +# CONFIG_LIVEPATCH is not set
 +# end of Processor type and features
 +
 +CONFIG_ARCH_HAS_ADD_PAGES=y
 +CONFIG_ARCH_ENABLE_MEMORY_HOTPLUG=y
 +CONFIG_USE_PERCPU_NUMA_NODE_ID=y
 +CONFIG_ARCH_ENABLE_SPLIT_PMD_PTLOCK=y
 +CONFIG_ARCH_ENABLE_HUGEPAGE_MIGRATION=y
 +CONFIG_ARCH_ENABLE_THP_MIGRATION=y
 +
 +#
 +# Power management and ACPI options
 +#
 +# CONFIG_SUSPEND is not set
 +# CONFIG_HIBERNATION is not set
 +# CONFIG_PM is not set
 +# CONFIG_ENERGY_MODEL is not set
 +CONFIG_ARCH_SUPPORTS_ACPI=y
 +CONFIG_ACPI=y
 +CONFIG_ACPI_LEGACY_TABLES_LOOKUP=y
 +CONFIG_ARCH_MIGHT_HAVE_ACPI_PDC=y
 +CONFIG_ACPI_SYSTEM_POWER_STATES_SUPPORT=y
 +# CONFIG_ACPI_DEBUGGER is not set
 +# CONFIG_ACPI_SPCR_TABLE is not set
 +CONFIG_ACPI_LPIT=y
 +# CONFIG_ACPI_REV_OVERRIDE_POSSIBLE is not set
 +# CONFIG_ACPI_EC_DEBUGFS is not set
 +# CONFIG_ACPI_AC is not set
 +# CONFIG_ACPI_BATTERY is not set
 +# CONFIG_ACPI_BUTTON is not set
 +# CONFIG_ACPI_TINY_POWER_BUTTON is not set
 +# CONFIG_ACPI_VIDEO is not set
 +# CONFIG_ACPI_FAN is not set
 +# CONFIG_ACPI_DOCK is not set
 +CONFIG_ACPI_CPU_FREQ_PSS=y
 +CONFIG_ACPI_PROCESSOR_CSTATE=y
 +CONFIG_ACPI_PROCESSOR_IDLE=y
 +CONFIG_ACPI_CPPC_LIB=y
 +CONFIG_ACPI_PROCESSOR=y
 +CONFIG_ACPI_HOTPLUG_CPU=y
 +# CONFIG_ACPI_PROCESSOR_AGGREGATOR is not set
 +# CONFIG_ACPI_THERMAL is not set
 +CONFIG_ARCH_HAS_ACPI_TABLE_UPGRADE=y
 +# CONFIG_ACPI_TABLE_UPGRADE is not set
 +# CONFIG_ACPI_DEBUG is not set
 +# CONFIG_ACPI_PCI_SLOT is not set
 +CONFIG_ACPI_CONTAINER=y
 +CONFIG_ACPI_HOTPLUG_IOAPIC=y
 +# CONFIG_ACPI_SBS is not set
 +# CONFIG_ACPI_HED is not set
 +# CONFIG_ACPI_CUSTOM_METHOD is not set
 +# CONFIG_ACPI_BGRT is not set
 +# CONFIG_ACPI_REDUCED_HARDWARE_ONLY is not set
 +# CONFIG_ACPI_NFIT is not set
 +CONFIG_ACPI_NUMA=y
 +# CONFIG_ACPI_HMAT is not set
 +CONFIG_HAVE_ACPI_APEI=y
 +CONFIG_HAVE_ACPI_APEI_NMI=y
 +# CONFIG_ACPI_APEI is not set
 +# CONFIG_DPTF_POWER is not set
 +# CONFIG_PMIC_OPREGION is not set
 +# CONFIG_ACPI_CONFIGFS is not set
 +# CONFIG_X86_PM_TIMER is not set
 +# CONFIG_SFI is not set
 +
 +#
 +# CPU Frequency scaling
 +#
 +CONFIG_CPU_FREQ=y
 +CONFIG_CPU_FREQ_GOV_ATTR_SET=y
 +CONFIG_CPU_FREQ_GOV_COMMON=y
 +CONFIG_CPU_FREQ_STAT=y
 +CONFIG_CPU_FREQ_DEFAULT_GOV_PERFORMANCE=y
 +# CONFIG_CPU_FREQ_DEFAULT_GOV_POWERSAVE is not set
 +# CONFIG_CPU_FREQ_DEFAULT_GOV_USERSPACE is not set
 +# CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND is not set
 +# CONFIG_CPU_FREQ_DEFAULT_GOV_CONSERVATIVE is not set
 +# CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL is not set
 +CONFIG_CPU_FREQ_GOV_PERFORMANCE=y
 +# CONFIG_CPU_FREQ_GOV_POWERSAVE is not set
 +CONFIG_CPU_FREQ_GOV_USERSPACE=y
 +CONFIG_CPU_FREQ_GOV_ONDEMAND=y
 +CONFIG_CPU_FREQ_GOV_CONSERVATIVE=y
 +CONFIG_CPU_FREQ_GOV_SCHEDUTIL=y
 +
 +#
 +# CPU frequency scaling drivers
 +#
 +CONFIG_X86_INTEL_PSTATE=y
 +# CONFIG_X86_PCC_CPUFREQ is not set
 +CONFIG_X86_ACPI_CPUFREQ=y
 +CONFIG_X86_ACPI_CPUFREQ_CPB=y
 +CONFIG_X86_POWERNOW_K8=y
 +# CONFIG_X86_AMD_FREQ_SENSITIVITY is not set
 +# CONFIG_X86_SPEEDSTEP_CENTRINO is not set
 +# CONFIG_X86_P4_CLOCKMOD is not set
 +
 +#
 +# shared options
 +#
 +# end of CPU Frequency scaling
 +
 +#
 +# CPU Idle
 +#
 +CONFIG_CPU_IDLE=y
 +CONFIG_CPU_IDLE_GOV_LADDER=y
 +CONFIG_CPU_IDLE_GOV_MENU=y
 +# CONFIG_CPU_IDLE_GOV_TEO is not set
 +# end of CPU Idle
 +
 +# CONFIG_INTEL_IDLE is not set
 +# end of Power management and ACPI options
 +
 +#
 +# Bus options (PCI etc.)
 +#
 +CONFIG_PCI_DIRECT=y
 +CONFIG_PCI_MMCONFIG=y
 +CONFIG_MMCONF_FAM10H=y
 +# CONFIG_PCI_CNB20LE_QUIRK is not set
 +# CONFIG_ISA_BUS is not set
 +CONFIG_ISA_DMA_API=y
 +CONFIG_AMD_NB=y
 +# CONFIG_X86_SYSFB is not set
 +# end of Bus options (PCI etc.)
 +
 +#
 +# Binary Emulations
 +#
 +# CONFIG_IA32_EMULATION is not set
 +# CONFIG_X86_X32 is not set
 +# end of Binary Emulations
 +
 +#
 +# Firmware Drivers
 +#
 +# CONFIG_EDD is not set
 +CONFIG_FIRMWARE_MEMMAP=y
 +CONFIG_DMIID=y
 +# CONFIG_DMI_SYSFS is not set
 +CONFIG_DMI_SCAN_MACHINE_NON_EFI_FALLBACK=y
 +# CONFIG_FW_CFG_SYSFS is not set
 +# CONFIG_GOOGLE_FIRMWARE is not set
 +
 +#
 +# EFI (Extensible Firmware Interface) Support
 +#
 +# CONFIG_EFI_VARS is not set
 +CONFIG_EFI_ESRT=y
 +CONFIG_EFI_RUNTIME_MAP=y
 +# CONFIG_EFI_FAKE_MEMMAP is not set
 +CONFIG_EFI_RUNTIME_WRAPPERS=y
 +CONFIG_EFI_GENERIC_STUB_INITRD_CMDLINE_LOADER=y
 +# CONFIG_EFI_CAPSULE_LOADER is not set
 +# CONFIG_EFI_TEST is not set
 +# CONFIG_APPLE_PROPERTIES is not set
 +# CONFIG_RESET_ATTACK_MITIGATION is not set
 +# CONFIG_EFI_RCI2_TABLE is not set
 +# CONFIG_EFI_DISABLE_PCI_DMA is not set
 +# end of EFI (Extensible Firmware Interface) Support
 +
 +CONFIG_EFI_EARLYCON=y
 +
 +#
 +# Tegra firmware driver
 +#
 +# end of Tegra firmware driver
 +# end of Firmware Drivers
 +
 +CONFIG_HAVE_KVM=y
 +CONFIG_VIRTUALIZATION=y
 +# CONFIG_KVM is not set
 +CONFIG_KVM_WERROR=y
 +CONFIG_AS_AVX512=y
 +CONFIG_AS_SHA1_NI=y
 +CONFIG_AS_SHA256_NI=y
 +
 +#
 +# General architecture-dependent options
 +#
 +CONFIG_CRASH_CORE=y
 +CONFIG_KEXEC_CORE=y
 +CONFIG_HOTPLUG_SMT=y
 +CONFIG_GENERIC_ENTRY=y
 +# CONFIG_OPROFILE is not set
 +CONFIG_HAVE_OPROFILE=y
 +CONFIG_OPROFILE_NMI_TIMER=y
 +CONFIG_KPROBES=y
 +CONFIG_JUMP_LABEL=y
 +# CONFIG_STATIC_KEYS_SELFTEST is not set
 +CONFIG_OPTPROBES=y
 +CONFIG_KPROBES_ON_FTRACE=y
 +CONFIG_UPROBES=y
 +CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS=y
 +CONFIG_ARCH_USE_BUILTIN_BSWAP=y
 +CONFIG_KRETPROBES=y
 +CONFIG_HAVE_IOREMAP_PROT=y
 +CONFIG_HAVE_KPROBES=y
 +CONFIG_HAVE_KRETPROBES=y
 +CONFIG_HAVE_OPTPROBES=y
 +CONFIG_HAVE_KPROBES_ON_FTRACE=y
 +CONFIG_HAVE_FUNCTION_ERROR_INJECTION=y
 +CONFIG_HAVE_NMI=y
 +CONFIG_HAVE_ARCH_TRACEHOOK=y
 +CONFIG_HAVE_DMA_CONTIGUOUS=y
 +CONFIG_GENERIC_SMP_IDLE_THREAD=y
 +CONFIG_ARCH_HAS_FORTIFY_SOURCE=y
 +CONFIG_ARCH_HAS_SET_MEMORY=y
 +CONFIG_ARCH_HAS_SET_DIRECT_MAP=y
 +CONFIG_HAVE_ARCH_THREAD_STRUCT_WHITELIST=y
 +CONFIG_ARCH_WANTS_DYNAMIC_TASK_STRUCT=y
 +CONFIG_HAVE_ASM_MODVERSIONS=y
 +CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y
 +CONFIG_HAVE_RSEQ=y
 +CONFIG_HAVE_FUNCTION_ARG_ACCESS_API=y
 +CONFIG_HAVE_HW_BREAKPOINT=y
 +CONFIG_HAVE_MIXED_BREAKPOINTS_REGS=y
 +CONFIG_HAVE_USER_RETURN_NOTIFIER=y
 +CONFIG_HAVE_PERF_EVENTS_NMI=y
 +CONFIG_HAVE_HARDLOCKUP_DETECTOR_PERF=y
 +CONFIG_HAVE_PERF_REGS=y
 +CONFIG_HAVE_PERF_USER_STACK_DUMP=y
 +CONFIG_HAVE_ARCH_JUMP_LABEL=y
 +CONFIG_HAVE_ARCH_JUMP_LABEL_RELATIVE=y
 +CONFIG_ARCH_HAVE_NMI_SAFE_CMPXCHG=y
 +CONFIG_HAVE_ALIGNED_STRUCT_PAGE=y
 +CONFIG_HAVE_CMPXCHG_LOCAL=y
 +CONFIG_HAVE_CMPXCHG_DOUBLE=y
 +CONFIG_HAVE_ARCH_SECCOMP_FILTER=y
 +CONFIG_SECCOMP_FILTER=y
 +CONFIG_HAVE_ARCH_STACKLEAK=y
 +CONFIG_HAVE_STACKPROTECTOR=y
 +# CONFIG_STACKPROTECTOR is not set
 +CONFIG_HAVE_ARCH_WITHIN_STACK_FRAMES=y
 +CONFIG_HAVE_CONTEXT_TRACKING=y
 +CONFIG_HAVE_VIRT_CPU_ACCOUNTING_GEN=y
 +CONFIG_HAVE_IRQ_TIME_ACCOUNTING=y
 +CONFIG_HAVE_MOVE_PMD=y
 +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE=y
 +CONFIG_HAVE_ARCH_TRANSPARENT_HUGEPAGE_PUD=y
 +CONFIG_HAVE_ARCH_HUGE_VMAP=y
 +CONFIG_ARCH_WANT_HUGE_PMD_SHARE=y
 +CONFIG_HAVE_ARCH_SOFT_DIRTY=y
 +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y
 +CONFIG_MODULES_USE_ELF_RELA=y
 +CONFIG_ARCH_HAS_ELF_RANDOMIZE=y
 +CONFIG_HAVE_ARCH_MMAP_RND_BITS=y
 +CONFIG_HAVE_EXIT_THREAD=y
 +CONFIG_ARCH_MMAP_RND_BITS=28
 +CONFIG_HAVE_STACK_VALIDATION=y
 +CONFIG_HAVE_RELIABLE_STACKTRACE=y
 +CONFIG_COMPAT_32BIT_TIME=y
 +CONFIG_HAVE_ARCH_VMAP_STACK=y
 +CONFIG_VMAP_STACK=y
 +CONFIG_ARCH_HAS_STRICT_KERNEL_RWX=y
 +CONFIG_STRICT_KERNEL_RWX=y
 +CONFIG_ARCH_HAS_STRICT_MODULE_RWX=y
 +CONFIG_STRICT_MODULE_RWX=y
 +CONFIG_HAVE_ARCH_PREL32_RELOCATIONS=y
 +CONFIG_ARCH_USE_MEMREMAP_PROT=y
 +# CONFIG_LOCK_EVENT_COUNTS is not set
 +CONFIG_ARCH_HAS_MEM_ENCRYPT=y
 +
 +#
 +# GCOV-based kernel profiling
 +#
 +# CONFIG_GCOV_KERNEL is not set
 +CONFIG_ARCH_HAS_GCOV_PROFILE_ALL=y
 +# end of GCOV-based kernel profiling
 +
 +CONFIG_HAVE_GCC_PLUGINS=y
 +# end of General architecture-dependent options
 +
 +CONFIG_RT_MUTEXES=y
 +CONFIG_BASE_SMALL=0
 +CONFIG_MODULES=y
 +# CONFIG_MODULE_FORCE_LOAD is not set
 +CONFIG_MODULE_UNLOAD=y
 +# CONFIG_MODULE_FORCE_UNLOAD is not set
 +CONFIG_MODVERSIONS=y
 +CONFIG_ASM_MODVERSIONS=y
 +CONFIG_MODULE_SRCVERSION_ALL=y
 +# CONFIG_MODULE_SIG is not set
 +# CONFIG_MODULE_COMPRESS is not set
 +# CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS is not set
 +# CONFIG_UNUSED_SYMBOLS is not set
 +# CONFIG_TRIM_UNUSED_KSYMS is not set
 +CONFIG_MODULES_TREE_LOOKUP=y
 +CONFIG_BLOCK=y
 +CONFIG_BLK_SCSI_REQUEST=y
 +CONFIG_BLK_CGROUP_RWSTAT=y
 +CONFIG_BLK_DEV_BSG=y
 +CONFIG_BLK_DEV_BSGLIB=y
 +# CONFIG_BLK_DEV_INTEGRITY is not set
 +# CONFIG_BLK_DEV_ZONED is not set
 +CONFIG_BLK_DEV_THROTTLING=y
 +# CONFIG_BLK_DEV_THROTTLING_LOW is not set
 +# CONFIG_BLK_CMDLINE_PARSER is not set
 +# CONFIG_BLK_WBT is not set
 +CONFIG_BLK_CGROUP_IOLATENCY=y
 +# CONFIG_BLK_CGROUP_IOCOST is not set
 +CONFIG_BLK_DEBUG_FS=y
 +# CONFIG_BLK_SED_OPAL is not set
 +# CONFIG_BLK_INLINE_ENCRYPTION is not set
 +
 +#
 +# Partition Types
 +#
 +CONFIG_PARTITION_ADVANCED=y
 +# CONFIG_ACORN_PARTITION is not set
 +# CONFIG_AIX_PARTITION is not set
 +CONFIG_OSF_PARTITION=y
 +CONFIG_AMIGA_PARTITION=y
 +# CONFIG_ATARI_PARTITION is not set
 +CONFIG_MAC_PARTITION=y
 +CONFIG_MSDOS_PARTITION=y
 +CONFIG_BSD_DISKLABEL=y
 +CONFIG_MINIX_SUBPARTITION=y
 +CONFIG_SOLARIS_X86_PARTITION=y
 +CONFIG_UNIXWARE_DISKLABEL=y
 +# CONFIG_LDM_PARTITION is not set
 +CONFIG_SGI_PARTITION=y
 +# CONFIG_ULTRIX_PARTITION is not set
 +CONFIG_SUN_PARTITION=y
 +CONFIG_KARMA_PARTITION=y
 +CONFIG_EFI_PARTITION=y
 +# CONFIG_SYSV68_PARTITION is not set
 +# CONFIG_CMDLINE_PARTITION is not set
 +# end of Partition Types
 +
 +CONFIG_BLK_MQ_PCI=y
 +CONFIG_BLK_MQ_VIRTIO=y
 +
 +#
 +# IO Schedulers
 +#
 +CONFIG_MQ_IOSCHED_DEADLINE=y
 +CONFIG_MQ_IOSCHED_KYBER=y
 +# CONFIG_IOSCHED_BFQ is not set
 +# end of IO Schedulers
 +
 +CONFIG_ASN1=y
 +CONFIG_UNINLINE_SPIN_UNLOCK=y
 +CONFIG_ARCH_SUPPORTS_ATOMIC_RMW=y
 +CONFIG_MUTEX_SPIN_ON_OWNER=y
 +CONFIG_RWSEM_SPIN_ON_OWNER=y
 +CONFIG_LOCK_SPIN_ON_OWNER=y
 +CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
 +CONFIG_QUEUED_SPINLOCKS=y
 +CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
 +CONFIG_QUEUED_RWLOCKS=y
 +CONFIG_ARCH_HAS_NON_OVERLAPPING_ADDRESS_SPACE=y
 +CONFIG_ARCH_HAS_SYNC_CORE_BEFORE_USERMODE=y
 +CONFIG_ARCH_HAS_SYSCALL_WRAPPER=y
 +CONFIG_FREEZER=y
 +
 +#
 +# Executable file formats
 +#
 +CONFIG_BINFMT_ELF=y
 +CONFIG_ELFCORE=y
 +# CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set
 +CONFIG_BINFMT_SCRIPT=y
 +CONFIG_BINFMT_MISC=y
 +CONFIG_COREDUMP=y
 +# end of Executable file formats
 +
 +#
 +# Memory Management options
 +#
 +CONFIG_SELECT_MEMORY_MODEL=y
 +CONFIG_SPARSEMEM_MANUAL=y
 +CONFIG_SPARSEMEM=y
 +CONFIG_NEED_MULTIPLE_NODES=y
 +CONFIG_SPARSEMEM_EXTREME=y
 +CONFIG_SPARSEMEM_VMEMMAP_ENABLE=y
 +CONFIG_SPARSEMEM_VMEMMAP=y
 +CONFIG_HAVE_FAST_GUP=y
 +CONFIG_MEMORY_ISOLATION=y
 +# CONFIG_MEMORY_HOTPLUG is not set
 +CONFIG_SPLIT_PTLOCK_CPUS=4
 +CONFIG_MEMORY_BALLOON=y
 +CONFIG_BALLOON_COMPACTION=y
 +CONFIG_COMPACTION=y
 +CONFIG_PAGE_REPORTING=y
 +CONFIG_MIGRATION=y
 +CONFIG_CONTIG_ALLOC=y
 +CONFIG_PHYS_ADDR_T_64BIT=y
 +CONFIG_BOUNCE=y
 +CONFIG_VIRT_TO_BUS=y
 +CONFIG_KSM=y
 +CONFIG_DEFAULT_MMAP_MIN_ADDR=4096
 +CONFIG_ARCH_SUPPORTS_MEMORY_FAILURE=y
 +CONFIG_MEMORY_FAILURE=y
 +CONFIG_HWPOISON_INJECT=y
 +CONFIG_TRANSPARENT_HUGEPAGE=y
 +# CONFIG_TRANSPARENT_HUGEPAGE_ALWAYS is not set
 +CONFIG_TRANSPARENT_HUGEPAGE_MADVISE=y
 +CONFIG_ARCH_WANTS_THP_SWAP=y
 +CONFIG_THP_SWAP=y
 +# CONFIG_CLEANCACHE is not set
 +# CONFIG_FRONTSWAP is not set
 +CONFIG_CMA=y
 +# CONFIG_CMA_DEBUG is not set
 +# CONFIG_CMA_DEBUGFS is not set
 +CONFIG_CMA_AREAS=7
 +# CONFIG_ZPOOL is not set
 +# CONFIG_ZBUD is not set
 +# CONFIG_ZSMALLOC is not set
 +CONFIG_GENERIC_EARLY_IOREMAP=y
 +# CONFIG_DEFERRED_STRUCT_PAGE_INIT is not set
 +# CONFIG_IDLE_PAGE_TRACKING is not set
 +CONFIG_ARCH_HAS_PTE_DEVMAP=y
 +# CONFIG_PERCPU_STATS is not set
 +# CONFIG_GUP_BENCHMARK is not set
 +# CONFIG_READ_ONLY_THP_FOR_FS is not set
 +CONFIG_ARCH_HAS_PTE_SPECIAL=y
 +# end of Memory Management options
 +
 +CONFIG_NET=y
 +CONFIG_NET_INGRESS=y
 +CONFIG_NET_EGRESS=y
 +CONFIG_SKB_EXTENSIONS=y
 +
 +#
 +# Networking options
 +#
 +CONFIG_PACKET=y
 +# CONFIG_PACKET_DIAG is not set
 +CONFIG_UNIX=y
 +CONFIG_UNIX_SCM=y
 +# CONFIG_UNIX_DIAG is not set
 +CONFIG_TLS=y
 +# CONFIG_TLS_DEVICE is not set
 +# CONFIG_TLS_TOE is not set
 +CONFIG_XFRM=y
 +CONFIG_XFRM_ALGO=y
 +CONFIG_XFRM_USER=y
 +# CONFIG_XFRM_INTERFACE is not set
 +CONFIG_XFRM_SUB_POLICY=y
 +# CONFIG_XFRM_MIGRATE is not set
 +# CONFIG_XFRM_STATISTICS is not set
 +# CONFIG_NET_KEY is not set
 +CONFIG_XDP_SOCKETS=y
 +CONFIG_XDP_SOCKETS_DIAG=y
 +CONFIG_INET=y
 +CONFIG_IP_MULTICAST=y
 +CONFIG_IP_ADVANCED_ROUTER=y
 +# CONFIG_IP_FIB_TRIE_STATS is not set
 +CONFIG_IP_MULTIPLE_TABLES=y
 +CONFIG_IP_ROUTE_MULTIPATH=y
 +CONFIG_IP_ROUTE_VERBOSE=y
 +# CONFIG_IP_PNP is not set
 +CONFIG_NET_IPIP=y
 +CONFIG_NET_IPGRE_DEMUX=y
 +CONFIG_NET_IP_TUNNEL=y
 +CONFIG_NET_IPGRE=y
 +CONFIG_NET_IPGRE_BROADCAST=y
 +CONFIG_IP_MROUTE_COMMON=y
 +CONFIG_IP_MROUTE=y
 +# CONFIG_IP_MROUTE_MULTIPLE_TABLES is not set
 +CONFIG_IP_PIMSM_V1=y
 +CONFIG_IP_PIMSM_V2=y
 +CONFIG_SYN_COOKIES=y
 +# CONFIG_NET_IPVTI is not set
 +CONFIG_NET_UDP_TUNNEL=y
 +# CONFIG_NET_FOU is not set
 +# CONFIG_NET_FOU_IP_TUNNELS is not set
 +# CONFIG_INET_AH is not set
 +# CONFIG_INET_ESP is not set
 +# CONFIG_INET_IPCOMP is not set
 +CONFIG_INET_TUNNEL=y
 +CONFIG_INET_DIAG=y
 +CONFIG_INET_TCP_DIAG=y
 +# CONFIG_INET_UDP_DIAG is not set
 +# CONFIG_INET_RAW_DIAG is not set
 +# CONFIG_INET_DIAG_DESTROY is not set
 +CONFIG_TCP_CONG_ADVANCED=y
 +CONFIG_TCP_CONG_BIC=m
 +CONFIG_TCP_CONG_CUBIC=y
 +CONFIG_TCP_CONG_WESTWOOD=m
 +CONFIG_TCP_CONG_HTCP=m
 +# CONFIG_TCP_CONG_HSTCP is not set
 +# CONFIG_TCP_CONG_HYBLA is not set
 +# CONFIG_TCP_CONG_VEGAS is not set
 +# CONFIG_TCP_CONG_NV is not set
 +# CONFIG_TCP_CONG_SCALABLE is not set
 +# CONFIG_TCP_CONG_LP is not set
 +# CONFIG_TCP_CONG_VENO is not set
 +# CONFIG_TCP_CONG_YEAH is not set
 +# CONFIG_TCP_CONG_ILLINOIS is not set
 +# CONFIG_TCP_CONG_DCTCP is not set
 +# CONFIG_TCP_CONG_CDG is not set
 +# CONFIG_TCP_CONG_BBR is not set
 +# CONFIG_DEFAULT_CUBIC is not set
 +CONFIG_DEFAULT_RENO=y
 +CONFIG_DEFAULT_TCP_CONG="reno"
 +CONFIG_TCP_MD5SIG=y
 +CONFIG_IPV6=y
 +CONFIG_IPV6_ROUTER_PREF=y
 +CONFIG_IPV6_ROUTE_INFO=y
 +# CONFIG_IPV6_OPTIMISTIC_DAD is not set
 +# CONFIG_INET6_AH is not set
 +# CONFIG_INET6_ESP is not set
 +# CONFIG_INET6_IPCOMP is not set
 +CONFIG_IPV6_MIP6=y
 +# CONFIG_IPV6_ILA is not set
 +CONFIG_INET6_TUNNEL=y
 +# CONFIG_IPV6_VTI is not set
 +CONFIG_IPV6_SIT=y
 +# CONFIG_IPV6_SIT_6RD is not set
 +CONFIG_IPV6_NDISC_NODETYPE=y
 +CONFIG_IPV6_TUNNEL=y
 +CONFIG_IPV6_GRE=y
 +CONFIG_IPV6_MULTIPLE_TABLES=y
 +CONFIG_IPV6_SUBTREES=y
 +# CONFIG_IPV6_MROUTE is not set
 +CONFIG_IPV6_SEG6_LWTUNNEL=y
 +# CONFIG_IPV6_SEG6_HMAC is not set
 +CONFIG_IPV6_SEG6_BPF=y
 +# CONFIG_IPV6_RPL_LWTUNNEL is not set
 +CONFIG_NETLABEL=y
 +# CONFIG_MPTCP is not set
 +CONFIG_NETWORK_SECMARK=y
 +CONFIG_NET_PTP_CLASSIFY=y
 +# CONFIG_NETWORK_PHY_TIMESTAMPING is not set
 +CONFIG_NETFILTER=y
 +CONFIG_NETFILTER_ADVANCED=y
 +
 +#
 +# Core Netfilter Configuration
 +#
 +CONFIG_NETFILTER_INGRESS=y
 +CONFIG_NETFILTER_NETLINK=y
 +# CONFIG_NETFILTER_NETLINK_ACCT is not set
 +CONFIG_NETFILTER_NETLINK_QUEUE=y
 +CONFIG_NETFILTER_NETLINK_LOG=y
 +# CONFIG_NETFILTER_NETLINK_OSF is not set
++CONFIG_NF_CONNTRACK=y
 +# CONFIG_NF_LOG_NETDEV is not set
 +# CONFIG_NF_TABLES is not set
 +CONFIG_NETFILTER_XTABLES=y
 +
 +#
 +# Xtables combined modules
 +#
 +# CONFIG_NETFILTER_XT_MARK is not set
 +
 +#
 +# Xtables targets
 +#
 +# CONFIG_NETFILTER_XT_TARGET_AUDIT is not set
 +# CONFIG_NETFILTER_XT_TARGET_CLASSIFY is not set
 +# CONFIG_NETFILTER_XT_TARGET_HMARK is not set
 +# CONFIG_NETFILTER_XT_TARGET_IDLETIMER is not set
 +# CONFIG_NETFILTER_XT_TARGET_LOG is not set
 +# CONFIG_NETFILTER_XT_TARGET_MARK is not set
 +# CONFIG_NETFILTER_XT_TARGET_NFLOG is not set
 +# CONFIG_NETFILTER_XT_TARGET_NFQUEUE is not set
 +# CONFIG_NETFILTER_XT_TARGET_RATEEST is not set
 +# CONFIG_NETFILTER_XT_TARGET_TEE is not set
 +# CONFIG_NETFILTER_XT_TARGET_SECMARK is not set
 +# CONFIG_NETFILTER_XT_TARGET_TCPMSS is not set
 +
 +#
 +# Xtables matches
 +#
 +# CONFIG_NETFILTER_XT_MATCH_ADDRTYPE is not set
 +CONFIG_NETFILTER_XT_MATCH_BPF=y
 +# CONFIG_NETFILTER_XT_MATCH_CGROUP is not set
 +# CONFIG_NETFILTER_XT_MATCH_COMMENT is not set
 +# CONFIG_NETFILTER_XT_MATCH_CPU is not set
 +# CONFIG_NETFILTER_XT_MATCH_DCCP is not set
 +# CONFIG_NETFILTER_XT_MATCH_DEVGROUP is not set
 +# CONFIG_NETFILTER_XT_MATCH_DSCP is not set
 +# CONFIG_NETFILTER_XT_MATCH_ECN is not set
 +# CONFIG_NETFILTER_XT_MATCH_ESP is not set
 +# CONFIG_NETFILTER_XT_MATCH_HASHLIMIT is not set
 +# CONFIG_NETFILTER_XT_MATCH_HL is not set
 +# CONFIG_NETFILTER_XT_MATCH_IPCOMP is not set
 +# CONFIG_NETFILTER_XT_MATCH_IPRANGE is not set
 +# CONFIG_NETFILTER_XT_MATCH_L2TP is not set
 +# CONFIG_NETFILTER_XT_MATCH_LENGTH is not set
 +# CONFIG_NETFILTER_XT_MATCH_LIMIT is not set
 +# CONFIG_NETFILTER_XT_MATCH_MAC is not set
 +# CONFIG_NETFILTER_XT_MATCH_MARK is not set
 +# CONFIG_NETFILTER_XT_MATCH_MULTIPORT is not set
 +# CONFIG_NETFILTER_XT_MATCH_NFACCT is not set
 +# CONFIG_NETFILTER_XT_MATCH_OSF is not set
 +# CONFIG_NETFILTER_XT_MATCH_OWNER is not set
 +# CONFIG_NETFILTER_XT_MATCH_POLICY is not set
 +# CONFIG_NETFILTER_XT_MATCH_PKTTYPE is not set
 +# CONFIG_NETFILTER_XT_MATCH_QUOTA is not set
 +# CONFIG_NETFILTER_XT_MATCH_RATEEST is not set
 +# CONFIG_NETFILTER_XT_MATCH_REALM is not set
 +# CONFIG_NETFILTER_XT_MATCH_RECENT is not set
 +# CONFIG_NETFILTER_XT_MATCH_SCTP is not set
 +# CONFIG_NETFILTER_XT_MATCH_SOCKET is not set
 +CONFIG_NETFILTER_XT_MATCH_STATISTIC=y
 +# CONFIG_NETFILTER_XT_MATCH_STRING is not set
 +# CONFIG_NETFILTER_XT_MATCH_TCPMSS is not set
 +# CONFIG_NETFILTER_XT_MATCH_TIME is not set
 +# CONFIG_NETFILTER_XT_MATCH_U32 is not set
 +# end of Core Netfilter Configuration
 +
 +# CONFIG_IP_SET is not set
 +# CONFIG_IP_VS is not set
 +
 +#
 +# IP: Netfilter Configuration
 +#
++CONFIG_NF_DEFRAG_IPV4=y
 +# CONFIG_NF_SOCKET_IPV4 is not set
 +# CONFIG_NF_TPROXY_IPV4 is not set
 +# CONFIG_NF_DUP_IPV4 is not set
 +# CONFIG_NF_LOG_ARP is not set
 +# CONFIG_NF_LOG_IPV4 is not set
 +# CONFIG_NF_REJECT_IPV4 is not set
 +CONFIG_IP_NF_IPTABLES=y
 +# CONFIG_IP_NF_MATCH_AH is not set
 +# CONFIG_IP_NF_MATCH_ECN is not set
 +# CONFIG_IP_NF_MATCH_TTL is not set
 +# CONFIG_IP_NF_FILTER is not set
 +# CONFIG_IP_NF_MANGLE is not set
 +# CONFIG_IP_NF_RAW is not set
 +# CONFIG_IP_NF_SECURITY is not set
 +# CONFIG_IP_NF_ARPTABLES is not set
 +# end of IP: Netfilter Configuration
 +
 +#
 +# IPv6: Netfilter Configuration
 +#
 +# CONFIG_NF_SOCKET_IPV6 is not set
 +# CONFIG_NF_TPROXY_IPV6 is not set
 +# CONFIG_NF_DUP_IPV6 is not set
 +# CONFIG_NF_REJECT_IPV6 is not set
 +# CONFIG_NF_LOG_IPV6 is not set
 +CONFIG_IP6_NF_IPTABLES=y
 +# CONFIG_IP6_NF_MATCH_AH is not set
 +# CONFIG_IP6_NF_MATCH_EUI64 is not set
 +# CONFIG_IP6_NF_MATCH_FRAG is not set
 +# CONFIG_IP6_NF_MATCH_OPTS is not set
 +# CONFIG_IP6_NF_MATCH_HL is not set
 +# CONFIG_IP6_NF_MATCH_IPV6HEADER is not set
 +# CONFIG_IP6_NF_MATCH_MH is not set
 +# CONFIG_IP6_NF_MATCH_RT is not set
 +# CONFIG_IP6_NF_MATCH_SRH is not set
 +# CONFIG_IP6_NF_FILTER is not set
 +# CONFIG_IP6_NF_MANGLE is not set
 +# CONFIG_IP6_NF_RAW is not set
 +# CONFIG_IP6_NF_SECURITY is not set
 +# end of IPv6: Netfilter Configuration
 +
++CONFIG_NF_DEFRAG_IPV6=y
 +CONFIG_BPFILTER=y
 +CONFIG_BPFILTER_UMH=m
 +# CONFIG_IP_DCCP is not set
 +# CONFIG_IP_SCTP is not set
 +# CONFIG_RDS is not set
 +# CONFIG_TIPC is not set
 +# CONFIG_ATM is not set
 +# CONFIG_L2TP is not set
 +# CONFIG_BRIDGE is not set
 +CONFIG_HAVE_NET_DSA=y
 +# CONFIG_NET_DSA is not set
 +CONFIG_VLAN_8021Q=y
 +# CONFIG_VLAN_8021Q_GVRP is not set
 +# CONFIG_VLAN_8021Q_MVRP is not set
 +# CONFIG_DECNET is not set
 +# CONFIG_LLC2 is not set
 +# CONFIG_ATALK is not set
 +# CONFIG_X25 is not set
 +# CONFIG_LAPB is not set
 +# CONFIG_PHONET is not set
 +# CONFIG_6LOWPAN is not set
 +# CONFIG_IEEE802154 is not set
 +CONFIG_NET_SCHED=y
 +
 +#
 +# Queueing/Scheduling
 +#
 +# CONFIG_NET_SCH_CBQ is not set
 +# CONFIG_NET_SCH_HTB is not set
 +# CONFIG_NET_SCH_HFSC is not set
 +# CONFIG_NET_SCH_PRIO is not set
 +# CONFIG_NET_SCH_MULTIQ is not set
 +# CONFIG_NET_SCH_RED is not set
 +# CONFIG_NET_SCH_SFB is not set
 +# CONFIG_NET_SCH_SFQ is not set
 +# CONFIG_NET_SCH_TEQL is not set
 +# CONFIG_NET_SCH_TBF is not set
 +# CONFIG_NET_SCH_CBS is not set
 +# CONFIG_NET_SCH_ETF is not set
 +# CONFIG_NET_SCH_TAPRIO is not set
 +# CONFIG_NET_SCH_GRED is not set
 +# CONFIG_NET_SCH_DSMARK is not set
 +# CONFIG_NET_SCH_NETEM is not set
 +# CONFIG_NET_SCH_DRR is not set
 +# CONFIG_NET_SCH_MQPRIO is not set
 +# CONFIG_NET_SCH_SKBPRIO is not set
 +# CONFIG_NET_SCH_CHOKE is not set
 +# CONFIG_NET_SCH_QFQ is not set
 +# CONFIG_NET_SCH_CODEL is not set
 +CONFIG_NET_SCH_FQ_CODEL=y
 +# CONFIG_NET_SCH_CAKE is not set
 +# CONFIG_NET_SCH_FQ is not set
 +# CONFIG_NET_SCH_HHF is not set
 +# CONFIG_NET_SCH_PIE is not set
 +CONFIG_NET_SCH_INGRESS=y
 +# CONFIG_NET_SCH_PLUG is not set
 +# CONFIG_NET_SCH_ETS is not set
 +CONFIG_NET_SCH_DEFAULT=y
 +CONFIG_DEFAULT_FQ_CODEL=y
 +# CONFIG_DEFAULT_PFIFO_FAST is not set
 +CONFIG_DEFAULT_NET_SCH="fq_codel"
 +
 +#
 +# Classification
 +#
 +CONFIG_NET_CLS=y
 +# CONFIG_NET_CLS_BASIC is not set
 +# CONFIG_NET_CLS_TCINDEX is not set
 +# CONFIG_NET_CLS_ROUTE4 is not set
 +# CONFIG_NET_CLS_FW is not set
 +# CONFIG_NET_CLS_U32 is not set
 +# CONFIG_NET_CLS_RSVP is not set
 +# CONFIG_NET_CLS_RSVP6 is not set
 +# CONFIG_NET_CLS_FLOW is not set
 +CONFIG_NET_CLS_CGROUP=y
 +CONFIG_NET_CLS_BPF=y
 +# CONFIG_NET_CLS_FLOWER is not set
 +# CONFIG_NET_CLS_MATCHALL is not set
 +CONFIG_NET_EMATCH=y
 +CONFIG_NET_EMATCH_STACK=32
 +# CONFIG_NET_EMATCH_CMP is not set
 +# CONFIG_NET_EMATCH_NBYTE is not set
 +# CONFIG_NET_EMATCH_U32 is not set
 +# CONFIG_NET_EMATCH_META is not set
 +# CONFIG_NET_EMATCH_TEXT is not set
 +# CONFIG_NET_EMATCH_IPT is not set
 +CONFIG_NET_CLS_ACT=y
 +# CONFIG_NET_ACT_POLICE is not set
 +# CONFIG_NET_ACT_GACT is not set
 +# CONFIG_NET_ACT_MIRRED is not set
 +# CONFIG_NET_ACT_SAMPLE is not set
 +# CONFIG_NET_ACT_IPT is not set
 +# CONFIG_NET_ACT_NAT is not set
 +# CONFIG_NET_ACT_PEDIT is not set
 +# CONFIG_NET_ACT_SIMP is not set
 +# CONFIG_NET_ACT_SKBEDIT is not set
 +# CONFIG_NET_ACT_CSUM is not set
 +# CONFIG_NET_ACT_MPLS is not set
 +# CONFIG_NET_ACT_VLAN is not set
 +CONFIG_NET_ACT_BPF=y
 +# CONFIG_NET_ACT_SKBMOD is not set
 +# CONFIG_NET_ACT_IFE is not set
 +# CONFIG_NET_ACT_TUNNEL_KEY is not set
 +# CONFIG_NET_ACT_GATE is not set
 +CONFIG_NET_TC_SKB_EXT=y
 +CONFIG_NET_SCH_FIFO=y
 +CONFIG_DCB=y
 +CONFIG_DNS_RESOLVER=y
 +# CONFIG_BATMAN_ADV is not set
 +# CONFIG_OPENVSWITCH is not set
 +# CONFIG_VSOCKETS is not set
 +# CONFIG_NETLINK_DIAG is not set
 +CONFIG_MPLS=y
 +# CONFIG_NET_MPLS_GSO is not set
 +# CONFIG_MPLS_ROUTING is not set
 +# CONFIG_NET_NSH is not set
 +# CONFIG_HSR is not set
 +# CONFIG_NET_SWITCHDEV is not set
 +# CONFIG_NET_L3_MASTER_DEV is not set
 +# CONFIG_QRTR is not set
 +# CONFIG_NET_NCSI is not set
 +CONFIG_RPS=y
 +CONFIG_RFS_ACCEL=y
 +CONFIG_XPS=y
 +# CONFIG_CGROUP_NET_PRIO is not set
 +CONFIG_CGROUP_NET_CLASSID=y
 +CONFIG_NET_RX_BUSY_POLL=y
 +CONFIG_BQL=y
 +CONFIG_BPF_JIT=y
 +CONFIG_BPF_STREAM_PARSER=y
 +CONFIG_NET_FLOW_LIMIT=y
 +
 +#
 +# Network testing
 +#
 +# CONFIG_NET_PKTGEN is not set
 +# CONFIG_NET_DROP_MONITOR is not set
 +# end of Network testing
 +# end of Networking options
 +
 +# CONFIG_HAMRADIO is not set
 +# CONFIG_CAN is not set
 +# CONFIG_BT is not set
 +# CONFIG_AF_RXRPC is not set
 +# CONFIG_AF_KCM is not set
 +CONFIG_STREAM_PARSER=y
 +CONFIG_FIB_RULES=y
 +CONFIG_WIRELESS=y
 +# CONFIG_CFG80211 is not set
 +
 +#
 +# CFG80211 needs to be enabled for MAC80211
 +#
 +CONFIG_MAC80211_STA_HASH_MAX_SIZE=0
 +# CONFIG_WIMAX is not set
 +# CONFIG_RFKILL is not set
 +CONFIG_NET_9P=y
 +CONFIG_NET_9P_VIRTIO=y
 +# CONFIG_NET_9P_DEBUG is not set
 +# CONFIG_CAIF is not set
 +# CONFIG_CEPH_LIB is not set
 +# CONFIG_NFC is not set
 +# CONFIG_PSAMPLE is not set
 +# CONFIG_NET_IFE is not set
 +CONFIG_LWTUNNEL=y
 +CONFIG_LWTUNNEL_BPF=y
 +CONFIG_DST_CACHE=y
 +CONFIG_GRO_CELLS=y
 +CONFIG_NET_SOCK_MSG=y
 +CONFIG_NET_DEVLINK=y
 +CONFIG_FAILOVER=y
 +CONFIG_ETHTOOL_NETLINK=y
 +CONFIG_HAVE_EBPF_JIT=y
 +
 +#
 +# Device Drivers
 +#
 +CONFIG_HAVE_EISA=y
 +# CONFIG_EISA is not set
 +CONFIG_HAVE_PCI=y
 +CONFIG_PCI=y
 +CONFIG_PCI_DOMAINS=y
 +CONFIG_PCIEPORTBUS=y
 +# CONFIG_PCIEAER is not set
 +CONFIG_PCIEASPM=y
 +CONFIG_PCIEASPM_DEFAULT=y
 +# CONFIG_PCIEASPM_POWERSAVE is not set
 +# CONFIG_PCIEASPM_POWER_SUPERSAVE is not set
 +# CONFIG_PCIEASPM_PERFORMANCE is not set
 +# CONFIG_PCIE_PTM is not set
 +# CONFIG_PCIE_BW is not set
 +CONFIG_PCI_MSI=y
 +CONFIG_PCI_MSI_IRQ_DOMAIN=y
 +CONFIG_PCI_QUIRKS=y
 +# CONFIG_PCI_DEBUG is not set
 +# CONFIG_PCI_REALLOC_ENABLE_AUTO is not set
 +# CONFIG_PCI_STUB is not set
 +# CONFIG_PCI_PF_STUB is not set
 +CONFIG_PCI_ATS=y
 +CONFIG_PCI_LOCKLESS_CONFIG=y
 +CONFIG_PCI_IOV=y
 +# CONFIG_PCI_PRI is not set
 +# CONFIG_PCI_PASID is not set
 +CONFIG_PCI_LABEL=y
 +# CONFIG_HOTPLUG_PCI is not set
 +
 +#
 +# PCI controller drivers
 +#
 +# CONFIG_VMD is not set
 +
 +#
 +# DesignWare PCI Core Support
 +#
 +# CONFIG_PCIE_DW_PLAT_HOST is not set
 +# CONFIG_PCI_MESON is not set
 +# end of DesignWare PCI Core Support
 +
 +#
 +# Mobiveil PCIe Core Support
 +#
 +# end of Mobiveil PCIe Core Support
 +
 +#
 +# Cadence PCIe controllers support
 +#
 +# end of Cadence PCIe controllers support
 +# end of PCI controller drivers
 +
 +#
 +# PCI Endpoint
 +#
 +# CONFIG_PCI_ENDPOINT is not set
 +# end of PCI Endpoint
 +
 +#
 +# PCI switch controller drivers
 +#
 +# CONFIG_PCI_SW_SWITCHTEC is not set
 +# end of PCI switch controller drivers
 +
 +# CONFIG_PCCARD is not set
 +# CONFIG_RAPIDIO is not set
 +
 +#
 +# Generic Driver Options
 +#
 +# CONFIG_UEVENT_HELPER is not set
 +CONFIG_DEVTMPFS=y
 +CONFIG_DEVTMPFS_MOUNT=y
 +CONFIG_STANDALONE=y
 +# CONFIG_PREVENT_FIRMWARE_BUILD is not set
 +
 +#
 +# Firmware loader
 +#
 +CONFIG_FW_LOADER=y
 +CONFIG_FW_LOADER_PAGED_BUF=y
 +CONFIG_EXTRA_FIRMWARE=""
 +CONFIG_FW_LOADER_USER_HELPER=y
 +# CONFIG_FW_LOADER_USER_HELPER_FALLBACK is not set
 +# CONFIG_FW_LOADER_COMPRESS is not set
 +# end of Firmware loader
 +
 +CONFIG_ALLOW_DEV_COREDUMP=y
 +# CONFIG_DEBUG_DRIVER is not set
 +# CONFIG_DEBUG_DEVRES is not set
 +# CONFIG_DEBUG_TEST_DRIVER_REMOVE is not set
 +# CONFIG_TEST_ASYNC_DRIVER_PROBE is not set
 +CONFIG_GENERIC_CPU_AUTOPROBE=y
 +CONFIG_GENERIC_CPU_VULNERABILITIES=y
 +CONFIG_DMA_SHARED_BUFFER=y
 +# CONFIG_DMA_FENCE_TRACE is not set
 +# end of Generic Driver Options
 +
 +#
 +# Bus devices
 +#
 +# CONFIG_MHI_BUS is not set
 +# end of Bus devices
 +
 +# CONFIG_CONNECTOR is not set
 +# CONFIG_GNSS is not set
 +# CONFIG_MTD is not set
 +# CONFIG_OF is not set
 +CONFIG_ARCH_MIGHT_HAVE_PC_PARPORT=y
 +# CONFIG_PARPORT is not set
 +CONFIG_PNP=y
 +# CONFIG_PNP_DEBUG_MESSAGES is not set
 +
 +#
 +# Protocols
 +#
 +CONFIG_PNPACPI=y
 +CONFIG_BLK_DEV=y
 +# CONFIG_BLK_DEV_NULL_BLK is not set
 +# CONFIG_BLK_DEV_FD is not set
 +# CONFIG_BLK_DEV_PCIESSD_MTIP32XX is not set
 +# CONFIG_BLK_DEV_UMEM is not set
 +CONFIG_BLK_DEV_LOOP=y
 +# CONFIG_BLK_DEV_DRBD is not set
 +# CONFIG_BLK_DEV_NBD is not set
 +# CONFIG_BLK_DEV_SKD is not set
 +# CONFIG_BLK_DEV_SX8 is not set
 +CONFIG_BLK_DEV_RAM=y
 +CONFIG_BLK_DEV_RAM_COUNT=16
 +CONFIG_BLK_DEV_RAM_SIZE=16384
 +# CONFIG_CDROM_PKTCDVD is not set
 +# CONFIG_ATA_OVER_ETH is not set
 +CONFIG_VIRTIO_BLK=y
 +# CONFIG_BLK_DEV_RBD is not set
 +# CONFIG_BLK_DEV_RSXX is not set
 +
 +#
 +# NVME Support
 +#
 +# CONFIG_BLK_DEV_NVME is not set
 +# CONFIG_NVME_FC is not set
 +# end of NVME Support
 +
 +#
 +# Misc devices
 +#
 +# CONFIG_DUMMY_IRQ is not set
 +# CONFIG_IBM_ASM is not set
 +# CONFIG_PHANTOM is not set
 +# CONFIG_TIFM_CORE is not set
 +# CONFIG_ENCLOSURE_SERVICES is not set
 +# CONFIG_HP_ILO is not set
 +# CONFIG_SRAM is not set
 +# CONFIG_PCI_ENDPOINT_TEST is not set
 +# CONFIG_XILINX_SDFEC is not set
 +# CONFIG_PVPANIC is not set
 +# CONFIG_C2PORT is not set
 +
 +#
 +# EEPROM support
 +#
 +# CONFIG_EEPROM_93CX6 is not set
 +# end of EEPROM support
 +
 +# CONFIG_CB710_CORE is not set
 +
 +#
 +# Texas Instruments shared transport line discipline
 +#
 +# end of Texas Instruments shared transport line discipline
 +
 +#
 +# Altera FPGA firmware download module (requires I2C)
 +#
 +# CONFIG_INTEL_MEI is not set
 +# CONFIG_INTEL_MEI_ME is not set
 +# CONFIG_INTEL_MEI_TXE is not set
 +# CONFIG_VMWARE_VMCI is not set
 +
 +#
 +# Intel MIC & related support
 +#
 +# CONFIG_INTEL_MIC_BUS is not set
 +# CONFIG_SCIF_BUS is not set
 +# CONFIG_VOP_BUS is not set
 +# end of Intel MIC & related support
 +
 +# CONFIG_GENWQE is not set
 +# CONFIG_ECHO is not set
 +# CONFIG_MISC_ALCOR_PCI is not set
 +# CONFIG_MISC_RTSX_PCI is not set
 +# CONFIG_HABANA_AI is not set
 +# end of Misc devices
 +
 +CONFIG_HAVE_IDE=y
 +# CONFIG_IDE is not set
 +
 +#
 +# SCSI device support
 +#
 +CONFIG_SCSI_MOD=y
 +# CONFIG_RAID_ATTRS is not set
 +# CONFIG_SCSI is not set
 +# end of SCSI device support
 +
 +# CONFIG_ATA is not set
 +# CONFIG_MD is not set
 +# CONFIG_TARGET_CORE is not set
 +# CONFIG_FUSION is not set
 +
 +#
 +# IEEE 1394 (FireWire) support
 +#
 +# CONFIG_FIREWIRE is not set
 +# CONFIG_FIREWIRE_NOSY is not set
 +# end of IEEE 1394 (FireWire) support
 +
 +# CONFIG_MACINTOSH_DRIVERS is not set
 +CONFIG_NETDEVICES=y
 +CONFIG_NET_CORE=y
 +CONFIG_BONDING=y
 +# CONFIG_DUMMY is not set
 +# CONFIG_WIREGUARD is not set
 +# CONFIG_EQUALIZER is not set
 +# CONFIG_IFB is not set
 +# CONFIG_NET_TEAM is not set
 +# CONFIG_MACVLAN is not set
 +# CONFIG_IPVLAN is not set
 +CONFIG_VXLAN=y
 +# CONFIG_GENEVE is not set
 +# CONFIG_BAREUDP is not set
 +# CONFIG_GTP is not set
 +# CONFIG_MACSEC is not set
 +# CONFIG_NETCONSOLE is not set
 +CONFIG_TUN=y
 +# CONFIG_TUN_VNET_CROSS_LE is not set
 +CONFIG_VETH=y
 +CONFIG_VIRTIO_NET=y
 +# CONFIG_NLMON is not set
 +# CONFIG_ARCNET is not set
 +
 +#
 +# Distributed Switch Architecture drivers
 +#
 +# end of Distributed Switch Architecture drivers
 +
 +# CONFIG_ETHERNET is not set
 +# CONFIG_FDDI is not set
 +# CONFIG_HIPPI is not set
 +# CONFIG_NET_SB1000 is not set
 +# CONFIG_MDIO_DEVICE is not set
 +# CONFIG_PHYLIB is not set
 +# CONFIG_PPP is not set
 +# CONFIG_SLIP is not set
 +
 +#
 +# Host-side USB support is needed for USB Network Adapter support
 +#
 +# CONFIG_WLAN is not set
 +
 +#
 +# Enable WiMAX (Networking options) to see the WiMAX drivers
 +#
 +# CONFIG_WAN is not set
 +# CONFIG_VMXNET3 is not set
 +# CONFIG_FUJITSU_ES is not set
 +CONFIG_NETDEVSIM=y
 +CONFIG_NET_FAILOVER=y
 +# CONFIG_ISDN is not set
 +# CONFIG_NVM is not set
 +
 +#
 +# Input device support
 +#
 +CONFIG_INPUT=y
 +CONFIG_INPUT_FF_MEMLESS=y
 +# CONFIG_INPUT_POLLDEV is not set
 +# CONFIG_INPUT_SPARSEKMAP is not set
 +# CONFIG_INPUT_MATRIXKMAP is not set
 +
 +#
 +# Userland interfaces
 +#
 +# CONFIG_INPUT_MOUSEDEV is not set
 +# CONFIG_INPUT_JOYDEV is not set
 +CONFIG_INPUT_EVDEV=y
 +# CONFIG_INPUT_EVBUG is not set
 +
 +#
 +# Input Device Drivers
 +#
 +# CONFIG_INPUT_KEYBOARD is not set
 +# CONFIG_INPUT_MOUSE is not set
 +# CONFIG_INPUT_JOYSTICK is not set
 +# CONFIG_INPUT_TABLET is not set
 +# CONFIG_INPUT_TOUCHSCREEN is not set
 +# CONFIG_INPUT_MISC is not set
 +# CONFIG_RMI4_CORE is not set
 +
 +#
 +# Hardware I/O ports
 +#
 +CONFIG_SERIO=y
 +CONFIG_ARCH_MIGHT_HAVE_PC_SERIO=y
 +CONFIG_SERIO_I8042=y
 +CONFIG_SERIO_SERPORT=y
 +# CONFIG_SERIO_CT82C710 is not set
 +# CONFIG_SERIO_PCIPS2 is not set
 +CONFIG_SERIO_LIBPS2=y
 +# CONFIG_SERIO_RAW is not set
 +# CONFIG_SERIO_ALTERA_PS2 is not set
 +# CONFIG_SERIO_PS2MULT is not set
 +# CONFIG_SERIO_ARC_PS2 is not set
 +# CONFIG_USERIO is not set
 +# CONFIG_GAMEPORT is not set
 +# end of Hardware I/O ports
 +# end of Input device support
 +
 +#
 +# Character devices
 +#
 +CONFIG_TTY=y
 +CONFIG_VT=y
 +CONFIG_CONSOLE_TRANSLATIONS=y
 +CONFIG_VT_CONSOLE=y
 +CONFIG_HW_CONSOLE=y
 +CONFIG_VT_HW_CONSOLE_BINDING=y
 +CONFIG_UNIX98_PTYS=y
 +# CONFIG_LEGACY_PTYS is not set
 +CONFIG_LDISC_AUTOLOAD=y
 +
 +#
 +# Serial drivers
 +#
 +CONFIG_SERIAL_EARLYCON=y
 +CONFIG_SERIAL_8250=y
 +CONFIG_SERIAL_8250_DEPRECATED_OPTIONS=y
 +CONFIG_SERIAL_8250_PNP=y
 +# CONFIG_SERIAL_8250_16550A_VARIANTS is not set
 +# CONFIG_SERIAL_8250_FINTEK is not set
 +CONFIG_SERIAL_8250_CONSOLE=y
 +CONFIG_SERIAL_8250_PCI=y
 +CONFIG_SERIAL_8250_EXAR=y
 +CONFIG_SERIAL_8250_NR_UARTS=32
 +CONFIG_SERIAL_8250_RUNTIME_UARTS=4
 +CONFIG_SERIAL_8250_EXTENDED=y
 +CONFIG_SERIAL_8250_MANY_PORTS=y
 +CONFIG_SERIAL_8250_SHARE_IRQ=y
 +CONFIG_SERIAL_8250_DETECT_IRQ=y
 +CONFIG_SERIAL_8250_RSA=y
 +# CONFIG_SERIAL_8250_DW is not set
 +# CONFIG_SERIAL_8250_RT288X is not set
 +# CONFIG_SERIAL_8250_LPSS is not set
 +# CONFIG_SERIAL_8250_MID is not set
 +
 +#
 +# Non-8250 serial port support
 +#
 +# CONFIG_SERIAL_UARTLITE is not set
 +CONFIG_SERIAL_CORE=y
 +CONFIG_SERIAL_CORE_CONSOLE=y
 +# CONFIG_SERIAL_JSM is not set
 +# CONFIG_SERIAL_LANTIQ is not set
 +# CONFIG_SERIAL_SCCNXP is not set
 +# CONFIG_SERIAL_ALTERA_JTAGUART is not set
 +# CONFIG_SERIAL_ALTERA_UART is not set
 +# CONFIG_SERIAL_ARC is not set
 +# CONFIG_SERIAL_RP2 is not set
 +# CONFIG_SERIAL_FSL_LPUART is not set
 +# CONFIG_SERIAL_FSL_LINFLEXUART is not set
 +# CONFIG_SERIAL_SPRD is not set
 +# end of Serial drivers
 +
 +CONFIG_SERIAL_NONSTANDARD=y
 +# CONFIG_ROCKETPORT is not set
 +# CONFIG_CYCLADES is not set
 +# CONFIG_MOXA_INTELLIO is not set
 +# CONFIG_MOXA_SMARTIO is not set
 +# CONFIG_SYNCLINK is not set
 +# CONFIG_SYNCLINKMP is not set
 +# CONFIG_SYNCLINK_GT is not set
 +# CONFIG_ISI is not set
 +# CONFIG_N_HDLC is not set
 +# CONFIG_N_GSM is not set
 +# CONFIG_NOZOMI is not set
 +# CONFIG_NULL_TTY is not set
 +# CONFIG_TRACE_SINK is not set
 +CONFIG_HVC_DRIVER=y
 +# CONFIG_SERIAL_DEV_BUS is not set
 +# CONFIG_TTY_PRINTK is not set
 +CONFIG_VIRTIO_CONSOLE=y
 +# CONFIG_IPMI_HANDLER is not set
 +# CONFIG_HW_RANDOM is not set
 +# CONFIG_APPLICOM is not set
 +# CONFIG_MWAVE is not set
 +CONFIG_DEVMEM=y
 +CONFIG_DEVKMEM=y
 +# CONFIG_NVRAM is not set
 +# CONFIG_RAW_DRIVER is not set
 +CONFIG_DEVPORT=y
 +CONFIG_HPET=y
 +# CONFIG_HPET_MMAP is not set
 +# CONFIG_HANGCHECK_TIMER is not set
 +CONFIG_TCG_TPM=y
 +CONFIG_TCG_TIS_CORE=y
 +CONFIG_TCG_TIS=y
 +# CONFIG_TCG_NSC is not set
 +# CONFIG_TCG_ATMEL is not set
 +# CONFIG_TCG_INFINEON is not set
 +CONFIG_TCG_CRB=y
 +# CONFIG_TCG_VTPM_PROXY is not set
 +# CONFIG_TELCLOCK is not set
 +# CONFIG_XILLYBUS is not set
 +# end of Character devices
 +
 +# CONFIG_RANDOM_TRUST_CPU is not set
 +# CONFIG_RANDOM_TRUST_BOOTLOADER is not set
 +
 +#
 +# I2C support
 +#
 +# CONFIG_I2C is not set
 +# end of I2C support
 +
 +# CONFIG_I3C is not set
 +# CONFIG_SPI is not set
 +# CONFIG_SPMI is not set
 +# CONFIG_HSI is not set
 +CONFIG_PPS=y
 +# CONFIG_PPS_DEBUG is not set
 +
 +#
 +# PPS clients support
 +#
 +# CONFIG_PPS_CLIENT_KTIMER is not set
 +# CONFIG_PPS_CLIENT_LDISC is not set
 +# CONFIG_PPS_CLIENT_GPIO is not set
 +
 +#
 +# PPS generators support
 +#
 +
 +#
 +# PTP clock support
 +#
 +CONFIG_PTP_1588_CLOCK=y
 +
 +#
 +# Enable PHYLIB and NETWORK_PHY_TIMESTAMPING to see the additional clocks.
 +#
 +# end of PTP clock support
 +
 +# CONFIG_PINCTRL is not set
 +# CONFIG_GPIOLIB is not set
 +# CONFIG_W1 is not set
 +# CONFIG_POWER_AVS is not set
 +# CONFIG_POWER_RESET is not set
 +CONFIG_POWER_SUPPLY=y
 +# CONFIG_POWER_SUPPLY_DEBUG is not set
 +# CONFIG_PDA_POWER is not set
 +# CONFIG_TEST_POWER is not set
 +# CONFIG_BATTERY_DS2780 is not set
 +# CONFIG_BATTERY_DS2781 is not set
 +# CONFIG_BATTERY_BQ27XXX is not set
 +# CONFIG_CHARGER_MAX8903 is not set
 +# CONFIG_HWMON is not set
 +CONFIG_THERMAL=y
 +# CONFIG_THERMAL_NETLINK is not set
 +# CONFIG_THERMAL_STATISTICS is not set
 +CONFIG_THERMAL_EMERGENCY_POWEROFF_DELAY_MS=0
 +CONFIG_THERMAL_WRITABLE_TRIPS=y
 +CONFIG_THERMAL_DEFAULT_GOV_STEP_WISE=y
 +# CONFIG_THERMAL_DEFAULT_GOV_FAIR_SHARE is not set
 +# CONFIG_THERMAL_DEFAULT_GOV_USER_SPACE is not set
 +# CONFIG_THERMAL_GOV_FAIR_SHARE is not set
 +CONFIG_THERMAL_GOV_STEP_WISE=y
 +# CONFIG_THERMAL_GOV_BANG_BANG is not set
 +CONFIG_THERMAL_GOV_USER_SPACE=y
 +# CONFIG_THERMAL_EMULATION is not set
 +
 +#
 +# Intel thermal drivers
 +#
 +CONFIG_INTEL_POWERCLAMP=y
 +CONFIG_X86_PKG_TEMP_THERMAL=m
 +# CONFIG_INTEL_SOC_DTS_THERMAL is not set
 +
 +#
 +# ACPI INT340X thermal drivers
 +#
 +# CONFIG_INT340X_THERMAL is not set
 +# end of ACPI INT340X thermal drivers
 +
 +# CONFIG_INTEL_PCH_THERMAL is not set
 +# end of Intel thermal drivers
 +
 +# CONFIG_WATCHDOG is not set
 +CONFIG_SSB_POSSIBLE=y
 +# CONFIG_SSB is not set
 +CONFIG_BCMA_POSSIBLE=y
 +# CONFIG_BCMA is not set
 +
 +#
 +# Multifunction device drivers
 +#
 +# CONFIG_MFD_MADERA is not set
 +# CONFIG_HTC_PASIC3 is not set
 +# CONFIG_MFD_INTEL_QUARK_I2C_GPIO is not set
 +# CONFIG_LPC_ICH is not set
 +# CONFIG_LPC_SCH is not set
 +# CONFIG_MFD_INTEL_LPSS_ACPI is not set
 +# CONFIG_MFD_INTEL_LPSS_PCI is not set
 +# CONFIG_MFD_JANZ_CMODIO is not set
 +# CONFIG_MFD_KEMPLD is not set
 +# CONFIG_MFD_MT6397 is not set
 +# CONFIG_MFD_RDC321X is not set
 +# CONFIG_MFD_SM501 is not set
 +# CONFIG_ABX500_CORE is not set
 +# CONFIG_MFD_SYSCON is not set
 +# CONFIG_MFD_TI_AM335X_TSCADC is not set
 +# CONFIG_MFD_TQMX86 is not set
 +# CONFIG_MFD_VX855 is not set
 +# end of Multifunction device drivers
 +
 +# CONFIG_REGULATOR is not set
 +CONFIG_RC_CORE=y
 +# CONFIG_RC_MAP is not set
 +CONFIG_LIRC=y
 +CONFIG_BPF_LIRC_MODE2=y
 +# CONFIG_RC_DECODERS is not set
 +CONFIG_RC_DEVICES=y
 +CONFIG_RC_LOOPBACK=y
 +# CONFIG_IR_SERIAL is not set
 +# CONFIG_MEDIA_CEC_SUPPORT is not set
 +# CONFIG_MEDIA_SUPPORT is not set
 +
 +#
 +# Graphics support
 +#
 +CONFIG_AGP=y
 +CONFIG_AGP_AMD64=y
 +CONFIG_AGP_INTEL=y
 +CONFIG_AGP_SIS=y
 +CONFIG_AGP_VIA=y
 +CONFIG_INTEL_GTT=y
 +CONFIG_VGA_ARB=y
 +CONFIG_VGA_ARB_MAX_GPUS=16
 +# CONFIG_VGA_SWITCHEROO is not set
 +# CONFIG_DRM is not set
 +
 +#
 +# ARM devices
 +#
 +# end of ARM devices
 +
 +#
 +# Frame buffer Devices
 +#
 +CONFIG_FB_CMDLINE=y
 +CONFIG_FB_NOTIFY=y
 +CONFIG_FB=y
 +# CONFIG_FIRMWARE_EDID is not set
 +CONFIG_FB_BOOT_VESA_SUPPORT=y
 +CONFIG_FB_CFB_FILLRECT=y
 +CONFIG_FB_CFB_COPYAREA=y
 +CONFIG_FB_CFB_IMAGEBLIT=y
 +# CONFIG_FB_FOREIGN_ENDIAN is not set
 +CONFIG_FB_MODE_HELPERS=y
 +CONFIG_FB_TILEBLITTING=y
 +
 +#
 +# Frame buffer hardware drivers
 +#
 +# CONFIG_FB_CIRRUS is not set
 +# CONFIG_FB_PM2 is not set
 +# CONFIG_FB_CYBER2000 is not set
 +# CONFIG_FB_ARC is not set
 +# CONFIG_FB_ASILIANT is not set
 +# CONFIG_FB_IMSTT is not set
 +# CONFIG_FB_VGA16 is not set
 +CONFIG_FB_VESA=y
 +# CONFIG_FB_EFI is not set
 +# CONFIG_FB_N411 is not set
 +# CONFIG_FB_HGA is not set
 +# CONFIG_FB_OPENCORES is not set
 +# CONFIG_FB_S1D13XXX is not set
 +# CONFIG_FB_NVIDIA is not set
 +# CONFIG_FB_RIVA is not set
 +# CONFIG_FB_I740 is not set
 +# CONFIG_FB_LE80578 is not set
 +# CONFIG_FB_INTEL is not set
 +# CONFIG_FB_MATROX is not set
 +# CONFIG_FB_RADEON is not set
 +# CONFIG_FB_ATY128 is not set
 +# CONFIG_FB_ATY is not set
 +# CONFIG_FB_S3 is not set
 +# CONFIG_FB_SAVAGE is not set
 +# CONFIG_FB_SIS is not set
 +# CONFIG_FB_NEOMAGIC is not set
 +# CONFIG_FB_KYRO is not set
 +# CONFIG_FB_3DFX is not set
 +# CONFIG_FB_VOODOO1 is not set
 +# CONFIG_FB_VT8623 is not set
 +# CONFIG_FB_TRIDENT is not set
 +# CONFIG_FB_ARK is not set
 +# CONFIG_FB_PM3 is not set
 +# CONFIG_FB_CARMINE is not set
 +# CONFIG_FB_IBM_GXT4500 is not set
 +# CONFIG_FB_VIRTUAL is not set
 +# CONFIG_FB_METRONOME is not set
 +# CONFIG_FB_MB862XX is not set
 +# CONFIG_FB_SIMPLE is not set
 +# CONFIG_FB_SM712 is not set
 +# end of Frame buffer Devices
 +
 +#
 +# Backlight & LCD device support
 +#
 +# CONFIG_LCD_CLASS_DEVICE is not set
 +CONFIG_BACKLIGHT_CLASS_DEVICE=y
 +# CONFIG_BACKLIGHT_APPLE is not set
 +# CONFIG_BACKLIGHT_QCOM_WLED is not set
 +# CONFIG_BACKLIGHT_SAHARA is not set
 +# end of Backlight & LCD device support
 +
 +#
 +# Console display driver support
 +#
 +CONFIG_VGA_CONSOLE=y
 +CONFIG_VGACON_SOFT_SCROLLBACK=y
 +CONFIG_VGACON_SOFT_SCROLLBACK_SIZE=64
 +# CONFIG_VGACON_SOFT_SCROLLBACK_PERSISTENT_ENABLE_BY_DEFAULT is not set
 +CONFIG_DUMMY_CONSOLE=y
 +CONFIG_DUMMY_CONSOLE_COLUMNS=80
 +CONFIG_DUMMY_CONSOLE_ROWS=25
 +CONFIG_FRAMEBUFFER_CONSOLE=y
 +CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY=y
 +CONFIG_FRAMEBUFFER_CONSOLE_ROTATION=y
 +# CONFIG_FRAMEBUFFER_CONSOLE_DEFERRED_TAKEOVER is not set
 +# end of Console display driver support
 +
 +CONFIG_LOGO=y
 +# CONFIG_LOGO_LINUX_MONO is not set
 +# CONFIG_LOGO_LINUX_VGA16 is not set
 +CONFIG_LOGO_LINUX_CLUT224=y
 +# end of Graphics support
 +
 +# CONFIG_SOUND is not set
 +
 +#
 +# HID support
 +#
 +CONFIG_HID=y
 +# CONFIG_HID_BATTERY_STRENGTH is not set
 +# CONFIG_HIDRAW is not set
 +# CONFIG_UHID is not set
 +CONFIG_HID_GENERIC=y
 +
 +#
 +# Special HID drivers
 +#
 +CONFIG_HID_A4TECH=y
 +# CONFIG_HID_ACRUX is not set
 +CONFIG_HID_APPLE=y
 +# CONFIG_HID_AUREAL is not set
 +CONFIG_HID_BELKIN=y
 +CONFIG_HID_CHERRY=y
 +CONFIG_HID_CHICONY=y
 +# CONFIG_HID_COUGAR is not set
 +# CONFIG_HID_MACALLY is not set
 +# CONFIG_HID_CMEDIA is not set
 +CONFIG_HID_CYPRESS=y
 +CONFIG_HID_DRAGONRISE=y
 +# CONFIG_DRAGONRISE_FF is not set
 +# CONFIG_HID_EMS_FF is not set
 +# CONFIG_HID_ELECOM is not set
 +CONFIG_HID_EZKEY=y
 +# CONFIG_HID_GEMBIRD is not set
 +# CONFIG_HID_GFRM is not set
 +# CONFIG_HID_GLORIOUS is not set
 +# CONFIG_HID_KEYTOUCH is not set
 +CONFIG_HID_KYE=y
 +# CONFIG_HID_WALTOP is not set
 +# CONFIG_HID_VIEWSONIC is not set
 +CONFIG_HID_GYRATION=y
 +# CONFIG_HID_ICADE is not set
 +# CONFIG_HID_ITE is not set
 +# CONFIG_HID_JABRA is not set
 +CONFIG_HID_TWINHAN=y
 +CONFIG_HID_KENSINGTON=y
 +# CONFIG_HID_LCPOWER is not set
 +# CONFIG_HID_LENOVO is not set
 +# CONFIG_HID_MAGICMOUSE is not set
 +# CONFIG_HID_MALTRON is not set
 +# CONFIG_HID_MAYFLASH is not set
 +# CONFIG_HID_REDRAGON is not set
 +CONFIG_HID_MICROSOFT=y
 +CONFIG_HID_MONTEREY=y
 +# CONFIG_HID_MULTITOUCH is not set
 +# CONFIG_HID_NTI is not set
 +# CONFIG_HID_ORTEK is not set
 +CONFIG_HID_PANTHERLORD=y
 +# CONFIG_PANTHERLORD_FF is not set
 +CONFIG_HID_PETALYNX=y
 +# CONFIG_HID_PICOLCD is not set
 +# CONFIG_HID_PLANTRONICS is not set
 +# CONFIG_HID_PRIMAX is not set
 +# CONFIG_HID_SAITEK is not set
 +CONFIG_HID_SAMSUNG=y
 +# CONFIG_HID_SPEEDLINK is not set
 +# CONFIG_HID_STEAM is not set
 +# CONFIG_HID_STEELSERIES is not set
 +CONFIG_HID_SUNPLUS=y
 +# CONFIG_HID_RMI is not set
 +CONFIG_HID_GREENASIA=y
 +# CONFIG_GREENASIA_FF is not set
 +CONFIG_HID_SMARTJOYPLUS=y
 +# CONFIG_SMARTJOYPLUS_FF is not set
 +# CONFIG_HID_TIVO is not set
 +CONFIG_HID_TOPSEED=y
 +CONFIG_HID_THRUSTMASTER=y
 +CONFIG_THRUSTMASTER_FF=y
 +# CONFIG_HID_UDRAW_PS3 is not set
 +# CONFIG_HID_XINMO is not set
 +CONFIG_HID_ZEROPLUS=y
 +CONFIG_ZEROPLUS_FF=y
 +# CONFIG_HID_ZYDACRON is not set
 +# CONFIG_HID_SENSOR_HUB is not set
 +# CONFIG_HID_ALPS is not set
 +# end of Special HID drivers
 +
 +#
 +# Intel ISH HID support
 +#
 +# CONFIG_INTEL_ISH_HID is not set
 +# end of Intel ISH HID support
 +# end of HID support
 +
 +CONFIG_USB_OHCI_LITTLE_ENDIAN=y
 +# CONFIG_USB_SUPPORT is not set
 +# CONFIG_MMC is not set
 +# CONFIG_MEMSTICK is not set
 +# CONFIG_NEW_LEDS is not set
 +# CONFIG_ACCESSIBILITY is not set
 +# CONFIG_INFINIBAND is not set
 +CONFIG_EDAC_ATOMIC_SCRUB=y
 +CONFIG_EDAC_SUPPORT=y
 +# CONFIG_EDAC is not set
 +CONFIG_RTC_LIB=y
 +CONFIG_RTC_MC146818_LIB=y
 +# CONFIG_RTC_CLASS is not set
 +# CONFIG_DMADEVICES is not set
 +
 +#
 +# DMABUF options
 +#
 +CONFIG_SYNC_FILE=y
 +# CONFIG_SW_SYNC is not set
 +# CONFIG_UDMABUF is not set
 +# CONFIG_DMABUF_MOVE_NOTIFY is not set
 +# CONFIG_DMABUF_SELFTESTS is not set
 +# CONFIG_DMABUF_HEAPS is not set
 +# end of DMABUF options
 +
 +# CONFIG_AUXDISPLAY is not set
 +# CONFIG_UIO is not set
 +CONFIG_VIRT_DRIVERS=y
 +# CONFIG_VBOXGUEST is not set
 +CONFIG_VIRTIO=y
 +CONFIG_VIRTIO_MENU=y
 +CONFIG_VIRTIO_PCI=y
 +CONFIG_VIRTIO_PCI_LEGACY=y
 +CONFIG_VIRTIO_BALLOON=y
 +# CONFIG_VIRTIO_INPUT is not set
 +# CONFIG_VIRTIO_MMIO is not set
 +# CONFIG_VDPA is not set
 +CONFIG_VHOST_MENU=y
 +# CONFIG_VHOST_NET is not set
 +# CONFIG_VHOST_CROSS_ENDIAN_LEGACY is not set
 +
 +#
 +# Microsoft Hyper-V guest support
 +#
 +# end of Microsoft Hyper-V guest support
 +
 +# CONFIG_GREYBUS is not set
 +# CONFIG_STAGING is not set
 +# CONFIG_X86_PLATFORM_DEVICES is not set
 +CONFIG_PMC_ATOM=y
 +# CONFIG_MFD_CROS_EC is not set
 +# CONFIG_CHROME_PLATFORMS is not set
 +# CONFIG_MELLANOX_PLATFORM is not set
 +CONFIG_HAVE_CLK=y
 +CONFIG_CLKDEV_LOOKUP=y
 +CONFIG_HAVE_CLK_PREPARE=y
 +CONFIG_COMMON_CLK=y
 +# CONFIG_HWSPINLOCK is not set
 +
 +#
 +# Clock Source drivers
 +#
 +CONFIG_CLKEVT_I8253=y
 +CONFIG_I8253_LOCK=y
 +CONFIG_CLKBLD_I8253=y
 +# end of Clock Source drivers
 +
 +CONFIG_MAILBOX=y
 +CONFIG_PCC=y
 +# CONFIG_ALTERA_MBOX is not set
 +# CONFIG_IOMMU_SUPPORT is not set
 +
 +#
 +# Remoteproc drivers
 +#
 +# CONFIG_REMOTEPROC is not set
 +# end of Remoteproc drivers
 +
 +#
 +# Rpmsg drivers
 +#
 +# CONFIG_RPMSG_QCOM_GLINK_RPM is not set
 +# CONFIG_RPMSG_VIRTIO is not set
 +# end of Rpmsg drivers
 +
 +# CONFIG_SOUNDWIRE is not set
 +
 +#
 +# SOC (System On Chip) specific Drivers
 +#
 +
 +#
 +# Amlogic SoC drivers
 +#
 +# end of Amlogic SoC drivers
 +
 +#
 +# Aspeed SoC drivers
 +#
 +# end of Aspeed SoC drivers
 +
 +#
 +# Broadcom SoC drivers
 +#
 +# end of Broadcom SoC drivers
 +
 +#
 +# NXP/Freescale QorIQ SoC drivers
 +#
 +# end of NXP/Freescale QorIQ SoC drivers
 +
 +#
 +# i.MX SoC drivers
 +#
 +# end of i.MX SoC drivers
 +
 +#
 +# Qualcomm SoC drivers
 +#
 +# end of Qualcomm SoC drivers
 +
 +# CONFIG_SOC_TI is not set
 +
 +#
 +# Xilinx SoC drivers
 +#
 +# CONFIG_XILINX_VCU is not set
 +# end of Xilinx SoC drivers
 +# end of SOC (System On Chip) specific Drivers
 +
 +# CONFIG_PM_DEVFREQ is not set
 +# CONFIG_EXTCON is not set
 +# CONFIG_MEMORY is not set
 +# CONFIG_IIO is not set
 +# CONFIG_NTB is not set
 +# CONFIG_VME_BUS is not set
 +# CONFIG_PWM is not set
 +
 +#
 +# IRQ chip support
 +#
 +# end of IRQ chip support
 +
 +# CONFIG_IPACK_BUS is not set
 +# CONFIG_RESET_CONTROLLER is not set
 +
 +#
 +# PHY Subsystem
 +#
 +CONFIG_GENERIC_PHY=y
 +# CONFIG_BCM_KONA_USB2_PHY is not set
 +# CONFIG_PHY_PXA_28NM_HSIC is not set
 +# CONFIG_PHY_PXA_28NM_USB2 is not set
 +# CONFIG_PHY_INTEL_EMMC is not set
 +# end of PHY Subsystem
 +
 +# CONFIG_POWERCAP is not set
 +# CONFIG_MCB is not set
 +
 +#
 +# Performance monitor support
 +#
 +# end of Performance monitor support
 +
 +CONFIG_RAS=y
 +# CONFIG_RAS_CEC is not set
 +# CONFIG_USB4 is not set
 +
 +#
 +# Android
 +#
 +# CONFIG_ANDROID is not set
 +# end of Android
 +
 +# CONFIG_LIBNVDIMM is not set
 +# CONFIG_DAX is not set
 +CONFIG_NVMEM=y
 +# CONFIG_NVMEM_SYSFS is not set
 +
 +#
 +# HW tracing support
 +#
 +# CONFIG_STM is not set
 +# CONFIG_INTEL_TH is not set
 +# end of HW tracing support
 +
 +# CONFIG_FPGA is not set
 +# CONFIG_TEE is not set
 +# CONFIG_UNISYS_VISORBUS is not set
 +# CONFIG_SIOX is not set
 +# CONFIG_SLIMBUS is not set
 +# CONFIG_INTERCONNECT is not set
 +# CONFIG_COUNTER is not set
 +# end of Device Drivers
 +
 +#
 +# File systems
 +#
 +CONFIG_DCACHE_WORD_ACCESS=y
 +CONFIG_VALIDATE_FS_PARSER=y
 +CONFIG_FS_IOMAP=y
 +# CONFIG_EXT2_FS is not set
 +# CONFIG_EXT3_FS is not set
 +CONFIG_EXT4_FS=y
 +CONFIG_EXT4_USE_FOR_EXT2=y
 +CONFIG_EXT4_FS_POSIX_ACL=y
 +CONFIG_EXT4_FS_SECURITY=y
 +# CONFIG_EXT4_DEBUG is not set
 +CONFIG_JBD2=y
 +# CONFIG_JBD2_DEBUG is not set
 +CONFIG_FS_MBCACHE=y
 +# CONFIG_REISERFS_FS is not set
 +# CONFIG_JFS_FS is not set
 +# CONFIG_XFS_FS is not set
 +# CONFIG_GFS2_FS is not set
 +# CONFIG_BTRFS_FS is not set
 +# CONFIG_NILFS2_FS is not set
 +# CONFIG_F2FS_FS is not set
 +# CONFIG_FS_DAX is not set
 +CONFIG_FS_POSIX_ACL=y
 +CONFIG_EXPORTFS=y
 +# CONFIG_EXPORTFS_BLOCK_OPS is not set
 +CONFIG_FILE_LOCKING=y
 +CONFIG_MANDATORY_FILE_LOCKING=y
 +# CONFIG_FS_ENCRYPTION is not set
 +# CONFIG_FS_VERITY is not set
 +CONFIG_FSNOTIFY=y
 +CONFIG_DNOTIFY=y
 +CONFIG_INOTIFY_USER=y
 +# CONFIG_FANOTIFY is not set
 +# CONFIG_QUOTA is not set
 +# CONFIG_AUTOFS4_FS is not set
 +# CONFIG_AUTOFS_FS is not set
 +# CONFIG_FUSE_FS is not set
 +# CONFIG_OVERLAY_FS is not set
 +
 +#
 +# Caches
 +#
 +# CONFIG_FSCACHE is not set
 +# end of Caches
 +
 +#
 +# CD-ROM/DVD Filesystems
 +#
 +# CONFIG_ISO9660_FS is not set
 +# CONFIG_UDF_FS is not set
 +# end of CD-ROM/DVD Filesystems
 +
 +#
 +# DOS/FAT/EXFAT/NT Filesystems
 +#
 +# CONFIG_MSDOS_FS is not set
 +# CONFIG_VFAT_FS is not set
 +# CONFIG_EXFAT_FS is not set
 +# CONFIG_NTFS_FS is not set
 +# end of DOS/FAT/EXFAT/NT Filesystems
 +
 +#
 +# Pseudo filesystems
 +#
 +CONFIG_PROC_FS=y
 +CONFIG_PROC_KCORE=y
 +CONFIG_PROC_SYSCTL=y
 +CONFIG_PROC_PAGE_MONITOR=y
 +# CONFIG_PROC_CHILDREN is not set
 +CONFIG_PROC_PID_ARCH_STATUS=y
 +CONFIG_KERNFS=y
 +CONFIG_SYSFS=y
 +CONFIG_TMPFS=y
 +CONFIG_TMPFS_POSIX_ACL=y
 +CONFIG_TMPFS_XATTR=y
 +# CONFIG_TMPFS_INODE64 is not set
 +CONFIG_HUGETLBFS=y
 +CONFIG_HUGETLB_PAGE=y
 +CONFIG_MEMFD_CREATE=y
 +CONFIG_ARCH_HAS_GIGANTIC_PAGE=y
 +# CONFIG_CONFIGFS_FS is not set
 +# CONFIG_EFIVAR_FS is not set
 +# end of Pseudo filesystems
 +
 +# CONFIG_MISC_FILESYSTEMS is not set
 +CONFIG_NETWORK_FILESYSTEMS=y
 +# CONFIG_NFS_FS is not set
 +# CONFIG_NFSD is not set
 +# CONFIG_CEPH_FS is not set
 +# CONFIG_CIFS is not set
 +# CONFIG_CODA_FS is not set
 +# CONFIG_AFS_FS is not set
 +CONFIG_9P_FS=y
 +CONFIG_9P_FS_POSIX_ACL=y
 +CONFIG_9P_FS_SECURITY=y
 +CONFIG_NLS=y
 +CONFIG_NLS_DEFAULT="utf8"
 +CONFIG_NLS_CODEPAGE_437=y
 +# CONFIG_NLS_CODEPAGE_737 is not set
 +# CONFIG_NLS_CODEPAGE_775 is not set
 +# CONFIG_NLS_CODEPAGE_850 is not set
 +# CONFIG_NLS_CODEPAGE_852 is not set
 +# CONFIG_NLS_CODEPAGE_855 is not set
 +# CONFIG_NLS_CODEPAGE_857 is not set
 +# CONFIG_NLS_CODEPAGE_860 is not set
 +# CONFIG_NLS_CODEPAGE_861 is not set
 +# CONFIG_NLS_CODEPAGE_862 is not set
 +# CONFIG_NLS_CODEPAGE_863 is not set
 +# CONFIG_NLS_CODEPAGE_864 is not set
 +# CONFIG_NLS_CODEPAGE_865 is not set
 +# CONFIG_NLS_CODEPAGE_866 is not set
 +# CONFIG_NLS_CODEPAGE_869 is not set
 +# CONFIG_NLS_CODEPAGE_936 is not set
 +# CONFIG_NLS_CODEPAGE_950 is not set
 +# CONFIG_NLS_CODEPAGE_932 is not set
 +# CONFIG_NLS_CODEPAGE_949 is not set
 +# CONFIG_NLS_CODEPAGE_874 is not set
 +# CONFIG_NLS_ISO8859_8 is not set
 +# CONFIG_NLS_CODEPAGE_1250 is not set
 +# CONFIG_NLS_CODEPAGE_1251 is not set
 +CONFIG_NLS_ASCII=y
 +# CONFIG_NLS_ISO8859_1 is not set
 +# CONFIG_NLS_ISO8859_2 is not set
 +# CONFIG_NLS_ISO8859_3 is not set
 +# CONFIG_NLS_ISO8859_4 is not set
 +# CONFIG_NLS_ISO8859_5 is not set
 +# CONFIG_NLS_ISO8859_6 is not set
 +# CONFIG_NLS_ISO8859_7 is not set
 +# CONFIG_NLS_ISO8859_9 is not set
 +# CONFIG_NLS_ISO8859_13 is not set
 +# CONFIG_NLS_ISO8859_14 is not set
 +# CONFIG_NLS_ISO8859_15 is not set
 +# CONFIG_NLS_KOI8_R is not set
 +# CONFIG_NLS_KOI8_U is not set
 +# CONFIG_NLS_MAC_ROMAN is not set
 +# CONFIG_NLS_MAC_CELTIC is not set
 +# CONFIG_NLS_MAC_CENTEURO is not set
 +# CONFIG_NLS_MAC_CROATIAN is not set
 +# CONFIG_NLS_MAC_CYRILLIC is not set
 +# CONFIG_NLS_MAC_GAELIC is not set
 +# CONFIG_NLS_MAC_GREEK is not set
 +# CONFIG_NLS_MAC_ICELAND is not set
 +# CONFIG_NLS_MAC_INUIT is not set
 +# CONFIG_NLS_MAC_ROMANIAN is not set
 +# CONFIG_NLS_MAC_TURKISH is not set
 +# CONFIG_NLS_UTF8 is not set
 +# CONFIG_UNICODE is not set
 +CONFIG_IO_WQ=y
 +# end of File systems
 +
 +#
 +# Security options
 +#
 +CONFIG_KEYS=y
 +# CONFIG_KEYS_REQUEST_CACHE is not set
 +# CONFIG_PERSISTENT_KEYRINGS is not set
 +# CONFIG_TRUSTED_KEYS is not set
 +# CONFIG_ENCRYPTED_KEYS is not set
 +# CONFIG_KEY_DH_OPERATIONS is not set
 +# CONFIG_SECURITY_DMESG_RESTRICT is not set
 +CONFIG_SECURITY=y
 +CONFIG_SECURITYFS=y
 +CONFIG_SECURITY_NETWORK=y
 +CONFIG_PAGE_TABLE_ISOLATION=y
 +# CONFIG_SECURITY_NETWORK_XFRM is not set
 +# CONFIG_SECURITY_PATH is not set
 +CONFIG_LSM_MMAP_MIN_ADDR=65536
 +CONFIG_HAVE_HARDENED_USERCOPY_ALLOCATOR=y
 +# CONFIG_HARDENED_USERCOPY is not set
 +# CONFIG_FORTIFY_SOURCE is not set
 +# CONFIG_STATIC_USERMODEHELPER is not set
 +CONFIG_SECURITY_SELINUX=y
 +# CONFIG_SECURITY_SELINUX_BOOTPARAM is not set
 +# CONFIG_SECURITY_SELINUX_DISABLE is not set
 +CONFIG_SECURITY_SELINUX_DEVELOP=y
 +CONFIG_SECURITY_SELINUX_AVC_STATS=y
 +CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE=0
 +CONFIG_SECURITY_SELINUX_SIDTAB_HASH_BITS=9
 +CONFIG_SECURITY_SELINUX_SID2STR_CACHE_SIZE=256
 +# CONFIG_SECURITY_SMACK is not set
 +# CONFIG_SECURITY_TOMOYO is not set
 +# CONFIG_SECURITY_APPARMOR is not set
 +# CONFIG_SECURITY_LOADPIN is not set
 +# CONFIG_SECURITY_YAMA is not set
 +# CONFIG_SECURITY_SAFESETID is not set
 +# CONFIG_SECURITY_LOCKDOWN_LSM is not set
 +CONFIG_INTEGRITY=y
 +# CONFIG_INTEGRITY_SIGNATURE is not set
 +CONFIG_INTEGRITY_AUDIT=y
 +CONFIG_IMA=y
 +CONFIG_IMA_MEASURE_PCR_IDX=10
 +CONFIG_IMA_LSM_RULES=y
 +# CONFIG_IMA_TEMPLATE is not set
 +CONFIG_IMA_NG_TEMPLATE=y
 +# CONFIG_IMA_SIG_TEMPLATE is not set
 +CONFIG_IMA_DEFAULT_TEMPLATE="ima-ng"
 +CONFIG_IMA_DEFAULT_HASH_SHA1=y
 +# CONFIG_IMA_DEFAULT_HASH_SHA256 is not set
 +CONFIG_IMA_DEFAULT_HASH="sha1"
 +CONFIG_IMA_WRITE_POLICY=y
 +CONFIG_IMA_READ_POLICY=y
 +# CONFIG_IMA_APPRAISE is not set
 +CONFIG_IMA_MEASURE_ASYMMETRIC_KEYS=y
 +CONFIG_IMA_QUEUE_EARLY_BOOT_KEYS=y
 +# CONFIG_IMA_SECURE_AND_OR_TRUSTED_BOOT is not set
 +# CONFIG_EVM is not set
 +# CONFIG_DEFAULT_SECURITY_SELINUX is not set
 +CONFIG_DEFAULT_SECURITY_DAC=y
 +CONFIG_LSM="selinux,bpf,integrity"
 +
 +#
 +# Kernel hardening options
 +#
 +
 +#
 +# Memory initialization
 +#
 +CONFIG_INIT_STACK_NONE=y
 +# CONFIG_INIT_ON_ALLOC_DEFAULT_ON is not set
 +# CONFIG_INIT_ON_FREE_DEFAULT_ON is not set
 +# end of Memory initialization
 +# end of Kernel hardening options
 +# end of Security options
 +
 +CONFIG_CRYPTO=y
 +
 +#
 +# Crypto core or helper
 +#
 +CONFIG_CRYPTO_ALGAPI=y
 +CONFIG_CRYPTO_ALGAPI2=y
 +CONFIG_CRYPTO_AEAD=y
 +CONFIG_CRYPTO_AEAD2=y
 +CONFIG_CRYPTO_SKCIPHER=y
 +CONFIG_CRYPTO_SKCIPHER2=y
 +CONFIG_CRYPTO_HASH=y
 +CONFIG_CRYPTO_HASH2=y
 +CONFIG_CRYPTO_RNG=y
 +CONFIG_CRYPTO_RNG2=y
 +CONFIG_CRYPTO_RNG_DEFAULT=y
 +CONFIG_CRYPTO_AKCIPHER2=y
 +CONFIG_CRYPTO_AKCIPHER=y
 +CONFIG_CRYPTO_KPP2=y
 +CONFIG_CRYPTO_ACOMP2=y
 +CONFIG_CRYPTO_MANAGER=y
 +CONFIG_CRYPTO_MANAGER2=y
 +# CONFIG_CRYPTO_USER is not set
 +CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y
 +CONFIG_CRYPTO_GF128MUL=y
 +CONFIG_CRYPTO_NULL=y
 +CONFIG_CRYPTO_NULL2=y
 +# CONFIG_CRYPTO_PCRYPT is not set
 +# CONFIG_CRYPTO_CRYPTD is not set
 +# CONFIG_CRYPTO_AUTHENC is not set
 +# CONFIG_CRYPTO_TEST is not set
 +CONFIG_CRYPTO_ENGINE=m
 +
 +#
 +# Public-key cryptography
 +#
 +CONFIG_CRYPTO_RSA=y
 +# CONFIG_CRYPTO_DH is not set
 +# CONFIG_CRYPTO_ECDH is not set
 +# CONFIG_CRYPTO_ECRDSA is not set
 +# CONFIG_CRYPTO_CURVE25519 is not set
 +# CONFIG_CRYPTO_CURVE25519_X86 is not set
 +
 +#
 +# Authenticated Encryption with Associated Data
 +#
 +# CONFIG_CRYPTO_CCM is not set
 +CONFIG_CRYPTO_GCM=y
 +# CONFIG_CRYPTO_CHACHA20POLY1305 is not set
 +# CONFIG_CRYPTO_AEGIS128 is not set
 +# CONFIG_CRYPTO_AEGIS128_AESNI_SSE2 is not set
 +CONFIG_CRYPTO_SEQIV=y
 +# CONFIG_CRYPTO_ECHAINIV is not set
 +
 +#
 +# Block modes
 +#
 +# CONFIG_CRYPTO_CBC is not set
 +# CONFIG_CRYPTO_CFB is not set
 +CONFIG_CRYPTO_CTR=y
 +# CONFIG_CRYPTO_CTS is not set
 +# CONFIG_CRYPTO_ECB is not set
 +# CONFIG_CRYPTO_LRW is not set
 +# CONFIG_CRYPTO_OFB is not set
 +# CONFIG_CRYPTO_PCBC is not set
 +# CONFIG_CRYPTO_XTS is not set
 +# CONFIG_CRYPTO_KEYWRAP is not set
 +# CONFIG_CRYPTO_NHPOLY1305_SSE2 is not set
 +# CONFIG_CRYPTO_NHPOLY1305_AVX2 is not set
 +# CONFIG_CRYPTO_ADIANTUM is not set
 +# CONFIG_CRYPTO_ESSIV is not set
 +
 +#
 +# Hash modes
 +#
 +# CONFIG_CRYPTO_CMAC is not set
 +CONFIG_CRYPTO_HMAC=y
 +# CONFIG_CRYPTO_XCBC is not set
 +# CONFIG_CRYPTO_VMAC is not set
 +
 +#
 +# Digest
 +#
 +CONFIG_CRYPTO_CRC32C=y
 +# CONFIG_CRYPTO_CRC32C_INTEL is not set
 +# CONFIG_CRYPTO_CRC32 is not set
 +# CONFIG_CRYPTO_CRC32_PCLMUL is not set
 +CONFIG_CRYPTO_XXHASH=y
 +CONFIG_CRYPTO_BLAKE2B=y
 +# CONFIG_CRYPTO_BLAKE2S is not set
 +# CONFIG_CRYPTO_BLAKE2S_X86 is not set
 +CONFIG_CRYPTO_CRCT10DIF=y
 +# CONFIG_CRYPTO_CRCT10DIF_PCLMUL is not set
 +CONFIG_CRYPTO_GHASH=y
 +# CONFIG_CRYPTO_POLY1305 is not set
 +# CONFIG_CRYPTO_POLY1305_X86_64 is not set
 +# CONFIG_CRYPTO_MD4 is not set
 +CONFIG_CRYPTO_MD5=y
 +# CONFIG_CRYPTO_MICHAEL_MIC is not set
 +# CONFIG_CRYPTO_RMD128 is not set
 +# CONFIG_CRYPTO_RMD160 is not set
 +# CONFIG_CRYPTO_RMD256 is not set
 +# CONFIG_CRYPTO_RMD320 is not set
 +CONFIG_CRYPTO_SHA1=y
 +# CONFIG_CRYPTO_SHA1_SSSE3 is not set
 +# CONFIG_CRYPTO_SHA256_SSSE3 is not set
 +# CONFIG_CRYPTO_SHA512_SSSE3 is not set
 +CONFIG_CRYPTO_SHA256=y
 +# CONFIG_CRYPTO_SHA512 is not set
 +# CONFIG_CRYPTO_SHA3 is not set
 +# CONFIG_CRYPTO_SM3 is not set
 +# CONFIG_CRYPTO_STREEBOG is not set
 +# CONFIG_CRYPTO_TGR192 is not set
 +# CONFIG_CRYPTO_WP512 is not set
 +# CONFIG_CRYPTO_GHASH_CLMUL_NI_INTEL is not set
 +
 +#
 +# Ciphers
 +#
 +CONFIG_CRYPTO_AES=y
 +# CONFIG_CRYPTO_AES_TI is not set
 +# CONFIG_CRYPTO_AES_NI_INTEL is not set
 +# CONFIG_CRYPTO_ANUBIS is not set
 +# CONFIG_CRYPTO_ARC4 is not set
 +# CONFIG_CRYPTO_BLOWFISH is not set
 +# CONFIG_CRYPTO_BLOWFISH_X86_64 is not set
 +# CONFIG_CRYPTO_CAMELLIA is not set
 +# CONFIG_CRYPTO_CAMELLIA_X86_64 is not set
 +# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX_X86_64 is not set
 +# CONFIG_CRYPTO_CAMELLIA_AESNI_AVX2_X86_64 is not set
 +# CONFIG_CRYPTO_CAST5 is not set
 +# CONFIG_CRYPTO_CAST5_AVX_X86_64 is not set
 +# CONFIG_CRYPTO_CAST6 is not set
 +# CONFIG_CRYPTO_CAST6_AVX_X86_64 is not set
 +# CONFIG_CRYPTO_DES is not set
 +# CONFIG_CRYPTO_DES3_EDE_X86_64 is not set
 +# CONFIG_CRYPTO_FCRYPT is not set
 +# CONFIG_CRYPTO_KHAZAD is not set
 +# CONFIG_CRYPTO_SALSA20 is not set
 +# CONFIG_CRYPTO_CHACHA20 is not set
 +# CONFIG_CRYPTO_CHACHA20_X86_64 is not set
 +# CONFIG_CRYPTO_SEED is not set
 +# CONFIG_CRYPTO_SERPENT is not set
 +# CONFIG_CRYPTO_SERPENT_SSE2_X86_64 is not set
 +# CONFIG_CRYPTO_SERPENT_AVX_X86_64 is not set
 +# CONFIG_CRYPTO_SERPENT_AVX2_X86_64 is not set
 +# CONFIG_CRYPTO_SM4 is not set
 +# CONFIG_CRYPTO_TEA is not set
 +# CONFIG_CRYPTO_TWOFISH is not set
 +# CONFIG_CRYPTO_TWOFISH_X86_64 is not set
 +# CONFIG_CRYPTO_TWOFISH_X86_64_3WAY is not set
 +# CONFIG_CRYPTO_TWOFISH_AVX_X86_64 is not set
 +
 +#
 +# Compression
 +#
 +# CONFIG_CRYPTO_DEFLATE is not set
 +# CONFIG_CRYPTO_LZO is not set
 +# CONFIG_CRYPTO_842 is not set
 +# CONFIG_CRYPTO_LZ4 is not set
 +# CONFIG_CRYPTO_LZ4HC is not set
 +# CONFIG_CRYPTO_ZSTD is not set
 +
 +#
 +# Random Number Generation
 +#
 +# CONFIG_CRYPTO_ANSI_CPRNG is not set
 +CONFIG_CRYPTO_DRBG_MENU=y
 +CONFIG_CRYPTO_DRBG_HMAC=y
 +# CONFIG_CRYPTO_DRBG_HASH is not set
 +# CONFIG_CRYPTO_DRBG_CTR is not set
 +CONFIG_CRYPTO_DRBG=y
 +CONFIG_CRYPTO_JITTERENTROPY=y
 +CONFIG_CRYPTO_USER_API=y
 +CONFIG_CRYPTO_USER_API_HASH=y
 +# CONFIG_CRYPTO_USER_API_SKCIPHER is not set
 +# CONFIG_CRYPTO_USER_API_RNG is not set
 +# CONFIG_CRYPTO_USER_API_AEAD is not set
 +CONFIG_CRYPTO_HASH_INFO=y
 +
 +#
 +# Crypto library routines
 +#
 +CONFIG_CRYPTO_LIB_AES=y
 +# CONFIG_CRYPTO_LIB_BLAKE2S is not set
 +# CONFIG_CRYPTO_LIB_CHACHA is not set
 +# CONFIG_CRYPTO_LIB_CURVE25519 is not set
 +CONFIG_CRYPTO_LIB_POLY1305_RSIZE=11
 +# CONFIG_CRYPTO_LIB_POLY1305 is not set
 +# CONFIG_CRYPTO_LIB_CHACHA20POLY1305 is not set
 +CONFIG_CRYPTO_LIB_SHA256=y
 +CONFIG_CRYPTO_HW=y
 +# CONFIG_CRYPTO_DEV_PADLOCK is not set
 +# CONFIG_CRYPTO_DEV_CCP is not set
 +# CONFIG_CRYPTO_DEV_QAT_DH895xCC is not set
 +# CONFIG_CRYPTO_DEV_QAT_C3XXX is not set
 +# CONFIG_CRYPTO_DEV_QAT_C62X is not set
 +# CONFIG_CRYPTO_DEV_QAT_DH895xCCVF is not set
 +# CONFIG_CRYPTO_DEV_QAT_C3XXXVF is not set
 +# CONFIG_CRYPTO_DEV_QAT_C62XVF is not set
 +# CONFIG_CRYPTO_DEV_NITROX_CNN55XX is not set
 +CONFIG_CRYPTO_DEV_VIRTIO=m
 +# CONFIG_CRYPTO_DEV_SAFEXCEL is not set
 +# CONFIG_CRYPTO_DEV_AMLOGIC_GXL is not set
 +CONFIG_ASYMMETRIC_KEY_TYPE=y
 +CONFIG_ASYMMETRIC_PUBLIC_KEY_SUBTYPE=y
 +CONFIG_X509_CERTIFICATE_PARSER=y
 +# CONFIG_PKCS8_PRIVATE_KEY_PARSER is not set
 +CONFIG_PKCS7_MESSAGE_PARSER=y
 +
 +#
 +# Certificates for signature checking
 +#
 +CONFIG_SYSTEM_TRUSTED_KEYRING=y
 +CONFIG_SYSTEM_TRUSTED_KEYS=""
 +# CONFIG_SYSTEM_EXTRA_CERTIFICATE is not set
 +# CONFIG_SECONDARY_TRUSTED_KEYRING is not set
 +# CONFIG_SYSTEM_BLACKLIST_KEYRING is not set
 +# end of Certificates for signature checking
 +
 +CONFIG_BINARY_PRINTF=y
 +
 +#
 +# Library routines
 +#
 +# CONFIG_PACKING is not set
 +CONFIG_BITREVERSE=y
 +CONFIG_GENERIC_STRNCPY_FROM_USER=y
 +CONFIG_GENERIC_STRNLEN_USER=y
 +CONFIG_GENERIC_NET_UTILS=y
 +CONFIG_GENERIC_FIND_FIRST_BIT=y
 +# CONFIG_CORDIC is not set
 +# CONFIG_PRIME_NUMBERS is not set
 +CONFIG_RATIONAL=y
 +CONFIG_GENERIC_PCI_IOMAP=y
 +CONFIG_GENERIC_IOMAP=y
 +CONFIG_ARCH_USE_CMPXCHG_LOCKREF=y
 +CONFIG_ARCH_HAS_FAST_MULTIPLIER=y
 +CONFIG_ARCH_USE_SYM_ANNOTATIONS=y
 +CONFIG_CRC_CCITT=y
 +CONFIG_CRC16=y
 +CONFIG_CRC_T10DIF=y
 +# CONFIG_CRC_ITU_T is not set
 +CONFIG_CRC32=y
 +# CONFIG_CRC32_SELFTEST is not set
 +CONFIG_CRC32_SLICEBY8=y
 +# CONFIG_CRC32_SLICEBY4 is not set
 +# CONFIG_CRC32_SARWATE is not set
 +# CONFIG_CRC32_BIT is not set
 +# CONFIG_CRC64 is not set
 +# CONFIG_CRC4 is not set
 +# CONFIG_CRC7 is not set
 +CONFIG_LIBCRC32C=y
 +# CONFIG_CRC8 is not set
 +CONFIG_XXHASH=y
 +# CONFIG_RANDOM32_SELFTEST is not set
 +CONFIG_ZLIB_INFLATE=y
 +CONFIG_LZO_DECOMPRESS=y
 +CONFIG_LZ4_DECOMPRESS=y
 +CONFIG_ZSTD_DECOMPRESS=y
 +CONFIG_XZ_DEC=y
 +CONFIG_XZ_DEC_X86=y
 +# CONFIG_XZ_DEC_POWERPC is not set
 +# CONFIG_XZ_DEC_IA64 is not set
 +# CONFIG_XZ_DEC_ARM is not set
 +# CONFIG_XZ_DEC_ARMTHUMB is not set
 +# CONFIG_XZ_DEC_SPARC is not set
 +CONFIG_XZ_DEC_BCJ=y
 +# CONFIG_XZ_DEC_TEST is not set
 +CONFIG_DECOMPRESS_GZIP=y
 +CONFIG_DECOMPRESS_BZIP2=y
 +CONFIG_DECOMPRESS_LZMA=y
 +CONFIG_DECOMPRESS_XZ=y
 +CONFIG_DECOMPRESS_LZO=y
 +CONFIG_DECOMPRESS_LZ4=y
 +CONFIG_DECOMPRESS_ZSTD=y
 +CONFIG_GENERIC_ALLOCATOR=y
 +CONFIG_XARRAY_MULTI=y
 +CONFIG_ASSOCIATIVE_ARRAY=y
 +CONFIG_HAS_IOMEM=y
 +CONFIG_HAS_IOPORT_MAP=y
 +CONFIG_HAS_DMA=y
 +CONFIG_DMA_OPS=y
 +CONFIG_NEED_SG_DMA_LENGTH=y
 +CONFIG_NEED_DMA_MAP_STATE=y
 +CONFIG_ARCH_DMA_ADDR_T_64BIT=y
 +CONFIG_SWIOTLB=y
 +CONFIG_DMA_CMA=y
 +
 +#
 +# Default contiguous memory area size:
 +#
 +CONFIG_CMA_SIZE_MBYTES=0
 +CONFIG_CMA_SIZE_SEL_MBYTES=y
 +# CONFIG_CMA_SIZE_SEL_PERCENTAGE is not set
 +# CONFIG_CMA_SIZE_SEL_MIN is not set
 +# CONFIG_CMA_SIZE_SEL_MAX is not set
 +CONFIG_CMA_ALIGNMENT=8
 +# CONFIG_DMA_API_DEBUG is not set
 +CONFIG_SGL_ALLOC=y
 +CONFIG_IOMMU_HELPER=y
 +CONFIG_CPU_RMAP=y
 +CONFIG_DQL=y
 +CONFIG_GLOB=y
 +# CONFIG_GLOB_SELFTEST is not set
 +CONFIG_NLATTR=y
 +CONFIG_CLZ_TAB=y
 +CONFIG_IRQ_POLL=y
 +CONFIG_MPILIB=y
 +CONFIG_OID_REGISTRY=y
 +CONFIG_UCS2_STRING=y
 +CONFIG_HAVE_GENERIC_VDSO=y
 +CONFIG_GENERIC_GETTIMEOFDAY=y
 +CONFIG_GENERIC_VDSO_TIME_NS=y
 +CONFIG_FONT_SUPPORT=y
 +CONFIG_FONTS=y
 +# CONFIG_FONT_8x8 is not set
 +CONFIG_FONT_8x16=y
 +# CONFIG_FONT_6x11 is not set
 +# CONFIG_FONT_7x14 is not set
 +# CONFIG_FONT_PEARL_8x8 is not set
 +# CONFIG_FONT_ACORN_8x8 is not set
 +CONFIG_FONT_MINI_4x6=y
 +# CONFIG_FONT_6x10 is not set
 +# CONFIG_FONT_10x18 is not set
 +# CONFIG_FONT_SUN8x16 is not set
 +# CONFIG_FONT_SUN12x22 is not set
 +# CONFIG_FONT_TER16x32 is not set
 +CONFIG_ARCH_HAS_PMEM_API=y
 +CONFIG_ARCH_HAS_UACCESS_FLUSHCACHE=y
 +CONFIG_ARCH_HAS_UACCESS_MCSAFE=y
 +CONFIG_ARCH_STACKWALK=y
 +CONFIG_SBITMAP=y
 +# CONFIG_STRING_SELFTEST is not set
 +# end of Library routines
 +
 +#
 +# Kernel hacking
 +#
 +
 +#
 +# printk and dmesg options
 +#
 +CONFIG_PRINTK_TIME=y
 +# CONFIG_PRINTK_CALLER is not set
 +CONFIG_CONSOLE_LOGLEVEL_DEFAULT=7
 +CONFIG_CONSOLE_LOGLEVEL_QUIET=4
 +CONFIG_MESSAGE_LOGLEVEL_DEFAULT=4
 +# CONFIG_BOOT_PRINTK_DELAY is not set
 +# CONFIG_DYNAMIC_DEBUG is not set
 +# CONFIG_DYNAMIC_DEBUG_CORE is not set
 +CONFIG_SYMBOLIC_ERRNAME=y
 +CONFIG_DEBUG_BUGVERBOSE=y
 +# end of printk and dmesg options
 +
 +#
 +# Compile-time checks and compiler options
 +#
 +CONFIG_DEBUG_INFO=y
 +# CONFIG_DEBUG_INFO_REDUCED is not set
 +# CONFIG_DEBUG_INFO_COMPRESSED is not set
 +# CONFIG_DEBUG_INFO_SPLIT is not set
 +# CONFIG_DEBUG_INFO_DWARF4 is not set
 +CONFIG_DEBUG_INFO_BTF=y
 +# CONFIG_GDB_SCRIPTS is not set
 +CONFIG_ENABLE_MUST_CHECK=y
 +CONFIG_FRAME_WARN=2048
 +# CONFIG_STRIP_ASM_SYMS is not set
 +# CONFIG_READABLE_ASM is not set
 +# CONFIG_HEADERS_INSTALL is not set
 +# CONFIG_DEBUG_SECTION_MISMATCH is not set
 +CONFIG_SECTION_MISMATCH_WARN_ONLY=y
 +# CONFIG_DEBUG_FORCE_FUNCTION_ALIGN_32B is not set
 +CONFIG_STACK_VALIDATION=y
 +# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set
 +# end of Compile-time checks and compiler options
 +
 +#
 +# Generic Kernel Debugging Instruments
 +#
 +CONFIG_MAGIC_SYSRQ=y
 +CONFIG_MAGIC_SYSRQ_DEFAULT_ENABLE=0x1
 +CONFIG_MAGIC_SYSRQ_SERIAL=y
 +CONFIG_MAGIC_SYSRQ_SERIAL_SEQUENCE=""
 +CONFIG_DEBUG_FS=y
 +CONFIG_DEBUG_FS_ALLOW_ALL=y
 +# CONFIG_DEBUG_FS_DISALLOW_MOUNT is not set
 +# CONFIG_DEBUG_FS_ALLOW_NONE is not set
 +CONFIG_HAVE_ARCH_KGDB=y
 +# CONFIG_KGDB is not set
 +CONFIG_ARCH_HAS_UBSAN_SANITIZE_ALL=y
 +# CONFIG_UBSAN is not set
 +# end of Generic Kernel Debugging Instruments
 +
 +CONFIG_DEBUG_KERNEL=y
 +CONFIG_DEBUG_MISC=y
 +
 +#
 +# Memory Debugging
 +#
 +# CONFIG_PAGE_EXTENSION is not set
 +# CONFIG_DEBUG_PAGEALLOC is not set
 +# CONFIG_PAGE_OWNER is not set
 +# CONFIG_PAGE_POISONING is not set
 +# CONFIG_DEBUG_PAGE_REF is not set
 +# CONFIG_DEBUG_RODATA_TEST is not set
 +CONFIG_ARCH_HAS_DEBUG_WX=y
 +# CONFIG_DEBUG_WX is not set
 +CONFIG_GENERIC_PTDUMP=y
 +# CONFIG_PTDUMP_DEBUGFS is not set
 +# CONFIG_DEBUG_OBJECTS is not set
 +# CONFIG_SLUB_DEBUG_ON is not set
 +# CONFIG_SLUB_STATS is not set
 +CONFIG_HAVE_DEBUG_KMEMLEAK=y
 +# CONFIG_DEBUG_KMEMLEAK is not set
 +# CONFIG_DEBUG_STACK_USAGE is not set
 +CONFIG_SCHED_STACK_END_CHECK=y
 +CONFIG_ARCH_HAS_DEBUG_VM_PGTABLE=y
 +# CONFIG_DEBUG_VM is not set
 +# CONFIG_DEBUG_VM_PGTABLE is not set
 +CONFIG_ARCH_HAS_DEBUG_VIRTUAL=y
 +# CONFIG_DEBUG_VIRTUAL is not set
 +CONFIG_DEBUG_MEMORY_INIT=y
 +# CONFIG_DEBUG_PER_CPU_MAPS is not set
 +CONFIG_HAVE_ARCH_KASAN=y
 +CONFIG_HAVE_ARCH_KASAN_VMALLOC=y
 +CONFIG_CC_HAS_KASAN_GENERIC=y
 +# end of Memory Debugging
 +
 +# CONFIG_DEBUG_SHIRQ is not set
 +
 +#
 +# Debug Oops, Lockups and Hangs
 +#
 +CONFIG_PANIC_ON_OOPS=y
 +CONFIG_PANIC_ON_OOPS_VALUE=1
 +CONFIG_PANIC_TIMEOUT=0
 +CONFIG_LOCKUP_DETECTOR=y
 +CONFIG_SOFTLOCKUP_DETECTOR=y
 +# CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC is not set
 +CONFIG_BOOTPARAM_SOFTLOCKUP_PANIC_VALUE=0
 +CONFIG_HARDLOCKUP_DETECTOR_PERF=y
 +CONFIG_HARDLOCKUP_CHECK_TIMESTAMP=y
 +CONFIG_HARDLOCKUP_DETECTOR=y
 +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC=y
 +CONFIG_BOOTPARAM_HARDLOCKUP_PANIC_VALUE=1
 +CONFIG_DETECT_HUNG_TASK=y
 +CONFIG_DEFAULT_HUNG_TASK_TIMEOUT=120
 +# CONFIG_BOOTPARAM_HUNG_TASK_PANIC is not set
 +CONFIG_BOOTPARAM_HUNG_TASK_PANIC_VALUE=0
 +# CONFIG_WQ_WATCHDOG is not set
 +# CONFIG_TEST_LOCKUP is not set
 +# end of Debug Oops, Lockups and Hangs
 +
 +#
 +# Scheduler Debugging
 +#
 +CONFIG_SCHED_DEBUG=y
 +CONFIG_SCHED_INFO=y
 +CONFIG_SCHEDSTATS=y
 +# end of Scheduler Debugging
 +
 +# CONFIG_DEBUG_TIMEKEEPING is not set
 +CONFIG_DEBUG_PREEMPT=y
 +
 +#
 +# Lock Debugging (spinlocks, mutexes, etc...)
 +#
 +CONFIG_LOCK_DEBUGGING_SUPPORT=y
 +CONFIG_PROVE_LOCKING=y
 +# CONFIG_PROVE_RAW_LOCK_NESTING is not set
 +# CONFIG_LOCK_STAT is not set
 +CONFIG_DEBUG_RT_MUTEXES=y
 +CONFIG_DEBUG_SPINLOCK=y
 +CONFIG_DEBUG_MUTEXES=y
 +CONFIG_DEBUG_WW_MUTEX_SLOWPATH=y
 +CONFIG_DEBUG_RWSEMS=y
 +CONFIG_DEBUG_LOCK_ALLOC=y
 +CONFIG_LOCKDEP=y
 +# CONFIG_DEBUG_LOCKDEP is not set
 +CONFIG_DEBUG_ATOMIC_SLEEP=y
 +# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set
 +# CONFIG_LOCK_TORTURE_TEST is not set
 +# CONFIG_WW_MUTEX_SELFTEST is not set
 +# end of Lock Debugging (spinlocks, mutexes, etc...)
 +
 +CONFIG_TRACE_IRQFLAGS=y
 +CONFIG_TRACE_IRQFLAGS_NMI=y
 +CONFIG_STACKTRACE=y
 +# CONFIG_WARN_ALL_UNSEEDED_RANDOM is not set
 +# CONFIG_DEBUG_KOBJECT is not set
 +
 +#
 +# Debug kernel data structures
 +#
 +# CONFIG_DEBUG_LIST is not set
 +# CONFIG_DEBUG_PLIST is not set
 +# CONFIG_DEBUG_SG is not set
 +# CONFIG_DEBUG_NOTIFIERS is not set
 +# CONFIG_BUG_ON_DATA_CORRUPTION is not set
 +# end of Debug kernel data structures
 +
 +CONFIG_DEBUG_CREDENTIALS=y
 +
 +#
 +# RCU Debugging
 +#
 +CONFIG_PROVE_RCU=y
 +# CONFIG_RCU_PERF_TEST is not set
 +# CONFIG_RCU_TORTURE_TEST is not set
 +# CONFIG_RCU_REF_SCALE_TEST is not set
 +CONFIG_RCU_CPU_STALL_TIMEOUT=60
 +# CONFIG_RCU_TRACE is not set
 +# CONFIG_RCU_EQS_DEBUG is not set
 +# end of RCU Debugging
 +
 +# CONFIG_DEBUG_WQ_FORCE_RR_CPU is not set
 +# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set
 +# CONFIG_CPU_HOTPLUG_STATE_CONTROL is not set
 +# CONFIG_LATENCYTOP is not set
 +CONFIG_USER_STACKTRACE_SUPPORT=y
 +CONFIG_NOP_TRACER=y
 +CONFIG_HAVE_FUNCTION_TRACER=y
 +CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
 +CONFIG_HAVE_DYNAMIC_FTRACE=y
 +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_REGS=y
 +CONFIG_HAVE_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
 +CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
 +CONFIG_HAVE_SYSCALL_TRACEPOINTS=y
 +CONFIG_HAVE_FENTRY=y
 +CONFIG_HAVE_C_RECORDMCOUNT=y
 +CONFIG_TRACE_CLOCK=y
 +CONFIG_RING_BUFFER=y
 +CONFIG_EVENT_TRACING=y
 +CONFIG_CONTEXT_SWITCH_TRACER=y
 +CONFIG_PREEMPTIRQ_TRACEPOINTS=y
 +CONFIG_TRACING=y
 +CONFIG_GENERIC_TRACER=y
 +CONFIG_TRACING_SUPPORT=y
 +CONFIG_FTRACE=y
 +CONFIG_BOOTTIME_TRACING=y
 +CONFIG_FUNCTION_TRACER=y
 +CONFIG_FUNCTION_GRAPH_TRACER=y
 +CONFIG_DYNAMIC_FTRACE=y
 +CONFIG_DYNAMIC_FTRACE_WITH_REGS=y
 +CONFIG_DYNAMIC_FTRACE_WITH_DIRECT_CALLS=y
 +# CONFIG_FUNCTION_PROFILER is not set
 +# CONFIG_STACK_TRACER is not set
 +# CONFIG_IRQSOFF_TRACER is not set
 +# CONFIG_PREEMPT_TRACER is not set
 +# CONFIG_SCHED_TRACER is not set
 +# CONFIG_HWLAT_TRACER is not set
 +# CONFIG_MMIOTRACE is not set
 +CONFIG_FTRACE_SYSCALLS=y
 +# CONFIG_TRACER_SNAPSHOT is not set
 +CONFIG_BRANCH_PROFILE_NONE=y
 +# CONFIG_PROFILE_ANNOTATED_BRANCHES is not set
 +# CONFIG_PROFILE_ALL_BRANCHES is not set
 +CONFIG_BLK_DEV_IO_TRACE=y
 +CONFIG_KPROBE_EVENTS=y
 +# CONFIG_KPROBE_EVENTS_ON_NOTRACE is not set
 +CONFIG_UPROBE_EVENTS=y
 +CONFIG_BPF_EVENTS=y
 +CONFIG_DYNAMIC_EVENTS=y
 +CONFIG_PROBE_EVENTS=y
 +CONFIG_BPF_KPROBE_OVERRIDE=y
 +CONFIG_FTRACE_MCOUNT_RECORD=y
 +# CONFIG_SYNTH_EVENTS is not set
 +# CONFIG_HIST_TRIGGERS is not set
 +# CONFIG_TRACE_EVENT_INJECT is not set
 +# CONFIG_TRACEPOINT_BENCHMARK is not set
 +# CONFIG_RING_BUFFER_BENCHMARK is not set
 +# CONFIG_TRACE_EVAL_MAP_FILE is not set
 +# CONFIG_FTRACE_STARTUP_TEST is not set
 +# CONFIG_RING_BUFFER_STARTUP_TEST is not set
 +# CONFIG_PREEMPTIRQ_DELAY_TEST is not set
 +# CONFIG_KPROBE_EVENT_GEN_TEST is not set
 +# CONFIG_PROVIDE_OHCI1394_DMA_INIT is not set
 +# CONFIG_SAMPLES is not set
 +CONFIG_HAVE_ARCH_KCSAN=y
 +CONFIG_ARCH_HAS_DEVMEM_IS_ALLOWED=y
 +# CONFIG_STRICT_DEVMEM is not set
 +
 +#
 +# x86 Debugging
 +#
 +CONFIG_TRACE_IRQFLAGS_SUPPORT=y
 +CONFIG_TRACE_IRQFLAGS_NMI_SUPPORT=y
 +CONFIG_X86_VERBOSE_BOOTUP=y
 +CONFIG_EARLY_PRINTK=y
 +# CONFIG_EARLY_PRINTK_DBGP is not set
 +# CONFIG_EARLY_PRINTK_USB_XDBC is not set
 +# CONFIG_EFI_PGT_DUMP is not set
 +# CONFIG_DEBUG_TLBFLUSH is not set
 +# CONFIG_IOMMU_DEBUG is not set
 +CONFIG_HAVE_MMIOTRACE_SUPPORT=y
 +# CONFIG_X86_DECODER_SELFTEST is not set
 +CONFIG_IO_DELAY_0X80=y
 +# CONFIG_IO_DELAY_0XED is not set
 +# CONFIG_IO_DELAY_UDELAY is not set
 +# CONFIG_IO_DELAY_NONE is not set
 +# CONFIG_DEBUG_BOOT_PARAMS is not set
 +# CONFIG_CPA_DEBUG is not set
 +# CONFIG_DEBUG_ENTRY is not set
 +# CONFIG_DEBUG_NMI_SELFTEST is not set
 +CONFIG_X86_DEBUG_FPU=y
 +# CONFIG_PUNIT_ATOM_DEBUG is not set
 +CONFIG_UNWINDER_ORC=y
 +# CONFIG_UNWINDER_FRAME_POINTER is not set
 +# CONFIG_UNWINDER_GUESS is not set
 +# end of x86 Debugging
 +
 +#
 +# Kernel Testing and Coverage
 +#
 +# CONFIG_KUNIT is not set
 +# CONFIG_NOTIFIER_ERROR_INJECTION is not set
 +CONFIG_FUNCTION_ERROR_INJECTION=y
 +CONFIG_FAULT_INJECTION=y
 +# CONFIG_FAILSLAB is not set
 +# CONFIG_FAIL_PAGE_ALLOC is not set
 +# CONFIG_FAIL_MAKE_REQUEST is not set
 +# CONFIG_FAIL_IO_TIMEOUT is not set
 +# CONFIG_FAIL_FUTEX is not set
 +CONFIG_FAULT_INJECTION_DEBUG_FS=y
 +CONFIG_FAIL_FUNCTION=y
 +CONFIG_ARCH_HAS_KCOV=y
 +CONFIG_CC_HAS_SANCOV_TRACE_PC=y
 +# CONFIG_KCOV is not set
 +# CONFIG_RUNTIME_TESTING_MENU is not set
 +# CONFIG_MEMTEST is not set
 +# end of Kernel Testing and Coverage
 +# end of Kernel hacking
Simple merge