root/tools/perf/util/aslr.c
// SPDX-License-Identifier: GPL-2.0
#include "aslr.h"

#include "addr_location.h"
#include "debug.h"
#include "event.h"
#include "evsel.h"
#include "evlist.h"
#include "machine.h"
#include "map.h"
#include "thread.h"
#include "tool.h"
#include "session.h"
#include "data.h"
#include "dso.h"
#include "pmus.h"

#include <internal/lib.h>  /* page_size */
#include <linux/compiler.h>
#include <linux/zalloc.h>
#include <errno.h>
#include <inttypes.h>
#include <unistd.h>
#include <byteswap.h>

/**
 * struct remap_addresses_key - Key for mapping original addresses to remapped ones.
 * @dso: Pointer to the DSO (Dynamic Shared Object) associated with the mapping.
 * @invariant: Unique offset invariant within the VMA (Virtual Memory Area).
 *             Calculated as `start - pgoff`. This value remains constant when
 *             perf's internal `maps__fixup_overlap_and_insert` splits a map into
 *             fragmented VMA pieces due to overlapping events, allowing us to
 *             resolve split maps consistently back to the original VMA.
 * @pid: Process ID associated with the mapping.
 */
struct remap_addresses_key {
        struct machine *machine;
        struct dso *dso;
        u64 invariant;
        pid_t pid;
};

struct aslr_mapping {
        struct list_head node;
        u64 orig_start;
        u64 len;
        u64 remap_start;
};

struct aslr_evsel_priv {
        u64 orig_sample_type;
        u64 orig_sample_regs_user;
        u64 orig_sample_regs_intr;
        int orig_sample_size;
};

static size_t evsel_hash(long key, void *ctx __maybe_unused)
{
        return (size_t)key;
}

static bool evsel_equal(long key1, long key2, void *ctx __maybe_unused)
{
        return key1 == key2;
}

struct process_top_address {
        u64 remapped_max;
};
struct aslr_tool {
        /** @tool: The tool implemented here and a pointer to a delegate to process the data. */
        struct delegate_tool tool;
        /** @machines: The machines with the input, not remapped, virtual address layout. */
        struct machines machines;
        /** @event_copy: Buffer used to create an event to pass to the delegate. */
        char event_copy[PERF_SAMPLE_MAX_SIZE] __aligned(8);
        /** @remap_addresses: mapping from remap_addresses_key to remapped address. */
        struct hashmap remap_addresses;
        /** @top_addresses: mapping from process to max remapped address. */
        struct hashmap top_addresses;
        /**
         * @evsel_orig_attrs: mapping from evsel pointer to its original
         *                    unstripped sample_type and registers bitmasks.
         */
        struct hashmap evsel_orig_attrs;
};

static const pid_t kernel_pid = -1;

/* Start remapping user processes from a small non-zero offset. */
static const u64 user_space_start = 0x200000;
static const u64 kernel_space_start_64 = 0xffff800010000000ULL;
static const u64 kernel_space_start_32 = 0x80000000ULL;

static size_t remap_addresses__hash(long _key, void *ctx __maybe_unused)
{
        struct remap_addresses_key *key = (struct remap_addresses_key *)_key;
        void *dso_ptr = key->dso ? RC_CHK_ACCESS(key->dso) : NULL;

        return (size_t)key->machine ^ (size_t)dso_ptr ^ key->invariant ^ key->pid;
}

static bool remap_addresses__equal(long _key1, long _key2, void *ctx __maybe_unused)
{
        struct remap_addresses_key *key1 = (struct remap_addresses_key *)_key1;
        struct remap_addresses_key *key2 = (struct remap_addresses_key *)_key2;

        return key1->machine == key2->machine &&
               RC_CHK_EQUAL(key1->dso, key2->dso) &&
               key1->invariant == key2->invariant &&
               key1->pid == key2->pid;
}

struct top_addresses_key {
        struct machine *machine;
        pid_t pid;
};

static size_t top_addresses__hash(long _key, void *ctx __maybe_unused)
{
        struct top_addresses_key *key = (struct top_addresses_key *)_key;

        return (size_t)key->machine ^ key->pid;
}

static bool top_addresses__equal(long _key1, long _key2, void *ctx __maybe_unused)
{
        struct top_addresses_key *key1 = (struct top_addresses_key *)_key1;
        struct top_addresses_key *key2 = (struct top_addresses_key *)_key2;

        return key1->machine == key2->machine && key1->pid == key2->pid;
}

static u64 round_up_to_page_size(u64 addr)
{
        return (addr + page_size - 1) & ~((u64)page_size - 1);
}

