summaryrefslogtreecommitdiff
path: root/tools/perf/util/libunwind-arch
diff options
context:
space:
mode:
Diffstat (limited to 'tools/perf/util/libunwind-arch')
-rw-r--r--tools/perf/util/libunwind-arch/Build11
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-arch.c319
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-arch.h296
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-arm.c290
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-arm64.c289
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-i386.c312
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-loongarch.c297
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-mips.c299
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-ppc32.c303
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-ppc64.c303
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-riscv.c297
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-s390.c299
-rw-r--r--tools/perf/util/libunwind-arch/libunwind-x86_64.c320
13 files changed, 3635 insertions, 0 deletions
diff --git a/tools/perf/util/libunwind-arch/Build b/tools/perf/util/libunwind-arch/Build
new file mode 100644
index 000000000000..80d3571918b1
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/Build
@@ -0,0 +1,11 @@
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arch.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-arm.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-loongarch.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-mips.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc32.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-ppc64.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-riscv.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-s390.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-i386.o
+perf-util-$(CONFIG_LIBUNWIND) += libunwind-x86_64.o
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.c b/tools/perf/util/libunwind-arch/libunwind-arch.c
new file mode 100644
index 000000000000..9a74cf3c8729
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.c
@@ -0,0 +1,319 @@
+// 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;
+ }
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arch.h b/tools/perf/util/libunwind-arch/libunwind-arch.h
new file mode 100644
index 000000000000..74a09cd58f38
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arch.h
@@ -0,0 +1,296 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef __LIBUNWIND_ARCH_H
+#define __LIBUNWIND_ARCH_H
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdlib.h>
+
+struct machine;
+struct maps;
+struct perf_sample;
+struct thread;
+
+struct unwind_info {
+ struct machine *machine;
+ struct thread *thread;
+ struct perf_sample *sample;
+ void *cursor;
+ uint64_t *ips;
+ int cur_ip;
+ int max_ips;
+ unsigned int unw_word_t_size;
+ uint16_t e_machine;
+ bool best_effort;
+};
+
+struct libarch_unwind__dyn_info {
+ uint64_t start_ip;
+ uint64_t end_ip;
+ uint64_t segbase;
+ uint64_t table_data;
+ uint64_t table_len;
+};
+struct libarch_unwind__proc_info;
+
+int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum);
+int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum);
+int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum);
+
+void __libunwind_arch__flush_access_arm(struct maps *maps);
+void __libunwind_arch__flush_access_arm64(struct maps *maps);
+void __libunwind_arch__flush_access_loongarch(struct maps *maps);
+void __libunwind_arch__flush_access_mips(struct maps *maps);
+void __libunwind_arch__flush_access_ppc32(struct maps *maps);
+void __libunwind_arch__flush_access_ppc64(struct maps *maps);
+void __libunwind_arch__flush_access_riscv(struct maps *maps);
+void __libunwind_arch__flush_access_s390(struct maps *maps);
+void __libunwind_arch__flush_access_i386(struct maps *maps);
+void __libunwind_arch__flush_access_x86_64(struct maps *maps);
+void libunwind_arch__flush_access(struct maps *maps);
+
+void __libunwind_arch__finish_access_arm(struct maps *maps);
+void __libunwind_arch__finish_access_arm64(struct maps *maps);
+void __libunwind_arch__finish_access_loongarch(struct maps *maps);
+void __libunwind_arch__finish_access_mips(struct maps *maps);
+void __libunwind_arch__finish_access_ppc32(struct maps *maps);
+void __libunwind_arch__finish_access_ppc64(struct maps *maps);
+void __libunwind_arch__finish_access_riscv(struct maps *maps);
+void __libunwind_arch__finish_access_s390(struct maps *maps);
+void __libunwind_arch__finish_access_i386(struct maps *maps);
+void __libunwind_arch__finish_access_x86_64(struct maps *maps);
+void libunwind_arch__finish_access(struct maps *maps);
+
+void *__libunwind_arch__create_addr_space_arm(void);
+void *__libunwind_arch__create_addr_space_arm64(void);
+void *__libunwind_arch__create_addr_space_loongarch(void);
+void *__libunwind_arch__create_addr_space_mips(void);
+void *__libunwind_arch__create_addr_space_ppc32(void);
+void *__libunwind_arch__create_addr_space_ppc64(void);
+void *__libunwind_arch__create_addr_space_riscv(void);
+void *__libunwind_arch__create_addr_space_s390(void);
+void *__libunwind_arch__create_addr_space_i386(void);
+void *__libunwind_arch__create_addr_space_x86_64(void);
+void *libunwind_arch__create_addr_space(unsigned int e_machine);
+
+int __libunwind__find_proc_info(void *as, uint64_t ip, void *pi, int need_unwind_info, void *arg);
+int __libunwind__access_mem(void *as, uint64_t addr, void *valp_word, int __write, void *arg);
+int __libunwind__access_reg(void *as, int regnum, void *valp_word, int __write, void *arg);
+
+int __libunwind_arch__dwarf_search_unwind_table_arm(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_mips(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_s390(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_i386(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as, uint64_t ip,
+ struct libarch_unwind__dyn_info *di,
+ void *pi,
+ int need_unwind_info,
+ void *arg);
+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);
+
+int __libunwind_arch__dwarf_find_debug_frame_arm(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);
+int __libunwind_arch__dwarf_find_debug_frame_arm64(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);
+int __libunwind_arch__dwarf_find_debug_frame_loongarch(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);
+int __libunwind_arch__dwarf_find_debug_frame_mips(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);
+int __libunwind_arch__dwarf_find_debug_frame_ppc32(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);
+int __libunwind_arch__dwarf_find_debug_frame_ppc64(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);
+int __libunwind_arch__dwarf_find_debug_frame_riscv(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);
+int __libunwind_arch__dwarf_find_debug_frame_s390(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);
+int __libunwind_arch__dwarf_find_debug_frame_i386(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);
+int __libunwind_arch__dwarf_find_debug_frame_x86_64(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);
+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);
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread,
+ struct perf_sample *sample,
+ int max_stack,
+ bool best_effort,
+ uint64_t first_ip);
+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);
+
+void libunwind_arch_unwind_info__delete(struct unwind_info *ui);
+
+int __libunwind_arch__unwind_step_arm(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_mips(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_s390(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_i386(struct unwind_info *ui);
+int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui);
+int libunwind_arch__unwind_step(struct unwind_info *ui);
+
+#endif /* __LIBUNWIND_ARCH_H */
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm.c b/tools/perf/util/libunwind-arch/libunwind-arm.c
new file mode 100644
index 000000000000..61b1c5a70b7f
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arm.c
@@ -0,0 +1,290 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/arm/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+#include <libunwind-arm.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_arm(int unw_regnum)
+{
+ if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM_MAX) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return unw_regnum;
+}
+
+void __libunwind_arch__flush_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_arm(void)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_arm(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_arm(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_ARM_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_ARM;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_arm(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-arm64.c b/tools/perf/util/libunwind-arch/libunwind-arm64.c
new file mode 100644
index 000000000000..4d467dd440a1
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-arm64.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/arm64/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+#include <libunwind-aarch64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_arm64(int unw_regnum)
+{
+ if (unw_regnum < 0 || unw_regnum >= PERF_REG_ARM64_EXTENDED_MAX) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return unw_regnum;
+}
+
+void __libunwind_arch__flush_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_arm64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_arm64(void)
+{
+#ifdef HAVE_LIBUNWIND_AARCH64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_arm64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_arm64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_ARM64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_ARM64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_arm64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_AARCH64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_arm64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_ARM64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-i386.c b/tools/perf/util/libunwind-arch/libunwind-i386.c
new file mode 100644
index 000000000000..3423be45096a
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-i386.c
@@ -0,0 +1,312 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+#include <libunwind-x86.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_i386(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_X86_SUPPORT
+ return -EINVAL;
+#else
+ static const int perf_i386_regnums[] = {
+#define REGNUM(reg) [UNW_X86_E ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(AX),
+ REGNUM(DX),
+ REGNUM(CX),
+ REGNUM(BX),
+ REGNUM(SI),
+ REGNUM(DI),
+ REGNUM(BP),
+ REGNUM(SP),
+ REGNUM(IP),
+#undef REGNUM
+ };
+
+ if (unw_regnum == UNW_X86_EAX)
+ return PERF_REG_X86_AX;
+
+ if (unw_regnum < 0 || unw_regnum >= (int)ARRAY_SIZE(perf_i386_regnums) ||
+ perf_i386_regnums[unw_regnum] == 0) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+
+ return perf_i386_regnums[unw_regnum];
+#endif // HAVE_LIBUNWIND_X86_SUPPORT
+}
+
+void __libunwind_arch__flush_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_i386(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_i386(void)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_i386(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_i386(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_X86_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_i386(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_I386;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_i386(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-loongarch.c b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
new file mode 100644
index 000000000000..ff225a540b43
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-loongarch.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/loongarch/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+#include <libunwind-loongarch64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_loongarch(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_LOONGARCH64_R1 ... UNW_LOONGARCH64_R31:
+ return unw_regnum - UNW_LOONGARCH64_R1 + PERF_REG_LOONGARCH_R1;
+ case UNW_LOONGARCH64_PC:
+ return PERF_REG_LOONGARCH_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+}
+
+void __libunwind_arch__flush_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_loongarch(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_loongarch(void)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_loongarch(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_loongarch(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_LOONGARCH_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_LOONGARCH)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_loongarch(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_LOONGARCH;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_loongarch(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_LOONGARCH_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-mips.c b/tools/perf/util/libunwind-arch/libunwind-mips.c
new file mode 100644
index 000000000000..ed14a8f94fa7
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-mips.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/mips/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+#include <libunwind-mips.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_mips(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_MIPS_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_MIPS_R1 ... UNW_MIPS_R25:
+ return unw_regnum - UNW_MIPS_R1 + PERF_REG_MIPS_R1;
+ case UNW_MIPS_R28 ... UNW_MIPS_R31:
+ return unw_regnum - UNW_MIPS_R28 + PERF_REG_MIPS_R28;
+ case UNW_MIPS_PC:
+ return PERF_REG_MIPS_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_MIPS_SUPPORT
+}
+
+void __libunwind_arch__flush_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_mips(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_mips(void)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_mips(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_mips(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_MIPS_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_MIPS)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_mips(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_MIPS;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_mips(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_MIPS_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc32.c b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
new file mode 100644
index 000000000000..289a3b8cde75
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc32.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+#include <libunwind-ppc32.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_ppc32(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_PPC32_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_PPC32_R0 ... UNW_PPC32_R31:
+ return unw_regnum - UNW_PPC32_R0 + PERF_REG_POWERPC_R0;
+ case UNW_PPC32_LR:
+ return PERF_REG_POWERPC_LINK;
+ case UNW_PPC32_CTR:
+ return PERF_REG_POWERPC_CTR;
+ case UNW_PPC32_XER:
+ return PERF_REG_POWERPC_XER;
+ case UNW_PPC32_NIP:
+ return PERF_REG_POWERPC_NIP;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_PPC32_SUPPORT
+}
+
+void __libunwind_arch__flush_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc32(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_ppc32(void)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_ppc32(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_ppc32(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_PPC32_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC32)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc32(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_PPC;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_ppc32(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC32_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-ppc64.c b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
new file mode 100644
index 000000000000..a4c9b6cc3c00
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-ppc64.c
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/powerpc/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+#include <libunwind-ppc64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_ppc64(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_PPC64_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_PPC64_R0 ... UNW_PPC64_R31:
+ return unw_regnum - UNW_PPC64_R0 + PERF_REG_POWERPC_R0;
+ case UNW_PPC64_LR:
+ return PERF_REG_POWERPC_LINK;
+ case UNW_PPC64_CTR:
+ return PERF_REG_POWERPC_CTR;
+ case UNW_PPC64_XER:
+ return PERF_REG_POWERPC_XER;
+ case UNW_PPC64_NIP:
+ return PERF_REG_POWERPC_NIP;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_PPC64_SUPPORT
+}
+
+void __libunwind_arch__flush_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_ppc64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_ppc64(void)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_ppc64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_ppc64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_PPC64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_PPC64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_ppc64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_PPC64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_ppc64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_PPC64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-riscv.c b/tools/perf/util/libunwind-arch/libunwind-riscv.c
new file mode 100644
index 000000000000..3220690cd7d1
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-riscv.c
@@ -0,0 +1,297 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/riscv/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+#include <libunwind-riscv.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_riscv(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_RISCV_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_RISCV_X1 ... UNW_RISCV_X31:
+ return unw_regnum - UNW_RISCV_X1 + PERF_REG_RISCV_RA;
+ case UNW_RISCV_PC:
+ return PERF_REG_RISCV_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_RISCV_SUPPORT
+}
+
+void __libunwind_arch__flush_access_riscv(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_riscv(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_riscv(void)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_riscv(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_riscv(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_RISCV_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_RISCV)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_riscv(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_RISCV;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_riscv(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_RISCV_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-s390.c b/tools/perf/util/libunwind-arch/libunwind-s390.c
new file mode 100644
index 000000000000..3e57cfc451c6
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-s390.c
@@ -0,0 +1,299 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/s390/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+#include <libunwind-s390x.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_s390(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_S390X_SUPPORT
+ return -EINVAL;
+#else
+ switch (unw_regnum) {
+ case UNW_S390X_R0 ... UNW_S390X_R15:
+ return unw_regnum - UNW_S390X_R0 + PERF_REG_S390_R0;
+ case UNW_S390X_F0 ... UNW_S390X_F15:
+ return unw_regnum - UNW_S390X_F0 + PERF_REG_S390_FP0;
+ case UNW_S390X_IP:
+ return PERF_REG_S390_PC;
+ default:
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+#endif // HAVE_LIBUNWIND_S390X_SUPPORT
+}
+
+void __libunwind_arch__flush_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_s390(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_s390(void)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_s390(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_s390(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_S390X_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_S390X)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_s390(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_S390;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_s390(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_S390X_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
diff --git a/tools/perf/util/libunwind-arch/libunwind-x86_64.c b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
new file mode 100644
index 000000000000..b13e9b1cc7a3
--- /dev/null
+++ b/tools/perf/util/libunwind-arch/libunwind-x86_64.c
@@ -0,0 +1,320 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "libunwind-arch.h"
+#include "../debug.h"
+#include "../maps.h"
+#include "../thread.h"
+#include "../../../arch/x86/include/uapi/asm/perf_regs.h"
+#include <linux/compiler.h>
+#include <linux/kernel.h>
+#include <linux/zalloc.h>
+#include <elf.h>
+#include <errno.h>
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+#include <libunwind-x86_64.h>
+#endif
+
+int __get_perf_regnum_for_unw_regnum_x86_64(int unw_regnum __maybe_unused)
+{
+#ifndef HAVE_LIBUNWIND_X86_64_SUPPORT
+ return -EINVAL;
+#else
+ static const int perf_x86_64_regnums[] = {
+#define REGNUM(reg) [UNW_X86_64_R ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(AX),
+ REGNUM(DX),
+ REGNUM(CX),
+ REGNUM(BX),
+ REGNUM(SI),
+ REGNUM(DI),
+ REGNUM(BP),
+ REGNUM(SP),
+ REGNUM(IP),
+#undef REGNUM
+#define REGNUM(reg) [UNW_X86_64_ ## reg] = PERF_REG_X86_ ## reg
+ REGNUM(R8),
+ REGNUM(R9),
+ REGNUM(R10),
+ REGNUM(R11),
+ REGNUM(R12),
+ REGNUM(R13),
+ REGNUM(R14),
+ REGNUM(R15),
+#undef REGNUM
+ };
+
+ if (unw_regnum == UNW_X86_64_RAX)
+ return PERF_REG_X86_AX;
+
+ if (unw_regnum < 0 || unw_regnum >= (int)ARRAY_SIZE(perf_x86_64_regnums) ||
+ perf_x86_64_regnums[unw_regnum] == 0) {
+ pr_err("unwind: invalid reg id %d\n", unw_regnum);
+ return -EINVAL;
+ }
+ return perf_x86_64_regnums[unw_regnum];
+#endif // HAVE_LIBUNWIND_X86_64_SUPPORT
+}
+
+void __libunwind_arch__flush_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_flush_cache(maps__addr_space(maps), 0, 0);
+#endif
+}
+
+void __libunwind_arch__finish_access_x86_64(struct maps *maps __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_destroy_addr_space(maps__addr_space(maps));
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+static int find_proc_info(unw_addr_space_t as, unw_word_t ip, unw_proc_info_t *pi,
+ int need_unwind_info, void *arg)
+{
+ return __libunwind__find_proc_info(as, ip, pi, need_unwind_info, arg);
+}
+
+static void put_unwind_info(unw_addr_space_t __maybe_unused as,
+ unw_proc_info_t *pi __maybe_unused,
+ void *arg __maybe_unused)
+{
+ pr_debug("unwind: put_unwind_info called\n");
+}
+
+static int get_dyn_info_list_addr(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused *dil_addr,
+ void __maybe_unused *arg)
+{
+ return -UNW_ENOINFO;
+}
+
+static int access_mem(unw_addr_space_t as, unw_word_t addr, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_mem(as, addr, valp, __write, arg);
+}
+
+static int access_reg(unw_addr_space_t as, unw_regnum_t regnum, unw_word_t *valp,
+ int __write, void *arg)
+{
+ return __libunwind__access_reg(as, regnum, valp, __write, arg);
+}
+
+static int access_fpreg(unw_addr_space_t __maybe_unused as,
+ unw_regnum_t __maybe_unused num,
+ unw_fpreg_t __maybe_unused *val,
+ int __maybe_unused __write,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: access_fpreg unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int resume(unw_addr_space_t __maybe_unused as,
+ unw_cursor_t __maybe_unused *cu,
+ void __maybe_unused *arg)
+{
+ pr_err("unwind: resume unsupported\n");
+ return -UNW_EINVAL;
+}
+
+static int get_proc_name(unw_addr_space_t __maybe_unused as,
+ unw_word_t __maybe_unused addr,
+ char __maybe_unused *bufp, size_t __maybe_unused buf_len,
+ unw_word_t __maybe_unused *offp, void __maybe_unused *arg)
+{
+ pr_err("unwind: get_proc_name unsupported\n");
+ return -UNW_EINVAL;
+}
+#endif
+
+void *__libunwind_arch__create_addr_space_x86_64(void)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ static unw_accessors_t accessors = {
+ .find_proc_info = find_proc_info,
+ .put_unwind_info = put_unwind_info,
+ .get_dyn_info_list_addr = get_dyn_info_list_addr,
+ .access_mem = access_mem,
+ .access_reg = access_reg,
+ .access_fpreg = access_fpreg,
+ .resume = resume,
+ .get_proc_name = get_proc_name,
+ };
+ unw_addr_space_t addr_space;
+
+ addr_space = unw_create_addr_space(&accessors, /*byte_order=*/0);
+ unw_set_caching_policy(addr_space, UNW_CACHE_GLOBAL);
+ return addr_space;
+#else
+ return NULL;
+#endif
+}
+
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+extern int UNW_OBJ(dwarf_search_unwind_table) (unw_addr_space_t as,
+ unw_word_t ip,
+ unw_dyn_info_t *di,
+ unw_proc_info_t *pi,
+ int need_unwind_info, void *arg);
+#define dwarf_search_unwind_table UNW_OBJ(dwarf_search_unwind_table)
+#endif
+
+int __libunwind_arch__dwarf_search_unwind_table_x86_64(void *as __maybe_unused,
+ uint64_t ip __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ void *pi __maybe_unused,
+ int need_unwind_info __maybe_unused,
+ void *arg __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_search_unwind_table(as, ip, &di, pi, need_unwind_info, arg);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.rti.segbase;
+ _di->table_data = di.u.rti.table_data;
+ _di->table_len = di.u.rti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
+extern int UNW_OBJ(dwarf_find_debug_frame) (int found, unw_dyn_info_t *di_debug,
+ unw_word_t ip,
+ unw_word_t segbase,
+ const char *obj_name, unw_word_t start,
+ unw_word_t end);
+#define dwarf_find_debug_frame UNW_OBJ(dwarf_find_debug_frame)
+#endif
+
+int __libunwind_arch__dwarf_find_debug_frame_x86_64(int found __maybe_unused,
+ struct libarch_unwind__dyn_info *_di __maybe_unused,
+ uint64_t ip __maybe_unused,
+ uint64_t segbase __maybe_unused,
+ const char *obj_name __maybe_unused,
+ uint64_t start __maybe_unused,
+ uint64_t end __maybe_unused)
+{
+#if defined(HAVE_LIBUNWIND_X86_64_SUPPORT) && !defined(NO_LIBUNWIND_DEBUG_FRAME_X86_64)
+ unw_dyn_info_t di = {
+ .format = UNW_INFO_FORMAT_REMOTE_TABLE,
+ .start_ip = _di->start_ip,
+ .end_ip = _di->end_ip,
+ .u = {
+ .rti = {
+ .segbase = _di->segbase,
+ .table_data = _di->table_data,
+ .table_len = _di->table_len,
+ },
+ },
+ };
+ int ret = dwarf_find_debug_frame(found, &di, ip, segbase, obj_name, start, end);
+
+ _di->start_ip = di.start_ip;
+ _di->end_ip = di.end_ip;
+ _di->segbase = di.u.ti.segbase;
+ _di->table_data = di.u.ti.table_data;
+ _di->table_len = di.u.ti.table_len;
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}
+
+struct unwind_info *__libunwind_arch_unwind_info__new_x86_64(struct thread *thread __maybe_unused,
+ struct perf_sample *sample __maybe_unused,
+ int max_stack __maybe_unused,
+ bool best_effort __maybe_unused,
+ uint64_t first_ip __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ struct arch_unwind_info {
+ struct unwind_info ui;
+ unw_cursor_t _cursor;
+ uint64_t _ips[];
+ };
+
+ struct maps *maps = thread__maps(thread);
+ void *addr_space = maps__addr_space(maps);
+ struct arch_unwind_info *ui;
+ int ret;
+
+ if (addr_space == NULL)
+ return NULL;
+
+ ui = zalloc(sizeof(*ui) + sizeof(ui->_ips[0]) * max_stack);
+ if (!ui)
+ return NULL;
+
+ ui->ui.machine = maps__machine(maps);
+ ui->ui.thread = thread;
+ ui->ui.sample = sample;
+ ui->ui.cursor = &ui->_cursor;
+ ui->ui.ips = &ui->_ips[0];
+ ui->ui.ips[0] = first_ip;
+ ui->ui.cur_ip = 1;
+ ui->ui.max_ips = max_stack;
+ ui->ui.unw_word_t_size = sizeof(unw_word_t);
+ ui->ui.e_machine = EM_X86_64;
+ ui->ui.best_effort = best_effort;
+
+ ret = unw_init_remote(&ui->_cursor, addr_space, &ui->ui);
+ if (ret) {
+ if (!best_effort)
+ pr_err("libunwind: %s\n", unw_strerror(ret));
+ free(ui);
+ return NULL;
+ }
+ return &ui->ui;
+#else
+ return NULL;
+#endif
+}
+
+int __libunwind_arch__unwind_step_x86_64(struct unwind_info *ui __maybe_unused)
+{
+#ifdef HAVE_LIBUNWIND_X86_64_SUPPORT
+ int ret;
+
+ if (ui->cur_ip >= ui->max_ips)
+ return 0;
+
+ ret = unw_step(ui->cursor);
+ if (ret > 0) {
+ uint64_t ip;
+
+ unw_get_reg(ui->cursor, UNW_REG_IP, &ip);
+
+ if (unw_is_signal_frame(ui->cursor) <= 0) {
+ /*
+ * Decrement the IP for any non-activation frames. This
+ * is required to properly find the srcline for caller
+ * frames. See also the documentation for
+ * dwfl_frame_pc(), which this code tries to replicate.
+ */
+ --ip;
+ }
+ ui->ips[ui->cur_ip++] = ip;
+ }
+ return ret;
+#else
+ return -EINVAL;
+#endif
+}