mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-15 04:31:49 +08:00
345bd07cce
I would like to be able to use non-trivial types in gdbarch_tdep types. This is not possible at the moment (in theory), because of the one definition rule. To allow it, rename all gdbarch_tdep types to <arch>_gdbarch_tdep, and make them inherit from a gdbarch_tdep base class. The inheritance is necessary to be able to pass pointers to all these <arch>_gdbarch_tdep objects to gdbarch_alloc, which takes a pointer to gdbarch_tdep. These objects are never deleted through a base class pointer, so I didn't include a virtual destructor. In the future, if gdbarch objects deletable, I could imagine that the gdbarch_tdep objects could become owned by the gdbarch objects, and then it would become useful to have a virtual destructor (so that the gdbarch object can delete the owned gdbarch_tdep object). But that's not necessary right now. It turns out that RISC-V already has a gdbarch_tdep that is non-default-constructible, so that provides a good motivation for this change. Most changes are fairly straightforward, mostly needing to add some casts all over the place. There is however the xtensa architecture, doing its own little weird thing to define its gdbarch_tdep. I did my best to adapt it, but I can't test those changes. Change-Id: Ic001903f91ddd106bd6ca09a79dabe8df2d69f3b
388 lines
10 KiB
C
388 lines
10 KiB
C
/* Target-dependent code for BPF.
|
||
|
||
Copyright (C) 2020-2021 Free Software Foundation, Inc.
|
||
|
||
This file is part of GDB.
|
||
|
||
This program is free software; you can redistribute it and/or modify
|
||
it under the terms of the GNU General Public License as published by
|
||
the Free Software Foundation; either version 3 of the License, or
|
||
(at your option) any later version.
|
||
|
||
This program is distributed in the hope that it will be useful,
|
||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
GNU General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||
|
||
#include "defs.h"
|
||
#include "arch-utils.h"
|
||
#include "dis-asm.h"
|
||
#include "frame.h"
|
||
#include "frame-unwind.h"
|
||
#include "trad-frame.h"
|
||
#include "symtab.h"
|
||
#include "value.h"
|
||
#include "gdbcmd.h"
|
||
#include "breakpoint.h"
|
||
#include "inferior.h"
|
||
#include "regcache.h"
|
||
#include "target.h"
|
||
#include "dwarf2/frame.h"
|
||
#include "osabi.h"
|
||
#include "target-descriptions.h"
|
||
#include "remote.h"
|
||
|
||
|
||
/* eBPF registers. */
|
||
|
||
enum bpf_regnum
|
||
{
|
||
BPF_R0_REGNUM, /* return value */
|
||
BPF_R1_REGNUM,
|
||
BPF_R2_REGNUM,
|
||
BPF_R3_REGNUM,
|
||
BPF_R4_REGNUM,
|
||
BPF_R5_REGNUM,
|
||
BPF_R6_REGNUM,
|
||
BPF_R7_REGNUM,
|
||
BPF_R8_REGNUM,
|
||
BPF_R9_REGNUM,
|
||
BPF_R10_REGNUM, /* sp */
|
||
BPF_PC_REGNUM,
|
||
};
|
||
|
||
#define BPF_NUM_REGS (BPF_PC_REGNUM + 1)
|
||
|
||
/* Target-dependent structure in gdbarch. */
|
||
struct bpf_gdbarch_tdep : gdbarch_tdep
|
||
{
|
||
};
|
||
|
||
|
||
/* Internal debugging facilities. */
|
||
|
||
/* When this is set to non-zero debugging information will be
|
||
printed. */
|
||
|
||
static unsigned int bpf_debug_flag = 0;
|
||
|
||
/* The show callback for 'show debug bpf'. */
|
||
|
||
static void
|
||
show_bpf_debug (struct ui_file *file, int from_tty,
|
||
struct cmd_list_element *c, const char *value)
|
||
{
|
||
fprintf_filtered (file, _("Debugging of BPF is %s.\n"), value);
|
||
}
|
||
|
||
|
||
/* BPF registers. */
|
||
|
||
static const char *bpf_register_names[] =
|
||
{
|
||
"r0", "r1", "r2", "r3", "r4", "r5", "r6", "r7",
|
||
"r8", "r9", "r10", "pc"
|
||
};
|
||
|
||
/* Return the name of register REGNUM. */
|
||
|
||
static const char *
|
||
bpf_register_name (struct gdbarch *gdbarch, int reg)
|
||
{
|
||
if (reg >= 0 && reg < BPF_NUM_REGS)
|
||
return bpf_register_names[reg];
|
||
return NULL;
|
||
}
|
||
|
||
/* Return the GDB type of register REGNUM. */
|
||
|
||
static struct type *
|
||
bpf_register_type (struct gdbarch *gdbarch, int reg)
|
||
{
|
||
if (reg == BPF_R10_REGNUM)
|
||
return builtin_type (gdbarch)->builtin_data_ptr;
|
||
else if (reg == BPF_PC_REGNUM)
|
||
return builtin_type (gdbarch)->builtin_func_ptr;
|
||
return builtin_type (gdbarch)->builtin_int64;
|
||
}
|
||
|
||
/* Return the GDB register number corresponding to DWARF's REG. */
|
||
|
||
static int
|
||
bpf_dwarf2_reg_to_regnum (struct gdbarch *gdbarch, int reg)
|
||
{
|
||
if (reg >= 0 && reg < BPF_NUM_REGS)
|
||
return reg;
|
||
return -1;
|
||
}
|
||
|
||
/* Implement the "print_insn" gdbarch method. */
|
||
|
||
static int
|
||
bpf_gdb_print_insn (bfd_vma memaddr, disassemble_info *info)
|
||
{
|
||
info->symbols = NULL;
|
||
return default_print_insn (memaddr, info);
|
||
}
|
||
|
||
|
||
/* Return PC of first real instruction of the function starting at
|
||
START_PC. */
|
||
|
||
static CORE_ADDR
|
||
bpf_skip_prologue (struct gdbarch *gdbarch, CORE_ADDR start_pc)
|
||
{
|
||
fprintf_unfiltered (gdb_stdlog,
|
||
"Skipping prologue: start_pc=%s\n",
|
||
paddress (gdbarch, start_pc));
|
||
/* XXX: to be completed. */
|
||
return start_pc + 0;
|
||
}
|
||
|
||
|
||
/* Frame unwinder.
|
||
|
||
XXX it is not clear how to unwind in eBPF, since the stack is not
|
||
guaranteed to be contiguous, and therefore no relative stack
|
||
addressing can be done in the callee in order to access the
|
||
caller's stack frame. To explore with xBPF, which will relax this
|
||
restriction. */
|
||
|
||
/* Given THIS_FRAME, return its ID. */
|
||
|
||
static void
|
||
bpf_frame_this_id (struct frame_info *this_frame,
|
||
void **this_prologue_cache,
|
||
struct frame_id *this_id)
|
||
{
|
||
/* Note that THIS_ID defaults to the outermost frame if we don't set
|
||
anything here. See frame.c:compute_frame_id. */
|
||
}
|
||
|
||
/* Return the reason why we can't unwind past THIS_FRAME. */
|
||
|
||
static enum unwind_stop_reason
|
||
bpf_frame_unwind_stop_reason (struct frame_info *this_frame,
|
||
void **this_cache)
|
||
{
|
||
return UNWIND_OUTERMOST;
|
||
}
|
||
|
||
/* Ask THIS_FRAME to unwind its register. */
|
||
|
||
static struct value *
|
||
bpf_frame_prev_register (struct frame_info *this_frame,
|
||
void **this_prologue_cache, int regnum)
|
||
{
|
||
return frame_unwind_got_register (this_frame, regnum, regnum);
|
||
}
|
||
|
||
/* Frame unwinder machinery for BPF. */
|
||
|
||
static const struct frame_unwind bpf_frame_unwind =
|
||
{
|
||
"bpf prologue",
|
||
NORMAL_FRAME,
|
||
bpf_frame_unwind_stop_reason,
|
||
bpf_frame_this_id,
|
||
bpf_frame_prev_register,
|
||
NULL,
|
||
default_frame_sniffer
|
||
};
|
||
|
||
|
||
/* Breakpoints. */
|
||
|
||
/* Enum describing the different kinds of breakpoints. We currently
|
||
just support one, implemented by the brkpt xbpf instruction. */
|
||
|
||
enum bpf_breakpoint_kinds
|
||
{
|
||
BPF_BP_KIND_BRKPT = 0,
|
||
};
|
||
|
||
/* Implement the breakpoint_kind_from_pc gdbarch method. */
|
||
|
||
static int
|
||
bpf_breakpoint_kind_from_pc (struct gdbarch *gdbarch, CORE_ADDR *start_pc)
|
||
{
|
||
/* We support just one kind of breakpoint. */
|
||
return BPF_BP_KIND_BRKPT;
|
||
}
|
||
|
||
/* Implement the sw_breakpoint_from_kind gdbarch method. */
|
||
|
||
static const gdb_byte *
|
||
bpf_sw_breakpoint_from_kind (struct gdbarch *gdbarch, int kind, int *size)
|
||
{
|
||
static unsigned char brkpt_insn[]
|
||
= {0x8c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
|
||
|
||
switch (kind)
|
||
{
|
||
case BPF_BP_KIND_BRKPT:
|
||
*size = 8;
|
||
return brkpt_insn;
|
||
default:
|
||
gdb_assert_not_reached ("unexpected BPF breakpoint kind");
|
||
}
|
||
}
|
||
|
||
|
||
/* Assuming THIS_FRAME is a dummy frame, return its frame ID. */
|
||
|
||
static struct frame_id
|
||
bpf_dummy_id (struct gdbarch *gdbarch, struct frame_info *this_frame)
|
||
{
|
||
CORE_ADDR sp = get_frame_register_unsigned (this_frame,
|
||
gdbarch_sp_regnum (gdbarch));
|
||
return frame_id_build (sp, get_frame_pc (this_frame));
|
||
}
|
||
|
||
/* Implement the push dummy call gdbarch callback. */
|
||
|
||
static CORE_ADDR
|
||
bpf_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
|
||
struct regcache *regcache, CORE_ADDR bp_addr,
|
||
int nargs, struct value **args, CORE_ADDR sp,
|
||
function_call_return_method return_method,
|
||
CORE_ADDR struct_addr)
|
||
{
|
||
fprintf_unfiltered (gdb_stdlog, "Pushing dummy call: sp=%s\n",
|
||
paddress (gdbarch, sp));
|
||
/* XXX writeme */
|
||
return sp;
|
||
}
|
||
|
||
/* Extract a function return value of TYPE from REGCACHE,
|
||
and copy it into VALBUF. */
|
||
|
||
static void
|
||
bpf_extract_return_value (struct type *type, struct regcache *regcache,
|
||
gdb_byte *valbuf)
|
||
{
|
||
int len = TYPE_LENGTH (type);
|
||
gdb_byte vbuf[8];
|
||
|
||
gdb_assert (len <= 8);
|
||
regcache->cooked_read (BPF_R0_REGNUM, vbuf);
|
||
memcpy (valbuf, vbuf + 8 - len, len);
|
||
}
|
||
|
||
/* Store the function return value of type TYPE from VALBUF into REGNAME. */
|
||
|
||
static void
|
||
bpf_store_return_value (struct type *type, struct regcache *regcache,
|
||
const gdb_byte *valbuf)
|
||
{
|
||
int len = TYPE_LENGTH (type);
|
||
gdb_byte vbuf[8];
|
||
|
||
gdb_assert (len <= 8);
|
||
memset (vbuf, 0, sizeof (vbuf));
|
||
memcpy (vbuf + 8 - len, valbuf, len);
|
||
regcache->cooked_write (BPF_R0_REGNUM, vbuf);
|
||
}
|
||
|
||
/* Handle function's return value. */
|
||
|
||
static enum return_value_convention
|
||
bpf_return_value (struct gdbarch *gdbarch, struct value *function,
|
||
struct type *type, struct regcache *regcache,
|
||
gdb_byte *readbuf, const gdb_byte *writebuf)
|
||
{
|
||
int len = TYPE_LENGTH (type);
|
||
|
||
if (len > 8)
|
||
return RETURN_VALUE_STRUCT_CONVENTION;
|
||
|
||
if (readbuf != NULL)
|
||
bpf_extract_return_value (type, regcache, readbuf);
|
||
if (writebuf != NULL)
|
||
bpf_store_return_value (type, regcache, writebuf);
|
||
|
||
return RETURN_VALUE_REGISTER_CONVENTION;
|
||
}
|
||
|
||
|
||
/* Initialize the current architecture based on INFO. If possible, re-use an
|
||
architecture from ARCHES, which is a list of architectures already created
|
||
during this debugging session. */
|
||
|
||
static struct gdbarch *
|
||
bpf_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
|
||
{
|
||
/* If there is already a candidate, use it. */
|
||
arches = gdbarch_list_lookup_by_info (arches, &info);
|
||
if (arches != NULL)
|
||
return arches->gdbarch;
|
||
|
||
/* Allocate space for the new architecture. */
|
||
bpf_gdbarch_tdep *tdep = new bpf_gdbarch_tdep;
|
||
struct gdbarch *gdbarch = gdbarch_alloc (&info, tdep);
|
||
|
||
/* Information about registers, etc. */
|
||
set_gdbarch_num_regs (gdbarch, BPF_NUM_REGS);
|
||
set_gdbarch_register_name (gdbarch, bpf_register_name);
|
||
set_gdbarch_register_type (gdbarch, bpf_register_type);
|
||
|
||
/* Register numbers of various important registers. */
|
||
set_gdbarch_sp_regnum (gdbarch, BPF_R10_REGNUM);
|
||
set_gdbarch_pc_regnum (gdbarch, BPF_PC_REGNUM);
|
||
|
||
/* Map DWARF2 registers to GDB registers. */
|
||
set_gdbarch_dwarf2_reg_to_regnum (gdbarch, bpf_dwarf2_reg_to_regnum);
|
||
|
||
/* Call dummy code. */
|
||
set_gdbarch_call_dummy_location (gdbarch, ON_STACK);
|
||
set_gdbarch_dummy_id (gdbarch, bpf_dummy_id);
|
||
set_gdbarch_push_dummy_call (gdbarch, bpf_push_dummy_call);
|
||
|
||
/* Returning results. */
|
||
set_gdbarch_return_value (gdbarch, bpf_return_value);
|
||
|
||
/* Advance PC across function entry code. */
|
||
set_gdbarch_skip_prologue (gdbarch, bpf_skip_prologue);
|
||
|
||
/* Stack grows downward. */
|
||
set_gdbarch_inner_than (gdbarch, core_addr_lessthan);
|
||
|
||
/* Breakpoint manipulation. */
|
||
set_gdbarch_breakpoint_kind_from_pc (gdbarch, bpf_breakpoint_kind_from_pc);
|
||
set_gdbarch_sw_breakpoint_from_kind (gdbarch, bpf_sw_breakpoint_from_kind);
|
||
|
||
/* Frame handling. */
|
||
set_gdbarch_frame_args_skip (gdbarch, 8);
|
||
|
||
/* Disassembly. */
|
||
set_gdbarch_print_insn (gdbarch, bpf_gdb_print_insn);
|
||
|
||
/* Hook in ABI-specific overrides, if they have been registered. */
|
||
gdbarch_init_osabi (info, gdbarch);
|
||
|
||
/* Install unwinders. */
|
||
frame_unwind_append_unwinder (gdbarch, &bpf_frame_unwind);
|
||
|
||
return gdbarch;
|
||
}
|
||
|
||
void _initialize_bpf_tdep ();
|
||
void
|
||
_initialize_bpf_tdep ()
|
||
{
|
||
register_gdbarch_init (bfd_arch_bpf, bpf_gdbarch_init);
|
||
|
||
/* Add commands 'set/show debug bpf'. */
|
||
add_setshow_zuinteger_cmd ("bpf", class_maintenance,
|
||
&bpf_debug_flag,
|
||
_("Set BPF debugging."),
|
||
_("Show BPF debugging."),
|
||
_("Enables BPF specific debugging output."),
|
||
NULL,
|
||
&show_bpf_debug,
|
||
&setdebuglist, &showdebuglist);
|
||
}
|