X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Flinux-tdep.c;h=cfbec40b0e1c9fe7fae99b2f3bd291603e2aee95;hb=190b495d472576db66a8727d5872fcca3d5519c6;hp=0b11e58ca1c048a7465ba699f4ebfa2ba93d9511;hpb=d249a14abe5c2ee3ba4dc6c47e68e41ddc2025a4;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/linux-tdep.c b/gdb/linux-tdep.c index 0b11e58ca1..cfbec40b0e 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-2015 Free Software Foundation, Inc. + Copyright (C) 2009-2016 Free Software Foundation, Inc. This file is part of GDB. @@ -37,6 +37,7 @@ #include "infcall.h" #include "gdbcmd.h" #include "gdb_regex.h" +#include "common/enum-flags.h" #include @@ -46,7 +47,7 @@ Documentation/filesystems/proc.txt, inside the Linux kernel tree. */ -enum +enum filter_flag { COREFILTER_ANON_PRIVATE = 1 << 0, COREFILTER_ANON_SHARED = 1 << 1, @@ -56,6 +57,7 @@ enum COREFILTER_HUGETLB_PRIVATE = 1 << 5, COREFILTER_HUGETLB_SHARED = 1 << 6, }; +DEF_ENUM_FLAGS_TYPE (enum filter_flag, filter_flags); /* This struct is used to map flags found in the "VmFlags:" field (in the /proc//smaps file). */ @@ -171,7 +173,8 @@ init_linux_gdbarch_data (struct gdbarch *gdbarch) static struct linux_gdbarch_data * get_linux_gdbarch_data (struct gdbarch *gdbarch) { - return gdbarch_data (gdbarch, linux_gdbarch_data_handle); + return ((struct linux_gdbarch_data *) + gdbarch_data (gdbarch, linux_gdbarch_data_handle)); } /* Per-inferior data key. */ @@ -203,7 +206,7 @@ invalidate_linux_cache_inf (struct inferior *inf) { struct linux_info *info; - info = inferior_data (inf, linux_inferior_data); + info = (struct linux_info *) inferior_data (inf, linux_inferior_data); if (info != NULL) { xfree (info); @@ -230,7 +233,7 @@ get_linux_inferior_data (void) struct linux_info *info; struct inferior *inf = current_inferior (); - info = inferior_data (inf, linux_inferior_data); + info = (struct linux_info *) inferior_data (inf, linux_inferior_data); if (info == NULL) { info = XCNEW (struct linux_info); @@ -240,11 +243,11 @@ get_linux_inferior_data (void) return info; } -/* This function is suitable for architectures that don't - extend/override the standard siginfo structure. */ +/* See linux-tdep.h. */ struct type * -linux_get_siginfo_type (struct gdbarch *gdbarch) +linux_get_siginfo_type_with_fields (struct gdbarch *gdbarch, + linux_siginfo_extra_fields extra_fields) { struct linux_gdbarch_data *linux_gdbarch_data; struct type *int_type, *uint_type, *long_type, *void_ptr_type; @@ -361,6 +364,15 @@ linux_get_siginfo_type (struct gdbarch *gdbarch) return siginfo_type; } +/* This function is suitable for architectures that don't + extend/override the standard siginfo structure. */ + +static struct type * +linux_get_siginfo_type (struct gdbarch *gdbarch) +{ + return linux_get_siginfo_type_with_fields (gdbarch, 0); +} + /* Return true if the target is running on uClinux instead of normal Linux kernel. */ @@ -598,7 +610,7 @@ mapping_is_anonymous_p (const char *filename) This should work OK enough, however. */ static int -dump_mapping_p (unsigned int filterflags, const struct smaps_vmflags *v, +dump_mapping_p (filter_flags filterflags, const struct smaps_vmflags *v, int maybe_private_p, int mapping_anon_p, int mapping_file_p, const char *filename) { @@ -719,7 +731,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, if (cmdline_f) { xsnprintf (filename, sizeof filename, "/proc/%ld/cmdline", pid); - data = target_fileio_read_stralloc (filename); + data = target_fileio_read_stralloc (NULL, filename); if (data) { struct cleanup *cleanup = make_cleanup (xfree, data); @@ -732,7 +744,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, if (cwd_f) { xsnprintf (filename, sizeof filename, "/proc/%ld/cwd", pid); - data = target_fileio_readlink (filename, &target_errno); + data = target_fileio_readlink (NULL, filename, &target_errno); if (data) { struct cleanup *cleanup = make_cleanup (xfree, data); @@ -745,7 +757,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, if (exe_f) { xsnprintf (filename, sizeof filename, "/proc/%ld/exe", pid); - data = target_fileio_readlink (filename, &target_errno); + data = target_fileio_readlink (NULL, filename, &target_errno); if (data) { struct cleanup *cleanup = make_cleanup (xfree, data); @@ -758,7 +770,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, if (mappings_f) { xsnprintf (filename, sizeof filename, "/proc/%ld/maps", pid); - data = target_fileio_read_stralloc (filename); + data = target_fileio_read_stralloc (NULL, filename); if (data) { struct cleanup *cleanup = make_cleanup (xfree, data); @@ -819,7 +831,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, if (status_f) { xsnprintf (filename, sizeof filename, "/proc/%ld/status", pid); - data = target_fileio_read_stralloc (filename); + data = target_fileio_read_stralloc (NULL, filename); if (data) { struct cleanup *cleanup = make_cleanup (xfree, data); @@ -832,7 +844,7 @@ linux_info_proc (struct gdbarch *gdbarch, const char *args, if (stat_f) { xsnprintf (filename, sizeof filename, "/proc/%ld/stat", pid); - data = target_fileio_read_stralloc (filename); + data = target_fileio_read_stralloc (NULL, filename); if (data) { struct cleanup *cleanup = make_cleanup (xfree, data); @@ -997,7 +1009,7 @@ linux_core_info_proc_mappings (struct gdbarch *gdbarch, const char *args) if (note_size < 2 * addr_size) error (_("malformed core note - too short for header")); - contents = xmalloc (note_size); + contents = (unsigned char *) 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")); @@ -1119,7 +1131,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, /* Default dump behavior of coredump_filter (0x33), according to Documentation/filesystems/proc.txt from the Linux kernel tree. */ - unsigned int filterflags = (COREFILTER_ANON_PRIVATE + filter_flags filterflags = (COREFILTER_ANON_PRIVATE | COREFILTER_ANON_SHARED | COREFILTER_ELF_HEADERS | COREFILTER_HUGETLB_PRIVATE); @@ -1134,21 +1146,25 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, { xsnprintf (coredumpfilter_name, sizeof (coredumpfilter_name), "/proc/%d/coredump_filter", pid); - coredumpfilterdata = target_fileio_read_stralloc (coredumpfilter_name); + coredumpfilterdata = target_fileio_read_stralloc (NULL, + coredumpfilter_name); if (coredumpfilterdata != NULL) { - sscanf (coredumpfilterdata, "%x", &filterflags); + unsigned int flags; + + sscanf (coredumpfilterdata, "%x", &flags); + filterflags = (enum filter_flag) flags; xfree (coredumpfilterdata); } } xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/smaps", pid); - data = target_fileio_read_stralloc (mapsfilename); + data = target_fileio_read_stralloc (NULL, mapsfilename); if (data == NULL) { /* Older Linux kernels did not support /proc/PID/smaps. */ xsnprintf (mapsfilename, sizeof mapsfilename, "/proc/%d/maps", pid); - data = target_fileio_read_stralloc (mapsfilename); + data = target_fileio_read_stralloc (NULL, mapsfilename); } if (data != NULL) @@ -1163,7 +1179,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, const char *permissions, *device, *filename; struct smaps_vmflags v; size_t permissions_len, device_len; - int read, write, exec, private; + int read, write, exec, priv; int has_anonymous = 0; int should_dump_p = 0; int mapping_anon_p; @@ -1197,7 +1213,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, not have the VmFlags there. In this case, there is really no way to know if we are dealing with VM_SHARED, so we just assume that VM_MAYSHARE is enough. */ - private = memchr (permissions, 'p', permissions_len) != 0; + priv = memchr (permissions, 'p', permissions_len) != 0; /* Try to detect if region should be dumped by parsing smaps counters. */ @@ -1257,7 +1273,7 @@ linux_find_memory_regions_full (struct gdbarch *gdbarch, } if (has_anonymous) - should_dump_p = dump_mapping_p (filterflags, &v, private, + should_dump_p = dump_mapping_p (filterflags, &v, priv, mapping_anon_p, mapping_file_p, filename); else @@ -1305,7 +1321,8 @@ linux_find_memory_regions_thunk (ULONGEST vaddr, ULONGEST size, int read, int write, int exec, int modified, const char *filename, void *arg) { - struct linux_find_memory_regions_data *data = arg; + struct linux_find_memory_regions_data *data + = (struct linux_find_memory_regions_data *) arg; return data->func (vaddr, size, read, write, exec, modified, data->obfd); } @@ -1339,18 +1356,6 @@ find_signalled_thread (struct thread_info *info, void *data) return 0; } -static enum gdb_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 GDB_SIGNAL_0; -} - /* Generate corefile notes for SPU contexts. */ static char * @@ -1453,7 +1458,8 @@ linux_make_mappings_callback (ULONGEST vaddr, ULONGEST size, int read, int write, int exec, int modified, const char *filename, void *data) { - struct linux_make_mappings_data *map_data = data; + struct linux_make_mappings_data *map_data + = (struct linux_make_mappings_data *) data; gdb_byte buf[sizeof (ULONGEST)]; if (*filename == '\0' || inode == 0) @@ -1554,14 +1560,15 @@ linux_collect_regset_section_cb (const char *sect_name, int size, const char *human_name, void *cb_data) { char *buf; - struct linux_collect_regset_section_cb_data *data = cb_data; + struct linux_collect_regset_section_cb_data *data + = (struct linux_collect_regset_section_cb_data *) cb_data; if (data->abort_iteration) return; gdb_assert (regset && regset->collect_regset); - buf = xmalloc (size); + buf = (char *) xmalloc (size); regset->collect_regset (regset, data->regcache, -1, buf, size); /* PRSTATUS still needs to be treated specially. */ @@ -1629,7 +1636,7 @@ linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size) siginfo_type = gdbarch_get_siginfo_type (gdbarch); - buf = xmalloc (TYPE_LENGTH (siginfo_type)); + buf = (gdb_byte *) xmalloc (TYPE_LENGTH (siginfo_type)); cleanups = make_cleanup (xfree, buf); bytes_read = target_read (¤t_target, TARGET_OBJECT_SIGNAL_INFO, NULL, @@ -1651,61 +1658,49 @@ linux_get_siginfo_data (struct gdbarch *gdbarch, LONGEST *size) struct linux_corefile_thread_data { struct gdbarch *gdbarch; - int pid; bfd *obfd; char *note_data; int *note_size; enum gdb_signal stop_signal; }; -/* Called by gdbthread.c once per thread. Records the thread's - register state for the corefile note section. */ +/* Records the thread's register state for the corefile note + section. */ -static int -linux_corefile_thread_callback (struct thread_info *info, void *data) +static void +linux_corefile_thread (struct thread_info *info, + struct linux_corefile_thread_data *args) { - struct linux_corefile_thread_data *args = data; - - /* It can be current thread - which cannot be removed by update_thread_list. */ - if (info->state == THREAD_EXITED) - return 0; - - if (ptid_get_pid (info->ptid) == args->pid) - { - 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 = linux_collect_thread_registers - (regcache, info->ptid, args->obfd, args->note_data, - args->note_size, args->stop_signal); - - /* 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; + 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 = linux_collect_thread_registers + (regcache, info->ptid, args->obfd, args->note_data, + args->note_size, args->stop_signal); + + /* 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); } /* Fill the PRPSINFO structure with information about the process being @@ -1755,7 +1750,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p) /* 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); + fname = target_fileio_read_stralloc (NULL, filename); if (fname == NULL || *fname == '\0') { @@ -1788,7 +1783,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p) 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); + proc_stat = target_fileio_read_stralloc (NULL, filename); make_cleanup (xfree, proc_stat); if (proc_stat == NULL || *proc_stat == '\0') @@ -1869,7 +1864,7 @@ linux_fill_prpsinfo (struct elf_internal_linux_prpsinfo *p) /* 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); + proc_status = target_fileio_read_stralloc (NULL, filename); make_cleanup (xfree, proc_status); if (proc_status == NULL || *proc_status == '\0') @@ -1921,6 +1916,7 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size) char *note_data = NULL; gdb_byte *auxv; int auxv_len; + struct thread_info *curr_thr, *signalled_thr, *thr; if (! gdbarch_iterate_over_regset_sections_p (gdbarch)) return NULL; @@ -1957,13 +1953,37 @@ linux_make_corefile_notes (struct gdbarch *gdbarch, bfd *obfd, int *note_size) } END_CATCH + /* Like the kernel, prefer dumping the signalled thread first. + "First thread" is what tools use to infer the signalled thread. + In case there's more than one signalled thread, prefer the + current thread, if it is signalled. */ + curr_thr = inferior_thread (); + if (curr_thr->suspend.stop_signal != GDB_SIGNAL_0) + signalled_thr = curr_thr; + else + { + signalled_thr = iterate_over_threads (find_signalled_thread, NULL); + if (signalled_thr == NULL) + signalled_thr = curr_thr; + } + 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.stop_signal = find_stop_signal (); - iterate_over_threads (linux_corefile_thread_callback, &thread_args); + thread_args.stop_signal = signalled_thr->suspend.stop_signal; + + linux_corefile_thread (signalled_thr, &thread_args); + ALL_NON_EXITED_THREADS (thr) + { + if (thr == signalled_thr) + continue; + if (ptid_get_pid (thr->ptid) != ptid_get_pid (inferior_ptid)) + continue; + + linux_corefile_thread (thr, &thread_args); + } + note_data = thread_args.note_data; if (!note_data) return NULL; @@ -2251,7 +2271,7 @@ find_mapping_size (CORE_ADDR vaddr, unsigned long size, int read, int write, int exec, int modified, void *data) { - struct mem_range *range = data; + struct mem_range *range = (struct mem_range *) data; if (vaddr == range->start) { @@ -2348,6 +2368,66 @@ linux_infcall_mmap (CORE_ADDR size, unsigned prot) return retval; } +/* See gdbarch.sh 'infcall_munmap'. */ + +static void +linux_infcall_munmap (CORE_ADDR addr, CORE_ADDR size) +{ + struct objfile *objf; + struct value *munmap_val = find_function_in_inferior ("munmap", &objf); + struct value *retval_val; + struct gdbarch *gdbarch = get_objfile_arch (objf); + LONGEST retval; + enum + { + ARG_ADDR, ARG_LENGTH, ARG_LAST + }; + struct value *arg[ARG_LAST]; + + arg[ARG_ADDR] = value_from_pointer (builtin_type (gdbarch)->builtin_data_ptr, + addr); + /* Assuming sizeof (unsigned long) == sizeof (size_t). */ + arg[ARG_LENGTH] = value_from_ulongest + (builtin_type (gdbarch)->builtin_unsigned_long, size); + retval_val = call_function_by_hand (munmap_val, ARG_LAST, arg); + retval = value_as_long (retval_val); + if (retval != 0) + warning (_("Failed inferior munmap call at %s for %s bytes, " + "errno is changed."), + hex_string (addr), pulongest (size)); +} + +/* See linux-tdep.h. */ + +CORE_ADDR +linux_displaced_step_location (struct gdbarch *gdbarch) +{ + CORE_ADDR addr; + int bp_len; + + /* Determine entry point from target auxiliary vector. This avoids + the need for symbols. Also, when debugging a stand-alone SPU + executable, entry_point_address () will point to an SPU + local-store address and is thus not usable as displaced stepping + location. The auxiliary vector gets us the PowerPC-side entry + point address instead. */ + if (target_auxv_search (¤t_target, AT_ENTRY, &addr) <= 0) + error (_("Cannot find AT_ENTRY auxiliary vector entry.")); + + /* Make certain that the address points at real code, and not a + function descriptor. */ + addr = gdbarch_convert_from_func_ptr_addr (gdbarch, addr, + ¤t_target); + + /* Inferior calls also use the entry point as a breakpoint location. + We don't want displaced stepping to interfere with those + breakpoints, so leave space. */ + gdbarch_breakpoint_from_pc (gdbarch, &addr, &bp_len); + addr += bp_len * 2; + + return addr; +} + /* Display whether the gcore command is using the /proc/PID/coredump_filter file. */ @@ -2378,6 +2458,8 @@ linux_init_abi (struct gdbarch_info info, struct gdbarch *gdbarch) linux_gdb_signal_to_target); set_gdbarch_vsyscall_range (gdbarch, linux_vsyscall_range); set_gdbarch_infcall_mmap (gdbarch, linux_infcall_mmap); + set_gdbarch_infcall_munmap (gdbarch, linux_infcall_munmap); + set_gdbarch_get_siginfo_type (gdbarch, linux_get_siginfo_type); } /* Provide a prototype to silence -Wmissing-prototypes. */