static u64 aslr_tool__remap_address(struct aslr_tool *aslr,
                                    struct thread *aslr_thread,
                                    u8 cpumode,
                                    u64 addr)
{
        struct addr_location al;
        struct remap_addresses_key key;
        u64 *remapped_invariant_ptr = NULL;
        u64 remap_addr = 0;
        u8 effective_cpumode = cpumode;
        struct dso *dso;
        const char *dso_name;

        if (!aslr_thread)
                return 0; /* No thread. */

        addr_location__init(&al);
        if (!thread__find_map(aslr_thread, cpumode, addr, &al)) {
                /*
                 * If lookup fails with specified cpumode, try fallback to the other space
                 * to be robust against bad cpumode in samples.
                 */
                if (cpumode == PERF_RECORD_MISC_KERNEL)
                        effective_cpumode = PERF_RECORD_MISC_USER;
                else if (cpumode == PERF_RECORD_MISC_USER)
                        effective_cpumode = PERF_RECORD_MISC_KERNEL;
                else if (cpumode == PERF_RECORD_MISC_GUEST_KERNEL)
                        effective_cpumode = PERF_RECORD_MISC_GUEST_USER;
                else if (cpumode == PERF_RECORD_MISC_GUEST_USER)
                        effective_cpumode = PERF_RECORD_MISC_GUEST_KERNEL;

                if (!thread__find_map(aslr_thread, effective_cpumode, addr, &al)) {
                        addr_location__exit(&al);
                        return 0; /* No mmap. */
                }
        }

        dso = map__dso(al.map);
        dso_name = dso ? dso__long_name(dso) : NULL;

        key.machine = maps__machine(thread__maps(aslr_thread));
        key.dso = dso;
        if (dso && !is_anon_memory(dso_name) && !is_no_dso_memory(dso_name))
                key.invariant = map__start(al.map) - map__pgoff(al.map);
        else
                key.invariant = map__start(al.map);
        key.pid = (effective_cpumode == PERF_RECORD_MISC_KERNEL ||
                   effective_cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ?
                  kernel_pid : thread__pid(aslr_thread);

        if (hashmap__find(&aslr->remap_addresses, &key, &remapped_invariant_ptr)) {
                remap_addr = *remapped_invariant_ptr + map__pgoff(al.map) +
                             (addr - map__start(al.map));
        } else {
                pr_debug("Cannot find a remapped entry for address %" PRIx64 " in mapping %" PRIx64 "(%zu) for pid=%d\n",
                         addr, map__start(al.map), map__size(al.map), key.pid);
        }

        addr_location__exit(&al);
        return remap_addr;
}

struct aslr_machine_priv {
        bool kernel_maps_loaded;
};

static int aslr_tool__preload_kernel_maps(struct machine *machine)
{
        struct aslr_machine_priv *mpriv = machine->priv;

        if (!mpriv) {
                mpriv = zalloc(sizeof(*mpriv));
                if (!mpriv)
                        return -ENOMEM;
                machine->priv = mpriv;
        }

        if (!mpriv->kernel_maps_loaded) {
                struct maps *kmaps = machine__kernel_maps(machine);

                if (kmaps) {
                        int err = maps__load_maps(kmaps);

                        if (err < 0) {
                                pr_err("ASLR: Failed to preload kernel maps for machine pid %d\n",
                                       machine->pid);
                                return err;
                        }
                }
                mpriv->kernel_maps_loaded = true;
        }
        return 0;
}

static void aslr_tool__free_machine_priv(struct machine *machine)
{
        free(machine->priv);
        machine->priv = NULL;
}

static void aslr_tool__destroy_machines_priv(struct machines *machines)
{
        struct rb_node *nd;

        aslr_tool__free_machine_priv(&machines->host);
        for (nd = rb_first_cached(&machines->guests); nd; nd = rb_next(nd)) {
                struct machine *machine = rb_entry(nd, struct machine, rb_node);

                aslr_tool__free_machine_priv(machine);
        }
}

static u64 aslr_tool__findnew_mapping(struct aslr_tool *aslr,
                                      struct machine *session_machine,
                                      struct thread *aslr_thread,
                                      u8 cpumode, u64 start,
                                      u64 len, u64 pgoff)
{
        /* Address location for dso lookup. */
        struct addr_location al;
        /* Original ASLR address based key for the remap table. */
        struct remap_addresses_key remap_key;
        /* The address in the ASLR sanitized address space less pg_off. */
        u64 *remapped_invariant_ptr;
        /* Key for the maximum address in a process. */
        struct top_addresses_key top_addr_key;
        /* Value in top address table. */
        struct process_top_address *top = NULL;
        /* Address in ASLR sanitized address space. */
        u64 remap_addr;
        /* Potentially allocated remap table key. */
        struct remap_addresses_key *new_remap_key = NULL;
        /*
         * Potentially allocated remap table key.
         * TODO: Avoid allocation necessary for perf 32-bit binary support.
         */
        u64 *new_remap_val = NULL;
        int err;

        if (!aslr_thread)
                return 0;

        /* The key to look up an incoming address to the outgoing value. */
        addr_location__init(&al);
        remap_key.machine = maps__machine(thread__maps(aslr_thread));
        remap_key.pid = (cpumode == PERF_RECORD_MISC_KERNEL ||
                         cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ?
                        kernel_pid : thread__pid(aslr_thread);
        if (thread__find_map(aslr_thread, cpumode, start, &al)) {
                struct dso *dso = map__dso(al.map);
                const char *dso_name = dso ? dso__long_name(dso) : NULL;

                remap_key.dso = dso;
                if (dso && !is_anon_memory(dso_name) && !is_no_dso_memory(dso_name))
                        remap_key.invariant = map__start(al.map) - map__pgoff(al.map);
                else
                        remap_key.invariant = map__start(al.map);
        } else {
                remap_key.dso = NULL;
                remap_key.invariant = start;
        }

        /* The key to look up top allocated address. */
        top_addr_key.machine = remap_key.machine;
        top_addr_key.pid = remap_key.pid;

        if (hashmap__find(&aslr->remap_addresses, &remap_key, &remapped_invariant_ptr)) {
                /* Mmap already exists. */
                u64 calculated_max;

                if (al.map) {
                        /*
                         * The cached value is the base of the invariant. We add the
                         * offset into the VMA (start - map__start), plus the map's
                         * pgoff, to get the precise virtual address within this chunk.
                         */
                        remap_addr = *remapped_invariant_ptr + map__pgoff(al.map) +
                                     (start - map__start(al.map));
                } else {
                        /*
                         * For unmapped memory (e.g. kernel anonymous), the cached value
                         * was stored offset by pgoff. Adding pgoff yields the true remap_addr.
                         */
                        remap_addr = *remapped_invariant_ptr + pgoff;
                }

                calculated_max = remap_addr + len;

                /* See if top mapping was expanded. */
                if (hashmap__find(&aslr->top_addresses, &top_addr_key, &top)) {
                        if (calculated_max > top->remapped_max)
                                top->remapped_max = calculated_max;
                }
                addr_location__exit(&al);
                return remap_addr;
        }
        /* No mmap, create an entry from the top address. */
        if (hashmap__find(&aslr->top_addresses, &top_addr_key, &top)) {
                struct addr_location prev_al;
                bool is_contiguous = false;

                /* Current max allocated mmap address within the process. */
                remap_addr = top->remapped_max;

                addr_location__init(&prev_al);
                if (thread__find_map(aslr_thread, cpumode, start - 1, &prev_al)) {
                        if (map__end(prev_al.map) == start)
                                is_contiguous = true;
                }
                addr_location__exit(&prev_al);

                if (is_contiguous) {
                        /* Contiguous mapping, do not add 1 page gap! */
                        remap_addr = round_up_to_page_size(remap_addr);
                } else {
                        /* Give 1 page gap from current max page. */
                        remap_addr = round_up_to_page_size(remap_addr);
                        remap_addr += page_size;
                }
                if (remap_addr + len > top->remapped_max)
                        top->remapped_max = remap_addr + len;
        } else {
                /* First address of the process, allocate key and first top address. */
                struct top_addresses_key *tk;
                struct process_top_address *top_val;
                struct perf_env *env = session_machine ? session_machine->env : NULL;
                bool is_64 = env ? perf_env__kernel_is_64_bit(env) : (sizeof(void *) == 8);
                u64 kernel_start_addr = is_64 ? kernel_space_start_64 : kernel_space_start_32;

                remap_addr = (cpumode == PERF_RECORD_MISC_KERNEL ||
                              cpumode == PERF_RECORD_MISC_GUEST_KERNEL) ?
                             kernel_start_addr : user_space_start;
                remap_addr = round_up_to_page_size(remap_addr);

                tk = malloc(sizeof(*tk));
                top_val = malloc(sizeof(*top_val));
                if (!tk || !top_val) {
                        err = -ENOMEM;
                } else {
                        *tk = top_addr_key;
                        top_val->remapped_max = remap_addr + len;
                        err = hashmap__insert(&aslr->top_addresses, tk, top_val,
                                              HASHMAP_ADD, NULL, NULL);
                }
                if (err) {
                        errno = -err;
                        pr_err("Failure to add ASLR process top address %m\n");
                        free(tk);
                        free(top_val);
                        addr_location__exit(&al);
                        return 0;
                }
        }
        /* Create rmeapping entry. */
        new_remap_key = malloc(sizeof(*new_remap_key));
        new_remap_val = malloc(sizeof(u64));
        if (!new_remap_key || !new_remap_val) {
                err = -ENOMEM;
        } else {
                *new_remap_key = remap_key;
                new_remap_key->dso = dso__get(remap_key.dso);
                if (cpumode == PERF_RECORD_MISC_KERNEL ||
                    cpumode == PERF_RECORD_MISC_GUEST_KERNEL) {
                        if (al.map) {
                                *new_remap_val = remap_addr -
                                                 (start - map__start(al.map)) -
                                                 map__pgoff(al.map);
                        } else {
                                /*
                                 * Subtract pgoff from the base virtual address so that
                                 * when the lookup path adds pgoff back, it perfectly
                                 * cancels out and returns remap_addr.
                                 */
                                *new_remap_val = remap_addr - pgoff;
                        }
                } else {
                        *new_remap_val = remap_addr - (al.map ? (start - map__start(al.map)) +
                                                            map__pgoff(al.map) : pgoff);
                }
                err = hashmap__add(&aslr->remap_addresses, new_remap_key, new_remap_val);
                if (err)
                        dso__put(new_remap_key->dso);
        }
        if (err) {
                errno = -err;
                pr_err("Failure to add ASLR remapping %m\n");
                free(new_remap_key);
                free(new_remap_val);
                addr_location__exit(&al);
                return 0;
        }
        addr_location__exit(&al);
        return remap_addr;
}

static int aslr_tool__process_mmap(const struct perf_tool *tool,
                                   union perf_event *event,
                                   struct perf_sample *sample,
                                   struct machine *machine)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        union perf_event *new_event;
        u8 cpumode;
        struct thread *thread;
        struct machine *aslr_machine;
        int err;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;
        new_event = (union perf_event *)aslr->event_copy;
        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        /* Create the thread, map, etc. in the ASLR before virtual address space. */
        err = perf_event__process_mmap(tool, event, sample, aslr_machine);
        if (err)
                return err;

        thread = machine__findnew_thread(aslr_machine, event->mmap.pid, event->mmap.tid);
        if (!thread)
                return -ENOMEM;
        memcpy(&new_event->mmap, &event->mmap, event->mmap.header.size);
        /* Remaps the mmap.start. */
        new_event->mmap.start = aslr_tool__findnew_mapping(aslr, machine, thread, cpumode,
                                                           event->mmap.start,
                                                           event->mmap.len,
                                                           event->mmap.pgoff);
        /*
         * For anonymous memory (and kernel maps), the kernel populates the
         * event's pgoff field with the original un-obfuscated virtual address
         * in bytes (i.e. (addr >> PAGE_SHIFT) << PAGE_SHIFT).
         * We must overwrite pgoff with the new remapped byte address to prevent
         * leaking the original ASLR layout.
         */
        if (is_anon_memory(event->mmap.filename) || is_no_dso_memory(event->mmap.filename) ||
            ((cpumode == PERF_RECORD_MISC_KERNEL || cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
             !is_kernel_module(event->mmap.filename, cpumode)))
                new_event->mmap.pgoff = new_event->mmap.start;
        err = delegate->mmap(delegate, new_event, sample, machine);
        thread__put(thread);
        return err;
}

static int aslr_tool__process_mmap2(const struct perf_tool *tool,
                                    union perf_event *event,
                                    struct perf_sample *sample,
                                    struct machine *machine)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        union perf_event *new_event;
        u8 cpumode;
        struct thread *thread;
        struct machine *aslr_machine;
        int err;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;
        new_event = (union perf_event *)aslr->event_copy;
        cpumode = event->header.misc & PERF_RECORD_MISC_CPUMODE_MASK;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        /* Create the thread, map, etc. in the ASLR before virtual address space. */
        err = perf_event__process_mmap2(tool, event, sample, aslr_machine);
        if (err)
                return err;

        thread = machine__findnew_thread(aslr_machine, event->mmap2.pid, event->mmap2.tid);
        if (!thread)
                return -ENOMEM;
        memcpy(&new_event->mmap2, &event->mmap2, event->mmap2.header.size);
        /* Remaps the mmap.start. */
        new_event->mmap2.start = aslr_tool__findnew_mapping(aslr, machine, thread, cpumode,
                                                            event->mmap2.start,
                                                            event->mmap2.len,
                                                            event->mmap2.pgoff);
        /*
         * For anonymous memory (and kernel maps), the kernel populates the
         * event's pgoff field with the original un-obfuscated virtual address
         * in bytes (i.e. (addr >> PAGE_SHIFT) << PAGE_SHIFT).
         * We must overwrite pgoff with the new remapped byte address to prevent
         * leaking the original ASLR layout.
         */
        if (is_anon_memory(event->mmap2.filename) || is_no_dso_memory(event->mmap2.filename) ||
            ((cpumode == PERF_RECORD_MISC_KERNEL || cpumode == PERF_RECORD_MISC_GUEST_KERNEL) &&
             !is_kernel_module(event->mmap2.filename, cpumode)))
                new_event->mmap2.pgoff = new_event->mmap2.start;
        err = delegate->mmap2(delegate, new_event, sample, machine);
        thread__put(thread);
        return err;
}

static int aslr_tool__process_comm(const struct perf_tool *tool,
                                   union perf_event *event,
                                   struct perf_sample *sample,
                                   struct machine *machine)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        struct machine *aslr_machine;
        int err;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        /* Create the thread, map, etc. in the ASLR before virtual address space. */
        err = perf_event__process_comm(tool, event, sample, aslr_machine);
        if (err)
                return err;

        return delegate->comm(delegate, event, sample, machine);
}

