mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-12-11 19:33:33 +08:00
2f822da535
Previously, the prefixname field of struct cmd_list_element was manually
set for prefix commands. This seems verbose and error prone as it
required every single call to functions adding prefix commands to
specify the prefix name while the same information can be easily
generated.
Historically, this was not possible as the prefix field was null for
many commands, but this was fixed in commit
3f4d92ebdf
by Philippe Waroquiers, so
we can rely on the prefix field being set when generating the prefix
name.
This commit also fixes a use after free in this scenario:
* A command gets created via Python (using the gdb.Command class).
The prefix name member is dynamically allocated.
* An alias to the new command is created. The alias's prefixname is set
to point to the prefixname for the original command with a direct
assignment.
* A new command with the same name as the Python command is created.
* The object for the original Python command gets freed and its
prefixname gets freed as well.
* The alias is updated to point to the new command, but its prefixname
is not updated so it keeps pointing to the freed one.
gdb/ChangeLog:
* command.h (add_prefix_cmd): Remove the prefixname argument as
it can now be generated automatically. Update all callers.
(add_basic_prefix_cmd): Ditto.
(add_show_prefix_cmd): Ditto.
(add_prefix_cmd_suppress_notification): Ditto.
(add_abbrev_prefix_cmd): Ditto.
* cli/cli-decode.c (add_prefix_cmd): Ditto.
(add_basic_prefix_cmd): Ditto.
(add_show_prefix_cmd): Ditto.
(add_prefix_cmd_suppress_notification): Ditto.
(add_prefix_cmd_suppress_notification): Ditto.
(add_abbrev_prefix_cmd): Ditto.
* cli/cli-decode.h (struct cmd_list_element): Replace the
prefixname member variable with a method which generates the
prefix name at runtime. Update all code reading the prefix
name to use the method, and remove all code setting it.
* python/py-cmd.c (cmdpy_destroyer): Remove code to free the
prefixname member as it's now a method.
(cmdpy_function): Determine if the command is a prefix by
looking at prefixlist, not prefixname.
417 lines
11 KiB
C
417 lines
11 KiB
C
/* BSD Kernel Data Access Library (libkvm) interface.
|
||
|
||
Copyright (C) 2004-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/>. */
|
||
|
||
#define _KMEMUSER
|
||
#include "defs.h"
|
||
#include "cli/cli-cmds.h"
|
||
#include "command.h"
|
||
#include "frame.h"
|
||
#include "regcache.h"
|
||
#include "target.h"
|
||
#include "process-stratum-target.h"
|
||
#include "value.h"
|
||
#include "gdbcore.h"
|
||
#include "inferior.h" /* for get_exec_file */
|
||
#include "gdbthread.h"
|
||
#include "gdbsupport/pathstuff.h"
|
||
|
||
#include <fcntl.h>
|
||
#include <kvm.h>
|
||
#ifdef HAVE_NLIST_H
|
||
#include <nlist.h>
|
||
#endif
|
||
#include <paths.h>
|
||
#include "readline/readline.h"
|
||
#include <sys/param.h>
|
||
#include <sys/proc.h>
|
||
#ifdef HAVE_SYS_USER_H
|
||
#include <sys/user.h>
|
||
#endif
|
||
|
||
#include "bsd-kvm.h"
|
||
|
||
/* Kernel memory device file. */
|
||
static const char *bsd_kvm_corefile;
|
||
|
||
/* Kernel memory interface descriptor. */
|
||
static kvm_t *core_kd;
|
||
|
||
/* Address of process control block. */
|
||
static struct pcb *bsd_kvm_paddr;
|
||
|
||
/* Pointer to architecture-specific function that reconstructs the
|
||
register state from PCB and supplies it to REGCACHE. */
|
||
static int (*bsd_kvm_supply_pcb)(struct regcache *regcache, struct pcb *pcb);
|
||
|
||
/* This is the ptid we use while we're connected to kvm. The kvm
|
||
target currently doesn't export any view of the running processes,
|
||
so this represents the kernel task. */
|
||
static ptid_t bsd_kvm_ptid;
|
||
|
||
/* The libkvm target. */
|
||
|
||
static const target_info bsd_kvm_target_info = {
|
||
"kvm",
|
||
N_("Kernel memory interface"),
|
||
N_("Use a kernel virtual memory image as a target.\n\
|
||
Optionally specify the filename of a core dump.")
|
||
};
|
||
|
||
class bsd_kvm_target final : public process_stratum_target
|
||
{
|
||
public:
|
||
bsd_kvm_target () = default;
|
||
|
||
const target_info &info () const override
|
||
{ return bsd_kvm_target_info; }
|
||
|
||
void close () override;
|
||
|
||
void fetch_registers (struct regcache *, int) override;
|
||
enum target_xfer_status xfer_partial (enum target_object object,
|
||
const char *annex,
|
||
gdb_byte *readbuf,
|
||
const gdb_byte *writebuf,
|
||
ULONGEST offset, ULONGEST len,
|
||
ULONGEST *xfered_len) override;
|
||
|
||
void files_info () override;
|
||
bool thread_alive (ptid_t ptid) override;
|
||
std::string pid_to_str (ptid_t) override;
|
||
|
||
bool has_memory () override { return true; }
|
||
bool has_stack () override { return true; }
|
||
bool has_registers () override { return true; }
|
||
};
|
||
|
||
/* Target ops for libkvm interface. */
|
||
static bsd_kvm_target bsd_kvm_ops;
|
||
|
||
static void
|
||
bsd_kvm_target_open (const char *arg, int from_tty)
|
||
{
|
||
char errbuf[_POSIX2_LINE_MAX];
|
||
const char *execfile = NULL;
|
||
kvm_t *temp_kd;
|
||
char *filename = NULL;
|
||
|
||
target_preopen (from_tty);
|
||
|
||
if (arg)
|
||
{
|
||
filename = tilde_expand (arg);
|
||
if (filename[0] != '/')
|
||
{
|
||
gdb::unique_xmalloc_ptr<char> temp (gdb_abspath (filename));
|
||
|
||
xfree (filename);
|
||
filename = temp.release ();
|
||
}
|
||
}
|
||
|
||
execfile = get_exec_file (0);
|
||
temp_kd = kvm_openfiles (execfile, filename, NULL,
|
||
write_files ? O_RDWR : O_RDONLY, errbuf);
|
||
if (temp_kd == NULL)
|
||
error (("%s"), errbuf);
|
||
|
||
bsd_kvm_corefile = filename;
|
||
current_inferior ()->unpush_target (&bsd_kvm_ops);
|
||
core_kd = temp_kd;
|
||
current_inferior ()->push_target (&bsd_kvm_ops);
|
||
|
||
thread_info *thr = add_thread_silent (&bsd_kvm_ops, bsd_kvm_ptid);
|
||
switch_to_thread (thr);
|
||
|
||
target_fetch_registers (get_current_regcache (), -1);
|
||
|
||
reinit_frame_cache ();
|
||
print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC, 1);
|
||
}
|
||
|
||
void
|
||
bsd_kvm_target::close ()
|
||
{
|
||
if (core_kd)
|
||
{
|
||
if (kvm_close (core_kd) == -1)
|
||
warning (("%s"), kvm_geterr(core_kd));
|
||
core_kd = NULL;
|
||
}
|
||
|
||
switch_to_no_thread ();
|
||
exit_inferior_silent (current_inferior ());
|
||
}
|
||
|
||
static LONGEST
|
||
bsd_kvm_xfer_memory (CORE_ADDR addr, ULONGEST len,
|
||
gdb_byte *readbuf, const gdb_byte *writebuf)
|
||
{
|
||
ssize_t nbytes = len;
|
||
|
||
if (readbuf)
|
||
nbytes = kvm_read (core_kd, addr, readbuf, nbytes);
|
||
if (writebuf && nbytes > 0)
|
||
nbytes = kvm_write (core_kd, addr, writebuf, nbytes);
|
||
return nbytes;
|
||
}
|
||
|
||
enum target_xfer_status
|
||
bsd_kvm_target::xfer_partial (enum target_object object,
|
||
const char *annex, gdb_byte *readbuf,
|
||
const gdb_byte *writebuf,
|
||
ULONGEST offset, ULONGEST len, ULONGEST *xfered_len)
|
||
{
|
||
switch (object)
|
||
{
|
||
case TARGET_OBJECT_MEMORY:
|
||
{
|
||
LONGEST ret = bsd_kvm_xfer_memory (offset, len, readbuf, writebuf);
|
||
|
||
if (ret < 0)
|
||
return TARGET_XFER_E_IO;
|
||
else if (ret == 0)
|
||
return TARGET_XFER_EOF;
|
||
else
|
||
{
|
||
*xfered_len = (ULONGEST) ret;
|
||
return TARGET_XFER_OK;
|
||
}
|
||
}
|
||
|
||
default:
|
||
return TARGET_XFER_E_IO;
|
||
}
|
||
}
|
||
|
||
void
|
||
bsd_kvm_target::files_info ()
|
||
{
|
||
if (bsd_kvm_corefile && strcmp (bsd_kvm_corefile, _PATH_MEM) != 0)
|
||
printf_filtered (_("\tUsing the kernel crash dump %s.\n"),
|
||
bsd_kvm_corefile);
|
||
else
|
||
printf_filtered (_("\tUsing the currently running kernel.\n"));
|
||
}
|
||
|
||
/* Fetch process control block at address PADDR. */
|
||
|
||
static int
|
||
bsd_kvm_fetch_pcb (struct regcache *regcache, struct pcb *paddr)
|
||
{
|
||
struct pcb pcb;
|
||
|
||
if (kvm_read (core_kd, (unsigned long) paddr, &pcb, sizeof pcb) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
gdb_assert (bsd_kvm_supply_pcb);
|
||
return bsd_kvm_supply_pcb (regcache, &pcb);
|
||
}
|
||
|
||
void
|
||
bsd_kvm_target::fetch_registers (struct regcache *regcache, int regnum)
|
||
{
|
||
struct nlist nl[2];
|
||
|
||
if (bsd_kvm_paddr)
|
||
{
|
||
bsd_kvm_fetch_pcb (regcache, bsd_kvm_paddr);
|
||
return;
|
||
}
|
||
|
||
/* On dumping core, BSD kernels store the faulting context (PCB)
|
||
in the variable "dumppcb". */
|
||
memset (nl, 0, sizeof nl);
|
||
nl[0].n_name = "_dumppcb";
|
||
|
||
if (kvm_nlist (core_kd, nl) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
if (nl[0].n_value != 0)
|
||
{
|
||
/* Found dumppcb. If it contains a valid context, return
|
||
immediately. */
|
||
if (bsd_kvm_fetch_pcb (regcache, (struct pcb *) nl[0].n_value))
|
||
return;
|
||
}
|
||
|
||
/* Traditional BSD kernels have a process proc0 that should always
|
||
be present. The address of proc0's PCB is stored in the variable
|
||
"proc0paddr". */
|
||
|
||
memset (nl, 0, sizeof nl);
|
||
nl[0].n_name = "_proc0paddr";
|
||
|
||
if (kvm_nlist (core_kd, nl) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
if (nl[0].n_value != 0)
|
||
{
|
||
struct pcb *paddr;
|
||
|
||
/* Found proc0paddr. */
|
||
if (kvm_read (core_kd, nl[0].n_value, &paddr, sizeof paddr) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
bsd_kvm_fetch_pcb (regcache, paddr);
|
||
return;
|
||
}
|
||
|
||
#ifdef HAVE_STRUCT_THREAD_TD_PCB
|
||
/* In FreeBSD kernels for 5.0-RELEASE and later, the PCB no longer
|
||
lives in `struct proc' but in `struct thread'. The `struct
|
||
thread' for the initial thread for proc0 can be found in the
|
||
variable "thread0". */
|
||
|
||
memset (nl, 0, sizeof nl);
|
||
nl[0].n_name = "_thread0";
|
||
|
||
if (kvm_nlist (core_kd, nl) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
if (nl[0].n_value != 0)
|
||
{
|
||
struct pcb *paddr;
|
||
|
||
/* Found thread0. */
|
||
nl[0].n_value += offsetof (struct thread, td_pcb);
|
||
if (kvm_read (core_kd, nl[0].n_value, &paddr, sizeof paddr) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
bsd_kvm_fetch_pcb (regcache, paddr);
|
||
return;
|
||
}
|
||
#endif
|
||
|
||
/* i18n: PCB == "Process Control Block". */
|
||
error (_("Cannot find a valid PCB"));
|
||
}
|
||
|
||
|
||
/* Kernel memory interface commands. */
|
||
struct cmd_list_element *bsd_kvm_cmdlist;
|
||
|
||
static void
|
||
bsd_kvm_cmd (const char *arg, int fromtty)
|
||
{
|
||
/* ??? Should this become an alias for "target kvm"? */
|
||
}
|
||
|
||
#ifndef HAVE_STRUCT_THREAD_TD_PCB
|
||
|
||
static void
|
||
bsd_kvm_proc_cmd (const char *arg, int fromtty)
|
||
{
|
||
CORE_ADDR addr;
|
||
|
||
if (arg == NULL)
|
||
error_no_arg (_("proc address"));
|
||
|
||
if (core_kd == NULL)
|
||
error (_("No kernel memory image."));
|
||
|
||
addr = parse_and_eval_address (arg);
|
||
#ifdef HAVE_STRUCT_LWP
|
||
addr += offsetof (struct lwp, l_addr);
|
||
#else
|
||
addr += offsetof (struct proc, p_addr);
|
||
#endif
|
||
|
||
if (kvm_read (core_kd, addr, &bsd_kvm_paddr, sizeof bsd_kvm_paddr) == -1)
|
||
error (("%s"), kvm_geterr (core_kd));
|
||
|
||
target_fetch_registers (get_current_regcache (), -1);
|
||
|
||
reinit_frame_cache ();
|
||
print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC, 1);
|
||
}
|
||
|
||
#endif
|
||
|
||
static void
|
||
bsd_kvm_pcb_cmd (const char *arg, int fromtty)
|
||
{
|
||
if (arg == NULL)
|
||
/* i18n: PCB == "Process Control Block". */
|
||
error_no_arg (_("pcb address"));
|
||
|
||
if (core_kd == NULL)
|
||
error (_("No kernel memory image."));
|
||
|
||
bsd_kvm_paddr = (struct pcb *)(u_long) parse_and_eval_address (arg);
|
||
|
||
target_fetch_registers (get_current_regcache (), -1);
|
||
|
||
reinit_frame_cache ();
|
||
print_stack_frame (get_selected_frame (NULL), 0, SRC_AND_LOC, 1);
|
||
}
|
||
|
||
bool
|
||
bsd_kvm_target::thread_alive (ptid_t ptid)
|
||
{
|
||
return true;
|
||
}
|
||
|
||
std::string
|
||
bsd_kvm_target::pid_to_str (ptid_t ptid)
|
||
{
|
||
return "<kvm>";
|
||
}
|
||
|
||
/* Add the libkvm interface to the list of all possible targets and
|
||
register CUPPLY_PCB as the architecture-specific process control
|
||
block interpreter. */
|
||
|
||
void
|
||
bsd_kvm_add_target (int (*supply_pcb)(struct regcache *, struct pcb *))
|
||
{
|
||
gdb_assert (bsd_kvm_supply_pcb == NULL);
|
||
bsd_kvm_supply_pcb = supply_pcb;
|
||
|
||
add_target (bsd_kvm_target_info, bsd_kvm_target_open);
|
||
|
||
add_prefix_cmd ("kvm", class_obscure, bsd_kvm_cmd, _("\
|
||
Generic command for manipulating the kernel memory interface."),
|
||
&bsd_kvm_cmdlist, 0, &cmdlist);
|
||
|
||
#ifndef HAVE_STRUCT_THREAD_TD_PCB
|
||
add_cmd ("proc", class_obscure, bsd_kvm_proc_cmd,
|
||
_("Set current context from proc address"), &bsd_kvm_cmdlist);
|
||
#endif
|
||
add_cmd ("pcb", class_obscure, bsd_kvm_pcb_cmd,
|
||
/* i18n: PCB == "Process Control Block". */
|
||
_("Set current context from pcb address"), &bsd_kvm_cmdlist);
|
||
|
||
/* Some notes on the ptid usage on this target.
|
||
|
||
The pid field represents the kvm inferior instance. Currently,
|
||
we don't support multiple kvm inferiors, but we start at 1
|
||
anyway. The lwp field is set to != 0, in case the core wants to
|
||
refer to the whole kvm inferior with ptid(1,0,0).
|
||
|
||
If kvm is made to export running processes as gdb threads,
|
||
the following form can be used:
|
||
ptid (1, 1, 0) -> kvm inferior 1, in kernel
|
||
ptid (1, 1, 1) -> kvm inferior 1, process 1
|
||
ptid (1, 1, 2) -> kvm inferior 1, process 2
|
||
ptid (1, 1, n) -> kvm inferior 1, process n */
|
||
|
||
bsd_kvm_ptid = ptid_t (1, 1, 0);
|
||
}
|