endif
 
 FEATURE_USER = .libbpf
-FEATURE_TESTS = libelf libelf-mmap zlib bpf reallocarray
+FEATURE_TESTS = libelf libelf-mmap zlib bpf
 FEATURE_DISPLAY = libelf zlib bpf
 
 INCLUDES = -I. -I$(srctree)/tools/include -I$(srctree)/tools/arch/$(ARCH)/include/uapi -I$(srctree)/tools/include/uapi
   override CFLAGS += -DHAVE_LIBELF_MMAP_SUPPORT
 endif
 
-ifeq ($(feature-reallocarray), 0)
-  override CFLAGS += -DCOMPAT_NEED_REALLOCARRAY
-endif
-
 # Append required CFLAGS
 override CFLAGS += $(EXTRA_WARNINGS) -Wno-switch-enum
 override CFLAGS += -Werror -Wall
 
                expand_by = max(btf->types_size >> 2, 16U);
                new_size = min(BTF_MAX_NR_TYPES, btf->types_size + expand_by);
 
-               new_types = realloc(btf->types, sizeof(*new_types) * new_size);
+               new_types = libbpf_reallocarray(btf->types, new_size, sizeof(*new_types));
                if (!new_types)
                        return -ENOMEM;
 
                __u32 *new_list;
 
                d->hypot_cap += max((size_t)16, d->hypot_cap / 2);
-               new_list = realloc(d->hypot_list, sizeof(__u32) * d->hypot_cap);
+               new_list = libbpf_reallocarray(d->hypot_list, d->hypot_cap, sizeof(__u32));
                if (!new_list)
                        return -ENOMEM;
                d->hypot_list = new_list;
                        struct btf_str_ptr *new_ptrs;
 
                        strs.cap += max(strs.cnt / 2, 16U);
