Index: gdb/linux-tdep.c |
diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c |
index b6f2efbd45a439e70fd2fee430f556107716daae..90b763f375e921ea327a0b441bd052a1e19ca362 100644 |
--- a/gdb/linux-tdep.c |
+++ b/gdb/linux-tdep.c |
@@ -1,6 +1,6 @@ |
/* Target-dependent code for GNU/Linux, architecture independent. |
- Copyright (C) 2009-2012 Free Software Foundation, Inc. |
+ Copyright (C) 2009-2013 Free Software Foundation, Inc. |
This file is part of GDB. |
@@ -30,9 +30,77 @@ |
#include "elf-bfd.h" /* for elfcore_write_* */ |
#include "inferior.h" |
#include "cli/cli-utils.h" |
+#include "arch-utils.h" |
+#include "gdb_obstack.h" |
+#include "cli/cli-utils.h" |
#include <ctype.h> |
+/* This enum represents the signals' numbers on a generic architecture |
+ running the Linux kernel. The definition of "generic" comes from |
+ the file <include/uapi/asm-generic/signal.h>, from the Linux kernel |
+ tree, which is the "de facto" implementation of signal numbers to |
+ be used by new architecture ports. |
+ |
+ For those architectures which have differences between the generic |
+ standard (e.g., Alpha), we define the different signals (and *only* |
+ those) in the specific target-dependent file (e.g., |
+ alpha-linux-tdep.c, for Alpha). Please refer to the architecture's |
+ tdep file for more information. |
+ |
+ ARM deserves a special mention here. On the file |
+ <arch/arm/include/uapi/asm/signal.h>, it defines only one different |
+ (and ARM-only) signal, which is SIGSWI, with the same number as |
+ SIGRTMIN. This signal is used only for a very specific target, |
+ called ArthurOS (from RISCOS). Therefore, we do not handle it on |
+ the ARM-tdep file, and we can safely use the generic signal handler |
+ here for ARM targets. |
+ |
+ As stated above, this enum is derived from |
+ <include/uapi/asm-generic/signal.h>, from the Linux kernel |
+ tree. */ |
+ |
+enum |
+ { |
+ LINUX_SIGHUP = 1, |
+ LINUX_SIGINT = 2, |
+ LINUX_SIGQUIT = 3, |
+ LINUX_SIGILL = 4, |
+ LINUX_SIGTRAP = 5, |
+ LINUX_SIGABRT = 6, |
+ LINUX_SIGIOT = 6, |
+ LINUX_SIGBUS = 7, |
+ LINUX_SIGFPE = 8, |
+ LINUX_SIGKILL = 9, |
+ LINUX_SIGUSR1 = 10, |
+ LINUX_SIGSEGV = 11, |
+ LINUX_SIGUSR2 = 12, |
+ LINUX_SIGPIPE = 13, |
+ LINUX_SIGALRM = 14, |
+ LINUX_SIGTERM = 15, |
+ LINUX_SIGSTKFLT = 16, |
+ LINUX_SIGCHLD = 17, |
+ LINUX_SIGCONT = 18, |
+ LINUX_SIGSTOP = 19, |
+ LINUX_SIGTSTP = 20, |
+ LINUX_SIGTTIN = 21, |
+ LINUX_SIGTTOU = 22, |
+ LINUX_SIGURG = 23, |
+ LINUX_SIGXCPU = 24, |
+ LINUX_SIGXFSZ = 25, |
+ LINUX_SIGVTALRM = 26, |
+ LINUX_SIGPROF = 27, |
+ LINUX_SIGWINCH = 28, |
+ LINUX_SIGIO = 29, |
+ LINUX_SIGPOLL = LINUX_SIGIO, |
+ LINUX_SIGPWR = 30, |
+ LINUX_SIGSYS = 31, |
+ LINUX_SIGUNUSED = 31, |
+ |
+ LINUX_SIGRTMIN = 32, |
+ LINUX_SIGRTMAX = 64, |
+ }; |
+ |
static struct gdbarch_data *linux_gdbarch_data_handle; |
struct linux_gdbarch_data |
@@ -173,19 +241,22 @@ linux_get_siginfo_type (struct gdbarch *gdbarch) |
return siginfo_type; |
} |
-static int |
-linux_has_shared_address_space (struct gdbarch *gdbarch) |
+/* Return true if the target is running on uClinux instead of normal |
+ Linux kernel. */ |
+ |
+int |
+linux_is_uclinux (void) |
{ |
- /* Determine whether we are running on uClinux or normal Linux |
- kernel. */ |
CORE_ADDR dummy; |
- int target_is_uclinux; |
- target_is_uclinux |
- = (target_auxv_search (¤t_target, AT_NULL, &dummy) > 0 |
- && target_auxv_search (¤t_target, AT_PAGESZ, &dummy) == 0); |
+ return (target_auxv_search (¤t_target, AT_NULL, &dummy) > 0 |
+ && target_auxv_search (¤t_target, AT_PAGESZ, &dummy) == 0); |
+} |
- return target_is_uclinux; |
+static int |
+linux_has_shared_address_space (struct gdbarch *gdbarch) |
+{ |
+ return linux_is_uclinux (); |
} |
/* This is how we want PTIDs from core files to be printed. */ |
@@ -222,8 +293,7 @@ read_mapping (const char *line, |
p++; |
*endaddr = strtoulst (p, &p, 16); |
- while (*p && isspace (*p)) |
- p++; |
+ p = skip_spaces_const (p); |
*permissions = p; |
while (*p && !isspace (*p)) |
p++; |
@@ -231,8 +301,7 @@ read_mapping (const char *line, |
*offset = strtoulst (p, &p, 16); |
- while (*p && isspace (*p)) |
- p++; |
+ p = skip_spaces_const (p); |
*device = p; |
while (*p && !isspace (*p)) |
p++; |
@@ -240,8 +309,7 @@ read_mapping (const char *line, |
*inode = strtoulst (p, &p, 10); |
- while (*p && isspace (*p)) |
- p++; |
+ p = skip_spaces_const (p); |
*filename = p; |
} |
@@ -261,7 +329,7 @@ linux_info_proc (struct gdbarch *gdbarch, char *args, |
int status_f = (what == IP_STATUS || what == IP_ALL); |
int stat_f = (what == IP_STAT || what == IP_ALL); |
char filename[100]; |
- gdb_byte *data; |
+ char *data; |
int target_errno; |
if (args && isdigit (args[0])) |
@@ -406,8 +474,7 @@ linux_info_proc (struct gdbarch *gdbarch, char *args, |
printf_filtered (_("Process: %s\n"), |
pulongest (strtoulst (p, &p, 10))); |
- while (*p && isspace (*p)) |
- p++; |
+ p = skip_spaces_const (p); |
if (*p == '(') |
{ |
const char *ep = strchr (p, ')'); |
@@ -419,8 +486,7 @@ linux_info_proc (struct gdbarch *gdbarch, char *args, |
} |
} |
- while (*p && isspace (*p)) |
- p++; |
+ p = skip_spaces_const (p); |
if (*p) |
printf_filtered (_("State: %c\n"), *p++); |
@@ -533,33 +599,180 @@ linux_info_proc (struct gdbarch *gdbarch, char *args, |
} |
} |
+/* Implement "info proc mappings" for a corefile. */ |
+ |
+static void |
+linux_core_info_proc_mappings (struct gdbarch *gdbarch, char *args) |
+{ |
+ asection *section; |
+ ULONGEST count, page_size; |
+ unsigned char *descdata, *filenames, *descend, *contents; |
+ size_t note_size; |
+ unsigned int addr_size_bits, addr_size; |
+ struct cleanup *cleanup; |
+ struct gdbarch *core_gdbarch = gdbarch_from_bfd (core_bfd); |
+ /* We assume this for reading 64-bit core files. */ |
+ gdb_static_assert (sizeof (ULONGEST) >= 8); |
+ |
+ section = bfd_get_section_by_name (core_bfd, ".note.linuxcore.file"); |
+ if (section == NULL) |
+ { |
+ warning (_("unable to find mappings in core file")); |
+ return; |
+ } |
+ |
+ addr_size_bits = gdbarch_addr_bit (core_gdbarch); |
+ addr_size = addr_size_bits / 8; |
+ note_size = bfd_get_section_size (section); |
+ |
+ if (note_size < 2 * addr_size) |
+ error (_("malformed core note - too short for header")); |
+ |
+ contents = xmalloc (note_size); |
+ cleanup = make_cleanup (xfree, contents); |
+ if (!bfd_get_section_contents (core_bfd, section, contents, 0, note_size)) |
+ error (_("could not get core note contents")); |
+ |
+ descdata = contents; |
+ descend = descdata + note_size; |
+ |
+ if (descdata[note_size - 1] != '\0') |
+ error (_("malformed note - does not end with \\0")); |
+ |
+ count = bfd_get (addr_size_bits, core_bfd, descdata); |
+ descdata += addr_size; |
+ |
+ page_size = bfd_get (addr_size_bits, core_bfd, descdata); |
+ descdata += addr_size; |
+ |
+ if (note_size < 2 * addr_size + count * 3 * addr_size) |
+ error (_("malformed note - too short for supplied file count")); |
+ |
+ printf_filtered (_("Mapped address spaces:\n\n")); |
+ if (gdbarch_addr_bit (gdbarch) == 32) |
+ { |
+ printf_filtered ("\t%10s %10s %10s %10s %s\n", |
+ "Start Addr", |
+ " End Addr", |
+ " Size", " Offset", "objfile"); |
+ } |
+ else |
+ { |
+ printf_filtered (" %18s %18s %10s %10s %s\n", |
+ "Start Addr", |
+ " End Addr", |
+ " Size", " Offset", "objfile"); |
+ } |
+ |
+ filenames = descdata + count * 3 * addr_size; |
+ while (--count > 0) |
+ { |
+ ULONGEST start, end, file_ofs; |
+ |
+ if (filenames == descend) |
+ error (_("malformed note - filenames end too early")); |
+ |
+ start = bfd_get (addr_size_bits, core_bfd, descdata); |
+ descdata += addr_size; |
+ end = bfd_get (addr_size_bits, core_bfd, descdata); |
+ descdata += addr_size; |
+ file_ofs = bfd_get (addr_size_bits, core_bfd, descdata); |
+ descdata += addr_size; |
+ |
+ file_ofs *= page_size; |
+ |
+ if (gdbarch_addr_bit (gdbarch) == 32) |
+ printf_filtered ("\t%10s %10s %10s %10s %s\n", |
+ paddress (gdbarch, start), |
+ paddress (gdbarch, end), |
+ hex_string (end - start), |
+ hex_string (file_ofs), |
+ filenames); |
+ else |
+ printf_filtered (" %18s %18s %10s %10s %s\n", |
+ paddress (gdbarch, start), |
+ paddress (gdbarch, end), |
+ hex_string (end - start), |
+ hex_string (file_ofs), |
+ filenames); |
+ |
+ filenames += 1 + strlen ((char *) filenames); |
+ } |
+ |
+ do_cleanups (cleanup); |
+} |
+ |
+/* Implement "info proc" for a corefile. */ |
+ |
+static void |
+linux_core_info_proc (struct gdbarch *gdbarch, char *args, |
+ enum info_proc_what what) |
+{ |
+ int exe_f = (what == IP_MINIMAL || what == IP_EXE || what == IP_ALL); |
+ int mappings_f = (what == IP_MAPPINGS || what == IP_ALL); |
+ |
+ if (exe_f) |
+ { |
+ const char *exe; |
+ |
+ exe = bfd_core_file_failing_command (core_bfd); |
+ if (exe != NULL) |
+ printf_filtered ("exe = '%s'\n", exe); |
+ else |
+ warning (_("unable to find command name in core file")); |
+ } |
+ |
+ if (mappings_f) |
+ linux_core_info_proc_mappings (gdbarch, args); |
+ |
+ if (!exe_f && !mappings_f) |
+ error (_("unable to handle request")); |
+} |
+ |
+typedef int linux_find_memory_region_ftype (ULONGEST vaddr, ULONGEST size, |
+ ULONGEST offset, ULONGEST inode, |
+ int read, int write, |
+ int exec, int modified, |
+ const char *filename, |
+ void *data); |
+ |
/* List memory regions in the inferior for a corefile. */ |
static int |
-linux_find_memory_regions (struct gdbarch *gdbarch, |
- find_memory_region_ftype func, void *obfd) |
+linux_find_memory_regions_full (struct gdbarch *gdbarch, |
+ linux_find_memory_region_ftype *func, |
+ void *obfd) |
{ |
- char filename[100]; |
- gdb_byte *data; |
+ char mapsfilename[100]; |
+ char *data; |
/* We need to know the real target PID to access /proc. */ |
if (current_inferior ()->fake_pid_p) |
return 1; |
- xsnprintf (filename, sizeof filename, |
- "/proc/%d/maps", current_inferior ()->pid); |
- data = target_fileio_read_stralloc (filename); |
+ xsnprintf (mapsfilename, sizeof mapsfilename, |
+ "/proc/%d/smaps", current_inferior ()->pid); |
+ data = target_fileio_read_stralloc (mapsfilename); |
+ if (data == NULL) |
+ { |
+ /* Older Linux kernels did not support /proc/PID/smaps. */ |
+ xsnprintf (mapsfilename, sizeof mapsfilename, |
+ "/proc/%d/maps", current_inferior ()->pid); |
+ data = target_fileio_read_stralloc (mapsfilename); |
+ } |
if (data) |
{ |
struct cleanup *cleanup = make_cleanup (xfree, data); |
char *line; |
- for (line = strtok (data, "\n"); line; line = strtok (NULL, "\n")) |
+ line = strtok (data, "\n"); |
+ while (line) |
{ |
ULONGEST addr, endaddr, offset, inode; |
const char *permissions, *device, *filename; |
size_t permissions_len, device_len; |
int read, write, exec; |
+ int modified = 0, has_anonymous = 0; |
read_mapping (line, &addr, &endaddr, &permissions, &permissions_len, |
&offset, &device, &device_len, &inode, &filename); |
@@ -569,8 +782,46 @@ linux_find_memory_regions (struct gdbarch *gdbarch, |
write = (memchr (permissions, 'w', permissions_len) != 0); |
exec = (memchr (permissions, 'x', permissions_len) != 0); |
+ /* Try to detect if region was modified by parsing smaps counters. */ |
+ for (line = strtok (NULL, "\n"); |
+ line && line[0] >= 'A' && line[0] <= 'Z'; |
+ line = strtok (NULL, "\n")) |
+ { |
+ char keyword[64 + 1]; |
+ |
+ if (sscanf (line, "%64s", keyword) != 1) |
+ { |
+ warning (_("Error parsing {s,}maps file '%s'"), mapsfilename); |
+ break; |
+ } |
+ if (strcmp (keyword, "Anonymous:") == 0) |
+ has_anonymous = 1; |
+ if (strcmp (keyword, "Shared_Dirty:") == 0 |
+ || strcmp (keyword, "Private_Dirty:") == 0 |
+ || strcmp (keyword, "Swap:") == 0 |
+ || strcmp (keyword, "Anonymous:") == 0) |
+ { |
+ unsigned long number; |
+ |
+ if (sscanf (line, "%*s%lu", &number) != 1) |
+ { |
+ warning (_("Error parsing {s,}maps file '%s' number"), |
+ mapsfilename); |
+ break; |
+ } |
+ if (number != 0) |
+ modified = 1; |
+ } |
+ } |
+ |
+ /* Older Linux kernels did not support the "Anonymous:" counter. |
+ If it is missing, we can't be sure - dump all the pages. */ |
+ if (!has_anonymous) |
+ modified = 1; |
+ |
/* Invoke the callback function to create the corefile segment. */ |
- func (addr, endaddr - addr, read, write, exec, obfd); |
+ func (addr, endaddr - addr, offset, inode, |
+ read, write, exec, modified, filename, obfd); |
} |
do_cleanups (cleanup); |
@@ -580,6 +831,51 @@ linux_find_memory_regions (struct gdbarch *gdbarch, |
return 1; |
} |
+/* A structure for passing information through |
+ linux_find_memory_regions_full. */ |
+ |
+struct linux_find_memory_regions_data |
+{ |
+ /* The original callback. */ |
+ |
+ find_memory_region_ftype func; |
+ |
+ /* The original datum. */ |
+ |
+ void *obfd; |
+}; |
+ |
+/* A callback for linux_find_memory_regions that converts between the |
+ "full"-style callback and find_memory_region_ftype. */ |
+ |
+static int |
+linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size, |
+ ULONGEST offset, ULONGEST inode, |
+ int read, int write, int exec, int modified, |
+ const char *filename, void *arg) |
+{ |
+ struct linux_find_memory_regions_data *data = arg; |
+ |
+ return data->func (vaddr, size, read, write, exec, modified, data->obfd); |
+} |
+ |
+/* A variant of linux_find_memory_regions_full that is suitable as the |
+ gdbarch find_memory_regions method. */ |
+ |
+static int |
+linux_find_memory_regions (struct gdbarch *gdbarch, |
+ find_memory_region_ftype func, void *obfd) |
+{ |
+ struct linux_find_memory_regions_data data; |
+ |
+ data.func = func; |
+ data.obfd = obfd; |
+ |
+ return linux_find_memory_regions_full (gdbarch, |
+ linux_find_memory_regions_thunk, |
+ &data); |
+} |
+ |
/* Determine which signal stopped execution. */ |
static int |
@@ -631,7 +927,7 @@ linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size) |
"proxydma_info", |
}; |
- enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch); |
+ enum bfd_endian byte_order = gdbarch_byte_order (target_gdbarch ()); |
gdb_byte *spu_ids; |
LONGEST i, j, size; |
@@ -676,6 +972,112 @@ linux_spu_make_corefile_notes (bfd *obfd, char *note_data, int *note_size) |
return note_data; |
} |
+/* This is used to pass information from |
+ linux_make_mappings_corefile_notes through |
+ linux_find_memory_regions_full. */ |
+ |
+struct linux_make_mappings_data |
+{ |
+ /* Number of files mapped. */ |
+ ULONGEST file_count; |
+ |
+ /* The obstack for the main part of the data. */ |
+ struct obstack *data_obstack; |
+ |
+ /* The filename obstack. */ |
+ struct obstack *filename_obstack; |
+ |
+ /* The architecture's "long" type. */ |
+ struct type *long_type; |
+}; |
+ |
+static linux_find_memory_region_ftype linux_make_mappings_callback; |
+ |
+/* A callback for linux_find_memory_regions_full that updates the |
+ mappings data for linux_make_mappings_corefile_notes. */ |
+ |
+static int |
+linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size, |
+ ULONGEST offset, ULONGEST inode, |
+ int read, int write, int exec, int modified, |
+ const char *filename, void *data) |
+{ |
+ struct linux_make_mappings_data *map_data = data; |
+ gdb_byte buf[sizeof (ULONGEST)]; |
+ |
+ if (*filename == '\0' || inode == 0) |
+ return 0; |
+ |
+ ++map_data->file_count; |
+ |
+ pack_long (buf, map_data->long_type, vaddr); |
+ obstack_grow (map_data->data_obstack, buf, TYPE_LENGTH (map_data->long_type)); |
+ pack_long (buf, map_data->long_type, vaddr + size); |
+ obstack_grow (map_data->data_obstack, buf, TYPE_LENGTH (map_data->long_type)); |
+ pack_long (buf, map_data->long_type, offset); |
+ obstack_grow (map_data->data_obstack, buf, TYPE_LENGTH (map_data->long_type)); |
+ |
+ obstack_grow_str0 (map_data->filename_obstack, filename); |
+ |
+ return 0; |
+} |
+ |
+/* Write the file mapping data to the core file, if possible. OBFD is |
+ the output BFD. NOTE_DATA is the current note data, and NOTE_SIZE |
+ is a pointer to the note size. Returns the new NOTE_DATA and |
+ updates NOTE_SIZE. */ |
+ |
+static char * |
+linux_make_mappings_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, |
+ char *note_data, int *note_size) |
+{ |
+ struct cleanup *cleanup; |
+ struct obstack data_obstack, filename_obstack; |
+ struct linux_make_mappings_data mapping_data; |
+ struct type *long_type |
+ = arch_integer_type (gdbarch, gdbarch_long_bit (gdbarch), 0, "long"); |
+ gdb_byte buf[sizeof (ULONGEST)]; |
+ |
+ obstack_init (&data_obstack); |
+ cleanup = make_cleanup_obstack_free (&data_obstack); |
+ obstack_init (&filename_obstack); |
+ make_cleanup_obstack_free (&filename_obstack); |
+ |
+ mapping_data.file_count = 0; |
+ mapping_data.data_obstack = &data_obstack; |
+ mapping_data.filename_obstack = &filename_obstack; |
+ mapping_data.long_type = long_type; |
+ |
+ /* Reserve space for the count. */ |
+ obstack_blank (&data_obstack, TYPE_LENGTH (long_type)); |
+ /* We always write the page size as 1 since we have no good way to |
+ determine the correct value. */ |
+ pack_long (buf, long_type, 1); |
+ obstack_grow (&data_obstack, buf, TYPE_LENGTH (long_type)); |
+ |
+ linux_find_memory_regions_full (gdbarch, linux_make_mappings_callback, |
+ &mapping_data); |
+ |
+ if (mapping_data.file_count != 0) |
+ { |
+ /* Write the count to the obstack. */ |
+ pack_long ((gdb_byte *) obstack_base (&data_obstack), |
+ long_type, mapping_data.file_count); |
+ |
+ /* Copy the filenames to the data obstack. */ |
+ obstack_grow (&data_obstack, obstack_base (&filename_obstack), |
+ obstack_object_size (&filename_obstack)); |
+ |
+ note_data = elfcore_write_note (obfd, note_data, note_size, |
+ "CORE", NT_FILE, |
+ obstack_base (&data_obstack), |
+ obstack_object_size (&data_obstack)); |
+ } |
+ |
+ do_cleanups (cleanup); |
+ return note_data; |
+} |
+ |
/* Records the thread's register state for the corefile note |
section. */ |
@@ -729,6 +1131,44 @@ linux_collect_thread_registers (const struct regcache *regcache, |
return note_data; |
} |
+/* Fetch the siginfo data for the current thread, if it exists. If |
+ there is no data, or we could not read it, return NULL. Otherwise, |
+ return a newly malloc'd buffer holding the data and fill in *SIZE |
+ with the size of the data. The caller is responsible for freeing |
+ the data. */ |
+ |
+static gdb_byte * |
+linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size) |
+{ |
+ struct type *siginfo_type; |
+ gdb_byte *buf; |
+ LONGEST bytes_read; |
+ struct cleanup *cleanups; |
+ |
+ if (!gdbarch_get_siginfo_type_p (gdbarch)) |
+ return NULL; |
+ |
+ siginfo_type = gdbarch_get_siginfo_type (gdbarch); |
+ |
+ buf = xmalloc (TYPE_LENGTH (siginfo_type)); |
+ cleanups = make_cleanup (xfree, buf); |
+ |
+ bytes_read = target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL, |
+ buf, 0, TYPE_LENGTH (siginfo_type)); |
+ if (bytes_read == TYPE_LENGTH (siginfo_type)) |
+ { |
+ discard_cleanups (cleanups); |
+ *size = bytes_read; |
+ } |
+ else |
+ { |
+ do_cleanups (cleanups); |
+ buf = NULL; |
+ } |
+ |
+ return buf; |
+} |
+ |
struct linux_corefile_thread_data |
{ |
struct gdbarch *gdbarch; |
@@ -736,7 +1176,6 @@ struct linux_corefile_thread_data |
bfd *obfd; |
char *note_data; |
int *note_size; |
- int num_notes; |
enum gdb_signal stop_signal; |
linux_collect_thread_registers_ftype collect; |
}; |
@@ -753,22 +1192,239 @@ linux_corefile_thread_callback (struct thread_info *info, void *data) |
{ |
struct cleanup *old_chain; |
struct regcache *regcache; |
+ gdb_byte *siginfo_data; |
+ LONGEST siginfo_size = 0; |
+ |
regcache = get_thread_arch_regcache (info->ptid, args->gdbarch); |
old_chain = save_inferior_ptid (); |
inferior_ptid = info->ptid; |
target_fetch_registers (regcache, -1); |
+ siginfo_data = linux_get_siginfo_data (args->gdbarch, &siginfo_size); |
do_cleanups (old_chain); |
+ old_chain = make_cleanup (xfree, siginfo_data); |
+ |
args->note_data = args->collect (regcache, info->ptid, args->obfd, |
args->note_data, args->note_size, |
args->stop_signal); |
- args->num_notes++; |
+ |
+ /* Don't return anything if we got no register information above, |
+ such a core file is useless. */ |
+ if (args->note_data != NULL) |
+ if (siginfo_data != NULL) |
+ args->note_data = elfcore_write_note (args->obfd, |
+ args->note_data, |
+ args->note_size, |
+ "CORE", NT_SIGINFO, |
+ siginfo_data, siginfo_size); |
+ |
+ do_cleanups (old_chain); |
} |
return !args->note_data; |
} |
+/* Fill the PRPSINFO structure with information about the process being |
+ debugged. Returns 1 in case of success, 0 for failures. Please note that |
+ even if the structure cannot be entirely filled (e.g., GDB was unable to |
+ gather information about the process UID/GID), this function will still |
+ return 1 since some information was already recorded. It will only return |
+ 0 iff nothing can be gathered. */ |
+ |
+static int |
+linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p) |
+{ |
+ /* The filename which we will use to obtain some info about the process. |
+ We will basically use this to store the `/proc/PID/FILENAME' file. */ |
+ char filename[100]; |
+ /* The full name of the program which generated the corefile. */ |
+ char *fname; |
+ /* The basename of the executable. */ |
+ const char *basename; |
+ /* The arguments of the program. */ |
+ char *psargs; |
+ char *infargs; |
+ /* The contents of `/proc/PID/stat' and `/proc/PID/status' files. */ |
+ char *proc_stat, *proc_status; |
+ /* Temporary buffer. */ |
+ char *tmpstr; |
+ /* The valid states of a process, according to the Linux kernel. */ |
+ const char valid_states[] = "RSDTZW"; |
+ /* The program state. */ |
+ const char *prog_state; |
+ /* The state of the process. */ |
+ char pr_sname; |
+ /* The PID of the program which generated the corefile. */ |
+ pid_t pid; |
+ /* Process flags. */ |
+ unsigned int pr_flag; |
+ /* Process nice value. */ |
+ long pr_nice; |
+ /* The number of fields read by `sscanf'. */ |
+ int n_fields = 0; |
+ /* Cleanups. */ |
+ struct cleanup *c; |
+ int i; |
+ |
+ gdb_assert (p != NULL); |
+ |
+ /* Obtaining PID and filename. */ |
+ pid = ptid_get_pid (inferior_ptid); |
+ xsnprintf (filename, sizeof (filename), "/proc/%d/cmdline", (int) pid); |
+ fname = target_fileio_read_stralloc (filename); |
+ |
+ if (fname == NULL || *fname == '\0') |
+ { |
+ /* No program name was read, so we won't be able to retrieve more |
+ information about the process. */ |
+ xfree (fname); |
+ return 0; |
+ } |
+ |
+ c = make_cleanup (xfree, fname); |
+ memset (p, 0, sizeof (*p)); |
+ |
+ /* Defining the PID. */ |
+ p->pr_pid = pid; |
+ |
+ /* Copying the program name. Only the basename matters. */ |
+ basename = lbasename (fname); |
+ strncpy (p->pr_fname, basename, sizeof (p->pr_fname)); |
+ p->pr_fname[sizeof (p->pr_fname) - 1] = '\0'; |
+ |
+ infargs = get_inferior_args (); |
+ |
+ psargs = xstrdup (fname); |
+ if (infargs != NULL) |
+ psargs = reconcat (psargs, psargs, " ", infargs, NULL); |
+ |
+ make_cleanup (xfree, psargs); |
+ |
+ strncpy (p->pr_psargs, psargs, sizeof (p->pr_psargs)); |
+ p->pr_psargs[sizeof (p->pr_psargs) - 1] = '\0'; |
+ |
+ xsnprintf (filename, sizeof (filename), "/proc/%d/stat", (int) pid); |
+ proc_stat = target_fileio_read_stralloc (filename); |
+ make_cleanup (xfree, proc_stat); |
+ |
+ if (proc_stat == NULL || *proc_stat == '\0') |
+ { |
+ /* Despite being unable to read more information about the |
+ process, we return 1 here because at least we have its |
+ command line, PID and arguments. */ |
+ do_cleanups (c); |
+ return 1; |
+ } |
+ |
+ /* Ok, we have the stats. It's time to do a little parsing of the |
+ contents of the buffer, so that we end up reading what we want. |
+ |
+ The following parsing mechanism is strongly based on the |
+ information generated by the `fs/proc/array.c' file, present in |
+ the Linux kernel tree. More details about how the information is |
+ displayed can be obtained by seeing the manpage of proc(5), |
+ specifically under the entry of `/proc/[pid]/stat'. */ |
+ |
+ /* Getting rid of the PID, since we already have it. */ |
+ while (isdigit (*proc_stat)) |
+ ++proc_stat; |
+ |
+ proc_stat = skip_spaces (proc_stat); |
+ |
+ /* Getting rid of the executable name, since we already have it. We |
+ know that this name will be in parentheses, so we can safely look |
+ for the close-paren. */ |
+ while (*proc_stat != ')') |
+ ++proc_stat; |
+ ++proc_stat; |
+ |
+ proc_stat = skip_spaces (proc_stat); |
+ |
+ n_fields = sscanf (proc_stat, |
+ "%c" /* Process state. */ |
+ "%d%d%d" /* Parent PID, group ID, session ID. */ |
+ "%*d%*d" /* tty_nr, tpgid (not used). */ |
+ "%u" /* Flags. */ |
+ "%*s%*s%*s%*s" /* minflt, cminflt, majflt, |
+ cmajflt (not used). */ |
+ "%*s%*s%*s%*s" /* utime, stime, cutime, |
+ cstime (not used). */ |
+ "%*s" /* Priority (not used). */ |
+ "%ld", /* Nice. */ |
+ &pr_sname, |
+ &p->pr_ppid, &p->pr_pgrp, &p->pr_sid, |
+ &pr_flag, |
+ &pr_nice); |
+ |
+ if (n_fields != 6) |
+ { |
+ /* Again, we couldn't read the complementary information about |
+ the process state. However, we already have minimal |
+ information, so we just return 1 here. */ |
+ do_cleanups (c); |
+ return 1; |
+ } |
+ |
+ /* Filling the structure fields. */ |
+ prog_state = strchr (valid_states, pr_sname); |
+ if (prog_state != NULL) |
+ p->pr_state = prog_state - valid_states; |
+ else |
+ { |
+ /* Zero means "Running". */ |
+ p->pr_state = 0; |
+ } |
+ |
+ p->pr_sname = p->pr_state > 5 ? '.' : pr_sname; |
+ p->pr_zomb = p->pr_sname == 'Z'; |
+ p->pr_nice = pr_nice; |
+ p->pr_flag = pr_flag; |
+ |
+ /* Finally, obtaining the UID and GID. For that, we read and parse the |
+ contents of the `/proc/PID/status' file. */ |
+ xsnprintf (filename, sizeof (filename), "/proc/%d/status", (int) pid); |
+ proc_status = target_fileio_read_stralloc (filename); |
+ make_cleanup (xfree, proc_status); |
+ |
+ if (proc_status == NULL || *proc_status == '\0') |
+ { |
+ /* Returning 1 since we already have a bunch of information. */ |
+ do_cleanups (c); |
+ return 1; |
+ } |
+ |
+ /* Extracting the UID. */ |
+ tmpstr = strstr (proc_status, "Uid:"); |
+ if (tmpstr != NULL) |
+ { |
+ /* Advancing the pointer to the beginning of the UID. */ |
+ tmpstr += sizeof ("Uid:"); |
+ while (*tmpstr != '\0' && !isdigit (*tmpstr)) |
+ ++tmpstr; |
+ |
+ if (isdigit (*tmpstr)) |
+ p->pr_uid = strtol (tmpstr, &tmpstr, 10); |
+ } |
+ |
+ /* Extracting the GID. */ |
+ tmpstr = strstr (proc_status, "Gid:"); |
+ if (tmpstr != NULL) |
+ { |
+ /* Advancing the pointer to the beginning of the GID. */ |
+ tmpstr += sizeof ("Gid:"); |
+ while (*tmpstr != '\0' && !isdigit (*tmpstr)) |
+ ++tmpstr; |
+ |
+ if (isdigit (*tmpstr)) |
+ p->pr_gid = strtol (tmpstr, &tmpstr, 10); |
+ } |
+ |
+ do_cleanups (c); |
+ |
+ return 1; |
+} |
+ |
/* Fills the "to_make_corefile_note" target vector. Builds the note |
section for a corefile, and returns it in a malloc buffer. */ |
@@ -777,26 +1433,30 @@ 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; |
+ struct elf_internal_linux_prpsinfo prpsinfo; |
char *note_data = NULL; |
gdb_byte *auxv; |
int auxv_len; |
- /* Process information. */ |
- if (get_exec_file (0)) |
+ if (linux_fill_prpsinfo (&prpsinfo)) |
{ |
- 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; |
+ if (gdbarch_elfcore_write_linux_prpsinfo_p (gdbarch)) |
+ { |
+ note_data = gdbarch_elfcore_write_linux_prpsinfo (gdbarch, obfd, |
+ note_data, note_size, |
+ &prpsinfo); |
+ } |
+ else |
+ { |
+ if (gdbarch_ptr_bit (gdbarch) == 64) |
+ note_data = elfcore_write_linux_prpsinfo64 (obfd, |
+ note_data, note_size, |
+ &prpsinfo); |
+ else |
+ note_data = elfcore_write_linux_prpsinfo32 (obfd, |
+ note_data, note_size, |
+ &prpsinfo); |
+ } |
} |
/* Thread register information. */ |
@@ -805,7 +1465,6 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size, |
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); |
@@ -831,6 +1490,10 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size, |
if (!note_data) |
return NULL; |
+ /* File mappings. */ |
+ note_data = linux_make_mappings_corefile_notes (gdbarch, obfd, |
+ note_data, note_size); |
+ |
make_cleanup (xfree, note_data); |
return note_data; |
} |
@@ -849,6 +1512,257 @@ linux_make_corefile_notes_1 (struct gdbarch *gdbarch, bfd *obfd, int *note_size) |
linux_collect_thread_registers); |
} |
+/* Implementation of `gdbarch_gdb_signal_from_target', as defined in |
+ gdbarch.h. This function is not static because it is exported to |
+ other -tdep files. */ |
+ |
+enum gdb_signal |
+linux_gdb_signal_from_target (struct gdbarch *gdbarch, int signal) |
+{ |
+ switch (signal) |
+ { |
+ case 0: |
+ return GDB_SIGNAL_0; |
+ |
+ case LINUX_SIGHUP: |
+ return GDB_SIGNAL_HUP; |
+ |
+ case LINUX_SIGINT: |
+ return GDB_SIGNAL_INT; |
+ |
+ case LINUX_SIGQUIT: |
+ return GDB_SIGNAL_QUIT; |
+ |
+ case LINUX_SIGILL: |
+ return GDB_SIGNAL_ILL; |
+ |
+ case LINUX_SIGTRAP: |
+ return GDB_SIGNAL_TRAP; |
+ |
+ case LINUX_SIGABRT: |
+ return GDB_SIGNAL_ABRT; |
+ |
+ case LINUX_SIGBUS: |
+ return GDB_SIGNAL_BUS; |
+ |
+ case LINUX_SIGFPE: |
+ return GDB_SIGNAL_FPE; |
+ |
+ case LINUX_SIGKILL: |
+ return GDB_SIGNAL_KILL; |
+ |
+ case LINUX_SIGUSR1: |
+ return GDB_SIGNAL_USR1; |
+ |
+ case LINUX_SIGSEGV: |
+ return GDB_SIGNAL_SEGV; |
+ |
+ case LINUX_SIGUSR2: |
+ return GDB_SIGNAL_USR2; |
+ |
+ case LINUX_SIGPIPE: |
+ return GDB_SIGNAL_PIPE; |
+ |
+ case LINUX_SIGALRM: |
+ return GDB_SIGNAL_ALRM; |
+ |
+ case LINUX_SIGTERM: |
+ return GDB_SIGNAL_TERM; |
+ |
+ case LINUX_SIGCHLD: |
+ return GDB_SIGNAL_CHLD; |
+ |
+ case LINUX_SIGCONT: |
+ return GDB_SIGNAL_CONT; |
+ |
+ case LINUX_SIGSTOP: |
+ return GDB_SIGNAL_STOP; |
+ |
+ case LINUX_SIGTSTP: |
+ return GDB_SIGNAL_TSTP; |
+ |
+ case LINUX_SIGTTIN: |
+ return GDB_SIGNAL_TTIN; |
+ |
+ case LINUX_SIGTTOU: |
+ return GDB_SIGNAL_TTOU; |
+ |
+ case LINUX_SIGURG: |
+ return GDB_SIGNAL_URG; |
+ |
+ case LINUX_SIGXCPU: |
+ return GDB_SIGNAL_XCPU; |
+ |
+ case LINUX_SIGXFSZ: |
+ return GDB_SIGNAL_XFSZ; |
+ |
+ case LINUX_SIGVTALRM: |
+ return GDB_SIGNAL_VTALRM; |
+ |
+ case LINUX_SIGPROF: |
+ return GDB_SIGNAL_PROF; |
+ |
+ case LINUX_SIGWINCH: |
+ return GDB_SIGNAL_WINCH; |
+ |
+ /* No way to differentiate between SIGIO and SIGPOLL. |
+ Therefore, we just handle the first one. */ |
+ case LINUX_SIGIO: |
+ return GDB_SIGNAL_IO; |
+ |
+ case LINUX_SIGPWR: |
+ return GDB_SIGNAL_PWR; |
+ |
+ case LINUX_SIGSYS: |
+ return GDB_SIGNAL_SYS; |
+ |
+ /* SIGRTMIN and SIGRTMAX are not continuous in <gdb/signals.def>, |
+ therefore we have to handle them here. */ |
+ case LINUX_SIGRTMIN: |
+ return GDB_SIGNAL_REALTIME_32; |
+ |
+ case LINUX_SIGRTMAX: |
+ return GDB_SIGNAL_REALTIME_64; |
+ } |
+ |
+ if (signal >= LINUX_SIGRTMIN + 1 && signal <= LINUX_SIGRTMAX - 1) |
+ { |
+ int offset = signal - LINUX_SIGRTMIN + 1; |
+ |
+ return (enum gdb_signal) ((int) GDB_SIGNAL_REALTIME_33 + offset); |
+ } |
+ |
+ return GDB_SIGNAL_UNKNOWN; |
+} |
+ |
+/* Implementation of `gdbarch_gdb_signal_to_target', as defined in |
+ gdbarch.h. This function is not static because it is exported to |
+ other -tdep files. */ |
+ |
+int |
+linux_gdb_signal_to_target (struct gdbarch *gdbarch, |
+ enum gdb_signal signal) |
+{ |
+ switch (signal) |
+ { |
+ case GDB_SIGNAL_0: |
+ return 0; |
+ |
+ case GDB_SIGNAL_HUP: |
+ return LINUX_SIGHUP; |
+ |
+ case GDB_SIGNAL_INT: |
+ return LINUX_SIGINT; |
+ |
+ case GDB_SIGNAL_QUIT: |
+ return LINUX_SIGQUIT; |
+ |
+ case GDB_SIGNAL_ILL: |
+ return LINUX_SIGILL; |
+ |
+ case GDB_SIGNAL_TRAP: |
+ return LINUX_SIGTRAP; |
+ |
+ case GDB_SIGNAL_ABRT: |
+ return LINUX_SIGABRT; |
+ |
+ case GDB_SIGNAL_FPE: |
+ return LINUX_SIGFPE; |
+ |
+ case GDB_SIGNAL_KILL: |
+ return LINUX_SIGKILL; |
+ |
+ case GDB_SIGNAL_BUS: |
+ return LINUX_SIGBUS; |
+ |
+ case GDB_SIGNAL_SEGV: |
+ return LINUX_SIGSEGV; |
+ |
+ case GDB_SIGNAL_SYS: |
+ return LINUX_SIGSYS; |
+ |
+ case GDB_SIGNAL_PIPE: |
+ return LINUX_SIGPIPE; |
+ |
+ case GDB_SIGNAL_ALRM: |
+ return LINUX_SIGALRM; |
+ |
+ case GDB_SIGNAL_TERM: |
+ return LINUX_SIGTERM; |
+ |
+ case GDB_SIGNAL_URG: |
+ return LINUX_SIGURG; |
+ |
+ case GDB_SIGNAL_STOP: |
+ return LINUX_SIGSTOP; |
+ |
+ case GDB_SIGNAL_TSTP: |
+ return LINUX_SIGTSTP; |
+ |
+ case GDB_SIGNAL_CONT: |
+ return LINUX_SIGCONT; |
+ |
+ case GDB_SIGNAL_CHLD: |
+ return LINUX_SIGCHLD; |
+ |
+ case GDB_SIGNAL_TTIN: |
+ return LINUX_SIGTTIN; |
+ |
+ case GDB_SIGNAL_TTOU: |
+ return LINUX_SIGTTOU; |
+ |
+ case GDB_SIGNAL_IO: |
+ return LINUX_SIGIO; |
+ |
+ case GDB_SIGNAL_XCPU: |
+ return LINUX_SIGXCPU; |
+ |
+ case GDB_SIGNAL_XFSZ: |
+ return LINUX_SIGXFSZ; |
+ |
+ case GDB_SIGNAL_VTALRM: |
+ return LINUX_SIGVTALRM; |
+ |
+ case GDB_SIGNAL_PROF: |
+ return LINUX_SIGPROF; |
+ |
+ case GDB_SIGNAL_WINCH: |
+ return LINUX_SIGWINCH; |
+ |
+ case GDB_SIGNAL_USR1: |
+ return LINUX_SIGUSR1; |
+ |
+ case GDB_SIGNAL_USR2: |
+ return LINUX_SIGUSR2; |
+ |
+ case GDB_SIGNAL_PWR: |
+ return LINUX_SIGPWR; |
+ |
+ case GDB_SIGNAL_POLL: |
+ return LINUX_SIGPOLL; |
+ |
+ /* GDB_SIGNAL_REALTIME_32 is not continuous in <gdb/signals.def>, |
+ therefore we have to handle it here. */ |
+ case GDB_SIGNAL_REALTIME_32: |
+ return LINUX_SIGRTMIN; |
+ |
+ /* Same comment applies to _64. */ |
+ case GDB_SIGNAL_REALTIME_64: |
+ return LINUX_SIGRTMAX; |
+ } |
+ |
+ /* GDB_SIGNAL_REALTIME_33 to _64 are continuous. */ |
+ if (signal >= GDB_SIGNAL_REALTIME_33 |
+ && signal <= GDB_SIGNAL_REALTIME_63) |
+ { |
+ int offset = signal - GDB_SIGNAL_REALTIME_33; |
+ |
+ return LINUX_SIGRTMIN + 1 + offset; |
+ } |
+ |
+ return -1; |
+} |
+ |
/* To be called from the various GDB_OSABI_LINUX handlers for the |
various GNU/Linux architectures and machine types. */ |
@@ -857,10 +1771,15 @@ 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_core_info_proc (gdbarch, linux_core_info_proc); |
set_gdbarch_find_memory_regions (gdbarch, linux_find_memory_regions); |
set_gdbarch_make_corefile_notes (gdbarch, linux_make_corefile_notes_1); |
set_gdbarch_has_shared_address_space (gdbarch, |
linux_has_shared_address_space); |
+ set_gdbarch_gdb_signal_from_target (gdbarch, |
+ linux_gdb_signal_from_target); |
+ set_gdbarch_gdb_signal_to_target (gdbarch, |
+ linux_gdb_signal_to_target); |
} |
/* Provide a prototype to silence -Wmissing-prototypes. */ |