diff options
-rw-r--r-- | gdb/ChangeLog | 39 | ||||
-rw-r--r-- | gdb/gcore.c | 44 | ||||
-rw-r--r-- | gdb/gdbarch.c | 33 | ||||
-rw-r--r-- | gdb/gdbarch.h | 8 | ||||
-rwxr-xr-x | gdb/gdbarch.sh | 3 | ||||
-rw-r--r-- | gdb/linux-nat.c | 323 | ||||
-rw-r--r-- | gdb/linux-tdep.c | 275 | ||||
-rw-r--r-- | gdb/linux-tdep.h | 12 |
8 files changed, 422 insertions, 315 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 1e41e81..ab0407e0 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,44 @@ 2012-01-20 Ulrich Weigand <ulrich.weigand@linaro.org> + * gdbarch.sh (make_corefile_notes): New architecture callback. + * gdbarch.c: Regenerate. + * gdbarch.h: Likewise. + + * gcore.c (write_gcore_file): Try gdbarch_make_corefile_notes + before target_make_corefile_notes. If NULL is returned, the + target does not support core file generation. + + * linux-nat.c: Include "linux-tdep.h". + (find_signalled_thread, find_stop_signal): Remove. + (linux_nat_do_thread_registers): Likewise. + (struct linux_nat_corefile_thread_data): Likewise. + (linux_nat_corefile_thread_callback): Likewise. + (iterate_over_spus): Likewise. + (struct linux_spu_corefile_data): Likewise. + (linux_spu_corefile_callback): Likewise. + (linux_spu_make_corefile_notes): Likewise. + (linux_nat_collect_thread_registers): New function. + (linux_nat_make_corefile_notes): Replace contents by call to + linux_make_corefile_notes passing linux_nat_collect_thread_registers + as native-only callback. + + * linux-tdep.h: Include "bfd.h". + (struct regcache): Add forward declaration. + (linux_collect_thread_registers_ftype): New typedef. + (linux_make_corefile_notes): Add prototype. + * linux-tdep.c: Include "gdbthread.h", "gdbcore.h", "regcache.h", + "regset.h", and "elf-bfd.h". + (find_signalled_thread, find_stop_signal): New functions. + (linux_spu_make_corefile_notes): Likewise. + (linux_collect_thread_registers): Likewise. + (struct linux_corefile_thread_data): New data structure. + (linux_corefile_thread_callback): New funcion. + (linux_make_corefile_notes): Likewise. + (linux_make_corefile_notes_1): Likewise. + (linux_init_abi): Install it. + +2012-01-20 Ulrich Weigand <ulrich.weigand@linaro.org> + * gdbarch.sh (info_proc): New callback. * gdbarch.c, gdbarch.h: Regenerate. diff --git a/gdb/gcore.c b/gdb/gcore.c index f9e7590..2a769d3 100644 --- a/gdb/gcore.c +++ b/gdb/gcore.c @@ -71,35 +71,37 @@ write_gcore_file (bfd *obfd) asection *note_sec = NULL; /* An external target method must build the notes section. */ - note_data = target_make_corefile_notes (obfd, ¬e_size); + /* FIXME: uweigand/2011-10-06: All architectures that support core file + generation should be converted to gdbarch_make_corefile_notes; at that + point, the target vector method can be removed. */ + if (!gdbarch_make_corefile_notes_p (target_gdbarch)) + note_data = target_make_corefile_notes (obfd, ¬e_size); + else + note_data = gdbarch_make_corefile_notes (target_gdbarch, obfd, ¬e_size); - /* Create the note section. */ - if (note_data != NULL && note_size != 0) - { - note_sec = bfd_make_section_anyway_with_flags (obfd, "note0", - SEC_HAS_CONTENTS - | SEC_READONLY - | SEC_ALLOC); - if (note_sec == NULL) - error (_("Failed to create 'note' section for corefile: %s"), - bfd_errmsg (bfd_get_error ())); + if (note_data == NULL || note_size == 0) + error (_("Target does not support core file generation.")); - bfd_set_section_vma (obfd, note_sec, 0); - bfd_set_section_alignment (obfd, note_sec, 0); - bfd_set_section_size (obfd, note_sec, note_size); - } + /* Create the note section. */ + note_sec = bfd_make_section_anyway_with_flags (obfd, "note0", + SEC_HAS_CONTENTS + | SEC_READONLY + | SEC_ALLOC); + if (note_sec == NULL) + error (_("Failed to create 'note' section for corefile: %s"), + bfd_errmsg (bfd_get_error ())); + + bfd_set_section_vma (obfd, note_sec, 0); + bfd_set_section_alignment (obfd, note_sec, 0); + bfd_set_section_size (obfd, note_sec, note_size); /* Now create the memory/load sections. */ if (gcore_memory_sections (obfd) == 0) error (_("gcore: failed to get corefile memory sections from target.")); /* Write out the contents of the note section. */ - if (note_data != NULL && note_size != 0) - { - if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size)) - warning (_("writing note section (%s)"), - bfd_errmsg (bfd_get_error ())); - } + if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size)) + warning (_("writing note section (%s)"), bfd_errmsg (bfd_get_error ())); } static void diff --git a/gdb/gdbarch.c b/gdb/gdbarch.c index 2dbdda9..7a1db10 100644 --- a/gdb/gdbarch.c +++ b/gdb/gdbarch.c @@ -239,6 +239,7 @@ struct gdbarch gdbarch_fetch_pointer_argument_ftype *fetch_pointer_argument; gdbarch_regset_from_core_section_ftype *regset_from_core_section; struct core_regset_section * core_regset_sections; + gdbarch_make_corefile_notes_ftype *make_corefile_notes; gdbarch_core_xfer_shared_libraries_ftype *core_xfer_shared_libraries; gdbarch_core_pid_to_str_ftype *core_pid_to_str; const char * gcore_bfd_target; @@ -395,6 +396,7 @@ struct gdbarch startup_gdbarch = 0, /* fetch_pointer_argument */ 0, /* regset_from_core_section */ 0, /* core_regset_sections */ + 0, /* make_corefile_notes */ 0, /* core_xfer_shared_libraries */ 0, /* core_pid_to_str */ 0, /* gcore_bfd_target */ @@ -683,6 +685,7 @@ verify_gdbarch (struct gdbarch *gdbarch) /* Skip verify of register_reggroup_p, invalid_p == 0 */ /* Skip verify of fetch_pointer_argument, has predicate. */ /* Skip verify of regset_from_core_section, has predicate. */ + /* Skip verify of make_corefile_notes, has predicate. */ /* Skip verify of core_xfer_shared_libraries, has predicate. */ /* Skip verify of core_pid_to_str, has predicate. */ /* Skip verify of gcore_bfd_target, has predicate. */ @@ -1039,6 +1042,12 @@ gdbarch_dump (struct gdbarch *gdbarch, struct ui_file *file) "gdbarch_dump: long_long_bit = %s\n", plongest (gdbarch->long_long_bit)); fprintf_unfiltered (file, + "gdbarch_dump: gdbarch_make_corefile_notes_p() = %d\n", + gdbarch_make_corefile_notes_p (gdbarch)); + fprintf_unfiltered (file, + "gdbarch_dump: make_corefile_notes = <%s>\n", + host_address_to_string (gdbarch->make_corefile_notes)); + fprintf_unfiltered (file, "gdbarch_dump: gdbarch_max_insn_length_p() = %d\n", gdbarch_max_insn_length_p (gdbarch)); fprintf_unfiltered (file, @@ -3245,6 +3254,30 @@ set_gdbarch_core_regset_sections (struct gdbarch *gdbarch, } int +gdbarch_make_corefile_notes_p (struct gdbarch *gdbarch) +{ + gdb_assert (gdbarch != NULL); + return gdbarch->make_corefile_notes != NULL; +} + +char * +gdbarch_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size) +{ + gdb_assert (gdbarch != NULL); + gdb_assert (gdbarch->make_corefile_notes != NULL); + if (gdbarch_debug >= 2) + fprintf_unfiltered (gdb_stdlog, "gdbarch_make_corefile_notes called\n"); + return gdbarch->make_corefile_notes (gdbarch, obfd, note_size); +} + +void +set_gdbarch_make_corefile_notes (struct gdbarch *gdbarch, + gdbarch_make_corefile_notes_ftype make_corefile_notes) +{ + gdbarch->make_corefile_notes = make_corefile_notes; +} + +int gdbarch_core_xfer_shared_libraries_p (struct gdbarch *gdbarch) { gdb_assert (gdbarch != NULL); diff --git a/gdb/gdbarch.h b/gdb/gdbarch.h index 7d1671d..b611760 100644 --- a/gdb/gdbarch.h +++ b/gdb/gdbarch.h @@ -709,6 +709,14 @@ extern void set_gdbarch_regset_from_core_section (struct gdbarch *gdbarch, gdbar extern struct core_regset_section * gdbarch_core_regset_sections (struct gdbarch *gdbarch); extern void set_gdbarch_core_regset_sections (struct gdbarch *gdbarch, struct core_regset_section * core_regset_sections); +/* Create core file notes */ + +extern int gdbarch_make_corefile_notes_p (struct gdbarch *gdbarch); + +typedef char * (gdbarch_make_corefile_notes_ftype) (struct gdbarch *gdbarch, bfd *obfd, int *note_size); +extern char * gdbarch_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size); +extern void set_gdbarch_make_corefile_notes (struct gdbarch *gdbarch, gdbarch_make_corefile_notes_ftype *make_corefile_notes); + /* Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from core file into buffer READBUF with length LEN. */ diff --git a/gdb/gdbarch.sh b/gdb/gdbarch.sh index eb8527c..c4ff5d1 100755 --- a/gdb/gdbarch.sh +++ b/gdb/gdbarch.sh @@ -631,6 +631,9 @@ M:const struct regset *:regset_from_core_section:const char *sect_name, size_t s # Supported register notes in a core file. v:struct core_regset_section *:core_regset_sections:const char *name, int len::::::host_address_to_string (gdbarch->core_regset_sections) +# Create core file notes +M:char *:make_corefile_notes:bfd *obfd, int *note_size:obfd, note_size + # Read offset OFFSET of TARGET_OBJECT_LIBRARIES formatted shared libraries list from # core file into buffer READBUF with length LEN. M:LONGEST:core_xfer_shared_libraries:gdb_byte *readbuf, ULONGEST offset, LONGEST len:readbuf, offset, len diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c index 0a46e83..3a40c03 100644 --- a/gdb/linux-nat.c +++ b/gdb/linux-nat.c @@ -58,6 +58,7 @@ #include <sys/vfs.h> #include "solib.h" #include "linux-osdata.h" +#include "linux-tdep.h" #ifndef SPUFS_MAGIC #define SPUFS_MAGIC 0x23c9b64e @@ -4479,325 +4480,59 @@ linux_nat_find_memory_regions (find_memory_region_ftype func, void *obfd) return 0; } -static int -find_signalled_thread (struct thread_info *info, void *data) -{ - if (info->suspend.stop_signal != TARGET_SIGNAL_0 - && ptid_get_pid (info->ptid) == ptid_get_pid (inferior_ptid)) - return 1; - - return 0; -} - -static enum target_signal -find_stop_signal (void) -{ - struct thread_info *info = - iterate_over_threads (find_signalled_thread, NULL); - - if (info) - return info->suspend.stop_signal; - else - return TARGET_SIGNAL_0; -} - /* Records the thread's register state for the corefile note section. */ static char * -linux_nat_do_thread_registers (bfd *obfd, ptid_t ptid, - char *note_data, int *note_size, - enum target_signal stop_signal) +linux_nat_collect_thread_registers (const struct regcache *regcache, + ptid_t ptid, bfd *obfd, + char *note_data, int *note_size, + enum target_signal stop_signal) { - unsigned long lwp = ptid_get_lwp (ptid); - struct gdbarch *gdbarch = target_gdbarch; - struct regcache *regcache = get_thread_arch_regcache (ptid, gdbarch); + struct gdbarch *gdbarch = get_regcache_arch (regcache); const struct regset *regset; int core_regset_p; - struct cleanup *old_chain; - struct core_regset_section *sect_list; - char *gdb_regset; - - old_chain = save_inferior_ptid (); - inferior_ptid = ptid; - target_fetch_registers (regcache, -1); - do_cleanups (old_chain); + gdb_gregset_t gregs; + gdb_fpregset_t fpregs; core_regset_p = gdbarch_regset_from_core_section_p (gdbarch); - sect_list = gdbarch_core_regset_sections (gdbarch); - - /* The loop below uses the new struct core_regset_section, which stores - the supported section names and sizes for the core file. Note that - note PRSTATUS needs to be treated specially. But the other notes are - structurally the same, so they can benefit from the new struct. */ - if (core_regset_p && sect_list != NULL) - while (sect_list->sect_name != NULL) - { - regset = gdbarch_regset_from_core_section (gdbarch, - sect_list->sect_name, - sect_list->size); - gdb_assert (regset && regset->collect_regset); - gdb_regset = xmalloc (sect_list->size); - regset->collect_regset (regset, regcache, -1, - gdb_regset, sect_list->size); - - if (strcmp (sect_list->sect_name, ".reg") == 0) - note_data = (char *) elfcore_write_prstatus - (obfd, note_data, note_size, - lwp, target_signal_to_host (stop_signal), - gdb_regset); - else - note_data = (char *) elfcore_write_register_note - (obfd, note_data, note_size, - sect_list->sect_name, gdb_regset, - sect_list->size); - xfree (gdb_regset); - sect_list++; - } - /* For architectures that does not have the struct core_regset_section - implemented, we use the old method. When all the architectures have - the new support, the code below should be deleted. */ + if (core_regset_p + && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg", + sizeof (gregs))) + != NULL && regset->collect_regset != NULL) + regset->collect_regset (regset, regcache, -1, &gregs, sizeof (gregs)); else - { - gdb_gregset_t gregs; - gdb_fpregset_t fpregs; - - if (core_regset_p - && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg", - sizeof (gregs))) - != NULL && regset->collect_regset != NULL) - regset->collect_regset (regset, regcache, -1, - &gregs, sizeof (gregs)); - else - fill_gregset (regcache, &gregs, -1); + fill_gregset (regcache, &gregs, -1); - note_data = (char *) elfcore_write_prstatus - (obfd, note_data, note_size, lwp, target_signal_to_host (stop_signal), - &gregs); + note_data = (char *) elfcore_write_prstatus + (obfd, note_data, note_size, ptid_get_lwp (ptid), + target_signal_to_host (stop_signal), &gregs); - if (core_regset_p - && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg2", - sizeof (fpregs))) + if (core_regset_p + && (regset = gdbarch_regset_from_core_section (gdbarch, ".reg2", + sizeof (fpregs))) != NULL && regset->collect_regset != NULL) - regset->collect_regset (regset, regcache, -1, - &fpregs, sizeof (fpregs)); - else - fill_fpregset (regcache, &fpregs, -1); + regset->collect_regset (regset, regcache, -1, &fpregs, sizeof (fpregs)); + else + fill_fpregset (regcache, &fpregs, -1); - note_data = (char *) elfcore_write_prfpreg (obfd, - note_data, - note_size, - &fpregs, sizeof (fpregs)); - } + note_data = (char *) elfcore_write_prfpreg (obfd, note_data, note_size, + &fpregs, sizeof (fpregs)); return note_data; } -struct linux_nat_corefile_thread_data -{ - bfd *obfd; - char *note_data; - int *note_size; - int num_notes; - enum target_signal stop_signal; -}; - -/* Called by gdbthread.c once per thread. Records the thread's - register state for the corefile note section. */ - -static int -linux_nat_corefile_thread_callback (struct lwp_info *ti, void *data) -{ - struct linux_nat_corefile_thread_data *args = data; - - args->note_data = linux_nat_do_thread_registers (args->obfd, - ti->ptid, - args->note_data, - args->note_size, - args->stop_signal); - args->num_notes++; - - return 0; -} - -/* Enumerate spufs IDs for process PID. */ - -static void -iterate_over_spus (int pid, void (*callback) (void *, int), void *data) -{ - char path[128]; - DIR *dir; - struct dirent *entry; - - xsnprintf (path, sizeof path, "/proc/%d/fd", pid); - dir = opendir (path); - if (!dir) - return; - - rewinddir (dir); - while ((entry = readdir (dir)) != NULL) - { - struct stat st; - struct statfs stfs; - int fd; - - fd = atoi (entry->d_name); - if (!fd) - continue; - - xsnprintf (path, sizeof path, "/proc/%d/fd/%d", pid, fd); - if (stat (path, &st) != 0) - continue; - if (!S_ISDIR (st.st_mode)) - continue; - - if (statfs (path, &stfs) != 0) - continue; - if (stfs.f_type != SPUFS_MAGIC) - continue; - - callback (data, fd); - } - - closedir (dir); -} - -/* Generate corefile notes for SPU contexts. */ - -struct linux_spu_corefile_data -{ - bfd *obfd; - char *note_data; - int *note_size; -}; - -static void -linux_spu_corefile_callback (void *data, int fd) -{ - struct linux_spu_corefile_data *args = data; - int i; - - static const char *spu_files[] = - { - "object-id", - "mem", - "regs", - "fpcr", - "lslr", - "decr", - "decr_status", - "signal1", - "signal1_type", - "signal2", - "signal2_type", - "event_mask", - "event_status", - "mbox_info", - "ibox_info", - "wbox_info", - "dma_info", - "proxydma_info", - }; - - for (i = 0; i < sizeof (spu_files) / sizeof (spu_files[0]); i++) - { - char annex[32], note_name[32]; - gdb_byte *spu_data; - LONGEST spu_len; - - xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[i]); - spu_len = target_read_alloc (¤t_target, TARGET_OBJECT_SPU, - annex, &spu_data); - if (spu_len > 0) - { - xsnprintf (note_name, sizeof note_name, "SPU/%s", annex); - args->note_data = elfcore_write_note (args->obfd, args->note_data, - args->note_size, note_name, - NT_SPU, spu_data, spu_len); - xfree (spu_data); - } - } -} - -static char * -linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size) -{ - struct linux_spu_corefile_data args; - - args.obfd = obfd; - args.note_data = note_data; - args.note_size = note_size; - - iterate_over_spus (PIDGET (inferior_ptid), - linux_spu_corefile_callback, &args); - - return args.note_data; -} - /* Fills the "to_make_corefile_note" target vector. Builds the note section for a corefile, and returns it in a malloc buffer. */ static char * linux_nat_make_corefile_notes (bfd *obfd, int *note_size) { - struct linux_nat_corefile_thread_data thread_args; - /* The variable size must be >= sizeof (prpsinfo_t.pr_fname). */ - char fname[16] = { '\0' }; - /* The variable size must be >= sizeof (prpsinfo_t.pr_psargs). */ - char psargs[80] = { '\0' }; - char *note_data = NULL; - ptid_t filter = pid_to_ptid (ptid_get_pid (inferior_ptid)); - gdb_byte *auxv; - int auxv_len; - - if (get_exec_file (0)) - { - strncpy (fname, lbasename (get_exec_file (0)), sizeof (fname)); - strncpy (psargs, get_exec_file (0), sizeof (psargs)); - if (get_inferior_args ()) - { - char *string_end; - char *psargs_end = psargs + sizeof (psargs); - - /* linux_elfcore_write_prpsinfo () handles zero unterminated - strings fine. */ - string_end = memchr (psargs, 0, sizeof (psargs)); - if (string_end != NULL) - { - *string_end++ = ' '; - strncpy (string_end, get_inferior_args (), - psargs_end - string_end); - } - } - note_data = (char *) elfcore_write_prpsinfo (obfd, - note_data, - note_size, fname, psargs); - } - - /* Dump information for threads. */ - thread_args.obfd = obfd; - thread_args.note_data = note_data; - thread_args.note_size = note_size; - thread_args.num_notes = 0; - thread_args.stop_signal = find_stop_signal (); - iterate_over_lwps (filter, linux_nat_corefile_thread_callback, &thread_args); - gdb_assert (thread_args.num_notes != 0); - note_data = thread_args.note_data; - - auxv_len = target_read_alloc (¤t_target, TARGET_OBJECT_AUXV, - NULL, &auxv); - if (auxv_len > 0) - { - note_data = elfcore_write_note (obfd, note_data, note_size, - "CORE", NT_AUXV, auxv, auxv_len); - xfree (auxv); - } - - note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size); - - make_cleanup (xfree, note_data); - return note_data; + /* FIXME: uweigand/2011-10-06: Once all GNU/Linux architectures have been + converted to gdbarch_core_regset_sections, this function can go away. */ + return linux_make_corefile_notes (target_gdbarch, obfd, note_size, + linux_nat_collect_thread_registers); } /* Implement the to_xfer_partial interface for memory reads using the /proc diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index ff649b8..60fe8b6 100644 --- a/gdb/linux-tdep.c +++ b/gdb/linux-tdep.c @@ -22,7 +22,12 @@ #include "linux-tdep.h" #include "auxv.h" #include "target.h" +#include "gdbthread.h" +#include "gdbcore.h" +#include "regcache.h" +#include "regset.h" #include "elf/common.h" +#include "elf-bfd.h" /* for elfcore_write_* */ #include "inferior.h" #include "cli/cli-utils.h" @@ -525,6 +530,275 @@ linux_info_proc (struct gdbarch *gdbarch, char *args, } } +/* Determine which signal stopped execution. */ + +static int +find_signalled_thread (struct thread_info *info, void *data) +{ + if (info->suspend.stop_signal != TARGET_SIGNAL_0 + && ptid_get_pid (info->ptid) == ptid_get_pid (inferior_ptid)) + return 1; + + return 0; +} + +static enum target_signal +find_stop_signal (void) +{ + struct thread_info *info = + iterate_over_threads (find_signalled_thread, NULL); + + if (info) + return info->suspend.stop_signal; + else + return TARGET_SIGNAL_0; +} + +/* Generate corefile notes for SPU contexts. */ + +static char * +linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size) +{ + static const char *spu_files[] = + { + "object-id", + "mem", + "regs", + "fpcr", + "lslr", + "decr", + "decr_status", + "signal1", + "signal1_type", + "signal2", + "signal2_type", + "event_mask", + "event_status", + "mbox_info", + "ibox_info", + "wbox_info", + "dma_info", + "proxydma_info", + }; + + enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); + gdb_byte *spu_ids; + LONGEST i, j, size; + + /* Determine list of SPU ids. */ + size = target_read_alloc (¤t_target, TARGET_OBJECT_SPU, + NULL, &spu_ids); + + /* Generate corefile notes for each SPU file. */ + for (i = 0; i < size; i += 4) + { + int fd = extract_unsigned_integer (spu_ids + i, 4, byte_order); + + for (j = 0; j < sizeof (spu_files) / sizeof (spu_files[0]); j++) + { + char annex[32], note_name[32]; + gdb_byte *spu_data; + LONGEST spu_len; + + xsnprintf (annex, sizeof annex, "%d/%s", fd, spu_files[j]); + spu_len = target_read_alloc (¤t_target, TARGET_OBJECT_SPU, + annex, &spu_data); + if (spu_len > 0) + { + xsnprintf (note_name, sizeof note_name, "SPU/%s", annex); + note_data = elfcore_write_note (obfd, note_data, note_size, + note_name, NT_SPU, + spu_data, spu_len); + xfree (spu_data); + + if (!note_data) + { + xfree (spu_ids); + return NULL; + } + } + } + } + + if (size > 0) + xfree (spu_ids); + + return note_data; +} + +/* Records the thread's register state for the corefile note + section. */ + +static char * +linux_collect_thread_registers (const struct regcache *regcache, + ptid_t ptid, bfd *obfd, + char *note_data, int *note_size, + enum target_signal stop_signal) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + struct core_regset_section *sect_list; + unsigned long lwp; + + sect_list = gdbarch_core_regset_sections (gdbarch); + gdb_assert (sect_list); + + /* For remote targets the LWP may not be available, so use the TID. */ + lwp = ptid_get_lwp (ptid); + if (!lwp) + lwp = ptid_get_tid (ptid); + + while (sect_list->sect_name != NULL) + { + const struct regset *regset; + char *buf; + + regset = gdbarch_regset_from_core_section (gdbarch, + sect_list->sect_name, + sect_list->size); + gdb_assert (regset && regset->collect_regset); + + buf = xmalloc (sect_list->size); + regset->collect_regset (regset, regcache, -1, buf, sect_list->size); + + /* PRSTATUS still needs to be treated specially. */ + if (strcmp (sect_list->sect_name, ".reg") == 0) + note_data = (char *) elfcore_write_prstatus + (obfd, note_data, note_size, lwp, + target_signal_to_host (stop_signal), buf); + else + note_data = (char *) elfcore_write_register_note + (obfd, note_data, note_size, + sect_list->sect_name, buf, sect_list->size); + xfree (buf); + sect_list++; + + if (!note_data) + return NULL; + } + + return note_data; +} + +struct linux_corefile_thread_data +{ + struct gdbarch *gdbarch; + int pid; + bfd *obfd; + char *note_data; + int *note_size; + int num_notes; + enum target_signal stop_signal; + linux_collect_thread_registers_ftype collect; +}; + +/* Called by gdbthread.c once per thread. Records the thread's + register state for the corefile note section. */ + +static int +linux_corefile_thread_callback (struct thread_info *info, void *data) +{ + struct linux_corefile_thread_data *args = data; + + if (ptid_get_pid (info->ptid) == args->pid) + { + struct cleanup *old_chain; + struct regcache *regcache; + regcache = get_thread_arch_regcache (info->ptid, args->gdbarch); + + old_chain = save_inferior_ptid (); + inferior_ptid = info->ptid; + target_fetch_registers (regcache, -1); + do_cleanups (old_chain); + + args->note_data = args->collect (regcache, info->ptid, args->obfd, + args->note_data, args->note_size, + args->stop_signal); + args->num_notes++; + } + + return !args->note_data; +} + +/* Fills the "to_make_corefile_note" target vector. Builds the note + section for a corefile, and returns it in a malloc buffer. */ + +char * +linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size, + linux_collect_thread_registers_ftype collect) +{ + struct linux_corefile_thread_data thread_args; + char *note_data = NULL; + gdb_byte *auxv; + int auxv_len; + + /* Process information. */ + if (get_exec_file (0)) + { + const char *fname = lbasename (get_exec_file (0)); + char *psargs = xstrdup (fname); + + if (get_inferior_args ()) + psargs = reconcat (psargs, psargs, " ", get_inferior_args (), + (char *) NULL); + + note_data = elfcore_write_prpsinfo (obfd, note_data, note_size, + fname, psargs); + xfree (psargs); + + if (!note_data) + return NULL; + } + + /* Thread register information. */ + thread_args.gdbarch = gdbarch; + thread_args.pid = ptid_get_pid (inferior_ptid); + thread_args.obfd = obfd; + thread_args.note_data = note_data; + thread_args.note_size = note_size; + thread_args.num_notes = 0; + thread_args.stop_signal = find_stop_signal (); + thread_args.collect = collect; + iterate_over_threads (linux_corefile_thread_callback, &thread_args); + note_data = thread_args.note_data; + if (!note_data) + return NULL; + + /* Auxillary vector. */ + auxv_len = target_read_alloc (¤t_target, TARGET_OBJECT_AUXV, + NULL, &auxv); + if (auxv_len > 0) + { + note_data = elfcore_write_note (obfd, note_data, note_size, + "CORE", NT_AUXV, auxv, auxv_len); + xfree (auxv); + + if (!note_data) + return NULL; + } + + /* SPU information. */ + note_data = linux_spu_make_corefile_notes (obfd, note_data, note_size); + if (!note_data) + return NULL; + + make_cleanup (xfree, note_data); + return note_data; +} + +static char * +linux_make_corefile_notes_1 (struct gdbarch *gdbarch, bfd *obfd, int *note_size) +{ + /* FIXME: uweigand/2011-10-06: Once all GNU/Linux architectures have been + converted to gdbarch_core_regset_sections, we no longer need to fall back + to the target method at this point. */ + + if (!gdbarch_core_regset_sections (gdbarch)) + return target_make_corefile_notes (obfd, note_size); + else + return linux_make_corefile_notes (gdbarch, obfd, note_size, + linux_collect_thread_registers); +} + /* To be called from the various GDB_OSABI_LINUX handlers for the various GNU/Linux architectures and machine types. */ @@ -533,6 +807,7 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) { set_gdbarch_core_pid_to_str (gdbarch, linux_core_pid_to_str); set_gdbarch_info_proc (gdbarch, linux_info_proc); + set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes_1); } void diff --git a/gdb/linux-tdep.h b/gdb/linux-tdep.h index 4e9f560..d2f4077 100644 --- a/gdb/linux-tdep.h +++ b/gdb/linux-tdep.h @@ -20,6 +20,18 @@ #ifndef LINUX_TDEP_H #define LINUX_TDEP_H +#include "bfd.h" + +struct regcache; + +typedef char *(*linux_collect_thread_registers_ftype) (const struct regcache *, + ptid_t, + bfd *, char *, int *, + enum target_signal); + +char *linux_make_corefile_notes (struct gdbarch *, bfd *, int *, + linux_collect_thread_registers_ftype); + struct type *linux_get_siginfo_type (struct gdbarch *); extern void linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch); |