static int aslr_tool__process_fork(const struct perf_tool *tool,
                                   union perf_event *event,
                                   struct perf_sample *sample,
                                   struct machine *machine)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        struct machine *aslr_machine;
        int err;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        /* Create the thread, map, etc. in the ASLR before virtual address space. */
        err = perf_event__process_fork(tool, event, sample, aslr_machine);
        if (err)
                return err;

        return delegate->fork(delegate, event, sample, machine);
}

static int aslr_tool__process_exit(const struct perf_tool *tool,
                                   union perf_event *event,
                                   struct perf_sample *sample,
                                   struct machine *machine)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        struct machine *aslr_machine;
        int err;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        /* Create the thread, map, etc. in the ASLR before virtual address space. */
        err = perf_event__process_exit(tool, event, sample, aslr_machine);
        if (err)
                return err;

        return delegate->exit(delegate, event, sample, machine);
}

static int aslr_tool__process_text_poke(const struct perf_tool *tool __maybe_unused,
                                        union perf_event *event __maybe_unused,
                                        struct perf_sample *sample __maybe_unused,
                                        struct machine *machine __maybe_unused)
{
        /* Drop in case the instruction encodes an ASLR revealing address. */
        return 0;
}

static int aslr_tool__process_ksymbol(const struct perf_tool *tool,
                                      union perf_event *event,
                                      struct perf_sample *sample,
                                      struct machine *machine)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        union perf_event *new_event;
        struct thread *thread;
        struct machine *aslr_machine;
        bool is_unregister;
        int err;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;
        new_event = (union perf_event *)aslr->event_copy;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        thread = machine__findnew_thread(aslr_machine, kernel_pid, 0);
        if (!thread)
                return -ENOMEM;

        is_unregister = (event->ksymbol.flags & PERF_RECORD_KSYMBOL_FLAGS_UNREGISTER);

        memcpy(&new_event->ksymbol, &event->ksymbol, event->ksymbol.header.size);

        if (is_unregister) {
                new_event->ksymbol.addr = aslr_tool__findnew_mapping(aslr, machine, thread,
                                                                     PERF_RECORD_MISC_KERNEL,
                                                                     event->ksymbol.addr,
                                                                     event->ksymbol.len,
                                                                     /*pgoff=*/0);
                err = perf_event__process_ksymbol(tool, event, sample, aslr_machine);
        } else {
                err = perf_event__process_ksymbol(tool, event, sample, aslr_machine);
                new_event->ksymbol.addr = aslr_tool__findnew_mapping(aslr, machine, thread,
                                                                     PERF_RECORD_MISC_KERNEL,
                                                                     event->ksymbol.addr,
                                                                     event->ksymbol.len,
                                                                     /*pgoff=*/0);
        }
        if (err) {
                thread__put(thread);
                return err;
        }

        err = delegate->ksymbol(delegate, new_event, sample, machine);
        thread__put(thread);
        return err;
}

