linux/tools/perf/util/libunwind-arch/libunwind-arch.c
Ian Rogers 834c557167 perf unwind-libunwind: Add RISC-V libunwind support
Add a RISC-V implementation for unwinding.

Signed-off-by: Ian Rogers <irogers@google.com>
Cc: Adrian Hunter <adrian.hunter@intel.com>
Cc: Albert Ou <aou@eecs.berkeley.edu>
Cc: Alexander Shishkin <alexander.shishkin@linux.intel.com>
Cc: Alexandre Ghiti <alex@ghiti.fr>
Cc: Andrew Jones <andrew.jones@oss.qualcomm.com>
Cc: Athira Rajeev <atrajeev@linux.ibm.com>
Cc: Dapeng Mi <dapeng1.mi@linux.intel.com>
Cc: Dmitrii Dolgov <9erthalion6@gmail.com>
Cc: Florian Fainelli <florian.fainelli@broadcom.com>
Cc: Howard Chu <howardchu95@gmail.com>
Cc: Ingo Molnar <mingo@redhat.com>
Cc: James Clark <james.clark@linaro.org>
Cc: Jiri Olsa <jolsa@kernel.org>
Cc: John Garry <john.g.garry@oracle.com>
Cc: Leo Yan <leo.yan@linux.dev>
Cc: Li Guan <guanli.oerv@isrc.iscas.ac.cn>
Cc: Namhyung Kim <namhyung@kernel.org>
Cc: Palmer Dabbelt <palmer@dabbelt.com>
Cc: Paul Walmsley <pjw@kernel.org>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Shimin Guo <shimin.guo@skydio.com>
Cc: Thomas Richter <tmricht@linux.ibm.com>
Cc: Tomas Glozar <tglozar@redhat.com>
Cc: Will Deacon <will@kernel.org>
Signed-off-by: Arnaldo Carvalho de Melo <acme@redhat.com>
2026-05-15 16:19:50 -03:00

320 lines
9.6 KiB
C

