162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * dwarf-regs.c : Mapping of DWARF debug register numbers into register names. 462306a36Sopenharmony_ci * Extracted from probe-finder.c 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Written by Masami Hiramatsu <mhiramat@redhat.com> 762306a36Sopenharmony_ci */ 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <stddef.h> 1062306a36Sopenharmony_ci#include <errno.h> /* for EINVAL */ 1162306a36Sopenharmony_ci#include <string.h> /* for strcmp */ 1262306a36Sopenharmony_ci#include <linux/ptrace.h> /* for struct pt_regs */ 1362306a36Sopenharmony_ci#include <linux/kernel.h> /* for offsetof */ 1462306a36Sopenharmony_ci#include <dwarf-regs.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * See arch/x86/kernel/ptrace.c. 1862306a36Sopenharmony_ci * Different from it: 1962306a36Sopenharmony_ci * 2062306a36Sopenharmony_ci * - Since struct pt_regs is defined differently for user and kernel, 2162306a36Sopenharmony_ci * but we want to use 'ax, bx' instead of 'rax, rbx' (which is struct 2262306a36Sopenharmony_ci * field name of user's pt_regs), we make REG_OFFSET_NAME to accept 2362306a36Sopenharmony_ci * both string name and reg field name. 2462306a36Sopenharmony_ci * 2562306a36Sopenharmony_ci * - Since accessing x86_32's pt_regs from x86_64 building is difficult 2662306a36Sopenharmony_ci * and vise versa, we simply fill offset with -1, so 2762306a36Sopenharmony_ci * get_arch_regstr() still works but regs_query_register_offset() 2862306a36Sopenharmony_ci * returns error. 2962306a36Sopenharmony_ci * The only inconvenience caused by it now is that we are not allowed 3062306a36Sopenharmony_ci * to generate BPF prologue for a x86_64 kernel if perf is built for 3162306a36Sopenharmony_ci * x86_32. This is really a rare usecase. 3262306a36Sopenharmony_ci * 3362306a36Sopenharmony_ci * - Order is different from kernel's ptrace.c for get_arch_regstr(). Use 3462306a36Sopenharmony_ci * the order defined by dwarf. 3562306a36Sopenharmony_ci */ 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_cistruct pt_regs_offset { 3862306a36Sopenharmony_ci const char *name; 3962306a36Sopenharmony_ci int offset; 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci#define REG_OFFSET_END {.name = NULL, .offset = 0} 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci#ifdef __x86_64__ 4562306a36Sopenharmony_ci# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)} 4662306a36Sopenharmony_ci# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = -1} 4762306a36Sopenharmony_ci#else 4862306a36Sopenharmony_ci# define REG_OFFSET_NAME_64(n, r) {.name = n, .offset = -1} 4962306a36Sopenharmony_ci# define REG_OFFSET_NAME_32(n, r) {.name = n, .offset = offsetof(struct pt_regs, r)} 5062306a36Sopenharmony_ci#endif 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci/* TODO: switching by dwarf address size */ 5362306a36Sopenharmony_ci#ifndef __x86_64__ 5462306a36Sopenharmony_cistatic const struct pt_regs_offset x86_32_regoffset_table[] = { 5562306a36Sopenharmony_ci REG_OFFSET_NAME_32("%ax", eax), 5662306a36Sopenharmony_ci REG_OFFSET_NAME_32("%cx", ecx), 5762306a36Sopenharmony_ci REG_OFFSET_NAME_32("%dx", edx), 5862306a36Sopenharmony_ci REG_OFFSET_NAME_32("%bx", ebx), 5962306a36Sopenharmony_ci REG_OFFSET_NAME_32("$stack", esp), /* Stack address instead of %sp */ 6062306a36Sopenharmony_ci REG_OFFSET_NAME_32("%bp", ebp), 6162306a36Sopenharmony_ci REG_OFFSET_NAME_32("%si", esi), 6262306a36Sopenharmony_ci REG_OFFSET_NAME_32("%di", edi), 6362306a36Sopenharmony_ci REG_OFFSET_END, 6462306a36Sopenharmony_ci}; 6562306a36Sopenharmony_ci 6662306a36Sopenharmony_ci#define regoffset_table x86_32_regoffset_table 6762306a36Sopenharmony_ci#else 6862306a36Sopenharmony_cistatic const struct pt_regs_offset x86_64_regoffset_table[] = { 6962306a36Sopenharmony_ci REG_OFFSET_NAME_64("%ax", rax), 7062306a36Sopenharmony_ci REG_OFFSET_NAME_64("%dx", rdx), 7162306a36Sopenharmony_ci REG_OFFSET_NAME_64("%cx", rcx), 7262306a36Sopenharmony_ci REG_OFFSET_NAME_64("%bx", rbx), 7362306a36Sopenharmony_ci REG_OFFSET_NAME_64("%si", rsi), 7462306a36Sopenharmony_ci REG_OFFSET_NAME_64("%di", rdi), 7562306a36Sopenharmony_ci REG_OFFSET_NAME_64("%bp", rbp), 7662306a36Sopenharmony_ci REG_OFFSET_NAME_64("%sp", rsp), 7762306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r8", r8), 7862306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r9", r9), 7962306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r10", r10), 8062306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r11", r11), 8162306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r12", r12), 8262306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r13", r13), 8362306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r14", r14), 8462306a36Sopenharmony_ci REG_OFFSET_NAME_64("%r15", r15), 8562306a36Sopenharmony_ci REG_OFFSET_END, 8662306a36Sopenharmony_ci}; 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci#define regoffset_table x86_64_regoffset_table 8962306a36Sopenharmony_ci#endif 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci/* Minus 1 for the ending REG_OFFSET_END */ 9262306a36Sopenharmony_ci#define ARCH_MAX_REGS ((sizeof(regoffset_table) / sizeof(regoffset_table[0])) - 1) 9362306a36Sopenharmony_ci 9462306a36Sopenharmony_ci/* Return architecture dependent register string (for kprobe-tracer) */ 9562306a36Sopenharmony_ciconst char *get_arch_regstr(unsigned int n) 9662306a36Sopenharmony_ci{ 9762306a36Sopenharmony_ci return (n < ARCH_MAX_REGS) ? regoffset_table[n].name : NULL; 9862306a36Sopenharmony_ci} 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci/* Reuse code from arch/x86/kernel/ptrace.c */ 10162306a36Sopenharmony_ci/** 10262306a36Sopenharmony_ci * regs_query_register_offset() - query register offset from its name 10362306a36Sopenharmony_ci * @name: the name of a register 10462306a36Sopenharmony_ci * 10562306a36Sopenharmony_ci * regs_query_register_offset() returns the offset of a register in struct 10662306a36Sopenharmony_ci * pt_regs from its name. If the name is invalid, this returns -EINVAL; 10762306a36Sopenharmony_ci */ 10862306a36Sopenharmony_ciint regs_query_register_offset(const char *name) 10962306a36Sopenharmony_ci{ 11062306a36Sopenharmony_ci const struct pt_regs_offset *roff; 11162306a36Sopenharmony_ci for (roff = regoffset_table; roff->name != NULL; roff++) 11262306a36Sopenharmony_ci if (!strcmp(roff->name, name)) 11362306a36Sopenharmony_ci return roff->offset; 11462306a36Sopenharmony_ci return -EINVAL; 11562306a36Sopenharmony_ci} 116