static int aslr_tool__process_sample(const struct perf_tool *tool,
                                     union perf_event *event,
                                     struct perf_sample *sample,
                                     struct machine *machine)
{
        struct evsel *evsel = sample->evsel;
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct perf_tool *delegate;
        int ret;
        int orig_sample_size;
        u64 sample_type;
        struct thread *thread;
        struct machine *aslr_machine;
        __u64 max_i;
        __u64 max_j;
        union perf_event *new_event;
        struct perf_sample new_sample;
        __u64 *in_array, *out_array;
        u8 cpumode;
        u64 addr;
        size_t i;
        size_t j;
        struct aslr_evsel_priv *priv = NULL;
        u64 orig_sample_type;
        u64 orig_regs_user;
        u64 orig_regs_intr;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);
        delegate = aslr->tool.delegate;

        if (evsel__is_dummy_event(evsel))
                return delegate->sample(delegate, event, sample, machine);

        ret = -EFAULT;

        if (hashmap__find(&aslr->evsel_orig_attrs, evsel, &priv)) {
                orig_sample_type = priv->orig_sample_type;
                orig_regs_user = priv->orig_sample_regs_user;
                orig_regs_intr = priv->orig_sample_regs_intr;
        } else {
                orig_sample_type = evsel->core.attr.sample_type;
                orig_regs_user = evsel->core.attr.sample_regs_user;
                orig_regs_intr = evsel->core.attr.sample_regs_intr;
        }

        orig_sample_size = evsel->sample_size;

        sample_type = orig_sample_type;
        sample_type &= ~PERF_SAMPLE_REGS_USER;
        sample_type &= ~PERF_SAMPLE_REGS_INTR;
        sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE;

        max_i = (event->header.size - sizeof(struct perf_event_header)) / sizeof(__u64);
        max_j = (PERF_SAMPLE_MAX_SIZE - sizeof(struct perf_event_header)) / sizeof(__u64);
        new_event = (union perf_event *)aslr->event_copy;
        cpumode = sample->cpumode;
        i = 0;
        j = 0;

        aslr_machine = machines__findnew(&aslr->machines, machine->pid);
        if (!aslr_machine)
                return -ENOMEM;
        if (aslr_tool__preload_kernel_maps(aslr_machine) < 0)
                return -ENOMEM;

        thread = machine__findnew_thread(aslr_machine, sample->pid, sample->tid);

        if (!thread)
                return -ENOMEM;

        if (max_i > PERF_SAMPLE_MAX_SIZE / sizeof(u64))
                goto out_put;

        new_event->sample.header = event->sample.header;

        in_array = &event->sample.array[0];
        out_array = &new_event->sample.array[0];

