5af949e350
(paddress): Add GDBARCH parameter. * utils.c (strlen_paddr, paddr, paddr_nz): Remove. (paddress): Add GDBARCH parameter, use it instead of current_gdbarch. * ui-out.h (ui_out_field_core_addr): Add GDBARCH parameter. * ui-out.c (ui_out_field_core_addr): Add GDBARCH parameter, use it instead of current_gdbarch. Update calls to ui_out_field_core_addr to pass architecture: * ada-lang.c (print_one_exception): Update. * breakpoint.c (print_one_breakpoint_location, print_one_exception_catchpoint): Update. * disasm.c (dump_insns): Update. * darwin-nat-info.c (darwin_debug_regions_recurse): Update. * mi/mi-main.c (mi_cmd_data_read_memory): Update. * mi/mi-symbol-cmds.c: Include "objfiles.h". (mi_cmd_symbol_list_lines): Update. * stack.c (print_frame_info, print_frame): Update. Update callers of paddress to pass architecture: * ada-tasks.c (info_task): Update. * ada-valprint.c (ada_val_print_1): Update. * annotate.c (annotate_source, annotate_frame_begin): Update. * breakpoint.c (insert_bp_location, describe_other_breakpoints, mention): Update. * cli/cli-cmds.c (edit_command, list_command, print_disassembly): Update. * corefile.c (memory_error): Update. * c-valprint.c (print_function_pointer_address, c_val_print): Update. * disasm.c (dis_asm_print_address): Update. * exec.c (print_section_info): Update. * f-valprint.c (f_val_print): Update. * infcmd.c: Include "arch-utils.h". (jump_command, program_info): Update. * linux-fork.c: Include "arch-utils.h". (info_forks_command): Update. * m2-valprint.c (print_function_pointer_address, print_unpacked_pointer, print_variable_at_address, m2_val_print): Update. * m32r-rom.c (m32r_load_section, m32r_load, m32r_upload_command): Update. * printcmd.c (print_address, print_address_demangle, address_info): Update. * p-valprint.c (pascal_val_print): Update. * source.c: Include "arch-utils.h". (line_info): Update. * stack.c (frame_info, print_block_frame_labels): Update. * symfile.c (add_symbol_file_command, list_overlays_command): Update. * symmisc.c (dump_msymbols, dump_psymtab, dump_symtab_1, print_symbol, print_partial_symbols, maintenance_info_psymtabs, maintenance_check_symtabs): Update. * symtab.c (find_pc_sect_symtab): Update. * target.c (deprecated_debug_xfer_memory): Update. * tracepoint.c (scope_info): Update. * tui/tui-stack.c (tui_make_status_line): Update. * valprint.c (val_print_string): Update. Update callers of paddr_nz to use paddress instead (keeping user-visible output identical): * alpha-tdep.c (alpha_heuristic_proc_start): Update. * amd64-tdep.c (fixup_riprel, amd64_displaced_step_copy_insn, amd64_displaced_step_fixup): Update. * arch-utils.c (simple_displaced_step_copy_insn): Update. * auxv.c (fprint_target_auxv): Update. * breakpoint.c (insert_single_step_breakpoint): Update. * buildsym.c (finish_block): Update. * cli/cli-dump.c (restore_section_callback): Update. * fbsd-nat.c (fbsd_find_memory_regions): Update. * frame.c (frame_unwind_register_value): Update. * gcore.c (gcore_create_callback): Update. * hppa-tdep.c (hppa_frame_cache, hppa_skip_trampoline_code): Update. * i386-tdep.c (i386_displaced_step_fixup, i386_record_modrm, i386_record_lea_modrm_addr, i386_record_lea_modrm, i386_process_record): Update. * ia64-tdep.c (ia64_frame_this_id, ia64_sigtramp_frame_this_id, ia64_libunwind_frame_this_id, ia64_libunwind_sigtramp_frame_this_id, ia64_dummy_id, ia64_access_reg, ia64_access_rse_reg): Update. * infrun.c (displaced_step_prepare, displaced_step_fixup, handle_inferior_event, insert_step_resume_breakpoint_at_sal, insert_longjmp_resume_breakpoint): Update. * linux-nat.c (linux_nat_find_memory_regions): Update. * linux-record.c (record_linux_system_call): Update. * mips-tdep.c (heuristic_proc_start, mips_eabi_push_dummy_call, mips_n32n64_push_dummy_call, mips_o32_push_dummy_call, mips_o64_push_dummy_call): Update. * monitor.c (monitor_error, monitor_remove_breakpoint): Update. * record.c (record_arch_list_add_mem, record_wait, record_xfer_partial): Update. * remote-mips.c (mips_fetch_word, mips_check_lsi_error, mips_common_breakpoint): Update. * remote-sim.c (gdbsim_xfer_inferior_memory): Update. * rs6000-tdep.c (ppc_displaced_step_fixup): Update. * solib-som.c (som_current_sos): Update. * symfile.c (load_progress, generic_load): Update. * symfile-mem.c (add_vsyscall_page): Update. * valops.c (value_fetch_lazy): Update. * windows-tdep.c (windows_xfer_shared_library): Update. Update callers of paddr_nz to use paddress instead (changing user-visible output to make it more correct): * dwarf2loc.c (locexpr_describe_location): Update. * ia64-tdep.c (ia64_memory_insert_breakpoint, ia64_memory_remove_breakpoint): Update. * jv-valprint.c (java_value_print): Update. * m32c-tdep.c (m32c_m16c_address_to_pointer): Update. * monitor.c (monitor_read_memory): Update. Update callers of paddr to use paddress instead (changing user-visible output to make it more correct): * arm-tdep.c (arm_push_dummy_call): Update. * breakpoint.c (insert_bp_location, create_thread_event_breakpoint, create_breakpoint): Update. * darwin-nat-info.c (darwin_debug_regions): Update. * dcache.c (dcache_info): Update. * dsrec.c (load_srec, make_srec): Update. * dwarf2-frame.c (dwarf2_restore_rule, execute_cfa_program, dwarf2_frame_cache): Update. * gcore.c (gcore_copy_callback): Update. * gnu-nat.c (gnu_xfer_memory): Update. * mips-linux-nat.c (mips_show_dr): Update. * monitor.c (monitor_write_memory, monitor_insert_breakpoint, monitor_remove_breakpoint): Update. * remote.c (compare_sections_command): Update. * remote-m32r-sdi.c (m32r_xfer_memory, m32r_insert_breakpoint, m32r_remove_breakpoint, m32r_insert_watchpoint, m32r_remove_watchpoint): Update. * sol-thread.c (info_cb): Update. * symfile.c (load_progress): Update. Update callers of paddress or paddr_nz to use hex_string instead (changes output of internal/error/debug messages only): * dwarf2read.c (dump_die_shallow): Update. * frame.c (fprint_field, fprint_frame, frame_pc_unwind, get_frame_func, create_new_frame): Update. * hppa-tdep.c (find_unwind_entry, unwind_command): Update. * ia64-tdep.c (get_kernel_table, ia64_find_proc_info_x, ia64_get_dyn_info_list): Update. * maint.c (maintenance_translate_address): Update. * mi/mi-cmd-var.c (mi_cmd_var_create): Update. * target.c (target_flash_erase): Update. Update callers of paddr/paddr_nz to use phex/phex_nz instead, using an appropriate address size. Remove use of strlen_paddr. * exec.c (exec_files_info): Update. * i386-nat.c (i386_show_dr): Update. * remote.c (remote_flash_erase): Update. * m32r-rom.c (m32r_load_section): Update. * monitor.c (monitor_vsprintf, monitor_store_register): Update. * remote.c (remote_check_symbols, remote_search_memory): Update. * remote-mips.c (mips_request, mips_common_breakpoint): Update. * scm-valprint.c (scm_ipruk, scm_scmval_print): Update. * sh64-tdep.c (sh64_show_media_regs, sh64_show_compact_regs): Update. * sh-tdep.c (sh_generic_show_regs, sh3_show_regs, sh2e_show_regs, sh2a_show_regs, sh2a_nofpu_show_regs, sh3e_show_regs, sh3_dsp_show_regs, sh4_show_regs, sh4_nofpu_show_regs, sh_dsp_show_regs): Update. * xcoffsolib.c (sharedlibrary_command): Update. * maint.c (maint_print_section_info): Add ADDR_SIZE parameter. Use hex_string_custom instead of paddr. (print_bfd_section_info): Pass address size. (print_objfile_section_info): Likewise. * annotate.h (annotate_source): Add GDBARCH parameter. (annotate_frame_begin): Likewise. * annotate.c (annotate_source): Add GDBARCH parameter. (annotate_frame_begin): Likewise. * source.c (identify_source_line): Update call to annotate_source. * stack.c (print_frame_info, print_frame): Update call to annotate_frame_begin. * breakpoint.c (describe_other_breakpoints): Add GDBARCH parameter. (create_breakpoint, create_ada_exception_breakpoint): Update call. * stack.c (print_block_frame_labels): Add GDBARCH parameter. (print_frame_label_vars): Update call. * symmisc.c (print_partial_symbols): Add GDBARCH parameter. (dump_psymtab): Update call to print_partial_symbols. (struct print_symbol_args): Add GDBARCH member. (dump_symtab_1): Set print_symbol_args architecture member. (print_symbol): Use it. * windows-tdep.h (windows_xfer_shared_library): Add GDBARCH parameter. * windows-tdep.c (windows_xfer_shared_library): Likewise. * i386-cygwin-tdep.c (struct cpms_data): Add GDBARCH member. (core_process_module_section): Pass architecture from cpms_data to windows_xfer_shared_library. (windows_core_xfer_shared_libraries): Initialize cmps_data architecture member. * windows-nat.c (windows_xfer_shared_libraries): Pass architecture to windows_xfer_shared_library. * defs.h (print_address): Add GDBARCH parameter. * printcmd.c (print_address): Add GDBARCH parameter. (print_scalar_formatted, do_examine): Update call. * findcmd.c (find_command): Update call. * tracepoint.c: Include "arch-utils.h". (trace_find_line_command): Update call. * tui/tui-disasm.c (tui_disassemble): Update call. * value.h (print_address_demangle): Add GDBARCH parameter. * printcmd.c (print_address_demangle): Add GDBARCH parameter. * c-valprint.c (print_function_pointer_address, c_val_print): Update call. * f-valprint.c (f_val_print): Update call. * gnu-v3-abi.c (gnuv3_print_method_ptr): Update call. * jv-valprint.c (java_val_print): Update call. * m2-valprint.c (print_function_pointer_address, m2_val_print): Update call. * p-valprint.c (pascal_val_print): Update call. * disasm.c (gdb_disassemble_info): Install architecture into di.application_data field. testsuite/ChangeLog: * gdb.threads/tls-shared.exp: Update to locexpr_describe_location change to prefix TLS offset in hex with 0x. doc/ChangeLog: * gdbint.texinfo (Item Output Functions): Update signature for ui_out_field_core_addr.
1090 lines
28 KiB
C
1090 lines
28 KiB
C
/* Native-dependent code for GNU/Linux on MIPS processors.
|
|
|
|
Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
|
|
Free Software Foundation, Inc.
|
|
|
|
This file is part of GDB.
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License as published by
|
|
the Free Software Foundation; either version 3 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
|
|
|
#include "defs.h"
|
|
#include "command.h"
|
|
#include "gdbcmd.h"
|
|
#include "gdb_assert.h"
|
|
#include "inferior.h"
|
|
#include "mips-tdep.h"
|
|
#include "target.h"
|
|
#include "regcache.h"
|
|
#include "linux-nat.h"
|
|
#include "mips-linux-tdep.h"
|
|
#include "target-descriptions.h"
|
|
|
|
#include "gdb_proc_service.h"
|
|
#include "gregset.h"
|
|
|
|
#include <sgidefs.h>
|
|
#include <sys/ptrace.h>
|
|
|
|
#include "features/mips-linux.c"
|
|
#include "features/mips64-linux.c"
|
|
|
|
#ifndef PTRACE_GET_THREAD_AREA
|
|
#define PTRACE_GET_THREAD_AREA 25
|
|
#endif
|
|
|
|
/* Assume that we have PTRACE_GETREGS et al. support. If we do not,
|
|
we'll clear this and use PTRACE_PEEKUSER instead. */
|
|
static int have_ptrace_regsets = 1;
|
|
|
|
/* Whether or not to print the mirrored debug registers. */
|
|
|
|
static int maint_show_dr;
|
|
|
|
/* Saved function pointers to fetch and store a single register using
|
|
PTRACE_PEEKUSER and PTRACE_POKEUSER. */
|
|
|
|
static void (*super_fetch_registers) (struct target_ops *,
|
|
struct regcache *, int);
|
|
static void (*super_store_registers) (struct target_ops *,
|
|
struct regcache *, int);
|
|
|
|
static void (*super_close) (int);
|
|
|
|
/* Map gdb internal register number to ptrace ``address''.
|
|
These ``addresses'' are normally defined in <asm/ptrace.h>.
|
|
|
|
ptrace does not provide a way to read (or set) MIPS_PS_REGNUM,
|
|
and there's no point in reading or setting MIPS_ZERO_REGNUM.
|
|
We also can not set BADVADDR, CAUSE, or FCRIR via ptrace(). */
|
|
|
|
static CORE_ADDR
|
|
mips_linux_register_addr (struct gdbarch *gdbarch, int regno, int store)
|
|
{
|
|
CORE_ADDR regaddr;
|
|
|
|
if (regno < 0 || regno >= gdbarch_num_regs (gdbarch))
|
|
error (_("Bogon register number %d."), regno);
|
|
|
|
if (regno > MIPS_ZERO_REGNUM && regno < MIPS_ZERO_REGNUM + 32)
|
|
regaddr = regno;
|
|
else if ((regno >= mips_regnum (gdbarch)->fp0)
|
|
&& (regno < mips_regnum (gdbarch)->fp0 + 32))
|
|
regaddr = FPR_BASE + (regno - mips_regnum (gdbarch)->fp0);
|
|
else if (regno == mips_regnum (gdbarch)->pc)
|
|
regaddr = PC;
|
|
else if (regno == mips_regnum (gdbarch)->cause)
|
|
regaddr = store? (CORE_ADDR) -1 : CAUSE;
|
|
else if (regno == mips_regnum (gdbarch)->badvaddr)
|
|
regaddr = store? (CORE_ADDR) -1 : BADVADDR;
|
|
else if (regno == mips_regnum (gdbarch)->lo)
|
|
regaddr = MMLO;
|
|
else if (regno == mips_regnum (gdbarch)->hi)
|
|
regaddr = MMHI;
|
|
else if (regno == mips_regnum (gdbarch)->fp_control_status)
|
|
regaddr = FPC_CSR;
|
|
else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
|
|
regaddr = store? (CORE_ADDR) -1 : FPC_EIR;
|
|
else if (mips_linux_restart_reg_p (gdbarch) && regno == MIPS_RESTART_REGNUM)
|
|
regaddr = 0;
|
|
else
|
|
regaddr = (CORE_ADDR) -1;
|
|
|
|
return regaddr;
|
|
}
|
|
|
|
static CORE_ADDR
|
|
mips64_linux_register_addr (struct gdbarch *gdbarch, int regno, int store)
|
|
{
|
|
CORE_ADDR regaddr;
|
|
|
|
if (regno < 0 || regno >= gdbarch_num_regs (gdbarch))
|
|
error (_("Bogon register number %d."), regno);
|
|
|
|
if (regno > MIPS_ZERO_REGNUM && regno < MIPS_ZERO_REGNUM + 32)
|
|
regaddr = regno;
|
|
else if ((regno >= mips_regnum (gdbarch)->fp0)
|
|
&& (regno < mips_regnum (gdbarch)->fp0 + 32))
|
|
regaddr = MIPS64_FPR_BASE + (regno - gdbarch_fp0_regnum (gdbarch));
|
|
else if (regno == mips_regnum (gdbarch)->pc)
|
|
regaddr = MIPS64_PC;
|
|
else if (regno == mips_regnum (gdbarch)->cause)
|
|
regaddr = store? (CORE_ADDR) -1 : MIPS64_CAUSE;
|
|
else if (regno == mips_regnum (gdbarch)->badvaddr)
|
|
regaddr = store? (CORE_ADDR) -1 : MIPS64_BADVADDR;
|
|
else if (regno == mips_regnum (gdbarch)->lo)
|
|
regaddr = MIPS64_MMLO;
|
|
else if (regno == mips_regnum (gdbarch)->hi)
|
|
regaddr = MIPS64_MMHI;
|
|
else if (regno == mips_regnum (gdbarch)->fp_control_status)
|
|
regaddr = MIPS64_FPC_CSR;
|
|
else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
|
|
regaddr = store? (CORE_ADDR) -1 : MIPS64_FPC_EIR;
|
|
else if (mips_linux_restart_reg_p (gdbarch) && regno == MIPS_RESTART_REGNUM)
|
|
regaddr = 0;
|
|
else
|
|
regaddr = (CORE_ADDR) -1;
|
|
|
|
return regaddr;
|
|
}
|
|
|
|
/* Fetch the thread-local storage pointer for libthread_db. */
|
|
|
|
ps_err_e
|
|
ps_get_thread_area (const struct ps_prochandle *ph,
|
|
lwpid_t lwpid, int idx, void **base)
|
|
{
|
|
if (ptrace (PTRACE_GET_THREAD_AREA, lwpid, NULL, base) != 0)
|
|
return PS_ERR;
|
|
|
|
/* IDX is the bias from the thread pointer to the beginning of the
|
|
thread descriptor. It has to be subtracted due to implementation
|
|
quirks in libthread_db. */
|
|
*base = (void *) ((char *)*base - idx);
|
|
|
|
return PS_OK;
|
|
}
|
|
|
|
/* Wrapper functions. These are only used by libthread_db. */
|
|
|
|
void
|
|
supply_gregset (struct regcache *regcache, const gdb_gregset_t *gregsetp)
|
|
{
|
|
if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
|
|
mips_supply_gregset (regcache, (const mips_elf_gregset_t *) gregsetp);
|
|
else
|
|
mips64_supply_gregset (regcache, (const mips64_elf_gregset_t *) gregsetp);
|
|
}
|
|
|
|
void
|
|
fill_gregset (const struct regcache *regcache,
|
|
gdb_gregset_t *gregsetp, int regno)
|
|
{
|
|
if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
|
|
mips_fill_gregset (regcache, (mips_elf_gregset_t *) gregsetp, regno);
|
|
else
|
|
mips64_fill_gregset (regcache, (mips64_elf_gregset_t *) gregsetp, regno);
|
|
}
|
|
|
|
void
|
|
supply_fpregset (struct regcache *regcache, const gdb_fpregset_t *fpregsetp)
|
|
{
|
|
if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
|
|
mips_supply_fpregset (regcache, (const mips_elf_fpregset_t *) fpregsetp);
|
|
else
|
|
mips64_supply_fpregset (regcache, (const mips64_elf_fpregset_t *) fpregsetp);
|
|
}
|
|
|
|
void
|
|
fill_fpregset (const struct regcache *regcache,
|
|
gdb_fpregset_t *fpregsetp, int regno)
|
|
{
|
|
if (mips_isa_regsize (get_regcache_arch (regcache)) == 4)
|
|
mips_fill_fpregset (regcache, (mips_elf_fpregset_t *) fpregsetp, regno);
|
|
else
|
|
mips64_fill_fpregset (regcache, (mips64_elf_fpregset_t *) fpregsetp, regno);
|
|
}
|
|
|
|
|
|
/* Fetch REGNO (or all registers if REGNO == -1) from the target
|
|
using PTRACE_GETREGS et al. */
|
|
|
|
static void
|
|
mips64_linux_regsets_fetch_registers (struct regcache *regcache, int regno)
|
|
{
|
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
|
int is_fp;
|
|
int tid;
|
|
|
|
if (regno >= mips_regnum (gdbarch)->fp0
|
|
&& regno <= mips_regnum (gdbarch)->fp0 + 32)
|
|
is_fp = 1;
|
|
else if (regno == mips_regnum (gdbarch)->fp_control_status)
|
|
is_fp = 1;
|
|
else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
|
|
is_fp = 1;
|
|
else
|
|
is_fp = 0;
|
|
|
|
tid = ptid_get_lwp (inferior_ptid);
|
|
if (tid == 0)
|
|
tid = ptid_get_pid (inferior_ptid);
|
|
|
|
if (regno == -1 || !is_fp)
|
|
{
|
|
mips64_elf_gregset_t regs;
|
|
|
|
if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) ®s) == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
have_ptrace_regsets = 0;
|
|
return;
|
|
}
|
|
perror_with_name (_("Couldn't get registers"));
|
|
}
|
|
|
|
mips64_supply_gregset (regcache,
|
|
(const mips64_elf_gregset_t *) ®s);
|
|
}
|
|
|
|
if (regno == -1 || is_fp)
|
|
{
|
|
mips64_elf_fpregset_t fp_regs;
|
|
|
|
if (ptrace (PTRACE_GETFPREGS, tid, 0L,
|
|
(PTRACE_TYPE_ARG3) &fp_regs) == -1)
|
|
{
|
|
if (errno == EIO)
|
|
{
|
|
have_ptrace_regsets = 0;
|
|
return;
|
|
}
|
|
perror_with_name (_("Couldn't get FP registers"));
|
|
}
|
|
|
|
mips64_supply_fpregset (regcache,
|
|
(const mips64_elf_fpregset_t *) &fp_regs);
|
|
}
|
|
}
|
|
|
|
/* Store REGNO (or all registers if REGNO == -1) to the target
|
|
using PTRACE_SETREGS et al. */
|
|
|
|
static void
|
|
mips64_linux_regsets_store_registers (const struct regcache *regcache, int regno)
|
|
{
|
|
struct gdbarch *gdbarch = get_regcache_arch (regcache);
|
|
int is_fp;
|
|
int tid;
|
|
|
|
if (regno >= mips_regnum (gdbarch)->fp0
|
|
&& regno <= mips_regnum (gdbarch)->fp0 + 32)
|
|
is_fp = 1;
|
|
else if (regno == mips_regnum (gdbarch)->fp_control_status)
|
|
is_fp = 1;
|
|
else if (regno == mips_regnum (gdbarch)->fp_implementation_revision)
|
|
is_fp = 1;
|
|
else
|
|
is_fp = 0;
|
|
|
|
tid = ptid_get_lwp (inferior_ptid);
|
|
if (tid == 0)
|
|
tid = ptid_get_pid (inferior_ptid);
|
|
|
|
if (regno == -1 || !is_fp)
|
|
{
|
|
mips64_elf_gregset_t regs;
|
|
|
|
if (ptrace (PTRACE_GETREGS, tid, 0L, (PTRACE_TYPE_ARG3) ®s) == -1)
|
|
perror_with_name (_("Couldn't get registers"));
|
|
|
|
mips64_fill_gregset (regcache, ®s, regno);
|
|
|
|
if (ptrace (PTRACE_SETREGS, tid, 0L, (PTRACE_TYPE_ARG3) ®s) == -1)
|
|
perror_with_name (_("Couldn't set registers"));
|
|
}
|
|
|
|
if (regno == -1 || is_fp)
|
|
{
|
|
mips64_elf_fpregset_t fp_regs;
|
|
|
|
if (ptrace (PTRACE_GETFPREGS, tid, 0L,
|
|
(PTRACE_TYPE_ARG3) &fp_regs) == -1)
|
|
perror_with_name (_("Couldn't get FP registers"));
|
|
|
|
mips64_fill_fpregset (regcache, &fp_regs, regno);
|
|
|
|
if (ptrace (PTRACE_SETFPREGS, tid, 0L,
|
|
(PTRACE_TYPE_ARG3) &fp_regs) == -1)
|
|
perror_with_name (_("Couldn't set FP registers"));
|
|
}
|
|
}
|
|
|
|
/* Fetch REGNO (or all registers if REGNO == -1) from the target
|
|
using any working method. */
|
|
|
|
static void
|
|
mips64_linux_fetch_registers (struct target_ops *ops,
|
|
struct regcache *regcache, int regnum)
|
|
{
|
|
/* Unless we already know that PTRACE_GETREGS does not work, try it. */
|
|
if (have_ptrace_regsets)
|
|
mips64_linux_regsets_fetch_registers (regcache, regnum);
|
|
|
|
/* If we know, or just found out, that PTRACE_GETREGS does not work, fall
|
|
back to PTRACE_PEEKUSER. */
|
|
if (!have_ptrace_regsets)
|
|
super_fetch_registers (ops, regcache, regnum);
|
|
}
|
|
|
|
/* Store REGNO (or all registers if REGNO == -1) to the target
|
|
using any working method. */
|
|
|
|
static void
|
|
mips64_linux_store_registers (struct target_ops *ops,
|
|
struct regcache *regcache, int regnum)
|
|
{
|
|
/* Unless we already know that PTRACE_GETREGS does not work, try it. */
|
|
if (have_ptrace_regsets)
|
|
mips64_linux_regsets_store_registers (regcache, regnum);
|
|
|
|
/* If we know, or just found out, that PTRACE_GETREGS does not work, fall
|
|
back to PTRACE_PEEKUSER. */
|
|
if (!have_ptrace_regsets)
|
|
super_store_registers (ops, regcache, regnum);
|
|
}
|
|
|
|
/* Return the address in the core dump or inferior of register
|
|
REGNO. */
|
|
|
|
static CORE_ADDR
|
|
mips_linux_register_u_offset (struct gdbarch *gdbarch, int regno, int store_p)
|
|
{
|
|
if (mips_abi_regsize (gdbarch) == 8)
|
|
return mips64_linux_register_addr (gdbarch, regno, store_p);
|
|
else
|
|
return mips_linux_register_addr (gdbarch, regno, store_p);
|
|
}
|
|
|
|
static const struct target_desc *
|
|
mips_linux_read_description (struct target_ops *ops)
|
|
{
|
|
/* Report that target registers are a size we know for sure
|
|
that we can get from ptrace. */
|
|
if (_MIPS_SIM == _ABIO32)
|
|
return tdesc_mips_linux;
|
|
else
|
|
return tdesc_mips64_linux;
|
|
}
|
|
|
|
#ifndef PTRACE_GET_WATCH_REGS
|
|
# define PTRACE_GET_WATCH_REGS 0xd0
|
|
#endif
|
|
|
|
#ifndef PTRACE_SET_WATCH_REGS
|
|
# define PTRACE_SET_WATCH_REGS 0xd1
|
|
#endif
|
|
|
|
#define W_BIT 0
|
|
#define R_BIT 1
|
|
#define I_BIT 2
|
|
|
|
#define W_MASK (1 << W_BIT)
|
|
#define R_MASK (1 << R_BIT)
|
|
#define I_MASK (1 << I_BIT)
|
|
|
|
#define IRW_MASK (I_MASK | R_MASK | W_MASK)
|
|
|
|
enum pt_watch_style {
|
|
pt_watch_style_mips32,
|
|
pt_watch_style_mips64
|
|
};
|
|
|
|
#define MAX_DEBUG_REGISTER 8
|
|
|
|
/* A value of zero in a watchlo indicates that it is available. */
|
|
|
|
struct mips32_watch_regs
|
|
{
|
|
uint32_t watchlo[MAX_DEBUG_REGISTER];
|
|
/* Lower 16 bits of watchhi. */
|
|
uint16_t watchhi[MAX_DEBUG_REGISTER];
|
|
/* Valid mask and I R W bits.
|
|
* bit 0 -- 1 if W bit is usable.
|
|
* bit 1 -- 1 if R bit is usable.
|
|
* bit 2 -- 1 if I bit is usable.
|
|
* bits 3 - 11 -- Valid watchhi mask bits.
|
|
*/
|
|
uint16_t watch_masks[MAX_DEBUG_REGISTER];
|
|
/* The number of valid watch register pairs. */
|
|
uint32_t num_valid;
|
|
/* There is confusion across gcc versions about structure alignment,
|
|
so we force 8 byte alignment for these structures so they match
|
|
the kernel even if it was build with a different gcc version. */
|
|
} __attribute__ ((aligned (8)));
|
|
|
|
struct mips64_watch_regs
|
|
{
|
|
uint64_t watchlo[MAX_DEBUG_REGISTER];
|
|
uint16_t watchhi[MAX_DEBUG_REGISTER];
|
|
uint16_t watch_masks[MAX_DEBUG_REGISTER];
|
|
uint32_t num_valid;
|
|
} __attribute__ ((aligned (8)));
|
|
|
|
struct pt_watch_regs
|
|
{
|
|
enum pt_watch_style style;
|
|
union
|
|
{
|
|
struct mips32_watch_regs mips32;
|
|
struct mips64_watch_regs mips64;
|
|
};
|
|
};
|
|
|
|
/* -1 if the kernel and/or CPU do not support watch registers.
|
|
1 if watch_readback is valid and we can read style, num_valid
|
|
and the masks.
|
|
0 if we need to read the watch_readback. */
|
|
|
|
static int watch_readback_valid;
|
|
|
|
/* Cached watch register read values. */
|
|
|
|
static struct pt_watch_regs watch_readback;
|
|
|
|
/* We keep list of all watchpoints we should install and calculate the
|
|
watch register values each time the list changes. This allows for
|
|
easy sharing of watch registers for more than one watchpoint. */
|
|
|
|
struct mips_watchpoint
|
|
{
|
|
CORE_ADDR addr;
|
|
int len;
|
|
int type;
|
|
struct mips_watchpoint *next;
|
|
};
|
|
|
|
static struct mips_watchpoint *current_watches;
|
|
|
|
/* The current set of watch register values for writing the
|
|
registers. */
|
|
|
|
static struct pt_watch_regs watch_mirror;
|
|
|
|
/* Assuming usable watch registers, return the irw_mask. */
|
|
|
|
static uint32_t
|
|
get_irw_mask (struct pt_watch_regs *regs, int set)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
return regs->mips32.watch_masks[set] & IRW_MASK;
|
|
case pt_watch_style_mips64:
|
|
return regs->mips64.watch_masks[set] & IRW_MASK;
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
/* Assuming usable watch registers, return the reg_mask. */
|
|
|
|
static uint32_t
|
|
get_reg_mask (struct pt_watch_regs *regs, int set)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
return regs->mips32.watch_masks[set] & ~IRW_MASK;
|
|
case pt_watch_style_mips64:
|
|
return regs->mips64.watch_masks[set] & ~IRW_MASK;
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
/* Assuming usable watch registers, return the num_valid. */
|
|
|
|
static uint32_t
|
|
get_num_valid (struct pt_watch_regs *regs)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
return regs->mips32.num_valid;
|
|
case pt_watch_style_mips64:
|
|
return regs->mips64.num_valid;
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
/* Assuming usable watch registers, return the watchlo. */
|
|
|
|
static CORE_ADDR
|
|
get_watchlo (struct pt_watch_regs *regs, int set)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
return regs->mips32.watchlo[set];
|
|
case pt_watch_style_mips64:
|
|
return regs->mips64.watchlo[set];
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
/* Assuming usable watch registers, set a watchlo value. */
|
|
|
|
static void
|
|
set_watchlo (struct pt_watch_regs *regs, int set, CORE_ADDR value)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
/* The cast will never throw away bits as 64 bit addresses can
|
|
never be used on a 32 bit kernel. */
|
|
regs->mips32.watchlo[set] = (uint32_t)value;
|
|
break;
|
|
case pt_watch_style_mips64:
|
|
regs->mips64.watchlo[set] = value;
|
|
break;
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
/* Assuming usable watch registers, return the watchhi. */
|
|
|
|
static uint32_t
|
|
get_watchhi (struct pt_watch_regs *regs, int n)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
return regs->mips32.watchhi[n];
|
|
case pt_watch_style_mips64:
|
|
return regs->mips64.watchhi[n];
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
/* Assuming usable watch registers, set a watchhi value. */
|
|
|
|
static void
|
|
set_watchhi (struct pt_watch_regs *regs, int n, uint16_t value)
|
|
{
|
|
switch (regs->style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
regs->mips32.watchhi[n] = value;
|
|
break;
|
|
case pt_watch_style_mips64:
|
|
regs->mips64.watchhi[n] = value;
|
|
break;
|
|
default:
|
|
internal_error (__FILE__, __LINE__,
|
|
_("Unrecognized watch register style"));
|
|
}
|
|
}
|
|
|
|
static void
|
|
mips_show_dr (const char *func, CORE_ADDR addr,
|
|
int len, enum target_hw_bp_type type)
|
|
{
|
|
int i;
|
|
|
|
puts_unfiltered (func);
|
|
if (addr || len)
|
|
printf_unfiltered (" (addr=%s, len=%d, type=%s)",
|
|
paddress (target_gdbarch, addr), len,
|
|
type == hw_write ? "data-write"
|
|
: (type == hw_read ? "data-read"
|
|
: (type == hw_access ? "data-read/write"
|
|
: (type == hw_execute ? "instruction-execute"
|
|
: "??unknown??"))));
|
|
puts_unfiltered (":\n");
|
|
|
|
for (i = 0; i < MAX_DEBUG_REGISTER; i++)
|
|
printf_unfiltered ("\tDR%d: lo=%s, hi=%s\n", i,
|
|
paddress (target_gdbarch,
|
|
get_watchlo (&watch_mirror, i)),
|
|
paddress (target_gdbarch,
|
|
get_watchhi (&watch_mirror, i)));
|
|
}
|
|
|
|
/* Return 1 if watch registers are usable. Cached information is used
|
|
unless force is true. */
|
|
|
|
static int
|
|
mips_linux_read_watch_registers (int force)
|
|
{
|
|
int tid;
|
|
|
|
if (force || watch_readback_valid == 0)
|
|
{
|
|
tid = ptid_get_lwp (inferior_ptid);
|
|
if (ptrace (PTRACE_GET_WATCH_REGS, tid, &watch_readback) == -1)
|
|
{
|
|
watch_readback_valid = -1;
|
|
return 0;
|
|
}
|
|
switch (watch_readback.style)
|
|
{
|
|
case pt_watch_style_mips32:
|
|
if (watch_readback.mips32.num_valid == 0)
|
|
{
|
|
watch_readback_valid = -1;
|
|
return 0;
|
|
}
|
|
break;
|
|
case pt_watch_style_mips64:
|
|
if (watch_readback.mips64.num_valid == 0)
|
|
{
|
|
watch_readback_valid = -1;
|
|
return 0;
|
|
}
|
|
break;
|
|
default:
|
|
watch_readback_valid = -1;
|
|
return 0;
|
|
}
|
|
/* Watch registers appear to be usable. */
|
|
watch_readback_valid = 1;
|
|
}
|
|
return (watch_readback_valid == 1) ? 1 : 0;
|
|
}
|
|
|
|
/* Convert GDB's type to an IRW mask. */
|
|
|
|
static unsigned
|
|
type_to_irw (int type)
|
|
{
|
|
switch (type)
|
|
{
|
|
case hw_write:
|
|
return W_MASK;
|
|
case hw_read:
|
|
return R_MASK;
|
|
case hw_access:
|
|
return (W_MASK | R_MASK);
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/* Target to_can_use_hw_breakpoint implementation. Return 1 if we can
|
|
handle the specified watch type. */
|
|
|
|
static int
|
|
mips_linux_can_use_hw_breakpoint (int type, int cnt, int ot)
|
|
{
|
|
int i;
|
|
uint32_t wanted_mask, irw_mask;
|
|
|
|
if (!mips_linux_read_watch_registers (0))
|
|
return 0;
|
|
|
|
switch (type)
|
|
{
|
|
case bp_hardware_watchpoint:
|
|
wanted_mask = W_MASK;
|
|
break;
|
|
case bp_read_watchpoint:
|
|
wanted_mask = R_MASK;
|
|
break;
|
|
case bp_access_watchpoint:
|
|
wanted_mask = R_MASK | W_MASK;
|
|
break;
|
|
default:
|
|
return 0;
|
|
}
|
|
|
|
for (i = 0; i < get_num_valid (&watch_readback) && cnt; i++)
|
|
{
|
|
irw_mask = get_irw_mask (&watch_readback, i);
|
|
if ((irw_mask & wanted_mask) == wanted_mask)
|
|
cnt--;
|
|
}
|
|
return (cnt == 0) ? 1 : 0;
|
|
}
|
|
|
|
/* Target to_stopped_by_watchpoint implementation. Return 1 if
|
|
stopped by watchpoint. The watchhi R and W bits indicate the watch
|
|
register triggered. */
|
|
|
|
static int
|
|
mips_linux_stopped_by_watchpoint (void)
|
|
{
|
|
int n;
|
|
int num_valid;
|
|
|
|
if (!mips_linux_read_watch_registers (1))
|
|
return 0;
|
|
|
|
num_valid = get_num_valid (&watch_readback);
|
|
|
|
for (n = 0; n < MAX_DEBUG_REGISTER && n < num_valid; n++)
|
|
if (get_watchhi (&watch_readback, n) & (R_MASK | W_MASK))
|
|
return 1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Target to_stopped_data_address implementation. Set the address
|
|
where the watch triggered (if known). Return 1 if the address was
|
|
known. */
|
|
|
|
static int
|
|
mips_linux_stopped_data_address (struct target_ops *t, CORE_ADDR *paddr)
|
|
{
|
|
/* On mips we don't know the low order 3 bits of the data address,
|
|
so we must return false. */
|
|
return 0;
|
|
}
|
|
|
|
/* Set any low order bits in mask that are not set. */
|
|
|
|
static CORE_ADDR
|
|
fill_mask (CORE_ADDR mask)
|
|
{
|
|
CORE_ADDR f = 1;
|
|
while (f && f < mask)
|
|
{
|
|
mask |= f;
|
|
f <<= 1;
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
/* Try to add a single watch to the specified registers. Return 1 on
|
|
success, 0 on failure. */
|
|
|
|
static int
|
|
try_one_watch (struct pt_watch_regs *regs, CORE_ADDR addr,
|
|
int len, unsigned irw)
|
|
{
|
|
CORE_ADDR base_addr, last_byte, break_addr, segment_len;
|
|
CORE_ADDR mask_bits, t_low, t_low_end;
|
|
uint16_t t_hi;
|
|
int i, free_watches;
|
|
struct pt_watch_regs regs_copy;
|
|
|
|
if (len <= 0)
|
|
return 0;
|
|
|
|
last_byte = addr + len - 1;
|
|
mask_bits = fill_mask (addr ^ last_byte) | IRW_MASK;
|
|
base_addr = addr & ~mask_bits;
|
|
|
|
/* Check to see if it is covered by current registers. */
|
|
for (i = 0; i < get_num_valid (regs); i++)
|
|
{
|
|
t_low = get_watchlo (regs, i);
|
|
if (t_low != 0 && irw == ((unsigned)t_low & irw))
|
|
{
|
|
t_hi = get_watchhi (regs, i) | IRW_MASK;
|
|
t_low &= ~(CORE_ADDR)t_hi;
|
|
if (addr >= t_low && last_byte <= (t_low + t_hi))
|
|
return 1;
|
|
}
|
|
}
|
|
/* Try to find an empty register. */
|
|
free_watches = 0;
|
|
for (i = 0; i < get_num_valid (regs); i++)
|
|
{
|
|
t_low = get_watchlo (regs, i);
|
|
if (t_low == 0 && irw == (get_irw_mask (regs, i) & irw))
|
|
{
|
|
if (mask_bits <= (get_reg_mask (regs, i) | IRW_MASK))
|
|
{
|
|
/* It fits, we'll take it. */
|
|
set_watchlo (regs, i, base_addr | irw);
|
|
set_watchhi (regs, i, mask_bits & ~IRW_MASK);
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
/* It doesn't fit, but has the proper IRW capabilities. */
|
|
free_watches++;
|
|
}
|
|
}
|
|
}
|
|
if (free_watches > 1)
|
|
{
|
|
/* Try to split it across several registers. */
|
|
regs_copy = *regs;
|
|
for (i = 0; i < get_num_valid (®s_copy); i++)
|
|
{
|
|
t_low = get_watchlo (®s_copy, i);
|
|
t_hi = get_reg_mask (®s_copy, i) | IRW_MASK;
|
|
if (t_low == 0 && irw == (t_hi & irw))
|
|
{
|
|
t_low = addr & ~(CORE_ADDR)t_hi;
|
|
break_addr = t_low + t_hi + 1;
|
|
if (break_addr >= addr + len)
|
|
segment_len = len;
|
|
else
|
|
segment_len = break_addr - addr;
|
|
mask_bits = fill_mask (addr ^ (addr + segment_len - 1));
|
|
set_watchlo (®s_copy, i, (addr & ~mask_bits) | irw);
|
|
set_watchhi (®s_copy, i, mask_bits & ~IRW_MASK);
|
|
if (break_addr >= addr + len)
|
|
{
|
|
*regs = regs_copy;
|
|
return 1;
|
|
}
|
|
len = addr + len - break_addr;
|
|
addr = break_addr;
|
|
}
|
|
}
|
|
}
|
|
/* It didn't fit anywhere, we failed. */
|
|
return 0;
|
|
}
|
|
|
|
/* Target to_region_ok_for_hw_watchpoint implementation. Return 1 if
|
|
the specified region can be covered by the watch registers. */
|
|
|
|
static int
|
|
mips_linux_region_ok_for_hw_watchpoint (CORE_ADDR addr, int len)
|
|
{
|
|
struct pt_watch_regs dummy_regs;
|
|
int i;
|
|
|
|
if (!mips_linux_read_watch_registers (0))
|
|
return 0;
|
|
|
|
dummy_regs = watch_readback;
|
|
/* Clear them out. */
|
|
for (i = 0; i < get_num_valid (&dummy_regs); i++)
|
|
set_watchlo (&dummy_regs, i, 0);
|
|
return try_one_watch (&dummy_regs, addr, len, 0);
|
|
}
|
|
|
|
|
|
/* Write the mirrored watch register values for each thread. */
|
|
|
|
static int
|
|
write_watchpoint_regs (void)
|
|
{
|
|
struct lwp_info *lp;
|
|
ptid_t ptid;
|
|
int tid;
|
|
|
|
ALL_LWPS (lp, ptid)
|
|
{
|
|
tid = ptid_get_lwp (ptid);
|
|
if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
|
|
perror_with_name (_("Couldn't write debug register"));
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* linux_nat new_thread implementation. Write the mirrored watch
|
|
register values for the new thread. */
|
|
|
|
static void
|
|
mips_linux_new_thread (ptid_t ptid)
|
|
{
|
|
int tid;
|
|
|
|
if (!mips_linux_read_watch_registers (0))
|
|
return;
|
|
|
|
tid = ptid_get_lwp (ptid);
|
|
if (ptrace (PTRACE_SET_WATCH_REGS, tid, &watch_mirror) == -1)
|
|
perror_with_name (_("Couldn't write debug register"));
|
|
}
|
|
|
|
/* Fill in the watch registers with the currently cached watches. */
|
|
|
|
static void
|
|
populate_regs_from_watches (struct pt_watch_regs *regs)
|
|
{
|
|
struct mips_watchpoint *w;
|
|
int i;
|
|
|
|
/* Clear them out. */
|
|
for (i = 0; i < get_num_valid (regs); i++)
|
|
{
|
|
set_watchlo (regs, i, 0);
|
|
set_watchhi (regs, i, 0);
|
|
}
|
|
|
|
w = current_watches;
|
|
while (w)
|
|
{
|
|
i = try_one_watch (regs, w->addr, w->len, type_to_irw (w->type));
|
|
/* They must all fit, because we previously calculated that they
|
|
would. */
|
|
gdb_assert (i);
|
|
w = w->next;
|
|
}
|
|
}
|
|
|
|
/* Target to_insert_watchpoint implementation. Try to insert a new
|
|
watch. Return zero on success. */
|
|
|
|
static int
|
|
mips_linux_insert_watchpoint (CORE_ADDR addr, int len, int type)
|
|
{
|
|
struct pt_watch_regs regs;
|
|
struct mips_watchpoint *new_watch;
|
|
struct mips_watchpoint **pw;
|
|
|
|
int i;
|
|
int retval;
|
|
|
|
if (!mips_linux_read_watch_registers (0))
|
|
return -1;
|
|
|
|
if (len <= 0)
|
|
return -1;
|
|
|
|
regs = watch_readback;
|
|
/* Add the current watches. */
|
|
populate_regs_from_watches (®s);
|
|
|
|
/* Now try to add the new watch. */
|
|
if (!try_one_watch (®s, addr, len, type_to_irw (type)))
|
|
return -1;
|
|
|
|
/* It fit. Stick it on the end of the list. */
|
|
new_watch = (struct mips_watchpoint *)
|
|
xmalloc (sizeof (struct mips_watchpoint));
|
|
new_watch->addr = addr;
|
|
new_watch->len = len;
|
|
new_watch->type = type;
|
|
new_watch->next = NULL;
|
|
|
|
pw = ¤t_watches;
|
|
while (*pw != NULL)
|
|
pw = &(*pw)->next;
|
|
*pw = new_watch;
|
|
|
|
watch_mirror = regs;
|
|
retval = write_watchpoint_regs ();
|
|
|
|
if (maint_show_dr)
|
|
mips_show_dr ("insert_watchpoint", addr, len, type);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* Target to_remove_watchpoint implementation. Try to remove a watch.
|
|
Return zero on success. */
|
|
|
|
static int
|
|
mips_linux_remove_watchpoint (CORE_ADDR addr, int len, int type)
|
|
{
|
|
int retval;
|
|
int deleted_one;
|
|
|
|
struct mips_watchpoint **pw;
|
|
struct mips_watchpoint *w;
|
|
|
|
/* Search for a known watch that matches. Then unlink and free
|
|
it. */
|
|
deleted_one = 0;
|
|
pw = ¤t_watches;
|
|
while ((w = *pw))
|
|
{
|
|
if (w->addr == addr && w->len == len && w->type == type)
|
|
{
|
|
*pw = w->next;
|
|
xfree (w);
|
|
deleted_one = 1;
|
|
break;
|
|
}
|
|
pw = &(w->next);
|
|
}
|
|
|
|
if (!deleted_one)
|
|
return -1; /* We don't know about it, fail doing nothing. */
|
|
|
|
/* At this point watch_readback is known to be valid because we
|
|
could not have added the watch without reading it. */
|
|
gdb_assert (watch_readback_valid == 1);
|
|
|
|
watch_mirror = watch_readback;
|
|
populate_regs_from_watches (&watch_mirror);
|
|
|
|
retval = write_watchpoint_regs ();
|
|
|
|
if (maint_show_dr)
|
|
mips_show_dr ("remove_watchpoint", addr, len, type);
|
|
|
|
return retval;
|
|
}
|
|
|
|
/* Target to_close implementation. Free any watches and call the
|
|
super implementation. */
|
|
|
|
static void
|
|
mips_linux_close (int quitting)
|
|
{
|
|
struct mips_watchpoint *w;
|
|
struct mips_watchpoint *nw;
|
|
|
|
/* Clean out the current_watches list. */
|
|
w = current_watches;
|
|
while (w)
|
|
{
|
|
nw = w->next;
|
|
xfree (w);
|
|
w = nw;
|
|
}
|
|
current_watches = NULL;
|
|
|
|
if (super_close)
|
|
super_close (quitting);
|
|
}
|
|
|
|
void _initialize_mips_linux_nat (void);
|
|
|
|
void
|
|
_initialize_mips_linux_nat (void)
|
|
{
|
|
struct target_ops *t;
|
|
|
|
add_setshow_boolean_cmd ("show-debug-regs", class_maintenance,
|
|
&maint_show_dr, _("\
|
|
Set whether to show variables that mirror the mips debug registers."), _("\
|
|
Show whether to show variables that mirror the mips debug registers."), _("\
|
|
Use \"on\" to enable, \"off\" to disable.\n\
|
|
If enabled, the debug registers values are shown when GDB inserts\n\
|
|
or removes a hardware breakpoint or watchpoint, and when the inferior\n\
|
|
triggers a breakpoint or watchpoint."),
|
|
NULL,
|
|
NULL,
|
|
&maintenance_set_cmdlist,
|
|
&maintenance_show_cmdlist);
|
|
|
|
t = linux_trad_target (mips_linux_register_u_offset);
|
|
|
|
super_close = t->to_close;
|
|
t->to_close = mips_linux_close;
|
|
|
|
super_fetch_registers = t->to_fetch_registers;
|
|
super_store_registers = t->to_store_registers;
|
|
|
|
t->to_fetch_registers = mips64_linux_fetch_registers;
|
|
t->to_store_registers = mips64_linux_store_registers;
|
|
|
|
t->to_can_use_hw_breakpoint = mips_linux_can_use_hw_breakpoint;
|
|
t->to_remove_watchpoint = mips_linux_remove_watchpoint;
|
|
t->to_insert_watchpoint = mips_linux_insert_watchpoint;
|
|
t->to_stopped_by_watchpoint = mips_linux_stopped_by_watchpoint;
|
|
t->to_stopped_data_address = mips_linux_stopped_data_address;
|
|
t->to_region_ok_for_hw_watchpoint = mips_linux_region_ok_for_hw_watchpoint;
|
|
|
|
t->to_read_description = mips_linux_read_description;
|
|
|
|
linux_nat_add_target (t);
|
|
linux_nat_set_new_thread (t, mips_linux_new_thread);
|
|
|
|
/* Initialize the standard target descriptions. */
|
|
initialize_tdesc_mips_linux ();
|
|
initialize_tdesc_mips64_linux ();
|
|
}
|