This is the mail archive of the
gdb-patches@sourceware.org
mailing list for the GDB project.
gdbserver Solaris [4/9] - gdbserver Solaris files
- From: "Pieter Maljaars" <pieter dot maljaars at altenpts dot nl>
- To: gdb-patches at sourceware dot org
- Date: Fri, 23 Apr 2010 17:11:21 +0200
- Subject: gdbserver Solaris [4/9] - gdbserver Solaris files
- Reply-to: pieter dot maljaars at altenpts dot nl
gdb/gdbserver ChangeLog entry:
2010-04-23 Pieter Maljaars <pieter.maljaars@altenpts.nl>
* solaris-i386-low.c: New file.
* solaris-low.c: New file.
* solaris-low.h: New file.
* solaris-sparc-low.c: New file.
* sol-thread-db.c: New file.
diff -upN src-orig/src/gdb/gdbserver/solaris-i386-low.c src/gdb/gdbserver/solaris-i386-low.c
--- src-orig/src/gdb/gdbserver/solaris-i386-low.c 1970-01-01 01:00:00.000000000 +0100
+++ src/gdb/gdbserver/solaris-i386-low.c 2010-04-08 15:41:51.000000000 +0200
@@ -0,0 +1,163 @@
+/* Solaris/i386 specific low level interface, for the remote server for GDB.
+ Copyright (C) 2010 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 "server.h"
+#include "solaris-low.h"
+
+#include "i387-fp.h"
+
+
+extern int debug_threads;
+
+/* This module only supports access to the general purpose registers. */
+
+#define i386_num_regs 16
+
+/* Mapping between the general-purpose registers in `struct user'
+ format and GDB's register array layout. */
+static int i386_regmap[] =
+{
+ EAX * 4, ECX * 4, EDX * 4, EBX * 4,
+ UESP * 4, EBP * 4, ESI * 4, EDI * 4,
+ EIP * 4, EFL * 4, CS * 4, SS * 4,
+ DS * 4, ES * 4, FS * 4, GS * 4
+};
+
+/* Defined in auto-generated file reg-sparc32.c. */
+void init_registers_i386 (void);
+
+static int
+i386_cannot_store_register (int regno)
+{
+ return (regno >= i386_num_regs);
+}
+
+static int
+i386_cannot_fetch_register (int regno)
+{
+ return (regno >= i386_num_regs);
+}
+
+static CORE_ADDR
+i386_get_pc (struct regcache *regcache)
+{
+ unsigned long pc;
+
+ collect_register_by_name (regcache, "eip", &pc);
+
+ if (debug_threads)
+ fprintf (stderr, "stop pc (before any decrement) is %08lx\n", pc);
+ return (CORE_ADDR) pc;
+}
+
+static void
+i386_set_pc (struct regcache *regcache, CORE_ADDR newpc)
+{
+ if (debug_threads)
+ fprintf (stderr, "set pc to %08lx\n", (long) newpc);
+ supply_register_by_name (regcache, "eip", &newpc);
+}
+
+static int
+i386_breakpoint_at (CORE_ADDR pc)
+{
+ unsigned char c;
+
+ read_inferior_memory (pc, &c, 1);
+ if (c == 0xCC)
+ return 1;
+
+ return 0;
+}
+
+static void
+i386_supply_gregset (struct regcache *regcache, const void *gregs)
+{
+ int i;
+ const gdb_byte *regs = gregs;
+
+ for (i = 0; i < i386_num_regs; i++)
+ supply_register (regcache, i, ((char *) regs) + i386_regmap[i]);
+}
+
+static void
+i386_supply_fpregset (struct regcache *regcache, const void *fpregs)
+{
+ i387_fsave_to_cache (regcache, fpregs);
+}
+
+static void
+i386_collect_gregset (struct regcache *regcache, void *gregs)
+{
+ int i;
+
+ for (i = 0; i < i386_num_regs; i++)
+ collect_register (regcache, i, ((char *) gregs) + i386_regmap[i]);
+}
+
+static void
+i386_collect_fpregset (struct regcache *regcache, void *fpregs)
+{
+ i387_cache_to_fsave (regcache, fpregs);
+}
+
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregs)
+{
+ i386_supply_gregset (regcache, gregs);
+}
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregs)
+{
+ i386_supply_fpregset (regcache, fpregs);
+}
+
+void
+fill_gregset (struct regcache *regcache, gdb_gregset_t *gregs, int regno)
+{
+ i386_collect_gregset (regcache, gregs);
+}
+
+void
+fill_fpregset (struct regcache *regcache, prfpregset_t *fpregs, int regnum)
+{
+ i386_collect_fpregset (regcache, fpregs);
+}
+
+static const unsigned char i386_breakpoint[] = { 0xCC };
+#define i386_breakpoint_len 1
+
+struct solaris_target_ops the_low_target = {
+ init_registers_i386,
+ i386_num_regs,
+ /* No regmap needs to be provided since this impl. doesn't use USRREGS. */
+ NULL,
+ i386_cannot_fetch_register,
+ i386_cannot_store_register,
+ i386_get_pc,
+ i386_set_pc,
+ (const unsigned char *) i386_breakpoint,
+ i386_breakpoint_len,
+ NULL,
+ 0,
+ i386_breakpoint_at,
+ NULL, NULL, NULL, NULL, /* watchpoint functions */
+ 0
+};
diff -upN src-orig/src/gdb/gdbserver/solaris-low.c src/gdb/gdbserver/solaris-low.c
--- src-orig/src/gdb/gdbserver/solaris-low.c 1970-01-01 01:00:00.000000000 +0100
+++ src/gdb/gdbserver/solaris-low.c 2010-04-08 15:41:50.000000000 +0200
@@ -0,0 +1,1006 @@
+/* Low level interface to the proc filesystem, for the remote server for GDB.
+ Copyright (C) 2009 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 "server.h"
+#include "solaris-low.h"
+#include "gdb_proc_service.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <ctype.h>
+#include <pwd.h>
+
+
+struct string_map
+{
+ int num;
+ char *str;
+};
+
+static int sol_thread_alive (ptid_t ptid);
+
+/* This is used in remote-utils.c */
+int using_threads = 1;
+
+static int new_inferior;
+
+/* This is a cached copy of the pid/waitstatus of the last event
+ returned by solaris_wait(). This information is returned by
+ get_last_target_status(). */
+static ptid_t target_last_wait_ptid;
+static struct target_waitstatus target_last_waitstatus;
+
+extern ptid_t do_attach (ptid_t ptid);
+extern void do_detach (int signo);
+extern int procfs_stop_process (ptid_t ptid);
+extern ptid_t procfs_wait (ptid_t ptid, struct target_waitstatus *status);
+extern void procfs_resume (ptid_t, int, enum target_signal);
+extern int procfs_thread_alive (ptid_t ptid);
+extern int procfs_xfer_memory (CORE_ADDR memaddr, char *myaddr, int len, int dowrite);
+extern void procfs_fetch_registers (struct regcache*, int);
+extern void procfs_store_registers (struct regcache*, int);
+extern void procfs_kill_inferior (ptid_t ptid);
+extern void procfs_set_exec_trap (void);
+extern void procfs_init_inferior (int pid);
+extern char *procfs_pid_to_str (ptid_t);
+
+extern void _initialize_proc_events (void);
+
+extern struct thread_info * find_thread_ptid (ptid_t gdb_id);
+
+/* Return the libthread_db error string associated with ERRCODE. If
+ ERRCODE is unknown, return an appropriate message. */
+extern char *thread_db_err_str (td_err_e err);
+
+/* Forward declarations */
+void solaris_fetch_registers (struct regcache *regcache, int regno);
+
+
+/* Return the the libthread_db state string assicoated with STATECODE.
+ If STATECODE is unknown, return an appropriate message. */
+static char *
+td_state_string (td_thr_state_e statecode)
+{
+ static struct string_map td_thr_state_table[] =
+ {
+ { TD_THR_ANY_STATE, "any state" },
+ { TD_THR_UNKNOWN, "unknown" },
+ { TD_THR_STOPPED, "stopped" },
+ { TD_THR_RUN, "run" },
+ { TD_THR_ACTIVE, "active" },
+ { TD_THR_ZOMBIE, "zombie" },
+ { TD_THR_SLEEP, "sleep" },
+ { TD_THR_STOPPED_ASLEEP, "stopped asleep" }
+ };
+ const int td_thr_state_table_size =
+ sizeof td_thr_state_table / sizeof (struct string_map);
+ int i;
+ static char buf[50];
+
+ for (i = 0; i < td_thr_state_table_size; i++)
+ if (td_thr_state_table[i].num == statecode)
+ return td_thr_state_table[i].str;
+
+ sprintf (buf, "Unknown libthread_db state code: %d", statecode);
+
+ return buf;
+}
+
+/* Get the thread ID from hte current selected inferior (the current
+ thread). */
+static ptid_t
+current_inferior_ptid (void)
+{
+ return ((struct inferior_list_entry*) current_inferior)->id;
+}
+
+/* Convert a POSIX or Solaris thread ID into a LWP ID. If THREAD_ID
+ doesn't exist, that's an error. If it's an inactive thread, return
+ DEFAULT_LPW.
+
+ NOTE: This function probably shouldn't call error(). */
+
+ptid_t
+thread_to_lwp (ptid_t thread_id, int default_lwp)
+{
+ td_thrinfo_t ti;
+ td_thrhandle_t th;
+ td_err_e val;
+ struct process_info_private *proc;
+
+ if (is_lwp (thread_id))
+ return thread_id; /* It's already an LWP ID. */
+
+ /* It's a thread. Convert to LWP. */
+
+ if (!current_process ())
+ return ptid_build (ptid_get_pid (thread_id),
+ ptid_get_tid (thread_id), 0);
+
+ proc = current_process ()->private;
+
+ if (!proc->thread_agent)
+ return ptid_build (ptid_get_pid (thread_id),
+ ptid_get_tid (thread_id), 0);
+
+ val = td_ta_map_id2thr (proc->thread_agent, GET_THREAD (thread_id), &th);
+ if (val == TD_NOTHR)
+ return pid_to_ptid (-1); /* Thread must have terminated. */
+ else if (val != TD_OK)
+ error ("thread_to_lwp: td_ta_map_id2thr %s", td_err_string (val));
+
+ val = td_thr_get_info (&th, &ti);
+ if (val == TD_NOTHR)
+ return pid_to_ptid (-1); /* Thread must have terminated. */
+ else if (val != TD_OK)
+ error ("thread_to_lwp: td_thr_get_info: %s", td_err_string (val));
+
+ if (ti.ti_state != TD_THR_ACTIVE)
+ {
+ if (default_lwp != -1)
+ return pid_to_ptid (default_lwp);
+ error ("thread_to_lwp: thread state not active: %s",
+ td_state_string (ti.ti_state));
+ }
+
+ return BUILD_LWP (ti.ti_lid, ptid_get_pid (thread_id));
+}
+
+/* Convert an LWP ID into a POSIX or Solaris thread ID. If LWP_ID
+ doesn't exists, that's an error.
+
+ NOTE: This function probably shouldn't call error(). */
+
+ptid_t
+lwp_to_thread (ptid_t lwp)
+{
+ td_thrinfo_t ti;
+ td_thrhandle_t th;
+ td_err_e val;
+ struct process_info_private *proc;
+
+ if (is_thread (lwp))
+ return lwp; /* It's already a thread ID. */
+
+ /* It's an LWP. Convert it to a thread ID. */
+
+ if (!current_process ())
+ return ptid_build (ptid_get_pid (lwp),
+ 0, ptid_get_lwp (lwp));
+
+ proc = current_process ()->private;
+
+ if (!proc->thread_agent)
+ return ptid_build (ptid_get_pid (lwp),
+ 0, ptid_get_lwp (lwp));
+
+ if (!sol_thread_alive (lwp))
+ return pid_to_ptid (-1); /* Must be a defunct LPW. */
+
+ val = td_ta_map_lwp2thr (proc->thread_agent, GET_LWP (lwp), &th);
+ if (val == TD_NOTHR)
+ return pid_to_ptid (-1); /* Thread must have terminated. */
+ else if (val != TD_OK)
+ error ("lwp_to_thread: td_ta_map_lwp2thr: %s.", td_err_string (val));
+
+ val = td_thr_validate (&th);
+ if (val == TD_NOTHR)
+ return lwp; /* Unknown to libthread; just return LPW, */
+ else if (val != TD_OK)
+ error ("lwp_to_thread: td_thr_validate: %s.", td_err_string (val));
+
+ val = td_thr_get_info (&th, &ti);
+ if (val == TD_NOTHR)
+ return pid_to_ptid (-1); /* Thread must have terminated. */
+ else if (val != TD_OK)
+ error ("lwp_to_thread: td_thr_get_info: %s.", td_err_string (val));
+
+ return BUILD_THREAD (ti.ti_tid, ptid_get_pid (lwp));
+}
+
+/* Add a process to the common process list, and sets its private
+ data. */
+
+static struct process_info *
+solaris_add_process (int pid, int attached)
+{
+ struct process_info *proc;
+
+ /* Is this hte first process? If so, then set the arch. */
+ if (all_processes.head == NULL)
+ new_inferior = 1;
+
+ proc = add_process (pid, attached);
+ proc->private = xcalloc (1, sizeof (*proc->private));
+
+ return proc;
+}
+
+/* Remove a process from the common process list,
+ also freeing all private data. */
+
+static void
+solaris_remove_process (struct process_info *process)
+{
+ free (process->private);
+ remove_process (process);
+}
+
+static int
+any_thread_of (struct inferior_list_entry *entry, void *args)
+{
+ int *pid_p = args;
+
+ if (ptid_get_pid (entry->id) == *pid_p)
+ return 1;
+
+ return 0;
+}
+
+static ptid_t
+pid_to_lwp (int pid)
+{
+ struct thread_info * ti;
+
+ ti = (struct thread_info *)
+ find_inferior (&all_threads, any_thread_of, &pid);
+
+ if (ti != NULL)
+ return ((struct inferior_list_entry*) ti)->id;
+
+ return null_ptid;
+}
+
+/* Return the cached copy of the last pid/waitstatus returned by
+ solaris_wait(). */
+void
+get_last_target_status (ptid_t *ptidp, struct target_waitstatus *status)
+{
+ *ptidp = target_last_wait_ptid;
+ *status = target_last_waitstatus;
+}
+
+
+/* Return true if PTID is still active in the inferior. */
+
+static int
+sol_thread_alive (ptid_t ptid)
+{
+ if (is_thread (ptid))
+ {
+ /* It's a (user-level) thread. */
+ td_err_e val;
+ td_thrhandle_t th;
+ int pid;
+ struct process_info_private *proc = current_process ()->private;
+
+ pid = GET_THREAD (ptid);
+ if ((val = td_ta_map_id2thr (proc->thread_agent, pid, &th)) != TD_OK)
+ return 0; /* Thread not found. */
+ if ((val = td_thr_validate (&th)) != TD_OK)
+ return 0; /* Thread not valid. */
+ return 1; /* Known thread. */
+ }
+ else
+ {
+ /* pass the request on to procfs. */
+ return procfs_thread_alive (ptid);
+ }
+}
+
+
+static int
+solaris_create_inferior (char *program, char **allargs)
+{
+ int pid;
+
+ pid = fork ();
+
+ if (pid < 0)
+ perror_with_name ("fork");
+
+ if (pid == 0)
+ {
+ // trace me
+ procfs_set_exec_trap ();
+
+ setpgid (0,0);
+
+ execv (program, allargs);
+ if (errno == ENOENT)
+ execvp (program, allargs);
+
+ // If we get here, it's an error.
+ fprintf(stderr, "Cannot exec %s: %s.\n", program,
+ strerror (errno));
+ fflush (stderr);
+ _exit (0177);
+ }
+
+ procfs_init_inferior (pid);
+ solaris_add_process (pid, 0 /* not attached */);
+
+ inferior_ptid = ptid_build (pid, pid, 0);
+ add_thread (inferior_ptid, NULL);
+
+ cont_thread = inferior_ptid;
+
+ return pid;
+}
+
+
+int
+solaris_attach (unsigned long pid)
+{
+ inferior_ptid = do_attach (pid_to_ptid (pid));
+ cont_thread = inferior_ptid;
+
+ add_thread (inferior_ptid, NULL);
+
+ solaris_add_process (pid, 1 /* attached */);
+
+ return 0;
+}
+
+static int
+solaris_kill (int pid)
+{
+ ptid_t ptid;
+ struct process_info *process;
+
+ process = find_process_pid (pid);
+ ptid = pid_to_lwp (pid);
+ if (ptid_equal (ptid, null_ptid) || process == NULL)
+ return -1;
+
+ procfs_kill_inferior (ptid);
+
+ clear_inferiors ();
+ inferior_ptid = null_ptid;
+
+ solaris_remove_process (process);
+
+ return 0;
+}
+
+static int
+solaris_detach (int pid)
+{
+ ptid_t ptid;
+ struct process_info *process;
+
+ process = find_process_pid (pid);
+ ptid = pid_to_lwp (pid);
+ if (ptid_equal (ptid, null_ptid) || process == NULL)
+ return -1;
+
+ delete_all_breakpoints ();
+
+#ifndef __sparc__
+ /* needed on x86, not on SPARC */
+ regcache_invalidate_one (find_inferior_id (&all_threads, inferior_ptid));
+#endif
+
+ inferior_ptid = ptid;
+
+ do_detach (0);
+
+ clear_inferiors ();
+ inferior_ptid = null_ptid;
+
+ solaris_remove_process (process);
+
+ return 0;
+}
+
+static void
+solaris_join (int pid)
+{
+ ptid_t ptid;
+ struct target_waitstatus *ourstatus = NULL;
+
+ ptid = pid_to_lwp (pid);
+ procfs_wait (ptid, ourstatus);
+}
+
+/* Return nonzero if the given thread is still alive. */
+static int
+solaris_thread_alive (ptid_t ptid)
+{
+ return sol_thread_alive (ptid);
+}
+
+struct thread_resume_array
+{
+ struct thread_resume *resume;
+ size_t n;
+};
+
+static void
+solaris_resume (struct thread_resume *resume_info, size_t n)
+{
+ struct thread_info *saved_inferior;
+ ptid_t ptid;
+ int step = resume_continue;
+
+
+ saved_inferior = current_inferior;
+
+ if (n > 1)
+ ptid = minus_one_ptid;
+ else
+ ptid = resume_info[0].thread;
+
+ step = (resume_info[0].kind == resume_step);
+
+ if (debug_threads)
+ printf("Resuming process [%d, %ld, %ld] (%d, signal %d)\n",
+ ptid_get_pid (ptid), ptid_get_lwp (ptid), ptid_get_tid (ptid),
+ step, resume_info[0].sig);
+
+ inferior_ptid = thread_to_lwp (inferior_ptid,
+ ptid_get_pid (current_inferior_ptid ()));
+ if (ptid_get_pid (inferior_ptid) == -1)
+ inferior_ptid = procfs_first_available ();
+
+ if (ptid_get_pid (ptid) != -1)
+ {
+ ptid_t save_ptid = ptid;
+
+ ptid = thread_to_lwp (ptid, -2);
+ if (ptid_get_pid (ptid) == -2) /* Inactive thread. */
+ error ("This version of Solaris can't start inactive threads.");
+ if (debug_threads && ptid_get_pid (ptid) == -1)
+ warning ("Specified thread %ld seems to have terminated",
+ GET_THREAD (save_ptid));
+ }
+
+#ifndef __sparc__
+ /* needed on x86, not on SPARC */
+ regcache_invalidate_one (find_inferior_id (&all_threads, inferior_ptid));
+#endif
+
+ procfs_resume (ptid, step, resume_info[0].sig);
+
+ current_inferior = saved_inferior;
+}
+
+/* Wait for the inferior process to change state.
+ STATUS will be filled in with a response code to send to GDB.
+ Returns the signal which caused the process to stop. */
+static ptid_t
+solaris_wait (ptid_t ptid,
+ struct target_waitstatus *ourstatus, int target_options)
+{
+ ptid_t rtnval, save_ptid, event_ptid;
+
+ if (debug_threads)
+ printf("solaris_wait: [%s]\n", target_pid_to_str (ptid));
+
+
+ save_ptid = inferior_ptid;
+
+retry:
+
+ inferior_ptid = thread_to_lwp (inferior_ptid,
+ ptid_get_pid (current_inferior_ptid()));
+ if (ptid_get_pid (inferior_ptid) == -1)
+ inferior_ptid = procfs_first_available ();
+
+ if (ptid_get_pid (cont_thread) != -1)
+ {
+ ptid_t save_ptid = cont_thread;
+
+ cont_thread = thread_to_lwp (cont_thread, -2);
+ if (ptid_get_pid (cont_thread) == -2) /* Inactive thread. */
+ error ("This version of Solaris can't start inactive threads.");
+ if (debug_threads && ptid_get_pid (cont_thread) == -1)
+ warning ("Specified thread %ld seems to have terminated",
+ GET_THREAD (save_ptid));
+ }
+
+ event_ptid = procfs_wait (cont_thread, ourstatus);
+
+ rtnval = event_ptid;
+ inferior_ptid = event_ptid;
+
+ current_inferior = (struct thread_info *)find_inferior_id (
+ &all_threads, event_ptid);
+
+ if (ourstatus->kind != TARGET_WAITKIND_EXITED)
+ {
+
+ if (current_inferior == NULL)
+ {
+ /* one thread has exited. */
+ struct thread_info *old_thread = NULL;
+
+ current_inferior = (struct thread_info *) all_threads.head;
+ if (debug_threads)
+ printf("Current inferior is now %s\n",
+ procfs_pid_to_str (current_inferior_ptid()));
+
+ /* remove thread, LWP is already removed. */
+ event_ptid = lwp_to_thread (event_ptid);
+
+ if (!ptid_equal (event_ptid, minus_one_ptid) &&
+ is_thread (event_ptid))
+ {
+ old_thread = find_thread_ptid (event_ptid);
+ if (old_thread != NULL)
+ remove_thread (old_thread);
+ }
+
+ procfs_resume (minus_one_ptid, 0, TARGET_SIGNAL_0);
+
+ /* wait for the next event. */
+ goto retry;
+ }
+
+
+ /* Map the LWP of interest back to the appropriate thread ID. */
+ event_ptid = lwp_to_thread (event_ptid);
+
+ if (ptid_get_pid (event_ptid) == -1)
+ event_ptid = save_ptid;
+
+ /* See if we have a new thread. */
+ if (is_thread (event_ptid)
+ && !ptid_equal (event_ptid, save_ptid)
+ && (find_thread_ptid (event_ptid) == NULL))
+ {
+ add_thread (event_ptid, NULL);
+
+ /* Architecture-specific setup after inferior is running.
+ This needs to happen after we have attached to the inferior
+ and it is stopped for the first time, but before we access
+ any inferior registers. */
+ if (new_inferior)
+ {
+ the_low_target.arch_setup ();
+ new_inferior = 0;
+ }
+ }
+
+#ifdef __sparc__
+ struct regcache *regcache;
+ regcache = get_thread_regcache (current_inferior, 1);
+ solaris_fetch_registers (regcache, -1);
+#endif
+ }
+ else
+ {
+ /* target exited, so clean up. */
+ while (all_threads.head != NULL)
+ remove_thread ((struct thread_info *) all_threads.head);
+ }
+
+ // save the last state
+ target_last_wait_ptid = event_ptid;
+ target_last_waitstatus = *ourstatus;
+
+ return rtnval;
+}
+
+void
+solaris_fetch_registers (struct regcache *regcache, int regno)
+{
+ if (!is_thread (inferior_ptid))
+ {
+ /* It's an LWP; pass the request on to procfs. */
+ procfs_fetch_registers (regcache, regno);
+ return;
+ }
+
+ /* Solaris thread: convert INFERIOR_PTID into a td_thrhandle_t. */
+ long thread;
+ td_err_e val;
+ td_thrhandle_t thandle;
+ prgregset_t gregset;
+ prfpregset_t fpregset;
+ gdb_gregset_t *gregset_p = &gregset;
+ gdb_fpregset_t *fpregset_p = &fpregset;
+ struct process_info_private *proc = current_process ()->private;
+
+ thread = GET_THREAD (inferior_ptid);
+ if (thread == 0)
+ error ("sol_fetch_registers: thread == 0");
+
+ val = td_ta_map_id2thr (proc->thread_agent, thread, &thandle);
+ if (val != TD_OK)
+ error ("sol_fetch_registers: td_ta_map_id2thr: %s",
+ td_err_string (val));
+
+ /* Get the general-purpose registers. */
+
+ val = td_thr_getgregs (&thandle, gregset);
+ if (val != TD_OK && val != TD_PARTIALREG)
+ error ("sol_fetch_registers: td_thr_getgregs %s",
+ td_err_string (val));
+
+ /* For SPARC, TD_PARTIALREG means that only %i0...%i7, %l0..%l7, %pc
+ and %sp are saved (by a thread context switch). */
+
+ /* And, now the floating-point registers. */
+
+ val = td_thr_getfpregs (&thandle, &fpregset);
+ if (val != TD_OK && val != TD_NOFPREGS)
+ error ("sol_fetch_registers: td_thr_getfpregs %s",
+ td_err_string (val));
+
+ /* Note that we must call supply_gregset and supply_fpregset *after*
+ calling the td routines because the td routines call ps_lget*
+ which affect the values stored in the registers array. */
+
+ supply_gregset (regcache, (const gdb_gregset_t *) gregset_p);
+ supply_fpregset (regcache, (const gdb_fpregset_t *) fpregset_p);
+}
+
+void
+solaris_store_registers (struct regcache *regcache, int regno)
+{
+ if (!is_thread (inferior_ptid))
+ {
+ /* It's an LWP; pass the request on to procfs. */
+ procfs_store_registers (regcache, regno);
+ return;
+ }
+
+ /* Solaris thread: convert INFERIOR_PTID into a td_thrhandle_t. */
+ long thread;
+ td_err_e val;
+ td_thrhandle_t thandle;
+ prgregset_t gregset;
+ prfpregset_t fpregset;
+ struct process_info_private *proc = current_process ()->private;
+
+ thread = GET_THREAD (inferior_ptid);
+
+ val = td_ta_map_id2thr (proc->thread_agent, thread, &thandle);
+ if (val != TD_OK)
+ error ("sol_store_registers: td_ta_map_id2thr %s",
+ td_err_string (val));
+
+ if (regno != -1)
+ {
+ /* Not writing all the registers. */
+ char old_value[MAX_REGISTER_SIZE];
+
+ /* Save new register value. */
+ collect_register (regcache, regno, old_value);
+
+ val = td_thr_getgregs (&thandle, gregset);
+ if (val != TD_OK)
+ error ("sol_store_registers: td_thr_getgregs %s",
+ td_err_string (val));
+ val = td_thr_getfpregs (&thandle, &fpregset);
+ if (val != TD_OK)
+ error ("sol_store_registers: td_thr_getfpregs %s",
+ td_err_string (val));
+
+ /* Restore new register value. */
+ supply_register (regcache, regno, old_value);
+ }
+
+ fill_gregset (regcache, (gdb_gregset_t *) &gregset, regno);
+ fill_fpregset (regcache, (gdb_fpregset_t *) &fpregset, regno);
+
+ val = td_thr_setgregs (&thandle, gregset);
+ if (val != TD_OK)
+ error ("sol_store_registers: td_thr_setgregs %s",
+ td_err_string (val));
+ val = td_thr_setfpregs (&thandle, &fpregset);
+ if (val != TD_OK)
+ error ("sol_store_registers: td_thr_setfpregs %s",
+ td_err_string (val));
+}
+
+/* Copy LEN bytes from inferior's memory starting at MEMADDR
+ to debugger memory starting at MYADDR. */
+
+static int
+solaris_read_memory (CORE_ADDR memaddr, unsigned char *myaddr, int len)
+{
+ int retval;
+
+ if (is_thread (inferior_ptid) || !procfs_thread_alive (inferior_ptid))
+ inferior_ptid = procfs_first_available ();
+
+ retval = procfs_xfer_memory (memaddr, myaddr, len, 0 /*read*/);
+ if (retval != len)
+ return retval;
+
+ return 0;
+}
+
+/* Copy LEN bytes of data from debugger memory at MYADDR
+ to inferior's memory at MEMADDR.
+ On failure (cannot write the inferior)
+ returns the value of errno. */
+
+static int
+solaris_write_memory (CORE_ADDR memaddr, const unsigned char *myaddr, int len)
+{
+ int retval;
+
+ if (is_thread (inferior_ptid) || !procfs_thread_alive (inferior_ptid))
+ inferior_ptid = procfs_first_available ();
+
+ retval = procfs_xfer_memory (memaddr, (unsigned char *)myaddr,
+ len, 1 /*write*/);
+ if (retval != len)
+ return retval;
+
+ return 0;
+}
+
+
+static void
+solaris_look_up_symbols (void)
+{
+ struct process_info *proc = current_process ();
+
+ if (proc->private->thread_db_active)
+ return;
+
+ proc->private->thread_db_active = thread_db_init (0);
+}
+
+static void
+solaris_request_interrupt (void)
+{
+ procfs_stop_process (inferior_ptid);
+}
+
+/* Copy LEN bytes from inferior's auxiliary vector starting at OFFSET
+ to debugger memory starting at MYADDR. */
+
+static int
+solaris_read_auxv (CORE_ADDR offset, unsigned char *myaddr, unsigned int len)
+{
+ char filename[MAX_PROC_NAME_SIZE];
+ int fd, n;
+
+ snprintf (filename, sizeof filename, "/proc/%d/auxv",
+ ptid_get_pid (inferior_ptid));
+
+ fd = open (filename, O_RDONLY);
+ if (fd < 0)
+ return -1;
+
+ if (offset != (CORE_ADDR) 0
+ && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
+ n = -1;
+ else
+ n = read (fd, myaddr, len);
+
+ close (fd);
+
+ return n;
+}
+
+/* These watchpoint related wrapper functions simply pass on the function call
+ if the target has registered a corresponding function. */
+
+static int
+solaris_insert_point (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.insert_point != NULL)
+ return the_low_target.insert_point (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+solaris_remove_point (char type, CORE_ADDR addr, int len)
+{
+ if (the_low_target.remove_point != NULL)
+ return the_low_target.remove_point (type, addr, len);
+ else
+ /* Unsupported (see target.h). */
+ return 1;
+}
+
+static int
+solaris_stopped_by_watchpoint (void)
+{
+ if (the_low_target.stopped_by_watchpoint != NULL)
+ return the_low_target.stopped_by_watchpoint ();
+ else
+ return 0;
+}
+
+static CORE_ADDR
+solaris_stopped_data_address (void)
+{
+ if (the_low_target.stopped_data_address != NULL)
+ return the_low_target.stopped_data_address ();
+ else
+ return 0;
+}
+
+static void
+show_process (int pid, const char *username, struct buffer *buffer)
+{
+ char pathname[128];
+ FILE *f;
+ struct psinfo ps;
+
+ sprintf (pathname, "/proc/%d/psinfo", pid);
+
+ if ((f = fopen (pathname, "rb")) != NULL)
+ {
+ size_t len = fread (&ps, 1, sizeof (struct psinfo) - 1, f);
+ fclose (f);
+
+ if (len > 0)
+ {
+ buffer_xml_printf (buffer,
+ "<item>"
+ "<column name=\"pid\">%d</column>"
+ "<column name=\"user\">%s</column>"
+ "<column name=\"command\">%s</column>",
+ pid,
+ username,
+ ps.pr_psargs
+ );
+ buffer_xml_printf (buffer, "</item>");
+ }
+ }
+}
+
+/* Return a list of all processes, with pid and username. */
+static int
+solaris_qxfer_osdata (const char *annex,
+ unsigned char *readbuf, unsigned const char *writebuf,
+ CORE_ADDR offset, int len)
+{
+ static const char *buf;
+ static long len_avail = -1;
+ static struct buffer buffer;
+
+ DIR *dirp;
+
+ if (!strcmp (annex, "processes") == 0)
+ return 0;
+
+ if (!readbuf || writebuf)
+ return 0;
+
+ if (offset == 0)
+ {
+ if (len_avail != -1 && len_avail != 0)
+ buffer_free (&buffer);
+ len_avail = 0;
+ buf = NULL;
+ buffer_init (&buffer);
+ buffer_grow_str (&buffer, "<osdata type=\"processes\">");
+
+ dirp = opendir ("/proc");
+ if (dirp)
+ {
+ struct dirent *dp;
+ while ((dp = readdir (dirp)) != NULL)
+ {
+ struct stat statbuf;
+ char procentry[sizeof ("/proc/4294967295")];
+
+ if (!isdigit (dp->d_name[0])
+ || strlen (dp->d_name) > sizeof ("4294967295") - 1)
+ continue;
+
+ sprintf (procentry, "/proc/%s", dp->d_name);
+ if (stat (procentry, &statbuf) == 0
+ && S_ISDIR (statbuf.st_mode))
+ {
+ int pid = (int) strtoul (dp->d_name, NULL, 10);
+
+ struct passwd *entry = getpwuid (statbuf.st_uid);
+ show_process (pid, entry ? entry->pw_name : "?", &buffer);
+ }
+ }
+
+ closedir (dirp);
+ }
+ buffer_grow_str0 (&buffer, "</osdata>\n");
+ buf = buffer_finish (&buffer);
+ len_avail = strlen (buf);
+ }
+
+ if (offset >= len_avail)
+ {
+ /* Done. Get rid of the data. */
+ buffer_free (&buffer);
+ buf = NULL;
+ len_avail = 0;
+ return 0;
+ }
+
+ if (len > len_avail - offset)
+ len = len_avail - offset;
+ memcpy (readbuf, buf + offset, len);
+
+ return len;
+}
+
+
+static int
+solaris_supports_multi_process (void)
+{
+ return 1;
+}
+
+static int
+solaris_thread_lwp_id (ptid_t ptid, char *buf)
+{
+ ptid_t lwp = thread_to_lwp (ptid, -2);
+
+ if (GET_PID (lwp) != -1 && GET_PID (lwp) != -2)
+ write_ptid (buf, lwp);
+
+ return 0;
+}
+
+
+static struct target_ops solaris_target_ops = {
+ solaris_create_inferior,
+ solaris_attach,
+ solaris_kill,
+ solaris_detach,
+ solaris_join,
+ solaris_thread_alive,
+ solaris_resume,
+ solaris_wait,
+ solaris_fetch_registers,
+ solaris_store_registers,
+ solaris_read_memory,
+ solaris_write_memory,
+ solaris_look_up_symbols,
+ solaris_request_interrupt,
+ solaris_read_auxv,
+ solaris_insert_point,
+ solaris_remove_point,
+ solaris_stopped_by_watchpoint,
+ solaris_stopped_data_address,
+ NULL,
+ NULL,
+ NULL, // qxfer_spu,
+ hostio_last_error_from_errno,
+ solaris_qxfer_osdata,
+ NULL, // qxfer_siginfo,
+ NULL, // supports_non_stop,
+ NULL, // async,
+ NULL, // start_non_stop,
+ solaris_supports_multi_process,
+ NULL, // handle_monitor_command
+ NULL, // core_of_thread
+ solaris_thread_lwp_id,
+};
+
+void
+initialize_low (void)
+{
+ set_target_ops (&solaris_target_ops);
+ set_breakpoint_data (the_low_target.breakpoint,
+ the_low_target.breakpoint_len);
+ _initialize_proc_events ();
+}
diff -upN src-orig/src/gdb/gdbserver/solaris-low.h src/gdb/gdbserver/solaris-low.h
--- src-orig/src/gdb/gdbserver/solaris-low.h 1970-01-01 01:00:00.000000000 +0100
+++ src/gdb/gdbserver/solaris-low.h 2010-04-08 15:41:51.000000000 +0200
@@ -0,0 +1,139 @@
+/* Internal interfaces for the Solaris specific target code for gdbserver.
+ Copyright (C) 2010 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 <thread_db.h>
+#include <procfs.h>
+
+#include "gdb_proc_service.h"
+
+/* Default definitions. */
+
+#define MAX_REGISTER_SIZE 16
+
+#define GET_PID(ptid) ptid_get_pid (ptid)
+#define GET_LWP(ptid) ptid_get_lwp (ptid)
+#define GET_THREAD(ptid) ptid_get_tid (ptid)
+
+#define is_lwp(ptid) (GET_LWP (ptid) != 0)
+#define is_thread(ptid) (GET_THREAD (ptid) != 0)
+
+#define BUILD_LWP(lwp, pid) ptid_build (pid, lwp, 0)
+#define BUILD_THREAD(tid, pid) ptid_build (pid, 0, tid)
+
+#define pid_of(proc) ptid_get_pid ((proc)->head.id)
+#define lwpid_of(proc) ptid_get_lwp ((proc)->head.id)
+
+#define get_process(inf) ((struct process_info *)(inf))
+#define get_thread_process(thr) (get_process (inferior_target_data (thr)))
+
+/* format strings for /proc paths */
+# ifndef CTL_PROC_NAME_FMT
+# define MAIN_PROC_NAME_FMT "/proc/%d"
+# define CTL_PROC_NAME_FMT "/proc/%d/ctl"
+# define AS_PROC_NAME_FMT "/proc/%d/as"
+# define MAP_PROC_NAME_FMT "/proc/%d/map"
+# define STATUS_PROC_NAME_FMT "/proc/%d/status"
+# define MAX_PROC_NAME_SIZE sizeof("/proc/99999/lwp/8096/lstatus")
+# endif
+/* the name of the proc status struct depends on the implementation */
+
+#ifndef GDB_GREGSET_T
+#define GDB_GREGSET_T gregset_t
+#endif
+
+#ifndef GDB_FPREGSET_T
+#define GDB_FPREGSET_T fpregset_t
+#endif
+
+typedef GDB_GREGSET_T gdb_gregset_t;
+typedef GDB_FPREGSET_T gdb_fpregset_t;
+
+typedef pstatus_t gdb_prstatus_t;
+
+struct process_info_private
+{
+ /* True if this process has loaded thread_db, and it is active. */
+ int thread_db_active;
+
+ /* Structure that identifies the child proces for the
+ <proc_service.h> interface. */
+ struct ps_prochandle proc_handle;
+
+ /* Connection to the libthread_db library. */
+ td_thragent_t *thread_agent;
+};
+
+struct solaris_target_ops
+{
+ /* Architecture-specific setup. */
+ void (*arch_setup) (void);
+
+ /* The number of target registers. */
+ int num_regs;
+ int *regmap;
+ int (*cannot_fetch_register) (int);
+
+ /* Returns 0 if we can store the register, 1 if we can not
+ store the register, and 2 if failure to store the register
+ is acceptable. */
+ int (*cannot_store_register) (int);
+ CORE_ADDR (*get_pc) (struct regcache *regcache);
+ void (*set_pc) (struct regcache *regcach, CORE_ADDR newpc);
+ const unsigned char *breakpoint;
+ int breakpoint_len;
+ CORE_ADDR (*breakpoint_reinsert_addr) (void);
+
+
+ int decr_pc_after_break;
+ int (*breakpoint_at) (CORE_ADDR pc);
+
+ /* Watchpoint related functions. See target.h for comments. */
+ int (*insert_point) (char type, CORE_ADDR addr, int len);
+ int (*remove_point) (char type, CORE_ADDR addr, int len);
+ int (*stopped_by_watchpoint) (void);
+ CORE_ADDR (*stopped_data_address) (void);
+
+ /* Whether to left-pad registers for PEEKUSR/POKEUSR if they are smaller
+ than an xfer unit. */
+ int left_pad_xfer;
+};
+
+#define td_err_string thread_db_err_str
+
+struct target_waitstatus;
+
+ptid_t inferior_ptid;
+
+extern struct solaris_target_ops the_low_target;
+extern struct inferior_list all_processes;
+
+ptid_t lwp_to_thread (ptid_t lwp);
+
+int thread_db_init (int use_events);
+int thread_db_get_tls_address (struct thread_info *thread, CORE_ADDR offset,
+ CORE_ADDR load_module, CORE_ADDR *address);
+
+void get_last_target_status (ptid_t *ptidp, struct target_waitstatus *status);
+
+extern ptid_t procfs_first_available (void);
+
+extern void supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregs);
+extern void supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregs);
+extern void fill_gregset (struct regcache *regcache, gdb_gregset_t *gregs, int regno);
+extern void fill_fpregset (struct regcache *regcache, gdb_fpregset_t *fpregs, int regno);
+
diff -upN src-orig/src/gdb/gdbserver/solaris-sparc-low.c src/gdb/gdbserver/solaris-sparc-low.c
--- src-orig/src/gdb/gdbserver/solaris-sparc-low.c 1970-01-01 01:00:00.000000000 +0100
+++ src/gdb/gdbserver/solaris-sparc-low.c 2010-04-08 15:41:51.000000000 +0200
@@ -0,0 +1,238 @@
+/* Solaris SPARC specific low level interface, for the remote server for GDB.
+ Copyright (C) 2010 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 "server.h"
+#include "solaris-low.h"
+
+/* The stack pointer is offset from the stack frame by a BIAS of 2047
+ (0x7ff) for 64-bit code. BIAS is likely to be defined on SPARC
+ hosts, so undefine it first. */
+#undef BIAS
+#define BIAS 2047
+
+
+#define INSN_SIZE 4
+
+#define SPARC_R_REGS_NUM 32
+#define SPARC_F_REGS_NUM 32
+#define SPARC_CONTROL_REGS_NUM 8
+
+#define sparc_num_regs (SPARC_R_REGS_NUM + SPARC_F_REGS_NUM + SPARC_CONTROL_REGS_NUM)
+
+/* Each offset is multiplied by 4, because of the register size.
+ These offsets apply to the buffer sent/filled by ptrace.
+ Additionally, the array elements order corresponds to the .dat file, and the
+ gdb's registers enumeration order. */
+
+static int sparc_regmap[] = {
+ /* These offsets correspond to GET/SETREGSET. */
+ -1, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4, /* g0 .. g7 */
+ 8*4, 9*4, 10*4, 11*4, 12*4, 13*4, 14*4, 15*4, /* o0 .. o5, sp, o7 */
+ 16*4, 17*4, 18*4, 19*4, 20*4, 21*4, 22*4, 23*4, /* l0 .. l7 */
+ 24*4, 25*4, 26*4, 27*4, 28*4, 29*4, 30*4, 31*4, /* i0 .. i5, fp, i7 */
+
+ /* Floating point registers offsets correspond to GET/SETFPREGSET. */
+ 0*4, 1*4, 2*4, 3*4, 4*4, 5*4, 6*4, 7*4, /* f0 .. f7 */
+ 8*4, 9*4, 10*4, 11*4, 12*4, 13*4, 14*4, 15*4, /* f8 .. f15 */
+ 16*4, 17*4, 18*4, 19*4, 20*4, 21*4, 22*4, 23*4, /* f16 .. f23 */
+ 24*4, 25*4, 26*4, 27*4, 28*4, 29*4, 30*4, 31*4, /* f24 .. f31 */
+
+ 35*4, /* y */
+ 32*4, /* psr */
+ 36*4, /* wim */
+ 37*4, /* tbr */
+ 33*4, /* pc */
+ 34*4, /* npc */
+ 33*4 /* fsr */
+ /* 32*4,*/ /* csr */
+};
+
+struct regs_range_t
+{
+ int regno_start;
+ int regno_end;
+};
+
+static const struct regs_range_t gregs_ranges[] = {
+ { 1, 31 }, /* g1 .. i7 */
+ { 64, 65 }, /* y, psr */
+ { 68, 69 } /* pc .. npc */
+};
+
+#define N_GREGS_RANGES (sizeof (gregs_ranges) / sizeof (struct regs_range_t))
+
+/* Defined in auto-generated file reg-sparc32.c. */
+void init_registers_sparc32 (void);
+
+static const struct regs_range_t fpregs_ranges[] = {
+ { 32, 63 }, /* f0 .. f31 */
+ { 70, 70 } /* fsr */
+};
+
+#define N_FPREGS_RANGES (sizeof (fpregs_ranges) / sizeof (struct regs_range_t))
+
+static void
+sparc_fill_gregset (struct regcache *regcache, void *buf)
+{
+ int i;
+ int range;
+ gdb_byte *regs = buf;
+
+ for (range = 0; range < N_GREGS_RANGES; range++)
+ for (i = gregs_ranges[range].regno_start; i <= gregs_ranges[range].regno_end; i++)
+ if (sparc_regmap[i] != -1)
+ collect_register (regcache, i, regs + sparc_regmap[i]);
+}
+
+static void
+sparc_fill_fpregset (struct regcache *regcache, void *buf)
+{
+ int i;
+ int range;
+
+ for (range = 0; range < N_FPREGS_RANGES; range++)
+ for (i = fpregs_ranges[range].regno_start; i <= fpregs_ranges[range].regno_end; i++)
+ collect_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+
+}
+
+static void
+sparc_store_gregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+ char zerobuf[4];
+ int range;
+
+ memset (zerobuf, 0, sizeof(zerobuf));
+
+ for (range = 0; range < N_GREGS_RANGES; range++)
+ for (i = gregs_ranges[range].regno_start; i <= gregs_ranges[range].regno_end; i++)
+ if (sparc_regmap[i] != -1)
+ supply_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+ else
+ supply_register (regcache, i, zerobuf);
+
+ /* %g0 is always zero. */
+ supply_register (regcache, 0, zerobuf);
+}
+
+static void
+sparc_store_fpregset (struct regcache *regcache, const void *buf)
+{
+ int i;
+ int range;
+
+ for (range = 0; range < N_FPREGS_RANGES; range++)
+ for (i = fpregs_ranges[range].regno_start; i <= fpregs_ranges[range].regno_end; i++)
+ supply_register (regcache, i, ((char *) buf) + sparc_regmap[i]);
+}
+
+static int
+sparc_cannot_fetch_register (int regno)
+{
+ return (regno >= sparc_num_regs || sparc_regmap[regno] == -1);
+}
+
+static int
+sparc_cannot_store_register (int regno)
+{
+ return (regno >= sparc_num_regs || sparc_regmap[regno] == -1);
+}
+
+static CORE_ADDR
+sparc_get_pc (struct regcache *regcache)
+{
+ unsigned long pc;
+ collect_register_by_name (regcache, "pc", &pc);
+ return pc;
+}
+
+static const unsigned char sparc_breakpoint[INSN_SIZE] = { 0x91, 0xd0, 0x20, 0x01 };
+#define sparc_breakpoint_len INSN_SIZE
+
+/* We only place breakpoints in empty marker functions, and thread locking
+ is outside of the function. So rather than importing software single-step,
+ we can just run until exit. */
+static CORE_ADDR
+sparc_reinsert_addr (void)
+{
+ struct regcache *regcache = get_thread_regcache (current_process, 1);
+ CORE_ADDR lr;
+ /* O7 is the equivalent to the 'lr' of other archs. */
+ collect_register_by_name (regcache, "o7", &lr);
+ return lr;
+}
+
+static int
+sparc_breakpoint_at (CORE_ADDR where)
+{
+ unsigned char insn[INSN_SIZE];
+
+ (*the_target->read_memory) (where, (unsigned char *) insn, sizeof(insn));
+
+ if (memcmp(sparc_breakpoint, insn, sizeof(insn)) == 0)
+ return 1;
+
+ /* If necessary, recognize more trap instructions here. GDB only uses TRAP Always. */
+
+ return 0;
+}
+
+void
+supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregs)
+{
+ sparc_store_gregset (regcache, gregs);
+}
+
+void
+supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregs)
+{
+ sparc_store_fpregset (regcache, fpregs);
+}
+
+void
+fill_gregset (struct regcache *regcache, gdb_gregset_t *gregs, int regno)
+{
+ sparc_fill_gregset (regcache, gregs);
+}
+
+void
+fill_fpregset (struct regcache *regcache, gdb_fpregset_t *fpregs, int regno)
+{
+ sparc_fill_fpregset (regcache, fpregs);
+}
+
+
+struct solaris_target_ops the_low_target = {
+ init_registers_sparc32,
+ sparc_num_regs,
+ /* No regmap needs to be provided since this impl. doesn't use USRREGS. */
+ NULL,
+ sparc_cannot_fetch_register,
+ sparc_cannot_store_register,
+ sparc_get_pc,
+ /* No sparc_set_pc is needed. */
+ NULL,
+ (const unsigned char *) sparc_breakpoint,
+ sparc_breakpoint_len,
+ sparc_reinsert_addr,
+ 0,
+ sparc_breakpoint_at,
+ NULL, NULL, NULL, NULL, /* watchpoint functions */
+ 0
+};
diff -upN src-orig/src/gdb/gdbserver/sol-thread-db.c src/gdb/gdbserver/sol-thread-db.c
--- src-orig/src/gdb/gdbserver/sol-thread-db.c 1970-01-01 01:00:00.000000000 +0100
+++ src/gdb/gdbserver/sol-thread-db.c 2010-04-08 15:41:51.000000000 +0200
@@ -0,0 +1,171 @@
+/* Thread management interface on Solaris, for the remote server for GDB.
+ Copyright (C) 2010 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 "server.h"
+#include "solaris-low.h"
+#include "gdb_proc_service.h"
+
+extern int debug_threads;
+
+#ifdef HAVE_THREAD_DB_H
+#include <thread_db.h>
+#endif
+
+#include <stdint.h>
+
+
+extern void procfs_find_new_threads (void);
+
+char *
+thread_db_err_str (td_err_e err)
+{
+ static char buf[64];
+
+ switch (err)
+ {
+ case TD_OK:
+ return "generic 'call succeeded'";
+ case TD_ERR:
+ return "generic error";
+ case TD_NOTHR:
+ return "no thread to satisfy query";
+ case TD_NOSV:
+ return "no sync handle to satisfy query";
+ case TD_NOLWP:
+ return "no LWP to satisfy query";
+ case TD_BADPH:
+ return "invalid process handle";
+ case TD_BADTH:
+ return "invalid thread handle";
+ case TD_BADSH:
+ return "invalid synchronization handle";
+ case TD_BADTA:
+ return "invalid thread agent";
+ case TD_BADKEY:
+ return "invalid key";
+ case TD_NOMSG:
+ return "no event message for getmsg";
+ case TD_NOFPREGS:
+ return "FPU register set not available";
+ case TD_NOLIBTHREAD:
+ return "application not linked with libthread";
+ case TD_NOEVENT:
+ return "requested event is not supported";
+ case TD_NOCAPAB:
+ return "capability not available";
+ case TD_DBERR:
+ return "debugger service failed";
+ case TD_NOAPLIC:
+ return "operation not applicable to";
+ case TD_NOTSD:
+ return "no thread-specific data for this thread";
+ case TD_MALLOC:
+ return "malloc failed";
+ case TD_PARTIALREG:
+ return "only part of register set was written/read";
+ case TD_NOXREGS:
+ return "X register set not available for this thread";
+#ifdef HAVE_TD_VERSION
+ case TD_VERSION:
+ return "version mismatch between libthread_db and libpthread";
+#endif
+ default:
+ snprintf (buf, sizeof (buf), "unknown thread_db error '%d'", err);
+ return buf;
+ }
+}
+
+static int
+find_new_threads_callback (const td_thrhandle_t *th_p, void *data)
+{
+ td_thrinfo_t ti;
+ td_err_e err;
+ ptid_t ptid;
+
+ err = td_thr_get_info (th_p, &ti);
+ if (err != TD_OK)
+ error ("Cannot get thread info: %s", thread_db_err_str (err));
+
+ /* Check for zombies. */
+ if (ti.ti_state == TD_THR_UNKNOWN || ti.ti_state == TD_THR_ZOMBIE)
+ return 0;
+
+ ptid = BUILD_THREAD (ti.ti_lid, GET_PID (inferior_ptid));
+ if (find_thread_ptid(ptid) == NULL)
+ add_thread (ptid, NULL);
+
+ return 0;
+}
+
+static void
+thread_db_find_new_threads (void)
+{
+ td_err_e err;
+ struct process_info_private *proc = current_process()->private;
+
+ /* First find any new LWP's */
+ procfs_find_new_threads ();
+
+ /* Iterate over all user-space threads to discover new threads. */
+ err = td_ta_thr_iter (proc->thread_agent,
+ find_new_threads_callback, NULL,
+ TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY,
+ TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS);
+}
+
+
+
+int
+thread_db_init (int use_events)
+{
+ int err;
+ struct process_info *proc = current_process ();
+ struct process_info_private *priv = proc->private;
+
+
+ /* Now, initialize libthread_db. This needs to be done after the
+ share libraries are located because it needs information from
+ the user's thread library. */
+
+ err = td_init();
+ if (err != TD_OK)
+ {
+ warning ("thread_db_init: td_init: %s", td_err_string (err));
+ return 0;
+ }
+
+ err = td_ta_new (&priv->proc_handle, &priv->thread_agent);
+ switch (err)
+ {
+ case TD_NOLIBTHREAD:
+ /* No thread library was detected. */
+ return 0;
+
+ case TD_OK:
+ /* The thread library was detected. */
+ thread_db_find_new_threads ();
+ proc->all_symbols_looked_up = 1;
+ return 1;
+
+ default:
+ warning ("error initializing thread_db library: %s",
+ thread_db_err_str (err));
+ }
+
+ return 0;
+}