Provides API for crash_target to fetch registers of given
CPU. It will allow gdb to perform such commands as "bt",
"frame", "info locals".
Highlevel API is crash_get_cpu_reg (). It calls machine
(architecture) specific function: machdep->get_cpu_reg().
Input arguments such as register number and register size
come from gdb arch information. So, get_cpu_regs()
implementations in crash must understand it.
Signed-off-by: Alexey Makhalov <amakhalov(a)vmware.com>
---
crash_target.c | 33 ++++++++++++++++++++++++++++++++-
defs.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++
gdb_interface.c | 18 +++++++++++++-----
vmware_vmss.c | 51 ++++++++++++++++++++++++++++++++++++++++++++++++++-
x86_64.c | 16 ++++++++++++++++
5 files changed, 164 insertions(+), 7 deletions(-)
diff --git a/crash_target.c b/crash_target.c
index 5f1c08e..b8796ec 100644
--- a/crash_target.c
+++ b/crash_target.c
@@ -27,6 +27,8 @@ void crash_target_init (void);
extern "C" int gdb_readmem_callback(unsigned long, void *, int, int);
extern "C" int crash_get_nr_cpus(void);
+extern "C" int crash_get_cpu_reg (int cpu, int regno, const char *regname,
+ int regsize, void *val);
/* The crash target. */
@@ -44,6 +46,7 @@ public:
const target_info &info () const override
{ return crash_target_info; }
+ void fetch_registers (struct regcache *, int) override;
enum target_xfer_status xfer_partial (enum target_object object,
const char *annex,
gdb_byte *readbuf,
@@ -54,7 +57,7 @@ public:
bool has_all_memory () override { return true; }
bool has_memory () override { return true; }
bool has_stack () override { return true; }
- bool has_registers () override { return false; }
+ bool has_registers () override { return true; }
bool thread_alive (ptid_t ptid) override { return true; }
std::string pid_to_str (ptid_t ptid) override
{ return string_printf ("CPU %ld", ptid.tid ()); }
@@ -62,6 +65,28 @@ public:
};
+/* We just get all the registers, so we don't use regno. */
+void
+crash_target::fetch_registers (struct regcache *regcache, int regno)
+{
+ gdb_byte regval[16];
+ int cpu = inferior_ptid.tid();
+ struct gdbarch *arch = regcache->arch ();
+
+ for (int r = 0; r < gdbarch_num_regs (arch); r++)
+ {
+ const char *regname = gdbarch_register_name(arch, r);
+ int regsize = register_size (arch, r);
+ if (regsize > sizeof (regval))
+ error (_("fatal error: buffer size is not enough to fit register
value"));
+
+ if (crash_get_cpu_reg (cpu, r, regname, regsize, (void *)®val))
+ regcache->raw_supply (r, regval);
+ else
+ regcache->raw_supply (r, NULL);
+ }
+}
+
enum target_xfer_status
crash_target::xfer_partial (enum target_object object, const char *annex,
gdb_byte *readbuf, const gdb_byte *writebuf,
@@ -101,4 +126,10 @@ crash_target_init (void)
if (!i)
switch_to_thread (thread);
}
+
+ /* Fetch all registers from core file. */
+ target_fetch_registers (get_current_regcache (), -1);
+
+ /* Now, set up the frame cache. */
+ reinit_frame_cache ();
}
diff --git a/defs.h b/defs.h
index 169d4dc..704c14a 100644
--- a/defs.h
+++ b/defs.h
@@ -1000,6 +1000,7 @@ struct machdep_table {
ulong (*processor_speed)(void);
int (*uvtop)(struct task_context *, ulong, physaddr_t *, int);
int (*kvtop)(struct task_context *, ulong, physaddr_t *, int);
+ int (*get_cpu_reg)(int, int, const char *, int, void *);
ulong (*get_task_pgd)(ulong);
void (*dump_irq)(int);
void (*get_stack_frame)(struct bt_info *, ulong *, ulong *);
@@ -6706,6 +6707,7 @@ int vmware_vmss_get_nr_cpus(void);
int vmware_vmss_get_cr3_cr4_idtr(int, ulong *, ulong *, ulong *);
int vmware_vmss_phys_base(ulong *phys_base);
int vmware_vmss_set_phys_base(ulong);
+int vmware_vmss_get_cpu_reg(int, int, const char *, int, void *);
/*
* vmware_guestdump.c
@@ -7130,4 +7132,55 @@ extern int have_full_symbols(void);
#define XEN_HYPERVISOR_ARCH
#endif
+#if defined(X86_64)
+/*
+ * Register numbers must be in sync with gdb/features/i386/64bit-core.c
+ * to make crash_target->fetch_registers() ---> machdep->get_cpu_reg()
+ * working properly.
+ */
+enum x86_64_regnum {
+ RAX_REGNUM,
+ RBX_REGNUM,
+ RCX_REGNUM,
+ RDX_REGNUM,
+ RSI_REGNUM,
+ RDI_REGNUM,
+ RBP_REGNUM,
+ RSP_REGNUM,
+ R8_REGNUM,
+ R9_REGNUM,
+ R10_REGNUM,
+ R11_REGNUM,
+ R12_REGNUM,
+ R13_REGNUM,
+ R14_REGNUM,
+ R15_REGNUM,
+ RIP_REGNUM,
+ EFLAGS_REGNUM,
+ CS_REGNUM,
+ SS_REGNUM,
+ DS_REGNUM,
+ ES_REGNUM,
+ FS_REGNUM,
+ GS_REGNUM,
+ ST0_REGNUM,
+ ST1_REGNUM,
+ ST2_REGNUM,
+ ST3_REGNUM,
+ ST4_REGNUM,
+ ST5_REGNUM,
+ ST6_REGNUM,
+ ST7_REGNUM,
+ FCTRL_REGNUM,
+ FSTAT_REGNUM,
+ FTAG_REGNUM,
+ FISEG_REGNUM,
+ FIOFF_REGNUM,
+ FOSEG_REGNUM,
+ FOOFF_REGNUM,
+ FOP_REGNUM,
+ LAST_REGNUM
+};
+#endif
+
#endif /* !GDB_COMMON */
diff --git a/gdb_interface.c b/gdb_interface.c
index c1ba5e0..6115db3 100644
--- a/gdb_interface.c
+++ b/gdb_interface.c
@@ -698,10 +698,9 @@ is_gdb_command(int merge_orig_args, ulong flags)
static char *prohibited_list[] = {
"run", "r", "break", "b", "tbreak",
"hbreak", "thbreak", "rbreak",
"watch", "rwatch", "awatch", "attach",
"continue", "c", "fg", "detach",
- "finish", "handle", "interrupt", "jump",
"kill", "next", "nexti",
- "signal", "step", "s", "stepi",
"target", "thread", "until", "delete",
- "clear", "disable", "enable", "condition",
"ignore", "frame",
- "select-frame", "f", "up", "down",
"catch", "tcatch", "return",
+ "finish", "handle", "interrupt", "jump",
"kill", "next", "nexti", "signal",
+ "step", "s", "stepi", "target",
"until", "delete", "clear", "disable",
+ "enable", "condition", "ignore", "catch",
"tcatch", "return",
"file", "exec-file", "core-file", "symbol-file",
"load", "si", "ni",
"shell",
NULL /* must be last */
@@ -1056,7 +1055,6 @@ get_frame_offset(ulong pc)
}
#endif /* !ALPHA */
-
unsigned long crash_get_kaslr_offset(void);
unsigned long crash_get_kaslr_offset(void)
{
@@ -1065,6 +1063,8 @@ unsigned long crash_get_kaslr_offset(void)
/* Callbacks for crash_target */
int crash_get_nr_cpus(void);
+int crash_get_cpu_reg (int cpu, int regno, const char *regname,
+ int regsize, void *val);
int crash_get_nr_cpus(void)
{
@@ -1081,3 +1081,11 @@ int crash_get_nr_cpus(void)
return 1;
}
+int crash_get_cpu_reg (int cpu, int regno, const char *regname,
+ int regsize, void *value)
+{
+ if (!machdep->get_cpu_reg)
+ return FALSE;
+ return machdep->get_cpu_reg(cpu, regno, regname, regsize, value);
+}
+
diff --git a/vmware_vmss.c b/vmware_vmss.c
index 52d58e8..fbd94bd 100644
--- a/vmware_vmss.c
+++ b/vmware_vmss.c
@@ -1,7 +1,7 @@
/*
* vmware_vmss.c
*
- * Copyright (c) 2015 VMware, Inc.
+ * Copyright (c) 2015, 2020 VMware, Inc.
* Copyright (c) 2018 Red Hat Inc.
*
* This program is free software; you can redistribute it and/or modify
@@ -16,6 +16,7 @@
*
* Authors: Dyno Hongjun Fu <hfu(a)vmware.com>
* Sergio Lopez <slp(a)redhat.com>
+ * Alexey Makhalov <amakhalov(a)vmware.com>
*/
#include "defs.h"
@@ -892,6 +893,54 @@ vmware_vmss_get_cr3_cr4_idtr(int cpu, ulong *cr3, ulong *cr4, ulong
*idtr)
}
int
+vmware_vmss_get_cpu_reg(int cpu, int regno, const char *name, int size,
+ void *value)
+{
+ if (cpu >= vmss.num_vcpus)
+ return FALSE;
+
+ /* All supported registers are 8 bytes long. */
+ if (size != 8)
+ return FALSE;
+
+#define CASE(R,r) \
+ case R##_REGNUM: \
+ if (!(vmss.vcpu_regs[cpu] & REGS_PRESENT_##R)) \
+ return FALSE; \
+ memcpy(value, &vmss.regs64[cpu]->r, size); \
+ break
+
+
+ switch (regno) {
+ CASE (RAX, rax);
+ CASE (RBX, rbx);
+ CASE (RCX, rcx);
+ CASE (RDX, rdx);
+ CASE (RSI, rsi);
+ CASE (RDI, rdi);
+ CASE (RBP, rbp);
+ CASE (RSP, rsp);
+ CASE (R8, r8);
+ CASE (R9, r9);
+ CASE (R10, r10);
+ CASE (R11, r11);
+ CASE (R12, r12);
+ CASE (R13, r13);
+ CASE (R14, r14);
+ CASE (R15, r15);
+ CASE (RIP, rip);
+ case EFLAGS_REGNUM:
+ if (!(vmss.vcpu_regs[cpu] & REGS_PRESENT_RFLAGS))
+ return FALSE;
+ memcpy(value, &vmss.regs64[cpu]->rflags, size);
+ break;
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
vmware_vmss_phys_base(ulong *phys_base)
{
*phys_base = vmss.phys_base;
diff --git a/x86_64.c b/x86_64.c
index 62a8563..8b5d2c2 100644
--- a/x86_64.c
+++ b/x86_64.c
@@ -126,6 +126,7 @@ static int x86_64_get_framesize(struct bt_info *, ulong, ulong);
static void x86_64_framesize_debug(struct bt_info *);
static void x86_64_get_active_set(void);
static int x86_64_get_kvaddr_ranges(struct vaddr_range *);
+static int x86_64_get_cpu_reg(int, int, const char *, int, void *);
static int x86_64_verify_paddr(uint64_t);
static void GART_init(void);
static void x86_64_exception_stacks_init(void);
@@ -191,6 +192,7 @@ x86_64_init(int when)
machdep->machspec->irq_eframe_link = UNINITIALIZED;
machdep->machspec->irq_stack_gap = UNINITIALIZED;
machdep->get_kvaddr_ranges = x86_64_get_kvaddr_ranges;
+ machdep->get_cpu_reg = x86_64_get_cpu_reg;
if (machdep->cmdline_args[0])
parse_cmdline_args();
if ((string = pc->read_vmcoreinfo("relocate"))) {
@@ -875,6 +877,7 @@ x86_64_dump_machdep_table(ulong arg)
fprintf(fp, " is_page_ptr: x86_64_is_page_ptr()\n");
fprintf(fp, " verify_paddr: x86_64_verify_paddr()\n");
fprintf(fp, " get_kvaddr_ranges: x86_64_get_kvaddr_ranges()\n");
+ fprintf(fp, " get_cpu_reg: x86_64_get_cpu_reg()\n");
fprintf(fp, " init_kernel_pgd: x86_64_init_kernel_pgd()\n");
fprintf(fp, "clear_machdep_cache: x86_64_clear_machdep_cache()\n");
fprintf(fp, " xendump_p2m_create: %s\n", PVOPS_XEN() ?
@@ -8883,6 +8886,19 @@ x86_64_get_kvaddr_ranges(struct vaddr_range *vrp)
return cnt;
}
+static int
+x86_64_get_cpu_reg(int cpu, int regno, const char *name,
+ int size, void *value)
+{
+ if (regno >= LAST_REGNUM)
+ return FALSE;
+
+ if (VMSS_DUMPFILE())
+ return vmware_vmss_get_cpu_reg(cpu, regno, name, size, value);
+
+ return FALSE;
+}
+
/*
* Determine the physical memory range reserved for GART.
*/
--
2.11.0