root/tools/perf/util/libunwind-arch/libunwind-arch.c
// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
#include <elf.h>
#include <errno.h>

int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
{
        switch (e_machine) {
        case EM_ARM:
                return __get_perf_regnum_for_unw_regnum_arm(unw_regnum);
        case EM_AARCH64:
                return __get_perf_regnum_for_unw_regnum_arm64(unw_regnum);
        case EM_LOONGARCH:
                return __get_perf_regnum_for_unw_regnum_loongarch(unw_regnum);
        case EM_MIPS:
                return __get_perf_regnum_for_unw_regnum_mips(unw_regnum);
        case EM_PPC:
                return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum);
        case EM_PPC64:
                return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum);
        case EM_RISCV:
                return __get_perf_regnum_for_unw_regnum_riscv(unw_regnum);
        case EM_S390:
                return __get_perf_regnum_for_unw_regnum_s390(unw_regnum);
        case EM_386:
                return __get_perf_regnum_for_unw_regnum_i386(unw_regnum);
        case EM_X86_64:
                return __get_perf_regnum_for_unw_regnum_x86_64(unw_regnum);
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                return -EINVAL;
        }
}


void libunwind_arch__flush_access(struct maps *maps)
{
        unsigned int e_machine = maps__e_machine(maps);

        switch (e_machine) {
        case EM_NONE:
                break;  // No libunwind info on the maps.
        case EM_ARM:
                __libunwind_arch__flush_access_arm(maps);
                break;
        case EM_AARCH64:
                __libunwind_arch__flush_access_arm64(maps);
                break;
        case EM_LOONGARCH:
                __libunwind_arch__flush_access_loongarch(maps);
                break;
        case EM_MIPS:
                __libunwind_arch__flush_access_mips(maps);
                break;
        case EM_PPC:
                __libunwind_arch__flush_access_ppc32(maps);
                break;
        case EM_PPC64:
                __libunwind_arch__flush_access_ppc64(maps);
                break;
        case EM_RISCV:
                __libunwind_arch__flush_access_riscv(maps);
                break;
        case EM_S390:
                __libunwind_arch__flush_access_s390(maps);
                break;
        case EM_386:
                __libunwind_arch__flush_access_i386(maps);
                break;
        case EM_X86_64:
                __libunwind_arch__flush_access_x86_64(maps);
                break;
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                break;
        }
}

void libunwind_arch__finish_access(struct maps *maps)
{
        unsigned int e_machine = maps__e_machine(maps);

        switch (e_machine) {
        case EM_NONE:
                break;  // No libunwind info on the maps.
        case EM_ARM:
                __libunwind_arch__finish_access_arm(maps);
                break;
        case EM_AARCH64:
                __libunwind_arch__finish_access_arm64(maps);
                break;
        case EM_LOONGARCH:
                __libunwind_arch__finish_access_loongarch(maps);
                break;
        case EM_MIPS:
                __libunwind_arch__finish_access_mips(maps);
                break;
        case EM_PPC:
                __libunwind_arch__finish_access_ppc32(maps);
                break;
        case EM_PPC64:
                __libunwind_arch__finish_access_ppc64(maps);
                break;
        case EM_RISCV:
                __libunwind_arch__finish_access_riscv(maps);
                break;
        case EM_S390:
                __libunwind_arch__finish_access_s390(maps);
                break;
        case EM_386:
                __libunwind_arch__finish_access_i386(maps);
                break;
        case EM_X86_64:
                __libunwind_arch__finish_access_x86_64(maps);
                break;
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                break;
        }
}

void *libunwind_arch__create_addr_space(unsigned int e_machine)
{
        switch (e_machine) {
        case EM_ARM:
                return __libunwind_arch__create_addr_space_arm();
        case EM_AARCH64:
                return __libunwind_arch__create_addr_space_arm64();
        case EM_LOONGARCH:
                return __libunwind_arch__create_addr_space_loongarch();
        case EM_MIPS:
                return __libunwind_arch__create_addr_space_mips();
        case EM_PPC:
                return __libunwind_arch__create_addr_space_ppc32();
        case EM_PPC64:
                return __libunwind_arch__create_addr_space_ppc64();
        case EM_RISCV:
                return __libunwind_arch__create_addr_space_riscv();
        case EM_S390:
                return __libunwind_arch__create_addr_space_s390();
        case EM_386:
                return __libunwind_arch__create_addr_space_i386();
        case EM_X86_64:
                return __libunwind_arch__create_addr_space_x86_64();
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                return NULL;
        }
}

