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