/* Select target systems and architectures at runtime for GDB.
- Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999,
- 2000, 2001, 2002
- Free Software Foundation, Inc.
+
+ Copyright 1990, 1991, 1992, 1993, 1994, 1995, 1996, 1997, 1998,
+ 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+
Contributed by Cygnus Support.
This file is part of GDB.
#include "dcache.h"
#include <signal.h>
#include "regcache.h"
-
-extern int errno;
+#include "gdb_assert.h"
+#include "gdbcore.h"
static void target_info (char *, int);
-static void cleanup_target (struct target_ops *);
-
static void maybe_kill_then_create_inferior (char *, char *, char **);
static void maybe_kill_then_attach (char *, int);
static struct target_ops *find_default_run_target (char *);
-static void update_current_target (void);
-
static void nosupport_runtime (void);
-static void normal_target_post_startup_inferior (ptid_t ptid);
+static LONGEST default_xfer_partial (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, void *readbuf,
+ const void *writebuf,
+ ULONGEST offset, LONGEST len);
/* Transfer LEN bytes between target address MEMADDR and GDB address
MYADDR. Returns 0 for success, errno code for failure (which
static void debug_to_stop (void);
-static int debug_to_query (int /*char */ , char *, char *, int *);
-
/* Pointer to array of target architecture structures; the size of the
array; the current index into the array; the allocated size of the
array. */
void
add_target (struct target_ops *t)
{
+ /* Provide default values for all "must have" methods. */
+ if (t->to_xfer_partial == NULL)
+ t->to_xfer_partial = default_xfer_partial;
+
if (!target_structs)
{
target_struct_allocsize = DEFAULT_ALLOCSIZE;
target_struct_allocsize * sizeof (*target_structs));
}
target_structs[target_struct_size++] = t;
-/* cleanup_target (t); */
if (targetlist == NULL)
add_prefix_cmd ("target", class_run, target_command,
target_create_inferior (exec, args, env);
}
-/* Clean up a target struct so it no longer has any zero pointers in it.
- We default entries, at least to stubs that print error messages. */
+/* Go through the target stack from top to bottom, copying over zero
+ entries in current_target, then filling in still empty entries. In
+ effect, we are doing class inheritance through the pushed target
+ vectors.
+
+ NOTE: cagney/2003-10-17: The problem with this inheritance, as it
+ is currently implemented, is that it discards any knowledge of
+ which target an inherited method originally belonged to.
+ Consequently, new new target methods should instead explicitly and
+ locally search the target stack for the target that can handle the
+ request. */
static void
-cleanup_target (struct target_ops *t)
+update_current_target (void)
{
+ struct target_ops *t;
+
+ /* First, reset curren'ts contents. */
+ memset (¤t_target, 0, sizeof (current_target));
+
+#define INHERIT(FIELD, TARGET) \
+ if (!current_target.FIELD) \
+ current_target.FIELD = (TARGET)->FIELD
+
+ for (t = target_stack; t; t = t->beneath)
+ {
+ INHERIT (to_shortname, t);
+ INHERIT (to_longname, t);
+ INHERIT (to_doc, t);
+ INHERIT (to_open, t);
+ INHERIT (to_close, t);
+ INHERIT (to_attach, t);
+ INHERIT (to_post_attach, t);
+ INHERIT (to_detach, t);
+ INHERIT (to_disconnect, t);
+ INHERIT (to_resume, t);
+ INHERIT (to_wait, t);
+ INHERIT (to_post_wait, t);
+ INHERIT (to_fetch_registers, t);
+ INHERIT (to_store_registers, t);
+ INHERIT (to_prepare_to_store, t);
+ INHERIT (to_xfer_memory, t);
+ INHERIT (to_files_info, t);
+ INHERIT (to_insert_breakpoint, t);
+ INHERIT (to_remove_breakpoint, t);
+ INHERIT (to_can_use_hw_breakpoint, t);
+ INHERIT (to_insert_hw_breakpoint, t);
+ INHERIT (to_remove_hw_breakpoint, t);
+ INHERIT (to_insert_watchpoint, t);
+ INHERIT (to_remove_watchpoint, t);
+ INHERIT (to_stopped_data_address, t);
+ INHERIT (to_stopped_by_watchpoint, t);
+ INHERIT (to_have_continuable_watchpoint, t);
+ INHERIT (to_region_size_ok_for_hw_watchpoint, t);
+ INHERIT (to_terminal_init, t);
+ INHERIT (to_terminal_inferior, t);
+ INHERIT (to_terminal_ours_for_output, t);
+ INHERIT (to_terminal_ours, t);
+ INHERIT (to_terminal_save_ours, t);
+ INHERIT (to_terminal_info, t);
+ INHERIT (to_kill, t);
+ INHERIT (to_load, t);
+ INHERIT (to_lookup_symbol, t);
+ INHERIT (to_create_inferior, t);
+ INHERIT (to_post_startup_inferior, t);
+ INHERIT (to_acknowledge_created_inferior, t);
+ INHERIT (to_insert_fork_catchpoint, t);
+ INHERIT (to_remove_fork_catchpoint, t);
+ INHERIT (to_insert_vfork_catchpoint, t);
+ INHERIT (to_remove_vfork_catchpoint, t);
+ INHERIT (to_follow_fork, t);
+ INHERIT (to_insert_exec_catchpoint, t);
+ INHERIT (to_remove_exec_catchpoint, t);
+ INHERIT (to_reported_exec_events_per_exec_call, t);
+ INHERIT (to_has_exited, t);
+ INHERIT (to_mourn_inferior, t);
+ INHERIT (to_can_run, t);
+ INHERIT (to_notice_signals, t);
+ INHERIT (to_thread_alive, t);
+ INHERIT (to_find_new_threads, t);
+ INHERIT (to_pid_to_str, t);
+ INHERIT (to_extra_thread_info, t);
+ INHERIT (to_stop, t);
+ /* Do not inherit to_xfer_partial. */
+ INHERIT (to_rcmd, t);
+ INHERIT (to_enable_exception_callback, t);
+ INHERIT (to_get_current_exception_event, t);
+ INHERIT (to_pid_to_exec_file, t);
+ INHERIT (to_stratum, t);
+ INHERIT (to_has_all_memory, t);
+ INHERIT (to_has_memory, t);
+ INHERIT (to_has_stack, t);
+ INHERIT (to_has_registers, t);
+ INHERIT (to_has_execution, t);
+ INHERIT (to_has_thread_control, t);
+ INHERIT (to_sections, t);
+ INHERIT (to_sections_end, t);
+ INHERIT (to_can_async_p, t);
+ INHERIT (to_is_async_p, t);
+ INHERIT (to_async, t);
+ INHERIT (to_async_mask_value, t);
+ INHERIT (to_find_memory_regions, t);
+ INHERIT (to_make_corefile_notes, t);
+ INHERIT (to_get_thread_local_address, t);
+ INHERIT (to_magic, t);
+ }
+#undef INHERIT
+
+ /* Clean up a target struct so it no longer has any zero pointers in
+ it. Some entries are defaulted to a method that print an error,
+ others are hard-wired to a standard recursive default. */
#define de_fault(field, value) \
- if (!t->field) \
- t->field = value
+ if (!current_target.field) \
+ current_target.field = value
de_fault (to_open,
(void (*) (char *, int))
de_fault (to_stop,
(void (*) (void))
target_ignore);
+ current_target.to_xfer_partial = default_xfer_partial;
de_fault (to_rcmd,
(void (*) (char *, struct ui_file *))
tcomplain);
(void (*) (void (*) (enum inferior_event_type, void*), void*))
tcomplain);
#undef de_fault
-}
-/* Go through the target stack from top to bottom, copying over zero entries in
- current_target. In effect, we are doing class inheritance through the
- pushed target vectors. */
-
-static void
-update_current_target (void)
-{
- struct target_ops *t;
-
- /* First, reset current_target */
- memset (¤t_target, 0, sizeof current_target);
-
- for (t = target_stack; t; t = t->beneath)
- {
-
-#define INHERIT(FIELD, TARGET) \
- if (!current_target.FIELD) \
- current_target.FIELD = TARGET->FIELD
-
- INHERIT (to_shortname, t);
- INHERIT (to_longname, t);
- INHERIT (to_doc, t);
- INHERIT (to_open, t);
- INHERIT (to_close, t);
- INHERIT (to_attach, t);
- INHERIT (to_post_attach, t);
- INHERIT (to_detach, t);
- INHERIT (to_disconnect, t);
- INHERIT (to_resume, t);
- INHERIT (to_wait, t);
- INHERIT (to_post_wait, t);
- INHERIT (to_fetch_registers, t);
- INHERIT (to_store_registers, t);
- INHERIT (to_prepare_to_store, t);
- INHERIT (to_xfer_memory, t);
- INHERIT (to_files_info, t);
- INHERIT (to_insert_breakpoint, t);
- INHERIT (to_remove_breakpoint, t);
- INHERIT (to_can_use_hw_breakpoint, t);
- INHERIT (to_insert_hw_breakpoint, t);
- INHERIT (to_remove_hw_breakpoint, t);
- INHERIT (to_insert_watchpoint, t);
- INHERIT (to_remove_watchpoint, t);
- INHERIT (to_stopped_data_address, t);
- INHERIT (to_stopped_by_watchpoint, t);
- INHERIT (to_have_continuable_watchpoint, t);
- INHERIT (to_region_size_ok_for_hw_watchpoint, t);
- INHERIT (to_terminal_init, t);
- INHERIT (to_terminal_inferior, t);
- INHERIT (to_terminal_ours_for_output, t);
- INHERIT (to_terminal_ours, t);
- INHERIT (to_terminal_save_ours, t);
- INHERIT (to_terminal_info, t);
- INHERIT (to_kill, t);
- INHERIT (to_load, t);
- INHERIT (to_lookup_symbol, t);
- INHERIT (to_create_inferior, t);
- INHERIT (to_post_startup_inferior, t);
- INHERIT (to_acknowledge_created_inferior, t);
- INHERIT (to_insert_fork_catchpoint, t);
- INHERIT (to_remove_fork_catchpoint, t);
- INHERIT (to_insert_vfork_catchpoint, t);
- INHERIT (to_remove_vfork_catchpoint, t);
- INHERIT (to_follow_fork, t);
- INHERIT (to_insert_exec_catchpoint, t);
- INHERIT (to_remove_exec_catchpoint, t);
- INHERIT (to_reported_exec_events_per_exec_call, t);
- INHERIT (to_has_exited, t);
- INHERIT (to_mourn_inferior, t);
- INHERIT (to_can_run, t);
- INHERIT (to_notice_signals, t);
- INHERIT (to_thread_alive, t);
- INHERIT (to_find_new_threads, t);
- INHERIT (to_pid_to_str, t);
- INHERIT (to_extra_thread_info, t);
- INHERIT (to_stop, t);
- INHERIT (to_query, t);
- INHERIT (to_rcmd, t);
- INHERIT (to_enable_exception_callback, t);
- INHERIT (to_get_current_exception_event, t);
- INHERIT (to_pid_to_exec_file, t);
- INHERIT (to_stratum, t);
- INHERIT (to_has_all_memory, t);
- INHERIT (to_has_memory, t);
- INHERIT (to_has_stack, t);
- INHERIT (to_has_registers, t);
- INHERIT (to_has_execution, t);
- INHERIT (to_has_thread_control, t);
- INHERIT (to_sections, t);
- INHERIT (to_sections_end, t);
- INHERIT (to_can_async_p, t);
- INHERIT (to_is_async_p, t);
- INHERIT (to_async, t);
- INHERIT (to_async_mask_value, t);
- INHERIT (to_find_memory_regions, t);
- INHERIT (to_make_corefile_notes, t);
- INHERIT (to_get_thread_local_address, t);
- INHERIT (to_magic, t);
-
-#undef INHERIT
- }
+ /* Finally, position the target-stack beneath the squashed
+ "current_target". That way code looking for a non-inherited
+ target method can quickly and simply find it. */
+ current_target.beneath = target_stack;
}
/* Push a new target type into the stack of the existing target accessors,
struct target_ops *tmp = (*cur);
(*cur) = (*cur)->beneath;
tmp->beneath = NULL;
- if (tmp->to_close)
- (tmp->to_close) (0);
+ target_close (tmp, 0);
}
/* We have removed all targets in our stratum, now add the new one. */
update_current_target ();
- cleanup_target (¤t_target); /* Fill in the gaps */
-
if (targetdebug)
setup_target_debug ();
struct target_ops **cur;
struct target_ops *tmp;
- if (t->to_close)
- t->to_close (0); /* Let it clean up */
-
/* Look for the specified target. Note that we assume that a target
can only occur once in the target stack. */
if ((*cur) == NULL)
return 0; /* Didn't find target_ops, quit now */
+ /* NOTE: cagney/2003-12-06: In '94 the close call was made
+ unconditional by moving it to before the above check that the
+ target was in the target stack (something about "Change the way
+ pushing and popping of targets work to support target overlays
+ and inheritance"). This doesn't make much sense - only open
+ targets should be closed. */
+ target_close (t, 0);
+
/* Unchain the target */
tmp = (*cur);
(*cur) = (*cur)->beneath;
tmp->beneath = NULL;
update_current_target ();
- cleanup_target (¤t_target);
return 1;
}
void
pop_target (void)
{
- (current_target.to_close) (0); /* Let it clean up */
+ target_close (¤t_target, 0); /* Let it clean up */
if (unpush_target (target_stack) == 1)
return;
return nbytes_read;
}
+/* Find a section containing ADDR. */
+struct section_table *
+target_section_by_addr (struct target_ops *target, CORE_ADDR addr)
+{
+ struct section_table *secp;
+ for (secp = target->to_sections;
+ secp < target->to_sections_end;
+ secp++)
+ {
+ if (addr >= secp->addr && addr < secp->endaddr)
+ return secp;
+ }
+ return NULL;
+}
+
/* Read LEN bytes of target memory at address MEMADDR, placing the results in
GDB's memory at MYADDR. Returns either 0 for success or an errno value
if any error occurs.
if (!write && trust_readonly)
{
+ struct section_table *secp;
/* User-settable option, "trust-readonly-sections". If true,
then memory from any SEC_READONLY bfd section may be read
- directly from the bfd file. */
-
- struct section_table *secp;
-
- for (secp = current_target.to_sections;
- secp < current_target.to_sections_end;
- secp++)
- {
- if (bfd_get_section_flags (secp->bfd, secp->the_bfd_section)
- & SEC_READONLY)
- if (memaddr >= secp->addr && memaddr < secp->endaddr)
- return xfer_memory (memaddr, myaddr, len, 0,
- attrib, ¤t_target);
- }
+ directly from the bfd file. */
+ secp = target_section_by_addr (¤t_target, memaddr);
+ if (secp != NULL
+ && (bfd_get_section_flags (secp->bfd, secp->the_bfd_section)
+ & SEC_READONLY))
+ return xfer_memory (memaddr, myaddr, len, 0, attrib, ¤t_target);
}
/* The quick case is that the top target can handle the transfer. */
return target_xfer_memory_partial (memaddr, buf, len, 1, err);
}
+/* More generic transfers. */
+
+static LONGEST
+default_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, void *readbuf,
+ const void *writebuf, ULONGEST offset, LONGEST len)
+{
+ if (object == TARGET_OBJECT_MEMORY
+ && ops->to_xfer_memory != NULL)
+ /* If available, fall back to the target's "to_xfer_memory"
+ method. */
+ {
+ int xfered = -1;
+ errno = 0;
+ if (writebuf != NULL)
+ {
+ void *buffer = xmalloc (len);
+ struct cleanup *cleanup = make_cleanup (xfree, buffer);
+ memcpy (buffer, writebuf, len);
+ xfered = ops->to_xfer_memory (offset, buffer, len, 1/*write*/, NULL,
+ ops);
+ do_cleanups (cleanup);
+ }
+ if (readbuf != NULL)
+ xfered = ops->to_xfer_memory (offset, readbuf, len, 0/*read*/, NULL,
+ ops);
+ if (xfered > 0)
+ return xfered;
+ else if (xfered == 0 && errno == 0)
+ /* "to_xfer_memory" uses 0, cross checked against ERRNO as one
+ indication of an error. */
+ return 0;
+ else
+ return -1;
+ }
+ else if (ops->beneath != NULL)
+ return ops->beneath->to_xfer_partial (ops->beneath, object, annex,
+ readbuf, writebuf, offset, len);
+ else
+ return -1;
+}
+
+/* Target vector read/write partial wrapper functions.
+
+ NOTE: cagney/2003-10-21: I wonder if having "to_xfer_partial
+ (inbuf, outbuf)", instead of separate read/write methods, make life
+ easier. */
+
+LONGEST
+target_read_partial (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, void *buf,
+ ULONGEST offset, LONGEST len)
+{
+ gdb_assert (ops->to_xfer_partial != NULL);
+ return ops->to_xfer_partial (ops, object, annex, buf, NULL, offset, len);
+}
+
+LONGEST
+target_write_partial (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, const void *buf,
+ ULONGEST offset, LONGEST len)
+{
+ gdb_assert (ops->to_xfer_partial != NULL);
+ return ops->to_xfer_partial (ops, object, annex, NULL, buf, offset, len);
+}
+
+/* Wrappers to perform the full transfer. */
+LONGEST
+target_read (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, void *buf,
+ ULONGEST offset, LONGEST len)
+{
+ LONGEST xfered = 0;
+ while (xfered < len)
+ {
+ LONGEST xfer = target_read_partial (ops, object, annex,
+ (bfd_byte *) buf + xfered,
+ offset + xfered, len - xfered);
+ /* Call an observer, notifying them of the xfer progress? */
+ if (xfer <= 0)
+ /* Call memory_error? */
+ return -1;
+ xfered += xfer;
+ QUIT;
+ }
+ return len;
+}
+
+LONGEST
+target_write (struct target_ops *ops,
+ enum target_object object,
+ const char *annex, const void *buf,
+ ULONGEST offset, LONGEST len)
+{
+ LONGEST xfered = 0;
+ while (xfered < len)
+ {
+ LONGEST xfer = target_write_partial (ops, object, annex,
+ (bfd_byte *) buf + xfered,
+ offset + xfered, len - xfered);
+ /* Call an observer, notifying them of the xfer progress? */
+ if (xfer <= 0)
+ /* Call memory_error? */
+ return -1;
+ xfered += xfer;
+ QUIT;
+ }
+ return len;
+}
+
+/* Memory transfer methods. */
+
+void
+get_target_memory (struct target_ops *ops, CORE_ADDR addr, void *buf,
+ LONGEST len)
+{
+ if (target_read (ops, TARGET_OBJECT_MEMORY, NULL, buf, addr, len)
+ != len)
+ memory_error (EIO, addr);
+}
+
+ULONGEST
+get_target_memory_unsigned (struct target_ops *ops,
+ CORE_ADDR addr, int len)
+{
+ char buf[sizeof (ULONGEST)];
+
+ gdb_assert (len <= sizeof (buf));
+ get_target_memory (ops, addr, buf, len);
+ return extract_unsigned_integer (buf, len);
+}
+
static void
target_info (char *args, int from_tty)
{
void
target_link (char *modname, CORE_ADDR *t_reloc)
{
- if (STREQ (current_target.to_shortname, "rombug"))
+ if (DEPRECATED_STREQ (current_target.to_shortname, "rombug"))
{
(current_target.to_lookup_symbol) (modname, t_reloc);
if (*t_reloc == 0)
static int
default_region_size_ok_for_hw_watchpoint (int byte_count)
{
- return (byte_count <= DEPRECATED_REGISTER_SIZE);
+ return (byte_count <= TYPE_LENGTH (builtin_type_void_data_ptr));
}
static int
if (!show_breakpoint_hit_counts)
breakpoint_clear_ignore_counts ();
- if (detach_hook)
- detach_hook ();
+ if (deprecated_detach_hook)
+ deprecated_detach_hook ();
}
\f
/* Helper function for child_wait and the Lynx derivatives of child_wait.
return buf;
}
-/* Some targets (such as ttrace-based HPUX) don't allow us to request
- notification of inferior events such as fork and vork immediately
- after the inferior is created. (This because of how gdb gets an
- inferior created via invoking a shell to do it. In such a scenario,
- if the shell init file has commands in it, the shell will fork and
- exec for each of those commands, and we will see each such fork
- event. Very bad.)
-
- This function is used by all targets that allow us to request
- notification of forks, etc at inferior creation time; e.g., in
- target_acknowledge_forked_child.
- */
-static void
-normal_target_post_startup_inferior (ptid_t ptid)
-{
- /* This space intentionally left blank. */
-}
-
/* Error-catcher for target_find_memory_regions */
static int dummy_find_memory_regions (int (*ignore1) (), void *ignore2)
{
dummy_target.to_stratum = dummy_stratum;
dummy_target.to_find_memory_regions = dummy_find_memory_regions;
dummy_target.to_make_corefile_notes = dummy_make_corefile_notes;
+ dummy_target.to_xfer_partial = default_xfer_partial;
dummy_target.to_magic = OPS_MAGIC;
}
\f
static void
debug_to_close (int quitting)
{
- debug_target.to_close (quitting);
-
+ target_close (&debug_target, quitting);
fprintf_unfiltered (gdb_stdlog, "target_close (%d)\n", quitting);
}
+void
+target_close (struct target_ops *targ, int quitting)
+{
+ if (targ->to_xclose != NULL)
+ targ->to_xclose (targ, quitting);
+ else if (targ->to_close != NULL)
+ targ->to_close (quitting);
+}
+
static void
debug_to_attach (char *args, int from_tty)
{
(unsigned int) memaddr, /* possable truncate long long */
len, write ? "write" : "read", retval);
-
-
if (retval > 0)
{
int i;
for (i = 0; i < retval; i++)
{
if ((((long) &(myaddr[i])) & 0xf) == 0)
- fprintf_unfiltered (gdb_stdlog, "\n");
+ {
+ if (targetdebug < 2 && i > 0)
+ {
+ fprintf_unfiltered (gdb_stdlog, " ...");
+ break;
+ }
+ fprintf_unfiltered (gdb_stdlog, "\n");
+ }
+
fprintf_unfiltered (gdb_stdlog, " %02x", myaddr[i] & 0xff);
}
}
fprintf_unfiltered (gdb_stdlog, "target_stop ()\n");
}
-static int
-debug_to_query (int type, char *req, char *resp, int *siz)
+static LONGEST
+debug_to_xfer_partial (struct target_ops *ops, enum target_object object,
+ const char *annex, void *readbuf, const void *writebuf,
+ ULONGEST offset, LONGEST len)
{
- int retval;
+ LONGEST retval;
- retval = debug_target.to_query (type, req, resp, siz);
+ retval = debug_target.to_xfer_partial (&debug_target, object, annex,
+ readbuf, writebuf, offset, len);
- fprintf_unfiltered (gdb_stdlog, "target_query (%c, %s, %s, %d) = %d\n", type, req, resp, *siz, retval);
+ fprintf_unfiltered (gdb_stdlog,
+ "target_xfer_partial (%d, %s, 0x%lx, 0x%lx, 0x%s, %s) = %s\n",
+ (int) object, (annex ? annex : "(null)"),
+ (long) readbuf, (long) writebuf, paddr_nz (offset),
+ paddr_d (len), paddr_d (retval));
return retval;
}
current_target.to_thread_alive = debug_to_thread_alive;
current_target.to_find_new_threads = debug_to_find_new_threads;
current_target.to_stop = debug_to_stop;
- current_target.to_query = debug_to_query;
+ current_target.to_xfer_partial = debug_to_xfer_partial;
current_target.to_rcmd = debug_to_rcmd;
current_target.to_enable_exception_callback = debug_to_enable_exception_callback;
current_target.to_get_current_exception_event = debug_to_get_current_exception_event;
(add_set_cmd ("target", class_maintenance, var_zinteger,
(char *) &targetdebug,
"Set target debugging.\n\
-When non-zero, target debugging is enabled.", &setdebuglist),
+When non-zero, target debugging is enabled. Higher numbers are more\n\
+verbose. Changes do not take effect until the next \"run\" or \"target\"\n\
+command.", &setdebuglist),
&showdebuglist);
add_setshow_boolean_cmd ("trust-readonly-sections", class_support,