int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
                                              void *as,
                                              uint64_t ip,
                                              struct libarch_unwind__dyn_info *di,
                                              void *pi,
                                              int need_unwind_info,
                                              void *arg)
{
        switch (e_machine) {
        case EM_ARM:
                return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi,
                                                                       need_unwind_info, arg);
        case EM_AARCH64:
                return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi,
                                                                         need_unwind_info, arg);
        case EM_LOONGARCH:
                return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di, pi,
                                                                             need_unwind_info, arg);
        case EM_MIPS:
                return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi,
                                                                        need_unwind_info, arg);
        case EM_PPC:
                return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi,
                                                                         need_unwind_info, arg);
        case EM_PPC64:
                return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi,
                                                                         need_unwind_info, arg);
        case EM_RISCV:
                return __libunwind_arch__dwarf_search_unwind_table_riscv(as, ip, di, pi,
                                                                        need_unwind_info, arg);
        case EM_S390:
                return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi,
                                                                        need_unwind_info, arg);
        case EM_386:
                return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi,
                                                                        need_unwind_info, arg);
        case EM_X86_64:
                return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi,
                                                                          need_unwind_info, arg);
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                return -EINVAL;
        }
}

int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
                                           int found,
                                           struct libarch_unwind__dyn_info *di_debug,
                                           uint64_t ip,
                                           uint64_t segbase,
                                           const char *obj_name,
                                           uint64_t start,
                                           uint64_t end)
{
        switch (e_machine) {
        case EM_ARM:
                return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip, segbase,
                                                                    obj_name, start, end);
        case EM_AARCH64:
                return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, ip, segbase,
                                                                      obj_name, start, end);
        case EM_LOONGARCH:
                return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debug, ip,
                                                                          segbase, obj_name,
                                                                          start, end);
        case EM_MIPS:
                return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip, segbase,
                                                                     obj_name, start, end);
        case EM_PPC:
                return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, ip, segbase,
                                                                      obj_name, start, end);
        case EM_PPC64:
                return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase,
                                                                      obj_name, start, end);
        case EM_RISCV:
                return __libunwind_arch__dwarf_find_debug_frame_riscv(found, di_debug, ip, segbase,
                                                                     obj_name, start, end);
        case EM_S390:
                return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase,
                                                                     obj_name, start, end);
        case EM_386:
                return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip, segbase,
                                                                     obj_name, start, end);
        case EM_X86_64:
                return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, ip, segbase,
                                                                       obj_name, start, end);
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                return -EINVAL;
        }
}

struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
                                                    struct perf_sample *sample, int max_stack,
                                                    bool best_effort, uint16_t e_machine,
                                                    uint64_t first_ip)
{
        switch (e_machine) {
        case EM_ARM:
                return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack,
                                                             best_effort, first_ip);
        case EM_AARCH64:
                return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack,
                                                               best_effort, first_ip);
        case EM_LOONGARCH:
                return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_stack,
                                                                   best_effort, first_ip);
        case EM_MIPS:
                return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack,
                                                              best_effort, first_ip);
        case EM_PPC:
                return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack,
                                                               best_effort, first_ip);
        case EM_PPC64:
                return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack,
                                                               best_effort, first_ip);
        case EM_RISCV:
                return __libunwind_arch_unwind_info__new_riscv(thread, sample, max_stack,
                                                              best_effort, first_ip);
        case EM_S390:
                return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack,
                                                              best_effort, first_ip);
        case EM_386:
                return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack,
                                                              best_effort, first_ip);
        case EM_X86_64:
                return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stack,
                                                                best_effort, first_ip);
        default:
                pr_err("ELF MACHINE %x is not supported.\n", e_machine);
                return NULL;
        }
}

void libunwind_arch_unwind_info__delete(struct unwind_info *ui)
{
        free(ui);
}

int libunwind_arch__unwind_step(struct unwind_info *ui)
{
        switch (ui->e_machine) {
        case EM_ARM:
                return __libunwind_arch__unwind_step_arm(ui);
        case EM_AARCH64:
                return __libunwind_arch__unwind_step_arm64(ui);
        case EM_LOONGARCH:
                return __libunwind_arch__unwind_step_loongarch(ui);
        case EM_MIPS:
                return __libunwind_arch__unwind_step_mips(ui);
        case EM_PPC:
                return __libunwind_arch__unwind_step_ppc32(ui);
        case EM_PPC64:
                return __libunwind_arch__unwind_step_ppc64(ui);
        case EM_RISCV:
                return __libunwind_arch__unwind_step_riscv(ui);
        case EM_S390:
                return __libunwind_arch__unwind_step_s390(ui);
        case EM_386:
                return __libunwind_arch__unwind_step_i386(ui);
        case EM_X86_64:
                return __libunwind_arch__unwind_step_x86_64(ui);
        default:
                pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine);
                return -EINVAL;
        }
}