-                       new_ptrs = realloc(strs.ptrs,
-                                          sizeof(strs.ptrs[0]) * strs.cap);
+                       new_ptrs = libbpf_reallocarray(strs.ptrs, strs.cap, sizeof(strs.ptrs[0]));
                        if (!new_ptrs) {
                                err = -ENOMEM;
                                goto done;
        d->btf->nr_types = next_type_id - 1;
        d->btf->types_size = d->btf->nr_types;
        d->btf->hdr->type_len = p - types_start;
-       new_types = realloc(d->btf->types,
-                           (1 + d->btf->nr_types) * sizeof(struct btf_type *));
+       new_types = libbpf_reallocarray(d->btf->types, (1 + d->btf->nr_types),
+                                       sizeof(struct btf_type *));
        if (!new_types)
                return -ENOMEM;
        d->btf->types = new_types;
 
 
        if (d->emit_queue_cnt >= d->emit_queue_cap) {
                new_cap = max(16, d->emit_queue_cap * 3 / 2);
-               new_queue = realloc(d->emit_queue,
-                                   new_cap * sizeof(new_queue[0]));
+               new_queue = libbpf_reallocarray(d->emit_queue, new_cap, sizeof(new_queue[0]));
                if (!new_queue)
                        return -ENOMEM;
                d->emit_queue = new_queue;
 
        if (d->decl_stack_cnt >= d->decl_stack_cap) {
                new_cap = max(16, d->decl_stack_cap * 3 / 2);
-               new_stack = realloc(d->decl_stack,
-                                   new_cap * sizeof(new_stack[0]));
+               new_stack = libbpf_reallocarray(d->decl_stack, new_cap, sizeof(new_stack[0]));
                if (!new_stack)
                        return -ENOMEM;
                d->decl_stack = new_stack;
 
 #include <sys/vfs.h>
 #include <sys/utsname.h>
 #include <sys/resource.h>
-#include <tools/libc_compat.h>
 #include <libelf.h>
 #include <gelf.h>
 #include <zlib.h>
        progs = obj->programs;
        nr_progs = obj->nr_programs;
 
-       progs = reallocarray(progs, nr_progs + 1, sizeof(progs[0]));
+       progs = libbpf_reallocarray(progs, nr_progs + 1, sizeof(progs[0]));
        if (!progs) {
                /*
                 * In this case the original obj->programs
                return &obj->maps[obj->nr_maps++];
 
        new_cap = max((size_t)4, obj->maps_cap * 3 / 2);
-       new_maps = realloc(obj->maps, new_cap * sizeof(*obj->maps));
+       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);
                                continue;
                        }
 
-                       sects = reallocarray(sects, nr_sects + 1,
-                                            sizeof(*obj->efile.reloc_sects));
+                       sects = libbpf_reallocarray(sects, nr_sects + 1,
+                                                   sizeof(*obj->efile.reloc_sects));
                        if (!sects) {
                                pr_warn("reloc_sects realloc failed\n");
                                return -ENOMEM;
                        continue;
 
                ext = obj->externs;
-               ext = reallocarray(ext, obj->nr_extern + 1, sizeof(*ext));
+               ext = libbpf_reallocarray(ext, obj->nr_extern + 1, sizeof(*ext));
                if (!ext)
                        return -ENOMEM;
                obj->externs = ext;
                        pr_debug("CO-RE relocating [%d] %s %s: found target candidate [%d] %s %s\n",
                                 local_type_id, btf_kind_str(local_t),
                                 local_name, i, targ_kind, targ_name);
-                       new_ids = reallocarray(cand_ids->data,
-                                              cand_ids->len + 1,
-                                              sizeof(*cand_ids->data));
+                       new_ids = libbpf_reallocarray(cand_ids->data,
+                                                     cand_ids->len + 1,
+                                                     sizeof(*cand_ids->data));
                        if (!new_ids) {
                                err = -ENOMEM;
                                goto err_out;
                        return -LIBBPF_ERRNO__RELOC;
                }
                new_cnt = prog->insns_cnt + text->insns_cnt;
-               new_insn = reallocarray(prog->insns, new_cnt, sizeof(*insn));
+               new_insn = libbpf_reallocarray(prog->insns, new_cnt, sizeof(*insn));
                if (!new_insn) {
                        pr_warn("oom in prog realloc\n");
                        return -ENOMEM;
                moff /= bpf_ptr_sz;
                if (moff >= map->init_slots_sz) {
                        new_sz = moff + 1;
-                       tmp = realloc(map->init_slots, new_sz * host_ptr_sz);
+                       tmp = libbpf_reallocarray(map->init_slots, new_sz, host_ptr_sz);
                        if (!tmp)
                                return -ENOMEM;
                        map->init_slots = tmp;
 
 #ifndef __LIBBPF_LIBBPF_INTERNAL_H
 #define __LIBBPF_LIBBPF_INTERNAL_H
 
+#include <stdlib.h>
 #include "libbpf.h"
 
 #define BTF_INFO_ENC(kind, kind_flag, vlen) \
 #define BTF_PARAM_ENC(name, type) (name), (type)
 #define BTF_VAR_SECINFO_ENC(type, offset, size) (type), (offset), (size)
 
+#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
 #define pr_info(fmt, ...)      __pr(LIBBPF_INFO, fmt, ##__VA_ARGS__)
 #define pr_debug(fmt, ...)     __pr(LIBBPF_DEBUG, fmt, ##__VA_ARGS__)
 
+/*
+ * 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 (unlikely(__builtin_mul_overflow(nmemb, size, &total)))
+               return NULL;
+       return realloc(ptr, total);
+}
+
 static inline bool libbpf_validate_opts(const char *opts,
                                        size_t opts_sz, size_t user_sz,
                                        const char *type_name)
 
 #include <asm/barrier.h>
 #include <sys/mman.h>
 #include <sys/epoll.h>
-#include <tools/libc_compat.h>
 
 #include "libbpf.h"
 #include "libbpf_internal.h"
                return -EINVAL;
        }
 
-       tmp = reallocarray(rb->rings, rb->ring_cnt + 1, sizeof(*rb->rings));
+       tmp = libbpf_reallocarray(rb->rings, rb->ring_cnt + 1, sizeof(*rb->rings));
        if (!tmp)
                return -ENOMEM;
        rb->rings = tmp;
 
-       tmp = reallocarray(rb->events, rb->ring_cnt + 1, sizeof(*rb->events));
+       tmp = libbpf_reallocarray(rb->events, rb->ring_cnt + 1, sizeof(*rb->events));
        if (!tmp)
                return -ENOMEM;
        rb->events = tmp;