#define CHECK_BOUNDS(required_i, required_j) \
        (i + (required_i) > max_i || j + (required_j) > max_j)

#define COPY_U64() \
        do { \
                if (CHECK_BOUNDS(1, 1)) { \
                        ret = -EFAULT; \
                        goto out_put; \
                } \
                out_array[j++] = in_array[i++]; \
        } while (0)

#define REMAP_U64(addr_field) \
        do { \
                u64 remapped; \
                if (CHECK_BOUNDS(1, 1)) { \
                        ret = -EFAULT; \
                        goto out_put; \
                } \
                remapped = aslr_tool__remap_address(aslr, thread, cpumode, addr_field); \
                out_array[j++] = remapped; \
                i++; \
        } while (0)

        if (orig_sample_type & PERF_SAMPLE_IDENTIFIER)
                COPY_U64(); /* id */
        if (orig_sample_type & PERF_SAMPLE_IP)
                REMAP_U64(sample->ip);
        if (orig_sample_type & PERF_SAMPLE_TID) {
                union {
                        u64 val64;
                        u32 val32[2];
                } u;

                if (CHECK_BOUNDS(1, 1)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                u.val32[0] = sample->pid;
                u.val32[1] = sample->tid;
                out_array[j++] = u.val64;
                i++;
        }
        if (orig_sample_type & PERF_SAMPLE_TIME)
                COPY_U64(); /* time */
        if (orig_sample_type & PERF_SAMPLE_ADDR)
                REMAP_U64(sample->addr);
        if (orig_sample_type & PERF_SAMPLE_ID)
                COPY_U64(); /* id */
        if (orig_sample_type & PERF_SAMPLE_STREAM_ID)
                COPY_U64(); /* stream_id */
        if (orig_sample_type & PERF_SAMPLE_CPU)
                COPY_U64(); /* cpu, res */
        if (orig_sample_type & PERF_SAMPLE_PERIOD)
                COPY_U64(); /* period */
        if (orig_sample_type & PERF_SAMPLE_READ) {
                if ((evsel->core.attr.read_format & PERF_FORMAT_GROUP) == 0) {
                        COPY_U64(); /* value */
                        if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
                                COPY_U64(); /* time_enabled */
                        if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
                                COPY_U64(); /* time_running */
                        if (evsel->core.attr.read_format & PERF_FORMAT_ID)
                                COPY_U64(); /* id */
                        if (evsel->core.attr.read_format & PERF_FORMAT_LOST)
                                COPY_U64(); /* lost */
                } else {
                        u64 nr;

                        if (CHECK_BOUNDS(1, 1)) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        nr = in_array[i];
                        COPY_U64();
                        if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_ENABLED)
                                COPY_U64(); /* time_enabled */
                        if (evsel->core.attr.read_format & PERF_FORMAT_TOTAL_TIME_RUNNING)
                                COPY_U64(); /* time_running */
                        for (u64 cntr = 0; cntr < nr; cntr++) {
                                COPY_U64(); /* value */
                                if (evsel->core.attr.read_format & PERF_FORMAT_ID)
                                        COPY_U64(); /* id */
                                if (evsel->core.attr.read_format & PERF_FORMAT_LOST)
                                        COPY_U64(); /* lost */
                        }
                }
        }
        if (orig_sample_type & PERF_SAMPLE_CALLCHAIN) {
                u64 nr;

                if (CHECK_BOUNDS(1, 1)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                nr = in_array[i];
                COPY_U64();

                for (u64 cntr = 0; cntr < nr; cntr++) {
                        if (CHECK_BOUNDS(1, 1)) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        addr = in_array[i++];
                        if (addr >= PERF_CONTEXT_MAX) {
                                out_array[j++] = addr;
                                switch (addr) {
                                case PERF_CONTEXT_HV:
                                        cpumode = PERF_RECORD_MISC_HYPERVISOR;
                                        break;
                                case PERF_CONTEXT_KERNEL:
                                        cpumode = PERF_RECORD_MISC_KERNEL;
                                        break;
                                case PERF_CONTEXT_USER:
                                        cpumode = PERF_RECORD_MISC_USER;
                                        break;
                                case PERF_CONTEXT_GUEST:
                                        cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
                                        break;
                                case PERF_CONTEXT_GUEST_KERNEL:
                                        cpumode = PERF_RECORD_MISC_GUEST_KERNEL;
                                        break;
                                case PERF_CONTEXT_GUEST_USER:
                                        cpumode = PERF_RECORD_MISC_GUEST_USER;
                                        break;
                                case PERF_CONTEXT_USER_DEFERRED:
                                        if (cntr + 1 >= nr) {
                                                pr_debug("Truncated callchain deferred cookie context\n");
                                                ret = 0;
                                                goto out_put;
                                        }
                                        /*
                                         * Immediately followed by a 64-bit
                                         * stitching cookie. Skip/Copy it!
                                         */
                                        if (CHECK_BOUNDS(1, 1)) {
                                                ret = -EFAULT;
                                                goto out_put;
                                        }
                                        out_array[j++] = in_array[i++];
                                        cntr++;
                                        cpumode = PERF_RECORD_MISC_USER;
                                        break;
                                default:
                                        pr_debug("invalid callchain context: %"PRIx64"\n", addr);
                                        ret = 0;
                                        goto out_put;
                                }
                                continue;
                        }
                        addr = aslr_tool__remap_address(aslr, thread, cpumode, addr);
                        out_array[j++] = addr;
                }
        }
        if (orig_sample_type & PERF_SAMPLE_RAW) {
                size_t bytes = sizeof(u32) + sample->raw_size;
                size_t u64_words = (bytes + 7) / 8;

                if (i + u64_words > max_i || j + u64_words > max_j) {
                        ret = -EFAULT;
                        goto out_put;
                }
                memcpy(&out_array[j], &in_array[i], bytes);
                i += u64_words;
                j += u64_words;
                /*
                 * TODO: certain raw samples can be remapped, such as
                 * tracepoints by examining their fields.
                 */
                pr_debug("Dropping raw samples as possible ASLR leak\n");
                ret = 0;
                goto out_put;
        }
        if (orig_sample_type & PERF_SAMPLE_BRANCH_STACK) {
                u64 nr;

                if (CHECK_BOUNDS(1, 1)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                nr = in_array[i];
                COPY_U64();

                if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_HW_INDEX)
                        COPY_U64(); /* hw_idx */

                if (nr > (ULLONG_MAX / 3)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                if (nr * 3 > max_i - i || nr * 3 > max_j - j) {
                        ret = -EFAULT;
                        goto out_put;
                }
                for (u64 cntr = 0; cntr < nr; cntr++) {
                        u64 from = in_array[i++];
                        u64 to = in_array[i++];

                        from = aslr_tool__remap_address(aslr, thread, sample->cpumode, from);
                        to = aslr_tool__remap_address(aslr, thread, sample->cpumode, to);

                        out_array[j++] = from;
                        out_array[j++] = to;
                        out_array[j++] = in_array[i++]; /* flags */
                }
                if (evsel->core.attr.branch_sample_type & PERF_SAMPLE_BRANCH_COUNTERS) {
                        if (nr > max_i - i || nr > max_j - j) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        for (u64 cntr = 0; cntr < nr; cntr++)
                                COPY_U64();
                }
        }
        if (orig_sample_type & PERF_SAMPLE_REGS_USER) {
                u64 abi;

                if (CHECK_BOUNDS(1, 0)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                abi = in_array[i++];
                if (abi != PERF_SAMPLE_REGS_ABI_NONE) {
                        u64 nr = hweight64(orig_regs_user);

                        if (nr > max_i - i) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        i += nr;
                }
        }
        if (orig_sample_type & PERF_SAMPLE_STACK_USER) {
                u64 size;

                if (CHECK_BOUNDS(1, 1)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                size = in_array[i];
                COPY_U64();
                if (size > 0) {
                        size_t u64_words = size / 8 + (size % 8 ? 1 : 0);

                        if (u64_words > max_i - i || u64_words > max_j - j) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        memcpy(&out_array[j], &in_array[i], size);
                        if (size % 8) {
                                size_t pad = 8 - (size % 8);

                                memset(((char *)&out_array[j]) + size, 0, pad);
                        }
                        i += u64_words;
                        j += u64_words;
                }
                /* TODO: can this be less conservative? */
                pr_debug("Dropping stack user sample as possible ASLR leak\n");
                ret = 0;
                goto out_put;
        }
        if (orig_sample_type & PERF_SAMPLE_WEIGHT_TYPE)
                COPY_U64(); /* perf_sample_weight */
        if (orig_sample_type & PERF_SAMPLE_DATA_SRC)
                COPY_U64(); /* data_src */
        if (orig_sample_type & PERF_SAMPLE_TRANSACTION)
                COPY_U64(); /* transaction */
        if (orig_sample_type & PERF_SAMPLE_REGS_INTR) {
                u64 abi;

                if (CHECK_BOUNDS(1, 0)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                abi = in_array[i++];
                if (abi != PERF_SAMPLE_REGS_ABI_NONE) {
                        u64 nr = hweight64(orig_regs_intr);

                        if (nr > max_i - i) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        i += nr;
                }
        }
        if (orig_sample_type & PERF_SAMPLE_PHYS_ADDR) {
                COPY_U64(); /* phys_addr */
                /* TODO: can this be less conservative? */
                pr_debug("Dropping physical address sample as possible ASLR leak\n");
                ret = 0;
                goto out_put;
        }
        if (orig_sample_type & PERF_SAMPLE_CGROUP)
                COPY_U64(); /* cgroup */
        if (orig_sample_type & PERF_SAMPLE_DATA_PAGE_SIZE)
                COPY_U64(); /* data_page_size */
        if (orig_sample_type & PERF_SAMPLE_CODE_PAGE_SIZE)
                COPY_U64(); /* code_page_size */

        if (orig_sample_type & PERF_SAMPLE_AUX) {
                u64 size;

                if (CHECK_BOUNDS(1, 1)) {
                        ret = -EFAULT;
                        goto out_put;
                }
                out_array[j] = in_array[i];
                size = out_array[j++];
                i++;
                if (size > 0) {
                        size_t u64_words = size / 8 + (size % 8 ? 1 : 0);

                        if (u64_words > max_i - i || u64_words > max_j - j) {
                                ret = -EFAULT;
                                goto out_put;
                        }
                        memcpy(&out_array[j], &in_array[i], size);
                        if (size % 8) {
                                size_t pad = 8 - (size % 8);

                                memset(((char *)&out_array[j]) + size, 0, pad);
                        }
                        i += u64_words;
                        j += u64_words;
                }
                /* TODO: can this be less conservative? */
                pr_debug("Dropping aux sample as possible ASLR leak\n");
                ret = 0;
                goto out_put;
        }

        if (evsel__is_offcpu_event(evsel)) {
                /* TODO: can this be less conservative? */
                pr_debug("Dropping off-CPU sample as possible ASLR leak\n");
                ret = 0;
                goto out_put;
        }

        new_event->sample.header.size = sizeof(struct perf_event_header) + j * sizeof(u64);
        /* Temporarily override evsel attributes to match the stripped new_event format! */
        evsel->sample_size = __evsel__sample_size(sample_type);
        evsel->core.attr.sample_type = sample_type;
        evsel->core.attr.sample_regs_user = 0;
        evsel->core.attr.sample_regs_intr = 0;
        perf_sample__init(&new_sample, /*all=*/ true);
        ret = __evsel__parse_sample(evsel, new_event, &new_sample, /*needs_swap=*/false);

        if (ret) {
                /* Restore original attributes immediately if parsing fails */
                evsel->sample_size = orig_sample_size;
                evsel->core.attr.sample_type = orig_sample_type;
                evsel->core.attr.sample_regs_user = orig_regs_user;
                evsel->core.attr.sample_regs_intr = orig_regs_intr;
                perf_sample__exit(&new_sample);
                goto out_put;
        }

        new_sample.evsel = evsel;
        ret = delegate->sample(delegate, new_event, &new_sample, machine);
        perf_sample__exit(&new_sample);

        /* Restore original attributes so trace ingestion never desynchronizes! */
        evsel->sample_size = orig_sample_size;
        evsel->core.attr.sample_type = orig_sample_type;
        evsel->core.attr.sample_regs_user = orig_regs_user;
        evsel->core.attr.sample_regs_intr = orig_regs_intr;

out_put:
        thread__put(thread);
        return ret;
}

#undef CHECK_BOUNDS
#undef COPY_U64
#undef REMAP_U64

static int skipn(int fd, off_t n)
{
        char buf[4096];
        ssize_t ret;

        while (n > 0) {
                ret = read(fd, buf, min_t(off_t, n, (off_t)sizeof(buf)));
                if (ret <= 0)
                        return ret;
                n -= ret;
        }

        return 0;
}

static s64 aslr_tool__process_auxtrace(const struct perf_tool *tool __maybe_unused,
                                       struct perf_session *session,
                                       union perf_event *event)
{
        pr_warning_once("ASLR: Dropping auxtrace data as it cannot be obfuscated.\n");
        if (perf_data__is_pipe(session->data)) {
                /* Copy behavior of the stub by reading all pipe data. */
                int err = skipn(perf_data__fd(session->data), event->auxtrace.size);

                if (err < 0)
                        return err;
        }
        return event->auxtrace.size;
}

static int aslr_tool__process_auxtrace_info(const struct perf_tool *tool __maybe_unused,
                                            struct perf_session *session __maybe_unused,
                                            union perf_event *event __maybe_unused)
{
        return 0;
}

static int aslr_tool__process_auxtrace_error(const struct perf_tool *tool __maybe_unused,
                                             struct perf_session *session __maybe_unused,
                                             union perf_event *event __maybe_unused)
{
        return 0;
}

void aslr_tool__strip_attr_event(union perf_event *event, struct evlist *evlist)
{
        u32 attr_size;

        if (!evlist)
                return;

        attr_size = event->attr.attr.size ?: PERF_ATTR_SIZE_VER0;

        if (attr_size >= (offsetof(struct perf_event_attr, sample_type) + sizeof(u64))) {
                event->attr.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE;

                if (attr_size >= (offsetof(struct perf_event_attr, sample_regs_user) + sizeof(u64)))
                        event->attr.attr.sample_regs_user = 0;
                if (attr_size >= (offsetof(struct perf_event_attr, sample_regs_intr) + sizeof(u64)))
                        event->attr.attr.sample_regs_intr = 0;
        }

        if (attr_size >= (offsetof(struct perf_event_attr, type) + sizeof(u32))) {
                u32 type = event->attr.attr.type;

                if (type == PERF_TYPE_BREAKPOINT &&
                    attr_size >= (offsetof(struct perf_event_attr, bp_addr) + sizeof(u64))) {
                        event->attr.attr.bp_addr = 0;
                } else if (type >= PERF_TYPE_MAX) {
                        struct perf_pmu *pmu;

                        pmu = perf_pmus__find_by_type(type);
                        if (pmu && (!strcmp(pmu->name, "kprobe") ||
                                    !strcmp(pmu->name, "uprobe"))) {
                                if (attr_size >= (offsetof(struct perf_event_attr, config1) + sizeof(u64)))
                                        event->attr.attr.config1 = 0;
                                if (attr_size >= (offsetof(struct perf_event_attr, config2) + sizeof(u64)))
                                        event->attr.attr.config2 = 0;
                        }
                }
        }
}

static int aslr_tool__init(struct aslr_tool *aslr, struct perf_tool *delegate)
{
        delegate_tool__init(&aslr->tool, delegate);
        aslr->tool.tool.ordered_events = true;

        if (machines__init(&aslr->machines))
                return -ENOMEM;

        hashmap__init(&aslr->remap_addresses,
                      remap_addresses__hash, remap_addresses__equal,
                      /*ctx=*/NULL);
        hashmap__init(&aslr->top_addresses,
                      top_addresses__hash, top_addresses__equal,
                      /*ctx=*/NULL);
        hashmap__init(&aslr->evsel_orig_attrs,
                      evsel_hash, evsel_equal,
                      /*ctx=*/NULL);

        aslr->tool.tool.sample  = aslr_tool__process_sample;
        /* read - reads a counter, okay to delegate. */
        aslr->tool.tool.mmap    = aslr_tool__process_mmap;
        aslr->tool.tool.mmap2   = aslr_tool__process_mmap2;
        aslr->tool.tool.comm    = aslr_tool__process_comm;
        aslr->tool.tool.fork    = aslr_tool__process_fork;
        aslr->tool.tool.exit    = aslr_tool__process_exit;
        /* namesspaces, cgroup, lost, lost_sample, aux, */
        /* itrace_start, aux_output_hw_id, context_switch, throttle, unthrottle */
        /* - no virtual addresses. */
        aslr->tool.tool.ksymbol = aslr_tool__process_ksymbol;
        /* bpf - no virtual address. */
        aslr->tool.tool.text_poke = aslr_tool__process_text_poke;
        /*
         * event_update, tracing_data, finished_round, build_id, id_index,
         * auxtrace_info, auxtrace_error, time_conv, thread_map, cpu_map,
         * stat_config, stat, feature, finished_init, bpf_metadata, compressed,
         * auxtrace - no virtual addresses.
         */
        aslr->tool.tool.auxtrace = aslr_tool__process_auxtrace;
        aslr->tool.tool.auxtrace_info = aslr_tool__process_auxtrace_info;
        aslr->tool.tool.auxtrace_error = aslr_tool__process_auxtrace_error;

        return 0;
}

struct perf_tool *aslr_tool__new(struct perf_tool *delegate)
{
        struct aslr_tool *aslr = zalloc(sizeof(*aslr));

        if (!aslr)
                return NULL;

        if (aslr_tool__init(aslr, delegate)) {
                free(aslr);
                return NULL;
        }
        return &aslr->tool.tool;
}

void aslr_tool__delete(struct perf_tool *tool)
{
        struct delegate_tool *del_tool;
        struct aslr_tool *aslr;
        struct hashmap_entry *cur;
        size_t bkt;
        struct rb_node *nd;

        if (!tool)
                return;

        del_tool = container_of(tool, struct delegate_tool, tool);
        aslr = container_of(del_tool, struct aslr_tool, tool);

        hashmap__for_each_entry(&aslr->remap_addresses, cur, bkt) {
                struct remap_addresses_key *key = (struct remap_addresses_key *)cur->pkey;

                if (key)
                        dso__put(key->dso);
                zfree(&cur->pkey);
                zfree(&cur->pvalue);
        }
        hashmap__for_each_entry(&aslr->top_addresses, cur, bkt) {
                zfree(&cur->pkey);
                zfree(&cur->pvalue);
        }
        hashmap__for_each_entry(&aslr->evsel_orig_attrs, cur, bkt) {
                zfree(&cur->pvalue);
        }

        hashmap__clear(&aslr->remap_addresses);
        hashmap__clear(&aslr->top_addresses);
        hashmap__clear(&aslr->evsel_orig_attrs);
        aslr_tool__destroy_machines_priv(&aslr->machines);
        machines__destroy_kernel_maps(&aslr->machines);

        while ((nd = rb_first_cached(&aslr->machines.guests)) != NULL) {
                struct machine *machine = rb_entry(nd, struct machine, rb_node);

                rb_erase_cached(nd, &aslr->machines.guests);
                machine__delete(machine);
        }

        machines__exit(&aslr->machines);
        free(aslr);
}

int aslr_tool__cache_orig_attrs(struct perf_tool *tool, struct evsel *evsel)
{
        struct delegate_tool *del_tool = container_of(tool, struct delegate_tool, tool);
        struct aslr_tool *aslr = container_of(del_tool, struct aslr_tool, tool);
        struct aslr_evsel_priv *priv = zalloc(sizeof(*priv));
        int err;

        if (!priv)
                return -ENOMEM;

        priv->orig_sample_type = evsel->core.attr.sample_type;
        priv->orig_sample_regs_user = evsel->core.attr.sample_regs_user;
        priv->orig_sample_regs_intr = evsel->core.attr.sample_regs_intr;
        priv->orig_sample_size = evsel->sample_size;

        err = hashmap__add(&aslr->evsel_orig_attrs, evsel, priv);
        if (err) {
                free(priv);
                return err;
        }
        return 0;
}

void aslr_tool__strip_evlist(const struct perf_tool *tool __maybe_unused, struct evlist *evlist)
{
        struct evsel *evsel;

        evlist__for_each_entry(evlist, evsel) {
                evsel->core.attr.sample_type &= ASLR_SUPPORTED_SAMPLE_TYPE;
                evsel->core.attr.sample_regs_user = 0;
                evsel->core.attr.sample_regs_intr = 0;
                evsel->sample_size = __evsel__sample_size(evsel->core.attr.sample_type);
                evsel__calc_id_pos(evsel);

                if (evsel->core.attr.type == PERF_TYPE_BREAKPOINT) {
                        evsel->core.attr.bp_addr = 0;
                } else if (evsel->core.attr.type >= PERF_TYPE_MAX) {
                        struct perf_pmu *pmu = perf_pmus__find_by_type(evsel->core.attr.type);

                        if (pmu && (!strcmp(pmu->name, "kprobe") ||
                                    !strcmp(pmu->name, "uprobe"))) {
                                evsel->core.attr.config1 = 0;
                                evsel->core.attr.config2 = 0;
                        }
                }
        }
}

void aslr_tool__restore_evlist(const struct perf_tool *tool, struct evlist *evlist)
{
        const struct delegate_tool *del_tool = container_of(tool, const struct delegate_tool, tool);
        const struct aslr_tool *aslr = container_of(del_tool, const struct aslr_tool, tool);
        struct evsel *evsel;
        struct aslr_evsel_priv *priv;

        evlist__for_each_entry(evlist, evsel) {
                if (hashmap__find(&aslr->evsel_orig_attrs, evsel, &priv)) {
                        evsel->core.attr.sample_type = priv->orig_sample_type;
                        evsel->core.attr.sample_regs_user = priv->orig_sample_regs_user;
                        evsel->core.attr.sample_regs_intr = priv->orig_sample_regs_intr;
                        evsel->sample_size = priv->orig_sample_size;
                        evsel__calc_id_pos(evsel);
                }
        }
}