X-Git-Url: http://drtracing.org/?a=blobdiff_plain;f=gdb%2Fgdbserver%2Flinux-mips-low.c;h=393488ac5ba97ab0c73f65a81951a5bc72136041;hb=96643e35c077ed36c74b231ceefa4c30b3b02e28;hp=70917fded36cf387a75c40fd9105635f0e4b0b29;hpb=ecd75fc8eed3bde86036141228074a20e55dcfc9;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/gdbserver/linux-mips-low.c b/gdb/gdbserver/linux-mips-low.c index 70917fded3..393488ac5b 100644 --- a/gdb/gdbserver/linux-mips-low.c +++ b/gdb/gdbserver/linux-mips-low.c @@ -1,5 +1,5 @@ /* GNU/Linux/MIPS specific low level interface, for the remote server for GDB. - Copyright (C) 1995-2014 Free Software Foundation, Inc. + Copyright (C) 1995-2018 Free Software Foundation, Inc. This file is part of GDB. @@ -19,10 +19,10 @@ #include "server.h" #include "linux-low.h" -#include +#include "nat/gdb_ptrace.h" #include -#include "mips-linux-watch.h" +#include "nat/mips-linux-watch.h" #include "gdb_proc_service.h" /* Defined in auto-generated file mips-linux.c. */ @@ -126,8 +126,9 @@ mips_read_description (void) { if (have_dsp < 0) { - int pid = lwpid_of (get_thread_lwp (current_inferior)); + int pid = lwpid_of (current_thread); + errno = 0; ptrace (PTRACE_PEEKUSER, pid, DSP_CONTROL, 0); switch (errno) { @@ -197,8 +198,8 @@ struct arch_lwp_info /* Pseudo registers can not be read. ptrace does not provide a way to read (or set) PS_REGNUM, and there's no point in reading or setting - ZERO_REGNUM. We also can not set BADVADDR, CAUSE, or FCRIR via - ptrace(). */ + ZERO_REGNUM, it's always 0. We also can not set BADVADDR, CAUSE, + or FCRIR via ptrace(). */ static int mips_cannot_fetch_register (int regno) @@ -210,6 +211,10 @@ mips_cannot_fetch_register (int regno) tdesc = current_process ()->tdesc; + /* On n32 we can't access 64-bit registers via PTRACE_PEEKUSR. */ + if (register_size (tdesc, regno) > sizeof (PTRACE_XFER_TYPE)) + return 1; + if (find_regno (tdesc, "r0") == regno) return 1; @@ -226,6 +231,10 @@ mips_cannot_store_register (int regno) tdesc = current_process ()->tdesc; + /* On n32 we can't access 64-bit registers via PTRACE_POKEUSR. */ + if (register_size (tdesc, regno) > sizeof (PTRACE_XFER_TYPE)) + return 1; + if (find_regno (tdesc, "r0") == regno) return 1; @@ -241,6 +250,20 @@ mips_cannot_store_register (int regno) return 0; } +static int +mips_fetch_register (struct regcache *regcache, int regno) +{ + const struct target_desc *tdesc = current_process ()->tdesc; + + if (find_regno (tdesc, "r0") == regno) + { + supply_register_zeroed (regcache, regno); + return 1; + } + + return 0; +} + static CORE_ADDR mips_get_pc (struct regcache *regcache) { @@ -265,16 +288,13 @@ mips_set_pc (struct regcache *regcache, CORE_ADDR pc) static const unsigned int mips_breakpoint = 0x0005000d; #define mips_breakpoint_len 4 -/* 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 -mips_reinsert_addr (void) +/* Implementation of linux_target_ops method "sw_breakpoint_from_kind". */ + +static const gdb_byte * +mips_sw_breakpoint_from_kind (int kind, int *size) { - struct regcache *regcache = get_thread_regcache (current_inferior, 1); - union mips_register ra; - collect_register_by_name (regcache, "r31", ra.buf); - return register_size (regcache->tdesc, 0) == 4 ? ra.reg32 : ra.reg64; + *size = mips_breakpoint_len; + return (const gdb_byte *) &mips_breakpoint; } static int @@ -291,30 +311,21 @@ mips_breakpoint_at (CORE_ADDR where) return 0; } -/* Mark the watch registers of lwp, represented by ENTRY, as changed, - if the lwp's process id is *PID_P. */ +/* Mark the watch registers of lwp, represented by ENTRY, as changed. */ -static int -update_watch_registers_callback (struct inferior_list_entry *entry, - void *pid_p) +static void +update_watch_registers_callback (thread_info *thread) { - struct lwp_info *lwp = (struct lwp_info *) entry; - int pid = *(int *) pid_p; + struct lwp_info *lwp = get_thread_lwp (thread); - /* Only update the threads of this process. */ - if (pid_of (lwp) == pid) - { - /* The actual update is done later just before resuming the lwp, - we just mark that the registers need updating. */ - lwp->arch_private->watch_registers_changed = 1; - - /* If the lwp isn't stopped, force it to momentarily pause, so - we can update its watch registers. */ - if (!lwp->stopped) - linux_stop_lwp (lwp); - } + /* The actual update is done later just before resuming the lwp, + we just mark that the registers need updating. */ + lwp->arch_private->watch_registers_changed = 1; - return 0; + /* If the lwp isn't stopped, force it to momentarily pause, so + we can update its watch registers. */ + if (!lwp->stopped) + linux_stop_lwp (lwp); } /* This is the implementation of linux_target_ops method @@ -323,25 +334,104 @@ update_watch_registers_callback (struct inferior_list_entry *entry, static struct arch_process_info * mips_linux_new_process (void) { - struct arch_process_info *info = xcalloc (1, sizeof (*info)); + struct arch_process_info *info = XCNEW (struct arch_process_info); return info; } +/* This is the implementation of linux_target_ops method + delete_process. */ + +static void +mips_linux_delete_process (struct arch_process_info *info) +{ + xfree (info); +} + /* This is the implementation of linux_target_ops method new_thread. Mark the watch registers as changed, so the threads' copies will be updated. */ -static struct arch_lwp_info * -mips_linux_new_thread (void) +static void +mips_linux_new_thread (struct lwp_info *lwp) { - struct arch_lwp_info *info = xcalloc (1, sizeof (*info)); + struct arch_lwp_info *info = XCNEW (struct arch_lwp_info); info->watch_registers_changed = 1; - return info; + lwp->arch_private = info; +} + +/* Function to call when a thread is being deleted. */ + +static void +mips_linux_delete_thread (struct arch_lwp_info *arch_lwp) +{ + xfree (arch_lwp); } +/* Create a new mips_watchpoint and add it to the list. */ + +static void +mips_add_watchpoint (struct arch_process_info *priv, CORE_ADDR addr, int len, + enum target_hw_bp_type watch_type) +{ + struct mips_watchpoint *new_watch; + struct mips_watchpoint **pw; + + new_watch = XNEW (struct mips_watchpoint); + new_watch->addr = addr; + new_watch->len = len; + new_watch->type = watch_type; + new_watch->next = NULL; + + pw = &priv->current_watches; + while (*pw != NULL) + pw = &(*pw)->next; + *pw = new_watch; +} + +/* Hook to call when a new fork is attached. */ + +static void +mips_linux_new_fork (struct process_info *parent, + struct process_info *child) +{ + struct arch_process_info *parent_private; + struct arch_process_info *child_private; + struct mips_watchpoint *wp; + + /* These are allocated by linux_add_process. */ + gdb_assert (parent->priv != NULL + && parent->priv->arch_private != NULL); + gdb_assert (child->priv != NULL + && child->priv->arch_private != NULL); + + /* Linux kernel before 2.6.33 commit + 72f674d203cd230426437cdcf7dd6f681dad8b0d + will inherit hardware debug registers from parent + on fork/vfork/clone. Newer Linux kernels create such tasks with + zeroed debug registers. + + GDB core assumes the child inherits the watchpoints/hw + breakpoints of the parent, and will remove them all from the + forked off process. Copy the debug registers mirrors into the + new process so that all breakpoints and watchpoints can be + removed together. The debug registers mirror will become zeroed + in the end before detaching the forked off process, thus making + this compatible with older Linux kernels too. */ + + parent_private = parent->priv->arch_private; + child_private = child->priv->arch_private; + + child_private->watch_readback_valid = parent_private->watch_readback_valid; + child_private->watch_readback = parent_private->watch_readback; + + for (wp = parent_private->current_watches; wp != NULL; wp = wp->next) + mips_add_watchpoint (child_private, wp->addr, wp->len, wp->type); + + child_private->watch_mirror = parent_private->watch_mirror; +} /* This is the implementation of linux_target_ops method prepare_to_resume. If the watch regs have changed, update the thread's copies. */ @@ -349,21 +439,21 @@ mips_linux_new_thread (void) static void mips_linux_prepare_to_resume (struct lwp_info *lwp) { - ptid_t ptid = ptid_of (lwp); - struct process_info *proc = find_process_pid (ptid_get_pid (ptid)); - struct arch_process_info *private = proc->private->arch_private; + ptid_t ptid = ptid_of (get_lwp_thread (lwp)); + struct process_info *proc = find_process_pid (ptid.pid ()); + struct arch_process_info *priv = proc->priv->arch_private; if (lwp->arch_private->watch_registers_changed) { /* Only update the watch registers if we have set or unset a watchpoint already. */ - if (mips_linux_watch_get_num_valid (&private->watch_mirror) > 0) + if (mips_linux_watch_get_num_valid (&priv->watch_mirror) > 0) { /* Write the mirrored watch register values. */ - int tid = ptid_get_lwp (ptid); + int tid = ptid.lwp (); if (-1 == ptrace (PTRACE_SET_WATCH_REGS, tid, - &private->watch_mirror)) + &priv->watch_mirror, NULL)) perror_with_name ("Couldn't write watch register"); } @@ -371,90 +461,61 @@ mips_linux_prepare_to_resume (struct lwp_info *lwp) } } -/* Translate breakpoint type TYPE in rsp to 'enum target_hw_bp_type'. */ - -static enum target_hw_bp_type -rsp_bp_type_to_target_hw_bp_type (char type) +static int +mips_supports_z_point_type (char z_type) { - switch (type) + switch (z_type) { - case '2': - return hw_write; - case '3': - return hw_read; - case '4': - return hw_access; + case Z_PACKET_WRITE_WP: + case Z_PACKET_READ_WP: + case Z_PACKET_ACCESS_WP: + return 1; + default: + return 0; } - - gdb_assert_not_reached ("unhandled RSP breakpoint type"); } /* This is the implementation of linux_target_ops method insert_point. */ static int -mips_insert_point (char type, CORE_ADDR addr, int len) +mips_insert_point (enum raw_bkpt_type type, CORE_ADDR addr, + int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); - struct arch_process_info *private = proc->private->arch_private; + struct arch_process_info *priv = proc->priv->arch_private; struct pt_watch_regs regs; - struct mips_watchpoint *new_watch; - struct mips_watchpoint **pw; - int pid; long lwpid; enum target_hw_bp_type watch_type; uint32_t irw; - /* Breakpoint/watchpoint types: - '0' - software-breakpoint (not supported) - '1' - hardware-breakpoint (not supported) - '2' - write watchpoint (supported) - '3' - read watchpoint (supported) - '4' - access watchpoint (supported). */ - - if (type < '2' || type > '4') - { - /* Unsupported. */ - return 1; - } - - lwpid = lwpid_of (get_thread_lwp (current_inferior)); + lwpid = lwpid_of (current_thread); if (!mips_linux_read_watch_registers (lwpid, - &private->watch_readback, - &private->watch_readback_valid, + &priv->watch_readback, + &priv->watch_readback_valid, 0)) return -1; if (len <= 0) return -1; - regs = private->watch_readback; + regs = priv->watch_readback; /* Add the current watches. */ - mips_linux_watch_populate_regs (private->current_watches, ®s); + mips_linux_watch_populate_regs (priv->current_watches, ®s); /* Now try to add the new watch. */ - watch_type = rsp_bp_type_to_target_hw_bp_type (type); + watch_type = raw_bkpt_type_to_target_hw_bp_type (type); irw = mips_linux_watch_type_to_irw (watch_type); if (!mips_linux_watch_try_one_watch (®s, addr, len, irw)) return -1; /* It fit. Stick it on the end of the list. */ - new_watch = xmalloc (sizeof (struct mips_watchpoint)); - new_watch->addr = addr; - new_watch->len = len; - new_watch->type = watch_type; - new_watch->next = NULL; + mips_add_watchpoint (priv, addr, len, watch_type); - pw = &private->current_watches; - while (*pw != NULL) - pw = &(*pw)->next; - *pw = new_watch; - - private->watch_mirror = regs; + priv->watch_mirror = regs; /* Only update the threads of this process. */ - pid = pid_of (proc); - find_inferior (&all_lwps, update_watch_registers_callback, &pid); + for_each_thread (proc->pid, update_watch_registers_callback); return 0; } @@ -463,35 +524,22 @@ mips_insert_point (char type, CORE_ADDR addr, int len) remove_point. */ static int -mips_remove_point (char type, CORE_ADDR addr, int len) +mips_remove_point (enum raw_bkpt_type type, CORE_ADDR addr, + int len, struct raw_breakpoint *bp) { struct process_info *proc = current_process (); - struct arch_process_info *private = proc->private->arch_private; + struct arch_process_info *priv = proc->priv->arch_private; int deleted_one; - int pid; enum target_hw_bp_type watch_type; struct mips_watchpoint **pw; struct mips_watchpoint *w; - /* Breakpoint/watchpoint types: - '0' - software-breakpoint (not supported) - '1' - hardware-breakpoint (not supported) - '2' - write watchpoint (supported) - '3' - read watchpoint (supported) - '4' - access watchpoint (supported). */ - - if (type < '2' || type > '4') - { - /* Unsupported. */ - return 1; - } - /* Search for a known watch that matches. Then unlink and free it. */ - watch_type = rsp_bp_type_to_target_hw_bp_type (type); + watch_type = raw_bkpt_type_to_target_hw_bp_type (type); deleted_one = 0; - pw = &private->current_watches; + pw = &priv->current_watches; while ((w = *pw)) { if (w->addr == addr && w->len == len && w->type == watch_type) @@ -509,15 +557,15 @@ mips_remove_point (char type, CORE_ADDR addr, int len) /* At this point watch_readback is known to be valid because we could not have added the watch without reading it. */ - gdb_assert (private->watch_readback_valid == 1); + gdb_assert (priv->watch_readback_valid == 1); - private->watch_mirror = private->watch_readback; - mips_linux_watch_populate_regs (private->current_watches, - &private->watch_mirror); + priv->watch_mirror = priv->watch_readback; + mips_linux_watch_populate_regs (priv->current_watches, + &priv->watch_mirror); /* Only update the threads of this process. */ - pid = pid_of (proc); - find_inferior (&all_lwps, update_watch_registers_callback, &pid); + for_each_thread (proc->pid, update_watch_registers_callback); + return 0; } @@ -529,21 +577,21 @@ static int mips_stopped_by_watchpoint (void) { struct process_info *proc = current_process (); - struct arch_process_info *private = proc->private->arch_private; + struct arch_process_info *priv = proc->priv->arch_private; int n; int num_valid; - long lwpid = lwpid_of (get_thread_lwp (current_inferior)); + long lwpid = lwpid_of (current_thread); if (!mips_linux_read_watch_registers (lwpid, - &private->watch_readback, - &private->watch_readback_valid, + &priv->watch_readback, + &priv->watch_readback_valid, 1)) return 0; - num_valid = mips_linux_watch_get_num_valid (&private->watch_readback); + num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback); for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++) - if (mips_linux_watch_get_watchhi (&private->watch_readback, n) + if (mips_linux_watch_get_watchhi (&priv->watch_readback, n) & (R_MASK | W_MASK)) return 1; @@ -557,10 +605,10 @@ static CORE_ADDR mips_stopped_data_address (void) { struct process_info *proc = current_process (); - struct arch_process_info *private = proc->private->arch_private; + struct arch_process_info *priv = proc->priv->arch_private; int n; int num_valid; - long lwpid = lwpid_of (get_thread_lwp (current_inferior)); + long lwpid = lwpid_of (current_thread); /* On MIPS we don't know the low order 3 bits of the data address. GDB does not support remote targets that can't report the @@ -569,28 +617,28 @@ mips_stopped_data_address (void) triggered. */ if (!mips_linux_read_watch_registers (lwpid, - &private->watch_readback, - &private->watch_readback_valid, + &priv->watch_readback, + &priv->watch_readback_valid, 0)) return 0; - num_valid = mips_linux_watch_get_num_valid (&private->watch_readback); + num_valid = mips_linux_watch_get_num_valid (&priv->watch_readback); for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++) - if (mips_linux_watch_get_watchhi (&private->watch_readback, n) + if (mips_linux_watch_get_watchhi (&priv->watch_readback, n) & (R_MASK | W_MASK)) { CORE_ADDR t_low, t_hi; int t_irw; struct mips_watchpoint *watch; - t_low = mips_linux_watch_get_watchlo (&private->watch_readback, n); + t_low = mips_linux_watch_get_watchlo (&priv->watch_readback, n); t_irw = t_low & IRW_MASK; - t_hi = (mips_linux_watch_get_watchhi (&private->watch_readback, n) + t_hi = (mips_linux_watch_get_watchhi (&priv->watch_readback, n) | IRW_MASK); t_low &= ~(CORE_ADDR)t_hi; - for (watch = private->current_watches; + for (watch = priv->current_watches; watch != NULL; watch = watch->next) { @@ -615,7 +663,7 @@ mips_stopped_data_address (void) /* Fetch the thread-local storage pointer for libthread_db. */ ps_err_e -ps_get_thread_area (const struct ps_prochandle *ph, +ps_get_thread_area (struct ps_prochandle *ph, lwpid_t lwpid, int idx, void **base) { if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0) @@ -629,8 +677,6 @@ ps_get_thread_area (const struct ps_prochandle *ph, return PS_OK; } -#ifdef HAVE_PTRACE_GETREGS - static void mips_collect_register (struct regcache *regcache, int use_64bit, int regno, union mips_register *reg) @@ -663,6 +709,8 @@ mips_supply_register (struct regcache *regcache, supply_register (regcache, regno, reg->buf + offset); } +#ifdef HAVE_PTRACE_GETREGS + static void mips_collect_register_32bit (struct regcache *regcache, int use_64bit, int regno, unsigned char *buf) @@ -690,7 +738,7 @@ mips_supply_register_32bit (struct regcache *regcache, static void mips_fill_gregset (struct regcache *regcache, void *buf) { - union mips_register *regset = buf; + union mips_register *regset = (union mips_register *) buf; int i, use_64bit; const struct target_desc *tdesc = regcache->tdesc; @@ -719,12 +767,14 @@ mips_fill_gregset (struct regcache *regcache, void *buf) static void mips_store_gregset (struct regcache *regcache, const void *buf) { - const union mips_register *regset = buf; + const union mips_register *regset = (const union mips_register *) buf; int i, use_64bit; use_64bit = (register_size (regcache->tdesc, 0) == 8); - for (i = 0; i < 32; i++) + supply_register_by_name_zeroed (regcache, "r0"); + + for (i = 1; i < 32; i++) mips_supply_register (regcache, use_64bit, i, regset + i); mips_supply_register (regcache, use_64bit, @@ -747,7 +797,7 @@ mips_store_gregset (struct regcache *regcache, const void *buf) static void mips_fill_fpregset (struct regcache *regcache, void *buf) { - union mips_register *regset = buf; + union mips_register *regset = (union mips_register *) buf; int i, use_64bit, first_fp, big_endian; use_64bit = (register_size (regcache->tdesc, 0) == 8); @@ -772,7 +822,7 @@ mips_fill_fpregset (struct regcache *regcache, void *buf) static void mips_store_fpregset (struct regcache *regcache, const void *buf) { - const union mips_register *regset = buf; + const union mips_register *regset = (const union mips_register *) buf; int i, use_64bit, first_fp, big_endian; use_64bit = (register_size (regcache->tdesc, 0) == 8); @@ -796,6 +846,44 @@ mips_store_fpregset (struct regcache *regcache, const void *buf) } #endif /* HAVE_PTRACE_GETREGS */ +/* Take care of 32-bit registers with 64-bit ptrace, POKEUSER side. */ + +static void +mips_collect_ptrace_register (struct regcache *regcache, + int regno, char *buf) +{ + int use_64bit = sizeof (PTRACE_XFER_TYPE) == 8; + + if (use_64bit && register_size (regcache->tdesc, regno) == 4) + { + union mips_register reg; + + mips_collect_register (regcache, 0, regno, ®); + memcpy (buf, ®, sizeof (reg)); + } + else + collect_register (regcache, regno, buf); +} + +/* Take care of 32-bit registers with 64-bit ptrace, PEEKUSER side. */ + +static void +mips_supply_ptrace_register (struct regcache *regcache, + int regno, const char *buf) +{ + int use_64bit = sizeof (PTRACE_XFER_TYPE) == 8; + + if (use_64bit && register_size (regcache->tdesc, regno) == 4) + { + union mips_register reg; + + memcpy (®, buf, sizeof (reg)); + mips_supply_register (regcache, 0, regno, ®); + } + else + supply_register (regcache, regno, buf); +} + static struct regset_info mips_regsets[] = { #ifdef HAVE_PTRACE_GETREGS { PTRACE_GETREGS, PTRACE_SETREGS, 0, 38 * 8, GENERAL_REGS, @@ -803,7 +891,7 @@ static struct regset_info mips_regsets[] = { { PTRACE_GETFPREGS, PTRACE_SETFPREGS, 0, 33 * 8, FP_REGS, mips_fill_fpregset, mips_store_fpregset }, #endif /* HAVE_PTRACE_GETREGS */ - { 0, 0, 0, -1, -1, NULL, NULL } + NULL_REGSET }; static struct regsets_info mips_regsets_info = @@ -853,23 +941,27 @@ struct linux_target_ops the_low_target = { mips_regs_info, mips_cannot_fetch_register, mips_cannot_store_register, - NULL, /* fetch_register */ + mips_fetch_register, mips_get_pc, mips_set_pc, - (const unsigned char *) &mips_breakpoint, - mips_breakpoint_len, - mips_reinsert_addr, + NULL, /* breakpoint_kind_from_pc */ + mips_sw_breakpoint_from_kind, + NULL, /* get_next_pcs */ 0, mips_breakpoint_at, + mips_supports_z_point_type, mips_insert_point, mips_remove_point, mips_stopped_by_watchpoint, mips_stopped_data_address, - NULL, - NULL, + mips_collect_ptrace_register, + mips_supply_ptrace_register, NULL, /* siginfo_fixup */ mips_linux_new_process, + mips_linux_delete_process, mips_linux_new_thread, + mips_linux_delete_thread, + mips_linux_new_fork, mips_linux_prepare_to_resume };