diff options
author | Jose E. Marchesi <jose.marchesi@oracle.com> | 2020-08-04 18:01:55 +0200 |
---|---|---|
committer | Jose E. Marchesi <jose.marchesi@oracle.com> | 2020-08-04 18:01:55 +0200 |
commit | 39791af2a2191a2f7765d7809ecedcd0442138bf (patch) | |
tree | bb7328d6686a00b465206f118d8cb9f959b7e165 /gdb/bpf-tdep.c | |
parent | 10c381b53f8d0295287b8205f170dfedd953993b (diff) | |
download | gdb-39791af2a2191a2f7765d7809ecedcd0442138bf.zip gdb-39791af2a2191a2f7765d7809ecedcd0442138bf.tar.gz gdb-39791af2a2191a2f7765d7809ecedcd0442138bf.tar.bz2 |
gdb: support for eBPF
This patch adds basic support for the eBPF target: tdep and build
machinery. The accompanying simulator is introduced in subsequent
patches.
gdb/ChangeLog:
2020-08-04 Weimin Pan <weimin.pan@oracle.com>
Jose E. Marchesi <jose.marchesi@oracle.com>
* configure.tgt: Add entry for bpf-*-*.
* Makefile.in (ALL_TARGET_OBS): Add bpf-tdep.o
(ALLDEPFILES): Add bpf-tdep.c.
* bpf-tdep.c: New file.
* MAINTAINERS: Add bpf target and maintainer.
gdb/doc/ChangeLog:
2020-08-04 Jose E. Marchesi <jose.marchesi@oracle.com>
* gdb.texinfo (Contributors): Add information for the eBPF
support.
(BPF): New section.
Diffstat (limited to 'gdb/bpf-tdep.c')
-rw-r--r-- | gdb/bpf-tdep.c | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/gdb/bpf-tdep.c b/gdb/bpf-tdep.c new file mode 100644 index 0000000..4854325 --- /dev/null +++ b/gdb/bpf-tdep.c @@ -0,0 +1,386 @@ +/* Target-dependent code for BPF. + + Copyright (C) 2020 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 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 = +{ + 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. */ + struct gdbarch_tdep *tdep = XCNEW (struct 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 (void) +{ + 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); +} |