// SPDX-License-Identifier: GPL-2.0
#include "libunwind-arch.h"
#include "../debug.h"
#include "../maps.h"
#include <elf.h>
#include <errno.h>
int get_perf_regnum_for_unw_regnum(unsigned int e_machine, int unw_regnum)
{
switch (e_machine) {
case EM_ARM:
return __get_perf_regnum_for_unw_regnum_arm(unw_regnum);
case EM_AARCH64:
return __get_perf_regnum_for_unw_regnum_arm64(unw_regnum);
case EM_LOONGARCH:
return __get_perf_regnum_for_unw_regnum_loongarch(unw_regnum);
case EM_MIPS:
return __get_perf_regnum_for_unw_regnum_mips(unw_regnum);
case EM_PPC:
return __get_perf_regnum_for_unw_regnum_ppc32(unw_regnum);
case EM_PPC64:
return __get_perf_regnum_for_unw_regnum_ppc64(unw_regnum);
case EM_RISCV:
return __get_perf_regnum_for_unw_regnum_riscv(unw_regnum);
case EM_S390:
return __get_perf_regnum_for_unw_regnum_s390(unw_regnum);
case EM_386:
return __get_perf_regnum_for_unw_regnum_i386(unw_regnum);
case EM_X86_64:
return __get_perf_regnum_for_unw_regnum_x86_64(unw_regnum);
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
return -EINVAL;
}
}
void libunwind_arch__flush_access(struct maps *maps)
{
unsigned int e_machine = maps__e_machine(maps);
switch (e_machine) {
case EM_NONE:
break; // No libunwind info on the maps.
case EM_ARM:
__libunwind_arch__flush_access_arm(maps);
break;
case EM_AARCH64:
__libunwind_arch__flush_access_arm64(maps);
break;
case EM_LOONGARCH:
__libunwind_arch__flush_access_loongarch(maps);
break;
case EM_MIPS:
__libunwind_arch__flush_access_mips(maps);
break;
case EM_PPC:
__libunwind_arch__flush_access_ppc32(maps);
break;
case EM_PPC64:
__libunwind_arch__flush_access_ppc64(maps);
break;
case EM_RISCV:
__libunwind_arch__flush_access_riscv(maps);
break;
case EM_S390:
__libunwind_arch__flush_access_s390(maps);
break;
case EM_386:
__libunwind_arch__flush_access_i386(maps);
break;
case EM_X86_64:
__libunwind_arch__flush_access_x86_64(maps);
break;
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
break;
}
}
void libunwind_arch__finish_access(struct maps *maps)
{
unsigned int e_machine = maps__e_machine(maps);
switch (e_machine) {
case EM_NONE:
break; // No libunwind info on the maps.
case EM_ARM:
__libunwind_arch__finish_access_arm(maps);
break;
case EM_AARCH64:
__libunwind_arch__finish_access_arm64(maps);
break;
case EM_LOONGARCH:
__libunwind_arch__finish_access_loongarch(maps);
break;
case EM_MIPS:
__libunwind_arch__finish_access_mips(maps);
break;
case EM_PPC:
__libunwind_arch__finish_access_ppc32(maps);
break;
case EM_PPC64:
__libunwind_arch__finish_access_ppc64(maps);
break;
case EM_RISCV:
__libunwind_arch__finish_access_riscv(maps);
break;
case EM_S390:
__libunwind_arch__finish_access_s390(maps);
break;
case EM_386:
__libunwind_arch__finish_access_i386(maps);
break;
case EM_X86_64:
__libunwind_arch__finish_access_x86_64(maps);
break;
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
break;
}
}
void *libunwind_arch__create_addr_space(unsigned int e_machine)
{
switch (e_machine) {
case EM_ARM:
return __libunwind_arch__create_addr_space_arm();
case EM_AARCH64:
return __libunwind_arch__create_addr_space_arm64();
case EM_LOONGARCH:
return __libunwind_arch__create_addr_space_loongarch();
case EM_MIPS:
return __libunwind_arch__create_addr_space_mips();
case EM_PPC:
return __libunwind_arch__create_addr_space_ppc32();
case EM_PPC64:
return __libunwind_arch__create_addr_space_ppc64();
case EM_RISCV:
return __libunwind_arch__create_addr_space_riscv();
case EM_S390:
return __libunwind_arch__create_addr_space_s390();
case EM_386:
return __libunwind_arch__create_addr_space_i386();
case EM_X86_64:
return __libunwind_arch__create_addr_space_x86_64();
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
return NULL;
}
}
int libunwind_arch__dwarf_search_unwind_table(unsigned int e_machine,
void *as,
uint64_t ip,
struct libarch_unwind__dyn_info *di,
void *pi,
int need_unwind_info,
void *arg)
{
switch (e_machine) {
case EM_ARM:
return __libunwind_arch__dwarf_search_unwind_table_arm(as, ip, di, pi,
need_unwind_info, arg);
case EM_AARCH64:
return __libunwind_arch__dwarf_search_unwind_table_arm64(as, ip, di, pi,
need_unwind_info, arg);
case EM_LOONGARCH:
return __libunwind_arch__dwarf_search_unwind_table_loongarch(as, ip, di, pi,
need_unwind_info, arg);
case EM_MIPS:
return __libunwind_arch__dwarf_search_unwind_table_mips(as, ip, di, pi,
need_unwind_info, arg);
case EM_PPC:
return __libunwind_arch__dwarf_search_unwind_table_ppc32(as, ip, di, pi,
need_unwind_info, arg);
case EM_PPC64:
return __libunwind_arch__dwarf_search_unwind_table_ppc64(as, ip, di, pi,
need_unwind_info, arg);
case EM_RISCV:
return __libunwind_arch__dwarf_search_unwind_table_riscv(as, ip, di, pi,
need_unwind_info, arg);
case EM_S390:
return __libunwind_arch__dwarf_search_unwind_table_s390(as, ip, di, pi,
need_unwind_info, arg);
case EM_386:
return __libunwind_arch__dwarf_search_unwind_table_i386(as, ip, di, pi,
need_unwind_info, arg);
case EM_X86_64:
return __libunwind_arch__dwarf_search_unwind_table_x86_64(as, ip, di, pi,
need_unwind_info, arg);
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
return -EINVAL;
}
}
int libunwind_arch__dwarf_find_debug_frame(unsigned int e_machine,
int found,
struct libarch_unwind__dyn_info *di_debug,
uint64_t ip,
uint64_t segbase,
const char *obj_name,
uint64_t start,
uint64_t end)
{
switch (e_machine) {
case EM_ARM:
return __libunwind_arch__dwarf_find_debug_frame_arm(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_AARCH64:
return __libunwind_arch__dwarf_find_debug_frame_arm64(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_LOONGARCH:
return __libunwind_arch__dwarf_find_debug_frame_loongarch(found, di_debug, ip,
segbase, obj_name,
start, end);
case EM_MIPS:
return __libunwind_arch__dwarf_find_debug_frame_mips(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_PPC:
return __libunwind_arch__dwarf_find_debug_frame_ppc32(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_PPC64:
return __libunwind_arch__dwarf_find_debug_frame_ppc64(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_RISCV:
return __libunwind_arch__dwarf_find_debug_frame_riscv(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_S390:
return __libunwind_arch__dwarf_find_debug_frame_s390(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_386:
return __libunwind_arch__dwarf_find_debug_frame_i386(found, di_debug, ip, segbase,
obj_name, start, end);
case EM_X86_64:
return __libunwind_arch__dwarf_find_debug_frame_x86_64(found, di_debug, ip, segbase,
obj_name, start, end);
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
return -EINVAL;
}
}
struct unwind_info *libunwind_arch_unwind_info__new(struct thread *thread,
struct perf_sample *sample, int max_stack,
bool best_effort, uint16_t e_machine,
uint64_t first_ip)
{
switch (e_machine) {
case EM_ARM:
return __libunwind_arch_unwind_info__new_arm(thread, sample, max_stack,
best_effort, first_ip);
case EM_AARCH64:
return __libunwind_arch_unwind_info__new_arm64(thread, sample, max_stack,
best_effort, first_ip);
case EM_LOONGARCH:
return __libunwind_arch_unwind_info__new_loongarch(thread, sample, max_stack,
best_effort, first_ip);
case EM_MIPS:
return __libunwind_arch_unwind_info__new_mips(thread, sample, max_stack,
best_effort, first_ip);
case EM_PPC:
return __libunwind_arch_unwind_info__new_ppc32(thread, sample, max_stack,
best_effort, first_ip);
case EM_PPC64:
return __libunwind_arch_unwind_info__new_ppc64(thread, sample, max_stack,
best_effort, first_ip);
case EM_RISCV:
return __libunwind_arch_unwind_info__new_riscv(thread, sample, max_stack,
best_effort, first_ip);
case EM_S390:
return __libunwind_arch_unwind_info__new_s390(thread, sample, max_stack,
best_effort, first_ip);
case EM_386:
return __libunwind_arch_unwind_info__new_i386(thread, sample, max_stack,
best_effort, first_ip);
case EM_X86_64:
return __libunwind_arch_unwind_info__new_x86_64(thread, sample, max_stack,
best_effort, first_ip);
default:
pr_err("ELF MACHINE %x is not supported.\n", e_machine);
return NULL;
}
}
void libunwind_arch_unwind_info__delete(struct unwind_info *ui)
{
free(ui);
}
int libunwind_arch__unwind_step(struct unwind_info *ui)
{
switch (ui->e_machine) {
case EM_ARM:
return __libunwind_arch__unwind_step_arm(ui);
case EM_AARCH64:
return __libunwind_arch__unwind_step_arm64(ui);
case EM_LOONGARCH:
return __libunwind_arch__unwind_step_loongarch(ui);
case EM_MIPS:
return __libunwind_arch__unwind_step_mips(ui);
case EM_PPC:
return __libunwind_arch__unwind_step_ppc32(ui);
case EM_PPC64:
return __libunwind_arch__unwind_step_ppc64(ui);
case EM_RISCV:
return __libunwind_arch__unwind_step_riscv(ui);
case EM_S390:
return __libunwind_arch__unwind_step_s390(ui);
case EM_386:
return __libunwind_arch__unwind_step_i386(ui);
case EM_X86_64:
return __libunwind_arch__unwind_step_x86_64(ui);
default:
pr_err("ELF MACHINE %x is not supported.\n", ui->e_machine);
return -EINVAL;
}
}