/* Native debugging support for Intel x86 running DJGPP.
- Copyright (C) 1997-2013 Free Software Foundation, Inc.
+ Copyright (C) 1997-2019 Free Software Foundation, Inc.
Written by Robert Hoehne.
This file is part of GDB.
#include <fcntl.h>
-#include "i386-nat.h"
+#include "x86-nat.h"
#include "inferior.h"
+#include "infrun.h"
#include "gdbthread.h"
-#include "gdb_wait.h"
+#include "common/gdb_wait.h"
#include "gdbcore.h"
#include "command.h"
#include "gdbcmd.h"
#include "floatformat.h"
-#include "buildsym.h"
+#include "buildsym-legacy.h"
#include "i387-tdep.h"
#include "i386-tdep.h"
+#include "nat/x86-cpuid.h"
#include "value.h"
#include "regcache.h"
-#include "gdb_string.h"
#include "top.h"
#include "cli/cli-utils.h"
+#include "inf-child.h"
-#include <stdio.h> /* might be required for __DJGPP_MINOR__ */
-#include <stdlib.h>
#include <ctype.h>
-#include <errno.h>
#include <unistd.h>
#include <sys/utsname.h>
#include <io.h>
#define SOME_PID 42
static int prog_has_started = 0;
-static void go32_open (char *name, int from_tty);
-static void go32_close (void);
-static void go32_attach (struct target_ops *ops, char *args, int from_tty);
-static void go32_detach (struct target_ops *ops, char *args, int from_tty);
-static void go32_resume (struct target_ops *ops,
- ptid_t ptid, int step,
- enum gdb_signal siggnal);
-static void go32_fetch_registers (struct target_ops *ops,
- struct regcache *, int regno);
-static void store_register (const struct regcache *, int regno);
-static void go32_store_registers (struct target_ops *ops,
- struct regcache *, int regno);
-static void go32_prepare_to_store (struct regcache *);
-static int go32_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len,
- int write,
- struct mem_attrib *attrib,
- struct target_ops *target);
-static void go32_files_info (struct target_ops *target);
-static void go32_kill_inferior (struct target_ops *ops);
-static void go32_create_inferior (struct target_ops *ops, char *exec_file,
- char *args, char **env, int from_tty);
-static void go32_mourn_inferior (struct target_ops *ops);
-static int go32_can_run (void);
-
-static struct target_ops go32_ops;
-static void go32_terminal_init (void);
-static void go32_terminal_inferior (void);
-static void go32_terminal_ours (void);
#define r_ofs(x) (offsetof(TSS,x))
{GDB_SIGNAL_LAST, -1}
};
-static void
-go32_open (char *name, int from_tty)
-{
- printf_unfiltered ("Done. Use the \"run\" command to run the program.\n");
-}
+/* The go32 target. */
-static void
-go32_close (void)
+struct go32_nat_target final : public x86_nat_target<inf_child_target>
{
-}
+ void attach (const char *, int) override;
-static void
-go32_attach (struct target_ops *ops, char *args, int from_tty)
+ void resume (ptid_t, int, enum gdb_signal) override;
+
+ ptid_t wait (ptid_t, struct target_waitstatus *, int) override;
+
+ void fetch_registers (struct regcache *, int) override;
+ void store_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;
+
+ void terminal_init () override;
+
+ void terminal_inferior () override;
+
+ void terminal_ours_for_output () override;
+
+ void terminal_ours () override;
+
+ void terminal_info (const char *, int) override;
+
+ void pass_ctrlc () override;
+
+ void kill () override;
+
+ void create_inferior (const char *, const std::string &,
+ char **, int) override;
+
+ void mourn_inferior () override;
+
+ bool thread_alive (ptid_t ptid) override;
+
+ std::string pid_to_str (ptid_t) override;
+};
+
+static go32_nat_target the_go32_nat_target;
+
+void
+go32_nat_target::attach (const char *args, int from_tty)
{
error (_("\
You cannot attach to a running program on this platform.\n\
Use the `run' command to run DJGPP programs."));
}
-static void
-go32_detach (struct target_ops *ops, char *args, int from_tty)
-{
-}
-
static int resume_is_step;
static int resume_signal = -1;
-static void
-go32_resume (struct target_ops *ops,
- ptid_t ptid, int step, enum gdb_signal siggnal)
+void
+go32_nat_target::resume (ptid_t ptid, int step, enum gdb_signal siggnal)
{
int i;
static char child_cwd[FILENAME_MAX];
-static ptid_t
-go32_wait (struct target_ops *ops,
- ptid_t ptid, struct target_waitstatus *status, int options)
+ptid_t
+go32_nat_target::wait (ptid_t ptid, struct target_waitstatus *status,
+ int options)
{
int i;
unsigned char saved_opcode;
}
}
}
- return pid_to_ptid (SOME_PID);
+ return ptid_t (SOME_PID);
}
static void
fetch_register (struct regcache *regcache, int regno)
{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch *gdbarch = regcache->arch ();
if (regno < gdbarch_fp0_regnum (gdbarch))
- regcache_raw_supply (regcache, regno,
- (char *) &a_tss + regno_mapping[regno].tss_ofs);
+ regcache->raw_supply (regno,
+ (char *) &a_tss + regno_mapping[regno].tss_ofs);
else if (i386_fp_regnum_p (gdbarch, regno) || i386_fpc_regnum_p (gdbarch,
regno))
i387_supply_fsave (regcache, regno, &npx);
_("Invalid register no. %d in fetch_register."), regno);
}
-static void
-go32_fetch_registers (struct target_ops *ops,
- struct regcache *regcache, int regno)
+void
+go32_nat_target::fetch_registers (struct regcache *regcache, int regno)
{
if (regno >= 0)
fetch_register (regcache, regno);
else
{
for (regno = 0;
- regno < gdbarch_fp0_regnum (get_regcache_arch (regcache));
+ regno < gdbarch_fp0_regnum (regcache->arch ());
regno++)
fetch_register (regcache, regno);
i387_supply_fsave (regcache, -1, &npx);
static void
store_register (const struct regcache *regcache, int regno)
{
- struct gdbarch *gdbarch = get_regcache_arch (regcache);
+ struct gdbarch *gdbarch = regcache->arch ();
if (regno < gdbarch_fp0_regnum (gdbarch))
- regcache_raw_collect (regcache, regno,
- (char *) &a_tss + regno_mapping[regno].tss_ofs);
+ regcache->raw_collect (regno,
+ (char *) &a_tss + regno_mapping[regno].tss_ofs);
else if (i386_fp_regnum_p (gdbarch, regno) || i386_fpc_regnum_p (gdbarch,
regno))
i387_collect_fsave (regcache, regno, &npx);
_("Invalid register no. %d in store_register."), regno);
}
-static void
-go32_store_registers (struct target_ops *ops,
- struct regcache *regcache, int regno)
+void
+go32_nat_target::store_registers (struct regcache *regcache, int regno)
{
unsigned r;
store_register (regcache, regno);
else
{
- for (r = 0; r < gdbarch_fp0_regnum (get_regcache_arch (regcache)); r++)
+ for (r = 0; r < gdbarch_fp0_regnum (regcache->arch ()); r++)
store_register (regcache, r);
i387_collect_fsave (regcache, -1, &npx);
}
}
-static void
-go32_prepare_to_store (struct regcache *regcache)
-{
-}
+/* Const-correct version of DJGPP's write_child, which unfortunately
+ takes a non-const buffer pointer. */
static int
-go32_xfer_memory (CORE_ADDR memaddr, gdb_byte *myaddr, int len, int write,
- struct mem_attrib *attrib, struct target_ops *target)
+my_write_child (unsigned child_addr, const void *buf, unsigned len)
{
- if (write)
+ static void *buffer = NULL;
+ static unsigned buffer_len = 0;
+ int res;
+
+ if (buffer_len < len)
{
- if (write_child (memaddr, myaddr, len))
- {
- return 0;
- }
- else
- {
- return len;
- }
+ buffer = xrealloc (buffer, len);
+ buffer_len = len;
}
+
+ memcpy (buffer, buf, len);
+ res = write_child (child_addr, buffer, len);
+ return res;
+}
+
+/* Helper for go32_xfer_partial that handles memory transfers.
+ Arguments are like target_xfer_partial. */
+
+static enum target_xfer_status
+go32_xfer_memory (gdb_byte *readbuf, const gdb_byte *writebuf,
+ ULONGEST memaddr, ULONGEST len, ULONGEST *xfered_len)
+{
+ int res;
+
+ if (writebuf != NULL)
+ res = my_write_child (memaddr, writebuf, len);
else
+ res = read_child (memaddr, readbuf, len);
+
+ /* read_child and write_child return zero on success, non-zero on
+ failure. */
+ if (res != 0)
+ return TARGET_XFER_E_IO;
+
+ *xfered_len = len;
+ return TARGET_XFER_OK;
+}
+
+/* Target to_xfer_partial implementation. */
+
+enum target_xfer_status
+go32_nat_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)
{
- if (read_child (memaddr, myaddr, len))
- {
- return 0;
- }
- else
- {
- return len;
- }
+ case TARGET_OBJECT_MEMORY:
+ return go32_xfer_memory (readbuf, writebuf, offset, len, xfered_len);
+
+ default:
+ return this->beneath ()->xfer_partial (object, annex,
+ readbuf, writebuf, offset, len,
+ xfered_len);
}
}
static cmdline_t child_cmd; /* Parsed child's command line kept here. */
-static void
-go32_files_info (struct target_ops *target)
+void
+go32_nat_target::files_info ()
{
printf_unfiltered ("You are running a DJGPP V2 program.\n");
}
-static void
-go32_kill_inferior (struct target_ops *ops)
+void
+go32_nat_target::kill_inferior ()
{
- go32_mourn_inferior (ops);
+ mourn_inferior ();
}
-static void
-go32_create_inferior (struct target_ops *ops, char *exec_file,
- char *args, char **env, int from_tty)
+void
+go32_nat_target::create_inferior (const char *exec_file,
+ const std::string &allargs,
+ char **env, int from_tty)
{
extern char **environ;
jmp_buf start_state;
char **env_save = environ;
size_t cmdlen;
struct inferior *inf;
+ int result;
+ const char *args = allargs.c_str ();
/* If no exec file handed to us, get it from the exec-file command -- with
a good, common error message if none is specified. */
if (cmdlen > 1024*1024)
error (_("Command line too long."));
- cmdline = xmalloc (cmdlen + 4);
+ cmdline = (char *) xmalloc (cmdlen + 4);
strcpy (cmdline + 1, args);
/* If the command-line length fits into DOS 126-char limits, use the
DOS command tail format; otherwise, tell v2loadimage to pass it
environ = env;
- if (v2loadimage (exec_file, cmdline, start_state))
- {
- environ = env_save;
- printf_unfiltered ("Load failed for image %s\n", exec_file);
- exit (1);
- }
+ result = v2loadimage (exec_file, cmdline, start_state);
+
environ = env_save;
xfree (cmdline);
+ if (result != 0)
+ error (_("Load failed for image %s"), exec_file);
+
edi_init (start_state);
#if __DJGPP_MINOR__ < 3
save_npx ();
#endif
- inferior_ptid = pid_to_ptid (SOME_PID);
+ inferior_ptid = ptid_t (SOME_PID);
inf = current_inferior ();
inferior_appeared (inf, SOME_PID);
- push_target (&go32_ops);
+ if (!target_is_pushed (this))
+ push_target (this);
add_thread_silent (inferior_ptid);
- clear_proceed_status ();
+ clear_proceed_status (0);
insert_breakpoints ();
prog_has_started = 1;
}
-static void
-go32_mourn_inferior (struct target_ops *ops)
+void
+go32_nat_target::mourn_inferior ()
{
ptid_t ptid;
be nice if GDB itself would take care to remove all breakpoints
at all times, but it doesn't, probably under an assumption that
the OS cleans up when the debuggee exits. */
- i386_cleanup_dregs ();
+ x86_cleanup_dregs ();
ptid = inferior_ptid;
inferior_ptid = null_ptid;
- delete_thread_silent (ptid);
prog_has_started = 0;
- unpush_target (ops);
generic_mourn_inferior ();
-}
-
-static int
-go32_can_run (void)
-{
- return 1;
+ maybe_unpush_target ();
}
/* Hardware watchpoint support. */
second call will always see GDB's own cooked terminal. */
static int terminal_is_ours = 1;
-static void
-go32_terminal_init (void)
+void
+go32_nat_target::terminal_init ()
{
inf_mode_valid = 0; /* Reinitialize, in case they are restarting child. */
terminal_is_ours = 1;
}
-static void
-go32_terminal_info (const char *args, int from_tty)
+void
+go32_nat_target::terminal_info (const char *args, int from_tty)
{
printf_unfiltered ("Inferior's terminal is in %s mode.\n",
!inf_mode_valid
#endif
}
-static void
-go32_terminal_inferior (void)
+void
+go32_nat_target::terminal_inferior ()
{
/* Redirect standard handles as child wants them. */
errno = 0;
}
}
-static void
-go32_terminal_ours (void)
+void
+go32_nat_target::terminal_ours ()
{
/* Switch to cooked mode on the gdb terminal and save the inferior
terminal mode to be restored when it is resumed. */
}
}
-static int
-go32_thread_alive (struct target_ops *ops, ptid_t ptid)
+void
+go32_nat_target::pass_ctrlc ()
{
- return !ptid_equal (inferior_ptid, null_ptid);
}
-static char *
-go32_pid_to_str (struct target_ops *ops, ptid_t ptid)
+bool
+go32_nat_target::thread_alive (ptid_t ptid)
{
- return normal_pid_to_str (ptid);
+ return ptid != null_ptid;
}
-static void
-init_go32_ops (void)
+std::string
+go32_nat_target::pid_to_str (ptid_t ptid)
{
- go32_ops.to_shortname = "djgpp";
- go32_ops.to_longname = "djgpp target process";
- go32_ops.to_doc =
- "Program loaded by djgpp, when gdb is used as an external debugger";
- go32_ops.to_open = go32_open;
- go32_ops.to_close = go32_close;
- go32_ops.to_attach = go32_attach;
- go32_ops.to_detach = go32_detach;
- go32_ops.to_resume = go32_resume;
- go32_ops.to_wait = go32_wait;
- go32_ops.to_fetch_registers = go32_fetch_registers;
- go32_ops.to_store_registers = go32_store_registers;
- go32_ops.to_prepare_to_store = go32_prepare_to_store;
- go32_ops.deprecated_xfer_memory = go32_xfer_memory;
- go32_ops.to_files_info = go32_files_info;
- go32_ops.to_insert_breakpoint = memory_insert_breakpoint;
- go32_ops.to_remove_breakpoint = memory_remove_breakpoint;
- go32_ops.to_terminal_init = go32_terminal_init;
- go32_ops.to_terminal_inferior = go32_terminal_inferior;
- go32_ops.to_terminal_ours_for_output = go32_terminal_ours;
- go32_ops.to_terminal_ours = go32_terminal_ours;
- go32_ops.to_terminal_info = go32_terminal_info;
- go32_ops.to_kill = go32_kill_inferior;
- go32_ops.to_create_inferior = go32_create_inferior;
- go32_ops.to_mourn_inferior = go32_mourn_inferior;
- go32_ops.to_can_run = go32_can_run;
- go32_ops.to_thread_alive = go32_thread_alive;
- go32_ops.to_pid_to_str = go32_pid_to_str;
- go32_ops.to_stratum = process_stratum;
- go32_ops.to_has_all_memory = default_child_has_all_memory;
- go32_ops.to_has_memory = default_child_has_memory;
- go32_ops.to_has_stack = default_child_has_stack;
- go32_ops.to_has_registers = default_child_has_registers;
- go32_ops.to_has_execution = default_child_has_execution;
-
- i386_use_watchpoints (&go32_ops);
-
-
- i386_dr_low.set_control = go32_set_dr7;
- i386_dr_low.set_addr = go32_set_dr;
- i386_dr_low.get_status = go32_get_dr6;
- i386_dr_low.get_control = go32_get_dr7;
- i386_dr_low.get_addr = go32_get_dr;
- i386_set_debug_register_length (4);
-
- go32_ops.to_magic = OPS_MAGIC;
-
- /* Initialize child's cwd as empty to be initialized when starting
- the child. */
- *child_cwd = 0;
-
- /* Initialize child's command line storage. */
- if (redir_debug_init (&child_cmd) == -1)
- internal_error (__FILE__, __LINE__,
- _("Cannot allocate redirection storage: "
- "not enough memory.\n"));
-
- /* We are always processing GCC-compiled programs. */
- processing_gcc_compilation = 2;
-
- /* Override the default name of the GDB init file. */
- strcpy (gdbinit, "gdb.ini");
+ return normal_pid_to_str (ptid);
}
/* Return the current DOS codepage number. */
/* Display assorted information about the underlying OS. */
static void
-go32_sysinfo (char *arg, int from_tty)
+go32_sysinfo (const char *arg, int from_tty)
{
static const char test_pattern[] =
"deadbeafdeadbeafdeadbeafdeadbeafdeadbeaf"
else if (u.machine[0] == 'i' && u.machine[1] > 4)
{
/* CPUID with EAX = 0 returns the Vendor ID. */
+#if 0
+ /* Ideally we would use x86_cpuid(), but it needs someone to run
+ native tests first to make sure things actually work. They should.
+ http://sourceware.org/ml/gdb-patches/2013-05/msg00164.html */
+ unsigned int eax, ebx, ecx, edx;
+
+ if (x86_cpuid (0, &eax, &ebx, &ecx, &edx))
+ {
+ cpuid_max = eax;
+ memcpy (&vendor[0], &ebx, 4);
+ memcpy (&vendor[4], &ecx, 4);
+ memcpy (&vendor[8], &edx, 4);
+ cpuid_vendor[12] = '\0';
+ }
+#else
__asm__ __volatile__ ("xorl %%ebx, %%ebx;"
"xorl %%ecx, %%ecx;"
"xorl %%edx, %%edx;"
:
: "%eax", "%ebx", "%ecx", "%edx");
cpuid_vendor[12] = '\0';
+#endif
}
printf_filtered ("CPU Type.......................%s", u.machine);
/* CPUID with EAX = 1 returns processor signature and features. */
if (cpuid_max >= 1)
{
- static char *brand_name[] = {
+ static const char *brand_name[] = {
"",
" Celeron",
" III",
int amd_p = strcmp (cpuid_vendor, "AuthenticAMD") == 0;
unsigned cpu_family, cpu_model;
+#if 0
+ /* See comment above about cpuid usage. */
+ x86_cpuid (1, &cpuid_eax, &cpuid_ebx, NULL, &cpuid_edx);
+#else
__asm__ __volatile__ ("movl $1, %%eax;"
"cpuid;"
: "=a" (cpuid_eax),
"=d" (cpuid_edx)
:
: "%ecx");
+#endif
brand_idx = cpuid_ebx & 0xff;
cpu_family = (cpuid_eax >> 8) & 0xf;
cpu_model = (cpuid_eax >> 4) & 0xf;
}
static void
-go32_sldt (char *arg, int from_tty)
+go32_sldt (const char *arg, int from_tty)
{
struct dtr_reg gdtr;
unsigned short ldtr = 0;
}
static void
-go32_sgdt (char *arg, int from_tty)
+go32_sgdt (const char *arg, int from_tty)
{
struct dtr_reg gdtr;
long gdt_entry = -1L;
}
static void
-go32_sidt (char *arg, int from_tty)
+go32_sidt (const char *arg, int from_tty)
{
struct dtr_reg idtr;
long idt_entry = -1L;
}
static void
-go32_pde (char *arg, int from_tty)
+go32_pde (const char *arg, int from_tty)
{
long pde_idx = -1, i;
}
static void
-go32_pte (char *arg, int from_tty)
+go32_pte (const char *arg, int from_tty)
{
long pde_idx = -1L, i;
}
static void
-go32_pte_for_address (char *arg, int from_tty)
+go32_pte_for_address (const char *arg, int from_tty)
{
CORE_ADDR addr = 0, i;
static struct cmd_list_element *info_dos_cmdlist = NULL;
static void
-go32_info_dos_command (char *args, int from_tty)
+go32_info_dos_command (const char *args, int from_tty)
{
help_list (info_dos_cmdlist, "info dos ", class_info, gdb_stdout);
}
-/* -Wmissing-prototypes */
-extern initialize_file_ftype _initialize_go32_nat;
-
void
_initialize_go32_nat (void)
{
- init_go32_ops ();
- add_target (&go32_ops);
+ x86_dr_low.set_control = go32_set_dr7;
+ x86_dr_low.set_addr = go32_set_dr;
+ x86_dr_low.get_status = go32_get_dr6;
+ x86_dr_low.get_control = go32_get_dr7;
+ x86_dr_low.get_addr = go32_get_dr;
+ x86_set_debug_register_length (4);
+
+ add_inf_child_target (&the_go32_nat_target);
+
+ /* Initialize child's cwd as empty to be initialized when starting
+ the child. */
+ *child_cwd = 0;
+
+ /* Initialize child's command line storage. */
+ if (redir_debug_init (&child_cmd) == -1)
+ internal_error (__FILE__, __LINE__,
+ _("Cannot allocate redirection storage: "
+ "not enough memory.\n"));
+
+ /* We are always processing GCC-compiled programs. */
+ processing_gcc_compilation = 2;
add_prefix_cmd ("dos", class_info, go32_info_dos_command, _("\
Print information specific to DJGPP (aka MS-DOS) debugging."),